Merge pull request #936 from FlightControl-Master/FF/Develop

No conflicts.
This commit is contained in:
thebgpikester 2018-06-28 20:54:53 +01:00 committed by GitHub
commit 1a4370d433
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1984 additions and 468 deletions

View File

@ -66,6 +66,7 @@ local _DATABASECoalition =
{
[1] = "Red",
[2] = "Blue",
[3] = "Neutral",
}
local _DATABASECategory =
@ -116,7 +117,7 @@ function DATABASE:New()
--- @param #DATABASE self
local function CheckPlayers( self )
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ), AlivePlayersNeutral = coalition.getPlayers( coalition.side.NEUTRAL )}
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
--self:E( { "CoalitionData:", CoalitionData } )
for UnitId, UnitData in pairs( CoalitionData ) do
@ -741,7 +742,7 @@ end
-- @return #DATABASE self
function DATABASE:_RegisterPlayers()
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ), AlivePlayersNeutral = coalition.getPlayers( coalition.side.NEUTRAL ) }
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
for UnitId, UnitData in pairs( CoalitionData ) do
self:T3( { "UnitData:", UnitData } )
@ -765,7 +766,7 @@ end
-- @return #DATABASE self
function DATABASE:_RegisterGroupsAndUnits()
local CoalitionsData = { GroupsRed = coalition.getGroups( coalition.side.RED ), GroupsBlue = coalition.getGroups( coalition.side.BLUE ) }
local CoalitionsData = { GroupsRed = coalition.getGroups( coalition.side.RED ), GroupsBlue = coalition.getGroups( coalition.side.BLUE ), GroupsNeutral = coalition.getGroups( coalition.side.NEUTRAL ) }
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
for DCSGroupId, DCSGroup in pairs( CoalitionData ) do
@ -1176,11 +1177,19 @@ function DATABASE:_RegisterTemplates()
self.UNITS = {}
--Build routines.db.units and self.Navpoints
for CoalitionName, coa_data in pairs(env.mission.coalition) do
self:T({CoalitionName=CoalitionName})
if (CoalitionName == 'red' or CoalitionName == 'blue') and type(coa_data) == 'table' then
if (CoalitionName == 'red' or CoalitionName == 'blue' or CoalitionName == 'neutrals') and type(coa_data) == 'table' then
--self.Units[coa_name] = {}
local CoalitionSide = coalition.side[string.upper(CoalitionName)]
if CoalitionName=="red" then
CoalitionSide=coalition.side.NEUTRAL
elseif CoalitionName=="blue" then
CoalitionSide=coalition.side.BLUE
else
CoalitionSide=coalition.side.NEUTRAL
end
-- build nav points DB
self.Navpoints[CoalitionName] = {}

View File

