Spawn improvements

COORDINATE:
- Improved GetClosestParkingSpot functions.
- Improved ScanObject function.
SPAWN:
- Improved SpawnAtAirbase function
RAT:
- Improved spawn function
- Added some takeoff type user functions.
UTILS:
- Added clock vs second functions
- Added split function
AIRBASE:
- Added checkonrunway function
- Other improvemetns.
This commit is contained in:
funkyfranky 2018-06-28 00:20:48 +02:00
parent 08ea3cd219
commit a7afb43ab6
5 changed files with 531 additions and 226 deletions

View File

@ -294,15 +294,18 @@ do -- COORDINATE
--- Returns if the 2 coordinates are at the same 2D position. --- Returns if the 2 coordinates are at the same 2D position.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #number radius Scan radius in meters. -- @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 units were found.
-- @return True if statics were found. -- @return True if statics were found.
-- @return True if scenery objects were found. -- @return True if scenery objects were found.
-- @return Unit objects found. -- @return Unit objects found.
-- @return Static objects found. -- @return Static objects found.
-- @return Scenery objects found. -- @return Scenery objects found.
function COORDINATE:ScanObjects(radius) function COORDINATE:ScanObjects(radius, scanunits, scanstatics, scanscenery)
self:T(string.format("Scanning in radius %.1f m.", radius)) self:F(string.format("Scanning in radius %.1f m.", radius))
local SphereSearch = { local SphereSearch = {
id = world.VolumeType.SPHERE, id = world.VolumeType.SPHERE,
@ -312,6 +315,30 @@ do -- COORDINATE
} }
} }
-- 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. -- Found stuff.
local Units = {} local Units = {}
local Statics = {} local Statics = {}
@ -352,7 +379,7 @@ do -- COORDINATE
end end
-- Search the world. -- Search the world.
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC, Object.Category.SCENERY}, SphereSearch, EvaluateZone) world.searchObjects(scanobjects, SphereSearch, EvaluateZone)
for _,unit in pairs(Units) do for _,unit in pairs(Units) do
self:T(string.format("Scan found unit %s", unit:getName())) self:T(string.format("Scan found unit %s", unit:getName()))
@ -361,7 +388,7 @@ do -- COORDINATE
self:T(string.format("Scan found static %s", static:getName())) self:T(string.format("Scan found static %s", static:getName()))
end end
for _,scenery in pairs(Scenery) do for _,scenery in pairs(Scenery) do
self:T(string.format("Scan found scenery %s", scenery:getName())) self:T(string.format("Scan found scenery %s", scenery:getTypeName()))
end end
return gotunits, gotstatics, gotscenery, Units, Statics, Scenery return gotunits, gotstatics, gotscenery, Units, Statics, Scenery
@ -1021,22 +1048,28 @@ do -- COORDINATE
--- Gets the nearest parking spot. --- Gets the nearest parking spot.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #boolean free (Optional) Only look for free parking spots. By default the closest parking spot is returned regardless of whether it is free or not. -- @param Wrapper.Airbase#AIRBASE airbase (Optional) Search only parking spots at this airbase.
-- @param Wrapper.Airbase#AIRBASE airbase (Optional) Search only parking spots at that airbase. -- @param Wrapper.Airbase#Terminaltype terminaltype (Optional) Type of the terminal. Default any execpt valid spawn points on runway.
-- @param Wrapper.Airbase#Terminaltype terminaltype Type of the terminal. -- @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 Core.Point#COORDINATE Coordinate of the nearest parking spot.
-- @return #number Distance to closest parking spot. -- @return #number Terminal ID.
function COORDINATE:GetClosestParkingSpot(free, airbase, terminaltype) -- @return #number Distance to closest parking spot in meters.
function COORDINATE:GetClosestParkingSpot(airbase, terminaltype, free)
-- Get airbase table.
local airbases={} local airbases={}
if airbase then if airbase then
table.insert(airbases,airbase) table.insert(airbases,airbase)
else else
airbases=AIRBASE:GetAllAirbases() airbases=AIRBASE.GetAllAirbases()
end end
-- Init.
local _closest=nil --Core.Point#COORDINATE local _closest=nil --Core.Point#COORDINATE
local _termID=nil
local _distmin=nil local _distmin=nil
-- Loop over all airbases.
for _,_airbase in pairs(airbases) do for _,_airbase in pairs(airbases) do
local mybase=_airbase --Wrapper.Airbase#AIRBASE local mybase=_airbase --Wrapper.Airbase#AIRBASE
@ -1044,44 +1077,51 @@ do -- COORDINATE
for _,_spot in pairs(parkingdata) do for _,_spot in pairs(parkingdata) do
-- Get coordinate if it matches the requirements. -- Check for parameters.
local _coord=nil --Core.Point#COORDINATE if (free==true and _spot.Free==true) or (free==false and _spot.Free==false) or free==nil then
if (free and _spot.Free) or free==nil then
if (terminaltype and _spot.TerminalType==terminaltype) or terminaltype==nil then local _coord=_spot.Coordinate --Core.Point#COORDINATE
_coord=_spot.Coordinate
end
end
-- Compare distance to closest one found so far.
if _coord then
local _dist=self:Get2DDistance(_coord) local _dist=self:Get2DDistance(_coord)
if _distmin==nil then if _distmin==nil then
_closest=_coord _closest=_coord
_distmin=_dist _distmin=_dist
_termID=_spot.TerminalID
else else
local _dist=self:Get2DDistance(_coord)
if _dist<_distmin then if _dist<_distmin then
_distmin=_dist _distmin=_dist
_closest=_coord _closest=_coord
_termID=_spot.TerminalID
end end
end end
end end
end end
end end
return _closest, _distmin return _closest, _termID, _distmin
end end
--- Gets the nearest free parking spot. --- Gets the nearest free parking spot.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param Wrapper.Airbase#AIRBASE airbase (Optional) Search only parking spots at that airbase. -- @param Wrapper.Airbase#AIRBASE airbase (Optional) Search only parking spots at that airbase.
-- @param Wrapper.Airbase#Terminaltype terminaltype Type of the terminal. -- @param Wrapper.Airbase#Terminaltype terminaltype (Optional) Type of the terminal.
-- @return #COORDINATE Coordinate of the nearest free parking spot. -- @return #COORDINATE Coordinate of the nearest free parking spot.
-- @return #number Distance to closest free parking spot. -- @return #number Terminal ID.
-- @return #number Distance to closest free parking spot in meters.
function COORDINATE:GetClosestFreeParkingSpot(airbase, terminaltype) function COORDINATE:GetClosestFreeParkingSpot(airbase, terminaltype)
return self:GetClosestParkingSpot(true, 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 end
--- Gets the nearest coordinate to a road. --- Gets the nearest coordinate to a road.

View File

@ -1218,7 +1218,8 @@ end
-- @param Wrapper.Airbase#AIRBASE SpawnAirbase The @{Wrapper.Airbase} where to spawn the group. -- @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 #SPAWN.Takeoff Takeoff (optional) The location and takeoff method. Default is Hot.
-- @param #number TakeoffAltitude (optional) The altitude above the ground. -- @param #number TakeoffAltitude (optional) The altitude above the ground.
-- @param #number TerminalType (optional) The terminal type the aircraft should be spawned at. -- @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. -- @return Wrapper.Group#GROUP that was spawned or nil when nothing was spawned.
-- @usage -- @usage
-- Spawn_Plane = SPAWN:New( "Plane" ) -- Spawn_Plane = SPAWN:New( "Plane" )
@ -1239,7 +1240,7 @@ end
-- --
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold, nil, AIRBASE.TerminalType.OpenBig ) -- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold, nil, AIRBASE.TerminalType.OpenBig )
-- --
function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType ) -- R2.2, R2.4 function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType, EmergencyAirSpawn ) -- R2.2, R2.4
self:F( { self.SpawnTemplatePrefix, SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType } ) self:F( { self.SpawnTemplatePrefix, SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType } )
-- Get position of airbase. -- Get position of airbase.
@ -1249,6 +1250,11 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
-- Set take off type. Default is hot. -- Set take off type. Default is hot.
Takeoff = Takeoff or SPAWN.Takeoff.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 if self:_GetSpawnIndex( self.SpawnIndex + 1 ) then
-- Get group template. -- Get group template.
@ -1259,9 +1265,13 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
-- Debug output -- Debug output
self:T( { "Current point of ", self.SpawnTemplatePrefix, SpawnAirbase } ) self:T( { "Current point of ", self.SpawnTemplatePrefix, SpawnAirbase } )
-- Template group and unit. -- Template group, unit and its attributes.
local TemplateGroup = GROUP:FindByName(self.SpawnTemplatePrefix) local TemplateGroup = GROUP:FindByName(self.SpawnTemplatePrefix)
local ishelo=TemplateGroup:GetUnit(1):HasAttribute("Helicopters") 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. -- First waypoint of the group.
local SpawnPoint = SpawnTemplate.route.points[1] local SpawnPoint = SpawnTemplate.route.points[1]
@ -1332,32 +1342,64 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
-- Number of free parking spots at the airbase. -- Number of free parking spots at the airbase.
if spawnonship or spawnonfarp or spawnonrunway then if spawnonship or spawnonfarp or spawnonrunway then
-- These places work procedural and have some kind of build in queue ==> Less effort. -- 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) nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype, spawnonship or spawnonfarp or spawnonrunway)
spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway) spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway)
else else
if ishelo and termtype==nil then if ishelo then
-- Helo is spawned. if termtype==nil then
-- Try helo spots first. -- 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) spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly)
nfree=#spots nfree=#spots
if nfree<#SpawnTemplate.units then if nfree<#SpawnTemplate.units then
-- Not enough helo ports. Let's try all terminal types. -- 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) spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable)
nfree=#spots nfree=#spots
end end
else else
-- Fixed wing aircraft is spawned. -- No terminal type specified. We try all spots except shelters.
--TODO: Add some default cases for transport, bombers etc. if no explicit terminal type is provided. self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), termtype))
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype) spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype)
nfree=#spots nfree=#spots
end 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)
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)
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)
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)
nfree=#spots
end
end
end end
-- Get parking data. -- Get parking data.
local parkingdata=SpawnAirbase:GetParkingSpotsTable(termtype) local parkingdata=SpawnAirbase:GetParkingSpotsTable(termtype)
self:T(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype))) self:T2(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype)))
for _,_spot in pairs(parkingdata) do for _,_spot in pairs(parkingdata) do
self:T(string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d", 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)) SpawnAirbase:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy))
end end
self:T(string.format("%s at %s: free parking spots = %d - number of units = %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), nfree, #SpawnTemplate.units)) self:T(string.format("%s at %s: free parking spots = %d - number of units = %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), nfree, #SpawnTemplate.units))
@ -1371,7 +1413,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
end end
else else
if not self.SpawnUnControlled then 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())) 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. -- Not enough parking spots at the airport ==> Spawn in air.
@ -1388,15 +1430,15 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
PointVec3.x=PointVec3.x+math.random(-500,500) PointVec3.x=PointVec3.x+math.random(-500,500)
PointVec3.z=PointVec3.z+math.random(-500,500) PointVec3.z=PointVec3.z+math.random(-500,500)
if ishelo then if ishelo then
PointVec3.y=PointVec3:GetLandHeight()+math.random(200,1200) PointVec3.y=PointVec3:GetLandHeight()+math.random(100,1000)
else else
-- Randomize position so that multiple AC wont be spawned on top even in air. -- Randomize position so that multiple AC wont be spawned on top even in air.
PointVec3.y=PointVec3:GetLandHeight()+math.random(500,5000) PointVec3.y=PointVec3:GetLandHeight()+math.random(500,2500)
end end
Takeoff=GROUP.Takeoff.Air Takeoff=GROUP.Takeoff.Air
else else
self:E(string.format("WARNING: Group %s has no parking spots at %s ==> Uncontrolled spawning ==> No spawn!", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) 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 return nil
end end
end end
@ -1483,6 +1525,11 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
end end
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, 25, true} , 1.0)
end
return GroupSpawned return GroupSpawned
end end
end end
@ -1490,8 +1537,6 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
return nil return nil
end end
--- Will spawn a group from a Vec3 in 3D space. --- 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. -- 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. -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.

