diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 586c0ab51..a5a08b2e7 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -393,7 +393,7 @@ end function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, PickupZone ) self:F( { Carrier, From, Event, To, Cargo, CarrierUnit:GetName() } ) - if Carrier and Carrier:IsAlive() then + if Carrier and Carrier:IsAlive() and From == "Boarding" then self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), Carrier:GetName() } ) if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then self:__Board( -10, Cargo, CarrierUnit, PickupZone ) @@ -509,7 +509,7 @@ end function AI_CARGO:onafterUnboard( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend ) self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone, Defend = Defend } ) - if Carrier and Carrier:IsAlive() then + if Carrier and Carrier:IsAlive() and From == "Unboarding" then if not Cargo:IsUnLoaded() then self:__Unboard( 10, Cargo, CarrierUnit, DeployZone, Defend ) return diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index c20475711..ac63297d0 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -428,18 +428,27 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, He self.RouteDeploy = true - local _speed=Speed or APC:GetSpeedMax()*0.5 - - local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) + -- Set speed in km/h. + local speedmax=APC:GetSpeedMax() + local _speed=Speed or speedmax*0.5 + _speed=math.min(_speed, speedmax) + -- Route on road. + local Waypoints = APC:TaskGroundOnRoad(Coordinate, _speed, "Line abreast", true) + + -- Task function local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self, Coordinate, DeployZone ) - self:F({Waypoints = Waypoints}) + -- Last waypoint local Waypoint = Waypoints[#Waypoints] - APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. + + -- Set task function + APC:SetTaskWaypoint(Waypoint, TaskFunction) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. + -- Route group APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. + -- Call parent function. self:GetParent( self, AI_CARGO_APC ).onafterDeploy( self, APC, From, Event, To, Coordinate, Speed, Height, DeployZone ) end diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 4b838fde5..0307266c7 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -67,16 +67,22 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) self:AddTransition( "Unloaded", "Pickup", "*" ) self:AddTransition( "Loaded", "Deploy", "*" ) - + --[[ self:AddTransition( { "Unloaded", "Loading" }, "Load", "Boarding" ) self:AddTransition( "Boarding", "Board", "Boarding" ) - self:AddTransition( "Boarding", "Loaded", "Boarding" ) + self:AddTransition( "Boarding", "Loaded", "Loaded" ) self:AddTransition( "Boarding", "PickedUp", "Loaded" ) + self:AddTransition( "Boarding", "Deploy", "Loaded" ) self:AddTransition( "Loaded", "Unload", "Unboarding" ) self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) self:AddTransition( "Unboarding", "Unloaded", "Unboarding" ) self:AddTransition( "Unboarding", "Deployed", "Unloaded" ) - + self:AddTransition( "Unboarding", "Pickup", "Unloaded" ) + --]] + self:AddTransition( "Boarding", "Loaded", "Loaded" ) + self:AddTransition( "Unboarding", "Pickup", "Unloaded" ) + self:AddTransition( "Unloaded", "Unboard", "Unloaded" ) + self:AddTransition( "Unloaded", "Unloaded", "Unloaded" ) self:AddTransition( "*", "Landed", "*" ) self:AddTransition( "*", "Queue", "*" ) self:AddTransition( "*", "Orbit" , "*" ) @@ -243,7 +249,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) self:F( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } ) if self.RoutePickup == true then - if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then + if Helicopter:GetHeight( true ) <= 5.5 and Helicopter:GetVelocityKMH() < 10 then --self:Load( Helicopter:GetPointVec2() ) self:Load( self.PickupZone ) self.RoutePickup = false @@ -251,7 +257,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) end if self.RouteDeploy == true then - if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then + if Helicopter:GetHeight( true ) <= 5.5 and Helicopter:GetVelocityKMH() < 10 then self:Unload( self.DeployZone ) self.RouteDeploy = false end @@ -308,7 +314,11 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina -- true -- ) -- Route[#Route+1] = WaypointFrom - local CoordinateTo = Coordinate + local CoordinateTo = Coordinate + + local landheight = CoordinateTo:GetLandHeight() -- get target height + CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground + local WaypointTo = CoordinateTo:WaypointAir( "RADIO", POINT_VEC3.RoutePointType.TurningPoint, @@ -362,7 +372,10 @@ function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordina -- true -- ) -- Route[#Route+1] = WaypointFrom - local CoordinateTo = Coordinate + local CoordinateTo = Coordinate + local landheight = CoordinateTo:GetLandHeight() -- get target height + CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground + local WaypointTo = CoordinateTo:WaypointAir( "RADIO", POINT_VEC3.RoutePointType.TurningPoint, @@ -422,7 +435,8 @@ end -- @param #number Height Height in meters to move to the pickup coordinate. This parameter is ignored for APCs. -- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed, Height, PickupZone ) - + self:F({Coordinate, Speed, Height, PickupZone }) + if Helicopter and Helicopter:IsAlive() ~= nil then Helicopter:Activate() @@ -436,7 +450,6 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin --- Calculate the target route point. local CoordinateFrom = Helicopter:GetCoordinate() - local CoordinateTo = Coordinate --- Create a route point of type air. local WaypointFrom = CoordinateFrom:WaypointAir( @@ -448,6 +461,10 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin ) --- Create a route point of type air. + local CoordinateTo = Coordinate + local landheight = CoordinateTo:GetLandHeight() -- get target height + CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground + local WaypointTo = CoordinateTo:WaypointAir( "RADIO", POINT_VEC3.RoutePointType.TurningPoint, @@ -525,7 +542,11 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin Route[#Route+1] = WaypointFrom --- Create a route point of type air. - local CoordinateTo = Coordinate + + local CoordinateTo = Coordinate + local landheight = CoordinateTo:GetLandHeight() -- get target height + CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground + local WaypointTo = CoordinateTo:WaypointAir( "RADIO", POINT_VEC3.RoutePointType.TurningPoint, @@ -595,7 +616,10 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat Route[#Route+1] = WaypointFrom --- Create a route point of type air. - local CoordinateTo = Coordinate + local CoordinateTo = Coordinate + local landheight = CoordinateTo:GetLandHeight() -- get target height + CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground + local WaypointTo = CoordinateTo:WaypointAir( "RADIO", POINT_VEC3.RoutePointType.TurningPoint, diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 6051c25e4..3e43b3ef7 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -1078,11 +1078,14 @@ do -- CARGO_REPRESENTABLE local self = BASE:Inherit( self, CARGO:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE self:F( { Type, Name, LoadRadius, NearRadius } ) - local Desc = CargoObject:GetDesc() - self:I( { Desc = Desc } ) + -- Descriptors. + local Desc=CargoObject:GetDesc() + self:T({Desc=Desc}) + -- Weight. local Weight = math.random( 80, 120 ) + -- Adjust weight.. if Desc then if Desc.typeName == "2B11 mortar" then Weight = 210 @@ -1091,13 +1094,8 @@ do -- CARGO_REPRESENTABLE end end + -- Set weight. self:SetWeight( Weight ) - --- local Box = CargoUnit:GetBoundingBox() --- local VolumeUnit = ( Box.max.x - Box.min.x ) * ( Box.max.y - Box.min.y ) * ( Box.max.z - Box.min.z ) --- self:I( { VolumeUnit = VolumeUnit, WeightUnit = WeightUnit } ) - --self:SetVolume( VolumeUnit ) - return self end diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 2d356f270..4a55946e3 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -78,6 +78,9 @@ do -- CARGO_GROUP local GroupName = CargoGroup:GetName() self.CargoName = Name self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) ) + + -- Deactivate late activation. + self.CargoTemplate.lateActivation=false self.GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) self.GroupTemplate.name = self.CargoName .. "#CARGO" diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 88ba8c725..4d0093b47 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -46,14 +46,17 @@ do -- CARGO_UNIT -- @param #number NearRadius (optional) -- @return #CARGO_UNIT function CARGO_UNIT:New( CargoUnit, Type, Name, LoadRadius, NearRadius ) - local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, LoadRadius, NearRadius ) ) -- #CARGO_UNIT - self:I( { Type, Name, LoadRadius, NearRadius } ) - self:T( CargoUnit ) + -- Inherit CARGO_REPRESENTABLE. + local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, LoadRadius, NearRadius ) ) -- #CARGO_UNIT + + -- Debug info. + self:T({Type=Type, Name=Name, LoadRadius=LoadRadius, NearRadius=NearRadius}) + + -- Set cargo object. self.CargoObject = CargoUnit - self:T( self.ClassName ) - + -- Set event prio. self:SetEventPriority( 5 ) return self diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index fe186131a..977f77e45 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -676,6 +676,13 @@ do -- Event Handling -- @param #BASE self -- @param Core.Event#EVENTDATA EventData The EventData structure. + --- Occurs when a player enters a slot and takes control of an aircraft. + -- **NOTE**: This is a workaround of a long standing DCS bug with the PLAYER_ENTER_UNIT event. + -- initiator : The unit that is being taken control of. + -- @function [parent=#BASE] OnEventPlayerEnterAircraft + -- @param #BASE self + -- @param Core.Event#EVENTDATA EventData The EventData structure. + end @@ -781,31 +788,48 @@ function BASE:CreateEventTakeoff( EventTime, Initiator ) world.onEvent( Event ) end + --- Creation of a `S_EVENT_PLAYER_ENTER_AIRCRAFT` event. + -- @param #BASE self + -- @param Wrapper.Unit#UNIT PlayerUnit The aircraft unit the player entered. + function BASE:CreateEventPlayerEnterAircraft( PlayerUnit ) + self:F( { PlayerUnit } ) + + local Event = { + id = EVENTS.PlayerEnterAircraft, + time = timer.getTime(), + initiator = PlayerUnit:GetDCSObject() + } + + world.onEvent(Event) + end + -- TODO: Complete DCS#Event structure. --- The main event handling function... This function captures all events generated for the class. -- @param #BASE self -- @param DCS#Event event function BASE:onEvent(event) - --self:F( { BaseEventCodes[event.id], event } ) if self then - for EventID, EventObject in pairs( self.Events ) do + + for EventID, EventObject in pairs(self.Events) do if EventObject.EventEnabled then - --env.info( 'onEvent Table EventObject.Self = ' .. tostring(EventObject.Self) ) - --env.info( 'onEvent event.id = ' .. tostring(event.id) ) - --env.info( 'onEvent EventObject.Event = ' .. tostring(EventObject.Event) ) + if event.id == EventObject.Event then + if self == EventObject.Self then + if event.initiator and event.initiator:isExist() then event.IniUnitName = event.initiator:getName() end + if event.target and event.target:isExist() then event.TgtUnitName = event.target:getName() end - --self:T( { BaseEventCodes[event.id], event } ) - --EventObject.EventFunction( self, event ) + end + end + end end end diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 7babe2481..5997f05ca 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -24,7 +24,7 @@ -- === -- -- ### Author: **FlightControl** --- ### Contributions: +-- ### Contributions: **funkyfranky** -- -- === -- @@ -33,6 +33,9 @@ --- @type DATABASE +-- @field #string ClassName Name of the class. +-- @field #table Templates Templates: Units, Groups, Statics, ClientsByName, ClientsByID. +-- @field #table CLIENTS Clients. -- @extends Core.Base#BASE --- Contains collections of wrapper objects defined within MOOSE that reflect objects within the simulator. @@ -126,8 +129,6 @@ function DATABASE:New() self:HandleEvent( EVENTS.DeleteCargo ) self:HandleEvent( EVENTS.NewZone ) self:HandleEvent( EVENTS.DeleteZone ) - - -- Follow alive players and clients --self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) -- This is not working anymore!, handling this through the birth event. self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit ) @@ -140,37 +141,6 @@ function DATABASE:New() self.UNITS_Position = 0 - --- @param #DATABASE self - local function CheckPlayers( self ) - - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ), AlivePlayersNeutral = coalition.getPlayers( coalition.side.NEUTRAL )} - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - --self:E( { "CoalitionData:", CoalitionData } ) - for UnitId, UnitData in pairs( CoalitionData ) do - if UnitData and UnitData:isExist() then - - local UnitName = UnitData:getName() - local PlayerName = UnitData:getPlayerName() - local PlayerUnit = UNIT:Find( UnitData ) - --self:T( { "UnitData:", UnitData, UnitName, PlayerName, PlayerUnit } ) - - 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 ) - local Settings = SETTINGS:Set( PlayerName ) - Settings:SetPlayerMenu( PlayerUnit ) - end - end - end - end - end - end - - --self:E( "Scheduling" ) - --PlayerCheckSchedule = SCHEDULER:New( nil, CheckPlayers, { self }, 1, 1 ) - return self end @@ -187,14 +157,22 @@ end --- Adds a Unit based on the Unit Name in the DATABASE. -- @param #DATABASE self +-- @param #string DCSUnitName Unit name. +-- @return Wrapper.Unit#UNIT The added unit. function DATABASE:AddUnit( DCSUnitName ) - if not self.UNITS[DCSUnitName] then - self:T( { "Add UNIT:", DCSUnitName } ) - local UnitRegister = UNIT:Register( DCSUnitName ) - self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName ) + if not self.UNITS[DCSUnitName] then - table.insert( self.UNITS_Index, DCSUnitName ) + -- Debug info. + self:T( { "Add UNIT:", DCSUnitName } ) + + --local UnitRegister = UNIT:Register( DCSUnitName ) + + -- Register unit + self.UNITS[DCSUnitName]=UNIT:Register(DCSUnitName) + + -- This is not used anywhere in MOOSE as far as I can see so I remove it until there comes an error somewhere. + --table.insert(self.UNITS_Index, DCSUnitName ) end return self.UNITS[DCSUnitName] @@ -210,6 +188,8 @@ end --- Adds a Static based on the Static Name in the DATABASE. -- @param #DATABASE self +-- @param #string DCSStaticName Name of the static. +-- @return Wrapper.Static#STATIC The static object. function DATABASE:AddStatic( DCSStaticName ) if not self.STATICS[DCSStaticName] then @@ -224,8 +204,7 @@ end --- Deletes a Static from the DATABASE based on the Static Name. -- @param #DATABASE self function DATABASE:DeleteStatic( DCSStaticName ) - - --self.STATICS[DCSStaticName] = nil + self.STATICS[DCSStaticName] = nil end --- Finds a STATIC based on the StaticName. @@ -432,7 +411,6 @@ do -- cargo local Groups = UTILS.DeepCopy( self.GROUPS ) -- This is a very important statement. CARGO_GROUP:New creates a new _DATABASE.GROUP entry, which will confuse the loop. I searched 4 hours on this to find the bug! for CargoGroupName, CargoGroup in pairs( Groups ) do - self:I( { Cargo = CargoGroupName } ) if self:IsCargo( CargoGroupName ) then local CargoInfo = CargoGroupName:match("#CARGO(.*)") local CargoParam = CargoInfo and CargoInfo:match( "%((.*)%)") @@ -489,6 +467,8 @@ end --- Adds a CLIENT based on the ClientName in the DATABASE. -- @param #DATABASE self +-- @param #string ClientName Name of the Client unit. +-- @return Wrapper.Client#CLIENT The client object. function DATABASE:AddClient( ClientName ) if not self.CLIENTS[ClientName] then @@ -626,6 +606,9 @@ function DATABASE:Spawn( SpawnTemplate ) end --- Set a status to a Group within the Database, this to check crossing events for example. +-- @param #DATABASE self +-- @param #string GroupName Group name. +-- @param #string Status Status. function DATABASE:SetStatusGroup( GroupName, Status ) self:F2( Status ) @@ -633,8 +616,11 @@ function DATABASE:SetStatusGroup( GroupName, Status ) end --- Get a status to a Group within the Database, this to check crossing events for example. +-- @param #DATABASE self +-- @param #string GroupName Group name. +-- @return #string Status or an empty string "". function DATABASE:GetStatusGroup( GroupName ) - self:F2( Status ) + self:F2( GroupName ) if self.Templates.Groups[GroupName] then return self.Templates.Groups[GroupName].Status @@ -648,7 +634,8 @@ end -- @param #table GroupTemplate -- @param DCS#coalition.side CoalitionSide The coalition.side of the object. -- @param DCS#Object.Category CategoryID The Object.category of the object. --- @param DCS#country.id CountryID the country.id of the object +-- @param DCS#country.id CountryID the country ID of the object. +-- @param #string GroupName (Optional) The name of the group. Default is `GroupTemplate.name`. -- @return #DATABASE self function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID, GroupName ) @@ -704,6 +691,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName end + -- Debug info. self:T( { Group = self.Templates.Groups[GroupTemplateName].GroupName, Coalition = self.Templates.Groups[GroupTemplateName].CoalitionID, Category = self.Templates.Groups[GroupTemplateName].CategoryID, @@ -713,6 +701,10 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category ) end +--- Get group template. +-- @param #DATABASE self +-- @param #string GroupName Group name. +-- @return #table Group template table. function DATABASE:GetGroupTemplate( GroupName ) local GroupTemplate = self.Templates.Groups[GroupName].Template GroupTemplate.SpawnCoalitionID = self.Templates.Groups[GroupName].CoalitionID @@ -723,7 +715,10 @@ end --- Private method that registers new Static Templates within the DATABASE Object. -- @param #DATABASE self --- @param #table StaticTemplate +-- @param #table StaticTemplate Template table. +-- @param #number CoalitionID Coalition ID. +-- @param #number CategoryID Category ID. +-- @param #number CountryID Country ID. -- @return #DATABASE self function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID ) @@ -744,7 +739,8 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category self.Templates.Statics[StaticTemplateName].CoalitionID = CoalitionID self.Templates.Statics[StaticTemplateName].CountryID = CountryID - self:I( { Static = self.Templates.Statics[StaticTemplateName].StaticName, + -- Debug info. + self:T( { Static = self.Templates.Statics[StaticTemplateName].StaticName, Coalition = self.Templates.Statics[StaticTemplateName].CoalitionID, Category = self.Templates.Statics[StaticTemplateName].CategoryID, Country = self.Templates.Statics[StaticTemplateName].CountryID @@ -753,48 +749,101 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category self:AddStatic( StaticTemplateName ) + return self end - ---- @param #DATABASE self +--- Get static group template. +-- @param #DATABASE self +-- @param #string StaticName Name of the static. +-- @return #table Static template table. function DATABASE:GetStaticGroupTemplate( StaticName ) - local StaticTemplate = self.Templates.Statics[StaticName].GroupTemplate - return StaticTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID + if self.Templates.Statics[StaticName] then + local StaticTemplate = self.Templates.Statics[StaticName].GroupTemplate + return StaticTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID + else + self:E("ERROR: Static group template does NOT exist for static "..tostring(StaticName)) + return nil + end end ---- @param #DATABASE self +--- Get static unit template. +-- @param #DATABASE self +-- @param #string StaticName Name of the static. +-- @return #table Static template table. function DATABASE:GetStaticUnitTemplate( StaticName ) - local UnitTemplate = self.Templates.Statics[StaticName].UnitTemplate - return UnitTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID + if self.Templates.Statics[StaticName] then + local UnitTemplate = self.Templates.Statics[StaticName].UnitTemplate + return UnitTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID + else + self:E("ERROR: Static unit template does NOT exist for static "..tostring(StaticName)) + return nil + end end - +--- Get group name from unit name. +-- @param #DATABASE self +-- @param #string UnitName Name of the unit. +-- @return #string Group name. function DATABASE:GetGroupNameFromUnitName( UnitName ) - return self.Templates.Units[UnitName].GroupName + if self.Templates.Units[UnitName] then + return self.Templates.Units[UnitName].GroupName + else + self:E("ERROR: Unit template does not exist for unit "..tostring(UnitName)) + return nil + end end +--- Get group template from unit name. +-- @param #DATABASE self +-- @param #string UnitName Name of the unit. +-- @return #table Group template. function DATABASE:GetGroupTemplateFromUnitName( UnitName ) - return self.Templates.Units[UnitName].GroupTemplate + if self.Templates.Units[UnitName] then + return self.Templates.Units[UnitName].GroupTemplate + else + self:E("ERROR: Unit template does not exist for unit "..tostring(UnitName)) + return nil + end end +--- Get coalition ID from client name. +-- @param #DATABASE self +-- @param #string ClientName Name of the Client. +-- @return #number Coalition ID. function DATABASE:GetCoalitionFromClientTemplate( ClientName ) return self.Templates.ClientsByName[ClientName].CoalitionID end +--- Get category ID from client name. +-- @param #DATABASE self +-- @param #string ClientName Name of the Client. +-- @return #number Category ID. function DATABASE:GetCategoryFromClientTemplate( ClientName ) return self.Templates.ClientsByName[ClientName].CategoryID end +--- Get country ID from client name. +-- @param #DATABASE self +-- @param #string ClientName Name of the Client. +-- @return #number Country ID. function DATABASE:GetCountryFromClientTemplate( ClientName ) return self.Templates.ClientsByName[ClientName].CountryID end --- Airbase +--- Get coalition ID from airbase name. +-- @param #DATABASE self +-- @param #string AirbaseName Name of the airbase. +-- @return #number Coalition ID. function DATABASE:GetCoalitionFromAirbase( AirbaseName ) return self.AIRBASES[AirbaseName]:GetCoalition() end +--- Get category from airbase name. +-- @param #DATABASE self +-- @param #string AirbaseName Name of the airbase. +-- @return #number Category. function DATABASE:GetCategoryFromAirbase( AirbaseName ) return self.AIRBASES[AirbaseName]:GetCategory() end @@ -831,33 +880,38 @@ end function DATABASE:_RegisterGroupsAndUnits() local CoalitionsData = { GroupsRed = coalition.getGroups( coalition.side.RED ), GroupsBlue = coalition.getGroups( coalition.side.BLUE ), GroupsNeutral = coalition.getGroups( coalition.side.NEUTRAL ) } + for CoalitionId, CoalitionData in pairs( CoalitionsData ) do + for DCSGroupId, DCSGroup in pairs( CoalitionData ) do if DCSGroup:isExist() then + + -- Group name. local DCSGroupName = DCSGroup:getName() - self:I( { "Register Group:", DCSGroupName } ) + -- Add group. + self:I(string.format("Register Group: %s", tostring(DCSGroupName))) self:AddGroup( DCSGroupName ) + -- Loop over units in group. for DCSUnitId, DCSUnit in pairs( DCSGroup:getUnits() ) do + -- Get unit name. local DCSUnitName = DCSUnit:getName() - self:I( { "Register Unit:", DCSUnitName } ) + + -- Add unit. + self:I(string.format("Register Unit: %s", tostring(DCSUnitName))) self:AddUnit( DCSUnitName ) + end else - self:E( { "Group does not exist: ", DCSGroup } ) + self:E({"Group does not exist: ", DCSGroup}) end end end - self:T("Groups:") - for GroupName, Group in pairs( self.GROUPS ) do - self:T( { "Group:", GroupName } ) - end - return self end @@ -867,7 +921,7 @@ end function DATABASE:_RegisterClients() for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do - self:T( { "Register Client:", ClientName } ) + self:I(string.format("Register Client: %s", tostring(ClientName))) self:AddClient( ClientName ) end @@ -877,15 +931,15 @@ end --- @param #DATABASE self function DATABASE:_RegisterStatics() - local CoalitionsData = { GroupsRed = coalition.getStaticObjects( coalition.side.RED ), GroupsBlue = coalition.getStaticObjects( coalition.side.BLUE ) } - self:I( { Statics = CoalitionsData } ) + local CoalitionsData={GroupsRed=coalition.getStaticObjects(coalition.side.RED), GroupsBlue=coalition.getStaticObjects(coalition.side.BLUE), GroupsNeutral=coalition.getStaticObjects(coalition.side.NEUTRAL)} + for CoalitionId, CoalitionData in pairs( CoalitionsData ) do for DCSStaticId, DCSStatic in pairs( CoalitionData ) do if DCSStatic:isExist() then local DCSStaticName = DCSStatic:getName() - self:T( { "Register Static:", DCSStaticName } ) + self:I(string.format("Register Static: %s", tostring(DCSStaticName))) self:AddStatic( DCSStaticName ) else self:E( { "Static does not exist: ", DCSStatic } ) @@ -912,8 +966,23 @@ function DATABASE:_RegisterAirbases() -- Add and register airbase. local airbase=self:AddAirbase( DCSAirbaseName ) + -- Unique ID. + local airbaseUID=airbase:GetID(true) + -- Debug output. - self:I(string.format("Register Airbase: %s, getID=%d, GetID=%d (unique=%d)", DCSAirbaseName, DCSAirbase:getID(), airbase:GetID(), airbase:GetID(true))) + local text=string.format("Register %s: %s (ID=%d UID=%d), parking=%d [", AIRBASE.CategoryName[airbase.category], tostring(DCSAirbaseName), airbaseID, airbaseUID, airbase.NparkingTotal) + for _,terminalType in pairs(AIRBASE.TerminalType) do + if airbase.NparkingTerminal and airbase.NparkingTerminal[terminalType] then + text=text..string.format("%d=%d ", terminalType, airbase.NparkingTerminal[terminalType]) + end + end + text=text.."]" + self:I(text) + + -- Check for DCS bug IDs. + if airbaseID~=airbase:GetID() then + --self:E("WARNING: :getID does NOT match :GetID!") + end end @@ -930,36 +999,74 @@ function DATABASE:_EventOnBirth( Event ) self:F( { Event } ) if Event.IniDCSUnit then + if Event.IniObjectCategory == 3 then + self:AddStatic( Event.IniDCSUnitName ) + else + if Event.IniObjectCategory == 1 then + self:AddUnit( Event.IniDCSUnitName ) self:AddGroup( Event.IniDCSGroupName ) + -- Add airbase if it was spawned later in the mission. local DCSAirbase = Airbase.getByName(Event.IniDCSUnitName) if DCSAirbase then self:I(string.format("Adding airbase %s", tostring(Event.IniDCSUnitName))) self:AddAirbase(Event.IniDCSUnitName) end + end end + if Event.IniObjectCategory == 1 then + Event.IniUnit = self:FindUnit( Event.IniDCSUnitName ) Event.IniGroup = self:FindGroup( Event.IniDCSGroupName ) + + -- Client + local client=self.CLIENTS[Event.IniDCSUnitName] --Wrapper.Client#CLIENT + + if client then + -- TODO: create event ClientAlive + end + + -- Get player name. local PlayerName = Event.IniUnit:GetPlayerName() + if PlayerName then - self:I( { "Player Joined:", PlayerName } ) - self:AddClient( Event.IniDCSUnitName ) + + -- Debug info. + self:I(string.format("Player '%s' joint unit '%s' of group '%s'", tostring(PlayerName), tostring(Event.IniDCSUnitName), tostring(Event.IniDCSGroupName))) + + -- Add client in case it does not exist already. + if not client then + client=self:AddClient(Event.IniDCSUnitName) + end + + -- Add player. + client:AddPlayer(PlayerName) + + -- Add player. if not self.PLAYERS[PlayerName] then self:AddPlayer( Event.IniUnitName, PlayerName ) end + + -- Player settings. local Settings = SETTINGS:Set( PlayerName ) - Settings:SetPlayerMenu( Event.IniUnit ) - --MENU_INDEX:Refresh( Event.IniGroup ) + Settings:SetPlayerMenu(Event.IniUnit) + + -- Create an event. + self:CreateEventPlayerEnterAircraft(Event.IniUnit) + end + end + end + end @@ -967,22 +1074,53 @@ end -- @param #DATABASE self -- @param Core.Event#EVENTDATA Event function DATABASE:_EventOnDeadOrCrash( Event ) - self:F2( { Event } ) if Event.IniDCSUnit then + + local name=Event.IniDCSUnitName + if Event.IniObjectCategory == 3 then + + --- + -- STATICS + --- + if self.STATICS[Event.IniDCSUnitName] then self:DeleteStatic( Event.IniDCSUnitName ) end + else + if Event.IniObjectCategory == 1 then + + --- + -- UNITS + --- + + -- Delete unit. if self.UNITS[Event.IniDCSUnitName] then - self:DeleteUnit( Event.IniDCSUnitName ) + self:DeleteUnit(Event.IniDCSUnitName) end + + -- Remove client players. + local client=self.CLIENTS[name] --Wrapper.Client#CLIENT + + if client then + client:RemovePlayers() + end + end end + + -- Add airbase if it was spawned later in the mission. + local airbase=self.AIRBASES[Event.IniDCSUnitName] --Wrapper.Airbase#AIRBASE + if airbase and (airbase:IsHelipad() or airbase:IsShip()) then + self:DeleteAirbase(Event.IniDCSUnitName) + end + end + -- Account destroys. self:AccountDestroys( Event ) end @@ -995,15 +1133,31 @@ function DATABASE:_EventOnPlayerEnterUnit( Event ) if Event.IniDCSUnit then if Event.IniObjectCategory == 1 then + + -- Add unit. self:AddUnit( Event.IniDCSUnitName ) + + -- Ini unit. Event.IniUnit = self:FindUnit( Event.IniDCSUnitName ) + + -- Add group. self:AddGroup( Event.IniDCSGroupName ) + + -- Get player unit. local PlayerName = Event.IniDCSUnit:getPlayerName() - if not self.PLAYERS[PlayerName] then - self:AddPlayer( Event.IniDCSUnitName, PlayerName ) + + if PlayerName then + + if not self.PLAYERS[PlayerName] then + self:AddPlayer( Event.IniDCSUnitName, PlayerName ) + end + + local Settings = SETTINGS:Set( PlayerName ) + Settings:SetPlayerMenu( Event.IniUnit ) + + else + self:E("ERROR: getPlayerName() returned nil for event PlayerEnterUnit") end - local Settings = SETTINGS:Set( PlayerName ) - Settings:SetPlayerMenu( Event.IniUnit ) end end end @@ -1016,13 +1170,30 @@ function DATABASE:_EventOnPlayerLeaveUnit( Event ) self:F2( { Event } ) if Event.IniUnit then + if Event.IniObjectCategory == 1 then + + -- Try to get the player name. This can be buggy for multicrew aircraft! local PlayerName = Event.IniUnit:GetPlayerName() - if PlayerName and self.PLAYERS[PlayerName] then - self:I( { "Player Left:", PlayerName } ) + + if PlayerName then --and self.PLAYERS[PlayerName] then + + -- Debug info. + self:I(string.format("Player '%s' left unit %s", tostring(PlayerName), tostring(Event.IniUnitName))) + + -- Remove player menu. local Settings = SETTINGS:Set( PlayerName ) - Settings:RemovePlayerMenu( Event.IniUnit ) - self:DeletePlayer( Event.IniUnit, PlayerName ) + Settings:RemovePlayerMenu(Event.IniUnit) + + -- Delete player. + self:DeletePlayer(Event.IniUnit, PlayerName) + + -- Client stuff. + local client=self.CLIENTS[Event.IniDCSUnitName] --Wrapper.Client#CLIENT + if client then + client:RemovePlayer(PlayerName) + end + end end end @@ -1355,19 +1526,13 @@ function DATABASE:_RegisterTemplates() for group_num, Template in pairs(obj_type_data.group) do if obj_type_name ~= "static" and Template and Template.units and type(Template.units) == 'table' then --making sure again- this is a valid group - self:_RegisterGroupTemplate( - Template, - CoalitionSide, - _DATABASECategory[string.lower(CategoryName)], - CountryID - ) + + self:_RegisterGroupTemplate(Template, CoalitionSide, _DATABASECategory[string.lower(CategoryName)], CountryID) + else - self:_RegisterStaticTemplate( - Template, - CoalitionSide, - _DATABASECategory[string.lower(CategoryName)], - CountryID - ) + + self:_RegisterStaticTemplate(Template, CoalitionSide, _DATABASECategory[string.lower(CategoryName)], CountryID) + end --if GroupTemplate and GroupTemplate.units then end --for group_num, GroupTemplate in pairs(obj_type_data.group) do end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 1f0b99b0a..4588c6ba7 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -192,6 +192,7 @@ world.event.S_EVENT_DELETE_ZONE = world.event.S_EVENT_MAX + 1003 world.event.S_EVENT_NEW_ZONE_GOAL = world.event.S_EVENT_MAX + 1004 world.event.S_EVENT_DELETE_ZONE_GOAL = world.event.S_EVENT_MAX + 1005 world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006 +world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007 --- The different types of events supported by MOOSE. @@ -233,6 +234,7 @@ EVENTS = { NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL, DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL, RemoveUnit = world.event.S_EVENT_REMOVE_UNIT, + PlayerEnterAircraft = world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT, -- Added with DCS 2.5.6 DetailedFailure = world.event.S_EVENT_DETAILED_FAILURE or -1, --We set this to -1 for backward compatibility to DCS 2.5.5 and earlier Kill = world.event.S_EVENT_KILL or -1, @@ -489,6 +491,11 @@ local _EVENTMETA = { Event = "OnEventRemoveUnit", Text = "S_EVENT_REMOVE_UNIT" }, + [EVENTS.PlayerEnterAircraft] = { + Order = 1, + Event = "OnEventPlayerEnterAircraft", + Text = "S_EVENT_PLAYER_ENTER_AIRCRAFT" + }, -- Added with DCS 2.5.6 [EVENTS.DetailedFailure] = { Order = 1, @@ -639,7 +646,7 @@ end -- @param #function EventFunction The function to be called when the event occurs for the unit. -- @param EventClass The instance of the class for which the event is. -- @param #function OnEventFunction --- @return #EVENT +-- @return #EVENT self function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EventID ) self:F2( EventTemplate.name ) @@ -686,8 +693,9 @@ end -- @param #string GroupName The name of the GROUP. -- @param #function EventFunction The function to be called when the event occurs for the GROUP. -- @param Core.Base#BASE EventClass The self instance of the class for which the event is. --- @param EventID --- @return #EVENT +-- @param #number EventID Event ID. +-- @param ... Optional arguments passed to the event function. +-- @return #EVENT self function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID, ... ) local Event = self:Init( EventID, EventClass ) @@ -704,7 +712,7 @@ do -- OnBirth -- @param Wrapper.Group#GROUP EventGroup -- @param #function EventFunction The function to be called when the event occurs for the unit. -- @param EventClass The self instance of the class for which the event is. - -- @return #EVENT + -- @return #EVENT self function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) @@ -812,7 +820,7 @@ do -- Event Creation -- @param #EVENT self -- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created. function EVENT:CreateEventNewCargo( Cargo ) - self:I( { Cargo } ) + self:F( { Cargo } ) local Event = { id = EVENTS.NewCargo, @@ -914,6 +922,21 @@ do -- Event Creation world.onEvent( Event ) end + + --- Creation of a S_EVENT_PLAYER_ENTER_AIRCRAFT event. + -- @param #EVENT self + -- @param Wrapper.Unit#UNIT PlayerUnit The aircraft unit the player entered. + function EVENT:CreateEventPlayerEnterAircraft( PlayerUnit ) + self:F( { PlayerUnit } ) + + local Event = { + id = EVENTS.PlayerEnterAircraft, + time = timer.getTime(), + initiator = PlayerUnit:GetDCSObject() + } + + world.onEvent( Event ) + end end diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index df5c431df..b34ceb757 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -4705,7 +4705,8 @@ do -- SET_AIRBASE if _DATABASE then -- We use the BaseCaptured event, which is generated by DCS when a base got captured. - self:HandleEvent( EVENTS.BaseCaptured ) + self:HandleEvent(EVENTS.BaseCaptured) + self:HandleEvent(EVENTS.Dead) -- We initialize the first set. for ObjectName, Object in pairs( self.Database ) do @@ -4720,10 +4721,9 @@ do -- SET_AIRBASE return self end - --- Starts the filtering. + --- Base capturing event. -- @param #SET_AIRBASE self -- @param Core.Event#EVENT EventData - -- @return #SET_AIRBASE self function SET_AIRBASE:OnEventBaseCaptured(EventData) -- When a base got captured, we reevaluate the set. @@ -4739,24 +4739,36 @@ do -- SET_AIRBASE end + --- Dead event. + -- @param #SET_AIRBASE self + -- @param Core.Event#EVENT EventData + function SET_AIRBASE:OnEventDead(EventData) + + local airbaseName, airbase=self:FindInDatabase(EventData) + + if airbase and airbase:IsShip() or airbase:IsHelipad() then + self:RemoveAirbasesByName(airbaseName) + end + + 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_AIRBASE self - -- @param Core.Event#EVENTDATA Event - -- @return #string The name of the AIRBASE - -- @return #table The AIRBASE + -- @param Core.Event#EVENTDATA Event Event data. + -- @return #string The name of the AIRBASE. + -- @return Wrapper.Airbase#AIRBASE The AIRBASE object. function SET_AIRBASE:AddInDatabase( Event ) - 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_AIRBASE self - -- @param Core.Event#EVENTDATA Event - -- @return #string The name of the AIRBASE - -- @return #table The AIRBASE + -- @param Core.Event#EVENTDATA Event Event data. + -- @return #string The name of the AIRBASE. + -- @return Wrapper.Airbase#AIRBASE The AIRBASE object. function SET_AIRBASE:FindInDatabase( Event ) self:F3( { Event } ) diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index f4ed1e8fb..8b4e91669 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -836,25 +836,27 @@ function AIRBASE:_InitParkingSpots() -- Put coordinates of parking spots into table. for _,spot in pairs(parkingdata) do - -- New parking spot. - local park={} --#AIRBASE.ParkingSpot - park.Vec3=spot.vTerminalPos - park.Coordinate=COORDINATE:NewFromVec3(spot.vTerminalPos) - park.DistToRwy=spot.fDistToRW - park.Free=nil - park.TerminalID=spot.Term_Index - park.TerminalID0=spot.Term_Index_0 - park.TerminalType=spot.Term_Type - park.TOAC=spot.TO_AC + -- New parking spot. + local park={} --#AIRBASE.ParkingSpot + park.Vec3=spot.vTerminalPos + park.Coordinate=COORDINATE:NewFromVec3(spot.vTerminalPos) + park.DistToRwy=spot.fDistToRW + park.Free=nil + park.TerminalID=spot.Term_Index + park.TerminalID0=spot.Term_Index_0 + park.TerminalType=spot.Term_Type + park.TOAC=spot.TO_AC - for _,terminalType in pairs(AIRBASE.TerminalType) do - if self._CheckTerminalType(terminalType, park.TerminalType) then - self.NparkingTerminal[terminalType]=self.NparkingTerminal[terminalType]+1 - end + self.NparkingTotal=self.NparkingTotal+1 + + for _,terminalType in pairs(AIRBASE.TerminalType) do + if self._CheckTerminalType(terminalType, park.TerminalType) then + self.NparkingTerminal[terminalType]=self.NparkingTerminal[terminalType]+1 end + end - self.parkingByID[park.TerminalID]=park - table.insert(self.parking, park) + self.parkingByID[park.TerminalID]=park + table.insert(self.parking, park) end return self @@ -897,20 +899,20 @@ function AIRBASE:GetParkingSpotsTable(termtype) if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then local spot=self:_GetParkingSpotByID(_spot.Term_Index) - + if spot then spot.Free=_isfree(_spot) -- updated spot.TOAC=_spot.TO_AC -- updated - + table.insert(spots, spot) - + else - + self:E(string.format("ERROR: Parking spot %s is nil!", tostring(_spot.Term_Index))) - + end - + end end diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 4e6cce0de..445c655ee 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -3,8 +3,7 @@ -- === -- -- ### Author: **FlightControl** --- --- ### Contributions: +-- ### Contributions: **funkyfranky** -- -- === -- @@ -52,13 +51,6 @@ -- -- @field #CLIENT CLIENT = { - ONBOARDSIDE = { - NONE = 0, - LEFT = 1, - RIGHT = 2, - BACK = 3, - FRONT = 4 - }, ClassName = "CLIENT", ClientName = nil, ClientAlive = false, @@ -66,27 +58,20 @@ CLIENT = { ClientBriefingShown = false, _Menus = {}, _Tasks = {}, - Messages = { - } + Messages = {}, + Players = {}, } --- Finds a CLIENT from the _DATABASE using the relevant DCS Unit. -- @param #CLIENT self --- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor. --- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client. --- @return #CLIENT --- @usage --- -- Create new Clients. --- local Mission = MISSIONSCHEDULER.AddMission( 'Russia Transport Troops SA-6', 'Operational', 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 'Russia' ) --- Mission:AddGoal( DeploySA6TroopsGoal ) --- --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() ) -function CLIENT:Find( DCSUnit, Error ) +-- @param DCS#Unit DCSUnit The DCS unit of the client. +-- @param #boolean Error Throw an error message. +-- @return #CLIENT The CLIENT found in the _DATABASE. +function CLIENT:Find(DCSUnit, Error) + local ClientName = DCSUnit:getName() + local ClientFound = _DATABASE:FindClient( ClientName ) if ClientFound then @@ -117,11 +102,15 @@ end -- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() ) -- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() ) function CLIENT:FindByName( ClientName, ClientBriefing, Error ) + + -- Client local ClientFound = _DATABASE:FindClient( ClientName ) if ClientFound then ClientFound:F( { ClientName, ClientBriefing } ) - ClientFound:AddBriefing( ClientBriefing ) + + ClientFound:AddBriefing(ClientBriefing) + ClientFound.MessageSwitch = true return ClientFound @@ -132,51 +121,105 @@ function CLIENT:FindByName( ClientName, ClientBriefing, Error ) end end -function CLIENT:Register( ClientName ) +--- Transport defines that the Client is a Transport. Transports show cargo. +-- @param #CLIENT self +-- @param #string ClientName Name of the client unit. +-- @return #CLIENT self +function CLIENT:Register(ClientName) - local self = BASE:Inherit( self, UNIT:Register( ClientName ) ) -- #CLIENT - - self:F( ClientName ) - self.ClientName = ClientName - self.MessageSwitch = true - self.ClientAlive2 = false + -- Inherit unit. + local self = BASE:Inherit( self, UNIT:Register(ClientName )) -- #CLIENT - --self.AliveCheckScheduler = routines.scheduleFunction( self._AliveCheckScheduler, { self }, timer.getTime() + 1, 5 ) - self.AliveCheckScheduler = SCHEDULER:New( self, self._AliveCheckScheduler, { "Client Alive " .. ClientName }, 1, 5, 0.5 ) - self.AliveCheckScheduler:NoTrace() + -- Set client name. + self.ClientName = ClientName + + -- Message switch. + self.MessageSwitch = true + + -- Alive2. + self.ClientAlive2 = false - self:F( self ) return self end --- Transport defines that the Client is a Transport. Transports show cargo. -- @param #CLIENT self --- @return #CLIENT +-- @return #CLIENT self function CLIENT:Transport() - self:F() - self.ClientTransport = true return self end ---- AddBriefing adds a briefing to a CLIENT when a player joins a mission. +--- Adds a briefing to a CLIENT when a player joins a mission. -- @param #CLIENT self -- @param #string ClientBriefing is the text defining the Mission briefing. -- @return #CLIENT self function CLIENT:AddBriefing( ClientBriefing ) - self:F( ClientBriefing ) + self.ClientBriefing = ClientBriefing self.ClientBriefingShown = false return self end +--- Add player name. +-- @param #CLIENT self +-- @param #string PlayerName Name of the player. +-- @return #CLIENT self +function CLIENT:AddPlayer(PlayerName) + + table.insert(self.Players, PlayerName) + + return self +end + +--- Get player name(s). +-- @param #CLIENT self +-- @return #table List of player names or an empty table `{}`. +function CLIENT:GetPlayers() + return self.Players +end + +--- Get name of player. +-- @param #CLIENT self +-- @return #string Player name or `nil`. +function CLIENT:GetPlayer() + if #self.Players>0 then + return self.Players[1] + end + return nil +end + +--- Remove player. +-- @param #CLIENT self +-- @param #string PlayerName Name of the player. +-- @return #CLIENT self +function CLIENT:RemovePlayer(PlayerName) + + for i,playername in pairs(self.Players) do + if PlayerName==playername then + table.remove(self.Players, i) + break + end + end + + return self +end + +--- Remove all players. +-- @param #CLIENT self +-- @return #CLIENT self +function CLIENT:RemovePlayers() + self.Players={} + return self +end + + --- Show the briefing of a CLIENT. -- @param #CLIENT self -- @return #CLIENT self function CLIENT:ShowBriefing() - self:F( { self.ClientName, self.ClientBriefingShown } ) if not self.ClientBriefingShown then self.ClientBriefingShown = true @@ -204,8 +247,6 @@ function CLIENT:ShowMissionBriefing( MissionBriefing ) return self end - - --- Resets a CLIENT. -- @param #CLIENT self -- @param #string ClientName Name of the Group as defined within the Mission Editor. The Group must have a Unit with the type Client. @@ -241,6 +282,7 @@ end --- Checks for a client alive event and calls a function on a continuous basis. -- @param #CLIENT self -- @param #function CallBackFunction Create a function that will be called when a player joins the slot. +-- @param ... (Optional) Arguments for callback function as comma separated list. -- @return #CLIENT function CLIENT:Alive( CallBackFunction, ... ) self:F() @@ -248,6 +290,9 @@ function CLIENT:Alive( CallBackFunction, ... ) self.ClientCallBack = CallBackFunction self.ClientParameters = arg + self.AliveCheckScheduler = SCHEDULER:New( self, self._AliveCheckScheduler, { "Client Alive " .. self.ClientName }, 0.1, 5, 0.5 ) + self.AliveCheckScheduler:NoTrace() + return self end @@ -255,19 +300,29 @@ end function CLIENT:_AliveCheckScheduler( SchedulerName ) self:F3( { SchedulerName, self.ClientName, self.ClientAlive2, self.ClientBriefingShown, self.ClientCallBack } ) - if self:IsAlive() then + if self:IsAlive() then + if self.ClientAlive2 == false then + + -- Show briefing. self:ShowBriefing() + + -- Callback function. if self.ClientCallBack then self:T("Calling Callback function") self.ClientCallBack( self, unpack( self.ClientParameters ) ) end + + -- Alive. self.ClientAlive2 = true end + else + if self.ClientAlive2 == true then self.ClientAlive2 = false end + end return true @@ -291,6 +346,7 @@ function CLIENT:GetDCSGroup() local ClientUnit = Unit.getByName( self.ClientName ) local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) } + for CoalitionId, CoalitionData in pairs( CoalitionsData ) do self:T3( { "CoalitionData:", CoalitionData } ) for UnitId, UnitData in pairs( CoalitionData ) do @@ -299,10 +355,14 @@ function CLIENT:GetDCSGroup() --self:F(self.ClientName) if ClientUnit then + local ClientGroup = ClientUnit:getGroup() + if ClientGroup then self:T3( "ClientGroup = " .. self.ClientName ) - if ClientGroup:isExist() and UnitData:getGroup():isExist() then + + if ClientGroup:isExist() and UnitData:getGroup():isExist() then + if ClientGroup:getID() == UnitData:getGroup():getID() then self:T3( "Normal logic" ) self:T3( self.ClientName .. " : group found!" ) @@ -310,15 +370,22 @@ function CLIENT:GetDCSGroup() self.ClientGroupName = ClientGroup:getName() return ClientGroup end + else + -- Now we need to resolve the bugs in DCS 1.5 ... -- Consult the database for the units of the Client Group. (ClientGroup:getUnits() returns nil) self:T3( "Bug 1.5 logic" ) + local ClientGroupTemplate = _DATABASE.Templates.Units[self.ClientName].GroupTemplate + self.ClientGroupID = ClientGroupTemplate.groupId + self.ClientGroupName = _DATABASE.Templates.Units[self.ClientName].GroupName + self:T3( self.ClientName .. " : group found in bug 1.5 resolvement logic!" ) return ClientGroup + end -- else -- error( "Client " .. self.ClientName .. " not found!" ) @@ -343,22 +410,22 @@ function CLIENT:GetDCSGroup() end end + -- Nothing could be found :( self.ClientGroupID = nil - self.ClientGroupUnit = nil + self.ClientGroupName = nil return nil end --- TODO: Check DCS#Group.ID --- Get the group ID of the client. -- @param #CLIENT self --- @return DCS#Group.ID +-- @return #number DCS#Group ID. function CLIENT:GetClientGroupID() - local ClientGroup = self:GetDCSGroup() + -- This updates the ID. + self:GetDCSGroup() - --self:F( self.ClientGroupID ) -- Determined in GetDCSGroup() return self.ClientGroupID end @@ -368,26 +435,28 @@ end -- @return #string function CLIENT:GetClientGroupName() - local ClientGroup = self:GetDCSGroup() - - self:T( self.ClientGroupName ) -- Determined in GetDCSGroup() + -- This updates the group name. + self:GetDCSGroup() + return self.ClientGroupName end --- Returns the UNIT of the CLIENT. -- @param #CLIENT self --- @return Wrapper.Unit#UNIT +-- @return Wrapper.Unit#UNIT The client UNIT or `nil`. function CLIENT:GetClientGroupUnit() self:F2() local ClientDCSUnit = Unit.getByName( self.ClientName ) self:T( self.ClientDCSUnit ) + if ClientDCSUnit and ClientDCSUnit:isExist() then - local ClientUnit = _DATABASE:FindUnit( self.ClientName ) - self:T2( ClientUnit ) + local ClientUnit=_DATABASE:FindUnit( self.ClientName ) return ClientUnit end + + return nil end --- Returns the DCSUnit of the CLIENT. diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 64b656b52..705e29303 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -13,7 +13,7 @@ -- -- ### Author: **FlightControl** -- --- ### Contributions: +-- ### Contributions: **funkyfranky** -- -- === -- @@ -22,6 +22,8 @@ --- @type UNIT +-- @field #string ClassName Name of the class. +-- @field #string UnitName Name of the unit. -- @extends Wrapper.Controllable#CONTROLLABLE --- For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _@{DATABASE} object. @@ -87,6 +89,7 @@ -- @field #UNIT UNIT UNIT = { ClassName="UNIT", + UnitName=nil, } @@ -103,12 +106,18 @@ UNIT = { --- Create a new UNIT from DCSUnit. -- @param #UNIT self -- @param #string UnitName The name of the DCS unit. --- @return #UNIT +-- @return #UNIT self function UNIT:Register( UnitName ) + + -- Inherit CONTROLLABLE. local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) ) + + -- Set unit name. self.UnitName = UnitName + -- Set event prio. self:SetEventPriority( 3 ) + return self end @@ -373,6 +382,32 @@ function UNIT:GetPlayerName() end +--- Checks is the unit is a *Player* or *Client* slot. +-- @param #UNIT self +-- @return #boolean If true, unit is a player or client aircraft +function UNIT:IsClient() + + if _DATABASE.CLIENTS[self.UnitName] then + return true + end + + return false +end + +--- Get the CLIENT of the unit +-- @param #UNIT self +-- @return Wrapper.Client#CLIENT +function UNIT:GetClient() + + local client=_DATABASE.CLIENTS[self.UnitName] + + if client then + return client + end + + return nil +end + --- Returns the unit's number in the group. -- The number is the same number the unit has in ME. -- It may not be changed during the mission.