@ -292,7 +292,107 @@ do -- COORDINATE
return x - Precision <= self.x and x + Precision >= self.x and z - Precision <= self.z and z + Precision >= self.z
end
--- Returns if the 2 coordinates are at the same 2D position.
-- @param #COORDINATE self
-- @param #number radius (Optional) Scan radius in meters. Default 100 m.
-- @param #boolean scanunits (Optional) If true scan for units. Default true.
-- @param #boolean scanstatics (Optional) If true scan for static objects. Default true.
-- @param #boolean scanscenery (Optional) If true scan for scenery objects. Default false.
-- @return True if units were found.
-- @return True if statics were found.
-- @return True if scenery objects were found.
-- @return Unit objects found.
-- @return Static objects found.
-- @return Scenery objects found.
function COORDINATE:ScanObjects(radius, scanunits, scanstatics, scanscenery)
self:F(string.format("Scanning in radius %.1f m.", radius))
local SphereSearch = {
id = world.VolumeType.SPHERE,
params = {
point = self:GetVec3(),
radius = radius,
}
}
-- Defaults
radius=radius or 100
if scanunits==nil then
scanunits=true
end
if scanstatics==nil then
scanstatics=true
end
if scanscenery==nil then
scanscenery=false
end
--{Object.Category.UNIT, Object.Category.STATIC, Object.Category.SCENERY}
local scanobjects={}
if scanunits then
table.insert(scanobjects, Object.Category.UNIT)
end
if scanstatics then
table.insert(scanobjects, Object.Category.STATIC)
end
if scanscenery then
table.insert(scanobjects, Object.Category.SCENERY)
end
-- Found stuff.
local Units = {}
local Statics = {}
local Scenery = {}
local gotstatics=false
local gotunits=false
local gotscenery=false
local function EvaluateZone( ZoneObject )
if ZoneObject then
-- Get category of scanned object.
local ObjectCategory = ZoneObject:getCategory()
-- Check for unit or static objects
--if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive()) then
if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist()) then
table.insert(Units, ZoneObject)
gotunits=true
elseif (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
table.insert(Statics, ZoneObject)
gotstatics=true
elseif ObjectCategory == Object.Category.SCENERY then
table.insert(Scenery, ZoneObject)
gotscenery=true
end
end
return true
end
-- Search the world.
world.searchObjects(scanobjects, SphereSearch, EvaluateZone)
for _,unit in pairs(Units) do
self:T(string.format("Scan found unit %s", unit:getName()))
end
for _,static in pairs(Statics) do
self:T(string.format("Scan found static %s", static:getName()))
end
for _,scenery in pairs(Scenery) do
self:T(string.format("Scan found scenery %s", scenery:getTypeName()))
end
return gotunits, gotstatics, gotscenery, Units, Statics, Scenery
end
--- Calculate the distance from a reference @{#COORDINATE}.
-- @param #COORDINATE self
@ -946,6 +1046,84 @@ do -- COORDINATE
return RoutePoint
end
--- Gets the nearest parking spot.
-- @param #COORDINATE self
-- @param Wrapper.Airbase#AIRBASE airbase (Optional) Search only parking spots at this airbase.
-- @param Wrapper.Airbase#Terminaltype terminaltype (Optional) Type of the terminal. Default any execpt valid spawn points on runway.
-- @param #boolean free (Optional) If true, returns the closest free spot. If false, returns the closest occupied spot. If nil, returns the closest spot regardless of free or occupied.
-- @return Core.Point#COORDINATE Coordinate of the nearest parking spot.
-- @return #number Terminal ID.
-- @return #number Distance to closest parking spot in meters.
function COORDINATE:GetClosestParkingSpot(airbase, terminaltype, free)
-- Get airbase table.
local airbases={}
if airbase then
table.insert(airbases,airbase)
else
airbases=AIRBASE.GetAllAirbases()
end
-- Init.
local _closest=nil --Core.Point#COORDINATE
local _termID=nil
local _distmin=nil
-- Loop over all airbases.
for _,_airbase in pairs(airbases) do
local mybase=_airbase --Wrapper.Airbase#AIRBASE
local parkingdata=mybase:GetParkingSpotsTable(terminaltype)
for _,_spot in pairs(parkingdata) do
-- Check for parameters.
if (free==true and _spot.Free==true) or (free==false and _spot.Free==false) or free==nil then
local _coord=_spot.Coordinate --Core.Point#COORDINATE
local _dist=self:Get2DDistance(_coord)
if _distmin==nil then
_closest=_coord
_distmin=_dist
_termID=_spot.TerminalID
else
if _dist<_distmin then
_distmin=_dist
_closest=_coord
_termID=_spot.TerminalID
end
end
end
end
end
return _closest, _termID, _distmin
end
--- Gets the nearest free parking spot.
-- @param #COORDINATE self
-- @param Wrapper.Airbase#AIRBASE airbase (Optional) Search only parking spots at that airbase.
-- @param Wrapper.Airbase#Terminaltype terminaltype (Optional) Type of the terminal.
-- @return #COORDINATE Coordinate of the nearest free parking spot.
-- @return #number Terminal ID.
-- @return #number Distance to closest free parking spot in meters.
function COORDINATE:GetClosestFreeParkingSpot(airbase, terminaltype)
return self:GetClosestParkingSpot(airbase, terminaltype, true)
end
--- Gets the nearest occupied parking spot.
-- @param #COORDINATE self
-- @param Wrapper.Airbase#AIRBASE airbase (Optional) Search only parking spots at that airbase.
-- @param Wrapper.Airbase#Terminaltype terminaltype (Optional) Type of the terminal.
-- @return #COORDINATE Coordinate of the nearest occupied parking spot.
-- @return #number Terminal ID.
-- @return #number Distance to closest occupied parking spot in meters.
function COORDINATE:GetClosestOccupiedParkingSpot(airbase, terminaltype)
return self:GetClosestParkingSpot(airbase, terminaltype, false)
end
--- Gets the nearest coordinate to a road.
-- @param #COORDINATE self
-- @return #COORDINATE Coordinate of the nearest road.

View File

@ -1218,8 +1218,9 @@ end
-- @param Wrapper.Airbase#AIRBASE SpawnAirbase The @{Wrapper.Airbase} where to spawn the group.
-- @param #SPAWN.Takeoff Takeoff (optional) The location and takeoff method. Default is Hot.
-- @param #number TakeoffAltitude (optional) The altitude above the ground.
-- @return Wrapper.Group#GROUP that was spawned.
-- @return #nil Nothing was spawned.
-- @param Wrapper.Airbase#AIRBASE.TerminalType TerminalType (optional) The terminal type the aircraft should be spawned at. See @{Wrapper.Airbase#AIRBASE.TerminalType}.
-- @param #boolean EmergencyAirSpawn (optional) If true (default), groups are spawned in air if there is no parking spot at the airbase. If false, nothing is spawned if no parking spot is available.
-- @return Wrapper.Group#GROUP that was spawned or nil when nothing was spawned.
-- @usage
-- Spawn_Plane = SPAWN:New( "Plane" )
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold )
@ -1237,33 +1238,55 @@ end
--
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "Carrier" ), SPAWN.Takeoff.Cold )
--
function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude ) -- R2.2
self:F( { self.SpawnTemplatePrefix, SpawnAirbase, Takeoff, TakeoffAltitude } )
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold, nil, AIRBASE.TerminalType.OpenBig )
--
function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType, EmergencyAirSpawn ) -- R2.2, R2.4
self:F( { self.SpawnTemplatePrefix, SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType } )
local PointVec3 = SpawnAirbase:GetPointVec3()
-- Get position of airbase.
local PointVec3 = SpawnAirbase:GetCoordinate()
self:T2(PointVec3)
-- Set take off type. Default is hot.
Takeoff = Takeoff or SPAWN.Takeoff.Hot
-- By default, groups are spawned in air if no parking spot is available.
if EmergencyAirSpawn==nil then
EmergencyAirSpawn=true
end
if self:_GetSpawnIndex( self.SpawnIndex + 1 ) then
-- Get group template.
local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate
if SpawnTemplate then
-- 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")
-- First waypoint of the group.
local SpawnPoint = SpawnTemplate.route.points[1]
-- These are only for ships.
-- 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
@ -1274,63 +1297,246 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude ) -- R2.2
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 then
-- Number of free parking spots.
local nfree=0
-- Set terminal type.
local termtype=TerminalType
if spawnonrunway then
termtype=AIRBASE.TerminalType.Runway
end
-- 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, spawnonship or spawnonfarp or spawnonrunway)
spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway)
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)
nfree=#spots
if nfree<#SpawnTemplate.units 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)
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)
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)
nfree=#spots
if nfree<#SpawnTemplate.units 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)
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)
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)
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, #SpawnTemplate.units))
-- Put parking spots in table. These spots are only used if spawing at an airbase.
if nfree >= #SpawnTemplate.units or (spawnonrunway and nfree>0) then
for i=1,#SpawnTemplate.units do
table.insert(parkingspots, spots[i].Coordinate)
table.insert(parkingindex, spots[i].TerminalID)
end
else
if EmergencyAirSpawn and not self.SpawnUnControlled then
self:E(string.format("WARNING: Group %s has no parking spots at %s ==> air start!", self.SpawnTemplatePrefix, SpawnAirbase:GetName()))
-- Not enough parking spots at the airport ==> Spawn in air.
spawnonground=false
spawnonship=false
spawnonfarp=false
spawnonrunway=false
-- Set waypoint type/action to turning point.
SpawnPoint.type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point
SpawnPoint.action = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -- action = Turning Point
-- Adjust altitude to be 500-1000 m above the airbase.
PointVec3.x=PointVec3.x+math.random(-500,500)
PointVec3.z=PointVec3.z+math.random(-500,500)
if ishelo then
PointVec3.y=PointVec3:GetLandHeight()+math.random(100,1000)
else
-- Randomize position so that multiple AC wont be spawned on top even in air.
PointVec3.y=PointVec3:GetLandHeight()+math.random(500,2500)
end
Takeoff=GROUP.Takeoff.Air
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
end
-- Translate the position of the Group Template to the Vec3.
for UnitID = 1, #SpawnTemplate.units do
self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
self:T2('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y)
-- These cause a lot of confusion.
-- Template of the current unit.
local UnitTemplate = SpawnTemplate.units[UnitID]
UnitTemplate.parking = nil
UnitTemplate.parking_id = nil
UnitTemplate.alt = 0
-- Tranlate position and preserve the relative position/formation of all aircraft.
local SX = UnitTemplate.x
local SY = UnitTemplate.y
local BX = SpawnPoint.x
local BY = SpawnPoint.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)
UnitTemplate.x = TX
UnitTemplate.y = TY
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
if Takeoff == GROUP.Takeoff.Air then
UnitTemplate.alt = PointVec3.y + ( TakeoffAltitude or 200 )
--else
-- UnitTemplate.alt = PointVec3.y + 10
end
self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y )
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
-- Set gereral spawnpoint position.
SpawnPoint.x = PointVec3.x
SpawnPoint.y = PointVec3.z
if Takeoff == GROUP.Takeoff.Air then
SpawnPoint.alt = PointVec3.y + ( TakeoffAltitude or 200 )
--else
-- SpawnPoint.alt = PointVec3.y + 10
end
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
-- 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() } , 1 )
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
@ -1338,8 +1544,6 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude ) -- R2.2
return nil
end
--- Will spawn a group from a Vec3 in 3D space.
-- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes.
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
@ -1801,7 +2005,7 @@ end
--- Get the index from a given group.
-- The function will search the name of the group for a #, and will return the number behind the #-mark.
function SPAWN:GetSpawnIndexFromGroup( SpawnGroup )
self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } )
self:F2( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } )
local IndexString = string.match( SpawnGroup:GetName(), "#(%d*)$" ):sub( 2 )
local Index = tonumber( IndexString )

