From c5236d337e77c05370bd59dbfe50a85cc5ee23ca Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 28 May 2019 20:44:51 +0300 Subject: [PATCH] Updates --- Moose Development/Moose/AI/AI_Escort.lua | 142 ++-- .../Moose/AI/AI_Escort_Request.lua | 25 +- Moose Development/Moose/AI/AI_Formation.lua | 5 +- Moose Development/Moose/Core/Spawn.lua | 651 +++++++++--------- Moose Development/Moose/Wrapper/Airbase.lua | 7 +- 5 files changed, 451 insertions(+), 379 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Escort.lua b/Moose Development/Moose/AI/AI_Escort.lua index 0cf5b5800..b8e169a5e 100644 --- a/Moose Development/Moose/AI/AI_Escort.lua +++ b/Moose Development/Moose/AI/AI_Escort.lua @@ -225,6 +225,7 @@ function AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) self.EscortBriefing = EscortBriefing + self.Menu = {} -- if not EscortBriefing then -- EscortGroup:MessageToClient( EscortGroup:GetCategoryName() .. " '" .. EscortName .. "' (" .. EscortGroup:GetCallsign() .. ") reporting! " .. @@ -545,26 +546,36 @@ function AI_ESCORT:MenuFormationBox( XStart, XSpace, YStart, YSpace, ZStart, ZSp end +--- Sets a menu slot to join formation for an escort. +-- @param #AI_ESCORT self +-- @return #AI_ESCORT +function AI_ESCORT:EscortMenuJoinUp( EscortGroup ) + + if self.Menu.JoinUp == true then + if EscortGroup:IsAir() then + local EscortGroupName = EscortGroup:GetName() + local EscortMenu = MENU_GROUP:New( self.PlayerGroup, EscortGroupName, self.MainMenu ) + local EscortMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", EscortMenu ) + local EscortMenuJoinUp = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Join Up", EscortMenuReportNavigation, AI_ESCORT._JoinUp, self, EscortGroup ) + end + end +end + + --- Defines --- Defines a menu slot to let the escort to join formation. --- This menu will appear under **Formation**. -- @param #AI_ESCORT self -- @return #AI_ESCORT function AI_ESCORT:MenuJoinUp() + self.Menu.JoinUp = true + local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu ) local FlightMenuJoinUp = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Join Up", FlightMenuReportNavigation, AI_ESCORT._FlightJoinUp, self ) self.EscortGroupSet:ForSomeGroupAlive( --- @param Core.Group#GROUP EscortGroup function( EscortGroup ) - if EscortGroup:IsAir() then - - local EscortGroupName = EscortGroup:GetName() - local EscortMenu = MENU_GROUP:New( self.PlayerGroup, EscortGroupName, self.MainMenu ) - local EscortMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", EscortMenu ) - local EscortMenuJoinUp = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Join Up", EscortMenuReportNavigation, AI_ESCORT._JoinUp, self ) - - end + self:EscortSetMenuJoinUp( EscortGroup ) end ) @@ -572,6 +583,32 @@ function AI_ESCORT:MenuJoinUp() end +function AI_ESCORT:EscortMenuHoldAtEscortPosition( EscortGroup ) + + for _, HoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition ) do + if EscortGroup:IsAir() then + local EscortGroupName = EscortGroup:GetName() + local EscortMenu = MENU_GROUP:New( self.PlayerGroup, EscortGroupName, self.MainMenu ) + local EscortMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", EscortMenu ) + local EscortMenuHoldPosition = MENU_GROUP_COMMAND + :New( + self.PlayerGroup, + HoldAtEscortPosition.MenuText, + EscortMenuReportNavigation, + AI_ESCORT._HoldPosition, + self, + EscortGroup, + EscortGroup, + HoldAtEscortPosition.Height, + HoldAtEscortPosition.Speed + ) + end + end + + return self +end + + --- Defines a menu slot to let the escort hold at their current position and stay low with a specified height during a specified time in seconds. -- This menu will appear under **Hold position**. -- @param #AI_ESCORT self @@ -619,28 +656,16 @@ function AI_ESCORT:MenuHoldAtEscortPosition( Height, Speed, MenuTextFormat ) Speed ) + self.Menu.HoldAtEscortPosition = self.Menu.HoldAtEscortPosition or {} + self.Menu.HoldAtEscortPosition[#self.Menu.HoldAtEscortPosition+1] = {} + self.Menu.HoldAtEscortPosition[#self.Menu.HoldAtEscortPosition].Height = Height + self.Menu.HoldAtEscortPosition[#self.Menu.HoldAtEscortPosition].Speed = Speed + self.Menu.HoldAtEscortPosition[#self.Menu.HoldAtEscortPosition].MenuText = MenuText + self.EscortGroupSet:ForSomeGroupAlive( --- @param Core.Group#GROUP EscortGroup function( EscortGroup ) - if EscortGroup:IsAir() then - - local EscortGroupName = EscortGroup:GetName() - local EscortMenu = MENU_GROUP:New( self.PlayerGroup, EscortGroupName, self.MainMenu ) - local EscortMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", EscortMenu ) - - local EscortMenuHoldPosition = MENU_GROUP_COMMAND - :New( - self.PlayerGroup, - MenuText, - EscortMenuReportNavigation, - AI_ESCORT._HoldPosition, - self, - EscortGroup, - EscortGroup, - Height, - Speed - ) - end + self:EscortMenuHoldAtEscortPosition( EscortGroup ) end ) @@ -648,6 +673,33 @@ function AI_ESCORT:MenuHoldAtEscortPosition( Height, Speed, MenuTextFormat ) end +function AI_ESCORT:EscortMenuHoldAtLeaderPosition( EscortGroup ) + + for _, HoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition ) do + if EscortGroup:IsAir() then + + local EscortGroupName = EscortGroup:GetName() + local EscortMenu = MENU_GROUP:New( self.PlayerGroup, EscortGroupName, self.MainMenu ) + local EscortMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", EscortMenu ) + + local EscortMenuHoldAtLeaderPosition = MENU_GROUP_COMMAND + :New( + self.PlayerGroup, + HoldAtLeaderPosition.MenuText, + EscortMenuReportNavigation, + AI_ESCORT._HoldPosition, + self, + self.PlayerGroup, + EscortGroup, + HoldAtLeaderPosition.Height, + HoldAtLeaderPosition.Speed + ) + end + end + + return self +end + --- Defines a menu slot to let the escort hold at the client position and stay low with a specified height during a specified time in seconds. -- This menu will appear under **Navigation**. -- @param #AI_ESCORT self @@ -695,28 +747,16 @@ function AI_ESCORT:MenuHoldAtLeaderPosition( Height, Speed, MenuTextFormat ) Speed ) + self.Menu.HoldAtLeaderPosition = self.Menu.HoldAtLeaderPosition or {} + self.Menu.HoldAtLeaderPosition[#self.Menu.HoldAtLeaderPosition+1] = {} + self.Menu.HoldAtLeaderPosition[#self.Menu.HoldAtLeaderPosition].Height = Height + self.Menu.HoldAtLeaderPosition[#self.Menu.HoldAtLeaderPosition].Speed = Speed + self.Menu.HoldAtLeaderPosition[#self.Menu.HoldAtLeaderPosition].MenuText = MenuText + self.EscortGroupSet:ForSomeGroupAlive( --- @param Core.Group#GROUP EscortGroup function( EscortGroup ) - if EscortGroup:IsAir() then - - local EscortGroupName = EscortGroup:GetName() - local EscortMenu = MENU_GROUP:New( self.PlayerGroup, EscortGroupName, self.MainMenu ) - local EscortMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", EscortMenu ) - - local EscortMenuHoldAtLeaderPosition = MENU_GROUP_COMMAND - :New( - self.PlayerGroup, - MenuText, - EscortMenuReportNavigation, - AI_ESCORT._HoldPosition, - self, - self.PlayerGroup, - EscortGroup, - Height, - Speed - ) - end + self:EscortMenuHoldAtLeaderPosition( EscortGroup ) end ) @@ -1140,7 +1180,7 @@ function AI_ESCORT:_JoinUp( EscortGroup ) end -function AI_ESCORT:_FlightJoinUp( EscortGroup ) +function AI_ESCORT:_FlightJoinUp() self.EscortGroupSet:ForSomeGroupAlive( --- @param Core.Group#GROUP EscortGroup @@ -1339,7 +1379,7 @@ function AI_ESCORT.___Resume( EscortGroup, self ) local PlayerGroup = self.PlayerGroup self:JoinFormation( EscortGroup ) - EscortGroup:MessageTypeToClient( "Destroyed all targets. Rejoining.", MESSAGE.Type.Information, PlayerGroup ) + EscortGroup:MessageTypeToGroup( "Destroyed all targets. Rejoining.", MESSAGE.Type.Information, PlayerGroup ) end @@ -1347,7 +1387,7 @@ end --- @param #AI_ESCORT self -- @param Wrapper.Group#GROUP EscortGroup -- @param #number WayPoint -function AI_ESCORT._ResumeMission( EscortGroup, WayPoint ) +function AI_ESCORT:_ResumeMission( EscortGroup, WayPoint ) --self.FollowScheduler:Stop( self.FollowSchedule ) @@ -1360,7 +1400,7 @@ function AI_ESCORT._ResumeMission( EscortGroup, WayPoint ) EscortGroup:SetTask( EscortGroup:TaskRoute( WayPoints ), 1 ) - EscortGroup:MessageTypeToClient( "Resuming mission from waypoint ", MESSAGE.Type.Information, self.PlayerGroup ) + EscortGroup:MessageTypeToGroup( "Resuming mission from waypoint ", MESSAGE.Type.Information, self.PlayerGroup ) end diff --git a/Moose Development/Moose/AI/AI_Escort_Request.lua b/Moose Development/Moose/AI/AI_Escort_Request.lua index e7d2b6d78..4b95b4b90 100644 --- a/Moose Development/Moose/AI/AI_Escort_Request.lua +++ b/Moose Development/Moose/AI/AI_Escort_Request.lua @@ -193,7 +193,7 @@ AI_ESCORT_REQUEST = { -- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where escorts will be spawned once requested. -- @param #string EscortName Name of the escort. -- @param #string EscortBriefing A text showing the AI_ESCORT_REQUEST briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown. --- @return #AI_ESCORT_REQUEST self +-- @return #AI_ESCORT_REQUEST -- @usage -- EscortSpawn = SPAWN:NewWithAlias( "Red A2G Escort Template", "Red A2G Escort AI" ):InitLimit( 10, 10 ) -- EscortSpawn:ParkAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Sochi_Adler ), AIRBASE.TerminalType.OpenBig ) @@ -223,10 +223,9 @@ function AI_ESCORT_REQUEST:New( EscortUnit, EscortSpawn, EscortAirbase, EscortNa end --- @param #AI_ESCORT_REQUEST self --- @param Core.Set#SET_GROUP EscortGroupSet function AI_ESCORT_REQUEST:SpawnEscort() - local EscortGroup = self.EscortSpawn:SpawnAtAirbase( self.EscortAirbase, SPAWN.Takeoff.Cold ) + local EscortGroup = self.EscortSpawn:SpawnAtAirbase( self.EscortAirbase, SPAWN.Takeoff.Hot ) self.EscortGroupSet:AddGroup( EscortGroup ) EscortGroup:OptionROTVertical() @@ -241,11 +240,27 @@ function AI_ESCORT_REQUEST:SpawnEscort() Report:Add( "Joining Up " .. self.EscortGroupSet:GetUnitTypeNames():Text( ", " ) .. " from " .. LeaderEscort:GetCoordinate():ToString( self.EscortUnit ) ) LeaderEscort:MessageTypeToGroup( Report:Text(), MESSAGE.Type.Information, self.PlayerUnit ) + self:FormationTrail( 50, 50, 50 ) if self.SpawnMode == self.__Enum.Mode.Formation then - self:FormationTrail( 50, 50, 50 ) self:JoinFormation( EscortGroup ) end - self:Menus( self.XStart, self.XSpace, self.YStart, self.YSpace, self.ZStart, self.ZSpace, self.ZLevels ) + + --self:Menus( self.XStart, self.XSpace, self.YStart, self.YSpace, self.ZStart, self.ZSpace, self.ZLevels ) + + self:EscortMenuJoinUp( EscortGroup ) + + self:EscortMenuHoldAtEscortPosition( EscortGroup ) + self:EscortMenuHoldAtLeaderPosition( EscortGroup ) + + self:MenuFlare() + self:MenuSmoke() + + self:MenuReportTargets( 60 ) + self:MenuAssistedAttack() + self:MenuROE() + self:MenuROT() + + self:MenuResumeMission() end ) diff --git a/Moose Development/Moose/AI/AI_Formation.lua b/Moose Development/Moose/AI/AI_Formation.lua index 5eb5d0e90..f4ad1902f 100644 --- a/Moose Development/Moose/AI/AI_Formation.lua +++ b/Moose Development/Moose/AI/AI_Formation.lua @@ -717,7 +717,6 @@ function AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, X i = i + 1 FollowGroup:SetState( FollowGroup, "Formation", Formation ) - FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation ) end return self @@ -873,7 +872,6 @@ function AI_FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event , FollowGroup:SetState( self, "FormationVec3", Vec3 ) i = i + 1 FollowGroup:SetState( FollowGroup, "Formation", self.__Enum.Formation.Vic ) - FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation ) end return self @@ -934,7 +932,6 @@ function AI_FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XS FollowGroup:SetState( self, "FormationVec3", Vec3 ) i = i + 1 FollowGroup:SetState( FollowGroup, "Formation", self.__Enum.Formation.Box ) - FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation ) end return self @@ -1026,7 +1023,7 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1 ClientUnit:SetState( self, "CV1", CV2 ) end - FollowGroupSet:ForEachGroup( + FollowGroupSet:ForEachGroupAlive( --- @param Wrapper.Group#GROUP FollowGroup -- @param Wrapper.Unit#UNIT ClientUnit function( FollowGroup, Formation, ClientUnit, CT1, CV1, CT2, CV2 ) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index d042f49e6..3495907ad 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1835,6 +1835,336 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT end +--- Will park a group at an @{Wrapper.Airbase}. +-- +-- @param #SPAWN self +-- @param Wrapper.Airbase#AIRBASE SpawnAirbase The @{Wrapper.Airbase} where to spawn the group. +-- @param Wrapper.Airbase#AIRBASE.TerminalType TerminalType (optional) The terminal type the aircraft should be spawned at. See @{Wrapper.Airbase#AIRBASE.TerminalType}. +-- @param #table Parkingdata (optional) Table holding the coordinates and terminal ids for all units of the group. Spawning will be forced to happen at exactily these spots! +-- @return #nil Nothing is returned! +function SPAWN:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex ) + + self:F( { SpawnIndex = SpawnIndex, SpawnMaxGroups = self.SpawnMaxGroups } ) + + -- Get position of airbase. + local PointVec3 = SpawnAirbase:GetCoordinate() + self:T2(PointVec3) + + -- Set take off type. Default is hot. + local Takeoff = SPAWN.Takeoff.Cold + + -- Get group template. + local SpawnTemplate = self.SpawnGroups[SpawnIndex].SpawnTemplate + + if SpawnTemplate then + + -- Check if the aircraft with the specified SpawnIndex is already spawned. + -- If yes, ensure that the aircraft is spawned at the same aircraft spot. + + local GroupAlive = self:GetGroupFromIndex( SpawnIndex ) + + -- Debug output + self:T( { "Current point of ", self.SpawnTemplatePrefix, SpawnAirbase } ) + + -- Template group, unit and its attributes. + local TemplateGroup = GROUP:FindByName(self.SpawnTemplatePrefix) + local TemplateUnit=TemplateGroup:GetUnit(1) + local ishelo=TemplateUnit:HasAttribute("Helicopters") + local isbomber=TemplateUnit:HasAttribute("Bombers") + local istransport=TemplateUnit:HasAttribute("Transports") + local isfighter=TemplateUnit:HasAttribute("Battleplanes") + + -- Number of units in the group. With grouping this can actually differ from the template group size! + local nunits=#SpawnTemplate.units + + -- First waypoint of the group. + local SpawnPoint = SpawnTemplate.route.points[1] + + -- These are only for ships and FARPS. + SpawnPoint.linkUnit = nil + SpawnPoint.helipadId = nil + SpawnPoint.airdromeId = nil + + -- Get airbase ID and category. + local AirbaseID = SpawnAirbase:GetID() + local AirbaseCategory = SpawnAirbase:GetDesc().category + self:F( { AirbaseCategory = AirbaseCategory } ) + + -- Set airdromeId. + if AirbaseCategory == Airbase.Category.SHIP then + SpawnPoint.linkUnit = AirbaseID + SpawnPoint.helipadId = AirbaseID + elseif AirbaseCategory == Airbase.Category.HELIPAD then + SpawnPoint.linkUnit = AirbaseID + SpawnPoint.helipadId = AirbaseID + elseif AirbaseCategory == Airbase.Category.AIRDROME then + SpawnPoint.airdromeId = AirbaseID + end + + -- Set waypoint type/action. + SpawnPoint.alt = 0 + SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type + SpawnPoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action + + -- Check if we spawn on ground. + local spawnonground=not (Takeoff==SPAWN.Takeoff.Air) + self:T({spawnonground=spawnonground, TOtype=Takeoff, TOair=Takeoff==SPAWN.Takeoff.Air}) + + -- Check where we actually spawn if we spawn on ground. + local spawnonship=false + local spawnonfarp=false + local spawnonrunway=false + local spawnonairport=false + if spawnonground then + if AirbaseCategory == Airbase.Category.SHIP then + spawnonship=true + elseif AirbaseCategory == Airbase.Category.HELIPAD then + spawnonfarp=true + elseif AirbaseCategory == Airbase.Category.AIRDROME then + spawnonairport=true + end + spawnonrunway=Takeoff==SPAWN.Takeoff.Runway + end + + -- Array with parking spots coordinates. + local parkingspots={} + local parkingindex={} + local spots + + -- Spawn happens on ground, i.e. at an airbase, a FARP or a ship. + if spawnonground and not SpawnTemplate.parked then + + + -- Number of free parking spots. + local nfree=0 + + -- Set terminal type. + local termtype=TerminalType + + -- Scan options. Might make that input somehow. + local scanradius=50 + local scanunits=true + local scanstatics=true + local scanscenery=false + local verysafe=false + + -- Number of free parking spots at the airbase. + if spawnonship or spawnonfarp or spawnonrunway then + -- These places work procedural and have some kind of build in queue ==> Less effort. + self:T(string.format("Group %s is spawned on farp/ship/runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) + nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype, true) + spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, true) + elseif Parkingdata~=nil then + -- Parking data explicitly set by user as input parameter. + nfree=#Parkingdata + spots=Parkingdata + else + if ishelo then + if termtype==nil then + -- Helo is spawned. Try exclusive helo spots first. + self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterOnly)) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits) + nfree=#spots + if nfree=1 then + + -- All units get the same spot. DCS takes care of the rest. + for i=1,nunits do + table.insert(parkingspots, spots[1].Coordinate) + table.insert(parkingindex, spots[1].TerminalID) + end + -- This is actually used... + PointVec3=spots[1].Coordinate + + else + -- If there is absolutely no spot ==> air start! + _notenough=true + end + + elseif spawnonairport then + + if nfree>=nunits then + + for i=1,nunits do + table.insert(parkingspots, spots[i].Coordinate) + table.insert(parkingindex, spots[i].TerminalID) + end + + else + -- Not enough spots for the whole group ==> air start! + _notenough=true + end + end + + -- Not enough spots ==> Prepare airstart. + if _notenough then + + if not self.SpawnUnControlled then + else + self:E(string.format("WARNING: Group %s has no parking spots at %s ==> No emergency air start or uncontrolled spawning ==> No spawn!", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) + return nil + end + end + + else + + end + + if not SpawnTemplate.parked then + -- Translate the position of the Group Template to the Vec3. + + SpawnTemplate.parked = true + + for UnitID = 1, nunits do + self:F('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) + + -- Template of the current unit. + local UnitTemplate = SpawnTemplate.units[UnitID] + + -- Tranlate position and preserve the relative position/formation of all aircraft. + local SX = UnitTemplate.x + local SY = UnitTemplate.y + local BX = SpawnTemplate.route.points[1].x + local BY = SpawnTemplate.route.points[1].y + local TX = PointVec3.x + (SX-BX) + local TY = PointVec3.z + (SY-BY) + + if spawnonground then + + -- Ships and FARPS seem to have a build in queue. + if spawnonship or spawnonfarp or spawnonrunway then + + self:T(string.format("Group %s spawning at farp, ship or runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) + + -- Spawn on ship. We take only the position of the ship. + SpawnTemplate.units[UnitID].x = PointVec3.x --TX + SpawnTemplate.units[UnitID].y = PointVec3.z --TY + SpawnTemplate.units[UnitID].alt = PointVec3.y + + else + + self:T(string.format("Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID])) + + -- Get coordinates of parking spot. + SpawnTemplate.units[UnitID].x = parkingspots[UnitID].x + SpawnTemplate.units[UnitID].y = parkingspots[UnitID].z + SpawnTemplate.units[UnitID].alt = parkingspots[UnitID].y + + --parkingspots[UnitID]:MarkToAll(string.format("Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID])) + end + + else + + self:T(string.format("Group %s spawning in air at %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) + + -- Spawn in air as requested initially. Original template orientation is perserved, altitude is already correctly set. + SpawnTemplate.units[UnitID].x = TX + SpawnTemplate.units[UnitID].y = TY + SpawnTemplate.units[UnitID].alt = PointVec3.y + + end + + -- Parking spot id. + UnitTemplate.parking = nil + UnitTemplate.parking_id = nil + if parkingindex[UnitID] then + UnitTemplate.parking = parkingindex[UnitID] + end + + -- Debug output. + self:T2(string.format("Group %s unit number %d: Parking = %s",self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking))) + self:T2(string.format("Group %s unit number %d: Parking ID = %s",self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking_id))) + self:T2('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) + end + end + + -- Set gereral spawnpoint position. + SpawnPoint.x = PointVec3.x + SpawnPoint.y = PointVec3.z + SpawnPoint.alt = PointVec3.y + + SpawnTemplate.x = PointVec3.x + SpawnTemplate.y = PointVec3.z + + SpawnTemplate.uncontrolled = true + + -- Spawn group. + local GroupSpawned = self:SpawnWithIndex( SpawnIndex, true ) + + -- When spawned in the air, we need to generate a Takeoff Event. + if Takeoff == GROUP.Takeoff.Air then + for UnitID, UnitSpawned in pairs( GroupSpawned:GetUnits() ) do + SCHEDULER:New( nil, BASE.CreateEventTakeoff, { GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject() } , 5 ) + end + end + + -- Check if we accidentally spawned on the runway. Needs to be schedules, because group is not immidiately alive. + if Takeoff~=SPAWN.Takeoff.Runway and Takeoff~=SPAWN.Takeoff.Air and spawnonairport then + SCHEDULER:New(nil, AIRBASE.CheckOnRunWay, {SpawnAirbase, GroupSpawned, 75, true} , 1.0) + end + + end + +end + --- Will park a group at an @{Wrapper.Airbase}. -- This method is mostly advisable to be used if you want to simulate parking units at an airbase and be visible. -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. @@ -1876,325 +2206,10 @@ end function SPAWN:ParkAtAirbase( SpawnAirbase, TerminalType, Parkingdata ) -- R2.2, R2.4, R2.5 self:F( { self.SpawnTemplatePrefix, SpawnAirbase, TerminalType } ) - -- Get position of airbase. - local PointVec3 = SpawnAirbase:GetCoordinate() + self:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, 1 ) - -- Set take off type. Default is hot. - local Takeoff = SPAWN.Takeoff.Cold - - for SpawnIndex = 1, self.SpawnMaxGroups do - - self:F( { SpawnIndex = SpawnIndex, SpawnMaxGroups = self.SpawnMaxGroups } ) - - -- Get group template. - local SpawnTemplate = self.SpawnGroups[SpawnIndex].SpawnTemplate - - if SpawnTemplate then - - -- Check if the aircraft with the specified SpawnIndex is already spawned. - -- If yes, ensure that the aircraft is spawned at the same aircraft spot. - - local GroupAlive = self:GetGroupFromIndex( SpawnIndex ) - - -- Debug output - self:T( { "Current point of ", self.SpawnTemplatePrefix, SpawnAirbase } ) - - -- Template group, unit and its attributes. - local TemplateGroup = GROUP:FindByName(self.SpawnTemplatePrefix) - local TemplateUnit=TemplateGroup:GetUnit(1) - local ishelo=TemplateUnit:HasAttribute("Helicopters") - local isbomber=TemplateUnit:HasAttribute("Bombers") - local istransport=TemplateUnit:HasAttribute("Transports") - local isfighter=TemplateUnit:HasAttribute("Battleplanes") - - -- Number of units in the group. With grouping this can actually differ from the template group size! - local nunits=#SpawnTemplate.units - - -- First waypoint of the group. - local SpawnPoint = SpawnTemplate.route.points[1] - - -- These are only for ships and FARPS. - SpawnPoint.linkUnit = nil - SpawnPoint.helipadId = nil - SpawnPoint.airdromeId = nil - - -- Get airbase ID and category. - local AirbaseID = SpawnAirbase:GetID() - local AirbaseCategory = SpawnAirbase:GetDesc().category - self:F( { AirbaseCategory = AirbaseCategory } ) - - -- Set airdromeId. - if AirbaseCategory == Airbase.Category.SHIP then - SpawnPoint.linkUnit = AirbaseID - SpawnPoint.helipadId = AirbaseID - elseif AirbaseCategory == Airbase.Category.HELIPAD then - SpawnPoint.linkUnit = AirbaseID - SpawnPoint.helipadId = AirbaseID - elseif AirbaseCategory == Airbase.Category.AIRDROME then - SpawnPoint.airdromeId = AirbaseID - end - - -- Set waypoint type/action. - SpawnPoint.alt = 0 - SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type - SpawnPoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action - - -- Check if we spawn on ground. - local spawnonground=not (Takeoff==SPAWN.Takeoff.Air) - self:T({spawnonground=spawnonground, TOtype=Takeoff, TOair=Takeoff==SPAWN.Takeoff.Air}) - - -- Check where we actually spawn if we spawn on ground. - local spawnonship=false - local spawnonfarp=false - local spawnonrunway=false - local spawnonairport=false - if spawnonground then - if AirbaseCategory == Airbase.Category.SHIP then - spawnonship=true - elseif AirbaseCategory == Airbase.Category.HELIPAD then - spawnonfarp=true - elseif AirbaseCategory == Airbase.Category.AIRDROME then - spawnonairport=true - end - spawnonrunway=Takeoff==SPAWN.Takeoff.Runway - end - - -- Array with parking spots coordinates. - local parkingspots={} - local parkingindex={} - local spots - - -- Spawn happens on ground, i.e. at an airbase, a FARP or a ship. - if spawnonground and not SpawnTemplate.parked then - - - -- Number of free parking spots. - local nfree=0 - - -- Set terminal type. - local termtype=TerminalType - - -- Scan options. Might make that input somehow. - local scanradius=50 - local scanunits=true - local scanstatics=true - local scanscenery=false - local verysafe=false - - -- Number of free parking spots at the airbase. - if spawnonship or spawnonfarp or spawnonrunway then - -- These places work procedural and have some kind of build in queue ==> Less effort. - self:T(string.format("Group %s is spawned on farp/ship/runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) - nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype, true) - spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, true) - elseif Parkingdata~=nil then - -- Parking data explicitly set by user as input parameter. - nfree=#Parkingdata - spots=Parkingdata - else - if ishelo then - if termtype==nil then - -- Helo is spawned. Try exclusive helo spots first. - self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterOnly)) - spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits) - nfree=#spots - if nfree=1 then - - -- All units get the same spot. DCS takes care of the rest. - for i=1,nunits do - table.insert(parkingspots, spots[1].Coordinate) - table.insert(parkingindex, spots[1].TerminalID) - end - -- This is actually used... - PointVec3=spots[1].Coordinate - - else - -- If there is absolutely no spot ==> air start! - _notenough=true - end - - elseif spawnonairport then - - if nfree>=nunits then - - for i=1,nunits do - table.insert(parkingspots, spots[i].Coordinate) - table.insert(parkingindex, spots[i].TerminalID) - end - - else - -- Not enough spots for the whole group ==> air start! - _notenough=true - end - end - - -- Not enough spots ==> Prepare airstart. - if _notenough then - - if not self.SpawnUnControlled then - else - self:E(string.format("WARNING: Group %s has no parking spots at %s ==> No emergency air start or uncontrolled spawning ==> No spawn!", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) - return nil - end - end - - else - - end - - if not SpawnTemplate.parked then - -- Translate the position of the Group Template to the Vec3. - - SpawnTemplate.parked = true - - for UnitID = 1, nunits do - self:F('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) - - -- Template of the current unit. - local UnitTemplate = SpawnTemplate.units[UnitID] - - -- Tranlate position and preserve the relative position/formation of all aircraft. - local SX = UnitTemplate.x - local SY = UnitTemplate.y - local BX = SpawnTemplate.route.points[1].x - local BY = SpawnTemplate.route.points[1].y - local TX = PointVec3.x + (SX-BX) - local TY = PointVec3.z + (SY-BY) - - if spawnonground then - - -- Ships and FARPS seem to have a build in queue. - if spawnonship or spawnonfarp or spawnonrunway then - - self:T(string.format("Group %s spawning at farp, ship or runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) - - -- Spawn on ship. We take only the position of the ship. - SpawnTemplate.units[UnitID].x = PointVec3.x --TX - SpawnTemplate.units[UnitID].y = PointVec3.z --TY - SpawnTemplate.units[UnitID].alt = PointVec3.y - - else - - self:T(string.format("Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID])) - - -- Get coordinates of parking spot. - SpawnTemplate.units[UnitID].x = parkingspots[UnitID].x - SpawnTemplate.units[UnitID].y = parkingspots[UnitID].z - SpawnTemplate.units[UnitID].alt = parkingspots[UnitID].y - - --parkingspots[UnitID]:MarkToAll(string.format("Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID])) - end - - else - - self:T(string.format("Group %s spawning in air at %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) - - -- Spawn in air as requested initially. Original template orientation is perserved, altitude is already correctly set. - SpawnTemplate.units[UnitID].x = TX - SpawnTemplate.units[UnitID].y = TY - SpawnTemplate.units[UnitID].alt = PointVec3.y - - end - - -- Parking spot id. - UnitTemplate.parking = nil - UnitTemplate.parking_id = nil - if parkingindex[UnitID] then - UnitTemplate.parking = parkingindex[UnitID] - end - - -- Debug output. - self:T2(string.format("Group %s unit number %d: Parking = %s",self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking))) - self:T2(string.format("Group %s unit number %d: Parking ID = %s",self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking_id))) - self:T2('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) - end - end - - -- Set gereral spawnpoint position. - SpawnPoint.x = PointVec3.x - SpawnPoint.y = PointVec3.z - SpawnPoint.alt = PointVec3.y - - SpawnTemplate.x = PointVec3.x - SpawnTemplate.y = PointVec3.z - - SpawnTemplate.uncontrolled = true - - -- Spawn group. - local GroupSpawned = self:SpawnWithIndex( SpawnIndex, true ) - - -- When spawned in the air, we need to generate a Takeoff Event. - if Takeoff == GROUP.Takeoff.Air then - for UnitID, UnitSpawned in pairs( GroupSpawned:GetUnits() ) do - SCHEDULER:New( nil, BASE.CreateEventTakeoff, { GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject() } , 5 ) - end - end - - -- Check if we accidentally spawned on the runway. Needs to be schedules, because group is not immidiately alive. - if Takeoff~=SPAWN.Takeoff.Runway and Takeoff~=SPAWN.Takeoff.Air and spawnonairport then - SCHEDULER:New(nil, AIRBASE.CheckOnRunWay, {SpawnAirbase, GroupSpawned, 75, true} , 1.0) - end - - end + for SpawnIndex = 2, self.SpawnMaxGroups do + self:ScheduleOnce( SpawnIndex * 0.1, SPAWN.ParkAircraft, self, SpawnAirbase, TerminalType, Parkingdata, SpawnIndex ) end self:SetSpawnIndex() diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 953ba2457..1960b67a0 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -549,12 +549,15 @@ function AIRBASE:GetParkingSpotsTable(termtype) local spots={} for _,_spot in pairs(parkingdata) do if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then + self:I({_spot=_spot}) local _free=_isfree(_spot) local _coord=COORDINATE:NewFromVec3(_spot.vTerminalPos) table.insert(spots, {Coordinate=_coord, TerminalID=_spot.Term_Index, TerminalType=_spot.Term_Type, TOAC=_spot.TO_AC, Free=_free, TerminalID0=_spot.Term_Index_0, DistToRwy=_spot.fDistToRW}) end end + self:I({ spots = spots } ) + return spots end @@ -704,6 +707,8 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE local _termid=parkingspot.TerminalID + self:I({_termid=_termid}) + if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) then -- Very safe uses the DCS getParking() info to check if a spot is free. Unfortunately, the function returns free=false until the aircraft has actually taken-off. @@ -783,7 +788,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, --_spot:MarkToAll(string.format("Parking spot %d free=%s", parkingspot.TerminalID, tostring(not occupied))) if occupied then - self:T(string.format("%s: Parking spot id %d occupied.", airport, _termid)) + self:I(string.format("%s: Parking spot id %d occupied.", airport, _termid)) else self:I(string.format("%s: Parking spot id %d free.", airport, _termid)) if nvalid<_nspots then