Finish Feature-Escort

This commit is contained in:
FlightControl 2019-06-18 20:22:38 +03:00
commit 9c0c4a11b1
12 changed files with 1926 additions and 610 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,293 @@
--- **Functional** -- Taking the lead of AI escorting your flight or of other AI, upon request using the menu.
--
-- ===
--
-- ## Features:
--
-- * Escort navigation commands.
-- * Escort hold at position commands.
-- * Escorts reporting detected targets.
-- * Escorts scanning targets in advance.
-- * Escorts attacking specific targets.
-- * Request assistance from other groups for attack.
-- * Manage rule of engagement of escorts.
-- * Manage the allowed evasion techniques of escorts.
-- * Make escort to execute a defined mission or path.
-- * Escort tactical situation reporting.
--
-- ===
--
-- ## Missions:
--
-- [ESC - Escorting](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/ESC%20-%20Escorting)
--
-- ===
--
-- Allows you to interact with escorting AI on your flight and take the lead.
--
-- Each escorting group can be commanded with a complete set of radio commands (radio menu in your flight, and then F10).
--
-- The radio commands will vary according the category of the group. The richest set of commands are with helicopters and airPlanes.
-- Ships and Ground troops will have a more limited set, but they can provide support through the bombing of targets designated by the other escorts.
--
-- Escorts detect targets using a built-in detection mechanism. The detected targets are reported at a specified time interval.
-- Once targets are reported, each escort has these targets as menu options to command the attack of these targets.
-- Targets are by default grouped per area of 5000 meters, but the kind of detection and the grouping range can be altered.
--
-- Different formations can be selected in the Flight menu: Trail, Stack, Left Line, Right Line, Left Wing, Right Wing, Central Wing and Boxed formations are available.
-- The Flight menu also allows for a mass attack, where all of the escorts are commanded to attack a target.
--
-- Escorts can emit flares to reports their location. They can be commanded to hold at a location, which can be their current or the leader location.
-- In this way, you can spread out the escorts over the battle field before a coordinated attack.
--
-- But basically, the escort class provides 4 modes of operation, and depending on the mode, you are either leading the flight, or following the flight.
--
-- ## Leading the flight
--
-- When leading the flight, you are expected to guide the escorts towards the target areas,
-- and carefully coordinate the attack based on the threat levels reported, and the available weapons
-- carried by the escorts. Ground ships or ground troops can execute A-assisted attacks, when they have long-range ground precision weapons for attack.
--
-- ## Following the flight
--
-- Escorts can be commanded to execute a specific mission path. In this mode, the escorts are in the lead.
-- You as a player, are following the escorts, and are commanding them to progress the mission while
-- ensuring that the escorts survive. You are joining the escorts in the battlefield. They will detect and report targets
-- and you will ensure that the attacks are well coordinated, assigning the correct escort type for the detected target
-- type. Once the attack is finished, the escort will resume the mission it was assigned.
-- In other words, you can use the escorts for reconnaissance, and for guiding the attack.
-- Imagine you as a mi-8 pilot, assigned to pickup cargo. Two ka-50s are guiding the way, and you are
-- following. You are in control. The ka-50s detect targets, report them, and you command how the attack
-- will commence and from where. You can control where the escorts are holding position and which targets
-- are attacked first. You are in control how the ka-50s will follow their mission path.
--
-- Escorts can act as part of a AI A2G dispatcher offensive. In this way, You was a player are in control.
-- The mission is defined by the A2G dispatcher, and you are responsible to join the flight and ensure that the
-- attack is well coordinated.
--
-- It is with great proud that I present you this class, and I hope you will enjoy the functionality and the dynamism
-- it brings in your DCS world simulations.
--
-- # RADIO MENUs that can be created:
--
-- Find a summary below of the current available commands:
--
-- ## Navigation ...:
--
-- Escort group navigation functions:
--
-- * **"Join-Up":** The escort group fill follow you in the assigned formation.
-- * **"Flare":** Provides menu commands to let the escort group shoot a flare in the air in a color.
-- * **"Smoke":** Provides menu commands to let the escort group smoke the air in a color. Note that smoking is only available for ground and naval troops.
--
-- ## Hold position ...:
--
-- Escort group navigation functions:
--
-- * **"At current location":** The escort group will hover above the ground at the position they were. The altitude can be specified as a parameter.
-- * **"At my location":** The escort group will hover or orbit at the position where you are. The escort will fly to your location and hold position. The altitude can be specified as a parameter.
--
-- ## Report targets ...:
--
-- Report targets will make the escort group to report any target that it identifies within detection range. Any detected target can be attacked using the "Attack Targets" menu function. (see below).
--
-- * **"Report now":** Will report the current detected targets.
-- * **"Report targets on":** Will make the escorts to report the detected targets and will fill the "Attack Targets" menu list.
-- * **"Report targets off":** Will stop detecting targets.
--
-- ## Attack targets ...:
--
-- This menu item will list all detected targets within a 15km range. Depending on the level of detection (known/unknown) and visuality, the targets type will also be listed.
-- This menu will be available in Flight menu or in each Escort menu.
--
-- ## Scan targets ...:
--
-- Menu items to pop-up the escort group for target scanning. After scanning, the escort group will resume with the mission or rejoin formation.
--
-- * **"Scan targets 30 seconds":** Scan 30 seconds for targets.
-- * **"Scan targets 60 seconds":** Scan 60 seconds for targets.
--
-- ## Request assistance from ...:
--
-- This menu item will list all detected targets within a 15km range, similar as with the menu item **Attack Targets**.
-- This menu item allows to request attack support from other ground based escorts supporting the current escort.
-- eg. the function allows a player to request support from the Ship escort to attack a target identified by the Plane escort with its Tomahawk missiles.
-- eg. the function allows a player to request support from other Planes escorting to bomb the unit with illumination missiles or bombs, so that the main plane escort can attack the area.
--
-- ## ROE ...:
--
-- Sets the Rules of Engagement (ROE) of the escort group when in flight.
--
-- * **"Hold Fire":** The escort group will hold fire.
-- * **"Return Fire":** The escort group will return fire.
-- * **"Open Fire":** The escort group will open fire on designated targets.
-- * **"Weapon Free":** The escort group will engage with any target.
--
-- ## Evasion ...:
--
-- Will define the evasion techniques that the escort group will perform during flight or combat.
--
-- * **"Fight until death":** The escort group will have no reaction to threats.
-- * **"Use flares, chaff and jammers":** The escort group will use passive defense using flares and jammers. No evasive manoeuvres are executed.
-- * **"Evade enemy fire":** The rescort group will evade enemy fire before firing.
-- * **"Go below radar and evade fire":** The escort group will perform evasive vertical manoeuvres.
--
-- ## Resume Mission ...:
--
-- Escort groups can have their own mission. This menu item will allow the escort group to resume their Mission from a given waypoint.
-- Note that this is really fantastic, as you now have the dynamic of taking control of the escort groups, and allowing them to resume their path or mission.
--
-- ===
--
-- ### Authors: **FlightControl**
--
-- ===
--
-- @module AI.AI_Escort
-- @image Escorting.JPG
--- @type AI_ESCORT_REQUEST
-- @extends AI.AI_Escort#AI_ESCORT
--- AI_ESCORT_REQUEST class
--
-- # AI_ESCORT_REQUEST construction methods.
--
-- Create a new AI_ESCORT_REQUEST object with the @{#AI_ESCORT_REQUEST.New} method:
--
-- * @{#AI_ESCORT_REQUEST.New}: Creates a new AI_ESCORT_REQUEST object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT}, with an optional briefing text.
--
-- @usage
-- -- Declare a new EscortPlanes object as follows:
--
-- -- First find the GROUP object and the CLIENT object.
-- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor.
-- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client.
--
-- -- Now use these 2 objects to construct the new EscortPlanes object.
-- EscortPlanes = AI_ESCORT_REQUEST:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." )
--
-- @field #AI_ESCORT_REQUEST
AI_ESCORT_REQUEST = {
ClassName = "AI_ESCORT_REQUEST",
}
--- AI_ESCORT_REQUEST.Mode class
-- @type AI_ESCORT_REQUEST.MODE
-- @field #number FOLLOW
-- @field #number MISSION
--- MENUPARAM type
-- @type MENUPARAM
-- @field #AI_ESCORT_REQUEST ParamSelf
-- @field #Distance ParamDistance
-- @field #function ParamFunction
-- @field #string ParamMessage
--- AI_ESCORT_REQUEST class constructor for an AI group
-- @param #AI_ESCORT_REQUEST self
-- @param Wrapper.Client#CLIENT EscortUnit The client escorted by the EscortGroup.
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object of AI, escorting the EscortUnit.
-- @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
-- @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 )
--
-- local EscortUnit = UNIT:FindByName( "Red A2G Pilot" )
--
-- Escort = AI_ESCORT_REQUEST:New( EscortUnit, EscortSpawn, AIRBASE:FindByName(AIRBASE.Caucasus.Sochi_Adler), "A2G", "Briefing" )
-- Escort:FormationTrail( 50, 100, 100 )
-- Escort:Menus()
-- Escort:__Start( 5 )
function AI_ESCORT_REQUEST:New( EscortUnit, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
self.EscortGroupSet = SET_GROUP:New()
self.EscortSpawn = EscortSpawn
self.EscortAirbase = EscortAirbase
local self = BASE:Inherit( self, AI_ESCORT:New( EscortUnit, self.EscortGroupSet, EscortName, EscortBriefing ) ) -- #AI_ESCORT_REQUEST
self.LeaderGroup = self.PlayerUnit:GetGroup()
self.Detection = DETECTION_AREAS:New( self.EscortGroupSet, 5000 )
self.Detection:__Start( 30 )
self.SpawnMode = self.__Enum.Mode.Mission
return self
end
--- @param #AI_ESCORT_REQUEST self
function AI_ESCORT_REQUEST:SpawnEscort()
local EscortGroup = self.EscortSpawn:SpawnAtAirbase( self.EscortAirbase, SPAWN.Takeoff.Hot )
EscortGroup:OptionROTVertical()
EscortGroup:OptionROEHoldFire()
self:ScheduleOnce( 0.1,
function( EscortGroup )
self.EscortGroupSet:AddGroup( EscortGroup )
local LeaderEscort = self.EscortGroupSet:GetFirst() -- Wrapper.Group#GROUP
local Report = REPORT:New()
Report:Add( "Joining Up " .. self.EscortGroupSet:GetUnitTypeNames():Text( ", " ) .. " from " .. LeaderEscort:GetCoordinate():ToString( self.EscortUnit ) )
LeaderEscort:MessageTypeToGroup( Report:Text(), MESSAGE.Type.Information, self.PlayerUnit )
if self.SpawnMode == self.__Enum.Mode.Formation then
self:ModeFormation( EscortGroup )
end
self:_InitFlightMenus()
self:_InitEscortMenus( EscortGroup )
self:_InitEscortRoute( EscortGroup )
--- @param #AI_ESCORT self
-- @param Core.Event#EVENTDATA EventData
function EscortGroup:OnEventDeadOrCrash( EventData )
self:F( { "EventDead", EventData } )
self.EscortMenu:Remove()
end
EscortGroup:HandleEvent( EVENTS.Dead, EscortGroup.OnEventDeadOrCrash )
EscortGroup:HandleEvent( EVENTS.Crash, EscortGroup.OnEventDeadOrCrash )
end, EscortGroup
)
end
--- @param #AI_ESCORT_REQUEST self
-- @param Core.Set#SET_GROUP EscortGroupSet
function AI_ESCORT_REQUEST:onafterStart( EscortGroupSet )
self:F()
if not self.MenuRequestEscort then
self.MenuRequestEscort = MENU_GROUP_COMMAND:New( self.LeaderGroup, "Request new escort ", self.MainMenu,
function()
self:SpawnEscort()
end
)
end
self:GetParent( self ).onafterStart( self, EscortGroupSet )
self:HandleEvent( EVENTS.Dead, self.OnEventDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self.OnEventDeadOrCrash )
end
--- Set the spawn mode to be mission execution.
-- @param #AI_ESCORT_REQUEST self
function AI_ESCORT_REQUEST:SetEscortSpawnMission()
self.SpawnMode = self.__Enum.Mode.Mission
end

View File

@ -110,10 +110,45 @@ AI_FORMATION = {
dtFollow = 0.5, dtFollow = 0.5,
} }
--- AI_FORMATION.Mode class AI_FORMATION.__Enum = {}
-- @type AI_FORMATION.MODE
-- @field #number FOLLOW --- @type AI_FORMATION.__Enum.Formation
-- @field #number MISSION -- @field #number None
-- @field #number Line
-- @field #number Trail
-- @field #number Stack
-- @field #number LeftLine
-- @field #number RightLine
-- @field #number LeftWing
-- @field #number RightWing
-- @field #number Vic
-- @field #number Box
AI_FORMATION.__Enum.Formation = {
None = 0,
Mission = 1,
Line = 2,
Trail = 3,
Stack = 4,
LeftLine = 5,
RightLine = 6,
LeftWing = 7,
RightWing = 8,
Vic = 9,
Box = 10,
}
--- @type AI_FORMATION.__Enum.Mode
-- @field #number Mission
-- @field #number Formation
AI_FORMATION.__Enum.Mode = {
Mission = 0,
Formation = 1,
Attack = 2,
Reconnaissance = 3,
}
--- MENUPARAM type --- MENUPARAM type
-- @type MENUPARAM -- @type MENUPARAM
@ -139,7 +174,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
self.FollowGroupSet:ForEachGroup( self.FollowGroupSet:ForEachGroup(
function( FollowGroup ) function( FollowGroup )
self:E("Following") self:E("Following")
FollowGroup.Following = true FollowGroup:SetState( self, "Mode", self.__Enum.Mode.Formation )
end end
) )
@ -663,8 +698,8 @@ end
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #AI_FORMATION -- @return #AI_FORMATION
function AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1 function AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, Formation ) --R2.1
self:F( { FollowGroupSet, From , Event ,To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace } ) self:F( { FollowGroupSet, From , Event ,To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, Formation } )
FollowGroupSet:Flush( self ) FollowGroupSet:Flush( self )
@ -682,6 +717,8 @@ function AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, X
local Vec3 = PointVec3:GetVec3() local Vec3 = PointVec3:GetVec3()
FollowGroup:SetState( self, "FormationVec3", Vec3 ) FollowGroup:SetState( self, "FormationVec3", Vec3 )
i = i + 1 i = i + 1
FollowGroup:SetState( FollowGroup, "Formation", Formation )
end end
return self return self
@ -700,7 +737,7 @@ end
-- @return #AI_FORMATION -- @return #AI_FORMATION
function AI_FORMATION:onafterFormationTrail( FollowGroupSet, From , Event , To, XStart, XSpace, YStart ) --R2.1 function AI_FORMATION:onafterFormationTrail( FollowGroupSet, From , Event , To, XStart, XSpace, YStart ) --R2.1
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,0,0) self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,0,0, self.__Enum.Formation.Trail )
return self return self
end end
@ -719,7 +756,7 @@ end
-- @return #AI_FORMATION -- @return #AI_FORMATION
function AI_FORMATION:onafterFormationStack( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace ) --R2.1 function AI_FORMATION:onafterFormationStack( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace ) --R2.1
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,0,0) self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,0,0, self.__Enum.Formation.Stack )
return self return self
end end
@ -740,7 +777,7 @@ end
-- @return #AI_FORMATION -- @return #AI_FORMATION
function AI_FORMATION:onafterFormationLeftLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1 function AI_FORMATION:onafterFormationLeftLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,ZStart,ZSpace) self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,-ZStart,-ZSpace, self.__Enum.Formation.LeftLine )
return self return self
end end
@ -759,7 +796,7 @@ end
-- @return #AI_FORMATION -- @return #AI_FORMATION
function AI_FORMATION:onafterFormationRightLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1 function AI_FORMATION:onafterFormationRightLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,-ZStart,-ZSpace) self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,ZStart,ZSpace,self.__Enum.Formation.RightLine)
return self return self
end end
@ -778,7 +815,7 @@ end
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
function AI_FORMATION:onafterFormationLeftWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1 function AI_FORMATION:onafterFormationLeftWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,ZStart,ZSpace) self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,-ZStart,-ZSpace,self.__Enum.Formation.LeftWing)
return self return self
end end
@ -798,7 +835,7 @@ end
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
function AI_FORMATION:onafterFormationRightWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1 function AI_FORMATION:onafterFormationRightWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,-ZStart,-ZSpace) self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,ZStart,ZSpace,self.__Enum.Formation.RightWing)
return self return self
end end
@ -836,6 +873,7 @@ function AI_FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event ,
local Vec3 = PointVec3:GetVec3() local Vec3 = PointVec3:GetVec3()
FollowGroup:SetState( self, "FormationVec3", Vec3 ) FollowGroup:SetState( self, "FormationVec3", Vec3 )
i = i + 1 i = i + 1
FollowGroup:SetState( FollowGroup, "Formation", self.__Enum.Formation.Vic )
end end
return self return self
@ -895,6 +933,7 @@ function AI_FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XS
local Vec3 = PointVec3:GetVec3() local Vec3 = PointVec3:GetVec3()
FollowGroup:SetState( self, "FormationVec3", Vec3 ) FollowGroup:SetState( self, "FormationVec3", Vec3 )
i = i + 1 i = i + 1
FollowGroup:SetState( FollowGroup, "Formation", self.__Enum.Formation.Box )
end end
return self return self
@ -913,30 +952,79 @@ function AI_FORMATION:SetFlightRandomization( FlightRandomization ) --R2.1
end end
--- This releases the air unit in your flight from the formation flight.
--- This sets your escorts to fly a mission.
-- @param #AI_FORMATION self -- @param #AI_FORMATION self
-- @param Wrapper.Group#GROUP FollowGroup FollowGroup. -- @param Wrapper.Group#GROUP FollowGroup FollowGroup.
-- @return #AI_FORMATION -- @return #AI_FORMATION
function AI_FORMATION:ReleaseFormation( FollowGroup ) function AI_FORMATION:ModeMission( FollowGroup )
FollowGroup.Following = false if FollowGroup then
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission )
else
self.EscortGroupSet:ForSomeGroupAlive(
--- @param Core.Group#GROUP EscortGroup
function( FollowGroup )
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission )
end
)
end
return self return self
end end
--- This joins up the air unit in your formation flight. --- This sets your escorts to execute an attack.
-- @param #AI_FORMATION self -- @param #AI_FORMATION self
-- @param Wrapper.Group#GROUP FollowGroup FollowGroup. -- @param Wrapper.Group#GROUP FollowGroup FollowGroup.
-- @return #AI_FORMATION -- @return #AI_FORMATION
function AI_FORMATION:JoinFormation( FollowGroup ) function AI_FORMATION:ModeAttack( FollowGroup )
FollowGroup.Following = true if FollowGroup then
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Attack )
else
self.EscortGroupSet:ForSomeGroupAlive(
--- @param Core.Group#GROUP EscortGroup
function( FollowGroup )
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Attack )
end
)
end
return self return self
end end
--- This sets your escorts to fly in a formation.
-- @param #AI_FORMATION self
-- @param Wrapper.Group#GROUP FollowGroup FollowGroup.
-- @return #AI_FORMATION
function AI_FORMATION:ModeFormation( FollowGroup )
if FollowGroup then
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation )
else
self.EscortGroupSet:ForSomeGroupAlive(
--- @param Core.Group#GROUP EscortGroup
function( FollowGroup )
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation )
end
)
end
return self
end
--- Stop function. Formation will not be updated any more. --- Stop function. Formation will not be updated any more.
-- @param #AI_FORMATION self -- @param #AI_FORMATION self
@ -963,15 +1051,11 @@ end
--- @param #AI_FORMATION self --- @param #AI_FORMATION self
function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1 function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
self:F( )
self:T( { self.FollowUnit.UnitName, self.FollowUnit:IsAlive() } )
if self.FollowUnit:IsAlive() then if self.FollowUnit:IsAlive() then
local ClientUnit = self.FollowUnit local ClientUnit = self.FollowUnit
self:T( {ClientUnit.UnitName } )
local CT1, CT2, CV1, CV2 local CT1, CT2, CV1, CV2
CT1 = ClientUnit:GetState( self, "CT1" ) CT1 = ClientUnit:GetState( self, "CT1" )
@ -988,12 +1072,14 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
ClientUnit:SetState( self, "CV1", CV2 ) ClientUnit:SetState( self, "CV1", CV2 )
end end
FollowGroupSet:ForEachGroup( FollowGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP FollowGroup --- @param Wrapper.Group#GROUP FollowGroup
-- @param Wrapper.Unit#UNIT ClientUnit -- @param Wrapper.Unit#UNIT ClientUnit
function( FollowGroup, Formation, ClientUnit, CT1, CV1, CT2, CV2 ) function( FollowGroup, Formation, ClientUnit, CT1, CV1, CT2, CV2 )
if FollowGroup.Following == true then self:I({Mode=FollowGroup:GetState( FollowGroup, "Mode" )})
if FollowGroup:GetState( FollowGroup, "Mode" ) == self.__Enum.Mode.Formation then
FollowGroup:OptionROTEvadeFire() FollowGroup:OptionROTEvadeFire()
FollowGroup:OptionROEReturnFire() FollowGroup:OptionROEReturnFire()
@ -1055,8 +1141,13 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
-- Now we calculate the intersecting vector between the circle around CV2 with radius FollowDistance and GH2. -- Now we calculate the intersecting vector between the circle around CV2 with radius FollowDistance and GH2.
-- From the GeoGebra model: CVI = (x(CV2) + FollowDistance cos(alpha), y(GH2) + FollowDistance sin(alpha), z(CV2)) -- From the GeoGebra model: CVI = (x(CV2) + FollowDistance cos(alpha), y(GH2) + FollowDistance sin(alpha), z(CV2))
local Inclination = ( Distance + FollowFormation.x ) / 10
if Inclination < -30 then
Inclination = - 30
end
local CVI = { x = CV2.x + CS * 10 * math.sin(Ca), local CVI = { x = CV2.x + CS * 10 * math.sin(Ca),
y = GH2.y - ( Distance + FollowFormation.x ) / 5, -- + FollowFormation.y, y = GH2.y + Inclination, -- + FollowFormation.y,
y = GH2.y,
z = CV2.z + CS * 10 * math.cos(Ca), z = CV2.z + CS * 10 * math.cos(Ca),
} }
@ -1087,13 +1178,22 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
local Time = 60 local Time = 120
local Speed = - ( Distance + FollowFormation.x ) / Time local Speed = - ( Distance + FollowFormation.x ) / Time
local GS = Speed + CS
if Speed < 0 then if Distance > -10000 then
Speed = 0 Speed = - ( Distance + FollowFormation.x ) / 60
end end
if Distance > -2500 then
Speed = - ( Distance + FollowFormation.x ) / 20
end
local GS = Speed + CS
self:F( { Distance = Distance, Speed = Speed, CS = CS, GS = GS } )
-- Now route the escort to the desired point with the desired speed. -- Now route the escort to the desired point with the desired speed.
FollowGroup:RouteToVec3( GDV_Formation, GS ) -- DCS models speed in Mps (Miles per second) FollowGroup:RouteToVec3( GDV_Formation, GS ) -- DCS models speed in Mps (Miles per second)

View File

@ -879,7 +879,7 @@ end
-- @param #DATABASE self -- @param #DATABASE self
-- @param Core.Event#EVENTDATA Event -- @param Core.Event#EVENTDATA Event
function DATABASE:_EventOnBirth( Event ) function DATABASE:_EventOnBirth( Event )
self:F2( { Event } ) self:F( { Event } )
if Event.IniDCSUnit then if Event.IniDCSUnit then
if Event.IniObjectCategory == 3 then if Event.IniObjectCategory == 3 then
@ -896,6 +896,7 @@ function DATABASE:_EventOnBirth( Event )
local PlayerName = Event.IniUnit:GetPlayerName() local PlayerName = Event.IniUnit:GetPlayerName()
if PlayerName then if PlayerName then
self:I( { "Player Joined:", PlayerName } ) self:I( { "Player Joined:", PlayerName } )
self:AddClient( Event.IniDCSUnitName )
if not self.PLAYERS[PlayerName] then if not self.PLAYERS[PlayerName] then
self:AddPlayer( Event.IniUnitName, PlayerName ) self:AddPlayer( Event.IniUnitName, PlayerName )
end end

View File

@ -990,7 +990,7 @@ function EVENT:onEvent( Event )
local PriorityEnd = PriorityOrder == -1 and 1 or 5 local PriorityEnd = PriorityOrder == -1 and 1 or 5
if Event.IniObjectCategory ~= Object.Category.STATIC then if Event.IniObjectCategory ~= Object.Category.STATIC then
self:T( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) self:F( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } )
end end
for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do