View File

@ -155,6 +155,7 @@ do -- country
-- @field UK
-- @field FRANCE
-- @field GERMANY
-- @field AGGRESSORS
-- @field CANADA
-- @field SPAIN
-- @field THE_NETHERLANDS
@ -167,6 +168,60 @@ do -- country
-- @field ABKHAZIA
-- @field SOUTH_OSETIA
-- @field ITALY
-- @field AUSTRALIA
-- @field SWITZERLAND
-- @field AUSTRIA
-- @field BELARUS
-- @field BULGARIA
-- @field CHEZH_REPUBLIC
-- @field CHINA
-- @field CROATIA
-- @field EGYPT
-- @field FINLAND
-- @field GREECE
-- @field HUNGARY
-- @field INDIA
-- @field IRAN
-- @field IRAQ
-- @field JAPAN
-- @field KAZAKHSTAN
-- @field NORTH_KOREA
-- @field PAKISTAN
-- @field POLAND
-- @field ROMANIA
-- @field SAUDI_ARABIA
-- @field SERBIA
-- @field SLOVAKIA
-- @field SOUTH_KOREA
-- @field SWEDEN
-- @field SYRIA
-- @field YEMEN
-- @field VIETNAM
-- @field VENEZUELA
-- @field TUNISIA
-- @field THAILAND
-- @field SUDAN
-- @field PHILIPPINES
-- @field MOROCCO
-- @field MEXICO
-- @field MALAYSIA
-- @field LIBYA
-- @field JORDAN
-- @field INDONESIA
-- @field HONDURAS
-- @field ETHIOPIA
-- @field CHILE
-- @field BRAZIL
-- @field BAHRAIN
-- @field THIRDREICH
-- @field YUGOSLAVIA
-- @field USSR
-- @field ITALIAN_SOCIAL_REPUBLIC
-- @field ALGERIA
-- @field KUWAIT
-- @field QATAR
-- @field OMAN
-- @field UNITED_ARAB_EMIRATES
country = {} -- #country