View File

@ -129,7 +129,6 @@
-- @field #string livery Livery of the aircraft set by user. -- @field #string livery Livery of the aircraft set by user.
-- @field #string skill Skill of AI. -- @field #string skill Skill of AI.
-- @field #boolean ATCswitch Enable/disable ATC if set to true/false. -- @field #boolean ATCswitch Enable/disable ATC if set to true/false.
-- @field #string parking_id String with a special parking ID for the aircraft.
-- @field #boolean radio If true/false disables radio messages from the RAT groups. -- @field #boolean radio If true/false disables radio messages from the RAT groups.
-- @field #number frequency Radio frequency used by the RAT groups. -- @field #number frequency Radio frequency used by the RAT groups.
-- @field #string modulation Ratio modulation. Either "FM" or "AM". -- @field #string modulation Ratio modulation. Either "FM" or "AM".
@ -365,7 +364,6 @@ RAT={
livery=nil, -- Livery of the aircraft. livery=nil, -- Livery of the aircraft.
skill="High", -- Skill of AI. skill="High", -- Skill of AI.
ATCswitch=true, -- Enable ATC. ATCswitch=true, -- Enable ATC.
parking_id=nil, -- Specific parking ID when aircraft are spawned at airports.
radio=nil, -- If true/false disables radio messages from the RAT groups. radio=nil, -- If true/false disables radio messages from the RAT groups.
frequency=nil, -- Radio frequency used by the RAT groups. frequency=nil, -- Radio frequency used by the RAT groups.
modulation=nil, -- Ratio modulation. Either "FM" or "AM". modulation=nil, -- Ratio modulation. Either "FM" or "AM".
@ -997,6 +995,36 @@ function RAT:SetTakeoff(type)
self.takeoff=_Type self.takeoff=_Type
end end
--- Set takeoff type cold. Aircraft will spawn at a parking spot with engines off.
-- @param #RAT self
function RAT:SetTakeoffCold()
self.takeoff=RAT.wp.cold
end
--- Set takeoff type to hot. Aircraft will spawn at a parking spot with engines on.
-- @param #RAT self
function RAT:SetTakeoffHot()
self.takeoff=RAT.wp.hot
end
--- Set takeoff type to runway. Aircraft will spawn directly on the runway.
-- @param #RAT self
function RAT:SetTakeoffRunway()
self.takeoff=RAT.wp.runway
end
--- Set takeoff type to cold or hot. Aircraft will spawn at a parking spot with 50:50 change of engines on or off.
-- @param #RAT self
function RAT:SetTakeoffColdOrHot()
self.takeoff=RAT.wp.coldorhot
end
--- Set takeoff type to air. Aircraft will spawn in the air.
-- @param #RAT self
function RAT:SetTakeoffAir()
self.takeoff=RAT.wp.air
end
--- Set possible departure ports. This can be an airport or a zone defined in the mission editor. --- Set possible departure ports. This can be an airport or a zone defined in the mission editor.
-- @param #RAT self -- @param #RAT self
-- @param #string departurenames Name or table of names of departure airports or zones. -- @param #string departurenames Name or table of names of departure airports or zones.
@ -1332,15 +1360,6 @@ function RAT:ParkingSpotDB(switch)
self.useparkingdb=switch self.useparkingdb=switch
end end
--- Set parking id of aircraft.
-- @param #RAT self
-- @param #string id Parking ID of the aircraft.
function RAT:SetParkingID(id)
self:F2(id)
self.parking_id=id
self:T(RAT.id.."Setting parking ID to "..self.parking_id)
end
--- Enable Radio. Overrules the ME setting. --- Enable Radio. Overrules the ME setting.
-- @param #RAT self -- @param #RAT self
function RAT:RadioON() function RAT:RadioON()
@ -4920,8 +4939,9 @@ end
-- @param Core.Point#COORDINATE spawnplace (Optional) Place where spawning should happen. If not present, first waypoint is taken. -- @param Core.Point#COORDINATE spawnplace (Optional) Place where spawning should happen. If not present, first waypoint is taken.
-- @param Wrapper.Airbase#AIRBASE departure Departure airbase or zone. -- @param Wrapper.Airbase#AIRBASE departure Departure airbase or zone.
-- @param #number takeoff Takeoff type. -- @param #number takeoff Takeoff type.
-- @return #boolean True if modification was successful or nil if not, e.g. when no parking space was found and spawn in air is disabled.
function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, takeoff) function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, takeoff)
self:F2({waypoints=waypoints, livery=livery, spawnplace=spawnplace}) self:F2({waypoints=waypoints, livery=livery, spawnplace=spawnplace, departure=departure, takeoff=takeoff})
-- The 3D vector of the first waypoint, i.e. where we actually spawn the template group. -- The 3D vector of the first waypoint, i.e. where we actually spawn the template group.
local PointVec3 = COORDINATE:New(waypoints[1].x, waypoints[1].alt, waypoints[1].y) local PointVec3 = COORDINATE:New(waypoints[1].x, waypoints[1].alt, waypoints[1].y)
@ -4931,6 +4951,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
-- Template group and unit. -- Template group and unit.
local TemplateGroup = GROUP:FindByName(self.SpawnTemplatePrefix) local TemplateGroup = GROUP:FindByName(self.SpawnTemplatePrefix)
local TemplateUnit=TemplateGroup:GetUnit(1)
-- Check if we spawn on ground. -- Check if we spawn on ground.
local spawnonground=takeoff==RAT.wp.cold or takeoff==RAT.wp.hot or takeoff==RAT.wp.runway local spawnonground=takeoff==RAT.wp.cold or takeoff==RAT.wp.hot or takeoff==RAT.wp.runway
@ -4986,41 +5007,76 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
-- Number of free parking spots. -- Number of free parking spots.
local nfree=0 local nfree=0
-- Set terminal type. -- Set terminal type. Can also be nil.
local termtype=self.termtype local termtype=self.termtype
if spawnonrunway then if spawnonrunway then
termtype=AIRBASE.TerminalType.Runway termtype=AIRBASE.TerminalType.Runway
end end
-- Number of free parking spots at the airbase. -- Get free parking spots depending on where we spawn.
if spawnonship or spawnonfarp or spawnonrunway then if spawnonship or spawnonfarp or spawnonrunway then
-- These places work procedural and have some kind of build in queue ==> Less effort. -- These places work procedural and have some kind of build in queue ==> Less effort.
self:T(RAT.id..string.format("Group %s is spawned on farp/ship/runway %s.", self.alias, departure:GetName()))
nfree=departure:GetFreeParkingSpotsNumber(termtype, spawnonship or spawnonfarp or spawnonrunway) nfree=departure:GetFreeParkingSpotsNumber(termtype, spawnonship or spawnonfarp or spawnonrunway)
spots=departure:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway) spots=departure:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway)
else else
if self.category==RAT.cat.heli and termtype==nil then
-- Helo is spawned. -- Helo is spawned.
-- Try helo spots first. if self.category==RAT.cat.heli then
if termtype==nil then
-- Try exclusive helo spots first.
self:T(RAT.id..string.format("Helo group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.HelicopterOnly))
spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly) spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly)
nfree=#spots nfree=#spots
if nfree<#SpawnTemplate.units then if nfree<#SpawnTemplate.units then
-- Not enough helo ports. Let's try all terminal types. -- Not enough helo ports. Let's try also other terminal types.
self:T(RAT.id..string.format("Helo group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.HelicopterOnly))
spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable) spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable)
nfree=#spots nfree=#spots
end end
else
-- Terminal type specified explicitly.
self:T(RAT.id..string.format("Helo group %s is at %s using terminal type %d.", self.alias, departure:GetName(), termtype))
spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, termtype)
nfree=#spots
end
else else
-- Fixed wing aircraft is spawned. -- 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: 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.
local bomber=TemplateUnit:HasAttribute("Bombers")
local transport=TemplateUnit:HasAttribute("Transports")
if bomber or transport then
-- First we fill the potentially bigger spots.
self:T(RAT.id..string.format("Transport/bomber group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.OpenBig))
spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig)
nfree=#spots
if nfree<#SpawnTemplate.units then
-- Now we try the smaller ones.
self:T(RAT.id..string.format("Transport/bomber group %s is at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.OpenMed))
spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMed)
nfree=#spots
end
else
self:T(RAT.id..string.format("Fighter group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.FighterAircraft))
spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft)
nfree=#spots
end
else
-- Terminal type explicitly given.
self:T(RAT.id..string.format("Plane group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), termtype))
spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, termtype) spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, termtype)
nfree=#spots nfree=#spots
end end
end end
end
-- Get parking data. -- Get parking data (just for debugging).
local parkingdata=departure:GetParkingSpotsTable(termtype) local parkingdata=departure:GetParkingSpotsTable(termtype)
self:T(RAT.id..string.format("Parking at %s, terminal type %s:", departure:GetName(), tostring(termtype))) self:T2(RAT.id..string.format("Parking at %s, terminal type %s:", departure:GetName(), tostring(termtype)))
for _,_spot in pairs(parkingdata) do for _,_spot in pairs(parkingdata) do
self:T(RAT.id..string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d", self:T2(RAT.id..string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d",
departure:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)) departure:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy))
end end
self:T(RAT.id..string.format("%s at %s: free parking spots = %d - number of units = %d", self.alias, departure:GetName(), nfree, #SpawnTemplate.units)) self:T(RAT.id..string.format("%s at %s: free parking spots = %d - number of units = %d", self.alias, departure:GetName(), nfree, #SpawnTemplate.units))
@ -5033,10 +5089,6 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
table.insert(parkingindex, spots[i].TerminalID) table.insert(parkingindex, spots[i].TerminalID)
end end
if spawnonrunway then
--PointVec3=spots[1]
end
else else
if self.respawn_inair or self.uncontrolled then if self.respawn_inair or self.uncontrolled then
self:E(RAT.id..string.format("WARNING: RAT group %s has no parking spots at %s ==> air start!", self.alias, departure:GetName())) self:E(RAT.id..string.format("WARNING: RAT group %s has no parking spots at %s ==> air start!", self.alias, departure:GetName()))
@ -5051,8 +5103,14 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
waypoints[1].type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point waypoints[1].type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point
waypoints[1].action = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -- action = Turning Point waypoints[1].action = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -- action = Turning Point
-- Adjust altitude to be 500-1000 m above the airbase. -- Adjust and randomize position and altitude of the spawn point.
PointVec3.y=PointVec3:GetLandHeight()+math.random(500,1000) PointVec3.x=PointVec3.x+math.random(-500,500)
PointVec3.z=PointVec3.z+math.random(-500,500)
if self.category==RAT.cat.heli then
PointVec3.y=PointVec3:GetLandHeight()+math.random(100,1000)
else
PointVec3.y=PointVec3:GetLandHeight()+math.random(500,2500)
end
else else
self:E(RAT.id..string.format("WARNING: RAT group %s has no parking spots at %s. Air start deactivated or uncontrolled AC!", self.alias, departure:GetName())) self:E(RAT.id..string.format("WARNING: RAT group %s has no parking spots at %s. Air start deactivated or uncontrolled AC!", self.alias, departure:GetName()))
return nil return nil
@ -5063,7 +5121,6 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
-- Translate the position of the Group Template to the Vec3. -- Translate the position of the Group Template to the Vec3.
for UnitID = 1, #SpawnTemplate.units do for UnitID = 1, #SpawnTemplate.units 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. -- Template of the current unit.
local UnitTemplate = SpawnTemplate.units[UnitID] local UnitTemplate = SpawnTemplate.units[UnitID]
@ -5080,34 +5137,28 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
-- Shíps and FARPS seem to have a build in queue. -- Shíps and FARPS seem to have a build in queue.
if spawnonship or spawnonfarp or spawnonrunway or automatic then if spawnonship or spawnonfarp or spawnonrunway or automatic then
self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName())) self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName()))
-- Spawn on ship. We take only the position of the ship. -- Spawn on ship. We take only the position of the ship.
SpawnTemplate.units[UnitID].x = PointVec3.x --TX SpawnTemplate.units[UnitID].x = PointVec3.x --TX
SpawnTemplate.units[UnitID].y = PointVec3.z --TY SpawnTemplate.units[UnitID].y = PointVec3.z --TY
SpawnTemplate.units[UnitID].alt = PointVec3.y SpawnTemplate.units[UnitID].alt = PointVec3.y
else else
self:T(RAT.id..string.format("RAT group %s spawning at airbase %s on parking spot id %d", self.alias, departure:GetName(), parkingindex[UnitID])) self:T(RAT.id..string.format("RAT group %s spawning at airbase %s on parking spot id %d", self.alias, departure:GetName(), parkingindex[UnitID]))
-- Get coordinates of parking spot. -- Get coordinates of parking spot.
SpawnTemplate.units[UnitID].x = parkingspots[UnitID].x SpawnTemplate.units[UnitID].x = parkingspots[UnitID].x
SpawnTemplate.units[UnitID].y = parkingspots[UnitID].z SpawnTemplate.units[UnitID].y = parkingspots[UnitID].z
SpawnTemplate.units[UnitID].alt = parkingspots[UnitID].y SpawnTemplate.units[UnitID].alt = parkingspots[UnitID].y
end end
else else
self:T(RAT.id..string.format("RAT group %s spawning in air at %s.", self.alias, departure:GetName())) self:T(RAT.id..string.format("RAT group %s spawning in air at %s.", self.alias, departure:GetName()))
-- Spawn in air as requested initially. Original template orientation is perserved, altitude is already correctly set. -- Spawn in air as requested initially. Original template orientation is perserved, altitude is already correctly set.
SpawnTemplate.units[UnitID].x = TX SpawnTemplate.units[UnitID].x = TX
SpawnTemplate.units[UnitID].y = TY SpawnTemplate.units[UnitID].y = TY
SpawnTemplate.units[UnitID].alt = PointVec3.y SpawnTemplate.units[UnitID].alt = PointVec3.y
end end
-- Place marker at spawn position. -- Place marker at spawn position.
@ -5123,6 +5174,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
UnitTemplate.parking = parkingindex[UnitID] UnitTemplate.parking = parkingindex[UnitID]
end end
-- Debug info.
self:T2(RAT.id..string.format("RAT group %s unit number %d: Parking = %s",self.alias, UnitID, tostring(UnitTemplate.parking))) self:T2(RAT.id..string.format("RAT group %s unit number %d: Parking = %s",self.alias, UnitID, tostring(UnitTemplate.parking)))
self:T2(RAT.id..string.format("RAT group %s unit number %d: Parking ID = %s",self.alias, UnitID, tostring(UnitTemplate.parking_id))) self:T2(RAT.id..string.format("RAT group %s unit number %d: Parking ID = %s",self.alias, UnitID, tostring(UnitTemplate.parking_id)))
@ -5155,7 +5207,6 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
SpawnTemplate.CountryID=self.country SpawnTemplate.CountryID=self.country
end end
self:T2('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y)
end end
-- Copy waypoints into spawntemplate. By this we avoid the nasty DCS "landing bug" :) -- Copy waypoints into spawntemplate. By this we avoid the nasty DCS "landing bug" :)