View File

@ -2059,7 +2059,7 @@ do -- COORDINATE
-- * Uses default settings in COORDINATE. -- * Uses default settings in COORDINATE.
-- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default. -- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable -- @param Wrapper.Controllable#CONTROLLABLE Controllable The controllable to retrieve the settings from, otherwise the default settings will be chosen.
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
-- @param Tasking.Task#TASK Task The task for which coordinates need to be calculated. -- @param Tasking.Task#TASK Task The task for which coordinates need to be calculated.
-- @return #string The coordinate Text in the configured coordinate system. -- @return #string The coordinate Text in the configured coordinate system.

View File

@ -125,7 +125,7 @@ do -- SET_BASE
self.Index = {} self.Index = {}
self.CallScheduler = SCHEDULER:New( self ) self.CallScheduler = SCHEDULER:New( self )
self:SetEventPriority( 2 ) self:SetEventPriority( 2 )
return self return self
@ -341,6 +341,25 @@ do -- SET_BASE
return self return self
end end
--- Define the SET iterator **"limit"**.
-- @param #SET_BASE self
-- @param #number Limit Defines how many objects are evaluated of the set as part of the Some iterators. The default is 1.
-- @return #SET_BASE self
function SET_BASE:SetSomeIteratorLimit( Limit )
self.SomeIteratorLimit = Limit or 1
return self
end
--- Get the SET iterator **"limit"**.
-- @param #SET_BASE self
-- @return #number Defines how many objects are evaluated of the set as part of the Some iterators.
function SET_BASE:GetSomeIteratorLimit()
return self.SomeIteratorLimit or self:Count()
end
--- Filters for the defined collection. --- Filters for the defined collection.
-- @param #SET_BASE self -- @param #SET_BASE self
@ -590,6 +609,66 @@ do -- SET_BASE
return self return self
end end
--- Iterate the SET_BASE and derived classes and call an iterator function for the given SET_BASE, providing the Object for each element within the set and optional parameters.
-- @param #SET_BASE self
-- @param #function IteratorFunction The function that will be called.
-- @return #SET_BASE self
function SET_BASE:ForSome( IteratorFunction, arg, Set, Function, FunctionArguments )
self:F3( arg )
Set = Set or self:GetSet()
arg = arg or {}
local Limit = self:GetSomeIteratorLimit()
local function CoRoutine()
local Count = 0
for ObjectID, ObjectData in pairs( Set ) do
local Object = ObjectData
self:T3( Object )
if Function then
if Function( unpack( FunctionArguments ), Object ) == true then
IteratorFunction( Object, unpack( arg ) )
end
else
IteratorFunction( Object, unpack( arg ) )
end
Count = Count + 1
if Count >= Limit then
break
end
-- if Count % self.YieldInterval == 0 then
-- coroutine.yield( false )
-- end
end
return true
end
-- local co = coroutine.create( CoRoutine )
local co = CoRoutine
local function Schedule()
-- local status, res = coroutine.resume( co )
local status, res = co()
self:T3( { status, res } )
if status == false then
error( res )
end
if res == false then
return true -- resume next time the loop
end
return false
end
--self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 )
Schedule()
return self
end
----- Iterate the SET_BASE and call an interator function for each **alive** unit, providing the Unit and optional parameters. ----- Iterate the SET_BASE and call an interator function for each **alive** unit, providing the Unit and optional parameters.
@ -845,7 +924,40 @@ do -- SET_GROUP
return AliveSet.Set or {} return AliveSet.Set or {}
end end
--- Returns a report of of unit types.
-- @param #SET_GROUP self
-- @return Core.Report#REPORT A report of the unit types found. The key is the UnitTypeName and the value is the amount of unit types found.
function SET_GROUP:GetUnitTypeNames()
self:F2()
local MT = {} -- Message Text
local UnitTypes = {}
local ReportUnitTypes = REPORT:New()
for GroupID, GroupData in pairs( self:GetSet() ) do
local Units = GroupData:GetUnits()
for UnitID, UnitData in pairs( Units ) do
if UnitData:IsAlive() then
local UnitType = UnitData:GetTypeName()
if not UnitTypes[UnitType] then
UnitTypes[UnitType] = 1
else
UnitTypes[UnitType] = UnitTypes[UnitType] + 1
end
end
end
end
for UnitTypeID, UnitType in pairs( UnitTypes ) do
ReportUnitTypes:Add( UnitType .. " of " .. UnitTypeID )
end
return ReportUnitTypes
end
--- Add a GROUP to SET_GROUP. --- Add a GROUP to SET_GROUP.
-- Note that for each unit in the group that is set, a default cargo bay limit is initialized. -- Note that for each unit in the group that is set, a default cargo bay limit is initialized.
-- @param Core.Set#SET_GROUP self -- @param Core.Set#SET_GROUP self
@ -1144,7 +1256,7 @@ do -- SET_GROUP
--- Iterate the SET_GROUP and call an iterator function for each GROUP object, providing the GROUP and optional parameters. --- Iterate the SET_GROUP and call an iterator function for each GROUP object, providing the GROUP and optional parameters.
-- @param #SET_GROUP self -- @param #SET_GROUP self
-- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @param #function IteratorFunction The function that will be called for all GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
-- @return #SET_GROUP self -- @return #SET_GROUP self
function SET_GROUP:ForEachGroup( IteratorFunction, ... ) function SET_GROUP:ForEachGroup( IteratorFunction, ... )
self:F2( arg ) self:F2( arg )
@ -1154,6 +1266,18 @@ do -- SET_GROUP
return self return self
end end
--- Iterate the SET_GROUP and call an iterator function for some GROUP objects, providing the GROUP and optional parameters.
-- @param #SET_GROUP self
-- @param #function IteratorFunction The function that will be called for some GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
-- @return #SET_GROUP self
function SET_GROUP:ForSomeGroup( IteratorFunction, ... )
self:F2( arg )
self:ForSome( IteratorFunction, arg, self:GetSet() )
return self
end
--- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP object, providing the GROUP and optional parameters. --- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP object, providing the GROUP and optional parameters.
-- @param #SET_GROUP self -- @param #SET_GROUP self
-- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
@ -1166,6 +1290,18 @@ do -- SET_GROUP
return self return self
end end
--- Iterate the SET_GROUP and call an iterator function for some **alive** GROUP objects, providing the GROUP and optional parameters.
-- @param #SET_GROUP self
-- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
-- @return #SET_GROUP self
function SET_GROUP:ForSomeGroupAlive( IteratorFunction, ... )
self:F2( arg )
self:ForSome( IteratorFunction, arg, self:GetAliveSet() )
return self
end
--- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function.
-- @param #SET_GROUP self -- @param #SET_GROUP self
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.