File diff suppressed because it is too large Load Diff

View File

@ -496,3 +496,99 @@ function UTILS.BeaufortScale(speed)
end
return bn,bd
end
--- Split string at seperators. C.f. http://stackoverflow.com/questions/1426954/split-string-in-lua
-- @param #string str Sting to split.
-- @param #string sep Speparator for split.
-- @return #table Split text.
function UTILS.Split(str, sep)
local result = {}
local regex = ("([^%s]+)"):format(sep)
for each in str:gmatch(regex) do
table.insert(result, each)
end
return result
end
--- Convert time in seconds to hours, minutes and seconds.
-- @param #number seconds Time in seconds, e.g. from timer.getAbsTime() function.
-- @return #string Time in format Hours:Minutes:Seconds+Days (HH:MM:SS+D).
function UTILS.SecondsToClock(seconds)
-- Nil check.
if seconds==nil then
return nil
end
-- Seconds
local seconds = tonumber(seconds)
-- Seconds of this day.
local _seconds=seconds%(60*60*24)
if seconds <= 0 then
return nil
else
local hours = string.format("%02.f", math.floor(_seconds/3600))
local mins = string.format("%02.f", math.floor(_seconds/60 - (hours*60)))
local secs = string.format("%02.f", math.floor(_seconds - hours*3600 - mins *60))
local days = string.format("%d", seconds/(60*60*24))
return hours..":"..mins..":"..secs.."+"..days
end
end
--- Convert clock time from hours, minutes and seconds to seconds.
-- @param #string clock String of clock time. E.g., "06:12:35" or "5:1:30+1". Format is (H)H:(M)M:((S)S)(+D) H=Hours, M=Minutes, S=Seconds, D=Days.
-- @param #number Seconds. Corresponds to what you cet from timer.getAbsTime() function.
function UTILS.ClockToSeconds(clock)
-- Nil check.
if clock==nil then
return nil
end
-- Seconds init.
local seconds=0
-- Split additional days.
local dsplit=UTILS.split(clock, "+")
-- Convert days to seconds.
if #dsplit>1 then
seconds=seconds+tonumber(dsplit[2])*60*60*24
end
-- Split hours, minutes, seconds
local tsplit=UTILS.Split(dsplit[1], ":")
-- Get time in seconds
local i=1
for _,time in ipairs(tsplit) do
if i==1 then
-- Hours
seconds=seconds+tonumber(time)*60*60
elseif i==2 then
-- Minutes
seconds=seconds+tonumber(time)*60
elseif i==3 then
-- Seconds
seconds=seconds+tonumber(time)
end
i=i+1
end
return seconds
end
--- Display clock and mission time on screen as a message to all.
-- @param #number duration Duration in seconds how long the time is displayed. Default is 5 seconds.
function UTILS.DisplayMissionTime(duration)
duration=duration or 5
local Tnow=timer.getAbsTime()
local mission_time=Tnow-timer.getTime0()
local mission_time_minutes=mission_time/60
local mission_time_seconds=mission_time%60
local local_time=UTILS.SecondsToClock(Tnow)
local text=string.format("Time: %s - %02d:%02d", local_time, mission_time_minutes, mission_time_seconds)
MESSAGE:New(text, duration):ToAll()
end

View File

