diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index d978621f2..2e6f0d6ab 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -253,9 +253,39 @@ function AI_CARGO:New( Type, Name, Weight, ReportRadius, NearRadius ) CARGOS[self.Name] = self + self:SetEventPriority( 5 ) + return self end +--- Get the name of the Cargo. +-- @param #AI_CARGO self +-- @return #string The name of the Cargo. +function AI_CARGO:GetName() + return self.Name +end + +--- Get the type of the Cargo. +-- @param #AI_CARGO self +-- @return #string The type of the Cargo. +function AI_CARGO:GetType() + return self.Type +end + +--- Check if cargo is loaded. +-- @param #AI_CARGO self +-- @return #boolean true if loaded +function AI_CARGO:IsLoaded() + return self:Is( "Loaded" ) +end + +--- Check if cargo is unloaded. +-- @param #AI_CARGO self +-- @return #boolean true if unloaded +function AI_CARGO:IsUnLoaded() + return self:Is( "UnLoaded" ) +end + --- Template method to spawn a new representation of the AI_CARGO in the simulator. -- @param #AI_CARGO self @@ -398,6 +428,20 @@ function AI_CARGO_UNIT:New( CargoUnit, Type, Name, Weight, ReportRadius, NearRad self:T( self.ClassName ) + -- Cargo objects are added to the _DATABASE and SET_CARGO objects. + _EVENTDISPATCHER:CreateEventNewCargo( self ) + + return self +end + +--- AI_CARGO_UNIT Destructor. +-- @param #AI_CARGO_UNIT self +-- @return #AI_CARGO_UNIT +function AI_CARGO_UNIT:Destroy() + + -- Cargo objects are deleted from the _DATABASE and SET_CARGO objects. + _EVENTDISPATCHER:CreateEventDeleteCargo( self ) + return self end @@ -539,7 +583,7 @@ end -- @param #string From -- @param #string To -- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_UNIT:onenterBoarding( From, Event, To, CargoCarrier ) +function AI_CARGO_UNIT:onenterBoarding( From, Event, To, CargoCarrier, ... ) self:F( { CargoCarrier.UnitName, From, Event, To } ) local Speed = 10 @@ -571,14 +615,14 @@ end -- @param #string From -- @param #string To -- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_UNIT:onleaveBoarding( From, Event, To, CargoCarrier ) +function AI_CARGO_UNIT:onleaveBoarding( From, Event, To, CargoCarrier, ... ) self:F( { CargoCarrier.UnitName, From, Event, To } ) if self:IsNear( CargoCarrier:GetPointVec2() ) then - self:__Load( 1, CargoCarrier ) + self:__Load( 1, CargoCarrier, ... ) return true else - self:__Boarding( 1, CargoCarrier ) + self:__Boarding( 1, CargoCarrier, ... ) end return false end @@ -607,7 +651,7 @@ end -- @param #string Event -- @param #string From -- @param #string To -function AI_CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier ) +function AI_CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, ... ) self:F() self.CargoInAir = self.CargoObject:InAir() @@ -617,7 +661,7 @@ function AI_CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier ) -- Only move the group to the carrier when the cargo is not in the air -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). if not self.CargoInAir then - self:Load( CargoCarrier ) + self:Load( CargoCarrier, ... ) end end diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 3e9eea077..04f162690 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -6,12 +6,14 @@ -- =================================================== -- Mission designers can use the DATABASE class to refer to: -- +-- * STATICS -- * UNITS -- * GROUPS -- * CLIENTS --- * AIRPORTS +-- * AIRBASES -- * PLAYERSJOINED -- * PLAYERS +-- * CARGOS -- -- On top, for internal MOOSE administration purposes, the DATBASE administers the Unit and Group TEMPLATES as defined within the Mission Editor. -- @@ -53,6 +55,7 @@ DATABASE = { PLAYERS = {}, PLAYERSJOINED = {}, CLIENTS = {}, + CARGOS = {}, AIRBASES = {}, COUNTRY_ID = {}, COUNTRY_NAME = {}, @@ -84,13 +87,15 @@ local _DATABASECategory = function DATABASE:New() -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, BASE:New() ) -- #DATABASE self:SetEventPriority( 1 ) self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.NewCargo ) + self:HandleEvent( EVENTS.DeleteCargo ) -- Follow alive players and clients self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) @@ -166,22 +171,24 @@ end --- Adds a Airbase based on the Airbase Name in the DATABASE. -- @param #DATABASE self -function DATABASE:AddAirbase( DCSAirbaseName ) +-- @param #string AirbaseName The name of the airbase +function DATABASE:AddAirbase( AirbaseName ) - if not self.AIRBASES[DCSAirbaseName] then - self.AIRBASES[DCSAirbaseName] = AIRBASE:Register( DCSAirbaseName ) + if not self.AIRBASES[AirbaseName] then + self.AIRBASES[AirbaseName] = AIRBASE:Register( AirbaseName ) end end --- Deletes a Airbase from the DATABASE based on the Airbase Name. -- @param #DATABASE self -function DATABASE:DeleteAirbase( DCSAirbaseName ) +-- @param #string AirbaseName The name of the airbase +function DATABASE:DeleteAirbase( AirbaseName ) - --self.AIRBASES[DCSAirbaseName] = nil + self.AIRBASES[AirbaseName] = nil end ---- Finds a AIRBASE based on the AirbaseName. +--- Finds an AIRBASE based on the AirbaseName. -- @param #DATABASE self -- @param #string AirbaseName -- @return Wrapper.Airbase#AIRBASE The found AIRBASE. @@ -191,6 +198,35 @@ function DATABASE:FindAirbase( AirbaseName ) return AirbaseFound end +--- Adds a Cargo based on the Cargo Name in the DATABASE. +-- @param #DATABASE self +-- @param #string CargoName The name of the airbase +function DATABASE:AddCargo( Cargo ) + + if not self.CARGOS[Cargo.Name] then + self.CARGOS[Cargo.Name] = Cargo + end +end + + +--- Deletes a Cargo from the DATABASE based on the Cargo Name. +-- @param #DATABASE self +-- @param #string CargoName The name of the airbase +function DATABASE:DeleteCargo( CargoName ) + + self.CARGOS[CargoName] = nil +end + +--- Finds an CARGO based on the CargoName. +-- @param #DATABASE self +-- @param #string CargoName +-- @return Wrapper.Cargo#CARGO The found CARGO. +function DATABASE:FindCargo( CargoName ) + + local CargoFound = self.CARGOS[CargoName] + return CargoFound +end + --- Finds a CLIENT based on the ClientName. -- @param #DATABASE self @@ -665,7 +701,7 @@ end --- Iterate the DATABASE and call an iterator function for each **alive** UNIT, providing the UNIT and optional parameters. -- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the database. The function needs to accept a UNIT parameter. +-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a UNIT parameter. -- @return #DATABASE self function DATABASE:ForEachUnit( IteratorFunction, FinalizeFunction, ... ) self:F2( arg ) @@ -677,7 +713,7 @@ end --- Iterate the DATABASE and call an iterator function for each **alive** GROUP, providing the GROUP and optional parameters. -- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the database. The function needs to accept a GROUP parameter. +-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a GROUP parameter. -- @return #DATABASE self function DATABASE:ForEachGroup( IteratorFunction, ... ) self:F2( arg ) @@ -690,7 +726,7 @@ end --- Iterate the DATABASE and call an iterator function for each **ALIVE** player, providing the player name and optional parameters. -- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an player in the database. The function needs to accept the player name. +-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept the player name. -- @return #DATABASE self function DATABASE:ForEachPlayer( IteratorFunction, ... ) self:F2( arg ) @@ -703,7 +739,7 @@ end --- 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. -- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is was a player in the database. The function needs to accept a UNIT parameter. +-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a UNIT parameter. -- @return #DATABASE self function DATABASE:ForEachPlayerJoined( IteratorFunction, ... ) self:F2( arg ) @@ -715,7 +751,7 @@ end --- Iterate the DATABASE and call an iterator function for each CLIENT, providing the CLIENT to the function and optional parameters. -- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an alive player in the database. The function needs to accept a CLIENT parameter. +-- @param #function IteratorFunction The function that will be called object in the database. The function needs to accept a CLIENT parameter. -- @return #DATABASE self function DATABASE:ForEachClient( IteratorFunction, ... ) self:F2( arg ) @@ -725,6 +761,42 @@ function DATABASE:ForEachClient( IteratorFunction, ... ) return self end +--- Iterate the DATABASE and call an iterator function for each CARGO, providing the CARGO object to the function and optional parameters. +-- @param #DATABASE self +-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a CLIENT parameter. +-- @return #DATABASE self +function DATABASE:ForEachCargo( IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self.CARGOS ) + + return self +end + + +--- Handles the OnEventNewCargo event. +-- @param #DATABASE self +-- @param Core.Event#EVENTDATA EventData +function DATABASE:OnEventNewCargo( EventData ) + self:F2( { EventData } ) + + if EventData.Cargo then + self:AddCargo( EventData.Cargo ) + end +end + + +--- Handles the OnEventDeleteCargo. +-- @param #DATABASE self +-- @param Core.Event#EVENTDATA EventData +function DATABASE:OnEventDeleteCargo( EventData ) + self:F2( { EventData } ) + + if EventData.Cargo then + self:DeleteCargo( EventData.Cargo.Name ) + end +end + function DATABASE:_RegisterTemplates() self:F2() diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 39b0738af..7234188a9 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -197,6 +197,9 @@ EVENT = { ClassID = 0, } +world.event.S_EVENT_NEW_CARGO = world.event.S_EVENT_MAX + 1000 +world.event.S_EVENT_DELETE_CARGO = world.event.S_EVENT_MAX + 1001 + --- The different types of events supported by MOOSE. -- Use this structure to subscribe to events using the @{Base#BASE.HandleEvent}() method. -- @type EVENTS @@ -224,6 +227,8 @@ EVENTS = { PlayerComment = world.event.S_EVENT_PLAYER_COMMENT, ShootingStart = world.event.S_EVENT_SHOOTING_START, ShootingEnd = world.event.S_EVENT_SHOOTING_END, + NewCargo = world.event.S_EVENT_NEW_CARGO, + DeleteCargo = world.event.S_EVENT_DELETE_CARGO, } --- The Event structure @@ -271,6 +276,7 @@ EVENTS = { -- @field WeaponTgtDCSUnit + local _EVENTMETA = { [world.event.S_EVENT_SHOT] = { Order = 1, @@ -387,6 +393,16 @@ local _EVENTMETA = { Event = "OnEventShootingEnd", Text = "S_EVENT_SHOOTING_END" }, + [EVENTS.NewCargo] = { + Order = 1, + Event = "OnEventNewCargo", + Text = "S_EVENT_NEW_CARGO" + }, + [EVENTS.DeleteCargo] = { + Order = 1, + Event = "OnEventDeleteCargo", + Text = "S_EVENT_DELETE_CARGO" + }, } @@ -672,6 +688,39 @@ do -- OnEngineShutDown end +do -- Event Creation + + --- Creation of a New Cargo Event. + -- @param #EVENT self + -- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created. + function EVENT:CreateEventNewCargo( Cargo ) + self:F( { Cargo } ) + + local Event = { + id = EVENTS.NewCargo, + time = timer.getTime(), + cargo = Cargo, + } + + world.onEvent( Event ) + end + + --- Creation of a Cargo Deletion Event. + -- @param #EVENT self + -- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created. + function EVENT:CreateEventDeleteCargo( Cargo ) + self:F( { Cargo } ) + + local Event = { + id = EVENTS.DeleteCargo, + time = timer.getTime(), + cargo = Cargo, + } + + world.onEvent( Event ) + end + +end --- @param #EVENT self -- @param #EVENTDATA Event @@ -795,6 +844,11 @@ function EVENT:onEvent( Event ) --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() end + if Event.cargo then + Event.Cargo = Event.cargo + Event.CargoName = Event.cargo.Name + end + local PriorityOrder = _EVENTMETA[Event.id].Order local PriorityBegin = PriorityOrder == -1 and 5 or 1 local PriorityEnd = PriorityOrder == -1 and 1 or 5 @@ -956,7 +1010,8 @@ function EVENT:onEvent( Event ) -- If the EventData is not bound to a specific unit, then call the EventClass EventFunction. -- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon. - if (Event.IniDCSUnit or Event.WeaponUNIT) and not EventData.EventUnit then + if ( ( Event.IniDCSUnit or Event.WeaponUNIT) and not EventData.EventUnit ) or + Event.Cargo then if EventClass == EventData.EventClass then diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index bfb6ac69e..52e526061 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -2391,3 +2391,346 @@ function SET_AIRBASE:IsIncludeObject( MAirbase ) self:T2( MAirbaseInclude ) return MAirbaseInclude end + +--- @type SET_CARGO +-- @extends Core.Set#SET_BASE + +--- # SET_CARGO class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the @{Set#SET_CARGO} class to build sets of cargos optionally belonging to certain: +-- +-- * Coalitions +-- * Types +-- * Name or Prefix +-- +-- ## SET_CARGO constructor +-- +-- Create a new SET_CARGO object with the @{#SET_CARGO.New} method: +-- +-- * @{#SET_CARGO.New}: Creates a new SET_CARGO object. +-- +-- ## Add or Remove CARGOs from SET_CARGO +-- +-- CARGOs can be added and removed using the @{Set#SET_CARGO.AddCargosByName} and @{Set#SET_CARGO.RemoveCargosByName} respectively. +-- These methods take a single CARGO name or an array of CARGO names to be added or removed from SET_CARGO. +-- +-- ## SET_CARGO filter criteria +-- +-- You can set filter criteria to automatically maintain the SET_CARGO contents. +-- Filter criteria are defined by: +-- +-- * @{#SET_CARGO.FilterCoalitions}: Builds the SET_CARGO with the cargos belonging to the coalition(s). +-- * @{#SET_CARGO.FilterPrefixes}: Builds the SET_CARGO with the cargos containing the prefix string(s). +-- * @{#SET_CARGO.FilterTypes}: Builds the SET_CARGO with the cargos belonging to the cargo type(s). +-- * @{#SET_CARGO.FilterCountries}: Builds the SET_CARGO with the cargos belonging to the country(ies). +-- +-- Once the filter criteria have been set for the SET_CARGO, you can start filtering using: +-- +-- * @{#SET_CARGO.FilterStart}: Starts the filtering of the cargos within the SET_CARGO. +-- +-- ## SET_CARGO iterators +-- +-- Once the filters have been defined and the SET_CARGO has been built, you can iterate the SET_CARGO with the available iterator methods. +-- The iterator methods will walk the SET_CARGO set, and call for each cargo within the set a function that you provide. +-- The following iterator methods are currently available within the SET_CARGO: +-- +-- * @{#SET_CARGO.ForEachCargo}: Calls a function for each cargo it finds within the SET_CARGO. +-- +-- @field #SET_CARGO SET_CARGO +-- +SET_CARGO = { + ClassName = "SET_CARGO", + Cargos = {}, + Filter = { + Coalitions = nil, + Types = nil, + Countries = nil, + ClientPrefixes = nil, + }, + FilterMeta = { + Coalitions = { + red = coalition.side.RED, + blue = coalition.side.BLUE, + neutral = coalition.side.NEUTRAL, + }, + }, +} + + +--- Creates a new SET_CARGO object, building a set of cargos belonging to a coalitions and categories. +-- @param #SET_CARGO self +-- @return #SET_CARGO self +-- @usage +-- -- Define a new SET_CARGO Object. The DatabaseSet will contain a reference to all Cargos. +-- DatabaseSet = SET_CARGO:New() +function SET_CARGO:New() + -- Inherits from BASE + local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CARGOS ) ) + + self:HandleEvent( EVENTS.NewCargo ) + self:HandleEvent( EVENTS.DeleteCargo ) + + return self +end + +--- Add CARGOs to SET_CARGO. +-- @param Core.Set#SET_CARGO self +-- @param #string AddCargoNames A single name or an array of CARGO names. +-- @return self +function SET_CARGO:AddCargosByName( AddCargoNames ) + + local AddCargoNamesArray = ( type( AddCargoNames ) == "table" ) and AddCargoNames or { AddCargoNames } + + for AddCargoID, AddCargoName in pairs( AddCargoNamesArray ) do + self:Add( AddCargoName, CARGO:FindByName( AddCargoName ) ) + end + + return self +end + +--- Remove CARGOs from SET_CARGO. +-- @param Core.Set#SET_CARGO self +-- @param Wrapper.Cargo#CARGO RemoveCargoNames A single name or an array of CARGO names. +-- @return self +function SET_CARGO:RemoveCargosByName( RemoveCargoNames ) + + local RemoveCargoNamesArray = ( type( RemoveCargoNames ) == "table" ) and RemoveCargoNames or { RemoveCargoNames } + + for RemoveCargoID, RemoveCargoName in pairs( RemoveCargoNamesArray ) do + self:Remove( RemoveCargoName.CargoName ) + end + + return self +end + + +--- Finds a Cargo based on the Cargo Name. +-- @param #SET_CARGO self +-- @param #string CargoName +-- @return Wrapper.Cargo#CARGO The found Cargo. +function SET_CARGO:FindCargo( CargoName ) + + local CargoFound = self.Set[CargoName] + return CargoFound +end + + + +--- Builds a set of cargos of coalitions. +-- Possible current coalitions are red, blue and neutral. +-- @param #SET_CARGO self +-- @param #string Coalitions Can take the following values: "red", "blue", "neutral". +-- @return #SET_CARGO self +function SET_CARGO:FilterCoalitions( Coalitions ) + if not self.Filter.Coalitions then + self.Filter.Coalitions = {} + end + if type( Coalitions ) ~= "table" then + Coalitions = { Coalitions } + end + for CoalitionID, Coalition in pairs( Coalitions ) do + self.Filter.Coalitions[Coalition] = Coalition + end + return self +end + +--- Builds a set of cargos of defined cargo types. +-- Possible current types are those types known within DCS world. +-- @param #SET_CARGO self +-- @param #string Types Can take those type strings known within DCS world. +-- @return #SET_CARGO self +function SET_CARGO:FilterTypes( Types ) + if not self.Filter.Types then + self.Filter.Types = {} + end + if type( Types ) ~= "table" then + Types = { Types } + end + for TypeID, Type in pairs( Types ) do + self.Filter.Types[Type] = Type + end + return self +end + + +--- Builds a set of cargos of defined countries. +-- Possible current countries are those known within DCS world. +-- @param #SET_CARGO self +-- @param #string Countries Can take those country strings known within DCS world. +-- @return #SET_CARGO self +function SET_CARGO:FilterCountries( Countries ) + if not self.Filter.Countries then + self.Filter.Countries = {} + end + if type( Countries ) ~= "table" then + Countries = { Countries } + end + for CountryID, Country in pairs( Countries ) do + self.Filter.Countries[Country] = Country + end + return self +end + + +--- Builds a set of cargos of defined cargo prefixes. +-- All the cargos starting with the given prefixes will be included within the set. +-- @param #SET_CARGO self +-- @param #string Prefixes The prefix of which the cargo name starts with. +-- @return #SET_CARGO self +function SET_CARGO:FilterPrefixes( Prefixes ) + if not self.Filter.CargoPrefixes then + self.Filter.CargoPrefixes = {} + end + if type( Prefixes ) ~= "table" then + Prefixes = { Prefixes } + end + for PrefixID, Prefix in pairs( Prefixes ) do + self.Filter.CargoPrefixes[Prefix] = Prefix + end + return self +end + + + +--- Starts the filtering. +-- @param #SET_CARGO self +-- @return #SET_CARGO self +function SET_CARGO:FilterStart() + + if _DATABASE then + self:_FilterStart() + end + + return self +end + + +--- Handles the Database to check on an event (birth) that the Object was added in the Database. +-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! +-- @param #SET_CARGO self +-- @param Core.Event#EVENTDATA Event +-- @return #string The name of the CARGO +-- @return #table The CARGO +function SET_CARGO:AddInDatabase( Event ) + self:F3( { Event } ) + + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] +end + +--- Handles the Database to check on any event that Object exists in the Database. +-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa! +-- @param #SET_CARGO self +-- @param Core.Event#EVENTDATA Event +-- @return #string The name of the CARGO +-- @return #table The CARGO +function SET_CARGO:FindInDatabase( Event ) + self:F3( { Event } ) + + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] +end + +--- Iterate the SET_CARGO and call an interator function for each CARGO, providing the CARGO and optional parameters. +-- @param #SET_CARGO self +-- @param #function IteratorFunction The function that will be called when there is an alive CARGO in the SET_CARGO. The function needs to accept a CARGO parameter. +-- @return #SET_CARGO self +function SET_CARGO:ForEachCargo( IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self.Set ) + + return self +end + +--- Iterate the SET_CARGO while identifying the nearest @{Cargo#CARGO} from a @{Point#POINT_VEC2}. +-- @param #SET_CARGO self +-- @param Core.Point#POINT_VEC2 PointVec2 A @{Point#POINT_VEC2} object from where to evaluate the closest @{Cargo#CARGO}. +-- @return Wrapper.Cargo#CARGO The closest @{Cargo#CARGO}. +function SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) + self:F2( PointVec2 ) + + local NearestCargo = self:FindNearestObjectFromPointVec2( PointVec2 ) + return NearestCargo +end + + + +--- +-- @param #SET_CARGO self +-- @param AI.AI_Cargo#AI_CARGO MCargo +-- @return #SET_CARGO self +function SET_CARGO:IsIncludeObject( MCargo ) + self:F2( MCargo ) + + local MCargoInclude = true + + if MCargo then + local MCargoName = MCargo:GetName() + + if self.Filter.Coalitions then + local MCargoCoalition = false + for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do + local CargoCoalitionID = MCargo:GetCoalition() + self:T3( { "Coalition:", CargoCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == CargoCoalitionID then + MCargoCoalition = true + end + end + self:T( { "Evaluated Coalition", MCargoCoalition } ) + MCargoInclude = MCargoInclude and MCargoCoalition + end + + if self.Filter.Types then + local MCargoType = false + for TypeID, TypeName in pairs( self.Filter.Types ) do + self:T3( { "Type:", MCargo:GetType(), TypeName } ) + if TypeName == MCargo:GetType() then + MCargoType = true + end + end + self:T( { "Evaluated Type", MCargoType } ) + MCargoInclude = MCargoInclude and MCargoType + end + + if self.Filter.CargoPrefixes then + local MCargoPrefix = false + for CargoPrefixId, CargoPrefix in pairs( self.Filter.CargoPrefixes ) do + self:T3( { "Prefix:", string.find( MCargo.Name, CargoPrefix, 1 ), CargoPrefix } ) + if string.find( MCargo.Name, CargoPrefix, 1 ) then + MCargoPrefix = true + end + end + self:T( { "Evaluated Prefix", MCargoPrefix } ) + MCargoInclude = MCargoInclude and MCargoPrefix + end + end + + self:T2( MCargoInclude ) + return MCargoInclude +end + +--- Handles the OnEventNewCargo event for the Set. +-- @param #SET_CARGO self +-- @param Core.Event#EVENTDATA EventData +function SET_CARGO:OnEventNewCargo( EventData ) + + if EventData.Cargo then + if EventData.Cargo and self:IsIncludeObject( EventData.Cargo ) then + self:Add( EventData.Cargo.Name , EventData.Cargo ) + end + end +end + +--- Handles the OnDead or OnCrash event for alive units set. +-- @param #SET_CARGO self +-- @param Core.Event#EVENTDATA EventData +function SET_CARGO:OnEventDeleteCargo( EventData ) + self:F3( { EventData } ) + + if EventData.Cargo then + local Cargo = _DATABASE:FindCargo( EventData.Cargo.Name ) + if Cargo and Cargo.Name then + self:Remove( Cargo.Name ) + end + end +end + diff --git a/Moose Development/Moose/Moose.lua b/Moose Development/Moose/Moose.lua index 0312878a0..5195ff310 100644 --- a/Moose Development/Moose/Moose.lua +++ b/Moose Development/Moose/Moose.lua @@ -74,7 +74,7 @@ _EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT _SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.Timer#SCHEDULEDISPATCHER --- Declare the main database object, which is used internally by the MOOSE classes. -_DATABASE = DATABASE:New() -- Database#DATABASE +_DATABASE = DATABASE:New() -- Core.Database#DATABASE diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index b2ba8bce6..d44a8a920 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -66,14 +66,14 @@ do -- TASK_CARGO -- @param Tasking.Mission#MISSION Mission -- @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 AI.AI_Cargo#AI_CARGO Cargo The cargo. + -- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported. -- @param #string TaskType The type of Cargo task. -- @return #TASK_CARGO self - function TASK_CARGO:New( Mission, SetGroup, TaskName, Cargo, TaskType ) + function TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, TaskType ) local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) -- #TASK_CARGO - self:F( {Mission, SetGroup, TaskName, Cargo, TaskType}) + self:F( {Mission, SetGroup, TaskName, SetCargo, TaskType}) - self.Cargo = Cargo + self.SetCargo = SetCargo self.TaskType = TaskType Mission:AddTask( self ) @@ -81,46 +81,125 @@ do -- TASK_CARGO local Fsm = self:GetUnitProcess() - Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToCargo", Rejected = "Reject" } ) + Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } ) - Fsm:AddTransition( "Assigned", "RouteToCargo", "RoutingToCargo" ) - Fsm:AddProcess ( "RoutingToCargo", "RouteToCargoPickup", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtCargo" } ) + Fsm:AddTransition( { "Assigned", "Landed", "Boarded", "Deployed" } , "SelectAction", "WaitingForCommand" ) + + Fsm:AddTransition( "WaitingForCommand", "RouteToPickup", "RoutingToPickup" ) + Fsm:AddProcess ( "RoutingToPickup", "RouteToPickupPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtPickup" } ) + Fsm:AddTransition( "Arrived", "ArriveAtPickup", "ArrivedAtPickup" ) + + Fsm:AddTransition( "WaitingForCommand", "RouteToDeploy", "RoutingToDeploy" ) + Fsm:AddProcess ( "RoutingToDeploy", "RouteToDeployPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtDeploy" } ) + Fsm:AddTransition( "ArrivedAtDeploy", "ArriveAtDeploy", "ArrivedAtDeploy" ) - Fsm:AddTransition( { "Arrived", "RoutingToCargo" }, "ArriveAtCargo", "ArrivedAtCargo" ) - - Fsm:AddTransition( { "ArrivedAtCargo", "LandAtCargo" }, "Land", "Landing" ) + Fsm:AddTransition( { "ArrivedAtPickup", "ArrivedAtDeploy" }, "Land", "Landing" ) Fsm:AddTransition( "Landing", "Landed", "Landed" ) - Fsm:AddTransition( "OnGround", "PrepareBoarding", "AwaitBoarding" ) + Fsm:AddTransition( "WaitingForCommand", "PrepareBoarding", "AwaitBoarding" ) Fsm:AddTransition( "AwaitBoarding", "Board", "Boarding" ) Fsm:AddTransition( "Boarding", "Boarded", "Boarded" ) + + Fsm:AddTransition( "WaitingForCommand", "PrepareUnBoarding", "AwaitUnBoarding" ) + Fsm:AddTransition( "AwaitUnBoarding", "UnBoard", "UnBoarding" ) + Fsm:AddTransition( "UnBoarding", "UnBoarded", "UnBoarded" ) - Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) - Fsm:AddTransition( "Accounted", "Success", "Success" ) + + Fsm:AddTransition( "Deployed", "Success", "Success" ) Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Failed", "Fail", "Failed" ) - - --- Route to Cargo - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_CARGO#TASK_CARGO Task - function Fsm:onafterRouteToCargo( TaskUnit, Task ) - self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - - - Task:SetCargoPickup( Task.Cargo, TaskUnit ) - self:__RouteToCargoPickup( 0.1 ) - end --- -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_CARGO#TASK_CARGO Task - function Fsm:OnAfterArriveAtCargo( TaskUnit, Task ) + function Fsm:OnEnterWaitingForCommand( TaskUnit, Task ) self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - if Task.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + TaskUnit.Menu = MENU_GROUP:New( TaskUnit:GetGroup(), Task:GetName() .. " @ " .. TaskUnit:GetName() ) + + Task.SetCargo:ForEachCargo( + + --- @param AI.AI_Cargo#AI_CARGO Cargo + function( Cargo ) + if Cargo:IsUnLoaded() then + if Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + MENU_GROUP_COMMAND:New( + TaskUnit:GetGroup(), + "Pickup cargo " .. Cargo.Name, + TaskUnit.Menu, + self.MenuBoardCargo, + self, + Cargo + ) + else + MENU_GROUP_COMMAND:New( + TaskUnit:GetGroup(), + "Route to cargo " .. Cargo.Name, + TaskUnit.Menu, + self.MenuRouteToPickup, + self, + Cargo + ) + end + end + + if Cargo:IsLoaded() then + MENU_GROUP_COMMAND:New( + TaskUnit:GetGroup(), + "Deploy cargo " .. Cargo.Name, + TaskUnit.Menu, + self.MenuBoardCargo, + self, + Cargo + ) + end + + end + ) + end + + --- + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_CARGO#TASK_CARGO Task + function Fsm:OnLeaveWaitingForCommand( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + TaskUnit.Menu:Remove() + end + + function Fsm:MenuBoardCargo( Cargo ) + self:__PrepareBoarding( 1.0, Cargo ) + end + + function Fsm:MenuRouteToPickup( Cargo ) + self:__RouteToPickup( 1.0, Cargo ) + end + + --- Route to Cargo + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_CARGO#TASK_CARGO Task + function Fsm:onafterRouteToPickup( TaskUnit, Task, From, Event, To, Cargo ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + + self.Cargo = Cargo + Task:SetCargoPickup( self.Cargo, TaskUnit ) + self:__RouteToPickupPoint( 0.1 ) + end + + + --- + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_CARGO#TASK_CARGO Task + function Fsm:OnAfterArriveAtPickup( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then self:__Land( -0.1 ) else self:__ArriveAtCargo( -10 ) @@ -131,10 +210,10 @@ do -- TASK_CARGO -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_CARGO#TASK_CARGO Task - function Fsm:OnAfterLand( TaskUnit, Task ) + function Fsm:OnAfterLand( TaskUnit, Task, From, Event, To ) self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - if Task.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then if TaskUnit:InAir() then Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup(), "Land" ) self:__Land( -10 ) @@ -146,6 +225,82 @@ do -- TASK_CARGO self:__ArriveAtCargo( -0.1 ) end end + + --- + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_CARGO#TASK_CARGO Task + function Fsm:OnAfterLanded( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + if TaskUnit:InAir() then + self:__Land( -0.1 ) + else + Task:GetMission():GetCommandCenter():MessageToGroup( "Preparing to board in 10 seconds ...", TaskUnit:GetGroup(), "Boarding" ) + self:__PrepareBoarding( -10 ) + end + else + self:__ArriveAtCargo( -0.1 ) + end + end + + --- + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_CARGO#TASK_CARGO Task + function Fsm:OnAfterPrepareBoarding( TaskUnit, Task, From, Event, To, Cargo ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + self.Cargo = Cargo + if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + if TaskUnit:InAir() then + self:__Land( -0.1 ) + else + self:__Board( -0.1 ) + end + else + self:__ArriveAtCargo( -0.1 ) + end + end + + --- + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_CARGO#TASK_CARGO Task + function Fsm:OnAfterBoard( TaskUnit, Task ) + 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 + self:__Land( -0.1 ) + else + Task:GetMission():GetCommandCenter():MessageToGroup( "Boarding ...", TaskUnit:GetGroup(), "Boarding" ) + self.Cargo:Board( TaskUnit, self ) + end + else + self:__ArriveAtCargo( -0.1 ) + end + end + + --- + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_CARGO#TASK_CARGO Task + function Fsm:OnAfterBoarded( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + Task:GetMission():GetCommandCenter():MessageToGroup( "Boarded ...", TaskUnit:GetGroup(), "Boarding" ) + end return self @@ -165,7 +320,7 @@ do -- TASK_CARGO self:F({Cargo, TaskUnit}) local ProcessUnit = self:GetUnitProcess( TaskUnit ) - local ActRouteCargo = ProcessUnit:GetProcess( "RoutingToCargo", "RouteToCargoPickup" ) -- Actions.Act_Route#ACT_ROUTE_POINT + local ActRouteCargo = ProcessUnit:GetProcess( "RoutingToPickup", "RouteToPickupPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT ActRouteCargo:SetPointVec2( Cargo:GetPointVec2() ) ActRouteCargo:SetRange( Cargo:GetBoardingRange() ) return self @@ -284,10 +439,10 @@ do -- TASK_CARGO_TRANSPORT -- @param Tasking.Mission#MISSION Mission -- @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 AI_Cargo#AI_CARGO Cargo The cargo. + -- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported. -- @return #TASK_CARGO_TRANSPORT self - function TASK_CARGO_TRANSPORT:New( Mission, SetGroup, TaskName, Cargo ) - local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, Cargo, "TRANSPORT" ) ) -- #TASK_CARGO_TRANSPORT + 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 self:F() return self diff --git a/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua b/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua index 4db9338c3..3b2b611ed 100644 --- a/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua +++ b/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua @@ -1,5 +1,5 @@ env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20170325_0745' ) +env.info( 'Moose Generation Timestamp: 20170328_0728' ) local base = _G diff --git a/Moose Mission Setup/Moose.lua b/Moose Mission Setup/Moose.lua index 4db9338c3..3b2b611ed 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: 20170325_0745' ) +env.info( 'Moose Generation Timestamp: 20170328_0728' ) local base = _G diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-100 - Cargo Pickup/TSK-100 - Cargo Pickup.lua b/Moose Test Missions/TSK - Task Modelling/TSK-100 - Cargo Pickup/TSK-100 - Cargo Pickup.lua index 6ec266dd7..363e7fcdc 100644 --- a/Moose Test Missions/TSK - Task Modelling/TSK-100 - Cargo Pickup/TSK-100 - Cargo Pickup.lua +++ b/Moose Test Missions/TSK - Task Modelling/TSK-100 - Cargo Pickup/TSK-100 - Cargo Pickup.lua @@ -26,8 +26,10 @@ do CargoEngineer = UNIT:FindByName( "Engineer" ) InfantryCargo = AI_CARGO_UNIT:New( CargoEngineer, "Engineer", "Engineer Sven", "81", 500, 25 ) + + SetCargo = SET_CARGO:New():FilterTypes( "Engineer" ):FilterStart() - Task_Cargo_Pickup = TASK_CARGO_TRANSPORT:New( Mission, TransportHelicopters, "Transport.001", InfantryCargo ) + Task_Cargo_Pickup = TASK_CARGO_TRANSPORT:New( Mission, TransportHelicopters, "Transport.001", SetCargo ) end diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-100 - Cargo Pickup/TSK-100 - Cargo Pickup.miz b/Moose Test Missions/TSK - Task Modelling/TSK-100 - Cargo Pickup/TSK-100 - Cargo Pickup.miz index 834f0ccfd..a06b1f4d1 100644 Binary files a/Moose Test Missions/TSK - Task Modelling/TSK-100 - Cargo Pickup/TSK-100 - Cargo Pickup.miz and b/Moose Test Missions/TSK - Task Modelling/TSK-100 - Cargo Pickup/TSK-100 - Cargo Pickup.miz differ