View File

@ -1156,12 +1156,24 @@ function SPAWN:ReSpawn( SpawnIndex )
return SpawnGroup return SpawnGroup
end end
--- Set the spawn index to a specified index number.
-- This method can be used to "reset" the spawn counter to a specific index number.
-- This will actually enable a respawn of groups from the specific index.
-- @param #SPAWN self
-- @param #string SpawnIndex The index of the group from where the spawning will start again. The default value would be 0, which means a complete reset of the spawnindex.
-- @return #SPAWN self
function SPAWN:SetSpawnIndex( SpawnIndex )
self.SpawnIndex = SpawnIndex or 0
end
--- Will spawn a group with a specified index number. --- Will spawn a group with a specified index number.
-- Uses @{DATABASE} global object defined in MOOSE. -- Uses @{DATABASE} global object defined in MOOSE.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #string SpawnIndex The index of the group to be spawned. -- @param #string SpawnIndex The index of the group to be spawned.
-- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions. -- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions.
function SPAWN:SpawnWithIndex( SpawnIndex ) function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
self:F2( { SpawnTemplatePrefix = self.SpawnTemplatePrefix, SpawnIndex = SpawnIndex, AliveUnits = self.AliveUnits, SpawnMaxGroups = self.SpawnMaxGroups } ) self:F2( { SpawnTemplatePrefix = self.SpawnTemplatePrefix, SpawnIndex = SpawnIndex, AliveUnits = self.AliveUnits, SpawnMaxGroups = self.SpawnMaxGroups } )
if self:_GetSpawnIndex( SpawnIndex ) then if self:_GetSpawnIndex( SpawnIndex ) then
@ -1263,14 +1275,16 @@ function SPAWN:SpawnWithIndex( SpawnIndex )
SpawnTemplate.CoalitionID = self.SpawnInitCoalition or SpawnTemplate.CoalitionID SpawnTemplate.CoalitionID = self.SpawnInitCoalition or SpawnTemplate.CoalitionID
if SpawnTemplate.CategoryID == Group.Category.HELICOPTER or SpawnTemplate.CategoryID == Group.Category.AIRPLANE then -- if SpawnTemplate.CategoryID == Group.Category.HELICOPTER or SpawnTemplate.CategoryID == Group.Category.AIRPLANE then
if SpawnTemplate.route.points[1].type == "TakeOffParking" then -- if SpawnTemplate.route.points[1].type == "TakeOffParking" then
SpawnTemplate.uncontrolled = self.SpawnUnControlled -- SpawnTemplate.uncontrolled = self.SpawnUnControlled
end -- end
end -- end
end end
self:HandleEvent( EVENTS.Birth, self._OnBirth ) if not NoBirth then
self:HandleEvent( EVENTS.Birth, self._OnBirth )
end
self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash )
self:HandleEvent( EVENTS.RemoveUnit, self._OnDeadOrCrash ) self:HandleEvent( EVENTS.RemoveUnit, self._OnDeadOrCrash )
@ -1459,12 +1473,23 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
EmergencyAirSpawn=true EmergencyAirSpawn=true
end end
self:F( { SpawnIndex = self.SpawnIndex } )
if self:_GetSpawnIndex( self.SpawnIndex + 1 ) then if self:_GetSpawnIndex( self.SpawnIndex + 1 ) then
-- Get group template. -- Get group template.
local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate
self:F( { SpawnTemplate = SpawnTemplate } )
if SpawnTemplate then 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( self.SpawnIndex )
self:F( { GroupAlive = GroupAlive } )
-- Debug output -- Debug output
self:T( { "Current point of ", self.SpawnTemplatePrefix, SpawnAirbase } ) self:T( { "Current point of ", self.SpawnTemplatePrefix, SpawnAirbase } )
@ -1544,7 +1569,8 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
local spots local spots
-- Spawn happens on ground, i.e. at an airbase, a FARP or a ship. -- Spawn happens on ground, i.e. at an airbase, a FARP or a ship.
if spawnonground then if spawnonground and not SpawnTemplate.parked then
-- Number of free parking spots. -- Number of free parking spots.
local nfree=0 local nfree=0
@ -1714,9 +1740,347 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
end 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:T2('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 = nil
-- Spawn group.
local GroupSpawned = self:SpawnWithIndex( self.SpawnIndex )
-- 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
return GroupSpawned
end
end
return nil
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<nunits then
-- Not enough helo ports. Let's try also other terminal types.
self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterUsable))
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
nfree=#spots
end
else
-- No terminal type specified. We try all spots except shelters.
self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), termtype))
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
nfree=#spots
end
else
-- Fixed wing aircraft is spawned.
if termtype==nil then
--TODO: Add some default cases for transport, bombers etc. if no explicit terminal type is provided.
--TODO: We don't want Bombers to spawn in shelters. But I don't know a good attribute for just fighers.
--TODO: Some attributes are "Helicopters", "Bombers", "Transports", "Battleplanes". Need to check it out.
if isbomber or istransport then
-- First we fill the potentially bigger spots.
self:T(string.format("Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenBig))
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
nfree=#spots
if nfree<nunits then
-- Now we try the smaller ones.
self:T(string.format("Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenMedOrBig))
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMedOrBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
nfree=#spots
end
else
self:T(string.format("Fighter group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.FighterAircraft))
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
nfree=#spots
end
else
-- Terminal type explicitly given.
self:T(string.format("Plane group %s is at %s using terminal type %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), tostring(termtype)))
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
nfree=#spots
end
end
end
-- Get parking data.
local parkingdata=SpawnAirbase:GetParkingSpotsTable(termtype)
self:T2(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype)))
for _,_spot in pairs(parkingdata) do
self:T2(string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d",
SpawnAirbase:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy))
end
self:T(string.format("%s at %s: free parking spots = %d - number of units = %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), nfree, nunits))
-- Set this to true if not enough spots are available for emergency air start.
local _notenough=false
-- Need to differentiate some cases again.
if spawnonship or spawnonfarp or spawnonrunway then
-- On free spot required in these cases.
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. -- Translate the position of the Group Template to the Vec3.
SpawnTemplate.parked = true
for UnitID = 1, nunits do for UnitID = 1, nunits do
self:T2('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) 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. -- Template of the current unit.
local UnitTemplate = SpawnTemplate.units[UnitID] local UnitTemplate = SpawnTemplate.units[UnitID]
@ -1776,33 +2140,85 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
self:T2(string.format("Group %s unit number %d: Parking ID = %s",self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking_id))) 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) 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
-- Spawn group.
local GroupSpawned = self:SpawnWithIndex( self.SpawnIndex )
-- 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
return GroupSpawned
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
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.
--
-- All groups that are in the spawn collection and that are alive, and not in the air, are parked.
--
-- The @{Wrapper.Airbase#AIRBASE} object must refer to a valid airbase known in the sim.
-- You can use the following enumerations to search for the pre-defined airbases on the current known maps of DCS:
--
-- * @{Wrapper.Airbase#AIRBASE.Caucasus}: The airbases on the Caucasus map.
-- * @{Wrapper.Airbase#AIRBASE.Nevada}: The airbases on the Nevada (NTTR) map.
-- * @{Wrapper.Airbase#AIRBASE.Normandy}: The airbases on the Normandy map.
--
-- Use the method @{Wrapper.Airbase#AIRBASE.FindByName}() to retrieve the airbase object.
-- The known AIRBASE objects are automatically imported at mission start by MOOSE.
-- Therefore, there isn't any New() constructor defined for AIRBASE objects.
--
-- Ships and Farps are added within the mission, and are therefore not known.
-- For these AIRBASE objects, there isn't an @{Wrapper.Airbase#AIRBASE} enumeration defined.
-- You need to provide the **exact name** of the airbase as the parameter to the @{Wrapper.Airbase#AIRBASE.FindByName}() method!
--
-- @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!
-- @usage
-- Spawn_Plane = SPAWN:New( "Plane" )
-- Spawn_Plane:ParkAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ) )
--
-- Spawn_Heli = SPAWN:New( "Heli")
--
-- Spawn_Heli:ParkAtAirbase( AIRBASE:FindByName( "FARP Cold" ) )
--
-- Spawn_Heli:ParkAtAirbase( AIRBASE:FindByName( "Carrier" ) )
--
-- Spawn_Plane:ParkAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), AIRBASE.TerminalType.OpenBig )
--
function SPAWN:ParkAtAirbase( SpawnAirbase, TerminalType, Parkingdata ) -- R2.2, R2.4, R2.5
self:F( { self.SpawnTemplatePrefix, SpawnAirbase, TerminalType } )
self:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, 1 )
for SpawnIndex = 2, self.SpawnMaxGroups do
self:ScheduleOnce( SpawnIndex * 0.1, SPAWN.ParkAircraft, self, SpawnAirbase, TerminalType, Parkingdata, SpawnIndex )
end
self:SetSpawnIndex()
return nil return nil
end end
@ -2094,7 +2510,7 @@ end
function SPAWN:InitUnControlled( UnControlled ) function SPAWN:InitUnControlled( UnControlled )
self:F2( { self.SpawnTemplatePrefix, UnControlled } ) self:F2( { self.SpawnTemplatePrefix, UnControlled } )
self.SpawnUnControlled = UnControlled or true self.SpawnUnControlled = ( UnControlled == true ) and true or nil
for SpawnGroupID = 1, self.SpawnMaxGroups do for SpawnGroupID = 1, self.SpawnMaxGroups do
self.SpawnGroups[SpawnGroupID].UnControlled = self.SpawnUnControlled self.SpawnGroups[SpawnGroupID].UnControlled = self.SpawnUnControlled
@ -2428,6 +2844,19 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) --R2.2
SpawnTemplate.units[UnitID].unitId = nil SpawnTemplate.units[UnitID].unitId = nil
end end
end end
-- Callsign
for UnitID = 1, #SpawnTemplate.units do
local Callsign = SpawnTemplate.units[UnitID].callsign
if Callsign[1] ~= nil then -- blue callsign
Callsign[2] = ( ( SpawnIndex - 1 ) % 10 ) + 1
local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string
local CallsignLen = CallsignName:len()
SpawnTemplate.units[UnitID].callsign["name"] = CallsignName:sub(1,CallsignLen) .. SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3]
else
SpawnTemplate.units[UnitID].callsign = Callsign + SpawnIndex
end
end
self:T3( { "Template:", SpawnTemplate } ) self:T3( { "Template:", SpawnTemplate } )
return SpawnTemplate return SpawnTemplate

View File

@ -305,7 +305,7 @@ do -- DETECTION_BASE
--- DETECTION constructor. --- DETECTION constructor.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param Core.Set#SET_BASE DetectionSet The @{Set} that is used to detect the units. -- @param Core.Set#SET_GROUP DetectionSet The @{Set} of @{Group}s that is used to detect the units.
-- @return #DETECTION_BASE self -- @return #DETECTION_BASE self
function DETECTION_BASE:New( DetectionSet ) function DETECTION_BASE:New( DetectionSet )
@ -542,12 +542,16 @@ do -- DETECTION_BASE
end end
self.DetectionCount = self.DetectionSet:Count() self.DetectionCount = self.DetectionSet:Count()
for DetectionID, DetectionData in pairs( self.DetectionSet:GetSet() ) do
--self:F( { DetectionGroupData } ) self.DetectionSet:ForEachGroupAlive(
self:F( { DetectionGroup = DetectionData:GetName() } ) function( DetectionGroup )
self:__Detection( DetectDelay, DetectionData, DetectionTimeStamp ) -- Process each detection asynchronously. self:__Detection( DetectDelay, DetectionGroup, DetectionTimeStamp ) -- Process each detection asynchronously.
DetectDelay = DetectDelay + 1 DetectDelay = DetectDelay + 1
end end
)
self:__Detect( -self.RefreshTimeInterval )
end end
--- @param #DETECTION_BASE self --- @param #DETECTION_BASE self
@ -802,10 +806,9 @@ do -- DETECTION_BASE
self:__DetectedItem( 0.1, DetectedItem ) self:__DetectedItem( 0.1, DetectedItem )
end end
end end
self:__Detect( self.RefreshTimeInterval )
end end
end end
@ -2502,7 +2505,6 @@ do -- DETECTION_AREAS
local Report = REPORT:New() local Report = REPORT:New()
Report:Add( DetectedItemID ) Report:Add( DetectedItemID )
Report:Add( string.format( "Threat: [%s%s]", string.rep( "", ThreatLevelA2G ), string.rep( "", 10-ThreatLevelA2G ) ) ) Report:Add( string.format( "Threat: [%s%s]", string.rep( "", ThreatLevelA2G ), string.rep( "", 10-ThreatLevelA2G ) ) )
Report:Add( DetectedItemCoordText )
return Report return Report
end end

View File

@ -1,3 +1,4 @@
__Moose.Include( 'Scripts/Moose/Utilities/Enums.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Routines.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/Routines.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
@ -86,6 +87,7 @@ __Moose.Include( 'Scripts/Moose/AI/AI_Cas.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Bai.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Bai.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Formation.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Formation.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Escort.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Escort.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Request.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Cargo.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_APC.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Cargo_APC.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Helicopter.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Helicopter.lua' )

View File

@ -0,0 +1,15 @@
ENUMS = {}
ENUMS.ROE = {
HoldFire = 1,
ReturnFire = 2,
OpenFire = 3,
WeaponFree = 4
}
ENUMS.ROT = {
NoReaction = 1,
PassiveDefense = 2,
EvadeFire = 3,
Vertical = 4
}

View File

@ -553,12 +553,15 @@ function AIRBASE:GetParkingSpotsTable(termtype)
local spots={} local spots={}
for _,_spot in pairs(parkingdata) do for _,_spot in pairs(parkingdata) do
if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then
self:I({_spot=_spot})
local _free=_isfree(_spot) local _free=_isfree(_spot)
local _coord=COORDINATE:NewFromVec3(_spot.vTerminalPos) 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}) 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
end end
self:I({ spots = spots } )
return spots return spots
end end
@ -708,6 +711,8 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE
local _termid=parkingspot.TerminalID local _termid=parkingspot.TerminalID
self:I({_termid=_termid})
if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) then 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. -- 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.
@ -787,7 +792,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
--_spot:MarkToAll(string.format("Parking spot %d free=%s", parkingspot.TerminalID, tostring(not occupied))) --_spot:MarkToAll(string.format("Parking spot %d free=%s", parkingspot.TerminalID, tostring(not occupied)))
if occupied then 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 else
self:I(string.format("%s: Parking spot id %d free.", airport, _termid)) self:I(string.format("%s: Parking spot id %d free.", airport, _termid))
if nvalid<_nspots then if nvalid<_nspots then