View File

@ -496,3 +496,99 @@ function UTILS.BeaufortScale(speed)
end end
return bn,bd return bn,bd
end 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

@ -339,10 +339,9 @@ function AIRBASE:GetZone()
end end
--- Get all airbases of the current map. This includes ships and FARPS. --- Get all airbases of the current map. This includes ships and FARPS.
-- @param #AIRBASE self
-- @param DCS#Coalition coalition (Optional) Return only airbases belonging to the specified coalition. By default, all airbases of the map are returned. -- @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. -- @return #table Table containing all airbase objects of the current map.
function AIRBASE:GetAllAirbases(coalition) function AIRBASE.GetAllAirbases(coalition)
local airbases={} local airbases={}
for _,airbase in pairs(_DATABASE.AIRBASES) do for _,airbase in pairs(_DATABASE.AIRBASES) do
@ -402,7 +401,7 @@ function AIRBASE:GetParkingSpotsNumber(termtype)
local nspots=0 local nspots=0
for _,parkingspot in pairs(parkingdata) do for _,parkingspot in pairs(parkingdata) do
if self:_CheckTerminalType(parkingspot.Term_Type, termtype) then if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then
nspots=nspots+1 nspots=nspots+1
end end
end end
@ -423,7 +422,7 @@ function AIRBASE:GetFreeParkingSpotsNumber(termtype, allowTOAC)
local nfree=0 local nfree=0
for _,parkingspot in pairs(parkingdata) do for _,parkingspot in pairs(parkingdata) do
-- Spots on runway are not counted unless explicitly requested. -- Spots on runway are not counted unless explicitly requested.
if self:_CheckTerminalType(parkingspot.Term_Type, termtype) then if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then
if (allowTOAC and allowTOAC==true) or parkingspot.TO_AC==false then if (allowTOAC and allowTOAC==true) or parkingspot.TO_AC==false then
nfree=nfree+1 nfree=nfree+1
end end
@ -447,7 +446,7 @@ function AIRBASE:GetFreeParkingSpotsCoordinates(termtype, allowTOAC)
local spots={} local spots={}
for _,parkingspot in pairs(parkingdata) do for _,parkingspot in pairs(parkingdata) do
-- Coordinates on runway are not returned unless explicitly requested. -- Coordinates on runway are not returned unless explicitly requested.
if self:_CheckTerminalType(parkingspot.Term_Type, termtype) then if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then
if (allowTOAC and allowTOAC==true) or parkingspot.TO_AC==false then if (allowTOAC and allowTOAC==true) or parkingspot.TO_AC==false then
table.insert(spots, COORDINATE:NewFromVec3(parkingspot.vTerminalPos)) table.insert(spots, COORDINATE:NewFromVec3(parkingspot.vTerminalPos))
end end
@ -471,7 +470,7 @@ function AIRBASE:GetParkingSpotsCoordinates(termtype)
for _,parkingspot in pairs(parkingdata) do for _,parkingspot in pairs(parkingdata) do
-- Coordinates on runway are not returned unless explicitly requested. -- Coordinates on runway are not returned unless explicitly requested.
if self:_CheckTerminalType(parkingspot.Term_Type, termtype) then if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then
-- Get coordinate from Vec3 terminal position. -- Get coordinate from Vec3 terminal position.
local _coord=COORDINATE:NewFromVec3(parkingspot.vTerminalPos) local _coord=COORDINATE:NewFromVec3(parkingspot.vTerminalPos)
@ -510,7 +509,7 @@ function AIRBASE:GetParkingSpotsTable(termtype)
-- Put coordinates of parking spots into table. -- Put coordinates of parking spots into table.
local spots={} local spots={}
for _,_spot in pairs(parkingdata) do for _,_spot in pairs(parkingdata) do
if self:_CheckTerminalType(_spot.Term_Type, termtype) then if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then
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})
@ -533,7 +532,7 @@ function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC)
-- Put coordinates of free spots into table. -- Put coordinates of free spots into table.
local freespots={} local freespots={}
for _,_spot in pairs(parkingfree) do for _,_spot in pairs(parkingfree) do
if self:_CheckTerminalType(_spot.Term_Type, termtype) then if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then
if (allowTOAC and allowTOAC==true) or _spot.TO_AC==false then if (allowTOAC and allowTOAC==true) or _spot.TO_AC==false then
local _coord=COORDINATE:NewFromVec3(_spot.vTerminalPos) 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}) 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})
@ -572,13 +571,237 @@ function AIRBASE:MarkParkingSpots(termtype)
end end
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 #number 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 30 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.
-- @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)
-- Init default
scanradius=scanradius or 30
if scanunits==nil then
scanunits=true
end
if scanstatics==nil then
scanstatics=true
end
if scanscenery==nil then
scanscenery=false
end
-- Mark all found obstacles on F10 map.
local markobstacles=false
-- 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()
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
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 free parking spot data table.
--local parkingdata=self:GetFreeParkingSpotsTable(terminaltype, true)
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
-- Loop over all known parking spots
for _,parkingspot in pairs(parkingdata) do
-- Check for requested terminal type if any.
if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) then
-- Coordinate of the parking spot.
local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE
local _termid=parkingspot.TerminalID
-- 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, parkingspot.TerminalID))
else
self:E(string.format("%s: Parking spot id %d free.", airport, parkingspot.TerminalID))
if nvalid<nspots then
table.insert(validspots, {Coordinate=_spot, TerminalID=parkingspot.TerminalID})
end
nvalid=nvalid+1
end
end
-- We found enough spots.
if nvalid==nspots then
return validspots
end
end
return validspots
end
--- Helper function that checks if a group is 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 25 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 25
-- Debug.
self:T(string.format("%s, checking if group %s is on runway?",self:GetName(), group:GetName()))
if group and group:IsAlive() then
-- Get coordinates on runway.
local runwaypoints=self:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway)
--[[
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
if unit and unit:IsAlive() 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)
-- Check if unit is withing radius.
if dist<radius and not unit:InAir() then
self:T(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. --- Helper function to check for the correct terminal type including "artificial" ones.
-- @param #AIRBASE self
-- @param #number Term_Type Termial type from getParking routine. -- @param #number Term_Type Termial type from getParking routine.
-- @param #number termtype Terminal type from AIRBASE.TerminalType enumerator. -- @param #number termtype Terminal type from AIRBASE.TerminalType enumerator.
-- @return True if terminal types match. -- @return #boolean True if terminal types match.
function AIRBASE:_CheckTerminalType(Term_Type, termtype) function AIRBASE._CheckTerminalType(Term_Type, termtype)
-- Nill check for Term_Type. -- Nill check for Term_Type.
if Term_Type==nil then if Term_Type==nil then
@ -619,153 +842,3 @@ function AIRBASE:_CheckTerminalType(Term_Type, termtype)
return match return match
end end
--- Seach an unoccupied parking spot at a specific airport and for a specific aircraft.
-- @param #AIRBASE self
-- @param Wrapper.Group#GROUP group Aircraft group for which the parking spots are requested.
-- @param #number 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 30 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.
-- @return #table Table of coordinates and terminal IDs of the parking spots or nil, if no valid parking spots for the whole group could be found.
function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery)
-- Init default
scanradius=scanradius or 30
if scanunits==nil then
scanunits=true
end
if scanstatics==nil then
scanstatics=true
end
if scanscenery==nil then
scanscenery=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()
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
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 free parking spot data table.
--local parkingdata=self:GetFreeParkingSpotsTable(terminaltype, true)
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:T(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
-- Loop over all known parking spots
for _,parkingspot in pairs(parkingdata) do
-- Check for requested terminal type if any.
if self:_CheckTerminalType(parkingspot.TerminalType, terminaltype) then
end
-- Coordinate of the parking spot.
local _spot=parkingspot.Coordinate --Core.Point#COORDINATE
-- Scan a radius of 50 meters around the spot.
local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius)
-- 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)
--local l,x,y,z=_GetObjectSize(unit)
--_coord:MarkToAll(string.format("scan found unit %s\nx=%.2f y=%.2f z=%.2f\nl=%.2f d=%.2f safe=%s", unit:getName(), x, y, z, l, _dist, tostring(_safe)))
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)
--local l,x,y,z=_GetObjectSize(static)
--_coord:MarkToAll(string.format("scan found static %s\nx=%.2f y=%.2f z=%.2f\nl=%.2f d=%.2f safe=%s", static:getName(),x,y,z,l,_dist, tostring(_safe)))
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)
--local l,x,y,z=_GetObjectSize(scenery)
--_coord:MarkToAll(string.format("scan found scenery %s\nx=%.2f y=%.2f z=%.2f\nl=%.2f d=%.2f", scenery:getName(),x,y,z,l,_dist))
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, parkingspot.TerminalID))
else
self:T(string.format("%s: Parking spot id %d free.", airport, parkingspot.TerminalID))
if nvalid<nspots then
table.insert(validspots, {Coordinate=_spot, TerminalID=parkingspot.TerminalID})
end
nvalid=nvalid+1
end
-- We found enough spots.
if nvalid==nspots then
return validspots
end
end
return validspots
end