@ -107,9 +107,7 @@ AIRBASE.Caucasus = {
["Beslan"] = "Beslan",
}
--- @field Nevada
--
-- These are all airbases of Nevada:
--- These are all airbases of Nevada:
--
-- * AIRBASE.Nevada.Creech_AFB
-- * AIRBASE.Nevada.Groom_Lake_AFB
@ -129,7 +127,7 @@ AIRBASE.Caucasus = {
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
-- * AIRBASE.Nevada.Tonopah_Airport
-- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield
--
-- @field Nevada
AIRBASE.Nevada = {
["Creech_AFB"] = "Creech AFB",
["Groom_Lake_AFB"] = "Groom Lake AFB",
@ -151,9 +149,7 @@ AIRBASE.Nevada = {
["Tonopah_Test_Range_Airfield"] = "Tonopah Test Range Airfield",
}
--- @field Normandy
--
-- These are all airbases of Normandy:
--- These are all airbases of Normandy:
--
-- * AIRBASE.Normandy.Saint_Pierre_du_Mont
-- * AIRBASE.Normandy.Lignerolles
@ -186,6 +182,7 @@ AIRBASE.Nevada = {
-- * AIRBASE.Normandy.Funtington
-- * AIRBASE.Normandy.Tangmere
-- * AIRBASE.Normandy.Ford
-- @field Normandy
AIRBASE.Normandy = {
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
["Lignerolles"] = "Lignerolles",
@ -220,6 +217,70 @@ AIRBASE.Normandy = {
["Ford"] = "Ford",
}
--- These are all airbases of the Persion Gulf Map:
--
-- * AIRBASE.PersianGulf.Fujairah_Intl
-- * AIRBASE.PersianGulf.Qeshm_Island
-- * AIRBASE.PersianGulf.Sir_Abu_Nuayr
-- * AIRBASE.PersianGulf.Abu_Musa_Island_Airport
-- * AIRBASE.PersianGulf.Bandar_Abbas_Intl
-- * AIRBASE.PersianGulf.Bandar_Lengeh
-- * AIRBASE.PersianGulf.Tunb_Island_AFB
-- * AIRBASE.PersianGulf.Havadarya
-- * AIRBASE.PersianGulf.Lar_Airbase
-- * AIRBASE.PersianGulf.Sirri_Island
-- * AIRBASE.PersianGulf.Tunb_Kochak
-- * AIRBASE.PersianGulf.Al_Dhafra_AB
-- * AIRBASE.PersianGulf.Dubai_Intl
-- * AIRBASE.PersianGulf.Al_Maktoum_Intl
-- * AIRBASE.PersianGulf.Khasab
-- * AIRBASE.PersianGulf.Al_Minhad_AB
-- * AIRBASE.PersianGulf.Sharjah_Intl
-- @field PersianGulf
AIRBASE.PersianGulf = {
["Fujairah_Intl"] = "Fujairah Intl",
["Qeshm_Island"] = "Qeshm Island",
["Sir_Abu_Nuayr"] = "Sir Abu Nuayr",
["Abu_Musa_Island_Airport"] = "Abu Musa Island Airport",
["Bandar_Abbas_Intl"] = "Bandar Abbas Intl",
["Bandar_Lengeh"] = "Bandar Lengeh",
["Tunb_Island_AFB"] = "Tunb Island AFB",
["Havadarya"] = "Havadarya",
["Lar_Airbase"] = "Lar Airbase",
["Sirri_Island"] = "Sirri Island",
["Tunb_Kochak"] = "Tunb Kochak",
["Al_Dhafra_AB"] = "Al Dhafra AB",
["Dubai_Intl"] = "Dubai Intl",
["Al_Maktoum_Intl"] = "Al Maktoum Intl",
["Khasab"] = "Khasab",
["Al_Minhad_AB"] = "Al Minhad AB",
["Sharjah_Intl"] = "Sharjah Intl",
}
--- Terminal Types of parking spots. See also https://wiki.hoggitworld.com/view/DCS_func_getParking
--
-- Supported types are:
--
-- * AIRBASE.TerminalType.Runway: Valid spawn points on runway.
-- * AIRBASE.TerminalType.HelicopterOnly: Special spots for Helicopers.
-- * AIRBASE.TerminalType.Shelter: Hardened Air Shelter. Currently only on Caucaus map.
-- * AIRBASE.TerminalType.OpenMed: Open/Shelter air airplane only.
-- * AIRBASE.TerminalType.OpenBig: Open air spawn points. Generally larger but does not guarantee large aircraft are capable of spawning there.
-- * AIRBASE.TerminalType.OpenMedOrBig: Combines OpenMed and OpenBig spots.
-- * AIRBASE.TerminalType.HelicopterUnsable: Combines HelicopterOnly, OpenMed and OpenBig.
-- * AIRBASE.TerminalType.FighterAircraft: Combines Shelter. OpenMed and OpenBig spots. So effectively all spots usable by fixed wing aircraft.
-- @field TerminalType
AIRBASE.TerminalType = {
Runway=16,
HelicopterOnly=40,
Shelter=68,
OpenMed=72,
OpenBig=104,
OpenMedOrBig=176,
HelicopterUsable=216,
FighterAircraft=244,
}
-- Registration.
--- Create a new AIRBASE from DCSAirbase.
@ -257,6 +318,9 @@ function AIRBASE:FindByName( AirbaseName )
return AirbaseFound
end
--- Get the DCS object of an airbase
-- @param #AIRBASE self
-- @return DCS#Airbase DCS airbase object.
function AIRBASE:GetDCSObject()
local DCSAirbase = Airbase.getByName( self.AirbaseName )
@ -274,5 +338,548 @@ function AIRBASE:GetZone()
return self.AirbaseZone
end
--- Get all airbases of the current map. This includes ships and FARPS.
-- @param DCS#Coalition coalition (Optional) Return only airbases belonging to the specified coalition. By default, all airbases of the map are returned.
-- @return #table Table containing all airbase objects of the current map.
function AIRBASE.GetAllAirbases(coalition)
local airbases={}
for _,airbase in pairs(_DATABASE.AIRBASES) do
if (coalition~=nil and self:GetCoalition()==coalition) or coalition==nil then
table.insert(airbases, airbase)
end
end
return airbases
end
--- Returns a table of parking data for a given airbase. If the optional parameter *available* is true only available parking will be returned, otherwise all parking at the base is returned. Term types have the following enumerated values:
--
-- * 16 : Valid spawn points on runway
-- * 40 : Helicopter only spawn
-- * 68 : Hardened Air Shelter
-- * 72 : Open/Shelter air airplane only
-- * 104: Open air spawn
--
-- Note that only Caucuses will return 68 as it is the only map currently with hardened air shelters.
-- 104 are also generally larger, but does not guarantee a large aircraft like the B-52 or a C-130 are capable of spawning there.
--
-- Table entries:
--
-- * Term_index is the id for the parking
-- * vTerminal pos is its vec3 position in the world
-- * fDistToRW is the distance to the take-off position for the active runway from the parking.
--
-- @param #AIRBASE self
-- @param #boolean available If true, only available parking spots will be returned.
-- @return #table Table with parking data. See https://wiki.hoggitworld.com/view/DCS_func_getParking
function AIRBASE:GetParkingData(available)
self:F2(available)
-- Get DCS airbase object.
local DCSAirbase=self:GetDCSObject()
-- Get parking data.
local parkingdata=nil
if DCSAirbase then
parkingdata=DCSAirbase:getParking(available)
end
self:T2({parkingdata=parkingdata})
return parkingdata
end
--- Get number of parking spots at an airbase. Optionally, a specific terminal type can be requested.
-- @param #AIRBASE self
-- @param #AIRBASE.TerminalType termtype Terminal type of which the number of spots is counted. Default all spots but spawn points on runway.
-- @return #number Number of parking spots at this airbase.
function AIRBASE:GetParkingSpotsNumber(termtype)
-- Get free parking spots data.
local parkingdata=self:GetParkingData(false)
local nspots=0
for _,parkingspot in pairs(parkingdata) do
if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then
nspots=nspots+1
end
end
return nspots
end
--- Get number of free parking spots at an airbase.
-- @param #AIRBASE self
-- @param #AIRBASE.TerminalType termtype Terminal type.
-- @param #boolean allowTOAC If true, spots are considered free even though TO_AC is true. Default is off which is saver to avoid spawning aircraft on top of each other. Option might be enabled for FARPS and ships.
-- @return #number Number of free parking spots at this airbase.
function AIRBASE:GetFreeParkingSpotsNumber(termtype, allowTOAC)
-- Get free parking spots data.
local parkingdata=self:GetParkingData(true)
local nfree=0
for _,parkingspot in pairs(parkingdata) do
-- Spots on runway are not counted unless explicitly requested.
if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then
if (allowTOAC and allowTOAC==true) or parkingspot.TO_AC==false then
nfree=nfree+1
end
end
end
return nfree
end
--- Get the coordinates of free parking spots at an airbase.
-- @param #AIRBASE self
-- @param #AIRBASE.TerminalType termtype Terminal type.
-- @param #boolean allowTOAC If true, spots are considered free even though TO_AC is true. Default is off which is saver to avoid spawning aircraft on top of each other. Option might be enabled for FARPS and ships.
-- @return #table Table of coordinates of the free parking spots.
function AIRBASE:GetFreeParkingSpotsCoordinates(termtype, allowTOAC)
-- Get free parking spots data.
local parkingdata=self:GetParkingData(true)
-- Put coordinates of free spots into table.
local spots={}
for _,parkingspot in pairs(parkingdata) do
-- Coordinates on runway are not returned unless explicitly requested.
if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then
if (allowTOAC and allowTOAC==true) or parkingspot.TO_AC==false then
table.insert(spots, COORDINATE:NewFromVec3(parkingspot.vTerminalPos))
end
end
end
return spots
end
--- Get the coordinates of all parking spots at an airbase. Optionally only those of a specific terminal type. Spots on runways are excluded if not explicitly requested by terminal type.
-- @param #AIRBASE self
-- @param #AIRBASE.TerminalType termtype (Optional) Terminal type. Default all.
-- @return #table Table of coordinates of parking spots.
function AIRBASE:GetParkingSpotsCoordinates(termtype)
-- Get all parking spots data.
local parkingdata=self:GetParkingData(false)
-- Put coordinates of free spots into table.
local spots={}
for _,parkingspot in pairs(parkingdata) do
-- Coordinates on runway are not returned unless explicitly requested.
if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then
-- Get coordinate from Vec3 terminal position.
local _coord=COORDINATE:NewFromVec3(parkingspot.vTerminalPos)
-- Add to table.
table.insert(spots, _coord)
end
end
return spots
end
--- Get a table containing the coordinates, terminal index and terminal type of free parking spots at an airbase.
-- @param #AIRBASE self
-- @param #AIRBASE.TerminalType termtype Terminal type.
-- @return #table Table free parking spots. Table has the elements ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
function AIRBASE:GetParkingSpotsTable(termtype)
-- Get parking data of all spots (free or occupied)
local parkingdata=self:GetParkingData(false)
-- Get parking data of all free spots.
local parkingfree=self:GetParkingData(true)
-- Function to ckeck if any parking spot is free.
local function _isfree(_tocheck)
for _,_spot in pairs(parkingfree) do
if _spot.Term_Index==_tocheck.Term_Index then
return true
end
end
return false
end
-- Put coordinates of parking spots into table.
local spots={}
for _,_spot in pairs(parkingdata) do
if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then
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
return spots
end
--- Get a table containing the coordinates, terminal index and terminal type of free parking spots at an airbase.
-- @param #AIRBASE self
-- @param #AIRBASE.TerminalType termtype Terminal type.
-- @param #boolean allowTOAC If true, spots are considered free even though TO_AC is true. Default is off which is saver to avoid spawning aircraft on top of each other. Option might be enabled for FARPS and ships.
-- @return #table Table free parking spots. Table has the elements ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC)
-- Get parking data of all free spots.
local parkingfree=self:GetParkingData(true)
-- Put coordinates of free spots into table.
local freespots={}
for _,_spot in pairs(parkingfree) do
if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then
if (allowTOAC and allowTOAC==true) or _spot.TO_AC==false then
local _coord=COORDINATE:NewFromVec3(_spot.vTerminalPos)
table.insert(freespots, {Coordinate=_coord, TerminalID=_spot.Term_Index, TerminalType=_spot.Term_Type, TOAC=_spot.TO_AC, Free=true, TerminalID0=_spot.Term_Index_0, DistToRwy=_spot.fDistToRW})
end
end
end
return freespots
end
--- Place markers of parking spots on the F10 map.
-- @param #AIRBASE self
-- @param #AIRBASE.TerminalType termtype Terminal type for which marks should be placed.
-- @param #boolean mark If false, do not place markers but only give output to DCS.log file. Default true.
function AIRBASE:MarkParkingSpots(termtype, mark)
-- Default is true.
if mark==nil then
mark=true
end
-- Get parking data from getParking() wrapper function.
local parkingdata=self:GetParkingSpotsTable(termtype)
-- Get airbase name.
local airbasename=self:GetName()
self:E(string.format("Parking spots at %s for termial type %s:", airbasename, tostring(termtype)))
for _,_spot in pairs(parkingdata) do
-- Mark text.
local _text=string.format("Term Index=%d, Term Type=%d, Free=%s, TOAC=%s, Term ID0=%d, Dist2Rwy=%.1f m",
_spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)
-- Create mark on the F10 map.
if mark then
_spot.Coordinate:MarkToAll(_text)
end
-- Info to DCS.log file.
local _text=string.format("%s, Term Index=%3d, Term Type=%03d, Free=%5s, TOAC=%5s, Term ID0=%3d, Dist2Rwy=%.1f m",
airbasename, _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)
self:E(_text)
end
end
--- Seach unoccupied parking spots at the airbase for a specific group of aircraft. The routine also optionally checks for other unit, static and scenery options in a certain radius around the parking spot.
-- The dimension of the spawned aircraft and of the potential obstacle are taken into account. Note that the routine can only return so many spots that are free.
-- @param #AIRBASE self
-- @param Wrapper.Group#GROUP group Aircraft group for which the parking spots are requested.
-- @param #AIRBASE.TerminalType terminaltype (Optional) Only search spots at a specific terminal type. Default is all types execpt on runway.
-- @param #number scanradius (Optional) Radius in meters around parking spot to scan for obstacles. Default 50 m.
-- @param #boolean scanunits (Optional) Scan for units as obstacles. Default true.
-- @param #boolean scanstatics (Optional) Scan for statics as obstacles. Default true.
-- @param #boolean scanscenery (Optional) Scan for scenery as obstacles. Default false. Can cause problems with e.g. shelters.
-- @param #boolean verysafe (Optional) If true, wait until an aircraft has taken off until the parking spot is considered to be free. Defaul false.
-- @return #table Table of coordinates and terminal IDs of free parking spots. Each table entry has the elements .Coordinate and .TerminalID.
function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery, verysafe)
-- Init default
scanradius=scanradius or 50
if scanunits==nil then
scanunits=true
end
if scanstatics==nil then
scanstatics=true
end
if scanscenery==nil then
scanscenery=false
end
if verysafe==nil then
verysafe=false
end
-- Get the size of an object.
local function _GetObjectSize(unit,mooseobject)
if mooseobject then
unit=unit:GetDCSObject()
end
if unit and unit:isExist() then
local DCSdesc=unit:getDesc()
if DCSdesc.box then
local x=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x)
local y=DCSdesc.box.max.y+math.abs(DCSdesc.box.min.y) --height
local z=DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z)
return math.max(x,z), x , y, z
end
end
return 0,0,0,0
end
-- Function calculating the overlap of two (square) objects.
local function _overlap(mooseobject, dcsobject, dist)
local l1=_GetObjectSize(mooseobject, true)
local l2=_GetObjectSize(dcsobject)
local safedist=(l1/2+l2/2)*1.1
local safe = (dist > safedist)
self:T3(string.format("l1=%.1f l2=%.1f s=%.1f d=%.1f ==> safe=%s", l1,l2,safedist,dist,tostring(safe)))
return safe
end
-- Get airport name.
local airport=self:GetName()
-- Get parking spot data table. This contains free and "non-free" spots.
-- Note that there are three major issues with the DCS getParking() function:
-- 1. A spot is considered as NOT free until an aircraft that is present has finally taken off. This might be a bit long especiall at smaller airports.
-- 2. A "free" spot does not take the aircraft size into accound. So if two big aircraft are spawned on spots next to each other, they might overlap and get destroyed.
-- 3. The routine return a free spot, if there a static objects placed on the spot.
local parkingdata=self:GetParkingSpotsTable(terminaltype)
-- Get the aircraft size, i.e. it's longest side of x,z.
local aircraft=group:GetUnit(1)
local nspots=group:GetSize()
local _aircraftsize, ax,ay,az=_GetObjectSize(aircraft, true)
-- Debug info.
self:E(string.format("Looking for %d parking spot(s) at %s for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at termial type %s.", nspots, airport, _aircraftsize, ax, ay, az, tostring(terminaltype)))
-- Table of valid spots.
local validspots={}
local nvalid=0
-- Test other stuff if no parking spot is available.
local _test=false
if _test then
return validspots
end
-- Mark all found obstacles on F10 map for debugging.
local markobstacles=false
-- Loop over all known parking spots
for _,parkingspot in pairs(parkingdata) do
-- Coordinate of the parking spot.
local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE
local _termid=parkingspot.TerminalID
-- 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.
if verysafe and parkingspot.Free==false then
-- DCS getParking() routine returned that spot is not free.
self:T(string.format("%s: Parking spot id %d NOT free (or aircraft has not taken off yet).", airport, parkingspot.TerminalID))
else
-- Scan a radius of 50 meters around the spot.
local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius, scanunits, scanstatics, scanscenery)
-- Loop over objects within scan radius.
local occupied=false
-- Check all units.
for _,unit in pairs(_units) do
local _vec3=unit:getPoint()
local _coord=COORDINATE:NewFromVec3(_vec3)
local _dist=_coord:Get2DDistance(_spot)
local _safe=_overlap(aircraft, unit, _dist)
if markobstacles then
local l,x,y,z=_GetObjectSize(unit)
_coord:MarkToAll(string.format("Unit %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s", unit:getName(),x,y,z,l,_dist, _termid, tostring(_safe)))
end
if scanunits and not _safe then
occupied=true
end
end
-- Check all statics.
for _,static in pairs(_statics) do
local _vec3=static:getPoint()
local _coord=COORDINATE:NewFromVec3(_vec3)
local _dist=_coord:Get2DDistance(_spot)
local _safe=_overlap(aircraft, static, _dist)
if markobstacles then
local l,x,y,z=_GetObjectSize(static)
_coord:MarkToAll(string.format("Static %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s", static:getName(),x,y,z,l,_dist, _termid, tostring(_safe)))
end
if scanstatics and not _safe then
occupied=true
end
end
-- Check all scenery.
for _,scenery in pairs(_sceneries) do
local _vec3=scenery:getPoint()
local _coord=COORDINATE:NewFromVec3(_vec3)
local _dist=_coord:Get2DDistance(_spot)
local _safe=_overlap(aircraft, scenery, _dist)
if markobstacles then
local l,x,y,z=_GetObjectSize(scenery)
_coord:MarkToAll(string.format("Scenery %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s", scenery:getTypeName(),x,y,z,l,_dist, _termid, tostring(_safe)))
end
if scanscenery and not _safe then
occupied=true
end
end
--_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))
else
self:E(string.format("%s: Parking spot id %d free.", airport, _termid))
if nvalid<nspots then
table.insert(validspots, {Coordinate=_spot, TerminalID=_termid})
end
nvalid=nvalid+1
end
end
-- We found enough spots.
if nvalid>=nspots then
return validspots
end
end
-- Retrun spots we found, even if there were not enough.
return validspots
end
--- Function that checks if at leat one unit of a group has been spawned close to a spawn point on the runway.
-- @param #AIRBASE self
-- @param Wrapper.Group#GROUP group Group to be checked.
-- @param #number radius Radius around the spawn point to be checked. Default is 50 m.
-- @param #boolean despawn If true, the group is destroyed.
-- @return #boolean True if group is within radius around spawn points on runway.
function AIRBASE:CheckOnRunWay(group, radius, despawn)
-- Default radius.
radius=radius or 50
-- We only check at real airbases (not FARPS or ships).
if self:GetDesc().category~=Airbase.Category.AIRDROME then
return false
end
if group and group:IsAlive() then
-- Debug.
self:T(string.format("%s, checking if group %s is on runway?",self:GetName(), group:GetName()))
-- Get coordinates on runway.
local runwaypoints=self:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway)
-- Mark runway spawn points.
--[[
for _i,_coord in pairs(runwaypoints) do
_coord:MarkToAll(string.format("runway %d",_i))
end
]]
-- Get units of group.
local units=group:GetUnits()
-- Loop over units.
for _,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
-- Check if unit is alive and not in air.
if unit and unit:IsAlive() and not unit:InAir() then
self:T(string.format("%s, checking if unit %s is on runway?",self:GetName(), unit:GetName()))
-- Loop over runway spawn points.
for _i,_coord in pairs(runwaypoints) do
-- Distance between unit and spawn pos.
local dist=unit:GetCoordinate():Get2DDistance(_coord)
-- Mark unit spawn points for debugging.
--unit:GetCoordinate():MarkToAll(string.format("unit %s distance to rwy %d = %d",unit:GetName(),_i, dist))
-- Check if unit is withing radius.
if dist<radius then
self:E(string.format("%s, unit %s of group %s was spawned on runway #%d. Distance %.1f < radius %.1f m. Despawn = %s.", self:GetName(), unit:GetName(), group:GetName(),_i, dist, radius, tostring(despawn)))
--unit:FlareRed()
if despawn then
group:Destroy(true)
end
return true
else
self:T(string.format("%s, unit %s of group %s was NOT spawned on runway #%d. Distance %.1f > radius %.1f m. Despawn = %s.", self:GetName(), unit:GetName(), group:GetName(),_i, dist, radius, tostring(despawn)))
--unit:FlareGreen()
end
end
else
self:T(string.format("%s, checking if unit %s of group %s is on runway. Unit is NOT alive.",self:GetName(), unit:GetName(), group:GetName()))
end
end
else
self:T(string.format("%s, checking if group %s is on runway. Group is NOT alive.",self:GetName(), group:GetName()))
end
return false
end
--- Helper function to check for the correct terminal type including "artificial" ones.
-- @param #number Term_Type Termial type from getParking routine.
-- @param #AIRBASE.TerminalType termtype Terminal type from AIRBASE.TerminalType enumerator.
-- @return #boolean True if terminal types match.
function AIRBASE._CheckTerminalType(Term_Type, termtype)
-- Nill check for Term_Type.
if Term_Type==nil then
return false
end
-- If no terminal type is requested, we return true. BUT runways are excluded unless explicitly requested.
if termtype==nil then
if Term_Type==AIRBASE.TerminalType.Runway then
return false
else
return true
end
end
-- Init no match.
local match=false
-- Standar case.
if Term_Type==termtype then
match=true
end
-- Artificial cases. Combination of terminal types.
if termtype==AIRBASE.TerminalType.OpenMedOrBig then
if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig then
match=true
end
elseif termtype==AIRBASE.TerminalType.HelicopterUsable then
if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.HelicopterOnly then
match=true
end
elseif termtype==AIRBASE.TerminalType.FighterAircraft then
if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.Shelter then
match=true
end
end
return match
end