diff --git a/Moose Development/Moose/Core/Condition.lua b/Moose Development/Moose/Core/Condition.lua index 7c25f1948..d88a1fc33 100644 --- a/Moose Development/Moose/Core/Condition.lua +++ b/Moose Development/Moose/Core/Condition.lua @@ -273,7 +273,7 @@ function CONDITION:_EvalConditionsAny(functions) end end ---- Create conditon fucntion object. +--- Create conditon function object. -- @param #CONDITION self -- @param #function Function The function to call. -- @param ... (Optional) Parameters passed to the function (if any). diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 54328a0ec..2cbf1d6b5 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -378,6 +378,7 @@ do -- MENU_MISSION end end + --- Refreshes a radio item for a mission -- @param #MENU_MISSION self -- @return #MENU_MISSION @@ -832,6 +833,29 @@ do return self end + --- Refreshes a new radio item for a group and submenus, ordering by (numerical) MenuTag + -- @param #MENU_GROUP self + -- @return #MENU_GROUP + function MENU_GROUP:RefreshAndOrderByTag() + + do + missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) + missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath ) + + local MenuTable = {} + for MenuText, Menu in pairs( self.Menus or {} ) do + local tag = Menu.MenuTag or math.random(1,10000) + MenuTable[#MenuTable+1] = {Tag=tag, Enty=Menu} + end + table.sort(MenuTable, function (k1, k2) return k1.tag < k2.tag end ) + for _, Menu in pairs( MenuTable ) do + Menu.Entry:Refresh() + end + end + + return self + end + --- Removes the sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self -- @param MenuStamp diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 5debc3a12..02c915c90 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -899,8 +899,8 @@ end -- @param ObjectCategories An array of categories of the objects to find in the zone. E.g. `{Object.Category.UNIT}` -- @param UnitCategories An array of unit categories of the objects to find in the zone. E.g. `{Unit.Category.GROUND_UNIT,Unit.Category.SHIP}` -- @usage --- self.Zone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT}) --- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition ) +-- myzone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT}) +-- local IsAttacked = myzone:IsSomeInZoneOfCoalition( self.Coalition ) function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) self.ScanData = {} diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 8cbe4354d..b791abd27 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -1374,7 +1374,9 @@ do -- AI --- @type AI.Option.Ground -- @field #AI.Option.Ground.id id -- @field #AI.Option.Ground.val val - + -- @field #AI.Option.Ground.mid mid + -- @field #AI.Option.Ground.mval mval + -- --- @type AI.Option.Naval -- @field #AI.Option.Naval.id id -- @field #AI.Option.Naval.val val @@ -1397,6 +1399,11 @@ do -- AI -- @field PROHIBIT_AG -- @field MISSILE_ATTACK -- @field PROHIBIT_WP_PASS_REPORT + -- @field OPTION_RADIO_USAGE_CONTACT + -- @field OPTION_RADIO_USAGE_ENGAGE + -- @field OPTION_RADIO_USAGE_KILL + -- @field JETT_TANKS_IF_EMPTY + -- @field FORCED_ATTACK --- @type AI.Option.Air.id.FORMATION -- @field LINE_ABREAST @@ -1466,19 +1473,35 @@ do -- AI --- @type AI.Option.Ground.id -- @field NO_OPTION -- @field ROE @{#AI.Option.Ground.val.ROE} + -- @field FORMATION -- @field DISPERSE_ON_ATTACK true or false -- @field ALARM_STATE @{#AI.Option.Ground.val.ALARM_STATE} -- @field ENGAGE_AIR_WEAPONS + -- @field AC_ENGAGEMENT_RANGE_RESTRICTION + + --- @type AI.Option.Ground.mid -- Moose added + -- @field RESTRICT_AAA_MIN 27 + -- @field RESTRICT_AAA_MAX 29 + -- @field RESTRICT_TARGETS @{#AI.Option.Ground.mval.ENGAGE_TARGETS} 28 --- @type AI.Option.Ground.val -- @field #AI.Option.Ground.val.ROE ROE -- @field #AI.Option.Ground.val.ALARM_STATE ALARM_STATE + -- @field #AI.Option.Ground.val.ENGAGE_TARGETS RESTRICT_TARGETS --- @type AI.Option.Ground.val.ROE -- @field OPEN_FIRE -- @field RETURN_FIRE -- @field WEAPON_HOLD + --- @type AI.Option.Ground.mval -- Moose added + -- @field #AI.Option.Ground.mval.ENGAGE_TARGETS ENGAGE_TARGETS + + --- @type AI.Option.Ground.mval.ENGAGE_TARGETS -- Moose added + -- @field ANY_TARGET -- 0 + -- @field AIR_UNITS_ONLY -- 1 + -- @field GROUND_UNITS_ONLY -- 2 + --- @type AI.Option.Ground.val.ALARM_STATE -- @field AUTO -- @field GREEN diff --git a/Moose Development/Moose/Functional/ATC_Ground.lua b/Moose Development/Moose/Functional/ATC_Ground.lua index 269111aa6..2e72dcea3 100644 --- a/Moose Development/Moose/Functional/ATC_Ground.lua +++ b/Moose Development/Moose/Functional/ATC_Ground.lua @@ -18,6 +18,8 @@ -- -- ### Contributions: Dutch Baron - Concept & Testing -- ### Author: FlightControl - Framework Design & Programming +-- ### Refactoring to use the Runway auto-detection: Applevangelist +-- @date August 2022 -- -- === -- @@ -28,21 +30,20 @@ -- @field Core.Set#SET_CLIENT SetClient -- @extends Core.Base#BASE ---- Base class for ATC\_GROUND implementations. +--- [DEPRECATED, use ATC_GROUND_UNIVERSAL] Base class for ATC\_GROUND implementations. -- @field #ATC_GROUND ATC_GROUND = { ClassName = "ATC_GROUND", SetClient = nil, Airbases = nil, AirbaseNames = nil, - --KickSpeed = nil, -- The maximum speed in meters per second for all airbases until a player gets kicked. This is overridden at each derived class. } --- @type ATC_GROUND.AirbaseNames -- @list <#string> ---- Creates a new ATC\_GROUND object. +--- [DEPRECATED, use ATC_GROUND_UNIVERSAL] Creates a new ATC\_GROUND object. -- @param #ATC_GROUND self -- @param Airbases A table of Airbase Names. -- @return #ATC_GROUND self @@ -59,7 +60,7 @@ function ATC_GROUND:New( Airbases, AirbaseList ) for AirbaseID, Airbase in pairs( self.Airbases ) do - -- Specified ZoneBoundary is used if setted or Airbase radius by default + -- Specified ZoneBoundary is used if set or Airbase radius by default if Airbase.ZoneBoundary then Airbase.ZoneBoundary = ZONE_POLYGON_BASE:New( "Boundary " .. AirbaseID, Airbase.ZoneBoundary ) else @@ -67,8 +68,10 @@ function ATC_GROUND:New( Airbases, AirbaseList ) end Airbase.ZoneRunways = {} - for PointsRunwayID, PointsRunway in pairs( Airbase.PointsRunways ) do - Airbase.ZoneRunways[PointsRunwayID] = ZONE_POLYGON_BASE:New( "Runway " .. PointsRunwayID, PointsRunway ) + if Airbase.PointsRunways then + for PointsRunwayID, PointsRunway in pairs( Airbase.PointsRunways ) do + Airbase.ZoneRunways[PointsRunwayID] = ZONE_POLYGON_BASE:New( "Runway " .. PointsRunwayID, PointsRunway ) + end end Airbase.Monitor = self.AirbaseList and false or true -- When AirbaseList is not given, monitor every Airbase, otherwise don't monitor any (yet). end @@ -78,13 +81,6 @@ function ATC_GROUND:New( Airbases, AirbaseList ) self.Airbases[AirbaseName].Monitor = true end --- -- Template --- local TemplateBoundary = GROUP:FindByName( "Template Boundary" ) --- self.Airbases.Template.ZoneBoundary = ZONE_POLYGON:New( "Template Boundary", TemplateBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local TemplateRunway1 = GROUP:FindByName( "Template Runway 1" ) --- self.Airbases.Template.ZoneRunways[1] = ZONE_POLYGON:New( "Template Runway 1", TemplateRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - self.SetClient:ForEachClient( --- @param Wrapper.Client#CLIENT Client function( Client ) @@ -103,7 +99,6 @@ function ATC_GROUND:New( Airbases, AirbaseList ) return self end - --- Smoke the airbases runways. -- @param #ATC_GROUND self -- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke around the runways. @@ -117,7 +112,6 @@ function ATC_GROUND:SmokeRunways( SmokeColor ) end end - --- Set the maximum speed in meters per second (Mps) until the player gets kicked. -- An airbase can be specified to set the kick speed for. -- @param #ATC_GROUND self @@ -252,7 +246,6 @@ function ATC_GROUND:SetMaximumKickSpeedMiph( MaximumKickSpeedMiph, Airbase ) return self end - --- @param #ATC_GROUND self function ATC_GROUND:_AirbaseMonitor() @@ -408,11 +401,455 @@ function ATC_GROUND:_AirbaseMonitor() return true end +--- +-- @type ATC_GROUND_UNIVERSAL +-- @field Core.Set#SET_CLIENT SetClient +-- @field #string Version +-- @field #string ClassName +-- @field #table Airbases +-- @field #table AirbaseList +-- @field #number KickSpeed +-- @extends Core.Base#BASE + +--- Base class for ATC\_GROUND\_UNIVERSAL implementations. +-- @field #ATC_GROUND_UNIVERSAL +ATC_GROUND_UNIVERSAL = { + ClassName = "ATC_GROUND_UNIVERSAL", + Version = "0.0.1", + SetClient = nil, + Airbases = nil, + AirbaseList = nil, + KickSpeed = nil, -- The maximum speed in meters per second for all airbases until a player gets kicked. This is overridden at each derived class. +} + +--- Creates a new ATC\_GROUND\_UNIVERSAL object. This works on any map. +-- @param #ATC_GROUND_UNIVERSAL self +-- @param AirbaseList (Optional) A table of Airbase Names. +-- @return #ATC_GROUND_UNIVERSAL self +function ATC_GROUND_UNIVERSAL:New(AirbaseList) + + -- Inherits from BASE + local self = BASE:Inherit( self, BASE:New() ) -- #ATC_GROUND + self:E( { self.ClassName } ) + + self.Airbases = {} + + for _name,_ in pairs(_DATABASE.AIRBASES) do + self.Airbases[_name]={} + end + + self.AirbaseList = AirbaseList + + self.SetClient = SET_CLIENT:New():FilterCategories( "plane" ):FilterStart() + + + for AirbaseID, Airbase in pairs( self.Airbases ) do + -- Specified ZoneBoundary is used if set or Airbase radius by default + if Airbase.ZoneBoundary then + Airbase.ZoneBoundary = ZONE_POLYGON_BASE:New( "Boundary " .. AirbaseID, Airbase.ZoneBoundary ) + else + Airbase.ZoneBoundary = _DATABASE:FindAirbase( AirbaseID ):GetZone() + end + + Airbase.ZoneRunways = AIRBASE:FindByName(AirbaseID):GetRunways() + Airbase.Monitor = self.AirbaseList and false or true -- When AirbaseList is not given, monitor every Airbase, otherwise don't monitor any (yet). + end + + -- Now activate the monitoring for the airbases that need to be monitored. + for AirbaseID, AirbaseName in pairs( self.AirbaseList or {} ) do + self.Airbases[AirbaseName].Monitor = true + end + + self.SetClient:ForEachClient( + --- @param Wrapper.Client#CLIENT Client + function( Client ) + Client:SetState( self, "Speeding", false ) + Client:SetState( self, "Warnings", 0) + Client:SetState( self, "IsOffRunway", false ) + Client:SetState( self, "OffRunwayWarnings", 0 ) + Client:SetState( self, "Taxi", false ) + end + ) + + -- This is simple slot blocker is used on the server. + SSB = USERFLAG:New( "SSB" ) + SSB:Set( 100 ) + + -- Kickspeed + self.KickSpeed = UTILS.KnotsToMps(10) + self:SetMaximumKickSpeedMiph(30) + + return self +end + + +--- Add a specific Airbase Boundary if you don't want to use the round zone that is auto-created. +-- @param #ATC_GROUND_UNIVERSAL self +-- @param #string Airbase The name of the Airbase +-- @param Core.Zone#ZONE Zone The ZONE object to be used, e.g. a ZONE_POLYGON +-- @return #ATC_GROUND_UNIVERSAL self +function ATC_GROUND_UNIVERSAL:SetAirbaseBoundaries(Airbase, Zone) + self.Airbases[Airbase].ZoneBoundary = Zone + return self +end + +--- Smoke the airbases runways. +-- @param #ATC_GROUND_UNIVERSAL self +-- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke around the runways. +-- @return #ATC_GROUND_UNIVERSAL self +function ATC_GROUND_UNIVERSAL:SmokeRunways( SmokeColor ) + + local SmokeColor = SmokeColor or SMOKECOLOR.Red + for AirbaseID, Airbase in pairs( self.Airbases ) do + if Airbase.ZoneRunways then + for _,_runwaydata in pairs (Airbase.ZoneRunways) do + local runwaydata = _runwaydata -- Wrapper.Airbase#AIRBASE.Runway + runwaydata.zone:SmokeZone(SmokeColor) + end + end + end + + return self +end + +--- Draw the airbases runways. +-- @param #ATC_GROUND_UNIVERSAL self +-- @param #table Color The color of the line around the runways, in RGB, e.g `{1,0,0}` for red. +-- @return #ATC_GROUND_UNIVERSAL self +function ATC_GROUND_UNIVERSAL:DrawRunways( Color ) + + local Color = Color or {1,0,0} + for AirbaseID, Airbase in pairs( self.Airbases ) do + if Airbase.ZoneRunways then + for _,_runwaydata in pairs (Airbase.ZoneRunways) do + local runwaydata = _runwaydata -- Wrapper.Airbase#AIRBASE.Runway + runwaydata.zone:DrawZone(-1,Color) + end + end + end + + return self +end + +--- Draw the airbases boundaries. +-- @param #ATC_GROUND_UNIVERSAL self +-- @param #table Color The color of the line around the runways, in RGB, e.g `{1,0,0}` for red. +-- @return #ATC_GROUND_UNIVERSAL self +function ATC_GROUND_UNIVERSAL:DrawBoundaries( Color ) + + local Color = Color or {1,0,0} + for AirbaseID, Airbase in pairs( self.Airbases ) do + if Airbase.ZoneBoundary then + Airbase.ZoneBoundary:DrawZone(-1, Color) + end + end + + return self +end + +--- Set the maximum speed in meters per second (Mps) until the player gets kicked. +-- An airbase can be specified to set the kick speed for. +-- @param #ATC_GROUND_UNIVERSAL self +-- @param #number KickSpeed The speed in Mps. +-- @param #string Airbase (optional) The airbase name to set the kick speed for. +-- @return #ATC_GROUND_UNIVERSAL self +-- @usage +-- +-- -- Declare Atc_Ground +-- +-- Atc_Ground = ATC_GROUND_UNIVERSAL:New() +-- +-- -- Then use one of these methods... +-- +-- Atc_Ground:SetKickSpeed( UTILS.KmphToMps( 80 ) ) -- Kick the players at 80 kilometers per hour +-- +-- Atc_Ground:SetKickSpeed( UTILS.MiphToMps( 100 ) ) -- Kick the players at 100 miles per hour +-- +-- Atc_Ground:SetKickSpeed( 24 ) -- Kick the players at 24 meters per second ( 24 * 3.6 = 86.4 kilometers per hour ) +-- +function ATC_GROUND_UNIVERSAL:SetKickSpeed( KickSpeed, Airbase ) + + if not Airbase then + self.KickSpeed = KickSpeed + else + self.Airbases[Airbase].KickSpeed = KickSpeed + end + + return self +end + +--- Set the maximum speed in Kmph until the player gets kicked. +-- @param #ATC_GROUND_UNIVERSAL self +-- @param #number KickSpeed Set the speed in Kmph. +-- @param #string Airbase (optional) The airbase name to set the kick speed for. +-- @return #ATC_GROUND_UNIVERSAL self +-- +-- Atc_Ground:SetKickSpeedKmph( 80 ) -- Kick the players at 80 kilometers per hour +-- +function ATC_GROUND_UNIVERSAL:SetKickSpeedKmph( KickSpeed, Airbase ) + + self:SetKickSpeed( UTILS.KmphToMps( KickSpeed ), Airbase ) + + return self +end + +--- Set the maximum speed in Miph until the player gets kicked. +-- @param #ATC_GROUND_UNIVERSAL self +-- @param #number KickSpeedMiph Set the speed in Mph. +-- @param #string Airbase (optional) The airbase name to set the kick speed for. +-- @return #ATC_GROUND_UNIVERSAL self +-- +-- Atc_Ground:SetKickSpeedMiph( 100 ) -- Kick the players at 100 miles per hour +-- +function ATC_GROUND_UNIVERSAL:SetKickSpeedMiph( KickSpeedMiph, Airbase ) + + self:SetKickSpeed( UTILS.MiphToMps( KickSpeedMiph ), Airbase ) + + return self +end + + +--- Set the maximum kick speed in meters per second (Mps) until the player gets kicked. +-- There are no warnings given if this speed is reached, and is to prevent players to take off from the airbase! +-- An airbase can be specified to set the maximum kick speed for. +-- @param #ATC_GROUND_UNIVERSAL self +-- @param #number MaximumKickSpeed The speed in Mps. +-- @param #string Airbase (optional) The airbase name to set the kick speed for. +-- @return #ATC_GROUND_UNIVERSAL self +-- @usage +-- +-- -- Declare Atc_Ground +-- +-- Atc_Ground = ATC_GROUND_UNIVERSAL:New() +-- +-- -- Then use one of these methods... +-- +-- Atc_Ground:SetMaximumKickSpeed( UTILS.KmphToMps( 80 ) ) -- Kick the players at 80 kilometers per hour +-- +-- Atc_Ground:SetMaximumKickSpeed( UTILS.MiphToMps( 100 ) ) -- Kick the players at 100 miles per hour +-- +-- Atc_Ground:SetMaximumKickSpeed( 24 ) -- Kick the players at 24 meters per second ( 24 * 3.6 = 86.4 kilometers per hour ) +-- +function ATC_GROUND_UNIVERSAL:SetMaximumKickSpeed( MaximumKickSpeed, Airbase ) + + if not Airbase then + self.MaximumKickSpeed = MaximumKickSpeed + else + self.Airbases[Airbase].MaximumKickSpeed = MaximumKickSpeed + end + + return self +end + +--- Set the maximum kick speed in kilometers per hour (Kmph) until the player gets kicked. +-- There are no warnings given if this speed is reached, and is to prevent players to take off from the airbase! +-- An airbase can be specified to set the maximum kick speed for. +-- @param #ATC_GROUND_UNIVERSAL self +-- @param #number MaximumKickSpeed Set the speed in Kmph. +-- @param #string Airbase (optional) The airbase name to set the kick speed for. +-- @return #ATC_GROUND_UNIVERSAL self +-- +-- Atc_Ground:SetMaximumKickSpeedKmph( 150 ) -- Kick the players at 150 kilometers per hour +-- +function ATC_GROUND_UNIVERSAL:SetMaximumKickSpeedKmph( MaximumKickSpeed, Airbase ) + + self:SetMaximumKickSpeed( UTILS.KmphToMps( MaximumKickSpeed ), Airbase ) + + return self +end + +--- Set the maximum kick speed in miles per hour (Miph) until the player gets kicked. +-- There are no warnings given if this speed is reached, and is to prevent players to take off from the airbase! +-- An airbase can be specified to set the maximum kick speed for. +-- @param #ATC_GROUND_UNIVERSAL self +-- @param #number MaximumKickSpeedMiph Set the speed in Mph. +-- @param #string Airbase (optional) The airbase name to set the kick speed for. +-- @return #ATC_GROUND_UNIVERSAL self +-- +-- Atc_Ground:SetMaximumKickSpeedMiph( 100 ) -- Kick the players at 100 miles per hour +-- +function ATC_GROUND_UNIVERSAL:SetMaximumKickSpeedMiph( MaximumKickSpeedMiph, Airbase ) + + self:SetMaximumKickSpeed( UTILS.MiphToMps( MaximumKickSpeedMiph ), Airbase ) + + return self +end + +--- [Internal] Monitoring function +-- @param #ATC_GROUND_UNIVERSAL self +-- @return #ATC_GROUND_UNIVERSAL self +function ATC_GROUND_UNIVERSAL:_AirbaseMonitor() + + self.SetClient:ForEachClient( + --- @param Wrapper.Client#CLIENT Client + function( Client ) + + if Client:IsAlive() then + + local IsOnGround = Client:InAir() == false + + for AirbaseID, AirbaseMeta in pairs( self.Airbases ) do + self:E( AirbaseID, AirbaseMeta.KickSpeed ) + + if AirbaseMeta.Monitor == true and Client:IsInZone( AirbaseMeta.ZoneBoundary ) then + + local NotInRunwayZone = true + + if AirbaseMeta.ZoneRunways then + for _,_runwaydata in pairs (AirbaseMeta.ZoneRunways) do + local runwaydata = _runwaydata -- Wrapper.Airbase#AIRBASE.Runway + NotInRunwayZone = ( Client:IsNotInZone( _runwaydata.zone ) == true ) and NotInRunwayZone or false + end + end + + if NotInRunwayZone then + + if IsOnGround then + local Taxi = Client:GetState( self, "Taxi" ) + self:E( Taxi ) + if Taxi == false then + local Velocity = VELOCITY:New( AirbaseMeta.KickSpeed or self.KickSpeed ) + Client:Message( "Welcome to " .. AirbaseID .. ". The maximum taxiing speed is " .. + Velocity:ToString() , 20, "ATC" ) + Client:SetState( self, "Taxi", true ) + end + + -- TODO: GetVelocityKMH function usage + local Velocity = VELOCITY_POSITIONABLE:New( Client ) + --MESSAGE:New( "Velocity = " .. Velocity:ToString(), 1 ):ToAll() + local IsAboveRunway = Client:IsAboveRunway() + self:T( {IsAboveRunway, IsOnGround, Velocity:Get() }) + + if IsOnGround then + local Speeding = false + if AirbaseMeta.MaximumKickSpeed then + if Velocity:Get() > AirbaseMeta.MaximumKickSpeed then + Speeding = true + end + else + if Velocity:Get() > self.MaximumKickSpeed then + Speeding = true + end + end + if Speeding == true then + MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. + " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll() + Client:Destroy() + Client:SetState( self, "Speeding", false ) + Client:SetState( self, "Warnings", 0 ) + end + end + + + if IsOnGround then + + local Speeding = false + if AirbaseMeta.KickSpeed then -- If there is a speed defined for the airbase, use that only. + if Velocity:Get() > AirbaseMeta.KickSpeed then + Speeding = true + end + else + if Velocity:Get() > self.KickSpeed then + Speeding = true + end + end + if Speeding == true then + local IsSpeeding = Client:GetState( self, "Speeding" ) + + if IsSpeeding == true then + local SpeedingWarnings = Client:GetState( self, "Warnings" ) + self:T( SpeedingWarnings ) + + if SpeedingWarnings <= 3 then + Client:Message( "Warning " .. SpeedingWarnings .. "/3! Airbase traffic rule violation! Slow down now! Your speed is " .. + Velocity:ToString(), 5, "ATC" ) + Client:SetState( self, "Warnings", SpeedingWarnings + 1 ) + else + MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll() + --- @param Wrapper.Client#CLIENT Client + Client:Destroy() + Client:SetState( self, "Speeding", false ) + Client:SetState( self, "Warnings", 0 ) + end + + else + Client:Message( "Attention! You are speeding on the taxiway, slow down! Your speed is " .. + Velocity:ToString(), 5, "ATC" ) + Client:SetState( self, "Speeding", true ) + Client:SetState( self, "Warnings", 1 ) + end + + else + Client:SetState( self, "Speeding", false ) + Client:SetState( self, "Warnings", 0 ) + end + end + + if IsOnGround and not IsAboveRunway then + + local IsOffRunway = Client:GetState( self, "IsOffRunway" ) + + if IsOffRunway == true then + local OffRunwayWarnings = Client:GetState( self, "OffRunwayWarnings" ) + self:T( OffRunwayWarnings ) + + if OffRunwayWarnings <= 3 then + Client:Message( "Warning " .. OffRunwayWarnings .. "/3! Airbase traffic rule violation! Get back on the taxi immediately!", 5, "ATC" ) + Client:SetState( self, "OffRunwayWarnings", OffRunwayWarnings + 1 ) + else + MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll() + --- @param Wrapper.Client#CLIENT Client + Client:Destroy() + Client:SetState( self, "IsOffRunway", false ) + Client:SetState( self, "OffRunwayWarnings", 0 ) + end + else + Client:Message( "Attention! You are off the taxiway. Get back on the taxiway immediately!", 5, "ATC" ) + Client:SetState( self, "IsOffRunway", true ) + Client:SetState( self, "OffRunwayWarnings", 1 ) + end + + else + Client:SetState( self, "IsOffRunway", false ) + Client:SetState( self, "OffRunwayWarnings", 0 ) + end + end + else + Client:SetState( self, "Speeding", false ) + Client:SetState( self, "Warnings", 0 ) + Client:SetState( self, "IsOffRunway", false ) + Client:SetState( self, "OffRunwayWarnings", 0 ) + local Taxi = Client:GetState( self, "Taxi" ) + if Taxi == true then + Client:Message( "You have progressed to the runway ... Await take-off clearance ...", 20, "ATC" ) + Client:SetState( self, "Taxi", false ) + end + end + end + end + else + Client:SetState( self, "Taxi", false ) + end + end + ) + + return true +end + +--- Start SCHEDULER for ATC_GROUND_UNIVERSAL object. +-- @param #ATC_GROUND_UNIVERSAL self +-- @param RepeatScanSeconds Time in second for defining occurency of alerts. +-- @return #ATC_GROUND_UNIVERSAL self +function ATC_GROUND_UNIVERSAL:Start( RepeatScanSeconds ) + RepeatScanSeconds = RepeatScanSeconds or 0.05 + self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds ) + return self +end --- @type ATC_GROUND_CAUCASUS -- @extends #ATC_GROUND ---- # ATC\_GROUND\_CAUCASUS, extends @{#ATC_GROUND} +--- # ATC\_GROUND\_CAUCASUS, extends @{#ATC_GROUND_UNIVERSAL} -- -- The ATC\_GROUND\_CAUCASUS class monitors the speed of the airplanes at the airbase during taxi. -- The pilots may not drive faster than the maximum speed for the airbase, or they will be despawned. @@ -520,269 +957,6 @@ end -- @field #ATC_GROUND_CAUCASUS ATC_GROUND_CAUCASUS = { ClassName = "ATC_GROUND_CAUCASUS", - Airbases = { - [AIRBASE.Caucasus.Anapa_Vityazevo] = { - PointsRunways = { - [1] = { - [1]={["y"]=242140.57142858,["x"]=-6478.8571428583,}, - [2]={["y"]=242188.57142858,["x"]=-6522.0000000011,}, - [3]={["y"]=244124.2857143,["x"]=-4344.0000000011,}, - [4]={["y"]=244068.2857143,["x"]=-4296.5714285726,}, - [5]={["y"]=242140.57142858,["x"]=-6480.0000000011,} - }, - }, - }, - [AIRBASE.Caucasus.Batumi] = { - PointsRunways = { - [1] = { - [1]={["y"]=616442.28571429,["x"]=-355090.28571429,}, - [2]={["y"]=618450.57142857,["x"]=-356522,}, - [3]={["y"]=618407.71428571,["x"]=-356584.85714286,}, - [4]={["y"]=618361.99999999,["x"]=-356554.85714286,}, - [5]={["y"]=618324.85714285,["x"]=-356599.14285715,}, - [6]={["y"]=618250.57142856,["x"]=-356543.42857143,}, - [7]={["y"]=618257.7142857,["x"]=-356496.28571429,}, - [8]={["y"]=618237.7142857,["x"]=-356459.14285715,}, - [9]={["y"]=616555.71428571,["x"]=-355258.85714286,}, - [10]={["y"]=616486.28571428,["x"]=-355280.57142858,}, - [11]={["y"]=616410.57142856,["x"]=-355227.71428572,}, - [12]={["y"]=616441.99999999,["x"]=-355179.14285715,}, - [13]={["y"]=616401.99999999,["x"]=-355147.71428572,}, - [14]={["y"]=616441.42857142,["x"]=-355092.57142858,}, - }, - }, - }, - [AIRBASE.Caucasus.Beslan] = { - PointsRunways = { - [1] = { - [1]={["y"]=842104.57142857,["x"]=-148460.57142857,}, - [2]={["y"]=845225.71428572,["x"]=-148656,}, - [3]={["y"]=845220.57142858,["x"]=-148750,}, - [4]={["y"]=842098.85714286,["x"]=-148556.28571429,}, - [5]={["y"]=842104,["x"]=-148460.28571429,}, - }, - }, - }, - [AIRBASE.Caucasus.Gelendzhik] = { - PointsRunways = { - [1] = { - [1]={["y"]=297834.00000001,["x"]=-51107.428571429,}, - [2]={["y"]=297786.57142858,["x"]=-51068.857142858,}, - [3]={["y"]=298946.57142858,["x"]=-49686.000000001,}, - [4]={["y"]=298993.14285715,["x"]=-49725.714285715,}, - [5]={["y"]=297835.14285715,["x"]=-51107.714285715,}, - }, - }, - }, - [AIRBASE.Caucasus.Gudauta] = { - PointsRunways = { - [1] = { - [1]={["y"]=517096.57142857,["x"]=-197804.57142857,}, - [2]={["y"]=515880.85714285,["x"]=-195590.28571429,}, - [3]={["y"]=515812.28571428,["x"]=-195628.85714286,}, - [4]={["y"]=517036.57142857,["x"]=-197834.57142857,}, - [5]={["y"]=517097.99999999,["x"]=-197807.42857143,}, - }, - }, - }, - [AIRBASE.Caucasus.Kobuleti] = { - PointsRunways = { - [1] = { - [1]={["y"]=634509.71428571,["x"]=-318339.42857144,}, - [2]={["y"]=636767.42857143,["x"]=-317516.57142858,}, - [3]={["y"]=636790,["x"]=-317575.71428572,}, - [4]={["y"]=634531.42857143,["x"]=-318398.00000001,}, - [5]={["y"]=634510.28571429,["x"]=-318339.71428572,}, - }, - }, - }, - [AIRBASE.Caucasus.Krasnodar_Center] = { - PointsRunways = { - [1] = { - [1]={["y"]=369205.42857144,["x"]=11789.142857142,}, - [2]={["y"]=369209.71428572,["x"]=11714.857142856,}, - [3]={["y"]=366699.71428572,["x"]=11581.714285713,}, - [4]={["y"]=366698.28571429,["x"]=11659.142857142,}, - [5]={["y"]=369208.85714286,["x"]=11788.57142857,}, - }, - }, - }, - [AIRBASE.Caucasus.Krasnodar_Pashkovsky] = { - PointsRunways = { - [1] = { - [1]={["y"]=385891.14285715,["x"]=8416.5714285703,}, - [2]={["y"]=385842.28571429,["x"]=8467.9999999989,}, - [3]={["y"]=384180.85714286,["x"]=6917.1428571417,}, - [4]={["y"]=384228.57142858,["x"]=6867.7142857132,}, - [5]={["y"]=385891.14285715,["x"]=8416.5714285703,}, - }, - [2] = { - [1]={["y"]=386714.85714286,["x"]=6674.857142856,}, - [2]={["y"]=386757.71428572,["x"]=6627.7142857132,}, - [3]={["y"]=389028.57142858,["x"]=8741.4285714275,}, - [4]={["y"]=388981.71428572,["x"]=8790.5714285703,}, - [5]={["y"]=386714.57142858,["x"]=6674.5714285703,}, - }, - }, - }, - [AIRBASE.Caucasus.Krymsk] = { - PointsRunways = { - [1] = { - [1]={["y"]=293522.00000001,["x"]=-7567.4285714297,}, - [2]={["y"]=293578.57142858,["x"]=-7616.0000000011,}, - [3]={["y"]=295246.00000001,["x"]=-5591.142857144,}, - [4]={["y"]=295187.71428573,["x"]=-5546.0000000011,}, - [5]={["y"]=293523.14285715,["x"]=-7568.2857142868,}, - }, - }, - }, - [AIRBASE.Caucasus.Kutaisi] = { - PointsRunways = { - [1] = { - [1]={["y"]=682638,["x"]=-285202.28571429,}, - [2]={["y"]=685050.28571429,["x"]=-284507.42857144,}, - [3]={["y"]=685068.85714286,["x"]=-284578.85714286,}, - [4]={["y"]=682657.42857143,["x"]=-285264.28571429,}, - [5]={["y"]=682638.28571429,["x"]=-285202.85714286,}, - }, - }, - }, - [AIRBASE.Caucasus.Maykop_Khanskaya] = { - PointsRunways = { - [1] = { - [1]={["y"]=457005.42857143,["x"]=-27668.000000001,}, - [2]={["y"]=459028.85714286,["x"]=-25168.857142858,}, - [3]={["y"]=459082.57142857,["x"]=-25216.857142858,}, - [4]={["y"]=457060,["x"]=-27714.285714287,}, - [5]={["y"]=457004.57142857,["x"]=-27669.714285715,}, - }, - }, - }, - [AIRBASE.Caucasus.Mineralnye_Vody] = { - PointsRunways = { - [1] = { - [1]={["y"]=703904,["x"]=-50352.571428573,}, - [2]={["y"]=707596.28571429,["x"]=-52094.571428573,}, - [3]={["y"]=707560.57142858,["x"]=-52161.714285716,}, - [4]={["y"]=703871.71428572,["x"]=-50420.571428573,}, - [5]={["y"]=703902,["x"]=-50352.000000002,}, - }, - }, - }, - [AIRBASE.Caucasus.Mozdok] = { - PointsRunways = { - [1] = { - [1]={["y"]=832201.14285715,["x"]=-83699.428571431,}, - [2]={["y"]=832212.57142857,["x"]=-83780.571428574,}, - [3]={["y"]=835730.28571429,["x"]=-83335.714285717,}, - [4]={["y"]=835718.85714286,["x"]=-83246.571428574,}, - [5]={["y"]=832200.57142857,["x"]=-83700.000000002,}, - }, - }, - }, - [AIRBASE.Caucasus.Nalchik] = { - PointsRunways = { - [1] = { - [1]={["y"]=759454.28571429,["x"]=-125551.42857143,}, - [2]={["y"]=759492.85714286,["x"]=-125610.85714286,}, - [3]={["y"]=761406.28571429,["x"]=-124304.28571429,}, - [4]={["y"]=761361.14285714,["x"]=-124239.71428572,}, - [5]={["y"]=759456,["x"]=-125552.57142857,}, - }, - }, - }, - [AIRBASE.Caucasus.Novorossiysk] = { - PointsRunways = { - [1] = { - [1]={["y"]=278673.14285716,["x"]=-41615.142857144,}, - [2]={["y"]=278625.42857144,["x"]=-41570.571428572,}, - [3]={["y"]=279835.42857144,["x"]=-40226.000000001,}, - [4]={["y"]=279882.2857143,["x"]=-40270.000000001,}, - [5]={["y"]=278672.00000001,["x"]=-41614.857142858,}, - }, - }, - }, - [AIRBASE.Caucasus.Senaki_Kolkhi] = { - PointsRunways = { - [1] = { - [1]={["y"]=646060.85714285,["x"]=-281736,}, - [2]={["y"]=646056.57142857,["x"]=-281631.71428571,}, - [3]={["y"]=648442.28571428,["x"]=-281840.28571428,}, - [4]={["y"]=648432.28571428,["x"]=-281918.85714286,}, - [5]={["y"]=646063.71428571,["x"]=-281738.85714286,}, - }, - }, - }, - [AIRBASE.Caucasus.Sochi_Adler] = { - PointsRunways = { - [1] = { - [1]={["y"]=460831.42857143,["x"]=-165180,}, - [2]={["y"]=460878.57142857,["x"]=-165257.14285714,}, - [3]={["y"]=463663.71428571,["x"]=-163793.14285714,}, - [4]={["y"]=463612.28571428,["x"]=-163697.42857143,}, - [5]={["y"]=460831.42857143,["x"]=-165177.14285714,}, - }, - [2] = { - [1]={["y"]=460831.42857143,["x"]=-165180,}, - [2]={["y"]=460878.57142857,["x"]=-165257.14285714,}, - [3]={["y"]=463663.71428571,["x"]=-163793.14285714,}, - [4]={["y"]=463612.28571428,["x"]=-163697.42857143,}, - [5]={["y"]=460831.42857143,["x"]=-165177.14285714,}, - }, - }, - }, - [AIRBASE.Caucasus.Soganlug] = { - PointsRunways = { - [1] = { - [1]={["y"]=894525.71428571,["x"]=-316964,}, - [2]={["y"]=896363.14285714,["x"]=-318634.28571428,}, - [3]={["y"]=896299.14285714,["x"]=-318702.85714286,}, - [4]={["y"]=894464,["x"]=-317031.71428571,}, - [5]={["y"]=894524.57142857,["x"]=-316963.71428571,}, - }, - }, - }, - [AIRBASE.Caucasus.Sukhumi_Babushara] = { - PointsRunways = { - [1] = { - [1]={["y"]=562684,["x"]=-219779.71428571,}, - [2]={["y"]=562717.71428571,["x"]=-219718,}, - [3]={["y"]=566046.85714286,["x"]=-221376.57142857,}, - [4]={["y"]=566012.28571428,["x"]=-221446.57142857,}, - [5]={["y"]=562684.57142857,["x"]=-219782.57142857,}, - }, - }, - }, - [AIRBASE.Caucasus.Tbilisi_Lochini] = { - PointsRunways = { - [1] = { - [1]={["y"]=895261.14285715,["x"]=-314652.28571428,}, - [2]={["y"]=897654.57142857,["x"]=-316523.14285714,}, - [3]={["y"]=897711.71428571,["x"]=-316450.28571429,}, - [4]={["y"]=895327.42857143,["x"]=-314568.85714286,}, - [5]={["y"]=895261.71428572,["x"]=-314656,}, - }, - [2] = { - [1]={["y"]=895605.71428572,["x"]=-314724.57142857,}, - [2]={["y"]=897639.71428572,["x"]=-316148,}, - [3]={["y"]=897683.42857143,["x"]=-316087.14285714,}, - [4]={["y"]=895650,["x"]=-314660,}, - [5]={["y"]=895606,["x"]=-314724.85714286,} - }, - }, - }, - [AIRBASE.Caucasus.Vaziani] = { - PointsRunways = { - [1] = { - [1]={["y"]=902239.14285714,["x"]=-318190.85714286,}, - [2]={["y"]=904014.28571428,["x"]=-319994.57142857,}, - [3]={["y"]=904064.85714285,["x"]=-319945.14285715,}, - [4]={["y"]=902294.57142857,["x"]=-318146,}, - [5]={["y"]=902247.71428571,["x"]=-318190.85714286,}, - }, - }, - }, - }, } --- Creates a new ATC_GROUND_CAUCASUS object. @@ -792,216 +966,11 @@ ATC_GROUND_CAUCASUS = { function ATC_GROUND_CAUCASUS:New( AirbaseNames ) -- Inherits from BASE - local self = BASE:Inherit( self, ATC_GROUND:New( self.Airbases, AirbaseNames ) ) + local self = BASE:Inherit( self, ATC_GROUND_UNIVERSAL:New(AirbaseNames) ) self:SetKickSpeedKmph( 50 ) self:SetMaximumKickSpeedKmph( 150 ) - -- -- AnapaVityazevo - -- local AnapaVityazevoBoundary = GROUP:FindByName( "AnapaVityazevo Boundary" ) - -- self.Airbases.AnapaVityazevo.ZoneBoundary = ZONE_POLYGON:New( "AnapaVityazevo Boundary", AnapaVityazevoBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local AnapaVityazevoRunway1 = GROUP:FindByName( "AnapaVityazevo Runway 1" ) - -- self.Airbases.AnapaVityazevo.ZoneRunways[1] = ZONE_POLYGON:New( "AnapaVityazevo Runway 1", AnapaVityazevoRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Batumi - -- local BatumiBoundary = GROUP:FindByName( "Batumi Boundary" ) - -- self.Airbases.Batumi.ZoneBoundary = ZONE_POLYGON:New( "Batumi Boundary", BatumiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local BatumiRunway1 = GROUP:FindByName( "Batumi Runway 1" ) - -- self.Airbases.Batumi.ZoneRunways[1] = ZONE_POLYGON:New( "Batumi Runway 1", BatumiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Beslan - -- local BeslanBoundary = GROUP:FindByName( "Beslan Boundary" ) - -- self.Airbases.Beslan.ZoneBoundary = ZONE_POLYGON:New( "Beslan Boundary", BeslanBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local BeslanRunway1 = GROUP:FindByName( "Beslan Runway 1" ) - -- self.Airbases.Beslan.ZoneRunways[1] = ZONE_POLYGON:New( "Beslan Runway 1", BeslanRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Gelendzhik - -- local GelendzhikBoundary = GROUP:FindByName( "Gelendzhik Boundary" ) - -- self.Airbases.Gelendzhik.ZoneBoundary = ZONE_POLYGON:New( "Gelendzhik Boundary", GelendzhikBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local GelendzhikRunway1 = GROUP:FindByName( "Gelendzhik Runway 1" ) - -- self.Airbases.Gelendzhik.ZoneRunways[1] = ZONE_POLYGON:New( "Gelendzhik Runway 1", GelendzhikRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Gudauta - -- local GudautaBoundary = GROUP:FindByName( "Gudauta Boundary" ) - -- self.Airbases.Gudauta.ZoneBoundary = ZONE_POLYGON:New( "Gudauta Boundary", GudautaBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local GudautaRunway1 = GROUP:FindByName( "Gudauta Runway 1" ) - -- self.Airbases.Gudauta.ZoneRunways[1] = ZONE_POLYGON:New( "Gudauta Runway 1", GudautaRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Kobuleti - -- local KobuletiBoundary = GROUP:FindByName( "Kobuleti Boundary" ) - -- self.Airbases.Kobuleti.ZoneBoundary = ZONE_POLYGON:New( "Kobuleti Boundary", KobuletiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KobuletiRunway1 = GROUP:FindByName( "Kobuleti Runway 1" ) - -- self.Airbases.Kobuleti.ZoneRunways[1] = ZONE_POLYGON:New( "Kobuleti Runway 1", KobuletiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- KrasnodarCenter - -- local KrasnodarCenterBoundary = GROUP:FindByName( "KrasnodarCenter Boundary" ) - -- self.Airbases.KrasnodarCenter.ZoneBoundary = ZONE_POLYGON:New( "KrasnodarCenter Boundary", KrasnodarCenterBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KrasnodarCenterRunway1 = GROUP:FindByName( "KrasnodarCenter Runway 1" ) - -- self.Airbases.KrasnodarCenter.ZoneRunways[1] = ZONE_POLYGON:New( "KrasnodarCenter Runway 1", KrasnodarCenterRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- KrasnodarPashkovsky - -- local KrasnodarPashkovskyBoundary = GROUP:FindByName( "KrasnodarPashkovsky Boundary" ) - -- self.Airbases.KrasnodarPashkovsky.ZoneBoundary = ZONE_POLYGON:New( "KrasnodarPashkovsky Boundary", KrasnodarPashkovskyBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KrasnodarPashkovskyRunway1 = GROUP:FindByName( "KrasnodarPashkovsky Runway 1" ) - -- self.Airbases.KrasnodarPashkovsky.ZoneRunways[1] = ZONE_POLYGON:New( "KrasnodarPashkovsky Runway 1", KrasnodarPashkovskyRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- local KrasnodarPashkovskyRunway2 = GROUP:FindByName( "KrasnodarPashkovsky Runway 2" ) - -- self.Airbases.KrasnodarPashkovsky.ZoneRunways[2] = ZONE_POLYGON:New( "KrasnodarPashkovsky Runway 2", KrasnodarPashkovskyRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Krymsk - -- local KrymskBoundary = GROUP:FindByName( "Krymsk Boundary" ) - -- self.Airbases.Krymsk.ZoneBoundary = ZONE_POLYGON:New( "Krymsk Boundary", KrymskBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KrymskRunway1 = GROUP:FindByName( "Krymsk Runway 1" ) - -- self.Airbases.Krymsk.ZoneRunways[1] = ZONE_POLYGON:New( "Krymsk Runway 1", KrymskRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Kutaisi - -- local KutaisiBoundary = GROUP:FindByName( "Kutaisi Boundary" ) - -- self.Airbases.Kutaisi.ZoneBoundary = ZONE_POLYGON:New( "Kutaisi Boundary", KutaisiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KutaisiRunway1 = GROUP:FindByName( "Kutaisi Runway 1" ) - -- self.Airbases.Kutaisi.ZoneRunways[1] = ZONE_POLYGON:New( "Kutaisi Runway 1", KutaisiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- MaykopKhanskaya - -- local MaykopKhanskayaBoundary = GROUP:FindByName( "MaykopKhanskaya Boundary" ) - -- self.Airbases.MaykopKhanskaya.ZoneBoundary = ZONE_POLYGON:New( "MaykopKhanskaya Boundary", MaykopKhanskayaBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local MaykopKhanskayaRunway1 = GROUP:FindByName( "MaykopKhanskaya Runway 1" ) - -- self.Airbases.MaykopKhanskaya.ZoneRunways[1] = ZONE_POLYGON:New( "MaykopKhanskaya Runway 1", MaykopKhanskayaRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- MineralnyeVody - -- local MineralnyeVodyBoundary = GROUP:FindByName( "MineralnyeVody Boundary" ) - -- self.Airbases.MineralnyeVody.ZoneBoundary = ZONE_POLYGON:New( "MineralnyeVody Boundary", MineralnyeVodyBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local MineralnyeVodyRunway1 = GROUP:FindByName( "MineralnyeVody Runway 1" ) - -- self.Airbases.MineralnyeVody.ZoneRunways[1] = ZONE_POLYGON:New( "MineralnyeVody Runway 1", MineralnyeVodyRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Mozdok - -- local MozdokBoundary = GROUP:FindByName( "Mozdok Boundary" ) - -- self.Airbases.Mozdok.ZoneBoundary = ZONE_POLYGON:New( "Mozdok Boundary", MozdokBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local MozdokRunway1 = GROUP:FindByName( "Mozdok Runway 1" ) - -- self.Airbases.Mozdok.ZoneRunways[1] = ZONE_POLYGON:New( "Mozdok Runway 1", MozdokRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Nalchik - -- local NalchikBoundary = GROUP:FindByName( "Nalchik Boundary" ) - -- self.Airbases.Nalchik.ZoneBoundary = ZONE_POLYGON:New( "Nalchik Boundary", NalchikBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local NalchikRunway1 = GROUP:FindByName( "Nalchik Runway 1" ) - -- self.Airbases.Nalchik.ZoneRunways[1] = ZONE_POLYGON:New( "Nalchik Runway 1", NalchikRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Novorossiysk - -- local NovorossiyskBoundary = GROUP:FindByName( "Novorossiysk Boundary" ) - -- self.Airbases.Novorossiysk.ZoneBoundary = ZONE_POLYGON:New( "Novorossiysk Boundary", NovorossiyskBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local NovorossiyskRunway1 = GROUP:FindByName( "Novorossiysk Runway 1" ) - -- self.Airbases.Novorossiysk.ZoneRunways[1] = ZONE_POLYGON:New( "Novorossiysk Runway 1", NovorossiyskRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- SenakiKolkhi - -- local SenakiKolkhiBoundary = GROUP:FindByName( "SenakiKolkhi Boundary" ) - -- self.Airbases.SenakiKolkhi.ZoneBoundary = ZONE_POLYGON:New( "SenakiKolkhi Boundary", SenakiKolkhiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local SenakiKolkhiRunway1 = GROUP:FindByName( "SenakiKolkhi Runway 1" ) - -- self.Airbases.SenakiKolkhi.ZoneRunways[1] = ZONE_POLYGON:New( "SenakiKolkhi Runway 1", SenakiKolkhiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- SochiAdler - -- local SochiAdlerBoundary = GROUP:FindByName( "SochiAdler Boundary" ) - -- self.Airbases.SochiAdler.ZoneBoundary = ZONE_POLYGON:New( "SochiAdler Boundary", SochiAdlerBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local SochiAdlerRunway1 = GROUP:FindByName( "SochiAdler Runway 1" ) - -- self.Airbases.SochiAdler.ZoneRunways[1] = ZONE_POLYGON:New( "SochiAdler Runway 1", SochiAdlerRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- local SochiAdlerRunway2 = GROUP:FindByName( "SochiAdler Runway 2" ) - -- self.Airbases.SochiAdler.ZoneRunways[2] = ZONE_POLYGON:New( "SochiAdler Runway 2", SochiAdlerRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Soganlug - -- local SoganlugBoundary = GROUP:FindByName( "Soganlug Boundary" ) - -- self.Airbases.Soganlug.ZoneBoundary = ZONE_POLYGON:New( "Soganlug Boundary", SoganlugBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local SoganlugRunway1 = GROUP:FindByName( "Soganlug Runway 1" ) - -- self.Airbases.Soganlug.ZoneRunways[1] = ZONE_POLYGON:New( "Soganlug Runway 1", SoganlugRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- SukhumiBabushara - -- local SukhumiBabusharaBoundary = GROUP:FindByName( "SukhumiBabushara Boundary" ) - -- self.Airbases.SukhumiBabushara.ZoneBoundary = ZONE_POLYGON:New( "SukhumiBabushara Boundary", SukhumiBabusharaBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local SukhumiBabusharaRunway1 = GROUP:FindByName( "SukhumiBabushara Runway 1" ) - -- self.Airbases.SukhumiBabushara.ZoneRunways[1] = ZONE_POLYGON:New( "SukhumiBabushara Runway 1", SukhumiBabusharaRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- TbilisiLochini - -- local TbilisiLochiniBoundary = GROUP:FindByName( "TbilisiLochini Boundary" ) - -- self.Airbases.TbilisiLochini.ZoneBoundary = ZONE_POLYGON:New( "TbilisiLochini Boundary", TbilisiLochiniBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local TbilisiLochiniRunway1 = GROUP:FindByName( "TbilisiLochini Runway 1" ) - -- self.Airbases.TbilisiLochini.ZoneRunways[1] = ZONE_POLYGON:New( "TbilisiLochini Runway 1", TbilisiLochiniRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- local TbilisiLochiniRunway2 = GROUP:FindByName( "TbilisiLochini Runway 2" ) - -- self.Airbases.TbilisiLochini.ZoneRunways[2] = ZONE_POLYGON:New( "TbilisiLochini Runway 2", TbilisiLochiniRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Vaziani - -- local VazianiBoundary = GROUP:FindByName( "Vaziani Boundary" ) - -- self.Airbases.Vaziani.ZoneBoundary = ZONE_POLYGON:New( "Vaziani Boundary", VazianiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local VazianiRunway1 = GROUP:FindByName( "Vaziani Runway 1" ) - -- self.Airbases.Vaziani.ZoneRunways[1] = ZONE_POLYGON:New( "Vaziani Runway 1", VazianiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - - - -- Template - -- local TemplateBoundary = GROUP:FindByName( "Template Boundary" ) - -- self.Airbases.Template.ZoneBoundary = ZONE_POLYGON:New( "Template Boundary", TemplateBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local TemplateRunway1 = GROUP:FindByName( "Template Runway 1" ) - -- self.Airbases.Template.ZoneRunways[1] = ZONE_POLYGON:New( "Template Runway 1", TemplateRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - return self end @@ -1128,257 +1097,6 @@ end -- @field #ATC_GROUND_NEVADA ATC_GROUND_NEVADA = { ClassName = "ATC_GROUND_NEVADA", - Airbases = { - - [AIRBASE.Nevada.Beatty_Airport] = { - PointsRunways = { - [1] = { - [1]={["y"]=-174950.05857143,["x"]=-329679.65,}, - [2]={["y"]=-174946.53828571,["x"]=-331394.03885715,}, - [3]={["y"]=-174967.10971429,["x"]=-331394.32457143,}, - [4]={["y"]=-174971.01828571,["x"]=-329682.59171429,}, - }, - }, - }, - [AIRBASE.Nevada.Boulder_City_Airport] = { - PointsRunways = { - [1] = { - [1] = {["y"]=-1317.841714286,["x"]=-429014.92857142,}, - [2] = {["y"]=-951.26228571458,["x"]=-430310.21142856,}, - [3] = {["y"]=-978.11942857172,["x"]=-430317.06857142,}, - [4] = {["y"]=-1347.5088571432,["x"]=-429023.98485713,}, - }, - [2] = { - [1] = {["y"]=-1879.955714286,["x"]=-429783.83742856,}, - [2] = {["y"]=-256.25257142886,["x"]=-430023.63542856,}, - [3] = {["y"]=-260.25257142886,["x"]=-430048.77828571,}, - [4] = {["y"]=-1883.955714286,["x"]=-429807.83742856,}, - }, - }, - }, - [AIRBASE.Nevada.Creech_AFB] = { - PointsRunways = { - [1] = { - [1] = {["y"]=-74234.729142857,["x"]=-360501.80857143,}, - [2] = {["y"]=-77606.122285714,["x"]=-360417.86542857,}, - [3] = {["y"]=-77608.578,["x"]=-360486.13428571,}, - [4] = {["y"]=-74237.930571428,["x"]=-360586.25628571,}, - }, - [2] = { - [1] = {["y"]=-75807.571428572,["x"]=-359073.42857142,}, - [2] = {["y"]=-74770.142857144,["x"]=-360581.71428571,}, - [3] = {["y"]=-74641.285714287,["x"]=-360585.42857142,}, - [4] = {["y"]=-75734.142857144,["x"]=-359023.14285714,}, - }, - }, - }, - [AIRBASE.Nevada.Echo_Bay] = { - PointsRunways = { - [1] = { - [1] = {["y"]=33182.919428572,["x"]=-388698.21657142,}, - [2] = {["y"]=34202.543142857,["x"]=-388469.55485714,}, - [3] = {["y"]=34207.686,["x"]=-388488.69771428,}, - [4] = {["y"]=33185.422285715,["x"]=-388717.82228571,}, - }, - }, - }, - [AIRBASE.Nevada.Groom_Lake_AFB] = { - PointsRunways = { - [1] = { - [1] = {["y"]=-85971.465428571,["x"]=-290567.77,}, - [2] = {["y"]=-87691.155428571,["x"]=-286637.75428571,}, - [3] = {["y"]=-87756.714285715,["x"]=-286663.99999999,}, - [4] = {["y"]=-86035.940285714,["x"]=-290598.81314286,}, - }, - [2] = { - [1] = {["y"]=-86741.547142857,["x"]=-290353.31971428,}, - [2] = {["y"]=-89672.714285714,["x"]=-283546.57142855,}, - [3] = {["y"]=-89772.142857143,["x"]=-283587.71428569,}, - [4] = {["y"]=-86799.623714285,["x"]=-290374.16771428,}, - }, - }, - }, - [AIRBASE.Nevada.Henderson_Executive_Airport] = { - PointsRunways = { - [1] = { - [1] = {["y"]=-25837.500571429,["x"]=-426404.25257142,}, - [2] = {["y"]=-25843.509428571,["x"]=-428752.67942856,}, - [3] = {["y"]=-25902.343714286,["x"]=-428749.96399999,}, - [4] = {["y"]=-25934.667142857,["x"]=-426411.45657142,}, - }, - [2] = { - [1] = {["y"]=-25650.296285714,["x"]=-426510.17971428,}, - [2] = {["y"]=-25632.443428571,["x"]=-428297.11428571,}, - [3] = {["y"]=-25686.690285714,["x"]=-428299.37457142,}, - [4] = {["y"]=-25708.296285714,["x"]=-426515.15114285,}, - }, - }, - }, - [AIRBASE.Nevada.Jean_Airport] = { - PointsRunways = { - [1] = { - [1] = {["y"]=-42549.187142857,["x"]=-449663.23257143,}, - [2] = {["y"]=-43367.466285714,["x"]=-451044.77657143,}, - [3] = {["y"]=-43395.180571429,["x"]=-451028.20514286,}, - [4] = {["y"]=-42579.893142857,["x"]=-449648.18371428,}, - }, - [2] = { - [1] = {["y"]=-42588.359428572,["x"]=-449900.14342857,}, - [2] = {["y"]=-43349.698285714,["x"]=-451185.46857143,}, - [3] = {["y"]=-43369.624571429,["x"]=-451173.49342857,}, - [4] = {["y"]=-42609.216571429,["x"]=-449891.28628571,}, - }, - }, - }, - [AIRBASE.Nevada.Laughlin_Airport] = { - PointsRunways = { - [1] = { - [1] = {["y"]=28231.600857143,["x"]=-515555.94114286,}, - [2] = {["y"]=28453.728285714,["x"]=-518170.78885714,}, - [3] = {["y"]=28370.788285714,["x"]=-518176.25742857,}, - [4] = {["y"]=28138.022857143,["x"]=-515573.07514286,}, - }, - [2] = { - [1] = {["y"]=28231.600857143,["x"]=-515555.94114286,}, - [2] = {["y"]=28453.728285714,["x"]=-518170.78885714,}, - [3] = {["y"]=28370.788285714,["x"]=-518176.25742857,}, - [4] = {["y"]=28138.022857143,["x"]=-515573.07514286,}, - }, - }, - }, - [AIRBASE.Nevada.Lincoln_County] = { - PointsRunways = { - [1] = { - [1]={["y"]=33222.34171429,["x"]=-223959.40171429,}, - [2]={["y"]=33200.040000004,["x"]=-225369.36828572,}, - [3]={["y"]=33177.634571428,["x"]=-225369.21485715,}, - [4]={["y"]=33201.198857147,["x"]=-223960.54457143,}, - }, - }, - }, - [AIRBASE.Nevada.McCarran_International_Airport] = { - PointsRunways = { - [1] = { - [1] = {["y"]=-29406.035714286,["x"]=-416102.48199999,}, - [2] = {["y"]=-24680.714285715,["x"]=-416003.14285713,}, - [3] = {["y"]=-24681.857142858,["x"]=-415926.57142856,}, - [4] = {["y"]=-29408.42857143,["x"]=-416016.57142856,}, - }, - [2] = { - [1] = {["y"]=-28567.221714286,["x"]=-416378.61799999,}, - [2] = {["y"]=-25109.912285714,["x"]=-416309.92914285,}, - [3] = {["y"]=-25112.508,["x"]=-416240.78714285,}, - [4] = {["y"]=-28576.247428571,["x"]=-416308.49514285,}, - }, - [3] = { - [1] = {["y"]=-29255.953142857,["x"]=-416307.10657142,}, - [2] = {["y"]=-28005.571428572,["x"]=-413449.7142857,}, - [3] = {["y"]=-28068.714285715,["x"]=-413422.85714284,}, - [4] = {["y"]=-29331.000000001,["x"]=-416275.7142857,}, - }, - [4] = { - [1] = {["y"]=-28994.901714286,["x"]=-416423.0522857,}, - [2] = {["y"]=-27697.571428572,["x"]=-413464.57142856,}, - [3] = {["y"]=-27767.857142858,["x"]=-413434.28571427,}, - [4] = {["y"]=-29073.000000001,["x"]=-416386.85714284,}, - }, - }, - }, - [AIRBASE.Nevada.Mesquite] = { - PointsRunways = { - [1] = { - [1] = {["y"]=68188.340285714,["x"]=-330302.54742857,}, - [2] = {["y"]=68911.303428571,["x"]=-328920.76571429,}, - [3] = {["y"]=68936.927142857,["x"]=-328933.888,}, - [4] = {["y"]=68212.460285714,["x"]=-330317.19171429,}, - }, - }, - }, - [AIRBASE.Nevada.Mina_Airport] = { - PointsRunways = { - [1] = { - [1] = {["y"]=-290054.57371429,["x"]=-160930.02228572,}, - [2] = {["y"]=-289469.77457143,["x"]=-162048.73571429,}, - [3] = {["y"]=-289520.06028572,["x"]=-162074.73571429,}, - [4] = {["y"]=-290104.69085714,["x"]=-160956.19457143,}, - }, - }, - }, - [AIRBASE.Nevada.Nellis_AFB] = { - PointsRunways = { - [1] = { - [1] = {["y"]=-18614.218571428,["x"]=-399437.91085714,}, - [2] = {["y"]=-16217.857142857,["x"]=-396596.85714286,}, - [3] = {["y"]=-16300.142857143,["x"]=-396530,}, - [4] = {["y"]=-18692.543428571,["x"]=-399381.31114286,}, - }, - [2] = { - [1] = {["y"]=-18388.948857143,["x"]=-399630.51828571,}, - [2] = {["y"]=-16011,["x"]=-396806.85714286,}, - [3] = {["y"]=-16074.714285714,["x"]=-396751.71428572,}, - [4] = {["y"]=-18451.571428572,["x"]=-399580.85714285,}, - }, - }, - }, - [AIRBASE.Nevada.Pahute_Mesa_Airstrip] = { - PointsRunways = { - [1] = { - [1] = {["y"]=-132690.40942857,["x"]=-302733.53085714,}, - [2] = {["y"]=-133112.43228571,["x"]=-304499.70742857,}, - [3] = {["y"]=-133179.91685714,["x"]=-304485.544,}, - [4] = {["y"]=-132759.988,["x"]=-302723.326,}, - }, - }, - }, - [AIRBASE.Nevada.Tonopah_Test_Range_Airfield] = { - PointsRunways = { - [1] = { - [1] = {["y"]=-175389.162,["x"]=-224778.07685715,}, - [2] = {["y"]=-173942.15485714,["x"]=-228210.27571429,}, - [3] = {["y"]=-174001.77085714,["x"]=-228233.60371429,}, - [4] = {["y"]=-175452.38685714,["x"]=-224806.84200001,}, - }, - }, - }, - [AIRBASE.Nevada.Tonopah_Airport] = { - PointsRunways = { - [1] = { - [1] = {["y"]=-202128.25228571,["x"]=-196701.34314286,}, - [2] = {["y"]=-201562.40828571,["x"]=-198814.99714286,}, - [3] = {["y"]=-201591.44828571,["x"]=-198820.93714286,}, - [4] = {["y"]=-202156.06828571,["x"]=-196707.68714286,}, - }, - [2] = { - [1] = {["y"]=-202084.57171428,["x"]=-196722.02228572,}, - [2] = {["y"]=-200592.75485714,["x"]=-197768.05571429,}, - [3] = {["y"]=-200605.37285714,["x"]=-197783.49228572,}, - [4] = {["y"]=-202097.14314285,["x"]=-196739.16514286,}, - }, - }, - }, - [AIRBASE.Nevada.North_Las_Vegas] = { - PointsRunways = { - [1] = { - [1] = {["y"]=-32599.017714286,["x"]=-400913.26485714,}, - [2] = {["y"]=-30881.068857143,["x"]=-400837.94628571,}, - [3] = {["y"]=-30879.354571428,["x"]=-400873.08914285,}, - [4] = {["y"]=-32595.966285714,["x"]=-400947.13571428,}, - }, - [2] = { - [1] = {["y"]=-32499.448571428,["x"]=-400690.99514285,}, - [2] = {["y"]=-31247.514857143,["x"]=-401868.95571428,}, - [3] = {["y"]=-31271.802857143,["x"]=-401894.97857142,}, - [4] = {["y"]=-32520.02,["x"]=-400716.99514285,}, - }, - [3] = { - [1] = {["y"]=-31865.254857143,["x"]=-400999.74057143,}, - [2] = {["y"]=-30893.604,["x"]=-401908.85742857,}, - [3] = {["y"]=-30915.578857143,["x"]=-401936.03685714,}, - [4] = {["y"]=-31884.969142858,["x"]=-401020.59771429,}, - }, - }, - }, - }, } --- Creates a new ATC_GROUND_NEVADA object. @@ -1388,168 +1106,11 @@ ATC_GROUND_NEVADA = { function ATC_GROUND_NEVADA:New( AirbaseNames ) -- Inherits from BASE - local self = BASE:Inherit( self, ATC_GROUND:New( self.Airbases, AirbaseNames ) ) + local self = BASE:Inherit( self, ATC_GROUND_UNIVERSAL:New( AirbaseNames ) ) self:SetKickSpeedKmph( 50 ) self:SetMaximumKickSpeedKmph( 150 ) - -- These lines here are for the demonstration mission. - -- They create in the dcs.log the coordinates of the runway polygons, that are then - -- taken by the moose designer from the dcs.log and reworked to define the - -- Airbases structure, which is part of the class. - -- When new airbases are added or airbases are changed on the map, - -- the MOOSE designer willde-comment this section and apply the changes in the demo - -- mission, and do a re-run to create a new dcs.log, and then add the changed coordinates - -- in the Airbases structure. - -- So, this needs to stay commented normally once a map has been finished. - - --[[ - - -- Beatty - do - local VillagePrefix = "Beatty" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- Boulder - do - local VillagePrefix = "Boulder" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- Creech - do - local VillagePrefix = "Creech" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- Echo - do - local VillagePrefix = "Echo" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- Groom Lake - do - local VillagePrefix = "GroomLake" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- Henderson - do - local VillagePrefix = "Henderson" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- Jean - do - local VillagePrefix = "Jean" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- Laughlin - do - local VillagePrefix = "Laughlin" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- Lincoln - do - local VillagePrefix = "Lincoln" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- McCarran - do - local VillagePrefix = "McCarran" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway3 = GROUP:FindByName( VillagePrefix .. " 3" ) - local Zone3 = ZONE_POLYGON:New( VillagePrefix .. " 3", Runway3 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway4 = GROUP:FindByName( VillagePrefix .. " 4" ) - local Zone4 = ZONE_POLYGON:New( VillagePrefix .. " 4", Runway4 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- Mesquite - do - local VillagePrefix = "Mesquite" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- Mina - do - local VillagePrefix = "Mina" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- Nellis - do - local VillagePrefix = "Nellis" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- Pahute - do - local VillagePrefix = "Pahute" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- TonopahTR - do - local VillagePrefix = "TonopahTR" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- Tonopah - do - local VillagePrefix = "Tonopah" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - -- Vegas - do - local VillagePrefix = "Vegas" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway3 = GROUP:FindByName( VillagePrefix .. " 3" ) - local Zone3 = ZONE_POLYGON:New( VillagePrefix .. " 3", Runway3 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - --]] - return self end @@ -1690,440 +1251,7 @@ end -- -- @field #ATC_GROUND_NORMANDY ATC_GROUND_NORMANDY = { - ClassName = "ATC_GROUND_NORMANDY", - Airbases = { - [AIRBASE.Normandy.Azeville] = { - PointsRunways = { - [1] = { - [1]={["y"]=-74194.387714285,["x"]=-2691.1399999998,}, - [2]={["y"]=-73160.282571428,["x"]=-2310.0274285712,}, - [3]={["y"]=-73141.711142857,["x"]=-2357.7417142855,}, - [4]={["y"]=-74176.959142857,["x"]=-2741.997142857,}, - }, - }, - }, - [AIRBASE.Normandy.Bazenville] = { - PointsRunways = { - [1] = { - [1]={["y"]=-19246.209999999,["x"]=-21246.748,}, - [2]={["y"]=-17883.70142857,["x"]=-20219.009714285,}, - [3]={["y"]=-17855.415714285,["x"]=-20256.438285714,}, - [4]={["y"]=-19217.791999999,["x"]=-21283.597714285,}, - }, - }, - }, - [AIRBASE.Normandy.Beny_sur_Mer] = { - PointsRunways = { - [1] = { - [1]={["y"]=-8592.7442857133,["x"]=-20386.15542857,}, - [2]={["y"]=-8404.4931428561,["x"]=-21744.113142856,}, - [3]={["y"]=-8267.9917142847,["x"]=-21724.97742857,}, - [4]={["y"]=-8451.0482857133,["x"]=-20368.87542857,}, - }, - }, - }, - [AIRBASE.Normandy.Beuzeville] = { - PointsRunways = { - [1] = { - [1]={["y"]=-71552.573428571,["x"]=-8744.3688571427,}, - [2]={["y"]=-72577.765714285,["x"]=-9638.5682857141,}, - [3]={["y"]=-72609.304285714,["x"]=-9601.2954285712,}, - [4]={["y"]=-71585.849428571,["x"]=-8709.9648571426,}, - }, - }, - }, - [AIRBASE.Normandy.Biniville] = { - PointsRunways = { - [1] = { - [1]={["y"]=-84757.320285714,["x"]=-7377.1354285713,}, - [2]={["y"]=-84271.482,["x"]=-7956.4859999999,}, - [3]={["y"]=-84299.482,["x"]=-7981.6288571427,}, - [4]={["y"]=-84784.969714286,["x"]=-7402.0588571427,}, - }, - }, - }, - [AIRBASE.Normandy.Brucheville] = { - PointsRunways = { - [1] = { - [1]={["y"]=-65546.792857142,["x"]=-14615.640857143,}, - [2]={["y"]=-66914.692,["x"]=-15232.713714285,}, - [3]={["y"]=-66896.527714285,["x"]=-15271.948571428,}, - [4]={["y"]=-65528.393714285,["x"]=-14657.995714286,}, - }, - }, - }, - [AIRBASE.Normandy.Cardonville] = { - PointsRunways = { - [1] = { - [1]={["y"]=-54280.445428571,["x"]=-15843.749142857,}, - [2]={["y"]=-53646.998571428,["x"]=-17143.012285714,}, - [3]={["y"]=-53683.93,["x"]=-17161.317428571,}, - [4]={["y"]=-54323.354571428,["x"]=-15855.004,}, - }, - }, - }, - [AIRBASE.Normandy.Carpiquet] = { - PointsRunways = { - [1] = { - [1]={["y"]=-10751.325714285,["x"]=-34229.494,}, - [2]={["y"]=-9283.5279999993,["x"]=-35192.352857142,}, - [3]={["y"]=-9325.2005714274,["x"]=-35260.967714285,}, - [4]={["y"]=-10794.90942857,["x"]=-34287.041428571,}, - }, - }, - }, - [AIRBASE.Normandy.Chailey] = { - PointsRunways = { - [1] = { - [1]={["y"]=12895.585714292,["x"]=164683.05657144,}, - [2]={["y"]=11410.727142863,["x"]=163606.54485715,}, - [3]={["y"]=11363.012857149,["x"]=163671.97342858,}, - [4]={["y"]=12797.537142863,["x"]=164711.01857144,}, - [5]={["y"]=12862.902857149,["x"]=164726.99685715,}, - }, - [2] = { - [1]={["y"]=11805.316000006,["x"]=164502.90971429,}, - [2]={["y"]=11997.280857149,["x"]=163032.65542858,}, - [3]={["y"]=11918.640857149,["x"]=163023.04657144,}, - [4]={["y"]=11726.973428578,["x"]=164489.94257143,}, - }, - }, - }, - [AIRBASE.Normandy.Chippelle] = { - PointsRunways = { - [1] = { - [1]={["y"]=-48540.313999999,["x"]=-28884.795999999,}, - [2]={["y"]=-47251.820285713,["x"]=-28140.128571427,}, - [3]={["y"]=-47274.551714285,["x"]=-28103.758285713,}, - [4]={["y"]=-48555.657714285,["x"]=-28839.90142857,}, - }, - }, - }, - [AIRBASE.Normandy.Cretteville] = { - PointsRunways = { - [1] = { - [1]={["y"]=-78351.723142857,["x"]=-18177.725428571,}, - [2]={["y"]=-77220.322285714,["x"]=-19125.687714286,}, - [3]={["y"]=-77247.899428571,["x"]=-19158.49,}, - [4]={["y"]=-78380.008857143,["x"]=-18208.011142857,}, - }, - }, - }, - [AIRBASE.Normandy.Cricqueville_en_Bessin] = { - PointsRunways = { - [1] = { - [1]={["y"]=-50875.034571428,["x"]=-14322.404571428,}, - [2]={["y"]=-50681.148571428,["x"]=-15825.258,}, - [3]={["y"]=-50717.434285713,["x"]=-15829.829428571,}, - [4]={["y"]=-50910.569428571,["x"]=-14327.562857142,}, - }, - }, - }, - [AIRBASE.Normandy.Deux_Jumeaux] = { - PointsRunways = { - [1] = { - [1]={["y"]=-49575.410857142,["x"]=-16575.161142857,}, - [2]={["y"]=-48149.077999999,["x"]=-16952.193428571,}, - [3]={["y"]=-48159.935142856,["x"]=-16996.764857142,}, - [4]={["y"]=-49584.839428571,["x"]=-16617.732571428,}, - }, - }, - }, - [AIRBASE.Normandy.Evreux] = { - PointsRunways = { - [1] = { - [1]={["y"]=112906.84828572,["x"]=-45585.824857142,}, - [2]={["y"]=112050.38228572,["x"]=-46811.871999999,}, - [3]={["y"]=111980.05371429,["x"]=-46762.173142856,}, - [4]={["y"]=112833.54542857,["x"]=-45540.010571428,}, - }, - [2] = { - [1]={["y"]=112046.02085714,["x"]=-45091.056571428,}, - [2]={["y"]=112488.668,["x"]=-46623.617999999,}, - [3]={["y"]=112405.66914286,["x"]=-46647.419142856,}, - [4]={["y"]=111966.03657143,["x"]=-45112.604285713,}, - }, - }, - }, - [AIRBASE.Normandy.Ford_AF] = { - PointsRunways = { - [1] = { - [1]={["y"]=-26506.13971428,["x"]=147514.39971429,}, - [2]={["y"]=-25012.977428565,["x"]=147566.14485715,}, - [3]={["y"]=-25009.851428565,["x"]=147482.63600001,}, - [4]={["y"]=-26503.693999994,["x"]=147427.33228572,}, - }, - [2] = { - [1]={["y"]=-25169.701999994,["x"]=148421.09257143,}, - [2]={["y"]=-26092.421999994,["x"]=147190.89628572,}, - [3]={["y"]=-26158.136285708,["x"]=147240.89628572,}, - [4]={["y"]=-25252.357999994,["x"]=148448.64457143,}, - }, - }, - }, - [AIRBASE.Normandy.Funtington] = { - PointsRunways = { - [1] = { - [1]={["y"]=-44698.388571423,["x"]=152952.17257143,}, - [2]={["y"]=-46452.993142851,["x"]=152388.77885714,}, - [3]={["y"]=-46476.361142851,["x"]=152470.05885714,}, - [4]={["y"]=-44787.256571423,["x"]=153009.52,}, - [5]={["y"]=-44715.581428566,["x"]=153002.08714286,}, - }, - [2] = { - [1]={["y"]=-45792.665999994,["x"]=153123.894,}, - [2]={["y"]=-46068.084857137,["x"]=151665.98342857,}, - [3]={["y"]=-46148.632285708,["x"]=151681.58685714,}, - [4]={["y"]=-45871.25971428,["x"]=153136.82714286,}, - }, - }, - }, - [AIRBASE.Normandy.Lantheuil] = { - PointsRunways = { - [1] = { - [1]={["y"]=-17158.84542857,["x"]=-24602.999428571,}, - [2]={["y"]=-15978.59342857,["x"]=-23922.978571428,}, - [3]={["y"]=-15932.021999999,["x"]=-24004.121428571,}, - [4]={["y"]=-17090.734857142,["x"]=-24673.248,}, - }, - }, - }, - [AIRBASE.Normandy.Lessay] = { - PointsRunways = { - [1] = { - [1]={["y"]=-87667.304571429,["x"]=-33220.165714286,}, - [2]={["y"]=-86146.607714286,["x"]=-34248.483142857,}, - [3]={["y"]=-86191.538285714,["x"]=-34316.991142857,}, - [4]={["y"]=-87712.212,["x"]=-33291.774857143,}, - }, - [2] = { - [1]={["y"]=-87125.123142857,["x"]=-34183.682571429,}, - [2]={["y"]=-85803.278285715,["x"]=-33498.428857143,}, - [3]={["y"]=-85768.408285715,["x"]=-33570.13,}, - [4]={["y"]=-87087.688571429,["x"]=-34258.272285715,}, - }, - }, - }, - [AIRBASE.Normandy.Lignerolles] = { - PointsRunways = { - [1] = { - [1]={["y"]=-35279.611714285,["x"]=-35232.026857142,}, - [2]={["y"]=-33804.948857142,["x"]=-35770.713999999,}, - [3]={["y"]=-33789.876285713,["x"]=-35726.655714284,}, - [4]={["y"]=-35263.548285713,["x"]=-35192.75542857,}, - }, - }, - }, - [AIRBASE.Normandy.Longues_sur_Mer] = { - PointsRunways = { - [1] = { - [1]={["y"]=-29444.070285713,["x"]=-16334.105428571,}, - [2]={["y"]=-28265.52942857,["x"]=-17011.557999999,}, - [3]={["y"]=-28344.74742857,["x"]=-17143.587999999,}, - [4]={["y"]=-29529.616285713,["x"]=-16477.766571428,}, - }, - }, - }, - [AIRBASE.Normandy.Maupertus] = { - PointsRunways = { - [1] = { - [1]={["y"]=-85605.340857143,["x"]=16175.267714286,}, - [2]={["y"]=-84132.567142857,["x"]=15895.905714286,}, - [3]={["y"]=-84139.995142857,["x"]=15847.623714286,}, - [4]={["y"]=-85613.626571429,["x"]=16132.410571429,}, - }, - }, - }, - [AIRBASE.Normandy.Meautis] = { - PointsRunways = { - [1] = { - [1]={["y"]=-72642.527714286,["x"]=-24593.622285714,}, - [2]={["y"]=-71298.672571429,["x"]=-24352.651142857,}, - [3]={["y"]=-71290.101142857,["x"]=-24398.365428571,}, - [4]={["y"]=-72631.715714286,["x"]=-24639.966857143,}, - }, - }, - }, - [AIRBASE.Normandy.Le_Molay] = { - PointsRunways = { - [1] = { - [1]={["y"]=-41876.526857142,["x"]=-26701.052285713,}, - [2]={["y"]=-40979.545714285,["x"]=-25675.045999999,}, - [3]={["y"]=-41017.687428571,["x"]=-25644.272571427,}, - [4]={["y"]=-41913.638285713,["x"]=-26665.137999999,}, - }, - }, - }, - [AIRBASE.Normandy.Needs_Oar_Point] = { - PointsRunways = { - [1] = { - [1]={["y"]=-83882.441142851,["x"]=141429.83314286,}, - [2]={["y"]=-85138.159428566,["x"]=140187.52828572,}, - [3]={["y"]=-85208.323428566,["x"]=140161.04371429,}, - [4]={["y"]=-85245.751999994,["x"]=140201.61514286,}, - [5]={["y"]=-83939.966571423,["x"]=141485.22085714,}, - }, - [2] = { - [1]={["y"]=-84528.76571428,["x"]=141988.01428572,}, - [2]={["y"]=-84116.98971428,["x"]=140565.78685714,}, - [3]={["y"]=-84199.35771428,["x"]=140541.14685714,}, - [4]={["y"]=-84605.051428566,["x"]=141966.01428572,}, - }, - }, - }, - [AIRBASE.Normandy.Picauville] = { - PointsRunways = { - [1] = { - [1]={["y"]=-80808.838571429,["x"]=-11834.554571428,}, - [2]={["y"]=-79531.574285714,["x"]=-12311.274,}, - [3]={["y"]=-79549.355428571,["x"]=-12356.928285714,}, - [4]={["y"]=-80827.815142857,["x"]=-11901.835142857,}, - }, - }, - }, - [AIRBASE.Normandy.Rucqueville] = { - PointsRunways = { - [1] = { - [1]={["y"]=-20023.988857141,["x"]=-26569.565428571,}, - [2]={["y"]=-18688.92542857,["x"]=-26571.086571428,}, - [3]={["y"]=-18688.012571427,["x"]=-26611.252285713,}, - [4]={["y"]=-20022.218857141,["x"]=-26608.505428571,}, - }, - }, - }, - [AIRBASE.Normandy.Saint_Pierre_du_Mont] = { - PointsRunways = { - [1] = { - [1]={["y"]=-48015.384571428,["x"]=-11886.631714285,}, - [2]={["y"]=-46540.412285713,["x"]=-11945.226571428,}, - [3]={["y"]=-46541.349999999,["x"]=-11991.174571428,}, - [4]={["y"]=-48016.837142856,["x"]=-11929.371142857,}, - }, - }, - }, - [AIRBASE.Normandy.Sainte_Croix_sur_Mer] = { - PointsRunways = { - [1] = { - [1]={["y"]=-15877.817999999,["x"]=-18812.579999999,}, - [2]={["y"]=-14464.377142856,["x"]=-18807.46,}, - [3]={["y"]=-14463.879714285,["x"]=-18759.706857142,}, - [4]={["y"]=-15878.229142856,["x"]=-18764.071428571,}, - }, - }, - }, - [AIRBASE.Normandy.Sainte_Laurent_sur_Mer] = { - PointsRunways = { - [1] = { - [1]={["y"]=-41676.834857142,["x"]=-14475.109428571,}, - [2]={["y"]=-40566.11142857,["x"]=-14817.319999999,}, - [3]={["y"]=-40579.543999999,["x"]=-14860.059999999,}, - [4]={["y"]=-41687.120571427,["x"]=-14509.680857142,}, - }, - }, - }, - [AIRBASE.Normandy.Sommervieu] = { - PointsRunways = { - [1] = { - [1]={["y"]=-26821.913714284,["x"]=-21390.466571427,}, - [2]={["y"]=-25465.308857142,["x"]=-21296.859999999,}, - [3]={["y"]=-25462.451714284,["x"]=-21343.717142856,}, - [4]={["y"]=-26818.002285713,["x"]=-21440.532857142,}, - }, - }, - }, - [AIRBASE.Normandy.Tangmere] = { - PointsRunways = { - [1] = { - [1]={["y"]=-34684.581142851,["x"]=150459.61657143,}, - [2]={["y"]=-33250.625428566,["x"]=149954.17,}, - [3]={["y"]=-33275.724285708,["x"]=149874.69028572,}, - [4]={["y"]=-34709.020571423,["x"]=150377.93742857,}, - }, - [2] = { - [1]={["y"]=-33103.438857137,["x"]=150812.72542857,}, - [2]={["y"]=-34410.246285708,["x"]=150009.73142857,}, - [3]={["y"]=-34453.535142851,["x"]=150082.02685714,}, - [4]={["y"]=-33176.545999994,["x"]=150870.22542857,}, - }, - }, - }, - [AIRBASE.Normandy.Argentan] = { - PointsRunways = { - [1] = { - [1]={["y"]=22322.280338032,["x"]=-78607.309765269,}, - [2]={["y"]=23032.778713963,["x"]=-78967.17709893,}, - [3]={["y"]=23015.27074041,["x"]=-79008.02903722,}, - [4]={["y"]=22299.944963827,["x"]=-78650.366148928,}, - }, - }, - }, - [AIRBASE.Normandy.Goulet] = { - PointsRunways = { - [1] = { - [1]={["y"]=24901.788373185,["x"]=-89139.367511763,}, - [2]={["y"]=25459.965967043,["x"]=-89709.67940114,}, - [3]={["y"]=25422.459962713,["x"]=-89741.669816598,}, - [4]={["y"]=24857.663662208,["x"]=-89173.56416277,}, - }, - }, - }, - [AIRBASE.Normandy.Essay] = { - PointsRunways = { - [1] = { - [1]={["y"]=44610.072022849,["x"]=-105469.21149064,}, - [2]={["y"]=45417.939023956,["x"]=-105536.08535277,}, - [3]={["y"]=45412.558368383,["x"]=-105585.27991801,}, - [4]={["y"]=44602.38537203,["x"]=-105516.10006064,}, - }, - }, - }, - [AIRBASE.Normandy.Hauterive] = { - PointsRunways = { - [1] = { - [1]={["y"]=40617.185360953,["x"]=-107657.10147517,}, - [2]={["y"]=41114.628372034,["x"]=-108298.77015609,}, - [3]={["y"]=41080.006684855,["x"]=-108319.06562788,}, - [4]={["y"]=40584.558402807,["x"]=-107692.29370481,}, - }, - }, - }, - [AIRBASE.Normandy.Vrigny] = { - PointsRunways = { - [1] = { - [1]={["y"]=24892.131051827,["x"]=-89131.628297486,}, - [2]={["y"]=25469.738000575,["x"]=-89709.235246234,}, - [3]={["y"]=25418.869206793,["x"]=-89738.771965204,}, - [4]={["y"]=24859.312475193,["x"]=-89171.010589446,}, - }, - }, - }, - [AIRBASE.Normandy.Barville] = { - PointsRunways = { - [1] = { - [1]={["y"]=49027.850333166,["x"]=-109217.05049066,}, - [2]={["y"]=49755.022185805,["x"]=-110346.63783457,}, - [3]={["y"]=49682.657996586,["x"]=-110401.35222154,}, - [4]={["y"]=48921.951519675,["x"]=-109285.88471943,}, - }, - [2] = { - [1]={["y"]=48429.522036941,["x"]=-109818.90874734,}, - [2]={["y"]=49746.197284681,["x"]=-109954.81222465,}, - [3]={["y"]=49735.607403332,["x"]=-110032.47135455,}, - [4]={["y"]=48420.697135816,["x"]=-109900.09783768,}, - }, - }, - }, - [AIRBASE.Normandy.Conches] = { - PointsRunways = { - [1] = { - [1]={["y"]=95099.187473266,["x"]=-56389.619005858,}, - [2]={["y"]=95181.545025963,["x"]=-56465.440244849,}, - [3]={["y"]=94071.678958666,["x"]=-57627.596821795,}, - [4]={["y"]=94005.008558864,["x"]=-57558.31189651,}, - }, - }, - }, - }, + ClassName = "ATC_GROUND_NORMANDY", } @@ -2134,285 +1262,10 @@ ATC_GROUND_NORMANDY = { function ATC_GROUND_NORMANDY:New( AirbaseNames ) -- Inherits from BASE - local self = BASE:Inherit( self, ATC_GROUND:New( self.Airbases, AirbaseNames ) ) -- #ATC_GROUND_NORMANDY + local self = BASE:Inherit( self, ATC_GROUND_UNIVERSAL:New( AirbaseNames ) ) -- #ATC_GROUND_NORMANDY self:SetKickSpeedKmph( 40 ) self:SetMaximumKickSpeedKmph( 100 ) - - -- These lines here are for the demonstration mission. - -- They create in the dcs.log the coordinates of the runway polygons, that are then - -- taken by the moose designer from the dcs.log and reworked to define the - -- Airbases structure, which is part of the class. - -- When new airbases are added or airbases are changed on the map, - -- the MOOSE designer willde-comment this section and apply the changes in the demo - -- mission, and do a re-run to create a new dcs.log, and then add the changed coordinates - -- in the Airbases structure. - -- So, this needs to stay commented normally once a map has been finished. - - --[[ - - -- Azeville - do - local VillagePrefix = "Azeville" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Bazenville - do - local VillagePrefix = "Bazenville" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Beny - do - local VillagePrefix = "Beny" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Beuzeville - do - local VillagePrefix = "Beuzeville" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Biniville - do - local VillagePrefix = "Biniville" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Brucheville - do - local VillagePrefix = "Brucheville" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Cardonville - do - local VillagePrefix = "Cardonville" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Carpiquet - do - local VillagePrefix = "Carpiquet" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Chailey - do - local VillagePrefix = "Chailey" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Chippelle - do - local VillagePrefix = "Chippelle" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Cretteville - do - local VillagePrefix = "Cretteville" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Cricqueville - do - local VillagePrefix = "Cricqueville" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Deux - do - local VillagePrefix = "Deux" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Evreux - do - local VillagePrefix = "Evreux" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Ford - do - local VillagePrefix = "Ford" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Funtington - do - local VillagePrefix = "Funtington" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Lantheuil - do - local VillagePrefix = "Lantheuil" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Lessay - do - local VillagePrefix = "Lessay" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Lignerolles - do - local VillagePrefix = "Lignerolles" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Longues - do - local VillagePrefix = "Longues" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Maupertus - do - local VillagePrefix = "Maupertus" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Meautis - do - local VillagePrefix = "Meautis" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Molay - do - local VillagePrefix = "Molay" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Oar - do - local VillagePrefix = "Oar" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Picauville - do - local VillagePrefix = "Picauville" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Rucqueville - do - local VillagePrefix = "Rucqueville" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- SaintPierre - do - local VillagePrefix = "SaintPierre" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- SainteCroix - do - local VillagePrefix = "SainteCroix" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - --SainteLaurent - do - local VillagePrefix = "SainteLaurent" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Sommervieu - do - local VillagePrefix = "Sommervieu" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Tangmere - do - local VillagePrefix = "Tangmere" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - --]] return self end @@ -2544,452 +1397,8 @@ end -- @field #ATC_GROUND_PERSIANGULF ATC_GROUND_PERSIANGULF = { ClassName = "ATC_GROUND_PERSIANGULF", - Airbases = { - [AIRBASE.PersianGulf.Abu_Musa_Island_Airport] = { - PointsRunways = { - [1] = { - [1]={["y"]=-122813.71002344,["x"]=-31689.936027827,}, - [2]={["y"]=-122827.82488722,["x"]=-31590.105445836,}, - [3]={["y"]=-122769.5689949,["x"]=-31583.176330891,}, - [4]={["y"]=-122726.96776968,["x"]=-31614.998932862,}, - [5]={["y"]=-121293.92414543,["x"]=-31467.947715689,}, - [6]={["y"]=-121296.4904843,["x"]=-31432.018971528,}, - [7]={["y"]=-121236.18152088,["x"]=-31424.576588809,}, - [8]={["y"]=-121190.50068902,["x"]=-31458.452261875,}, - [9]={["y"]=-119839.83654246,["x"]=-31319.356695194,}, - [10]={["y"]=-119824.69514313,["x"]=-31423.293419374,}, - [11]={["y"]=-119886.80054375,["x"]=-31430.22253432,}, - [12]={["y"]=-119932.22474173,["x"]=-31395.320325706,}, - [13]={["y"]=-122813.9472789,["x"]=-31689.81193251,}, - }, - }, - }, - [AIRBASE.PersianGulf.Al_Dhafra_AB] = { - PointsRunways = { - [1] = { - [1]={["y"]=-174672.06004916,["x"]=-209880.97145616,}, - [2]={["y"]=-174705.15693282,["x"]=-209923.15131918,}, - [3]={["y"]=-171819.05380065,["x"]=-212172.84298281,}, - [4]={["y"]=-171785.09826475,["x"]=-212129.87417284,}, - [5]={["y"]=-174671.96413454,["x"]=-209880.52453983,}, - }, - [2] = { - [1]={["y"]=-174351.95872272,["x"]=-211813.88516693,}, - [2]={["y"]=-174381.29169939,["x"]=-211851.81242636,}, - [3]={["y"]=-171493.65648904,["x"]=-214102.92235002,}, - [4]={["y"]=-171464.99693831,["x"]=-214062.78788361,}, - [5]={["y"]=-174351.8628081,["x"]=-211813.4382506,}, - }, - }, - }, - [AIRBASE.PersianGulf.Al_Maktoum_Intl] = { - PointsRunways = { - [1] = { - [1]={["y"]=-111879.49046471,["x"]=-138953.80105841,}, - [2]={["y"]=-111917.23447224,["x"]=-139018.2804046,}, - [3]={["y"]=-108092.98121312,["x"]=-141406.67838426,}, - [4]={["y"]=-108052.34416748,["x"]=-141341.82058294,}, - [5]={["y"]=-111879.5412879,["x"]=-138952.87693763,}, - }, - }, - }, - [AIRBASE.PersianGulf.Al_Minhad_AB] = { - PointsRunways = { - [1] = { - [1]={["y"]=-91070.628933035,["x"]=-125989.64095162,}, - [2]={["y"]=-91072.346560159,["x"]=-126040.59722299,}, - [3]={["y"]=-87098.282779771,["x"]=-126039.41747017,}, - [4]={["y"]=-87099.632735396,["x"]=-125991.26905291,}, - [5]={["y"]=-91071.031270042,["x"]=-125987.44617225,}, - }, - }, - }, - [AIRBASE.PersianGulf.Bandar_Abbas_Intl] = { - PointsRunways = { - [1] = { - [1]={["y"]=12988.484058788,["x"]=113979.99250505,}, - [2]={["y"]=13037.8836239,["x"]=113952.60241152,}, - [3]={["y"]=14877.313199902,["x"]=117414.37833333,}, - [4]={["y"]=14828.777486364,["x"]=117439.06043783,}, - [5]={["y"]=12988.939584604,["x"]=113979.52494386,}, - }, - [2] = { - [1]={["y"]=13203.406014284,["x"]=113848.44907555,}, - [2]={["y"]=13258.268500181,["x"]=113818.47303925,}, - [3]={["y"]=15315.015323566,["x"]=117694.27156647,}, - [4]={["y"]=15264.815746383,["x"]=117725.22168173,}, - [5]={["y"]=13203.861540099,["x"]=113847.98151436,}, - }, - }, - }, - [AIRBASE.PersianGulf.Bandar_Lengeh] = { - PointsRunways = { - [1] = { - [1]={["y"]=-142373.15541415,["x"]=41364.94047809,}, - [2]={["y"]=-142363.30071107,["x"]=41298.112282592,}, - [3]={["y"]=-142217.57151662,["x"]=41320.35666061,}, - [4]={["y"]=-142213.00856728,["x"]=41291.838227254,}, - [5]={["y"]=-142131.44584788,["x"]=41301.534494595,}, - [6]={["y"]=-142132.58658522,["x"]=41323.778872613,}, - [7]={["y"]=-142123.17550221,["x"]=41336.041798956,}, - [8]={["y"]=-139580.45381288,["x"]=41711.022304533,}, - [9]={["y"]=-139590.04241918,["x"]=41778.350996659,}, - [10]={["y"]=-139732.41237808,["x"]=41757.089304408,}, - [11]={["y"]=-139736.7897853,["x"]=41785.646675372,}, - [12]={["y"]=-139816.41690726,["x"]=41775.641173137,}, - [13]={["y"]=-139816.00001133,["x"]=41754.58792885,}, - [14]={["y"]=-139824.1294819,["x"]=41743.748634761,}, - [15]={["y"]=-142373.20183966,["x"]=41365.161507021,}, - }, - }, - }, - [AIRBASE.PersianGulf.Dubai_Intl] = { - PointsRunways = { - [1] = { - [1]={["y"]=-89693.511670714,["x"]=-100490.47082052,}, - [2]={["y"]=-89731.488328846,["x"]=-100555.50584758,}, - [3]={["y"]=-85706.437275049,["x"]=-103076.68123933,}, - [4]={["y"]=-85669.519216262,["x"]=-103010.44994755,}, - [5]={["y"]=-89693.036962487,["x"]=-100489.9961123,}, - }, - [2] = { - [1]={["y"]=-90797.505501889,["x"]=-99344.082465487,}, - [2]={["y"]=-90835.482160021,["x"]=-99409.11749254,}, - [3]={["y"]=-87210.216900398,["x"]=-101681.72494832,}, - [4]={["y"]=-87171.474397253,["x"]=-101619.20256393,}, - [5]={["y"]=-90797.030793662,["x"]=-99343.607757261,}, - }, - }, - }, - [AIRBASE.PersianGulf.Fujairah_Intl] = { - PointsRunways = { - [1] = { - [1]={["y"]=5808.8716147284,["x"]=-116602.15633995,}, - [2]={["y"]=5781.9885293892,["x"]=-116666.67574476,}, - [3]={["y"]=9435.1910907931,["x"]=-118192.91910235,}, - [4]={["y"]=9459.878635843,["x"]=-118134.40047704,}, - [5]={["y"]=5808.4078522575,["x"]=-116603.31550719,}, - }, - }, - }, - [AIRBASE.PersianGulf.Havadarya] = { - PointsRunways = { - [1] = { - [1]={["y"]=-7565.4887830428,["x"]=109074.13162774,}, - [2]={["y"]=-7557.8281079193,["x"]=109030.65729641,}, - [3]={["y"]=-4987.3556518085,["x"]=109524.49147773,}, - [4]={["y"]=-4996.215358578,["x"]=109566.57508489,}, - [5]={["y"]=-7565.4936338604,["x"]=109074.32262205,}, - }, - }, - }, - [AIRBASE.PersianGulf.Kerman_Airport] = { - PointsRunways = { - [1] = { - [1]={["y"]=70375.468628778,["x"]=456046.12685302,}, - [2]={["y"]=70297.050081575,["x"]=456015.1578105,}, - [3]={["y"]=71814.291673715,["x"]=452165.51037702,}, - [4]={["y"]=71902.918622452,["x"]=452188.46411914,}, - [5]={["y"]=70860.465673482,["x"]=454829.89695989,}, - [6]={["y"]=70862.525255971,["x"]=454892.77675983,}, - [7]={["y"]=70816.157465062,["x"]=454922.77944807,}, - [8]={["y"]=70462.749176371,["x"]=455833.38051827,}, - [9]={["y"]=70483.400377364,["x"]=455901.17880077,}, - [10]={["y"]=70453.787334431,["x"]=455974.8217628,}, - [11]={["y"]=70405.860962315,["x"]=455961.57382254,}, - [12]={["y"]=70374.689338175,["x"]=456046.51649833,}, - }, - }, - }, - [AIRBASE.PersianGulf.Khasab] = { - PointsRunways = { - [1] = { - [1]={["y"]=-534.81827307392,["x"]=-1495.070060483,}, - [2]={["y"]=-434.82912685139,["x"]=-1519.8421462589,}, - [3]={["y"]=-405.55302547993,["x"]=-1413.0969766429,}, - [4]={["y"]=-424.92029254105,["x"]=-1352.0675653224,}, - [5]={["y"]=216.05735069389,["x"]=1206.9187095195,}, - [6]={["y"]=116.42961315781,["x"]=1229.9576238247,}, - [7]={["y"]=88.253643635887,["x"]=1123.7918160128,}, - [8]={["y"]=101.1741158476,["x"]=1042.6886109249,}, - [9]={["y"]=-535.31436058928,["x"]=-1494.8762081291,}, - }, - }, - }, - [AIRBASE.PersianGulf.Lar_Airbase] = { - PointsRunways = { - [1] = { - [1]={["y"]=-183987.5454359,["x"]=169021.72039309,}, - [2]={["y"]=-183988.41292374,["x"]=168955.27082471,}, - [3]={["y"]=-180847.92031188,["x"]=168930.46175795,}, - [4]={["y"]=-180806.58653731,["x"]=168888.39641215,}, - [5]={["y"]=-180740.37934087,["x"]=168886.56748407,}, - [6]={["y"]=-180735.62412787,["x"]=168932.65647164,}, - [7]={["y"]=-180685.14571291,["x"]=168934.11961411,}, - [8]={["y"]=-180682.5852136,["x"]=169001.78995301,}, - [9]={["y"]=-183987.48111493,["x"]=169021.35002828,}, - }, - }, - }, - [AIRBASE.PersianGulf.Qeshm_Island] = { - PointsRunways = { - [1] = { - [1]={["y"]=-35140.372717152,["x"]=63373.658918509,}, - [2]={["y"]=-35098.556715749,["x"]=63320.377239302,}, - [3]={["y"]=-34991.318905699,["x"]=63408.730403557,}, - [4]={["y"]=-34984.574389344,["x"]=63401.311435566,}, - [5]={["y"]=-34991.993357335,["x"]=63313.632722947,}, - [6]={["y"]=-34956.921872287,["x"]=63265.746656824,}, - [7]={["y"]=-34917.129225791,["x"]=63261.699947011,}, - [8]={["y"]=-34832.822771349,["x"]=63337.23853019,}, - [9]={["y"]=-34915.105870884,["x"]=63436.382920614,}, - [10]={["y"]=-34906.337999622,["x"]=63478.198922017,}, - [11]={["y"]=-32728.533668488,["x"]=65307.986209216,}, - [12]={["y"]=-32676.600892552,["x"]=65299.218337954,}, - [13]={["y"]=-32623.99366498,["x"]=65334.964274638,}, - [14]={["y"]=-32626.691471522,["x"]=65388.92040548,}, - [15]={["y"]=-31822.745121968,["x"]=66067.418750826,}, - [16]={["y"]=-31777.556862387,["x"]=66068.767654097,}, - [17]={["y"]=-31691.227053039,["x"]=65974.344425122,}, - [18]={["y"]=-31606.246146962,["x"]=66042.464040311,}, - [19]={["y"]=-31602.199437148,["x"]=66084.280041714,}, - [20]={["y"]=-31632.549760747,["x"]=66124.747139846,}, - [21]={["y"]=-31727.647441358,["x"]=66134.189462744,}, - [22]={["y"]=-31734.391957713,["x"]=66141.608430735,}, - [23]={["y"]=-31632.549760747,["x"]=66225.914885176,}, - [24]={["y"]=-31673.691310515,["x"]=66277.173209477,}, - [25]={["y"]=-35140.880825624,["x"]=63373.905965825,}, - }, - }, - }, - [AIRBASE.PersianGulf.Sharjah_Intl] = { - PointsRunways = { - [1] = { - [1]={["y"]=-71668.808658476,["x"]=-93980.156242153,}, - [2]={["y"]=-75307.847363315,["x"]=-91617.097584505,}, - [3]={["y"]=-75280.458023829,["x"]=-91574.709321014,}, - [4]={["y"]=-72249.697184234,["x"]=-93529.134331507,}, - [5]={["y"]=-72179.919581256,["x"]=-93526.199759419,}, - [6]={["y"]=-72138.183444896,["x"]=-93597.933743788,}, - [7]={["y"]=-71638.654062835,["x"]=-93927.584008321,}, - [8]={["y"]=-71668.325847279,["x"]=-93979.428115206,}, - }, - [2] = { - [1]={["y"]=-71553.225408723,["x"]=-93775.312323319,}, - [2]={["y"]=-75168.13829548,["x"]=-91426.51571111,}, - [3]={["y"]=-75125.388157445,["x"]=-91363.754870166,}, - [4]={["y"]=-71510.511081666,["x"]=-93703.252275385,}, - [5]={["y"]=-71552.247218027,["x"]=-93775.638386885,}, - }, - }, - }, - [AIRBASE.PersianGulf.Shiraz_International_Airport] = { - PointsRunways = { - [1] = { - [1]={["y"]=-353995.75579778,["x"]=382327.42294273,}, - [2]={["y"]=-354029.77009807,["x"]=382265.46199492,}, - [3]={["y"]=-349407.98049238,["x"]=379941.14030526,}, - [4]={["y"]=-349376.87025024,["x"]=380004.69408564,}, - [5]={["y"]=-353995.71101815,["x"]=382327.59771695,}, - }, - [2] = { - [1]={["y"]=-354056.29510012,["x"]=381845.97598829,}, - [2]={["y"]=-354091.48797289,["x"]=381783.6025623,}, - [3]={["y"]=-349650.64038107,["x"]=379550.92898242,}, - [4]={["y"]=-349624.41889127,["x"]=379614.92719482,}, - [5]={["y"]=-354056.25032049,["x"]=381846.15076251,}, - }, - }, - }, - [AIRBASE.PersianGulf.Sir_Abu_Nuayr] = { - PointsRunways = { - [1] = { - [1]={["y"]=-203367.3128691,["x"]=-103017.22553918,}, - [2]={["y"]=-203373.59664477,["x"]=-103054.92819323,}, - [3]={["y"]=-202578.27577922,["x"]=-103188.26018333,}, - [4]={["y"]=-202571.37254488,["x"]=-103151.01482599,}, - [5]={["y"]=-203367.65259839,["x"]=-103016.48202662,}, - [6]={["y"]=-203291.39594004,["x"]=-102985.49774228,}, - }, - }, - }, - [AIRBASE.PersianGulf.Sirri_Island] = { - PointsRunways = { - [1] = { - [1]={["y"]=-169713.12842428,["x"]=-27766.658020853,}, - [2]={["y"]=-169682.02009414,["x"]=-27726.583172021,}, - [3]={["y"]=-169727.21866794,["x"]=-27691.632048154,}, - [4]={["y"]=-169694.28043602,["x"]=-27650.276268081,}, - [5]={["y"]=-169763.08474269,["x"]=-27598.490047901,}, - [6]={["y"]=-169825.30140298,["x"]=-27607.090586235,}, - [7]={["y"]=-171614.98889813,["x"]=-26246.247907014,}, - [8]={["y"]=-171620.85326172,["x"]=-26187.105176343,}, - [9]={["y"]=-171686.10990337,["x"]=-26138.56820961,}, - [10]={["y"]=-171716.55468456,["x"]=-26178.745338885,}, - [11]={["y"]=-171764.9668776,["x"]=-26142.810515186,}, - [12]={["y"]=-171796.29599657,["x"]=-26183.416460911,}, - [13]={["y"]=-169713.5628285,["x"]=-27766.883787223,}, - }, - }, - }, - [AIRBASE.PersianGulf.Tunb_Island_AFB] = { - PointsRunways = { - [1] = { - [1]={["y"]=-92923.634698863,["x"]=9547.6862547173,}, - [2]={["y"]=-92963.030803298,["x"]=9565.7274614215,}, - [3]={["y"]=-92934.128053782,["x"]=9619.2987996964,}, - [4]={["y"]=-92970.946842975,["x"]=9640.1014155901,}, - [5]={["y"]=-92949.591945243,["x"]=9682.8112110532,}, - [6]={["y"]=-92899.518391942,["x"]=9699.7478540817,}, - [7]={["y"]=-91969.13471408,["x"]=11464.627292768,}, - [8]={["y"]=-91983.666755417,["x"]=11515.293058512,}, - [9]={["y"]=-91960.101282978,["x"]=11557.710908902,}, - [10]={["y"]=-91921.021874517,["x"]=11539.251288825,}, - [11]={["y"]=-91893.725202275,["x"]=11589.720675632,}, - [12]={["y"]=-91859.751646175,["x"]=11571.850192366,}, - [13]={["y"]=-92922.149728329,["x"]=9547.2937058617,}, - }, - }, - }, - [AIRBASE.PersianGulf.Tunb_Kochak] = { - PointsRunways = { - [1] = { - [1]={["y"]=-109925.50271188,["x"]=8974.5666013181,}, - [2]={["y"]=-109905.7382908,["x"]=8937.53274444,}, - [3]={["y"]=-109009.93726324,["x"]=9072.2234968343,}, - [4]={["y"]=-109040.82867587,["x"]=9104.9871291834,}, - [5]={["y"]=-109925.26515172,["x"]=8974.091480998,}, - }, - }, - }, - [AIRBASE.PersianGulf.Sas_Al_Nakheel_Airport] = { - PointsRunways = { - [1] = { - [1]={["y"]=-176230.75865538,["x"]=-188732.01369812,}, - [2]={["y"]=-176274.78045186,["x"]=-188744.8049371,}, - [3]={["y"]=-175692.03171595,["x"]=-190564.17145168,}, - [4]={["y"]=-175649.7486572,["x"]=-190550.58435053,}, - [5]={["y"]=-176230.66274076,["x"]=-188731.5667818,}, - }, - }, - }, - [AIRBASE.PersianGulf.Bandar_e_Jask_airfield] = { - PointsRunways = { - [1] = { - [1]={["y"]=155156.73167657,["x"]=-57837.031277333,}, - [2]={["y"]=155130.38996239,["x"]=-57790.475605714,}, - [3]={["y"]=157137.17872571,["x"]=-56710.411783359,}, - [4]={["y"]=157148.46631801,["x"]=-56688.071756941,}, - [5]={["y"]=157220.07198163,["x"]=-56649.035500253,}, - [6]={["y"]=157227.83220133,["x"]=-56662.204357931,}, - [7]={["y"]=157359.6383572,["x"]=-56590.481115222,}, - [8]={["y"]=157383.03659539,["x"]=-56633.044744502,}, - [9]={["y"]=155156.7940421,["x"]=-57837.149989814,}, - }, - }, - }, - [AIRBASE.PersianGulf.Abu_Dhabi_International_Airport] = { - PointsRunways = { - [1] = { - [1]={["y"]=-163964.56943899,["x"]=-189427.63621921,}, - [2]={["y"]=-164005.96838287,["x"]=-189478.90226888,}, - [3]={["y"]=-160798.22080495,["x"]=-192054.59531727,}, - [4]={["y"]=-160755.05282258,["x"]=-192002.58569997,}, - [5]={["y"]=-163964.47352437,["x"]=-189427.18930288,}, - }, - [2] = { - [1]={["y"]=-163615.44952024,["x"]=-187144.00786922,}, - [2]={["y"]=-163656.84846411,["x"]=-187195.27391888,}, - [3]={["y"]=-160452.71811093,["x"]=-189764.86593382,}, - [4]={["y"]=-160411.94568221,["x"]=-189715.47961171,}, - [5]={["y"]=-163615.35360562,["x"]=-187143.56095289,}, - }, - }, - }, - [AIRBASE.PersianGulf.Al_Bateen_Airport] = { - PointsRunways = { - [1] = { - [1]={["y"]=-183207.51774197,["x"]=-189871.8319832,}, - [2]={["y"]=-183240.61462564,["x"]=-189914.01184622,}, - [3]={["y"]=-180748.88998479,["x"]=-191943.30402837,}, - [4]={["y"]=-180711.83076051,["x"]=-191896.52435182,}, - [5]={["y"]=-183207.42182735,["x"]=-189871.38506688,}, - }, - }, - }, - [AIRBASE.PersianGulf.Kish_International_Airport] = { - PointsRunways = { - [1] = { - [1]={["y"]=-227330.79164594,["x"]=42691.91536494,}, - [2]={["y"]=-227321.58531968,["x"]=42758.113234714,}, - [3]={["y"]=-223235.73004619,["x"]=42313.579195302,}, - [4]={["y"]=-223240.99080406,["x"]=42247.819722016,}, - [5]={["y"]=-227330.67774245,["x"]=42691.785682556,}, - }, - [2] = { - [1]={["y"]=-227283.77911886,["x"]=42987.748941936,}, - [2]={["y"]=-227274.5727926,["x"]=43053.946811711,}, - [3]={["y"]=-222907.94761294,["x"]=42580.826755904,}, - [4]={["y"]=-222915.76510871,["x"]=42514.58376547,}, - [5]={["y"]=-227283.66521537,["x"]=42987.619259553,}, - }, - }, - }, - [AIRBASE.PersianGulf.Al_Ain_International_Airport] = { - PointsRunways = { - [1] = { - [1]={["y"]=-65165.315648901,["x"]=-209042.45716363,}, - [2]={["y"]=-65112.933878375,["x"]=-209048.84518442,}, - [3]={["y"]=-65672.013626755,["x"]=-213019.66479976,}, - [4]={["y"]=-65722.555424932,["x"]=-213013.91596964,}, - [5]={["y"]=-65165.400582791,["x"]=-209042.15059908,}, - }, - }, - }, - [AIRBASE.PersianGulf.Lavan_Island_Airport] = { - PointsRunways = { - [1] = { - [1]={["y"]=-288099.83301495,["x"]=76353.443273049,}, - [2]={["y"]=-288119.51457685,["x"]=76302.756224611,}, - [3]={["y"]=-288070.96603401,["x"]=76283.898526152,}, - [4]={["y"]=-288085.61084238,["x"]=76247.386812114,}, - [5]={["y"]=-288032.04695421,["x"]=76224.316223573,}, - [6]={["y"]=-287991.12173627,["x"]=76245.38067398,}, - [7]={["y"]=-287489.96435675,["x"]=76037.610404141,}, - [8]={["y"]=-287497.65444594,["x"]=76017.686082159,}, - [9]={["y"]=-287453.61120787,["x"]=75998.111309685,}, - [10]={["y"]=-287419.70490555,["x"]=76007.199596905,}, - [11]={["y"]=-285642.24565503,["x"]=75279.787069797,}, - [12]={["y"]=-285625.46727862,["x"]=75239.239326815,}, - [13]={["y"]=-285570.23845628,["x"]=75217.217707782,}, - [14]={["y"]=-285555.20782742,["x"]=75252.172658628,}, - [15]={["y"]=-285505.92134673,["x"]=75231.199688121,}, - [16]={["y"]=-285484.28380792,["x"]=75284.258832895,}, - [17]={["y"]=-288099.97979219,["x"]=76354.32393647,}, - }, - }, - }, - [AIRBASE.PersianGulf.Jiroft_Airport] = { - PointsRunways = { - [1] = { - [1]={["y"]=140376.87310595,["x"]=283748.07558774,}, - [2]={["y"]=140299.43760975,["x"]=283655.81201779,}, - [3]={["y"]=143008.43807723,["x"]=281517.41347718,}, - [4]={["y"]=143052.6952428,["x"]=281573.25195709,}, - [5]={["y"]=142946.60213095,["x"]=281656.5960586,}, - [6]={["y"]=142975.14179847,["x"]=281687.20381796,}, - [7]={["y"]=142932.12548801,["x"]=281724.01585287,}, - [8]={["y"]=142870.49635092,["x"]=281719.05243244,}, - [9]={["y"]=140437.35783025,["x"]=283640.84253664,}, - [10]={["y"]=140433.27045062,["x"]=283705.80267729,}, - [11]={["y"]=140376.77702493,["x"]=283747.8442964,}, - }, - }, - }, - }, } - --- Creates a new ATC_GROUND_PERSIANGULF object. -- @param #ATC_GROUND_PERSIANGULF self -- @param AirbaseNames A list {} of airbase names (Use AIRBASE.PersianGulf enumerator). @@ -2997,266 +1406,11 @@ ATC_GROUND_PERSIANGULF = { function ATC_GROUND_PERSIANGULF:New( AirbaseNames ) -- Inherits from BASE - local self = BASE:Inherit( self, ATC_GROUND:New( self.Airbases, AirbaseNames ) ) -- #ATC_GROUND_PERSIANGULF + local self = BASE:Inherit( self, ATC_GROUND_UNIVERSAL:New( AirbaseNames ) ) -- #ATC_GROUND_PERSIANGULF self:SetKickSpeedKmph( 50 ) self:SetMaximumKickSpeedKmph( 150 ) - - -- These lines here are for the demonstration mission. - -- They create in the dcs.log the coordinates of the runway polygons, that are then - -- taken by the moose designer from the dcs.log and reworked to define the - -- Airbases structure, which is part of the class. - -- When new airbases are added or airbases are changed on the map, - -- the MOOSE designer willde-comment this section and apply the changes in the demo - -- mission, and do a re-run to create a new dcs.log, and then add the changed coordinates - -- in the Airbases structure. - -- So, this needs to stay commented normally once a map has been finished. - - --[[ - - -- Abu_Musa_Island_Airport - do - local VillagePrefix = "Abu_Musa_Island_Airport" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Al_Dhafra_AB - do - local VillagePrefix = "Al_Dhafra_AB" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Al_Maktoum_Intl - do - local VillagePrefix = "Al_Maktoum_Intl" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Al_Minhad_AB - do - local VillagePrefix = "Al_Minhad_AB" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Bandar_Abbas_Intl - do - local VillagePrefix = "Bandar_Abbas_Intl" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Bandar_Lengeh - do - local VillagePrefix = "Bandar_Lengeh" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Dubai_Intl - do - local VillagePrefix = "Dubai_Intl" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Fujairah_Intl - do - local VillagePrefix = "Fujairah_Intl" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Havadarya - do - local VillagePrefix = "Havadarya" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Kerman_Airport - do - local VillagePrefix = "Kerman_Airport" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Khasab - do - local VillagePrefix = "Khasab" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Lar_Airbase - do - local VillagePrefix = "Lar_Airbase" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Qeshm_Island - do - local VillagePrefix = "Qeshm_Island" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Sharjah_Intl - do - local VillagePrefix = "Sharjah_Intl" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Shiraz_International_Airport - do - local VillagePrefix = "Shiraz_International_Airport" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Sir_Abu_Nuayr - do - local VillagePrefix = "Sir_Abu_Nuayr" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Sirri_Island - do - local VillagePrefix = "Sirri_Island" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Tunb_Island_AFB - do - local VillagePrefix = "Tunb_Island_AFB" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Tunb_Kochak - do - local VillagePrefix = "Tunb_Kochak" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Sas_Al_Nakheel_Airport - do - local VillagePrefix = "Sas_Al_Nakheel_Airport" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Bandar_e_Jask_airfield - do - local VillagePrefix = "Bandar_e_Jask_airfield" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Abu_Dhabi_International_Airport - do - local VillagePrefix = "Abu_Dhabi_International_Airport" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Al_Bateen_Airport - do - local VillagePrefix = "Al_Bateen_Airport" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Kish_International_Airport - do - local VillagePrefix = "Kish_International_Airport" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Al_Ain_International_Airport - do - local VillagePrefix = "Al_Ain_International_Airport" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Lavan_Island_Airport - do - local VillagePrefix = "Lavan_Island_Airport" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Jiroft_Airport - do - local VillagePrefix = "Jiroft_Airport" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - - -- Bandar_Abbas_Intl - do - local VillagePrefix = "Bandar_Abbas_Intl" - local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) - local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - local Runway2 = GROUP:FindByName( VillagePrefix .. " 2" ) - local Zone2 = ZONE_POLYGON:New( VillagePrefix .. " 2", Runway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - end - - --]] - - return self end --- Start SCHEDULER for ATC_GROUND_PERSIANGULF object. @@ -3366,142 +1520,6 @@ end ---- @field #ATC_GROUND_MARIANAISLANDS ATC_GROUND_MARIANAISLANDS = { ClassName = "ATC_GROUND_MARIANAISLANDS", - Airbases = { - - [AIRBASE.MarianaIslands.Andersen_AFB] = { - ZoneBoundary = { - [1]={["y"]=16534.138036037,["x"]=11357.42159178,}, - [2]={["y"]=16193.406442738,["x"]=12080.012957533,}, - [3]={["y"]=13846.966851869,["x"]=12017.348398727,}, - [4]={["y"]=13085.815989171,["x"]=11686.317876875,}, - [5]={["y"]=13157.991797443,["x"]=11307.826209991,}, - [6]={["y"]=12055.725179065,["x"]=10795.955695916,}, - [7]={["y"]=12762.455491112,["x"]=8890.9830441032,}, - [8]={["y"]=15955.829493693,["x"]=10333.527220132,}, - [9]={["y"]=16537.500532414,["x"]=11302.009499603,}, - }, - PointsRunways = { - [1]={ - [1]={["y"]=12586.683049611,["x"]=10224.374497932,}, - [2]={["y"]=16191.720475696,["x"]=11791.299100017,}, - [3]={["y"]=16126.93956642,["x"]=11938.855615591,}, - [4]={["y"]=12520.758127164,["x"]=10385.177131701,}, - [5]={["y"]=12584.654720512,["x"]=10227.416991581,}, - }, - [2]={ - [1]={["y"]=12663.030391743,["x"]=9661.9623015306,}, - [2]={["y"]=16478.347303358,["x"]=11328.665745976,}, - [3]={["y"]=16405.4731048,["x"]=11479.11570429,}, - [4]={["y"]=12597.277684174,["x"]=9817.9733769647,}, - [5]={["y"]=12661.894752524,["x"]=9674.4462086962,}, - }, - }, - }, - [AIRBASE.MarianaIslands.Antonio_B_Won_Pat_Intl] = { - ZoneBoundary = { - [1]={["y"]=2288.5182403943,["x"]=1469.0170841716,}, - [2]={["y"]=1126.2025877996,["x"]=1174.37135631,}, - [3]={["y"]=-2015.6461924287,["x"]=-484.62000718931,}, - [4]={["y"]=-2102.1292389114,["x"]=-988.03393750566,}, - [5]={["y"]=476.03853524366,["x"]=-1220.1783269883,}, - [6]={["y"]=2059.2220058047,["x"]=78.889693514402,}, - [7]={["y"]=1898.1396965104,["x"]=705.67531284795,}, - [8]={["y"]=2760.1768681934,["x"]=1026.0681119777,}, - [9]={["y"]=2317.2278959994,["x"]=1460.8143254273,}, - }, - PointsRunways = { - [1]={ - [1]={["y"]=-1872.6620108821,["x"]=-924.3572605835,}, - [2]={["y"]=1763.4754603305,["x"]=735.35988877983,}, - [3]={["y"]=1700.6941677961,["x"]=866.32615476157,}, - [4]={["y"]=-1934.0078007732,["x"]=-779.8149298453,}, - [5]={["y"]=-1875.0113982627,["x"]=-914.95971106094,}, - }, - [2]={ - [1]={["y"]=-1512.9403660377,["x"]=-1005.5903386188,}, - [2]={["y"]=1577.9055714735,["x"]=413.22750176368,}, - [3]={["y"]=1523.1182807849,["x"]=543.89726442232,}, - [4]={["y"]=-1572.5102998047,["x"]=-867.04004322806,}, - [5]={["y"]=-1514.2790162347,["x"]=-1003.5823633233,}, - }, - }, - }, - [AIRBASE.MarianaIslands.Rota_Intl] = { - ZoneBoundary = { - [1]={["y"]=47237.615412849,["x"]=76048.890408862,}, - [2]={["y"]=49938.030053628,["x"]=75921.721582932,}, - [3]={["y"]=49931.24873272,["x"]=75735.184004851,}, - [4]={["y"]=49295.999227075,["x"]=75754.716414519,}, - [5]={["y"]=49286.963307515,["x"]=75510.037806569,}, - [6]={["y"]=48774.280745707,["x"]=75513.331990155,}, - [7]={["y"]=48785.021396773,["x"]=75795.691662161,}, - [8]={["y"]=47232.749278491,["x"]=75839.239059146,}, - [9]={["y"]=47236.687866223,["x"]=76042.706764692,}, - }, - PointsRunways = { - [1]={ - [1]={["y"]=49741.295228062,["x"]=75901.50955922,}, - [2]={["y"]=49739.033213305,["x"]=75768.333440425,}, - [3]={["y"]=47448.460520408,["x"]=75857.400271466,}, - [4]={["y"]=47452.270177742,["x"]=75999.965448133,}, - [5]={["y"]=49738.502011054,["x"]=75905.338915708,}, - }, - }, - }, - [AIRBASE.MarianaIslands.Saipan_Intl] = { - ZoneBoundary = { - [1]={["y"]=100489.08491445,["x"]=179799.05158855,}, - [2]={["y"]=100869.73415313,["x"]=179948.98719903,}, - [3]={["y"]=101364.78967515,["x"]=180831.98517043,}, - [4]={["y"]=101563.85713359,["x"]=180885.21496237,}, - [5]={["y"]=101733.92591034,["x"]=180457.73296886,}, - [6]={["y"]=103340.30228775,["x"]=180990.08362622,}, - [7]={["y"]=103459.55080438,["x"]=180453.77747027,}, - [8]={["y"]=100406.63048095,["x"]=179266.60983762,}, - [9]={["y"]=100225.55027532,["x"]=179423.9380961,}, - [10]={["y"]=100477.48558937,["x"]=179791.9827288,}, - }, - PointsRunways = { - [1]={ - [1]={["y"]=103170.38882002,["x"]=180654.56630524,}, - [2]={["y"]=103235.37868835,["x"]=180497.25368418,}, - [3]={["y"]=100564.72969504,["x"]=179435.41443498,}, - [4]={["y"]=100509.30718722,["x"]=179584.65394733,}, - [5]={["y"]=103163.53918905,["x"]=180651.82645285,}, - }, - [2]={ - [1]={["y"]=103048.83223261,["x"]=180819.94107128,}, - [2]={["y"]=103087.60579257,["x"]=180720.06315265,}, - [3]={["y"]=101037.52694966,["x"]=179899.50061624,}, - [4]={["y"]=100994.61708907,["x"]=180009.33151758,}, - [5]={["y"]=103043.26643227,["x"]=180820.40488798,}, - }, - }, - }, - [AIRBASE.MarianaIslands.Tinian_Intl] = { - ZoneBoundary = { - [1]={["y"]=88393.477575413,["x"]=166704.16076438,}, - [2]={["y"]=91581.732441809,["x"]=167402.54409276,}, - [3]={["y"]=91533.451647402,["x"]=166826.23670062,}, - [4]={["y"]=90827.604136952,["x"]=166699.75590414,}, - [5]={["y"]=90894.853975623,["x"]=166375.37836304,}, - [6]={["y"]=89995.027922869,["x"]=166224.92495935,}, - [7]={["y"]=88937.62899352,["x"]=166244.48573911,}, - [8]={["y"]=88408.916178231,["x"]=166480.39896864,}, - [9]={["y"]=88387.745481732,["x"]=166685.82715656,}, - }, - PointsRunways = { - [1]={ - [1]={["y"]=91329.480937912,["x"]=167204.44064529,}, - [2]={["y"]=91363.95475433,["x"]=167038.15603429,}, - [3]={["y"]=88585.849307337,["x"]=166520.3807647,}, - [4]={["y"]=88554.422227212,["x"]=166686.49505251,}, - [5]={["y"]=91318.8152578,["x"]=167203.31794212,}, - }, - }, - }, - - }, } --- Creates a new ATC_GROUND_MARIANAISLANDS object. @@ -3511,63 +1529,14 @@ ATC_GROUND_MARIANAISLANDS = { function ATC_GROUND_MARIANAISLANDS:New( AirbaseNames ) -- Inherits from BASE - local self = BASE:Inherit( self, ATC_GROUND:New( self.Airbases, AirbaseNames ) ) + local self = BASE:Inherit( self, ATC_GROUND_UNIVERSAL:New( self.Airbases, AirbaseNames ) ) self:SetKickSpeedKmph( 50 ) self:SetMaximumKickSpeedKmph( 150 ) --- -- Andersen --- local AndersenBoundary = GROUP:FindByName( "Andersen Boundary" ) --- self.Airbases[AIRBASE.MarianaIslands.Andersen_AFB].ZoneBoundary = ZONE_POLYGON:New( "Andersen Boundary", AndersenBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local AndersenRunway1 = GROUP:FindByName( "Andersen Runway 1" ) --- self.Airbases[AIRBASE.MarianaIslands.Andersen_AFB].ZoneRunways[1] = ZONE_POLYGON:New( "Andersen Runway 1", AndersenRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local AndersenRunway2 = GROUP:FindByName( "Andersen Runway 2" ) --- self.Airbases[AIRBASE.MarianaIslands.Andersen_AFB].ZoneRunways[2] = ZONE_POLYGON:New( "Andersen Runway 2", AndersenRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- --- -- Antonio_B_Won_Pat_International_Airport --- local AntonioBoundary = GROUP:FindByName( "Antonio Boundary" ) --- self.Airbases[AIRBASE.MarianaIslands.Antonio_B_Won_Pat_Intl].ZoneBoundary = ZONE_POLYGON:New( "Antonio Boundary", AntonioBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local AntonioRunway1 = GROUP:FindByName( "Antonio Runway 1" ) --- self.Airbases[AIRBASE.MarianaIslands.Antonio_B_Won_Pat_Intl].ZoneRunways[1] = ZONE_POLYGON:New( "Antonio Runway 1", AntonioRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local AntonioRunway2 = GROUP:FindByName( "Antonio Runway 2" ) --- self.Airbases[AIRBASE.MarianaIslands.Antonio_B_Won_Pat_Intl].ZoneRunways[2] = ZONE_POLYGON:New( "Antonio Runway 2", AntonioRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- --- -- Rota_International_Airport --- local RotaBoundary = GROUP:FindByName( "Rota Boundary" ) --- self.Airbases[AIRBASE.MarianaIslands.Rota_Intl].ZoneBoundary = ZONE_POLYGON:New( "Rota Boundary", RotaBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local RotaRunway1 = GROUP:FindByName( "Rota Runway 1" ) --- self.Airbases[AIRBASE.MarianaIslands.Rota_Intl].ZoneRunways[1] = ZONE_POLYGON:New( "Rota Runway 1", RotaRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- --- -- Saipan_International_Airport --- local SaipanBoundary = GROUP:FindByName( "Saipan Boundary" ) --- self.Airbases[AIRBASE.MarianaIslands.Saipan_Intl].ZoneBoundary = ZONE_POLYGON:New( "Saipan Boundary", SaipanBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local SaipanRunway1 = GROUP:FindByName( "Saipan Runway 1" ) --- self.Airbases[AIRBASE.MarianaIslands.Saipan_Intl].ZoneRunways[1] = ZONE_POLYGON:New( "Saipan Runway 1", SaipanRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local SaipanRunway2 = GROUP:FindByName( "Saipan Runway 2" ) --- self.Airbases[AIRBASE.MarianaIslands.Saipan_Intl].ZoneRunways[2] = ZONE_POLYGON:New( "Saipan Runway 2", SaipanRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- --- -- Tinian_International_Airport --- local TinianBoundary = GROUP:FindByName( "Tinian Boundary" ) --- self.Airbases[AIRBASE.MarianaIslands.Tinian_Intl].ZoneBoundary = ZONE_POLYGON:New( "Tinian Boundary", TinianBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local TinianRunway1 = GROUP:FindByName( "Tinian Runway 1" ) --- self.Airbases[AIRBASE.MarianaIslands.Tinian_Intl].ZoneRunways[1] = ZONE_POLYGON:New( "Tinian Runway 1", TinianRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - return self end - --- Start SCHEDULER for ATC_GROUND_MARIANAISLANDS object. -- @param #ATC_GROUND_MARIANAISLANDS self -- @param RepeatScanSeconds Time in second for defining occurency of alerts. diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index daffdfa22..6c249e003 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -74,6 +74,7 @@ -- @image Designation.JPG -- -- Date: 24 Oct 2021 +-- Last Update: Aug 2022 -- --- Class AUTOLASE -- @type AUTOLASE @@ -110,7 +111,7 @@ AUTOLASE = { --- AUTOLASE class version. -- @field #string version -AUTOLASE.version = "0.0.12" +AUTOLASE.version = "0.1.14" ------------------------------------------------------------------- -- Begin Functional.Autolase.lua @@ -192,6 +193,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) self.SRSPath = "" self.SRSFreq = 251 self.SRSMod = radio.modulation.AM + self.NoMenus = false -- Set some string id for output to DCS.log file. self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown") @@ -207,14 +209,14 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) self:AddTransition("*", "Cancel", "*") -- Stop Autolase -- Menu Entry - if not PilotSet then - self.Menu = MENU_COALITION_COMMAND:New(self.coalition,"Autolase",nil,self.ShowStatus,self) - else + if PilotSet then self.usepilotset = true self.pilotset = PilotSet self:HandleEvent(EVENTS.PlayerEnterAircraft) - self:SetPilotMenu() + --self:SetPilotMenu() end + --self.SetPilotMenu() + self:SetClusterAnalysis(false, false) @@ -308,6 +310,10 @@ function AUTOLASE:SetPilotMenu() lasemenu:Refresh() end end + else + if not self.NoMenus then + self.Menu = MENU_COALITION_COMMAND:New(self.coalition,"Autolase",nil,self.ShowStatus,self) + end end return self end @@ -356,12 +362,45 @@ end -- @param #string Path Path to SRS directory, e.g. C:\\Program Files\\DCS-SimpleRadio-Standalon -- @param #number Frequency Frequency to send, e.g. 243 -- @param #number Modulation Modulation i.e. radio.modulation.AM or radio.modulation.FM +-- @param #string Label (Optional) Short label to be used on the SRS Client Overlay +-- @param #string Gender (Optional) Defaults to "male" +-- @param #string Culture (Optional) Defaults to "en-US" +-- @param #number Port (Optional) Defaults to 5002 +-- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. +-- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. +-- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest) +-- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS -- @return #AUTOLASE self -function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation) - self.useSRS = OnOff or true - self.SRSPath = Path or "E:\\Program Files\\DCS-SimpleRadio-Standalone" - self.SRSFreq = Frequency or 271 - self.SRSMod = Modulation or radio.modulation.AM +function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Culture,Port,Voice,Volume,PathToGoogleKey) + if OnOff then + self.useSRS = true + self.SRSPath = Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + self.SRSFreq = Frequency or 271 + self.SRSMod = Modulation or radio.modulation.AM + self.Gender = Gender or "male" + self.Culture = Culture or "en-US" + self.Port = Port or 5002 + self.Voice = Voice + self.PathToGoogleKey = PathToGoogleKey + self.Volume = Volume or 1.0 + self.Label = Label + -- set up SRS + self.SRS = MSRS:New(self.SRSPath,self.SRSFreq,self.SRSMod,self.Volume) + self.SRS:SetCoalition(self.coalition) + self.SRS:SetLabel(self.MenuName or self.Name) + self.SRS:SetGender(self.Gender) + self.SRS:SetCulture(self.Culture) + self.SRS:SetPort(self.Port) + self.SRS:SetVoice(self.Voice) + if self.PathToGoogleKey then + self.SRS:SetGoogle(self.PathToGoogleKey) + end + self.SRSQueue = MSRSQUEUE:New(self.alias) + else + self.useSRS = false + self.SRS= nil + self.SRSQueue = nil + end return self end @@ -643,20 +682,7 @@ end -- end function AUTOLASE:NotifyPilotsWithSRS(Message) if self.useSRS then - -- Create a SOUNDTEXT object. - if self.debug then - BASE:TraceOn() - BASE:TraceClass("SOUNDTEXT") - BASE:TraceClass("MSRS") - end - local path = self.SRSPath or "C:\\Program Files\\DCS-SimpleRadio-Standalone" - local freq = self.SRSFreq or 271 - local mod = self.SRSMod or radio.modulation.AM - local text=SOUNDTEXT:New(Message) - -- MOOSE SRS - local msrs=MSRS:New(path, freq, mod) - -- Text-to speech with default voice after 2 seconds. - msrs:PlaySoundText(text, 2) + self.SRSQueue:NewTransmission(Message,nil,self.SRS,nil,2) end if self.debug then self:I(Message) end return self diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 1fc035c8c..21af22579 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -1412,7 +1412,7 @@ function RANGE:AddBombingTargets( targetnames, goodhitrange, randommove ) elseif _isstatic == false then local _unit = UNIT:FindByName( name ) self:T2( self.id .. string.format( "Adding unit bombing target %s with hit range %d.", name, goodhitrange, randommove ) ) - self:AddBombingTargetUnit( _unit, goodhitrange ) + self:AddBombingTargetUnit( _unit, goodhitrange, randommove ) else self:E( self.id .. string.format( "ERROR! Could not find bombing target %s.", name ) ) end @@ -2419,7 +2419,8 @@ end --- Start smoking a coordinate with a delay. -- @param #table _args Argements passed. function RANGE._DelayedSmoke( _args ) - trigger.action.smoke( _args.coord:GetVec3(), _args.color ) + _args.coord:Smoke(_args.color) + --trigger.action.smoke( _args.coord:GetVec3(), _args.color ) end --- Display top 10 stafing results of a specific player. @@ -2447,7 +2448,7 @@ function RANGE:_DisplayMyStrafePitResults( _unitName ) -- Sort results table wrt number of hits. local _sort = function( a, b ) - return a.hits > b.hits + return a.roundsHit > b.roundsHit end table.sort( _results, _sort ) @@ -2464,7 +2465,7 @@ function RANGE:_DisplayMyStrafePitResults( _unitName ) -- Best result. if _bestMsg == "" then - _bestMsg = string.format( "Hits %d - %s - %s", _result.hits, _result.zone.name, _result.text ) + _bestMsg = string.format( "Hits %d - %s - %s", result.roundsHit, result.name, result.roundsQuality) end -- 10 runs @@ -2509,15 +2510,15 @@ function RANGE:_DisplayStrafePitResults( _unitName ) -- Get the best result of the player. local _best = nil for _, _result in pairs( _results ) do - if _best == nil or _result.hits > _best.hits then + if _best == nil or _result.roundsHit > _best.roundsHit then _best = _result end end -- Add best result to table. if _best ~= nil then - local text = string.format( "%s: Hits %i - %s - %s", _playerName, _best.hits, _best.zone.name, _best.text ) - table.insert( _playerResults, { msg = text, hits = _best.hits } ) + local text = string.format( "%s: Hits %i - %s - %s", _playerName, _best.roundsHit, _best.name, _best.roundsQuality ) + table.insert( _playerResults, { msg = text, hits = _best.roundsHit } ) end end @@ -3543,6 +3544,7 @@ function RANGE:_DisplayMessageToGroup( _unit, _text, _time, _clear, display ) -- Group ID. local _gid = _unit:GetGroup():GetID() + local _grp = _unit:GetGroup() -- Get playername and player settings local _, playername = self:_GetPlayerUnitAndName( _unit:GetName() ) @@ -3550,14 +3552,14 @@ function RANGE:_DisplayMessageToGroup( _unit, _text, _time, _clear, display ) -- Send message to player if messages enabled and not only for the examiner. if _gid and (playermessage == true or display) and (not self.examinerexclusive) then - trigger.action.outTextForGroup( _gid, _text, _time, _clear ) + local m = MESSAGE:New(_text,_time,nil,_clear):ToUnit(_unit) end -- Send message to examiner. if self.examinergroupname ~= nil then - local _examinerid = GROUP:FindByName( self.examinergroupname ):GetID() + local _examinerid = GROUP:FindByName( self.examinergroupname ) if _examinerid then - trigger.action.outTextForGroup( _examinerid, _text, _time, _clear ) + local m = MESSAGE:New(_text,_time,nil,_clear):ToGroup(_examinerid) end end end diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 9b9c14acb..9e9d31d29 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -17,7 +17,7 @@ -- === -- -- ### Author: **applevangelist** --- @date Last Update July 2022 +-- @date Last Update August 2022 -- @module Ops.AWACS -- @image OPS_AWACS.jpg @@ -109,6 +109,7 @@ do -- @field #string PlayerStationName -- @field #boolean GCI Act as GCI -- @field Wrapper.Group#GROUP GCIGroup EWR group object for GCI ops +-- @field #string locale Localization -- @extends Core.Fsm#FSM @@ -310,7 +311,8 @@ do -- ## 8 Bespoke Player CallSigns -- -- Append the GROUP name of your client slots with "#CallSign" to use bespoke callsigns in AWACS callouts. E.g. "Player F14#Ghostrider" will be refered to --- as "Ghostrider" plus group number, e.g. "Ghostrider 9". +-- as "Ghostrider" plus group number, e.g. "Ghostrider 9". Alternatively, if you have set up your Player name in the "Logbook" in the mission editor main screen +-- as e.g. "Pikes | Goose", you will be addressed as "Goose" by the AWACS callouts. -- -- ## 9 Options -- @@ -359,6 +361,7 @@ do -- -- messages or too long pauses -- testawacs.GoogleTTSPadding = 1 -- seconds -- testawacs.WindowsTTSPadding = 2.5 -- seconds +-- testawacs.PikesSpecialSwitch = false -- if set to true, AWACS will omit the "doing xy knots" on the station assignement callout -- -- ## 9.2 Bespoke random voices for AI CAP (Google TTS only) -- @@ -390,8 +393,98 @@ do -- in this example will be the name of the new station point. The user marker can then be deleted, an info marker point at the same place will remain. -- You can delete a player station point the same way: "AWACS Delete London"; note this will only work if currently there are no assigned flights on this station. -- Lastly, you can move the station around with keyword "Move": "AWACS Move London". --- --- ## 11 Discussion +-- +-- ## 11 Localization +-- +-- Localization for English text is build-in. Default setting is English. Change with @{#AWACS.SetLocale}() +-- +-- ### 11.1 Adding Localization +-- +-- A list of fields to be defined follows below. **Note** that in some cases `string.format()` is used to format texts for screen and SRS. +-- Hence, the `%d`, `%s` and `%f` special characters need to appear in the exact same amount and order of appearance in the localized text or it will create errors. +-- To add a localization, the following texts need to be translated and set in your mission script **before** @{#AWACS.Start}(): +-- +-- AWACS.Messages = { +-- EN = +-- { +-- DEFEND = "%s, %s! %s! %s! Defend!", +-- VECTORTO = "%s, %s. Vector%s %s", +-- VECTORTOTTS = "%s, %s, Vector%s %s", +-- ANGELS = ". Angels ", +-- ZERO = "zero", +-- VANISHED = "%s, %s Group. Vanished.", +-- VANISHEDTTS = "%s, %s group vanished.", +-- SHIFTCHANGE = "%s shift change for %s control.", +-- GROUPCAP = "Group", +-- GROUP = "group", +-- MILES = "miles", +-- THOUSAND = "thousand", +-- BOGEY = "Bogey", +-- ALLSTATIONS = "All Stations", +-- PICCLEAN = "%s. %s. Picture Clean.", +-- PICTURE = "Picture", +-- ONE = "One", +-- GROUPMULTI = "groups", +-- NOTCHECKEDIN = "%s. %s. Negative. You are not checked in.", +-- CLEAN = "%s. %s. Clean.", +-- DOPE = "%s. %s. Bogey Dope. ", +-- VIDPOS = "%s. %s. Copy, target identified as %s.", +-- VIDNEG = "%s. %s. Negative, get closer to target.", +-- FFNEUTRAL = "Neutral", +-- FFFRIEND = "Friendly", +-- FFHOSTILE = "Hostile", +-- FFSPADES = "Spades", +-- FFCLEAN = "Clean", +-- COPY = "%s. %s. Copy.", +-- TARGETEDBY = "Targeted by %s.", +-- STATUS = "Status", +-- ALREADYCHECKEDIN = "%s. %s. Negative. You are already checked in.", +-- ALPHACHECK = "Alpha Check", +-- CHECKINAI = "%s. %s. Checking in as fragged. Expected playtime %d hours. Request Alpha Check %s.", +-- SAFEFLIGHT = "%s. %s. Copy. Have a safe flight home.", +-- VERYLOW = "very low", +-- AIONSTATION = "%s. %s. On station over anchor %d at angels %d. Ready for tasking.", +-- POPUP = "Pop-up", +-- NEWGROUP = "New group", +-- HIGH= " High.", +-- VERYFAST = " Very fast.", +-- FAST = " Fast.", +-- THREAT = "Threat", +-- MERGED = "Merged", +-- SCREENVID = "Intercept and VID %s group.", +-- SCREENINTER = "Intercept %s group.", +-- ENGAGETAG = "Targeted by %s.", +-- REQCOMMIT = "%s. %s group. %s. %s, request commit.", +-- AICOMMIT = "%s. %s group. %s. %s, commit.", +-- COMMIT = "Commit", +-- SUNRISE = "%s. All stations, SUNRISE SUNRISE SUNRISE, %s.", +-- AWONSTATION = "%s on station for %s control.", +-- STATIONAT = "%s. %s. Station at %s at angels %d.", +-- STATIONATLONG = "%s. %s. Station at %s at angels %d doing %d knots.", +-- STATIONSCREEN = "%s. %s.\nStation at %s\nAngels %d\nSpeed %d knots\nCoord %s\nROE %s.", +-- STATIONTASK = "Station at %s\nAngels %d\nSpeed %d knots\nCoord %s\nROE %s", +-- VECTORSTATION = " to Station", +-- TEXTOPTIONS1 = "Lost friendly flight", +-- TEXTOPTIONS2 = "Vanished friendly flight", +-- TEXTOPTIONS3 = "Faded friendly contact", +-- TEXTOPTIONS4 = "Lost contact with", +-- }, +-- } +-- +-- e.g. +-- +-- testawacs.Messages = { +-- DE = { +-- ... +-- FFNEUTRAL = "Neutral", +-- FFFRIEND = "Freund", +-- FFHOSTILE = "Feind", +-- FFSPADES = "Uneindeutig", +-- FFCLEAN = "Sauber", +-- ... +-- }, +-- +-- ## 12 Discussion -- -- If you have questions or suggestions, please visit the [MOOSE Discord](https://discord.gg/AeYAkHP) #ops-awacs channel. -- @@ -401,7 +494,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "beta 0.2.33", -- #string + version = "0.2.40", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -485,6 +578,7 @@ AWACS = { PlayerStationName = nil, GCI = false, GCIGroup = nil, + locale = "en", } --- @@ -606,6 +700,76 @@ AWACS.CapVoices = { [9] = "en-US-Wavenet-J", [10] = "en-US-Wavenet-H", } + +--- +-- @field Messages +AWACS.Messages = { + EN = + { + DEFEND = "%s, %s! %s! %s! Defend!", + VECTORTO = "%s, %s. Vector%s %s", + VECTORTOTTS = "%s, %s, Vector%s %s", + ANGELS = ". Angels ", + ZERO = "zero", + VANISHED = "%s, %s Group. Vanished.", + VANISHEDTTS = "%s, %s group vanished.", + SHIFTCHANGE = "%s shift change for %s control.", + GROUPCAP = "Group", + GROUP = "group", + MILES = "miles", + THOUSAND = "thousand", + BOGEY = "Bogey", + ALLSTATIONS = "All Stations", + PICCLEAN = "%s. %s. Picture Clean.", + PICTURE = "Picture", + ONE = "One", + GROUPMULTI = "groups", + NOTCHECKEDIN = "%s. %s. Negative. You are not checked in.", + CLEAN = "%s. %s. Clean.", + DOPE = "%s. %s. Bogey Dope. ", + VIDPOS = "%s. %s. Copy, target identified as %s.", + VIDNEG = "%s. %s. Negative, get closer to target.", + FFNEUTRAL = "Neutral", + FFFRIEND = "Friendly", + FFHOSTILE = "Hostile", + FFSPADES = "Spades", + FFCLEAN = "Clean", + COPY = "%s. %s. Copy.", + TARGETEDBY = "Targeted by %s.", + STATUS = "Status", + ALREADYCHECKEDIN = "%s. %s. Negative. You are already checked in.", + ALPHACHECK = "Alpha Check", + CHECKINAI = "%s. %s. Checking in as fragged. Expected playtime %d hours. Request Alpha Check %s.", + SAFEFLIGHT = "%s. %s. Copy. Have a safe flight home.", + VERYLOW = "very low", + AIONSTATION = "%s. %s. On station over anchor %d at angels %d. Ready for tasking.", + POPUP = "Pop-up", + NEWGROUP = "New group", + HIGH= " High.", + VERYFAST = " Very fast.", + FAST = " Fast.", + THREAT = "Threat", + MERGED = "Merged", + SCREENVID = "Intercept and VID %s group.", + SCREENINTER = "Intercept %s group.", + ENGAGETAG = "Targeted by %s.", + REQCOMMIT = "%s. %s group. %s. %s, request commit.", + AICOMMIT = "%s. %s group. %s. %s, commit.", + COMMIT = "Commit", + SUNRISE = "%s. All stations, SUNRISE SUNRISE SUNRISE, %s.", + AWONSTATION = "%s on station for %s control.", + STATIONAT = "%s. %s. Station at %s at angels %d.", + STATIONATLONG = "%s. %s. Station at %s at angels %d doing %d knots.", + STATIONSCREEN = "%s. %s.\nStation at %s\nAngels %d\nSpeed %d knots\nCoord %s\nROE %s.", + STATIONTASK = "Station at %s\nAngels %d\nSpeed %d knots\nCoord %s\nROE %s", + VECTORSTATION = " to Station", + TEXTOPTIONS1 = "Lost friendly flight", + TEXTOPTIONS2 = "Vanished friendly flight", + TEXTOPTIONS3 = "Faded friendly contact", + TEXTOPTIONS4 = "Lost contact with", + }, +} + --- -- @type AWACS.MonitoringData -- @field #string AwacsStateMission @@ -847,7 +1011,7 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station elseif type(OpsZone) == "table" and OpsZone.ClassName and string.find(OpsZone.ClassName,"ZONE") then self.OpsZone = OpsZone else - self:E("AWACS - Invalid OpsZone passed!") + self:E("AWACS - Invalid Zone passed!") return end @@ -1053,6 +1217,7 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station -- Missile warning self:HandleEvent(EVENTS.Shot, self._EventHandler) + self:_InitLocalization() ------------------------ --- Pseudo Functions --- @@ -1174,6 +1339,34 @@ end -- Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- [Internal] Init localization +-- @param #AWACS self +-- @return #AWACS self +function AWACS:_InitLocalization() + self:T(self.lid.."_InitLocalization") + self.gettext = TEXTANDSOUND:New("AWACS","en") -- Core.TextAndSound#TEXTANDSOUND + self.locale = "en" + for locale,table in pairs(self.Messages) do + local Locale = string.lower(tostring(locale)) + self:T("**** Adding locale: "..Locale) + for ID,Text in pairs(table) do + self:T(string.format('Adding ID %s',tostring(ID))) + self.gettext:AddEntry(Locale,tostring(ID),Text) + end + end + return self +end + +--- [User] Set locale for localization. Defaults to "en" +-- @param #AWACS self +-- @param #string Locale The locale to use +-- @return #AWACS self +function AWACS:SetLocale(Locale) + self:T(self.lid.."SetLocale") + self.locale = Locale or "en" + return self +end + --- [User] Set this instance to act as GCI TACS Theater Air Control System -- @param #AWACS self -- @param Wrapper.Group#GROUP EWR The **main** Early Warning Radar (EWR) GROUP object for GCI. @@ -1376,15 +1569,15 @@ function AWACS:_EventHandler(EventData) if Event.id == EVENTS.Shot and self.PlayerGuidance and not self.NoMissileCalls then if Event.IniCoalition ~= self.coalition then self:T("Shot from: " .. Event.IniGroupName) - --self:T(UTILS.OneLineSerialize(Event)) + local position = Event.IniGroup:GetCoordinate() if not position then return self end - --self:T("Coalition = " .. UTILS.GetCoalitionName(Event.IniCoalition)) + -- Check missile type local Category = Event.WeaponCategory local WeaponDesc = EventData.Weapon:getDesc() -- https://wiki.hoggitworld.com/view/DCS_enum_weapon self:T({WeaponDesc}) - --self:T("Weapon = " .. tostring(WeaponDesc.displayName)) + if WeaponDesc.category == 1 and (WeaponDesc.missileCategory == 1 or WeaponDesc.missileCategory == 2) then self:T("AAM or SAM Missile fired") -- Missile fired @@ -1422,20 +1615,22 @@ end -- @return #AWACS self function AWACS:_MissileWarning(Coordinate,Type,Warndist) self:T(self.lid.."_MissileWarning Type="..Type.." WarnDist="..Warndist) - --self:T(UTILS.OneLineSerialize(Coordinate)) + if not Coordinate then return self end local shotzone = ZONE_RADIUS:New("WarningZone",Coordinate:GetVec2(),UTILS.NMToMeters(Warndist)) local targetgrpset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryAirplane():FilterActive():FilterZones({shotzone}):FilterOnce() if targetgrpset:Count() > 0 then local targets = targetgrpset:GetSetObjects() for _,_grp in pairs (targets) do - -- TODO -- player callouts only + -- DONE -- player callouts only if _grp and _grp:IsAlive() then local isPlayer = _grp:IsPlayer() - --if self.debug or isPlayer then + if isPlayer then local callsign = self:_GetCallSign(_grp) - local text = string.format("%s, %s! %s! %s! Defend!",callsign,Type,Type,Type) + local defend = self.gettext:GetEntry("DEFEND",self.locale) + --local text = string.format("%s, %s! %s! %s! Defend!",callsign,Type,Type,Type) + local text = string.format(defend,callsign,Type,Type,Type) self:_NewRadioEntry(text, text,0,false,self.debug,true,false,true) end end @@ -1750,12 +1945,21 @@ function AWACS:_MessageVector(GID,Tag,Coordinate,Angels) --local BRtext = Coordinate:ToStringBR(groupposition) local BRtext,BRtextTTS = self:_ToStringBR(groupposition,Coordinate) - local text = string.format("%s, %s. Vector%s %s",tocallsign, self.callsigntxt,Tag,BRtextTTS) - local textScreen = string.format("%s, %s, Vector%s %s",tocallsign, self.callsigntxt,Tag,BRtext) + local vector = self.gettext:GetEntry("VECTORTO",self.locale) + local vectortts = self.gettext:GetEntry("VECTORTOTTS",self.locale) + local angelstxt = self.gettext:GetEntry("ANGELS",self.locale) + + --local text = string.format("%s, %s. Vector%s %s",tocallsign, self.callsigntxt,Tag,BRtextTTS) + --local textScreen = string.format("%s, %s, Vector%s %s",tocallsign, self.callsigntxt,Tag,BRtext) + + local text = string.format(vectortts,tocallsign, self.callsigntxt,Tag,BRtextTTS) + local textScreen = string.format(vector,tocallsign, self.callsigntxt,Tag,BRtext) if Angels then - text = text .. ". Angels "..tostring(Angels).."." - textScreen = textScreen .. ". Angels "..tostring(Angels).."." + --text = text .. ". Angels "..tostring(Angels).."." + --textScreen = textScreen .. ". Angels "..tostring(Angels).."." + text = text .. angelstxt ..tostring(Angels).."." + textScreen = textScreen ..angelstxt..tostring(Angels).."." end self:_NewRadioEntry(text,textScreen,0,false,self.debug,true,false) @@ -1870,24 +2074,22 @@ function AWACS:_StartSettings(FlightGroup,Mission) group:SetCommandInvisible(self.invisible) group:SetCommandImmortal(self.immortal) group:CommandSetCallsign(self.CallSign,self.CallSignNo,2) - -- Non AWACS does not seem take AWACS CS in DCS Group - -- group:CommandSetCallsign(CALLSIGN.Aircraft.Pig,self.CallSignNo,2) AwacsFG:SetSRS(self.PathToSRS,self.Gender,self.Culture,self.Voice,self.Port,nil,"AWACS") - --self.callsigntxt = string.format("%s %d %d",AWACS.CallSignClear[self.CallSign],1,self.CallSignNo) + self.callsigntxt = string.format("%s",AWACS.CallSignClear[self.CallSign]) - local text = string.format("%s shift change for %s control.",self.callsigntxt,self.AOName or "Rock") + local shifting = self.gettext:GetEntry("SHIFTCHANGE",self.locale) + + local text = string.format(shifting,self.callsigntxt,self.AOName or "Rock") + self:T(self.lid..text) AwacsFG:RadioTransmission(text,1,false) self.AwacsFG = AwacsFG - --self:__CheckRadioQueue(10) - if self.HasEscorts then - --mission:SetRequiredEscorts(self.EscortNumber) self:_StartEscorts(true) end @@ -1907,9 +2109,8 @@ end -- @param #boolean TTS For non-Alpha checks, hand back in format "Rock 0 2 1, 16" -- @return #string BullseyeBR function AWACS:_ToStringBULLS( Coordinate, ssml, TTS ) - -- local BullsCoordinate = COORDINATE:NewFromVec3( coalition.getMainRefPoint( self.coalition ) ) + self:T(self.lid.."_ToStringBULLS") local bullseyename = self.AOName or "Rock" - --local BullsCoordinate = self.OpsZone:GetCoordinate() local BullsCoordinate = self.AOCoordinate local DirectionVec3 = BullsCoordinate:GetDirectionVec3( Coordinate ) local AngleRadians = Coordinate:GetAngleRadians( DirectionVec3 ) @@ -1921,7 +2122,8 @@ function AWACS:_ToStringBULLS( Coordinate, ssml, TTS ) return string.format("%s %03d, %d",bullseyename,Bearing,Distance) elseif TTS then Bearing = self:_ToStringBullsTTS(Bearing) - local BearingTTS = string.gsub(Bearing,"0","zero") + local zero = self.gettext:GetEntry("ZERO",self.locale) + local BearingTTS = string.gsub(Bearing,"0",zero) return string.format("%s %s, %d",bullseyename,BearingTTS,Distance) else return string.format("%s %s, %d",bullseyename,Bearing,Distance) @@ -1974,8 +2176,9 @@ end -- @param #AWACS self -- @param Wrapper.Group#GROUP Group Group to use -- @param #number GID GID to use +-- @param #booean IsPlayer Check in player if true -- @return #string Callsign -function AWACS:_GetCallSign(Group,GID) +function AWACS:_GetCallSign(Group,GID, IsPlayer) self:T(self.lid.."_GetCallSign - GID "..tostring(GID)) if GID and type(GID) == "number" and GID > 0 then @@ -1988,18 +2191,26 @@ function AWACS:_GetCallSign(Group,GID) if Group and Group:IsAlive() then local shortcallsign = Group:GetCallsign() or "unknown11"-- e.g.Uzi11, but we want Uzi 1 1 local callsignroot = string.match(shortcallsign, '(%a+)') - if self.callsignTranslations and self.callsignTranslations[callsignroot] then - shortcallsign = string.gsub(shortcallsign, callsignroot, self.callsignTranslations[callsignroot]) - end - + self:I("CallSign = " .. callsignroot) local groupname = Group:GetName() local callnumber = string.match(shortcallsign, "(%d+)$" ) or "unknown11" local callnumbermajor = string.char(string.byte(callnumber,1)) local callnumberminor = string.char(string.byte(callnumber,2)) - if string.find(groupname,"#") then + local personalized = false + if IsPlayer and string.find(groupname,"#") then -- personalized flight name in group naming shortcallsign = string.match(groupname,"#([%a]+)") + personalized = true end + if IsPlayer and string.find(Group:GetPlayerName(),"|") then + -- personalized flight name in group naming + shortcallsign = string.match(Group:GetPlayerName(),"| ([%a]+)") + personalized = true + end + if (not personalized) and self.callsignTranslations and self.callsignTranslations[callsignroot] then + shortcallsign = string.gsub(shortcallsign, callsignroot, self.callsignTranslations[callsignroot]) + end + if self.callsignshort then callsign = string.gsub(shortcallsign,callnumber,"").." "..callnumbermajor else @@ -2101,8 +2312,10 @@ function AWACS:_CleanUpContacts() deadcontacts:ForEach( function (Contact) local contact = Contact -- #AWACS.ManagedContact - local text = string.format("%s, %s Group. Vanished.",self.callsigntxt, contact.TargetGroupNaming) - local textScreen = string.format("%s, %s group vanished.", self.callsigntxt, contact.TargetGroupNaming) + local vanished = self.gettext:GetEntry("VANISHED",self.locale) + local vanishedtts = self.gettext:GetEntry("VANISHEDTTS",self.locale) + local text = string.format(vanishedtts,self.callsigntxt, contact.TargetGroupNaming) + local textScreen = string.format(vanished, self.callsigntxt, contact.TargetGroupNaming) self:_NewRadioEntry(text,textScreen,0,false,self.debug,true,false,true) self.Contacts:PullByID(contact.CID) -- end @@ -2374,21 +2587,27 @@ function AWACS:_CreatePicture(AO,Callsign,GID,MaxEntries,IsGeneral) local refBRAATTS = "" if self.NoGroupTags then - text = "Group." -- Alpha Group. - textScreen = "Group," + local grouptxt = self.gettext:GetEntry("GROUPCAP",self.locale) + text = grouptxt .. "." -- Alpha Group. + textScreen = grouptxt .."," else - text = contact.TargetGroupNaming.." group." -- Alpha Group. - textScreen = contact.TargetGroupNaming.." group," + local grouptxt = self.gettext:GetEntry("GROUP",self.locale) + text = contact.TargetGroupNaming.." "..grouptxt.."." -- Alpha Group. + textScreen = contact.TargetGroupNaming.." "..grouptxt.."," end if IsGeneral or not self.PlayerGuidance then + local milestxt = self.gettext:GetEntry("MILES",self.locale) + local thsdtxt = self.gettext:GetEntry("THOUSAND",self.locale) refBRAA=self:_ToStringBULLS(coordinate) refBRAATTS = self:_ToStringBULLS(coordinate, false, true) local alt = contact.Contact.group:GetAltitude() or 8000 alt = UTILS.Round(UTILS.MetersToFeet(alt)/1000,0) -- Alpha Group. Bulls eye 0 2 1, 16 miles, 25 thousand. - text = text .. " "..refBRAATTS.." miles, "..alt.." thousand." -- Alpha Group. Bulls eye 0 2 1, 16 miles, 25 thousand. - textScreen = textScreen .. " "..refBRAA.." miles, "..alt.." thousand." -- Alpha Group, Bullseye 021, 16 miles, 25 thousand, + --text = text .. " "..refBRAATTS.." miles, "..alt.." thousand." -- Alpha Group. Bulls eye 0 2 1, 16 miles, 25 thousand. + text = string.format("%s %s %s, %d %s.",text,refBRAATTS,milestxt,alt,thsdtxt) + --textScreen = textScreen .. " "..refBRAA.." miles, "..alt.." thousand." -- Alpha Group, Bullseye 021, 16 miles, 25 thousand, + textScreen = string.format("%s %s %s, %d %s.",textScreen,refBRAA,milestxt,alt,thsdtxt) else -- pilot reference refBRAA = coordinate:ToStringBRAANATO(groupcoord,true,true) @@ -2399,8 +2618,9 @@ function AWACS:_CreatePicture(AO,Callsign,GID,MaxEntries,IsGeneral) refBRAATTS = coordinate:ToStringBRAANATO(groupcoord,true,true,true,false,true) end if contact.IFF ~= AWACS.IFF.BOGEY then - refBRAA = string.gsub(refBRAA,"Bogey", contact.IFF) - refBRAATTS = string.gsub(refBRAATTS,"Bogey", contact.IFF) + local bogey = self.gettext:GetEntry("BOGEY",self.locale) + refBRAA = string.gsub(refBRAA,bogey, contact.IFF) + refBRAATTS = string.gsub(refBRAATTS,bogey, contact.IFF) end text = text .. " "..refBRAATTS textScreen = textScreen .." "..refBRAA @@ -2499,9 +2719,8 @@ function AWACS:_Picture(Group,IsGeneral) --local gcallsign = "" if general then - gcallsign = "All Stations" - --else - --gcallsign = self:_GetCallSign(Group,GID) or "Ghost 1" + local allst = self.gettext:GetEntry("ALLSTATIONS",self.locale) + gcallsign = allst end if Group and Outcome then @@ -2510,7 +2729,8 @@ function AWACS:_Picture(Group,IsGeneral) if not self.intel then -- no intel yet! - text = string.format("%s. %s. Picture Clean.",self.callsigntxt, gcallsign) + local picclean = self.gettext:GetEntry("PICCLEAN",self.locale) + text = string.format(picclean,self.callsigntxt, gcallsign) textScreen = text self:_NewRadioEntry(text,text,GID,false,true,true,false) @@ -2521,7 +2741,6 @@ function AWACS:_Picture(Group,IsGeneral) if Outcome or general then -- Pilot is checked in -- get clusters from Intel - -- DONE Use contacts table! local contactstable = self.Contacts:GetDataTable() --local clustertable = self.intel:GetClusterTable() or {} @@ -2529,12 +2748,8 @@ function AWACS:_Picture(Group,IsGeneral) for _,_contact in pairs(contactstable) do local contact = _contact -- #AWACS.ManagedContact - - --self:T(UTILS.OneLineSerialize(contact)) - - local coordVec2 = contact.Contact.position:GetVec2() - - --local coordVec2 = cluster.coordinate:GetVec2() + + local coordVec2 = contact.Contact.position:GetVec2() if self.OpsZone:IsVec2InZone(coordVec2) then self.PictureAO:Push(contact) @@ -2573,19 +2788,22 @@ function AWACS:_Picture(Group,IsGeneral) else if clustersAO > 0 then - --if general then - --text = string.format("%s, %s. ",gcallsign, self.callsigntxt) - --textScreen = string.format("%s, %s. ",gcallsign, self.callsigntxt) - --else - text = string.format("%s, %s. Picture. ",gcallsign, self.callsigntxt) - textScreen = string.format("%s, %s. Picture. ",gcallsign, self.callsigntxt) - --end + local picture = self.gettext:GetEntry("PICTURE",self.locale) + text = string.format("%s, %s. %s. ",gcallsign, self.callsigntxt,picture) + textScreen = string.format("%s, %s. %s. ",gcallsign, self.callsigntxt,picture) + local onetxt = self.gettext:GetEntry("ONE",self.locale) + local grptxt = self.gettext:GetEntry("GROUP",self.locale) + local groupstxt = self.gettext:GetEntry("GROUPMULTI",self.locale) if clustersAO == 1 then - text = text .. "One group. " - textScreen = textScreen .. "One group.\n" + --text = text .. "One group. " + text = string.format("%s%s %s. ",text,onetxt,grptxt) + --textScreen = textScreen .. "One group.\n" + textScreen = string.format("%s%s %s.\n",textScreen,onetxt,grptxt) else - text = text .. clustersAO .. " groups. " - textScreen = textScreen .. clustersAO .. " groups.\n" + text = string.format("%s%d %s. ",text,clustersAO,groupstxt) + --text = text .. clustersAO .. " groups. " + textScreen = string.format("%s%d %s.\n",textScreen,clustersAO,groupstxt) + --textScreen = textScreen .. clustersAO .. " groups.\n" end self:_NewRadioEntry(text,textScreen,GID,Outcome,true,true,false) @@ -2598,7 +2816,8 @@ function AWACS:_Picture(Group,IsGeneral) elseif self.AwacsFG then -- no, unknown - text = string.format("%s. %s. Negative. You are not checked in.",gcallsign, self.callsigntxt) + local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) + text = string.format(nocheckin,gcallsign, self.callsigntxt) self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) end return self @@ -2617,7 +2836,8 @@ function AWACS:_BogeyDope(Group) if not self.intel then -- no intel yet! - text = string.format("%s. %s. Clean.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local clean = self.gettext:GetEntry("CLEAN",self.locale) + text = string.format(clean,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) self:_NewRadioEntry(text,text,0,false,true,true,false,true) return self @@ -2630,7 +2850,6 @@ function AWACS:_BogeyDope(Group) local pilotgroup = managedgroup.Group local pilotcoord = managedgroup.Group:GetCoordinate() - -- TODO - Use known contacts local contactstable = self.Contacts:GetDataTable() -- sort into buckets - AO only for bogey dope! @@ -2659,15 +2878,17 @@ function AWACS:_BogeyDope(Group) if contactsAO == 0 then -- clean - - text = string.format("%s. %s. Clean.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local clean = self.gettext:GetEntry("CLEAN",self.locale) + text = string.format(clean,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) self:_NewRadioEntry(text,textScreen,GID,Outcome,Outcome,true,false,true) else if contactsAO > 0 then - text = string.format("%s. %s. Bogey Dope. ",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local dope = self.gettext:GetEntry("DOPE",self.locale) + text = string.format(dope,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + --[[ if contactsAO == 1 then text = text .. "One group. " textScreen = text .. "\n" @@ -2675,7 +2896,22 @@ function AWACS:_BogeyDope(Group) text = text .. contactsAO .. " groups. " textScreen = textScreen .. contactsAO .. " groups.\n" end - + --]] + local onetxt = self.gettext:GetEntry("ONE",self.locale) + local grptxt = self.gettext:GetEntry("GROUP",self.locale) + local groupstxt = self.gettext:GetEntry("GROUPMULTI",self.locale) + if contactsAO == 1 then + --text = text .. "One group. " + text = string.format("%s%s %s. ",text,onetxt,grptxt) + --textScreen = textScreen .. "One group.\n" + textScreen = string.format("%s%s %s.\n",textScreen,onetxt,grptxt) + else + text = string.format("%s%d %s. ",text,contactsAO,groupstxt) + --text = text .. clustersAO .. " groups. " + textScreen = string.format("%s%d %s.\n",textScreen,contactsAO,groupstxt) + --textScreen = textScreen .. clustersAO .. " groups.\n" + end + self:_NewRadioEntry(text,textScreen,GID,Outcome,true,true,false,true) self:_CreateBogeyDope(self:_GetCallSign(Group,GID) or "Ghost 1",GID) @@ -2684,8 +2920,9 @@ function AWACS:_BogeyDope(Group) elseif self.AwacsFG then -- no, unknown - text = string.format("%s. %s. Negative. You are not checked in.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) - + --text = string.format("%s. %s. Negative. You are not checked in.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) end @@ -2763,12 +3000,14 @@ function AWACS:_VID(Group,Declaration) self.ManagedTasks:Push(task,TID) self.Contacts:PullByID(CID) self.Contacts:Push(cluster,CID) - text = string.format("%s. %s. Copy, target identified as %s.",Callsign,self.callsigntxt, Declaration) + local vidpos = self.gettext:GetEntry("VIDPOS",self.locale) + text = string.format(vidpos,Callsign,self.callsigntxt, Declaration) self:T(text) else -- too far away self:T("Contact VID not close enough") - text = string.format("%s. %s. Negative, get closer to target.",Callsign,self.callsigntxt) + local vidneg = self.gettext:GetEntry("VIDNEG",self.locale) + text = string.format(vidneg,Callsign,self.callsigntxt) self:T(text) end self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,true) @@ -2777,7 +3016,8 @@ function AWACS:_VID(Group,Declaration) -- elseif self.AwacsFG then -- no, unknown - text = string.format("%s. %s. Negative. You are not checked in.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) end return self @@ -2828,17 +3068,22 @@ function AWACS:_Declare(Group) local ccoalition = contact:GetCoalition() local ctypename = contact:GetTypeName() - local friendorfoe = "Neutral" + local ffneutral = self.gettext:GetEntry("FFNEUTRAL",self.locale) + local fffriend = self.gettext:GetEntry("FFFRIEND",self.locale) + local ffhostile = self.gettext:GetEntry("FFHOSTILE",self.locale) + local ffspades = self.gettext:GetEntry("FFSPADES",self.locale) + + local friendorfoe = ffneutral if self.self.ModernEra then if ccoalition == self.coalition then - friendorfoe = "Friendly" + friendorfoe = fffriend elseif ccoalition == coalition.side.NEUTRAL then - friendorfoe = "Neutral" + friendorfoe = ffneutral elseif ccoalition ~= self.coalition then - friendorfoe = "Hostile" + friendorfoe = ffhostile end else - friendorfoe = "Spades" + friendorfoe = ffspades end -- see if that works self:T(string.format("Distance %d ContactName %s Coalition %d (%s) TypeName %s",distanz,contact:GetName(),ccoalition,friendorfoe,ctypename)) @@ -2852,14 +3097,16 @@ function AWACS:_Declare(Group) end else -- clean - text = string.format("%s. %s. %s.",Callsign,self.callsigntxt,"Clean") + local ffclean = self.gettext:GetEntry("FFCLEAN",self.locale) + text = string.format("%s. %s. %s.",Callsign,self.callsigntxt,ffclean) TextTTS = text end self:_NewRadioEntry(TextTTS,text,GID,Outcome,true,true,false,true) -- elseif self.AwacsFG then -- no, unknown - text = string.format("%s. %s. Negative. You are not checked in.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) end return self @@ -2891,8 +3138,10 @@ function AWACS:_Commit(Group) Pilot.HasAssignedTask = true Pilot.CurrentTask = currtaskid self.ManagedGrps[GID] = Pilot - text = string.format("%s. %s. Copy.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) - local EngagementTag = string.format("Targeted by %s.",Pilot.CallSign) + local copy = self.gettext:GetEntry("COPY",self.locale) + local targetedby = self.gettext:GetEntry("TARGETEDBY",self.locale) + text = string.format(copy,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local EngagementTag = string.format(targetedby,Pilot.CallSign) self:_UpdateContactEngagementTag(Pilot.ContactCID,EngagementTag,false,false,AWACS.TaskStatus.ASSIGNED) self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,true) else @@ -2903,7 +3152,8 @@ function AWACS:_Commit(Group) end elseif self.AwacsFG then -- no, unknown - text = string.format("%s. %s. Negative. You are not checked in.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) end return self @@ -2929,8 +3179,10 @@ function AWACS:_Judy(Group) managedtask = self.ManagedTasks:PullByID(currtaskid) managedtask.Status = AWACS.TaskStatus.ASSIGNED self.ManagedTasks:Push(managedtask,currtaskid) - text = string.format("%s. %s. Copy.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) - local EngagementTag = string.format("Targeted by %s.",Pilot.CallSign) + local copy = self.gettext:GetEntry("COPY",self.locale) + local targetedby = self.gettext:GetEntry("TARGETEDBY",self.locale) + text = string.format(copy,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local EngagementTag = string.format(targetedby,Pilot.CallSign) self:_UpdateContactEngagementTag(Pilot.ContactCID,EngagementTag,false,false,AWACS.TaskStatus.ASSIGNED) self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,true) else @@ -2941,7 +3193,8 @@ function AWACS:_Judy(Group) end elseif self.AwacsFG then -- no, unknown - text = string.format("%s. %s. Negative. You are not checked in.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) end return self @@ -2976,7 +3229,8 @@ function AWACS:_Unable(Group) Pilot.CurrentTask = 0 Pilot.LastTasking = timer.getTime() self.ManagedGrps[GID] = Pilot - text = string.format("%s. %s. Copy.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local copy = self.gettext:GetEntry("COPY",self.locale) + text = string.format(copy,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) local EngagementTag = "" self:_UpdateContactEngagementTag(Pilot.ContactCID,EngagementTag,false,false,AWACS.TaskStatus.UNASSIGNED) self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,true) @@ -2988,7 +3242,8 @@ function AWACS:_Unable(Group) end elseif self.AwacsFG then -- no, unknown - text = string.format("%s. %s. Negative. You are not checked in.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) end return self @@ -3026,7 +3281,8 @@ function AWACS:_TaskAbort(Group) Pilot.CurrentTask = 0 Pilot.LastTasking = timer.getTime() self.ManagedGrps[GID] = Pilot - text = string.format("%s. %s. Copy.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local copy = self.gettext:GetEntry("COPY",self.locale) + text = string.format(copy,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) local EngagementTag = "" self:_UpdateContactEngagementTag(Pilot.ContactCID,EngagementTag,false,false,AWACS.TaskStatus.UNASSIGNED) self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,true) @@ -3038,7 +3294,8 @@ function AWACS:_TaskAbort(Group) end elseif self.AwacsFG then -- no, unknown - text = string.format("%s. %s. Negative. You are not checked in.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) end @@ -3098,8 +3355,8 @@ function AWACS:_Showtask(Group) local direction = self:_ToStringBR(pposition,targetpos) description = description .. "\nBR "..direction end - - MESSAGE:New(string.format("%s\nStatus %s",description,status),30,"AWACS",true):ToGroup(Group) + local statustxt = self.gettext:GetEntry("STATUS",self.locale) + MESSAGE:New(string.format("%s\n%s %s",description,statustxt,status),30,"AWACS",true):ToGroup(Group) end end @@ -3107,7 +3364,8 @@ function AWACS:_Showtask(Group) elseif self.AwacsFG then -- no, unknown - text = string.format("%s. %s. Negative. You are not checked in.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) end return self @@ -3127,17 +3385,15 @@ function AWACS:_CheckIn(Group) self.ManagedGrpID = self.ManagedGrpID + 1 local managedgroup = {} -- #AWACS.ManagedGroup managedgroup.Group = Group - --managedgroup.GroupName = string.match(Group:GetName(),"([%a%s]+)#") managedgroup.GroupName = Group:GetName() managedgroup.IsPlayer = true managedgroup.IsAI = false - managedgroup.CallSign = self:_GetCallSign(Group,GID) or "Ghost 1" + managedgroup.CallSign = self:_GetCallSign(Group,GID,true) or "Ghost 1" managedgroup.CurrentAuftrag = 0 managedgroup.CurrentTask = 0 managedgroup.HasAssignedTask = true managedgroup.Blocked = true managedgroup.GID = self.ManagedGrpID - --managedgroup.TaskQueue = FIFO:New() managedgroup.LastKnownPosition = Group:GetCoordinate() managedgroup.LastTasking = timer.getTime() @@ -3145,12 +3401,12 @@ function AWACS:_CheckIn(Group) self.ManagedGrps[self.ManagedGrpID]=managedgroup local alphacheckbulls = self:_ToStringBULLS(Group:GetCoordinate()) - --local alphacheckbullstts = self:_ToStringBullsTTS(alphacheckbulls)-- make tts friendly local alphacheckbullstts = self:_ToStringBULLS(Group:GetCoordinate(),false,true) --self.ManagedGrps[self.ManagedGrpID]=managedgroup - text = string.format("%s. %s. Alpha Check. %s",managedgroup.CallSign,self.callsigntxt,alphacheckbulls) - textTTS = string.format("%s. %s. Alpha Check. %s",managedgroup.CallSign,self.callsigntxt,alphacheckbullstts) + local alpha = self.gettext:GetEntry("ALPHACHECK",self.locale) + text = string.format("%s. %s. %s. %s",managedgroup.CallSign,self.callsigntxt,alpha,alphacheckbulls) + textTTS = text self:__CheckedIn(1,managedgroup.GID) @@ -3161,7 +3417,9 @@ function AWACS:_CheckIn(Group) end elseif self.AwacsFG then - text = string.format("%s. %s. Negative. You are already checked in.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local nocheckin = self.gettext:GetEntry("ALREADYCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + textTTS = text end self:_NewRadioEntry(textTTS,text,GID,Outcome,true,true,false) @@ -3189,6 +3447,9 @@ function AWACS:_CheckInAI(FlightGroup,Group,AuftragsNr) managedgroup.IsPlayer = false managedgroup.IsAI = true local callsignstring = UTILS.GetCallsignName(self.AICAPCAllName) + if self.callsignTranslations and self.callsignTranslations[callsignstring] then + callsignstring = self.callsignTranslations[callsignstring] + end local callsignmajor = math.fmod(self.AICAPCAllNumber,9) local callsign = string.format("%s %d 1",callsignstring,callsignmajor) if self.callsignshort then @@ -3215,14 +3476,15 @@ function AWACS:_CheckInAI(FlightGroup,Group,AuftragsNr) FlightGroup:SetSRS(self.PathToSRS,self.CAPGender,self.CAPCulture,CAPVoice,self.Port,self.PathToGoogleKey,"FLIGHT") - text = string.format("%s. %s. Checking in as fragged. Expected playtime %d hours. Request Alpha Check %s.",self.callsigntxt, managedgroup.CallSign, self.CAPTimeOnStation, self.AOName) + local checkai = self.gettext:GetEntry("CHECKINAI",self.locale) + text = string.format(checkai,self.callsigntxt, managedgroup.CallSign, self.CAPTimeOnStation, self.AOName) self:_NewRadioEntry(text,text,managedgroup.GID,Outcome,false,true,true) local alphacheckbulls = self:_ToStringBULLS(Group:GetCoordinate(),false,true) - --alphacheckbulls = self:_ToStringBullsTTS(alphacheckbulls)-- make tts friendly - - text = string.format("%s. %s. Alpha Check. %s",managedgroup.CallSign,self.callsigntxt,alphacheckbulls) + + local alpha = self.gettext:GetEntry("ALPHACHECK",self.locale) + text = string.format("%s. %s. %s. %s",managedgroup.CallSign,self.callsigntxt,alpha,alphacheckbulls) self:__CheckedIn(1,managedgroup.GID) local AW = FlightGroup:GetAirWing() @@ -3232,7 +3494,8 @@ function AWACS:_CheckInAI(FlightGroup,Group,AuftragsNr) self:__AssignAnchor(5,managedgroup.GID) end else - text = string.format("%s. %s. Negative. You are already checked in.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local nocheckin = self.gettext:GetEntry("ALREADYCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) end self:_NewRadioEntry(text,text,GID,Outcome,false,true,false) @@ -3254,7 +3517,8 @@ function AWACS:_CheckOut(Group,GID,dead) local text = "" if Outcome then -- yes, known - text = string.format("%s. %s. Copy. Have a safe flight home.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local safeflight = self.gettext:GetEntry("SAFEFLIGHT",self.locale) + text = string.format(safeflight,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) self:T(text) -- grab some data before we nil the entry local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup @@ -3279,7 +3543,8 @@ function AWACS:_CheckOut(Group,GID,dead) else -- no, unknown if not dead then - text = string.format("%s. %s. Negative. You are not checked in.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) end end @@ -3329,6 +3594,10 @@ function AWACS:_SetClientMenus() basemenu:RemoveSubMenus() --basemenu:Refresh() + local bogeydope = MENU_GROUP_COMMAND:New(cgrp,"Bogey Dope",basemenu,self._BogeyDope,self,cgrp) + local picture = MENU_GROUP_COMMAND:New(cgrp,"Picture",basemenu,self._Picture,self,cgrp) + local declare = MENU_GROUP_COMMAND:New(cgrp,"Declare",basemenu,self._Declare,self,cgrp) + local tasking = MENU_GROUP:New(cgrp,"Tasking",basemenu) local showtask = MENU_GROUP_COMMAND:New(cgrp,"Showtask",tasking,self._Showtask,self,cgrp) local commit = MENU_GROUP_COMMAND:New(cgrp,"Commit",tasking,self._Commit,self,cgrp) @@ -3343,15 +3612,8 @@ function AWACS:_SetClientMenus() local friendly = MENU_GROUP_COMMAND:New(cgrp,"Friendly",vid,self._VID,self,cgrp,AWACS.IFF.FRIENDLY) end - local picture = MENU_GROUP_COMMAND:New(cgrp,"Picture",basemenu,self._Picture,self,cgrp) - local bogeydope = MENU_GROUP_COMMAND:New(cgrp,"Bogey Dope",basemenu,self._BogeyDope,self,cgrp) - - local declare = MENU_GROUP_COMMAND:New(cgrp,"Declare",basemenu,self._Declare,self,cgrp) local ainfo = MENU_GROUP_COMMAND:New(cgrp,"Awacs Info",basemenu,self._ShowAwacsInfo,self,cgrp) local checkout = MENU_GROUP_COMMAND:New(cgrp,"Check Out",basemenu,self._CheckOut,self,cgrp) - - --basemenu:Set() - basemenu:Refresh() local menus = { -- #AWACS.MenuStructure groupname = cgrpname, @@ -3415,7 +3677,6 @@ function AWACS:_SetClientMenus() local checkin = MENU_GROUP_COMMAND:New(cgrp,"Check In",basemenu,self._CheckIn,self,cgrp) local checkout = MENU_GROUP_COMMAND:New(cgrp,"Check Out",basemenu,self._CheckOut,self,cgrp) - --basemenu:Set() basemenu:Refresh() local menus = { -- #AWACS.MenuStructure @@ -3438,8 +3699,7 @@ function AWACS:_SetClientMenus() end end end - - --self.clientmenus = clientmenus + self.MonitoringData.Players = clientcount or 0 self.MonitoringData.PlayersCheckedin = clientcheckedin or 0 @@ -3702,8 +3962,6 @@ function AWACS:_RemoveIDFromAnchor(GID,AnchorStackNo,Angels) local removedID = Anchor.AnchorAssignedID:PullByID(GID) -- push free angels to stack Anchor.Anchors:Push(Angels) - -- push back AnchorStack - --self.AnchorStacks:Push(Anchor) end return self end @@ -3845,24 +4103,22 @@ function AWACS:_ToStringBR(FromCoordinate,ToCoordinate) local AngleDegText = string.format("%03d",AngleDegrees) -- 051 local AngleDegTextTTS = "" - --if self.PathToGoogleKey then - --AngleDegTextTTS = string.format("%s",AngleDegText) - --else - --AngleDegTextTTS = string.format("%s",AngleDegText) - --end + local zero = self.gettext:GetEntry("ZERO",self.locale) + local miles = self.gettext:GetEntry("MILES",self.locale) + AngleDegText = string.gsub(AngleDegText,"%d","%1 ") -- "0 5 1 " AngleDegText = string.gsub(AngleDegText," $","") -- "0 5 1" - AngleDegTextTTS = string.gsub(AngleDegText,"0","zero") + AngleDegTextTTS = string.gsub(AngleDegText,"0",zero) local Distance = ToCoordinate:Get2DDistance( FromCoordinate ) --meters local distancenm = UTILS.Round(UTILS.MetersToNM(Distance),0) - BRText = string.format("%03d, %d miles",AngleDegrees,distancenm) - BRTextTTS = string.format("%s, %d miles",AngleDegText,distancenm) + BRText = string.format("%03d, %d %s",AngleDegrees,distancenm,miles) + BRTextTTS = string.format("%s, %d %s",AngleDegText,distancenm,miles) if self.PathToGoogleKey then - BRTextTTS = string.format("%s, %d miles",AngleDegTextTTS,distancenm) + BRTextTTS = string.format("%s, %d %s",AngleDegTextTTS,distancenm,miles) end self:T(BRText,BRTextTTS) @@ -3886,24 +4142,29 @@ function AWACS:_ToStringBRA(FromCoordinate,ToCoordinate,Altitude) local AngleDegrees = UTILS.Round( UTILS.ToDegree( AngleRadians ), 0 ) -- degrees local AngleDegText = string.format("%03d",AngleDegrees) -- 051 - --local AngleDegTextTTS = string.format("%s",AngleDegText) AngleDegText = string.gsub(AngleDegText,"%d","%1 ") -- "0 5 1 " AngleDegText = string.gsub(AngleDegText," $","") -- "0 5 1" local AngleDegTextTTS = string.gsub(AngleDegText,"0","zero") local Distance = ToCoordinate:Get2DDistance( FromCoordinate ) --meters local distancenm = UTILS.Round(UTILS.MetersToNM(Distance),0) + + local zero = self.gettext:GetEntry("ZERO",self.locale) + local miles = self.gettext:GetEntry("MILES",self.locale) + local thsd = self.gettext:GetEntry("THOUSAND",self.locale) + local vlow = self.gettext:GetEntry("VERYLOW",self.locale) + if altitude >= 1 then - BRText = string.format("%03d, %d miles, %d thousand",AngleDegrees,distancenm,altitude) - BRTextTTS = string.format("%s, %d miles, %d thousand",AngleDegText,distancenm,altitude) + BRText = string.format("%03d, %d %s, %d %s",AngleDegrees,distancenm,miles,altitude,thsd) + BRTextTTS = string.format("%s, %d %s, %d %s",AngleDegText,distancenm,miles,altitude,thsd) if self.PathToGoogleKey then - BRTextTTS = string.format("%s, %d miles, %d thousand",AngleDegTextTTS,distancenm,altitude) + BRTextTTS = string.format("%s, %d %s, %d %s",AngleDegTextTTS,distancenm,miles,altitude,thsd) end else - BRText = string.format("%03d, %d miles, very low",AngleDegrees,distancenm) - BRTextTTS = string.format("%s, %d miles, very low",AngleDegText,distancenm) + BRText = string.format("%03d, %d %s, %s",AngleDegrees,distancenm,miles,vlow) + BRTextTTS = string.format("%s, %d %s, %s",AngleDegText,distancenm,miles,vlow) if self.PathToGoogleKey then - BRTextTTS = string.format("%s, %d miles, very low",AngleDegTextTTS,distancenm) + BRTextTTS = string.format("%s, %d %s, %s",AngleDegTextTTS,distancenm,miles,vlow) end end self:T(BRText,BRTextTTS) @@ -4026,7 +4287,6 @@ function AWACS:_CreateIdleTaskForContact(Description,Object,Contact) task.ToDo = Description task.Target = TARGET:New(Object) task.Contact = Contact - --task.IsContact = true task.ScreenText = Description if Description == AWACS.TaskDescription.ANCHOR or Description == AWACS.TaskDescription.REANCHOR then task.Target.Type = TARGET.ObjectType.ZONE @@ -4049,11 +4309,8 @@ function AWACS:_CreateIdleTaskForCluster(Description,Object,Cluster) task.AssignedGroupID = 0 task.Status = AWACS.TaskStatus.IDLE task.ToDo = Description - --self:T({Cluster.Contacts}) - --task.Target = TARGET:New(Cluster.Contacts[1]) task.Target = TARGET:New(self.intel:GetClusterCoordinate(Cluster)) task.Cluster = Cluster - --task.IsCluster = true task.ScreenText = Description if Description == AWACS.TaskDescription.ANCHOR or Description == AWACS.TaskDescription.REANCHOR then task.Target.Type = TARGET.ObjectType.ZONE @@ -4072,7 +4329,8 @@ function AWACS:_MessageAIReadyForTasking(GID) if GID >0 and self.ManagedGrps[GID] then local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup local GFCallsign = self:_GetCallSign(managedgroup.Group) - local TextTTS = string.format("%s. %s. On station over anchor %d at angels %d. Ready for tasking.",GFCallsign,self.callsigntxt,managedgroup.AnchorStackNo or 1,managedgroup.AnchorStackAngels or 25) + local aionst = self.gettext:GetEntry("AIONSTATION",self.locale) + local TextTTS = string.format(aionst,GFCallsign,self.callsigntxt,managedgroup.AnchorStackNo or 1,managedgroup.AnchorStackAngels or 25) self:_NewRadioEntry(TextTTS,TextTTS,GID,false,false,true,true) end return self @@ -4153,9 +4411,6 @@ function AWACS:_CheckTaskQueue() if managedgroup.IsAI then -- message AI on station self:_MessageAIReadyForTasking(managedgroup.GID) - elseif managedgroup.IsPlayer then - --self.TaskedCAPHuman:PullByPointer(entry.AssignedGroupID) - --self.CAPIdleHuman:Push(entry.AssignedGroupID) end -- end isAI managedgroup.HasAssignedTask = false self.ManagedGrps[entry.AssignedGroupID] = managedgroup @@ -4195,7 +4450,6 @@ function AWACS:_CheckTaskQueue() local targetgrp = entry.Contact.group local position = entry.Contact.position or entry.Cluster.coordinate if targetgrp and targetgrp:IsAlive() and managedgroup then - --position = targetgrp:GetCoordinate() if position and managedgroup.Group and managedgroup.Group:IsAlive() then local grouposition = managedgroup.Group:GetCoordinate() or managedgroup.Group:GetCoordinate() local distance = 1000 @@ -4354,7 +4608,6 @@ function AWACS:_CheckTaskQueue() -- check we're alive if (not managedgroup) or (not managedgroup.Group:IsAlive()) then self.ManagedTasks:PullByID(entry.TID) - --entry.Status = AWACS.TaskStatus.FAILED return self end @@ -4419,7 +4672,6 @@ function AWACS:_CheckTaskQueue() local targetgrp = entry.Contact.group local position = entry.Contact.position or entry.Cluster.coordinate if targetgrp and targetgrp:IsAlive() and managedgroup then - --position = targetgrp:GetCoordinate() if position and managedgroup.Group and managedgroup.Group:IsAlive() then local grouposition = managedgroup.Group:GetCoordinate() or managedgroup.Group:GetCoordinate() local distance = 1000 @@ -4464,14 +4716,12 @@ function AWACS:_CheckTaskQueue() if managedgroup.IsPlayer then entry.IsPlayerTask = false end - --self.ManagedTasks:PullByID(entry.TID) self.ManagedGrps[entry.AssignedGroupID] = managedgroup self:__ReAnchor(5,managedgroup.GID) end elseif Contact and Contact.IFF == AWACS.IFF.ENEMY then self:T("IFF outcome hostile for GroupID "..entry.AssignedGroupID) -- change to intercept - --self.ManagedTasks:PullByID(entry.TID) entry.ToDo = AWACS.TaskDescription.INTERCEPT entry.Status = AWACS.TaskStatus.ASSIGNED local cname = Contact.TargetGroupNaming @@ -4495,7 +4745,6 @@ function AWACS:_CheckTaskQueue() if managedgroup.IsPlayer then entry.IsPlayerTask = false end - --self.ManagedTasks:PullByID(entry.TID) self.ManagedGrps[entry.AssignedGroupID] = managedgroup if managedgroup.Group:IsAlive() or managedgroup.FlightGroup:IsAlive() then self:__ReAnchor(5,managedgroup.GID) @@ -4619,7 +4868,6 @@ end -- @return #AWACS self function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,ReportingName) self:T(self.lid.."_AnnounceContact") - --self:T({Contact}) -- do we have a group to talk to? local tag = "" local Tag = Tag @@ -4628,7 +4876,6 @@ function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,Repo -- injected data available? CID = Contact.CID or 0 Tag = Contact.TargetGroupNaming or "" - --self:T({CID,Tag}) end if self.NoGroupTags then Tag = nil @@ -4639,7 +4886,6 @@ function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,Repo if Group and Group:IsAlive() then GID, isGroup,grpcallsign = self:_GetManagedGrpID(Group) self:T("GID="..GID.." CheckedIn = "..tostring(isGroup)) - --grpcallsign = self:_GetCallSign(Group,GID) or "Ghost 1" end local cluster = Contact.Cluster @@ -4655,8 +4901,7 @@ function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,Repo clustercoordinate:SetHeading(Contact.Contact.group:GetHeading()) local BRAfromBulls, BRAfromBullsTTS = self:_GetBRAfromBullsOrAO(clustercoordinate) - - + self:T(BRAfromBulls) self:T(BRAfromBullsTTS) BRAfromBulls=BRAfromBulls.."." @@ -4670,9 +4915,6 @@ function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,Repo BRAfromBullsTTS = clustercoordinate:ToStringBRAANATO(Group:GetCoordinate(),true,true,true,false,true) end end - - -- "Uzi 1-1, Magic, BRA, 183 for 10 at 2000, hot" - -- ", , /, , BRA for at angels , , " local BRAText = "" local TextScreen = "" @@ -4685,18 +4927,31 @@ function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,Repo TextScreen = string.format("%s.",self.callsigntxt) end + local newgrp = self.gettext:GetEntry("NEWGROUP",self.locale) + local grptxt = self.gettext:GetEntry("GROUP",self.locale) + local GRPtxt = self.gettext:GetEntry("GROUPCAP",self.locale) + local popup = self.gettext:GetEntry("POPUP",self.locale) + if IsNew and self.PlayerGuidance then - BRAText = BRAText .. " New group." - TextScreen = TextScreen .. " New group." + --BRAText = BRAText .. " New group." + BRAText = string.format("%s %s.",BRAText,newgrp) + --TextScreen = TextScreen .. " New group." + TextScreen = string.format("%s %s.",TextScreen,newgrp) elseif IsPopup then - BRAText = BRAText .. " Pop-up group." - TextScreen = TextScreen .. " Pop-up group." + --BRAText = BRAText .. " Pop-up group." + BRAText = string.format("%s %s %s.",BRAText,popup,grptxt) + --TextScreen = TextScreen .. " Pop-up group." + TextScreen = string.format("%s %s %s.",TextScreen,popup,grptxt) elseif IsBogeyDope and Tag and Tag ~= "" then - BRAText = BRAText .. " "..Tag.." group." - TextScreen = TextScreen .. " "..Tag.." group." + --BRAText = BRAText .. " "..Tag.." group." + BRAText = string.format("%s %s %s.",BRAText,Tag,grptxt) + --TextScreen = TextScreen .. " "..Tag.." group." + TextScreen = string.format("%s %s %s.",TextScreen,Tag,grptxt) else - BRAText = BRAText .. " Group." - TextScreen = TextScreen .. " Group." + --BRAText = BRAText .. " Group." + BRAText = string.format("%s %s.",BRAText,GRPtxt) + --TextScreen = TextScreen .. " Group." + TextScreen = string.format("%s %s.",TextScreen,GRPtxt) end if not IsBogeyDope then @@ -4707,18 +4962,17 @@ function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,Repo end if threatsize > 1 then - --BRAText = BRAText .. " "..threatsizetext..". "..BRAfromBullsTTS BRAText = BRAText .. " "..BRAfromBullsTTS.." "..threatsizetext.."." - --TextScreen = TextScreen .. " "..threatsizetext..". "..BRAfromBulls TextScreen = TextScreen .. " "..BRAfromBulls.." "..threatsizetext.."." else - --BRAText = BRAText .. " "..threatsizetext..". "..BRAfromBullsTTS BRAText = BRAText .. " "..BRAfromBullsTTS - --TextScreen = TextScreen .. " "..threatsizetext..". "..BRAfromBulls TextScreen = TextScreen .. " "..BRAfromBulls end if self.ModernEra then + local high = self.gettext:GetEntry("HIGH",self.locale) + local vfast = self.gettext:GetEntry("VERYFAST",self.locale) + local fast = self.gettext:GetEntry("FAST",self.locale) -- Platform if ReportingName and ReportingName ~= "Bogey" then ReportingName = string.gsub(ReportingName,"_"," ") @@ -4729,24 +4983,23 @@ function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,Repo local height = Contact.Contact.group:GetHeight() local height = UTILS.Round(UTILS.MetersToFeet(height)/1000,0) -- e.g, 25 if height >= 40 then - BRAText = BRAText .. " High." - TextScreen = TextScreen .. " High." + BRAText = BRAText .. high + TextScreen = TextScreen .. high end -- Fast (>600kn) or Very fast (>900kn) local speed = Contact.Contact.group:GetVelocityKNOTS() if speed > 900 then - BRAText = BRAText .. " Very Fast." - TextScreen = TextScreen .. " Very Fast." + BRAText = BRAText .. vfast + TextScreen = TextScreen .. vfast elseif speed >= 600 and speed <= 900 then - BRAText = BRAText .. " Fast." - TextScreen = TextScreen .. " Fast." + BRAText = BRAText .. fast + TextScreen = TextScreen .. fast end end string.gsub(BRAText,"BRAA","brah") string.gsub(BRAText,"BRA","brah") - --self:T(BRAText) local prio = IsNew or IsBogeyDope self:_NewRadioEntry(BRAText,TextScreen,GID,isGroup,true,IsNew,false,prio) @@ -4764,7 +5017,6 @@ function AWACS:_GetAliveOpsGroupFromTable(OpsGroups) local OG = _OG -- Ops.OpsGroup#OPSGROUP if OG and OG:IsAlive() then handback = OG - --self:T("Handing back OG: " .. OG:GetName()) break end end @@ -4886,7 +5138,6 @@ function AWACS:_CheckAICAPOnStation() local onstation = capmissions + alert5missions - --if self.AIRequested > self.MaxAIonCAP then if capmissions > self.MaxAIonCAP then -- too many, send one home self:T(string.format("*** Onstation %d > MaxAIOnCAP %d",onstation,self.MaxAIonCAP)) @@ -4902,7 +5153,7 @@ function AWACS:_CheckAICAPOnStation() end -- control number of AI CAP Flights - if capmissions < self.MaxAIonCAP then + if capmissions < self.MaxAIonCAP and alert5missions < self.MaxAIonCAP+2 then -- not enough local AnchorStackNo,free = self:_GetFreeAnchorStack() if free then @@ -5056,7 +5307,9 @@ function AWACS:_TACRangeCall(GID,Contact) if position then local distance = position:Get2DDistance(managedgroup.Group:GetCoordinate()) distance = UTILS.Round(UTILS.MetersToNM(distance)) -- 30nm - hopefully - local text = string.format("%s. %s. %s group, %d miles.",self.callsigntxt,pilotcallsign,contacttag,distance) + local grptxt = self.gettext:GetEntry("GROUP",self.locale) + local miles = self.gettext:GetEntry("MILES",self.locale) + local text = string.format("%s. %s. %s %s, %d %s.",self.callsigntxt,pilotcallsign,contacttag,grptxt,distance,miles) self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) self:_UpdateContactEngagementTag(Contact.CID,Contact.EngagementTag,true,false,AWACS.TaskStatus.EXECUTING) end @@ -5087,7 +5340,8 @@ function AWACS:_MeldRangeCall(GID,Contact) else BRATExt = position:ToStringBRAANATO(flightpos,false,false) end - local text = string.format("%s. %s. %s group, %s",self.callsigntxt,pilotcallsign,contacttag,BRATExt) + local grptxt = self.gettext:GetEntry("GROUP",self.locale) + local text = string.format("%s. %s. %s %s, %s",self.callsigntxt,pilotcallsign,contacttag,grptxt,BRATExt) self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) self:_UpdateContactEngagementTag(Contact.CID,Contact.EngagementTag,true,true,AWACS.TaskStatus.EXECUTING) end @@ -5116,7 +5370,9 @@ function AWACS:_ThreatRangeCall(GID,Contact) else BRATExt = position:ToStringBRAANATO(flightpos,false,false) end - local text = string.format("%s. %s. %s group, Threat. %s",self.callsigntxt,pilotcallsign,contacttag,BRATExt) + local grptxt = self.gettext:GetEntry("GROUP",self.locale) + local thrt = self.gettext:GetEntry("THREAT",self.locale) + local text = string.format("%s. %s. %s %s, %s. %s",self.callsigntxt,pilotcallsign,contacttag,grptxt, thrt, BRATExt) self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) end end @@ -5131,8 +5387,8 @@ function AWACS:_MergedCall(GID) self:T(self.lid.."_MergedCall") -- AIC: “Enforcer, mergedb” local pilotcallsign = self:_GetCallSign(nil,GID) - --local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup - local text = string.format("%s. %s. Merged.",self.callsigntxt,pilotcallsign) + local merge = self.gettext:GetEntry("MERGED",self.locale) + local text = string.format("%s. %s. %s.",self.callsigntxt,pilotcallsign,merge) self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) return self end @@ -5188,10 +5444,12 @@ function AWACS:_AssignPilotToTarget(Pilots,Targets) local ScreenText = "" local TaskType = AWACS.TaskDescription.INTERCEPT if self.AwacsROE == AWACS.ROE.POLICE or self.AwacsROE == AWACS.ROE.VID then - ScreenText = string.format("Intercept and VID %s group.",Target.TargetGroupNaming) + local interc = self.gettext:GetEntry("SCREENVID",self.locale) + ScreenText = string.format(interc,Target.TargetGroupNaming) TaskType = AWACS.TaskDescription.VID else - ScreenText = string.format("Intercept %s group.",Target.TargetGroupNaming) + local interc = self.gettext:GetEntry("SCREENINTER",self.locale) + ScreenText = string.format(interc,Target.TargetGroupNaming) end Pilot.CurrentTask = self:_CreateTaskForGroup(Pilot.GID,TaskType,ScreenText,Target.Target,AWACS.TaskStatus.REQUESTED,nil,Target.Cluster,Target.Contact) @@ -5204,13 +5462,15 @@ function AWACS:_AssignPilotToTarget(Pilots,Targets) Target.LinkedTask = Pilot.CurrentTask Target.LinkedGroup = Pilot.GID Target.Status = AWACS.TaskStatus.REQUESTED - Target.EngagementTag = string.format("Targeted by %s.",Pilot.CallSign) + local targeted = self.gettext:GetEntry("ENGAGETAG",self.locale) + Target.EngagementTag = string.format(targeted,Pilot.CallSign) self.Contacts:PullByID(Target.CID) self.Contacts:Push(Target,Target.CID) - local text = string.format("%s. %s group. %s. %s, request commit.", self.callsigntxt,Target.TargetGroupNaming,TargetDirectionsTTS,Pilot.CallSign) - local textScreen = string.format("%s. %s group. %s. %s, request commit.", self.callsigntxt,Target.TargetGroupNaming,TargetDirections,Pilot.CallSign) + local reqcomm = self.gettext:GetEntry("REQCOMMIT",self.locale) + local text = string.format(reqcomm, self.callsigntxt,Target.TargetGroupNaming,TargetDirectionsTTS,Pilot.CallSign) + local textScreen = string.format(reqcomm, self.callsigntxt,Target.TargetGroupNaming,TargetDirections,Pilot.CallSign) self:_NewRadioEntry(text,textScreen,Pilot.GID,true,self.debug,true,false,true) @@ -5312,14 +5572,12 @@ function AWACS:_AssignPilotToTarget(Pilots,Targets) Target.LinkedTask = Pilot.CurrentTask Target.LinkedGroup = Pilot.GID Target.Status = AWACS.TaskStatus.ASSIGNED - Target.EngagementTag = string.format("Targeted by %s.",Pilot.CallSign) + local targeted = self.gettext:GetEntry("ENGAGETAG",self.locale) + Target.EngagementTag = string.format(targeted,Pilot.CallSign) self.Contacts:PullByID(Target.CID) self.Contacts:Push(Target,Target.CID) - -- message commit and return commit from AI - --local bratext = Target.Contact.position:ToStringBRA(Pilot.Group:GetCoordinate()) - local altitude = Target.Contact.altitude or Target.Contact.group:GetAltitude() local position = Target.Cluster.coordinate or Target.Contact.position if not position then @@ -5327,12 +5585,14 @@ function AWACS:_AssignPilotToTarget(Pilots,Targets) end local bratext, bratexttts = self:_ToStringBRA(Pilot.Group:GetCoordinate(),position,altitude or 8000) - local text = string.format("%s. %s group. %s. %s, commit.", self.callsigntxt,Target.TargetGroupNaming,bratexttts,Pilot.CallSign) - local textScreen = string.format("%s. %s group. %s. %s, request commit.", self.callsigntxt,Target.TargetGroupNaming,bratext,Pilot.CallSign) + local aicomm = self.gettext:GetEntry("AICOMMIT",self.locale) + local text = string.format(aicomm, self.callsigntxt,Target.TargetGroupNaming,bratexttts,Pilot.CallSign) + local textScreen = string.format(aicomm, self.callsigntxt,Target.TargetGroupNaming,bratext,Pilot.CallSign) self:_NewRadioEntry(text,textScreen,Pilot.GID,true,self.debug,true,false,true) - local text = string.format("%s. Commit.",Pilot.CallSign) + local comm = self.gettext:GetEntry("COMMIT",self.locale) + local text = string.format("%s. %s.",Pilot.CallSign,comm) self:_NewRadioEntry(text,text,Pilot.GID,true,self.debug,true,true,true) @@ -5421,7 +5681,8 @@ function AWACS:onafterStart(From, Event, To) self.callsigntxt = string.format("%s",AWACS.CallSignClear[self.CallSign]) self:__CheckRadioQueue(-10) - local text = string.format("%s. All stations, SUNRISE SUNRISE SUNRISE, %s.",self.callsigntxt,self.callsigntxt) + local sunrise = self.gettext:GetEntry("SUNRISE",self.locale) + local text = string.format(sunrise,self.callsigntxt,self.callsigntxt) self:_NewRadioEntry(text,text,0,false,false,false,false,true) self:T(self.lid..text) self.sunrisedone = true @@ -5476,19 +5737,16 @@ function AWACS:onafterStart(From, Event, To) -- Event functions function MarkerOps:OnAfterMarkAdded(From,Event,To,Text,Keywords,Coord) --local m = MESSAGE:New(string.format("AWACS %s Mark Added.", self.Tag),10,"Info",true):ToAllIf(self.debug) - BASE:I(string.format("%s Mark Added.", self.Tag)) Handler(Keywords,Coord,Text) end function MarkerOps:OnAfterMarkChanged(From,Event,To,Text,Keywords,Coord) - BASE:I(string.format("%s Mark Changed.", self.Tag)) - --local m = MESSAGE:New(string.format("AWACS %s Mark Changed.", self.Tag),10,"Info",true):ToAllIf(self.debug) + --BASE:I(string.format("%s Mark Changed.", self.Tag)) Handler(Keywords,Coord,Text) end function MarkerOps:OnAfterMarkDeleted(From,Event,To) - BASE:I(string.format("%s Mark Deleted.", self.Tag)) - --local m = MESSAGE:New(string.format("AWACS %s Mark Deleted.", self.Tag),10,"Info",true):ToAllIf(self.debug) + --BASE:I(string.format("%s Mark Deleted.", self.Tag)) end self.MarkerOps = MarkerOps @@ -5522,8 +5780,9 @@ function AWACS:_CheckAwacsStatus() -- arrived self.AwacsInZone = true self:T(self.lid.."Arrived in Orbit Zone: " .. orbitzone:GetName()) - local text = string.format("%s on station for %s control.",self.callsigntxt,self.AOName or "Rock") - local textScreen = string.format("%s on station for %s control.",self.callsigntxt,self.AOName or "Rock") + local onstationtxt = self.gettext:GetEntry("AWONSTATION",self.locale) + local text = string.format(onstationtxt,self.callsigntxt,self.AOName or "Rock") + local textScreen = text self:_NewRadioEntry(text,textScreen,0,false,true,true,false,true) end end @@ -5544,8 +5803,9 @@ function AWACS:_CheckAwacsStatus() if self.intelstarted and not self.sunrisedone then -- TODO Sunrise call on after airborne at ca 10k feet local alt = UTILS.Round(UTILS.MetersToFeet(awacs:GetAltitude())/1000,0) - if alt >= 10 then - local text = string.format("%s. All stations, SUNRISE SUNRISE SUNRISE, %s.",self.callsigntxt,self.callsigntxt) + if alt >= 10 then + local sunrise = self.gettext:GetEntry("SUNRISE",self.locale) + local text = string.format(sunrise,self.callsigntxt,self.callsigntxt) self:_NewRadioEntry(text,text,0,false,false,false,false,true) --self.AwacsFG:RadioTransmission(text,1,false) self:T(self.lid..text) @@ -5599,13 +5859,10 @@ function AWACS:_CheckAwacsStatus() local ESTOSLeft = UTILS.Round((((self.AwacsTimeOnStation+self.ShiftChangeTime)*3600) - ESmissiontime),0) -- seconds ESTOSLeft = UTILS.Round(ESTOSLeft/60,0) -- minutes local ChangeTime = UTILS.Round(((self.ShiftChangeTime * 3600)/60),0) - --local Changedue = "No" - --report:Add("====================") report:Add("AWACS REPLACEMENT:") report:Add(string.format("Auftrag Status: %s",esstatus)) report:Add(string.format("TOS Left: %d min",ESTOSLeft)) - --report:Add(string.format("Needs ShiftChange: %s",Changedue)) local OpsGroups = AWmission:GetOpsGroups() local OpsGroup = self:_GetAliveOpsGroupFromTable(OpsGroups) -- Ops.OpsGroup#OPSGROUP @@ -5931,16 +6188,25 @@ function AWACS:onafterAssignedAnchor(From, Event, To, GID, Anchor, AnchorStackNo local Angels = AnchorAngels or 25 local AnchorSpeed = self.CapSpeedBase or 270 local AuftragsNr = managedgroup.CurrentAuftrag - - local textTTS = string.format("%s. %s. Station at %s at angels %d doing %d knots.",CallSign,self.callsigntxt,AnchorName,Angels,AnchorSpeed) + + local textTTS = "" + if self.PikesSpecialSwitch then + local stationtxt = self.gettext:GetEntry("STATIONAT",self.locale) + textTTS = string.format(stationtxt,CallSign,self.callsigntxt,AnchorName,Angels) + else + local stationtxt = self.gettext:GetEntry("STATIONATLONG",self.locale) + textTTS = string.format(stationtxt,CallSign,self.callsigntxt,AnchorName,Angels,AnchorSpeed) + end local ROEROT = self.AwacsROE..", "..self.AwacsROT - local textScreen = string.format("%s. %s.\nStation at %s\nAngels %d\nSpeed %d knots\nCoord %s\nROE %s.",CallSign,self.callsigntxt,AnchorName,Angels,AnchorSpeed,AnchorCoordTxt,ROEROT) - local TextTasking = string.format("Station at %s\nAngels %d\nSpeed %d knots\nCoord %s\nROE %s",AnchorName,Angels,AnchorSpeed,AnchorCoordTxt,ROEROT) + local stationtxtsc = self.gettext:GetEntry("STATIONSCREEN",self.locale) + local stationtxtta = self.gettext:GetEntry("STATIONTASK",self.locale) + local textScreen = string.format(stationtxtsc,CallSign,self.callsigntxt,AnchorName,Angels,AnchorSpeed,AnchorCoordTxt,ROEROT) + local TextTasking = string.format(stationtxtta,AnchorName,Angels,AnchorSpeed,AnchorCoordTxt,ROEROT) self:_NewRadioEntry(textTTS,textScreen,GID,isPlayer,isPlayer,true,false) managedgroup.CurrentTask = self:_CreateTaskForGroup(GID,AWACS.TaskDescription.ANCHOR,TextTasking,Anchor.StationZone) - + -- if it's a Alert5, we want to push CAP instead if isAI then local auftrag = managedgroup.FlightGroup:GetMissionCurrent() -- Ops.Auftrag#AUFTRAG @@ -5952,10 +6218,8 @@ function AWACS:onafterAssignedAnchor(From, Event, To, GID, Anchor, AnchorStackNo capauftrag:SetTime(nil,((self.CAPTimeOnStation*3600)+(15*60))) capauftrag:AddAsset(managedgroup.FlightGroup) self.CatchAllMissions[#self.CatchAllMissions+1] = capauftrag - --local AirWing = managedgroup.FlightGroup:GetAirWing() managedgroup.FlightGroup:AddMission(capauftrag) auftrag:Cancel() - --AirWing:AddMission(capauftrag) else self:E("**** AssignedAnchor but Auftrag NOT ALERT5!") end @@ -6285,11 +6549,7 @@ end -- @return #AWACS self function AWACS:onafterReAnchor(From, Event, To, GID) self:T({From, Event, To, GID}) - -- get managedgroup - -- check AI FG state - -- check weapon state - -- check fuel state - -- vector back to anchor or RTB + -- get managedgroup, heck AI FG state, heck weapon state, check fuel state, vector back to anchor or RTB local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup if managedgroup then if managedgroup.IsAI then @@ -6306,8 +6566,7 @@ function AWACS:onafterReAnchor(From, Event, To, GID) self:_CheckOut(AIFG:GetGroup(),GID) self.AIRequested = self.AIRequested - 1 else - -- re-establish anchor task - -- get anchor zone data + -- re-establish anchor task, get anchor zone data local Anchor = self.AnchorStacks:ReadByPointer(managedgroup.AnchorStackNo) -- #AWACS.AnchorData local StationZone = Anchor.StationZone -- Core.Zone#ZONE managedgroup.CurrentTask = self:_CreateTaskForGroup(GID,AWACS.TaskDescription.ANCHOR,"Re-Station AI",StationZone) @@ -6319,8 +6578,9 @@ function AWACS:onafterReAnchor(From, Event, To, GID) managedgroup.CurrentAuftrag = 0 end managedgroup.ContactCID = 0 - self.ManagedGrps[GID] = managedgroup - self:_MessageVector(GID," to Station",Anchor.StationZoneCoordinate,managedgroup.AnchorStackAngels) + self.ManagedGrps[GID] = managedgroup + local tostation = self.gettext:GetEntry("VECTORSTATION",self.locale) + self:_MessageVector(GID,tostation,Anchor.StationZoneCoordinate,managedgroup.AnchorStackAngels) end else -- lost group, remove from known groups, declare vanished @@ -6331,28 +6591,26 @@ function AWACS:onafterReAnchor(From, Event, To, GID) local savedcallsign = managedgroup.CallSign --vanished/friendly flight faded/lost contact with C/S/CSAR Scramble -- Magic, RIGHTGUARD, RIGHTGUARD, Dodge 41, Bullseye X/Y - local textoptions = { - [1] = "Lost friendly flight", - [2] = "Vanished friendly flight", - [3] = "Faded friendly contact", - [4] = "Lost contact with", - } - + local textoptions = {} + textoptions[1] = self.gettext:GetEntry("TEXTOPTIONS1",self.locale) + textoptions[2] = self.gettext:GetEntry("TEXTOPTIONS2",self.locale) + textoptions[3] = self.gettext:GetEntry("TEXTOPTIONS3",self.locale) + textoptions[4] = self.gettext:GetEntry("TEXTOPTIONS4",self.locale) + local allstations = self.gettext:GetEntry("ALLSTATIONS",self.locale) + local milestxt = self.gettext:GetEntry("MILES",self.locale) -- DONE - need to save last known coordinate if managedgroup.LastKnownPosition then local lastknown = UTILS.DeepCopy(managedgroup.LastKnownPosition) local faded = textoptions[math.random(1,4)] - local text = string.format("All stations. %s. %s %s.",self.callsigntxt, faded, savedcallsign) - local textScreen = string.format("All stations, %s. %s %s.", self.callsigntxt, faded, savedcallsign) + local text = string.format("%s. %s. %s %s.",allstations,self.callsigntxt, faded, savedcallsign) + local textScreen = string.format("%s, %s. %s %s.",allstations, self.callsigntxt, faded, savedcallsign) local brtext = self:_ToStringBULLS(lastknown) local brtexttts = self:_ToStringBULLS(lastknown,false,true) - --if self.PathToGoogleKey then - --brtexttts = self:_ToStringBULLS(lastknown,true) - --end - text = text .. " "..brtexttts.." miles." - textScreen = textScreen .. " "..brtext.." miles." + + text = text .. " "..brtexttts.." "..milestxt.."." + textScreen = textScreen .. " "..brtext.." "..milestxt.."." self:_NewRadioEntry(text,textScreen,0,false,self.debug,true,false,true) end @@ -6371,12 +6629,14 @@ function AWACS:onafterReAnchor(From, Event, To, GID) local AnchorSpeed = self.CapSpeedBase or 270 local StationZone = Anchor.StationZone -- Core.Zone#ZONE local ROEROT = self.AwacsROE.." "..self.AwacsROT - local TextTasking = string.format("Station at %s\nAngels %d\nSpeed %d knots\nCoord %s\nROE %s",AnchorName,Angels,AnchorSpeed,AnchorCoordTxt,ROEROT) + local stationtxt = self.gettext:GetEntry("STATIONTASK",self.locale) + local TextTasking = string.format(stationtxt,AnchorName,Angels,AnchorSpeed,AnchorCoordTxt,ROEROT) managedgroup.CurrentTask = self:_CreateTaskForGroup(GID,AWACS.TaskDescription.ANCHOR,TextTasking,StationZone) managedgroup.HasAssignedTask = true managedgroup.ContactCID = 0 - self.ManagedGrps[GID] = managedgroup - self:_MessageVector(GID," to Station",Anchor.StationZoneCoordinate,managedgroup.AnchorStackAngels) + self.ManagedGrps[GID] = managedgroup + local vectortxt = self.gettext:GetEntry("VECTORSTATION",self.locale) + self:_MessageVector(GID,vectortxt,Anchor.StationZoneCoordinate,managedgroup.AnchorStackAngels) else -- lost group, remove from known groups, declare vanished -- ALL remove from managedgrps @@ -6384,17 +6644,18 @@ function AWACS:onafterReAnchor(From, Event, To, GID) local savedcallsign = managedgroup.CallSign --vanished/friendly flight faded/lost contact with C/S/CSAR Scramble -- Magic, RIGHTGUARD, RIGHTGUARD, Dodge 41, Bullseye X/Y - local textoptions = { - [1] = "Lost friendly flight", - [2] = "Vanished friendly flight", - [3] = "Faded friendly contact", - [4] = "Lost contact with", - } + local textoptions = {} + textoptions[1] = self.gettext:GetEntry("TEXTOPTIONS1",self.locale) + textoptions[2] = self.gettext:GetEntry("TEXTOPTIONS2",self.locale) + textoptions[3] = self.gettext:GetEntry("TEXTOPTIONS3",self.locale) + textoptions[4] = self.gettext:GetEntry("TEXTOPTIONS4",self.locale) + local allstations = self.gettext:GetEntry("ALLSTATIONS",self.locale) + local milestxt = self.gettext:GetEntry("MILES",self.locale) -- DONE - need to save last known coordinate local faded = textoptions[math.random(1,4)] - local text = string.format("All stations. %s. %s %s.",self.callsigntxt, faded, savedcallsign) - local textScreen = string.format("All stations, %s. %s %s.", self.callsigntxt, faded, savedcallsign) + local text = string.format("%s. %s. %s %s.",allstations, self.callsigntxt, faded, savedcallsign) + local textScreen = string.format("%s, %s. %s %s.", allstations,self.callsigntxt, faded, savedcallsign) if managedgroup.LastKnownPosition then local lastknown = UTILS.DeepCopy(managedgroup.LastKnownPosition) local brtext = self:_ToStringBULLS(lastknown) @@ -6402,8 +6663,8 @@ function AWACS:onafterReAnchor(From, Event, To, GID) --if self.PathToGoogleKey then --brtexttts = self:_ToStringBULLS(lastknown,true) --end - text = text .. " "..brtexttts.." miles." - textScreen = textScreen .. " "..brtext.." miles." + text = text .. " "..brtexttts.." "..milestxt.."." + textScreen = textScreen .. " "..brtext.." "..milestxt.."." self:_NewRadioEntry(text,textScreen,0,false,self.debug,true,false,true) end diff --git a/Moose Development/Moose/Ops/Operation.lua b/Moose Development/Moose/Ops/Operation.lua index 158af4fd4..a304395d7 100644 --- a/Moose Development/Moose/Ops/Operation.lua +++ b/Moose Development/Moose/Ops/Operation.lua @@ -454,7 +454,7 @@ function OPERATION:GetPhaseStatus(Phase) return Phase.status end ---- Set codition when the given phase is over. +--- Set condition when the given phase is over. -- @param #OPERATION self -- @param #OPERATION.Phase Phase The phase. -- @param Core.Condition#CONDITION Condition Condition when the phase is over. @@ -624,7 +624,7 @@ end --- Count phases. -- @param #OPERATION self -- @param #string Status (Optional) Only count phases in a certain status, e.g. `OPERATION.PhaseStatus.PLANNED`. --- @param #OPERATION.Branch (Optional) Branch. +-- @param #OPERATION.Branch Branch (Optional) Branch. -- @return #number Number of phases function OPERATION:CountPhases(Status, Branch) @@ -644,6 +644,7 @@ end --- Add a new branch to the operation. -- @param #OPERATION self +-- @param #string Name -- @return #OPERATION.Branch Branch table object. function OPERATION:AddBranch(Name) @@ -666,6 +667,7 @@ end --- Get name of the branch. -- @param #OPERATION self -- @param #OPERATION.Branch Branch The branch of which the name is requested. Default is the currently active or master branch. +-- @return #string Name Name or "None" function OPERATION:GetBranchName(Branch) Branch=Branch or self:GetBranchActive() if Branch then diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 39ac33044..866b8c054 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -3,13 +3,15 @@ -- ## Main Features: -- -- * Simplifies defining and executing Player tasks --- * FSM events when a mission is done, successful or failed +-- * FSM events when a mission is added, done, successful or failed, replanned +-- * Ready to use SRS and localization +-- * Mission locations can be smoked, flared and marked on the map -- -- === -- -- ## Example Missions: -- --- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/). +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20PlayerTask). -- -- === -- @@ -17,9 +19,16 @@ -- -- === -- @module Ops.PlayerTask --- @image OPS_PlayerTask.png +-- @image OPS_PlayerTask.jpg +-- @date Last Update August 2022 +do +------------------------------------------------------------------------------------------------------------------- +-- PLAYERTASK +-- TODO: PLAYERTASK +------------------------------------------------------------------------------------------------------------------- + --- PLAYERTASK class. -- @type PLAYERTASK -- @field #string ClassName Name of the class. @@ -38,9 +47,10 @@ -- @field #table conditionSuccess = {}, -- @field #table conditionFailure = {}, -- @field Ops.PlayerTask#PLAYERTASKCONTROLLER TaskController --- +-- @field #number timestamp -- @extends Core.Fsm#FSM + --- Global PlayerTaskNr counter _PlayerTaskNr = 0 @@ -48,10 +58,11 @@ _PlayerTaskNr = 0 -- @field #PLAYERTASK PLAYERTASK = { ClassName = "PLAYERTASK", - verbose = true, + verbose = false, lid = nil, PlayerTaskNr = nil, Type = nil, + TTSType = nil, Target = nil, Clients = nil, Repeat = false, @@ -63,11 +74,12 @@ PLAYERTASK = { conditionSuccess = {}, conditionFailure = {}, TaskController = nil, + timestamp = 0, } --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.0.7" +PLAYERTASK.version="0.0.9" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -81,7 +93,7 @@ PLAYERTASK.version="0.0.7" -- @param #boolean Repeat Repeat this task if true (default = false) -- @param #number Times Repeat on failure this many times if Repeat is true (default = 1) -- @return #PLAYERTASK self -function PLAYERTASK:New(Type, Target, Repeat, Times) +function PLAYERTASK:New(Type, Target, Repeat, Times, TTSType) -- Inherit everything from FSM class. local self=BASE:Inherit(self, FSM:New()) -- #PLAYERTASK @@ -97,6 +109,8 @@ function PLAYERTASK:New(Type, Target, Repeat, Times) self.conditionSuccess = {} self.conditionFailure = {} self.TaskController = nil -- Ops.PlayerTask#PLAYERTASKCONTROLLER + self.timestamp = timer.getTime() + self.TTSType = TTSType or "close air support" if Repeat then self.Repeat = true @@ -118,7 +132,7 @@ function PLAYERTASK:New(Type, Target, Repeat, Times) return self end - self:I(self.lid.."Created.") + self:T(self.lid.."Created.") -- FMS start state is PLANNED. self:SetStartState("Planned") @@ -139,6 +153,89 @@ function PLAYERTASK:New(Type, Target, Repeat, Times) self:__Status(-5) return self + + --- + -- Pseudo Functions + --- + + --- On After "Planned" event. Task has been planned. + -- @function [parent=#PLAYERTASK] OnAfterPlanned + -- @param #PLAYERTASK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- On After "Requested" event. Task has been Requested. + -- @function [parent=#PLAYERTASK] OnAfterRequested + -- @param #PLAYERTASK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- On After "ClientAdded" event. Client has been added to the task. + -- @function [parent=#PLAYERTASK] OnAfterClientAdded + -- @param #PLAYERTASK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Client#CLIENT Client + + --- On After "ClientRemoved" event. Client has been removed from the task. + -- @function [parent=#PLAYERTASK] OnAfterClientRemoved + -- @param #PLAYERTASK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- On After "Executing" event. Task is executed by the 1st client. + -- @function [parent=#PLAYERTASK] OnAfterExecuting + -- @param #PLAYERTASK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- On After "Done" event. Task is done. + -- @function [parent=#PLAYERTASK] OnAfterDone + -- @param #PLAYERTASK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- On After "Cancel" event. Task has been cancelled. + -- @function [parent=#PLAYERTASK] OnAfterCancel + -- @param #PLAYERTASK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- On After "Planned" event. Task has been planned. + -- @function [parent=#PLAYERTASK] OnAfterPilotPlanned + -- @param #PLAYERTASK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- On After "Success" event. Task has been a success. + -- @function [parent=#PLAYERTASK] OnAfterSuccess + -- @param #PLAYERTASK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- On After "ClientAborted" event. A client has aborted the task. + -- @function [parent=#PLAYERTASK] OnAfterClientAborted + -- @param #PLAYERTASK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- On After "Failed" event. Task has been a failure. + -- @function [parent=#PLAYERTASK] OnAfterFailed + -- @param #PLAYERTASK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + end --- [Internal] Add a PLAYERTASKCONTROLLER for this task @@ -146,7 +243,7 @@ end -- @param Ops.PlayerTask#PLAYERTASKCONTROLLER Controller -- @return #PLAYERTASK self function PLAYERTASK:_SetController(Controller) - self:I(self.lid.."_SetController") + self:T(self.lid.."_SetController") self.TaskController = Controller return self end @@ -155,7 +252,7 @@ end -- @param #PLAYERTASK self -- @return #boolean done function PLAYERTASK:IsDone() - self:I(self.lid.."IsDone?") + self:T(self.lid.."IsDone?") local IsDone = false local state = self:GetState() if state == "Done" or state == "Stopped" then @@ -168,17 +265,25 @@ end -- @param #PLAYERTASK self -- @return #table clients function PLAYERTASK:GetClients() - self:I(self.lid.."GetClients?") + self:T(self.lid.."GetClients") local clientlist = self.Clients:GetIDStackSorted() or {} return clientlist end +--- [User] Count clients +-- @param #PLAYERTASK self +-- @return #number clientcount +function PLAYERTASK:CountClients() + self:T(self.lid.."CountClients") + return self.Clients:Count() +end + --- [User] Check if a player name is assigned to this task -- @param #PLAYERTASK self -- @param #string Name -- @return #boolean HasName function PLAYERTASK:HasPlayerName(Name) - self:I(self.lid.."HasPlayerName?") + self:T(self.lid.."HasPlayerName?") return self.Clients:HasUniqueID(Name) end @@ -187,7 +292,7 @@ end -- @param Wrapper.Client#CLIENT Client -- @return #PLAYERTASK self function PLAYERTASK:AddClient(Client) - self:I(self.lid.."AddClient") + self:T(self.lid.."AddClient") local name = Client:GetPlayerName() if not self.Clients:HasUniqueID(name) then self.Clients:Push(Client,name) @@ -201,7 +306,7 @@ end -- @param Wrapper.Client#CLIENT Client -- @return #PLAYERTASK self function PLAYERTASK:RemoveClient(Client) - self:I(self.lid.."RemoveClient") + self:T(self.lid.."RemoveClient") local name = Client:GetPlayerName() if self.Clients:HasUniqueID(name) then self.Clients:PullByID(name) @@ -221,7 +326,7 @@ end -- @param Wrapper.Client#CLIENT Client (optional) -- @return #PLAYERTASK self function PLAYERTASK:ClientAbort(Client) - self:I(self.lid.."ClientAbort") + self:T(self.lid.."ClientAbort") if Client and Client:IsAlive() then self:RemoveClient(Client) self:__ClientAborted(-1,Client) @@ -238,9 +343,10 @@ end --- [User] Create target mark on F10 map -- @param #PLAYERTASK self +-- @param #string Text (optional) Text to show on the marker -- @return #PLAYERTASK self -function PLAYERTASK:MarkTargetOnF10Map() - self:I(self.lid.."MarkTargetOnF10Map") +function PLAYERTASK:MarkTargetOnF10Map(Text) + self:T(self.lid.."MarkTargetOnF10Map") if self.Target then local coordinate = self.Target:GetCoordinate() if coordinate then @@ -248,6 +354,7 @@ function PLAYERTASK:MarkTargetOnF10Map() -- Marker exists, delete one first self.TargetMarker:Remove() end + local text = Text or "Target of "..self.lid self.TargetMarker = MARKER:New(coordinate,"Target of "..self.lid) self.TargetMarker:ReadOnly() self.TargetMarker:ToAll() @@ -261,7 +368,7 @@ end -- @param #number Color, defaults to SMOKECOLOR.Red -- @return #PLAYERTASK self function PLAYERTASK:SmokeTarget(Color) - self:I(self.lid.."SmokeTarget") + self:T(self.lid.."SmokeTarget") local color = Color or SMOKECOLOR.Red if self.Target then local coordinate = self.Target:GetCoordinate() @@ -277,7 +384,7 @@ end -- @param #number Color, defaults to FLARECOLOR.Red -- @return #PLAYERTASK self function PLAYERTASK:FlareTarget(Color) - self:I(self.lid.."SmokeTarget") + self:T(self.lid.."SmokeTarget") local color = Color or FLARECOLOR.Red if self.Target then local coordinate = self.Target:GetCoordinate() @@ -361,8 +468,8 @@ end -- @param #string To -- @return #PLAYERTASK self function PLAYERTASK:onafterStatus(From, Event, To) - self:I({From, Event, To}) - self:I(self.lid.."onafterStatus") + self:T({From, Event, To}) + self:T(self.lid.."onafterStatus") local status = self:GetState() @@ -429,7 +536,7 @@ end -- @param #string To -- @return #PLAYERTASK self function PLAYERTASK:onafterPlanned(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) return self end @@ -440,7 +547,7 @@ end -- @param #string To -- @return #PLAYERTASK self function PLAYERTASK:onafterRequested(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) return self end @@ -451,7 +558,7 @@ end -- @param #string To -- @return #PLAYERTASK self function PLAYERTASK:onafterExecuting(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) return self end @@ -462,7 +569,7 @@ end -- @param #string To -- @return #PLAYERTASK self function PLAYERTASK:onafterStop(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) return self end @@ -474,9 +581,9 @@ end -- @param Wrapper.Client#CLIENT Client -- @return #PLAYERTASK self function PLAYERTASK:onafterClientAdded(From, Event, To, Client) - self:I({From, Event, To}) - if Client then - local text = string.format("Player %s joined task %d!",Client:GetPlayerName() or "Generic",self.PlayerTaskNr) + self:T({From, Event, To}) + if Client and self.verbose then + local text = string.format("Player %s joined task %03d!",Client:GetPlayerName() or "Generic",self.PlayerTaskNr) self:I(self.lid..text) end return self @@ -489,7 +596,7 @@ end -- @param #string To -- @return #PLAYERTASK self function PLAYERTASK:onafterDone(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) if self.TaskController then self.TaskController:__TaskDone(-1,self) end @@ -504,7 +611,7 @@ end -- @param #string To -- @return #PLAYERTASK self function PLAYERTASK:onafterCancel(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) if self.TaskController then self.TaskController:__TaskCancelled(-1,self) end @@ -519,7 +626,7 @@ end -- @param #string To -- @return #PLAYERTASK self function PLAYERTASK:onafterSuccess(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) if self.TaskController then self.TaskController:__TaskSuccess(-1,self) end @@ -537,7 +644,7 @@ end -- @param #string To -- @return #PLAYERTASK self function PLAYERTASK:onafterFailed(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) self.repeats = self.repeats + 1 -- repeat on failed? if self.Repeat and (self.repeats <= self.RepeatNo) then @@ -557,7 +664,12 @@ function PLAYERTASK:onafterFailed(From, Event, To) end return self end +------------------------------------------------------------------------------------------------------------------- +-- END PLAYERTASK +------------------------------------------------------------------------------------------------------------------- +end +do ------------------------------------------------------------------------------------------------------------------- -- PLAYERTASKCONTROLLER -- TODO: PLAYERTASKCONTROLLER @@ -571,16 +683,277 @@ end -- @field Utilities.FiFo#FIFO TargetQueue -- @field Utilities.FiFo#FIFO TaskQueue -- @field Utilities.FiFo#FIFO TasksPerPlayer +-- @field Utilities.FiFo#FIFO PrecisionTasks -- @field Core.Set#SET_CLIENT ClientSet -- @field #string ClientFilter -- @field #string Name -- @field #string Type -- @field #boolean UseGroupNames -- @field #table PlayerMenu --- - +-- @field #boolean usecluster +-- @field #number ClusterRadius +-- @field #string MenuName +-- @field #boolean NoScreenOutput +-- @field #number TargetRadius +-- @field #boolean UseWhiteList +-- @field #table WhiteList +-- @field #boolean UseBlackList +-- @field #table BlackList +-- @field Core.TextAndSound#TEXTANDSOUND gettext +-- @field #string locale +-- @field #boolean precisionbombing +-- @field Ops.FlightGroup#FLIGHTGROUP LasingDrone +-- @extends Core.Fsm#FSM --- +-- +-- *It is our attitude at the beginning of a difficult task which, more than anything else, which will affect its successful outcome.* (William James) +-- +-- === +-- +-- # PLAYERTASKCONTROLLER +-- +-- * Simplifies defining, executing and controlling of Player tasks +-- * FSM events when a mission is added, done, successful or failed, replanned +-- * Ready to use SRS and localization +-- * Mission locations can be smoked, flared and marked on the map +-- +-- ## 1 Overview +-- +-- PLAYERTASKCONTROLLER is used to auto-create (optional) and control tasks for players. It can be set up as Air-to-Ground (A2G, main focus), Air-to-Ship (A2S) or Air-to-Air (A2A) controller. +-- For the latter task type, also have a look at the @{Ops.Awacs#AWACS} class which allows for more complex scenarios. +-- One task at a time can be joined by the player from the F10 menu. A task can be joined by multiple players. Once joined, task information is available via the F10 menu, the task location +-- can be marked on the map and for A2G/S targets, the target can be marked with smoke and flares. +-- +-- For the mission designer, tasks can be auto-created by means of detection with the integrated @{Ops.Intel#INTEL} class setup, or be manually added to the task queue. +-- +-- ## 2 Task Types +-- +-- Targets can be of types GROUP, SET\_GROUP, UNIT, SET\_UNIT, STATIC, SET\_STATIC, AIRBASE, ZONE or COORDINATE. The system will auto-create tasks for players from these targets. +-- Tasks are created as @{Ops.PlayerTask#PLAYERTASK} objects, which leverage @{Ops.Target#TARGET} for the management of the actual target. The system creates these task types +-- from the target objects: +-- +-- * A2A - AUFTRAG.Type.INTERCEPT +-- * A2S - AUFTRAG.Type.ANTISHIP +-- * A2G - AUFTRAG.Type.CAS, AUFTRAG.Type.BAI, AUFTRAG.Type.SEAD, AUFTRAG.Type.BOMBING, AUFTRAG.Type.PRECISIONBOMBING, AUFTRAG.Type.BOMBRUNWAY +-- * A2GS - A2S and A2G combined +-- +-- Task types are derived from @{Ops.Auftrag#AUFTRAG}: +-- +-- * CAS - Close air support, created to attack ground units, where friendly ground units are around the location in a bespoke radius (default: 500m/1km diameter) +-- * BAI - Battlefield air interdiction, same as above, but no friendlies around +-- * SEAD - Same as CAS, but the enemy ground units field AAA, SAM or EWR units +-- * Bombing - Against static targets +-- * Precision Bombing - (if enabled) Laser-guided bombing, against static targets and high-value ground targets (MBTs etc) +-- * Bomb Runway - Against Airbase runways (in effect, drop bombs over the runway) +-- * ZONE and COORDINATE - Targets will be scanned for GROUND or STATIC enemy units and tasks created from these +-- * Intercept - Any airborne targets, if the controller is of type "A2A" +-- * Anti-Ship - Any ship targets, if the controller is of type "A2S" +-- +-- ## 3 Task repetition +-- +-- On failure, tasks will be replanned by default for a maximum of 5 times. +-- +-- ## 4 SETTINGS, SRS and language options (localization) +-- +-- The system can optionally communicate to players via SRS. Also localization is available, both "en" and "de" has been build in already. +-- Player and global @{Core.Settings#SETTINGS} for coordinates will be observed. +-- +-- ## 5 Setup +-- +-- A basic setup is very simple: +-- +-- -- Settings - we want players to have a settings menu, be on imperial measures, and get directions as BR +-- _SETTINGS:SetPlayerMenuOn() +-- _SETTINGS:SetImperial() +-- _SETTINGS:SetA2G_BR() +-- +-- -- Set up the A2G task controller for the blue side named "82nd Airborne" +-- local taskmanager = PLAYERTASKCONTROLLER:New("82nd Airborne",coalition.side.BLUE,PLAYERTASKCONTROLLER.Type.A2G) +-- +-- -- set locale to English +-- taskmanager:SetLocale("en") +-- +-- -- Set up detection with grup names *containing* "Blue Recce", these will add targets to our controller via detection. Can be e.g. a drone. +-- taskmanager:SetupIntel("Blue Recce") +-- +-- -- Add a single Recce group name "Blue Humvee" +-- taskmanager:AddAgent(GROUP:FindByName("Blue Humvee")) +-- +-- -- Set the callsign for SRS and Menu name to be "Groundhog" +-- taskmanager:SetMenuName("Groundhog") +-- +-- -- Add accept- and reject-zones for detection +-- -- Accept zones are handy to limit e.g. the engagement to a certain zone. The example is a round, mission editor created zone named "AcceptZone" +-- taskmanager:AddAcceptZone(ZONE:New("AcceptZone")) +-- +-- -- Reject zones are handy to create borders. The example is a ZONE_POLYGON, created in the mission editor, late activated with waypoints, +-- -- named "AcceptZone#ZONE_POLYGON" +-- taskmanager:AddRejectZone(ZONE:FindByName("RejectZone")) +-- +-- -- Set up using SRS for messaging +-- local hereSRSPath = "C:\\Program Files\\DCS-SimpleRadio-Standalone" +-- local hereSRSPort = 5002 +-- -- local hereSRSGoogle = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourkey.json" +-- taskmanager:SetSRS({130,255},{radio.modulation.AM,radio.modulation.AM},hereSRSPath,"female","en-GB",hereSRSPort,"Microsoft Hazel Desktop",0.7,hereSRSGoogle) +-- +-- -- Controller will announce itself under these broadcast frequencies, handy to use cold-start frequencies here of your aircraft +-- taskmanager:SetSRSBroadcast({127.5,305},{radio.modulation.AM,radio.modulation.AM}) +-- +-- -- Example: Manually add an AIRBASE as a target +-- taskmanager:AddTarget(AIRBASE:FindByName(AIRBASE.Caucasus.Senaki_Kolkhi)) +-- +-- -- Example: Manually add a COORDINATE as a target +-- taskmanager:AddTarget(GROUP:FindByName("Scout Coordinate"):GetCoordinate()) +-- +-- -- Set a whitelist for tasks, e.g. skip SEAD tasks +-- taskmanager:SetTaskWhiteList({AUFTRAG.Type.CAS, AUFTRAG.Type.BAI, AUFTRAG.Type.BOMBING, AUFTRAG.Type.BOMBRUNWAY}) +-- +-- -- Set target radius +-- taskmanager:SetTargetRadius(1000) +-- +-- ## 6 Localization +-- +-- Localization for English and German texts are build-in. Default setting is English. Change with @{#PLAYERTASKCONTROLLER.SetLocale}() +-- +-- ### 6.1 Adding Localization +-- +-- A list of fields to be defined follows below. **Note** that in some cases `string.format()` is used to format texts for screen and SRS. +-- Hence, the `%d`, `%s` and `%f` special characters need to appear in the exact same amount and order of appearance in the localized text or it will create errors. +-- To add a localization, the following texts need to be translated and set in your mission script **before** @{#PLAYERTASKCONTROLLER.New}(): +-- +-- PLAYERTASKCONTROLLER.Messages = { +-- EN = { +-- TASKABORT = "Task aborted!", +-- NOACTIVETASK = "No active task!", +-- FREQUENCIES = "frequencies ", +-- FREQUENCY = "frequency %.3f", +-- BROADCAST = "%s, %s, switch to %s for task assignment!", +-- CASTTS = "close air support", +-- SEADTTS = "suppress air defense", +-- BOMBTTS = "bombing", +-- PRECBOMBTTS = "precision bombing", +-- BAITTS = "battle field air interdiction", +-- ANTISHIPTTS = "anti-ship", +-- INTERCEPTTS = "intercept", +-- BOMBRUNWAYTTS = "bomb runway", +-- HAVEACTIVETASK = "You already have one active task! Complete it first!", +-- PILOTJOINEDTASK = "%s, %s joined task %03d", +-- TASKNAME = "%s Task ID %03d", +-- TASKNAMETTS = "%s Task ID %03d", +-- THREATHIGH = "high", +-- THREATMEDIUM = "medium", +-- THREATLOW = "low", +-- THREATTEXT = "%s\nThreat: %s\nTargets left: %d\nCoord: %s", +-- THREATTEXTTTS = "%s, %s. Target information for %s. Threat level %s. Targets left %d. Target location %s.", +-- MARKTASK = "%s, %s, copy, task %03d location marked on map!", +-- SMOKETASK = "%s, %s, copy, task %03d location smoked!", +-- FLARETASK = "%s, %s, copy, task %03d location illuminated!", +-- ABORTTASK = "%s, all stations, %s aborted task %03d!", +-- UNKNOWN = "Unknown", +-- MENUTASKING = " Tasking ", +-- MENUACTIVE = "Active Task", +-- MENUINFO = "Info", +-- MENUMARK = "Mark on map", +-- MENUSMOKE = "Smoke", +-- MENUFLARE = "Flare", +-- MENUABORT = "Abort", +-- MENUJOIN = "Join Task", +-- MENUTASKNO = "TaskNo", +-- MENUNOTASKS = "Currently no tasks available.", +-- TASKCANCELLED = "Task #%03d %s is cancelled!", +-- TASKCANCELLEDTTS = "%s, task %03d %s is cancelled!", +-- TASKSUCCESS = "Task #%03d %s completed successfully!", +-- TASKSUCCESSTTS = "%s, task %03d %s completed successfully!", +-- TASKFAILED = "Task #%03d %s was a failure!", +-- TASKFAILEDTTS = "%s, task %03d %s was a failure!", +-- TASKFAILEDREPLAN = "Task #%03d %s was a failure! Replanning!", +-- TASKFAILEDREPLANTTS = "%s, task %03d %s was a failure! Replanning!", +-- TASKADDED = "%s has a new task %s available!", +-- PILOTS = "\nPilot(s): ", +-- PILOTSTTS = ". Pilot(s): ", +-- YES = "Yes", +-- NO = "No", +-- POINTEROVERTARGET = "%s, %s, pointer over target for task %03d, lasing!", +-- POINTERTARGETREPORT = "\nPointer over target: %s\nLasing: %s", +-- POINTERTARGETLASINGTTS = ". Pointer over target and lasing.", +-- }, +-- +-- e.g. +-- +-- taskmanager.Messages = { +-- FR = { +-- TASKABORT = "Tâche abandonnée!", +-- NOACTIVETASK = "Aucune tâche active!", +-- FREQUENCIES = "fréquences ", +-- FREQUENCY = "fréquence %.3f", +-- BROADCAST = "%s, %s, passer au %s pour l'attribution des tâches!", +-- ... +-- TASKADDED = "%s a créé une nouvelle tâche %s", +-- PILOTS = "\nPilote(s): ", +-- PILOTSTTS = ". Pilote(s): ", +-- }, +-- +-- and then `taskmanager:SetLocale("fr")` **after** @{#PLAYERTASKCONTROLLER.New}() in your script. +-- +-- ## 7 Events +-- +-- The class comes with a number of FSM-based events that missions designers can use to shape their mission. +-- These are: +-- +-- ### 7.1 TaskAdded. +-- +-- The event is triggered when a new task is added to the controller. Use @{#PLAYERTASKCONTROLLER.OnAfterTaskAdded}() to link into this event: +-- +-- function taskmanager:OnAfterTaskAdded(From, Event, To, Task) +-- ... your code here ... +-- end +-- +-- ### 7.2 TaskDone. +-- +-- The event is triggered when a task has ended. Use @{#PLAYERTASKCONTROLLER.OnAfterTaskDone}() to link into this event: +-- +-- function taskmanager:OnAfterTaskDone(From, Event, To, Task) +-- ... your code here ... +-- end +-- +-- ### 7.3 TaskCancelled. +-- +-- The event is triggered when a task was cancelled manually. Use @{#PLAYERTASKCONTROLLER.OnAfterTaskCancelled}()` to link into this event: +-- +-- function taskmanager:OnAfterTaskCancelled(From, Event, To, Task) +-- ... your code here ... +-- end +-- +-- ### 7.4 TaskSuccess. +-- +-- The event is triggered when a task completed successfully. Use @{#PLAYERTASKCONTROLLER.OnAfterTaskSuccess}() to link into this event: +-- +-- function taskmanager:OnAfterTaskSuccess(From, Event, To, Task) +-- ... your code here ... +-- end +-- +-- ### 7.5 TaskFailed. +-- +-- The event is triggered when a task failed, no repeats. Use @{#PLAYERTASKCONTROLLER.OnAfterTaskFailed}() to link into this event: +-- +-- function taskmanager:OnAfterTaskFailed(From, Event, To, Task) +-- ... your code here ... +-- end +-- +-- ### 7.6 TaskRepeatOnFailed. +-- +-- The event is triggered when a task failed and is re-planned for execution. Use @{#PLAYERTASKCONTROLLER.OnAfterRepeatOnFailed}() to link into this event: +-- +-- function taskmanager:OnAfterRepeatOnFailed(From, Event, To, Task) +-- ... your code here ... +-- end +-- +-- ## 8 Discussion +-- +-- If you have questions or suggestions, please visit the [MOOSE Discord](https://discord.gg/AeYAkHP) #ops-playertask channel. +-- +-- -- @field #PLAYERTASKCONTROLLER PLAYERTASKCONTROLLER = { ClassName = "PLAYERTASKCONTROLLER", @@ -590,19 +963,152 @@ PLAYERTASKCONTROLLER = { ClientSet = nil, UseGroupNames = true, PlayerMenu = {}, + usecluster = false, + MenuName = nil, + ClusterRadius = 1250, + NoScreenOutput = false, + TargetRadius = 500, + UseWhiteList = false, + WhiteList = {}, + gettext = nil, + locale = "en", + precisionbombing = false, } --- --- @field Type +-- @type Type +-- @field #string A2A Air-to-Air Controller +-- @field #string A2G Air-to-Ground Controller +-- @field #string A2S Air-to-Ship Controller +-- @field #string A2GS Air-to-Ground-and-Ship Controller PLAYERTASKCONTROLLER.Type = { A2A = "Air-To-Air", A2G = "Air-To-Ground", A2S = "Air-To-Sea", + A2GS = "Air-To-Ground-Sea", +} + +--- Define a new AUFTRAG Type +AUFTRAG.Type.PRECISIONBOMBING = "Precision Bombing" + +--- +-- @field Messages +PLAYERTASKCONTROLLER.Messages = { + EN = { + TASKABORT = "Task aborted!", + NOACTIVETASK = "No active task!", + FREQUENCIES = "frequencies ", + FREQUENCY = "frequency %.3f", + BROADCAST = "%s, %s, switch to %s for task assignment!", + CASTTS = "close air support", + SEADTTS = "suppress air defense", + BOMBTTS = "bombing", + PRECBOMBTTS = "precision bombing", + BAITTS = "battle field air interdiction", + ANTISHIPTTS = "anti-ship", + INTERCEPTTS = "intercept", + BOMBRUNWAYTTS = "bomb runway", + HAVEACTIVETASK = "You already have one active task! Complete it first!", + PILOTJOINEDTASK = "%s, %s joined task %03d", + TASKNAME = "%s Task ID %03d", + TASKNAMETTS = "%s Task ID %03d", + THREATHIGH = "high", + THREATMEDIUM = "medium", + THREATLOW = "low", + THREATTEXT = "%s\nThreat: %s\nTargets left: %d\nCoord: %s", + THREATTEXTTTS = "%s, %s. Target information for %s. Threat level %s. Targets left %d. Target location %s.", + MARKTASK = "%s, %s, copy, task %03d location marked on map!", + SMOKETASK = "%s, %s, copy, task %03d location smoked!", + FLARETASK = "%s, %s, copy, task %03d location illuminated!", + ABORTTASK = "%s, all stations, %s aborted task %03d!", + UNKNOWN = "Unknown", + MENUTASKING = " Tasking ", + MENUACTIVE = "Active Task", + MENUINFO = "Info", + MENUMARK = "Mark on map", + MENUSMOKE = "Smoke", + MENUFLARE = "Flare", + MENUABORT = "Abort", + MENUJOIN = "Join Task", + MENUTASKNO = "TaskNo", + MENUNOTASKS = "Currently no tasks available.", + TASKCANCELLED = "Task #%03d %s is cancelled!", + TASKCANCELLEDTTS = "%s, task %03d %s is cancelled!", + TASKSUCCESS = "Task #%03d %s completed successfully!", + TASKSUCCESSTTS = "%s, task %03d %s completed successfully!", + TASKFAILED = "Task #%03d %s was a failure!", + TASKFAILEDTTS = "%s, task %03d %s was a failure!", + TASKFAILEDREPLAN = "Task #%03d %s was a failure! Replanning!", + TASKFAILEDREPLANTTS = "%s, task %03d %s was a failure! Replanning!", + TASKADDED = "%s has a new task %s available!", + PILOTS = "\nPilot(s): ", + PILOTSTTS = ". Pilot(s): ", + YES = "Yes", + NO = "No", + POINTEROVERTARGET = "%s, %s, pointer over target for task %03d, lasing!", + POINTERTARGETREPORT = "\nPointer over target: %s\nLasing: %s", + POINTERTARGETLASINGTTS = ". Pointer over target and lasing.", + }, + DE = { + TASKABORT = "Auftrag abgebrochen!", + NOACTIVETASK = "Kein aktiver Auftrag!", + FREQUENCIES = "Frequenzen ", + FREQUENCY = "Frequenz %.3f", + BROADCAST = "%s, %s, Radio %s für Aufgabenzuteilung!", + CASTTS = "Nahbereichsunterstützung", + SEADTTS = "Luftabwehr ausschalten", + BOMBTTS = "Bombardieren", + PRECBOMBTTS = "Präzisionsbombardieren", + BAITTS = "Luftunterstützung", + ANTISHIPTTS = "Anti-Schiff", + INTERCEPTTS = "Abfangen", + BOMBRUNWAYTTS = "Startbahn Bombardieren", + HAVEACTIVETASK = "Du hast einen aktiven Auftrag! Beende ihn zuerst!", + PILOTJOINEDTASK = "%s, %s hat Auftrag %03d angenommen", + TASKNAME = "%s Auftrag ID %03d", + TASKNAMETTS = "%s Auftrag ID %03d", + THREATHIGH = "hoch", + THREATMEDIUM = "mittel", + THREATLOW = "niedrig", + THREATTEXT = "%s\nGefahrstufe: %s\nZiele: %d\nKoord: %s", + THREATTEXTTTS = "%s, %s. Zielinformation zu %s. Gefahrstufe %s. Ziele %d. Zielposition %s.", + MARKTASK = "%s, %s, verstanden, Zielposition %03d auf der Karte markiert!", + SMOKETASK = "%s, %s, verstanden, Zielposition %03d mit Rauch markiert!", + FLARETASK = "%s, %s, verstanden, Zielposition %03d beleuchtet!", + ABORTTASK = "%s, an alle, %s hat Auftrag %03d abgebrochen!", + UNKNOWN = "Unbekannt", + MENUTASKING = " Aufträge ", + MENUACTIVE = "Aktiver Auftrag", + MENUINFO = "Information", + MENUMARK = "Kartenmarkierung", + MENUSMOKE = "Rauchgranate", + MENUFLARE = "Leuchtgranate", + MENUABORT = "Abbrechen", + MENUJOIN = "Auftrag annehmen", + MENUTASKNO = "AuftragsNr", + MENUNOTASKS = "Momentan keine Aufträge verfügbar.", + TASKCANCELLED = "Auftrag #%03d %s wurde beendet!", + TASKCANCELLEDTTS = "%s, Auftrag %03d %s wurde beendet!", + TASKSUCCESS = "Auftrag #%03d %s erfolgreich!", + TASKSUCCESSTTS = "%s, Auftrag %03d %s erfolgreich!", + TASKFAILED = "Auftrag #%03d %s gescheitert!", + TASKFAILEDTTS = "%s, Auftrag %03d %s gescheitert!", + TASKFAILEDREPLAN = "Auftrag #%03d %s gescheitert! Neuplanung!", + TASKFAILEDREPLANTTS = "%s, Auftrag %03d %s gescheitert! Neuplanung!", + TASKADDED = "%s hat einen neuen Auftrag %s erstellt!", + PILOTS = "\nPilot(en): ", + PILOTSTTS = ". Pilot(en): ", + YES = "Ja", + NO = "Nein", + POINTEROVERTARGET = "%s, %s, Marker im Zielbereich für %03d, Laser an!", + POINTERTARGETREPORT = "\nMarker im Zielbereich: %s\nLaser an: %s", + POINTERTARGETLASINGTTS = ". Marker im Zielbereich, Laser is an.", + }, } --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.0.7" +PLAYERTASKCONTROLLER.version="0.1.25" --- Constructor -- @param #PLAYERTASKCONTROLLER self @@ -620,17 +1126,29 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.Coalition = Coalition or coalition.side.BLUE self.CoalitionName = UTILS.GetCoalitionName(Coalition) self.Type = Type or PLAYERTASKCONTROLLER.Type.A2G + + self.usecluster = false + if self.Type == PLAYERTASKCONTROLLER.Type.A2A then + self.usecluster = true + end + + self.ClusterRadius = 1250 + self.TargetRadius = 500 + self.ClientFilter = ClientFilter or "" self.TargetQueue = FIFO:New() -- Utilities.FiFo#FIFO self.TaskQueue = FIFO:New() -- Utilities.FiFo#FIFO self.TasksPerPlayer = FIFO:New() -- Utilities.FiFo#FIFO + self.PrecisionTasks = FIFO:New() -- Utilities.FiFo#FIFO self.PlayerMenu = {} -- #table + self.MenuName = nil + self.repeatonfailed = true self.repeattimes = 5 self.UseGroupNames = true - + if ClientFilter then self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterPrefixes(ClientFilter):FilterStart() else @@ -639,6 +1157,8 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.lid=string.format("PlayerTaskController %s %s | ", self.Name, tostring(self.Type)) + self:_InitLocalization() + -- FSM start state is STOPPED. self:SetStartState("Stopped") @@ -660,21 +1180,189 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self:HandleEvent(EVENTS.Ejection, self._EventHandler) self:HandleEvent(EVENTS.Crash, self._EventHandler) self:HandleEvent(EVENTS.PilotDead, self._EventHandler) + self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) - self:I(self.lid.."Started.") + self:I(self.lid..self.version.." Started.") return self + + --- + -- Pseudo Functions + --- + + --- On After "TaskAdded" event. Task has been added. + -- @function [parent=#PLAYERTASKCONTROLLER] OnAfterTaskAdded + -- @param #PLAYERTASKCONTROLLER self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.PlayerTask#PLAYERTASK Task + + --- On After "TaskDone" event. Task is done. + -- @function [parent=#PLAYERTASKCONTROLLER] OnAfterTaskDone + -- @param #PLAYERTASKCONTROLLER self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.PlayerTask#PLAYERTASK Task + + --- On After "TaskCancelled" event. Task has been cancelled. + -- @function [parent=#PLAYERTASKCONTROLLER] OnAfterTaskCancelled + -- @param #PLAYERTASKCONTROLLER self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.PlayerTask#PLAYERTASK Task + + --- On After "TaskFailed" event. Task has failed. + -- @function [parent=#PLAYERTASKCONTROLLER] OnAfterTaskFailed + -- @param #PLAYERTASKCONTROLLER self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.PlayerTask#PLAYERTASK Task + + --- On After "TaskSuccess" event. Task has been a success. + -- @function [parent=#PLAYERTASKCONTROLLER] OnAfterTaskSuccess + -- @param #PLAYERTASKCONTROLLER self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.PlayerTask#PLAYERTASK Task + + --- On After "TaskRepeatOnFailed" event. Task has failed and will be repeated. + -- @function [parent=#PLAYERTASKCONTROLLER] OnAfterTaskRepeatOnFailed + -- @param #PLAYERTASKCONTROLLER self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.PlayerTask#PLAYERTASK Task + +end + +--- [Internal] Init localization +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:_InitLocalization() + self:T(self.lid.."_InitLocalization") + self.gettext = TEXTANDSOUND:New("PLAYERTASKCONTROLLER","en") -- Core.TextAndSound#TEXTANDSOUND + self.locale = "en" + for locale,table in pairs(self.Messages) do + local Locale = string.lower(tostring(locale)) + self:T("**** Adding locale: "..Locale) + for ID,Text in pairs(table) do + self:T(string.format('Adding ID %s',tostring(ID))) + self.gettext:AddEntry(Locale,tostring(ID),Text) + end + end + return self end ---- [internal] Event handling +--- [User] Set repetition options for tasks +-- @param #PLAYERTASKCONTROLLER self +-- @param #boolean OnOff Set to `true` to switch on and `false` to switch off (defaults to true) +-- @param #number Repeats Number of repeats (defaults to 5) +-- @return #PLAYERTASKCONTROLLER self +-- @usage `taskmanager:SetTaskRepetition(true, 5)` +function PLAYERTASKCONTROLLER:SetTaskRepetition(OnOff, Repeats) + self:T(self.lid.."SetTaskRepetition") + if OnOff then + self.repeatonfailed = true + self.repeattimes = Repeats or 5 + else + self.repeatonfailed = false + self.repeattimes = Repeats or 5 + end + return self +end + +--- [User] Allow precision laser-guided bombing on statics and "high-value" ground units (MBT etc) +-- @param #PLAYERTASKCONTROLLER self +-- @param Ops.FlightGroup#FLIGHTGROUP FlightGroup The FlightGroup (e.g. drone) to be used for lasing (one unit in one group only). +-- Can optionally be handed as Ops.ArmyGroup#ARMYGROUP - **Note** might not find an LOS spot or get lost on the way. Cannot island-hop. +-- @param #number LaserCode The lasercode to be used. Defaults to 1688. +-- @return #PLAYERTASKCONTROLLER self +-- @usage +-- -- Set up precision bombing, FlightGroup as lasing unit +-- local FlightGroup = FLIGHTGROUP:New("LasingUnit") +-- FlightGroup:Activate() +-- taskmanager:EnablePrecisionBombing(FlightGroup,1688) +-- +-- -- Alternatively, set up precision bombing, ArmyGroup as lasing unit +-- local ArmyGroup = ARMYGROUP:New("LasingUnit") +-- ArmyGroup:SetDefaultROE(ENUMS.ROE.WeaponHold) +-- ArmyGroup:SetDefaultInvisible(true) +-- ArmyGroup:Activate() +-- taskmanager:EnablePrecisionBombing(ArmyGroup,1688) +-- +function PLAYERTASKCONTROLLER:EnablePrecisionBombing(FlightGroup,LaserCode) + self:T(self.lid.."EnablePrecisionBombing") + if FlightGroup then + if FlightGroup.ClassName and (FlightGroup.ClassName == "FLIGHTGROUP" or FlightGroup.ClassName == "ARMYGROUP")then + -- ok we have a FG + self.LasingDrone = FlightGroup -- Ops.FlightGroup#FLIGHTGROUP FlightGroup + self.LasingDrone.playertask = {} + self.LasingDrone.playertask.busy = false + self.LasingDrone.playertask.id = 0 + self.precisionbombing = true + self.LasingDrone:SetLaser(LaserCode) + self.LaserCode = LaserCode or 1688 + self.LasingDroneTemplate = self.LasingDrone:_GetTemplate(true) + -- let it orbit the BullsEye if FG + if self.LasingDrone:IsFlightgroup() then + local BullsCoordinate = COORDINATE:NewFromVec3( coalition.getMainRefPoint( self.Coalition )) + local Orbit = AUFTRAG:NewORBIT_CIRCLE(BullsCoordinate,10000,120) + self.LasingDrone:AddMission(Orbit) + end + else + self:E(self.lid.."No FLIGHTGROUP object passed or FLIGHTGROUP is not alive!") + end + else + self.autolase = nil + self.precisionbombing = false + end + return self +end + +--- [Internal] Get player name +-- @param #PLAYERTASKCONTROLLER self +-- @param Wrapper.Client#CLIENT Client +-- @return #string playername +-- @return #string ttsplayername +function PLAYERTASKCONTROLLER:_GetPlayerName(Client) + self:T(self.lid.."DisablePrecisionBombing") + local playername = Client:GetPlayerName() + local ttsplayername = playername + if string.find(playername,"|") then + -- personalized flight name in player naming + ttsplayername = string.match(playername,"| ([%a]+)") + end + if string.find(playername,"#") then + -- personalized flight name in player naming + ttsplayername = string.match(playername,"# ([%a]+)") + end + return playername, ttsplayername +end + +--- [User] Disable precision laser-guided bombing on statics and "high-value" ground units (MBT etc) +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:DisablePrecisionBombing(FlightGroup,LaserCode) + self:T(self.lid.."DisablePrecisionBombing") + self.autolase = nil + self.precisionbombing = false + return self +end + +--- [Internal] Event handling -- @param #PLAYERTASKCONTROLLER self -- @param Core.Event#EVENTDATA EventData -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_EventHandler(EventData) - self:I(self.lid.."_EventHandler: "..EventData.id) + self:T(self.lid.."_EventHandler: "..EventData.id) if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then if EventData.IniPlayerName then - self:I(self.lid.."Event for player: "..EventData.IniPlayerName) + self:T(self.lid.."Event for player: "..EventData.IniPlayerName) if self.PlayerMenu[EventData.IniPlayerName] then self.PlayerMenu[EventData.IniPlayerName]:Remove() self.PlayerMenu[EventData.IniPlayerName] = nil @@ -685,28 +1373,108 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) local Client = _DATABASE:FindClient( EventData.IniPlayerName ) if Client then task:RemoveClient(Client) - text = "Task aborted!" + --text = "Task aborted!" + text = self.gettext:GetEntry("TASKABORT",self.locale) end else - text = "No active task!" + --text = "No active task!" + text = self.gettext:GetEntry("NOACTIVETASK",self.locale) end - self:I(self.lid..text) + self:T(self.lid..text) + end + elseif EventData.id == EVENTS.PlayerEnterAircraft and EventData.IniCoalition == self.Coalition then + if EventData.IniPlayerName and EventData.IniGroup and self.UseSRS then + self:T(self.lid.."Event for player: "..EventData.IniPlayerName) + local frequency = self.Frequency + local freqtext = "" + if type(frequency) == "table" then + freqtext = self.gettext:GetEntry("FREQUENCIES",self.locale) + freqtext = freqtext..table.concat(frequency,", ") + else + local freqt = self.gettext:GetEntry("FREQUENCY",self.locale) + freqtext = string.format(freqt,frequency) + end + local modulation = self.Modulation + if type(modulation) == "table" then modulation = modulation[1] end + modulation = UTILS.GetModulationName(modulation) + local switchtext = self.gettext:GetEntry("BROADCAST",self.locale) + + local playername = EventData.IniPlayerName + if string.find(playername,"|") then + -- personalized flight name in player naming + playername = string.match(playername,"| ([%a]+)") + end + --local text = string.format("%s, %s, switch to %s for task assignment!",EventData.IniPlayerName,self.MenuName or self.Name,freqtext) + local text = string.format(switchtext,self.MenuName or self.Name,playername,freqtext) + self.SRSQueue:NewTransmission(text,nil,self.SRS,timer.getAbsTime()+60,2,{EventData.IniGroup},text,30,self.BCFrequency,self.BCModulation) end end return self end function PLAYERTASKCONTROLLER:_DummyMenu(group) - self:I(self.lid.."_DummyMenu") + self:T(self.lid.."_DummyMenu") return self end ---- [user] Switch usage of target names for menu entries on or off +--- [User] Set locale for localization. Defaults to "en" +-- @param #PLAYERTASKCONTROLLER self +-- @param #string Locale The locale to use +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetLocale(Locale) + self:T(self.lid.."SetLocale") + self.locale = Locale or "en" + return self +end + +--- [User] Switch screen output. +-- @param #PLAYERTASKCONTROLLER self +-- @param #boolean OnOff. Switch screen output off (true) or on (false) +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SuppressScreenOutput(OnOff) + self:T(self.lid.."SuppressScreenOutput") + self.NoScreenOutput = OnOff or false + return self +end + +--- [User] Set target radius. Determines the zone radius to distinguish CAS from BAI tasks and to find enemies if the TARGET object is a COORDINATE. +-- @param #PLAYERTASKCONTROLLER self +-- @param #number Radius Radius to use in meters. Defaults to 500 meters. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetTargetRadius(Radius) + self:T(self.lid.."SetTargetRadius") + self.TargetRadius = Radius or 500 + return self +end + +--- [User] Set the cluster radius if you want to use target clusters rather than single group detection. +-- Note that for a controller type A2A target clustering is on by default. Also remember that the diameter of the resulting zone is double the radius. +-- @param #PLAYERTASKCONTROLLER self +-- @param #number Radius Target cluster radius in meters. Default is 1250m or 0.67NM +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetClusterRadius(Radius) + self:T(self.lid.."SetClusterRadius") + self.ClusterRadius = Radius or 1250 + self.usecluster = true + return self +end + +--- [User] Manually cancel a specific task +-- @param #PLAYERTASKCONTROLLER self +-- @param Ops.PlayerTask#PLAYERTASK Task The task to be cancelled +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:CancelTask(Task) + self:T(self.lid.."CancelTask") + Task:__Cancel(-1) + return self +end + +--- [User] Switch usage of target names for menu entries on or off -- @param #PLAYERTASKCONTROLLER self -- @param #boolean OnOff If true, set to on (default), if nil or false, set to off -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:SwitchUseGroupNames(OnOff) - self:I(self.lid.."SwitchUseGroupNames") + self:T(self.lid.."SwitchUseGroupNames") if OnOff then self.UseGroupNames = true else @@ -719,7 +1487,7 @@ end -- @param #PLAYERTASKCONTROLLER self -- @return #table TaskTypes function PLAYERTASKCONTROLLER:_GetAvailableTaskTypes() - self:I(self.lid.."_GetAvailableTaskTypes") + self:T(self.lid.."_GetAvailableTaskTypes") local tasktypes = {} self.TaskQueue:ForEach( function (Task) @@ -735,20 +1503,31 @@ end -- @param #PLAYERTASKCONTROLLER self -- @return #table TasksPerTypes function PLAYERTASKCONTROLLER:_GetTasksPerType() - self:I(self.lid.."_GetTasksPerType") + self:T(self.lid.."_GetTasksPerType") local tasktypes = self:_GetAvailableTaskTypes() - self:I({tasktypes}) + self:T({tasktypes}) - self.TaskQueue:ForEach( - function (Task) - local task = Task -- Ops.PlayerTask#PLAYERTASK - local type = Task.Type - if task:GetState() ~= "Executing" and not task:IsDone() then - table.insert(tasktypes[type],task) - end + -- Sort tasks per threat level first + local datatable = self.TaskQueue:GetDataTable() + local threattable = {} + for _,_task in pairs(datatable) do + local task = _task -- Ops.PlayerTask#PLAYERTASK + local threat = task.Target:GetThreatLevelMax() + threattable[#threattable+1]={task=task,threat=threat} + end + + table.sort(threattable, function (k1, k2) return k1.threat > k2.threat end ) + + + for _id,_data in pairs(threattable) do + local threat=_data.threat + local task = _data.task -- Ops.PlayerTask#PLAYERTASK + local type = task.Type + if not task:IsDone() then + table.insert(tasktypes[type],task) end - ) + end return tasktypes end @@ -757,7 +1536,7 @@ end -- @param #PLAYERTASKCONTROLLER self -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_CheckTargetQueue() - self:I(self.lid.."_CheckTargetQueue") + self:T(self.lid.."_CheckTargetQueue") if self.TargetQueue:Count() > 0 then local object = self.TargetQueue:Pull() local target = TARGET:New(object) @@ -770,19 +1549,19 @@ end -- @param #PLAYERTASKCONTROLLER self -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_CheckTaskQueue() - self:I(self.lid.."_CheckTaskQueue") + self:T(self.lid.."_CheckTaskQueue") if self.TaskQueue:Count() > 0 then -- remove done tasks local tasks = self.TaskQueue:GetIDStack() for _id,_entry in pairs(tasks) do local data = _entry.data -- Ops.PlayerTask#PLAYERTASK - self:I("Looking at Task: "..data.PlayerTaskNr.." Type: "..data.Type.." State: "..data:GetState()) + self:T("Looking at Task: "..data.PlayerTaskNr.." Type: "..data.Type.." State: "..data:GetState()) if data:GetState() == "Done" or data:GetState() == "Stopped" then local task = self.TaskQueue:ReadByID(_id) -- Ops.PlayerTask#PLAYERTASK - -- DEBUG: Remove clients from the task + -- DONE: Remove clients from the task local clientsattask = task.Clients:GetIDStackSorted() for _,_id in pairs(clientsattask) do - self:I("*****Removing player " .. _id) + self:T("*****Removing player " .. _id) self.TasksPerPlayer:PullByID(_id) end local task = self.TaskQueue:PullByID(_id) -- Ops.PlayerTask#PLAYERTASK @@ -793,89 +1572,398 @@ function PLAYERTASKCONTROLLER:_CheckTaskQueue() return self end ---- [user] Add a target object to the target queue +--- [Internal] Check precision task queue -- @param #PLAYERTASKCONTROLLER self --- @param Wrapper.Positionable#POSITIONABLE Target The target GROUP, SET_GROUP, UNIT, SET_UNIT, STATIC, AIRBASE or COORDINATE. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:_CheckPrecisionTasks() + self:T(self.lid.."_CheckTaskQueue") + if self.PrecisionTasks:Count() > 0 and self.precisionbombing then + if not self.LasingDrone or self.LasingDrone:IsDead() then + -- we need a new drone + self:E(self.lid.."Lasing drone is dead ... creating a new one!") + if self.LasingDrone then + self.LasingDrone:_Respawn(1,nil,true) + else + -- TODO: Handle ArmyGroup + local FG = FLIGHTGROUP:New(self.LasingDroneTemplate) + FG:Activate() + self:EnablePrecisionBombing(FG,self.LaserCode or 1688) + end + return self + end + -- do we have a lasing unit assigned? + if self.LasingDrone and self.LasingDrone:IsAlive() then + if self.LasingDrone.playertask and (not self.LasingDrone.playertask.busy) then + -- not busy, get a task + self:I(self.lid.."Sending lasing unit to target") + local task = self.PrecisionTasks:Pull() -- Ops.PlayerTask#PLAYERTASK + self.LasingDrone.playertask.id = task.PlayerTaskNr + self.LasingDrone.playertask.busy = true + self.LasingDrone.playertask.inreach = false + self.LasingDrone.playertask.reachmessage = false + -- move the drone to target + if self.LasingDrone:IsFlightgroup() then + local auftrag = AUFTRAG:NewORBIT_CIRCLE(task.Target:GetCoordinate(),10000,120) + local currmission = self.LasingDrone:GetMissionCurrent() + self.LasingDrone:AddMission(auftrag) + currmission:__Cancel(-2) + elseif self.LasingDrone:IsArmygroup() then + local tgtcoord = task.Target:GetCoordinate() + local tgtzone = ZONE_RADIUS:New("ArmyGroup-"..math.random(1,10000),tgtcoord:GetVec2(),3000) + local finalpos=nil -- Core.Point#COORDINATE + for i=1,50 do + finalpos = tgtzone:GetRandomCoordinate(2000,0,{land.SurfaceType.LAND,land.SurfaceType.ROAD,land.SurfaceType.SHALLOW_WATER}) + if finalpos then + if finalpos:IsLOS(tgtcoord,0) then + break + end + end + end + if finalpos then + -- yeah we got one + local auftrag = AUFTRAG:NewARMOREDGUARD(finalpos) + local currmission = self.LasingDrone:GetMissionCurrent() + self.LasingDrone:AddMission(auftrag) + if currmission then currmission:__Cancel(-2) end + else + -- could not find LOS position! + self:E("***Could not find LOS position to post ArmyGroup for lasing!") + self.LasingDrone.playertask.id = 0 + self.LasingDrone.playertask.busy = false + self.LasingDrone.playertask.inreach = false + self.LasingDrone.playertask.reachmessage = false + end + end + self.PrecisionTasks:Push(task,task.PlayerTaskNr) + elseif self.LasingDrone.playertask and self.LasingDrone.playertask.busy then + -- drone is busy, set up laser when over target + local task = self.PrecisionTasks:ReadByID(self.LasingDrone.playertask.id) -- Ops.PlayerTask#PLAYERTASK + self:I("Looking at Task: "..task.PlayerTaskNr.." Type: "..task.Type.." State: "..task:GetState()) + if (not task) or task:GetState() == "Done" or task:GetState() == "Stopped" then + -- we're done here + local task = self.PrecisionTasks:PullByID(self.LasingDrone.playertask.id) -- Ops.PlayerTask#PLAYERTASK + self:_CheckTaskQueue() + task = nil + if self.LasingDrone:IsLasing() then + self.LasingDrone:__LaserOff(-1) + end + self.LasingDrone.playertask.busy = false + self.LasingDrone.playertask.inreach = false + self.LasingDrone.playertask.id = 0 + self.LasingDrone.playertask.reachmessage = false + self:I(self.lid.."Laser Off") + else + -- not done yet + local dcoord = self.LasingDrone:GetCoordinate() + local tcoord = task.Target:GetCoordinate() + local dist = dcoord:Get2DDistance(tcoord) + -- close enough? + if dist < 3000 and not self.LasingDrone:IsLasing() then + self:I(self.lid.."Laser On") + self.LasingDrone:__LaserOn(-1,tcoord) + self.LasingDrone.playertask.inreach = true + if not self.LasingDrone.playertask.reachmessage then + --local textmark = self.gettext:GetEntry("FLARETASK",self.locale) + self.LasingDrone.playertask.reachmessage = true + local clients = task:GetClients() + local text = "" + for _,playername in pairs(clients) do + local pointertext = self.gettext:GetEntry("POINTEROVERTARGET",self.locale) + --text = string.format("%s, %s, pointer over target for task %03d, lasing!", playername, self.MenuName or self.Name, task.PlayerTaskNr) + text = string.format(pointertext, playername, self.MenuName or self.Name, task.PlayerTaskNr) + if not self.NoScreenOutput then + local client = nil + self.ClientSet:ForEachClient( + function(Client) + if Client:GetPlayerName() == playername then client = Client end + end + ) + if client then + local m = MESSAGE:New(text,15,"Tasking"):ToClient(client) + end + end + end + if self.UseSRS then + self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2) + end + end + end + end + end + end + end + return self +end + +--- [Internal] Check task queue for a specific player name +-- @param #PLAYERTASKCONTROLLER self +-- @return #boolean outcome +function PLAYERTASKCONTROLLER:_CheckPlayerHasTask(PlayerName) + self:T(self.lid.."_CheckPlayerHasTask") + return self.TasksPerPlayer:HasUniqueID(PlayerName) +end + +--- [User] Add a target object to the target queue +-- @param #PLAYERTASKCONTROLLER self +-- @param Wrapper.Positionable#POSITIONABLE Target The target GROUP, SET\_GROUP, UNIT, SET\_UNIT, STATIC, SET\_STATIC, AIRBASE, ZONE or COORDINATE. -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:AddTarget(Target) - self:I(self.lid.."AddTarget") + self:T(self.lid.."AddTarget") self.TargetQueue:Push(Target) return self end +--- [Internal] Check for allowed task type, if there is a (positive) whitelist +-- @param #PLAYERTASKCONTROLLER self +-- @param #string Type +-- @return #boolean Outcome +function PLAYERTASKCONTROLLER:_CheckTaskTypeAllowed(Type) + self:T(self.lid.."_CheckTaskTypeAllowed") + local Outcome = false + if self.UseWhiteList then + for _,_type in pairs(self.WhiteList) do + if Type == _type then + Outcome = true + break + end + end + else + return true + end + return Outcome +end + +--- [Internal] Check for allowed task type, if there is a (negative) blacklist +-- @param #PLAYERTASKCONTROLLER self +-- @param #string Type +-- @return #boolean Outcome +function PLAYERTASKCONTROLLER:_CheckTaskTypeDisallowed(Type) + self:T(self.lid.."_CheckTaskTypeDisallowed") + local Outcome = false + if self.UseBlackList then + for _,_type in pairs(self.BlackList) do + if Type == _type then + Outcome = true + break + end + end + else + return true + end + return Outcome +end + +--- [User] Set up a (positive) whitelist of allowed task types. Only these types will be generated. +-- @param #PLAYERTASKCONTROLLER self +-- @param #table WhiteList Table of task types that can be generated. Use to restrict available types. +-- @return #PLAYERTASKCONTROLLER self +-- @usage Currently, the following task types will be generated, if detection has been set up: +-- A2A - AUFTRAG.Type.INTERCEPT +-- A2S - AUFTRAG.Type.ANTISHIP +-- A2G - AUFTRAG.Type.CAS, AUFTRAG.Type.BAI, AUFTRAG.Type.SEAD, AUFTRAG.Type.BOMBING, AUFTRAG.Type.PRECISIONBOMBING, AUFTRAG.Type.BOMBRUNWAY +-- A2GS - A2G + A2S +-- If you don't want SEAD tasks generated, use as follows where "mycontroller" is your PLAYERTASKCONTROLLER object: +-- +-- `mycontroller:SetTaskWhiteList({AUFTRAG.Type.CAS, AUFTRAG.Type.BAI, AUFTRAG.Type.BOMBING, AUFTRAG.Type.BOMBRUNWAY})` +-- +function PLAYERTASKCONTROLLER:SetTaskWhiteList(WhiteList) + self:T(self.lid.."SetTaskWhiteList") + self.WhiteList = WhiteList + self.UseWhiteList = true + return self +end + +--- [User] Set up a (negative) blacklist of forbidden task types. These types will **not** be generated. +-- @param #PLAYERTASKCONTROLLER self +-- @param #table BlackList Table of task types that cannot be generated. Use to restrict available types. +-- @return #PLAYERTASKCONTROLLER self +-- @usage Currently, the following task types will be generated, if detection has been set up: +-- A2A - AUFTRAG.Type.INTERCEPT +-- A2S - AUFTRAG.Type.ANTISHIP +-- A2G - AUFTRAG.Type.CAS, AUFTRAG.Type.BAI, AUFTRAG.Type.SEAD, AUFTRAG.Type.BOMBING, AUFTRAG.Type.PRECISIONBOMBING, AUFTRAG.Type.BOMBRUNWAY +-- A2GS - A2G + A2S +-- If you don't want SEAD tasks generated, use as follows where "mycontroller" is your PLAYERTASKCONTROLLER object: +-- +-- `mycontroller:SetTaskBlackList({AUFTRAG.Type.SEAD})` +-- +function PLAYERTASKCONTROLLER:SetTaskBlackList(BlackList) + self:T(self.lid.."SetTaskBlackList") + self.BlackList = BlackList + self.UseBlackList = true + return self +end + --- [Internal] Add a task to the task queue -- @param #PLAYERTASKCONTROLLER self -- @param Ops.Target#TARGET Target -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_AddTask(Target) - self:I(self.lid.."_AddTask") + self:T(self.lid.."_AddTask") local cat = Target:GetCategory() + local threat = Target:GetThreatLevelMax() local type = AUFTRAG.Type.CAS + --local ttstype = "close air support" + local ttstype = self.gettext:GetEntry("CASTTS",self.locale) + if cat == TARGET.Category.GROUND then type = AUFTRAG.Type.CAS -- TODO: debug BAI, CAS, SEAD local targetobject = Target:GetObject() -- Wrapper.Positionable#POSITIONABLE if targetobject:IsInstanceOf("UNIT") then - self:I("SEAD Check UNIT") + self:T("SEAD Check UNIT") if targetobject:HasSEAD() then type = AUFTRAG.Type.SEAD + --ttstype = "suppress air defense" + ttstype = self.gettext:GetEntry("SEADTTS",self.locale) end elseif targetobject:IsInstanceOf("GROUP") then - self:I("SEAD Check GROUP") + self:T("SEAD Check GROUP") local attribute = targetobject:GetAttribute() - if attribute == GROUP.Attribute.GROUND_SAM or attribute == GROUP.Attribute.GROUND_AAA then + if attribute == GROUP.Attribute.GROUND_SAM or attribute == GROUP.Attribute.GROUND_AAA or attribute == GROUP.Attribute.GROUND_EWR then type = AUFTRAG.Type.SEAD + --ttstype = "suppress air defense" + ttstype = self.gettext:GetEntry("SEADTTS",self.locale) end elseif targetobject:IsInstanceOf("SET_GROUP") then - self:I("SEAD Check SET_GROUP") + self:T("SEAD Check SET_GROUP") targetobject:ForEachGroup( function (group) local attribute = group:GetAttribute() - if attribute == GROUP.Attribute.GROUND_SAM or attribute == GROUP.Attribute.GROUND_AAA then + if attribute == GROUP.Attribute.GROUND_SAM or attribute == GROUP.Attribute.GROUND_AAA or attribute == GROUP.Attribute.GROUND_EWR then type = AUFTRAG.Type.SEAD + --ttstype = "suppress air defense" + ttstype = self.gettext:GetEntry("SEADTTS",self.locale) end end ) elseif targetobject:IsInstanceOf("SET_UNIT") then - self:I("SEAD Check SET_UNIT") + self:T("SEAD Check SET_UNIT") targetobject:ForEachUnit( function (unit) if unit:HasSEAD() then type = AUFTRAG.Type.SEAD + --ttstype = "suppress air defenses" + ttstype = self.gettext:GetEntry("SEADTTS",self.locale) end end ) + elseif targetobject:IsInstanceOf("SET_STATIC") or targetobject:IsInstanceOf("STATIC") then + self:T("(PRECISION-)BOMBING SET_STATIC or STATIC") + if self.precisionbombing then + type = AUFTRAG.Type.PRECISIONBOMBING + ttstype = self.gettext:GetEntry("PRECBOMBTTS",self.locale) + else + type = AUFTRAG.Type.BOMBING + ttstype = self.gettext:GetEntry("BOMBTTS",self.locale) + end + end - -- if there are no friendlies nearby ~2km and task isn't SEAD, then it's BAI + -- if there are no friendlies nearby ~0.5km and task isn't SEAD, then it's BAI local targetcoord = Target:GetCoordinate() local targetvec2 = targetcoord:GetVec2() - local targetzone = ZONE_RADIUS:New(self.Name,targetvec2,2000) + local targetzone = ZONE_RADIUS:New(self.Name,targetvec2,self.TargetRadius) local coalition = targetobject:GetCoalitionName() or "Blue" coalition = string.lower(coalition) - self:I("Target coalition is "..tostring(coalition)) + self:T("Target coalition is "..tostring(coalition)) local filtercoalition = "blue" if coalition == "blue" then filtercoalition = "red" end local friendlyset = SET_GROUP:New():FilterCategoryGround():FilterCoalitions(filtercoalition):FilterZones({targetzone}):FilterOnce() - if friendlyset:Count() == 0 and type ~= AUFTRAG.Type.SEAD then + if friendlyset:Count() == 0 and type == AUFTRAG.Type.CAS then type = AUFTRAG.Type.BAI + --ttstype = "battle field air interdiction" + ttstype = self.gettext:GetEntry("BAITTS",self.locale) + end + -- see if we can do precision bombing + if (type == AUFTRAG.Type.BAI or type == AUFTRAG.Type.CAS) and self.precisionbombing then + -- threatlevel between 3 and 6 means, it's artillery, tank, modern tank or AAA + if threat > 2 and threat < 7 then + type = AUFTRAG.Type.PRECISIONBOMBING + ttstype = self.gettext:GetEntry("PRECBOMBTTS",self.locale) + end end elseif cat == TARGET.Category.NAVAL then type = AUFTRAG.Type.ANTISHIP + --ttstype = "anti-ship" + ttstype = self.gettext:GetEntry("ANTISHIPTTS",self.locale) elseif cat == TARGET.Category.AIRCRAFT then type = AUFTRAG.Type.INTERCEPT + --ttstype = "intercept" + ttstype = self.gettext:GetEntry("INTERCEPTTS",self.locale) elseif cat == TARGET.Category.AIRBASE then - --TODO: Define Success Criteria, AB hit? Runway blocked, how to determine? change of coalition? + --TODO: Define Success Criteria, AB hit? Runway blocked, how to determine? change of coalition? Void of enemies? + -- Current implementation - bombing in AFB zone (EVENTS.Shot) type = AUFTRAG.Type.BOMBRUNWAY + -- ttstype = "bomb runway" + ttstype = self.gettext:GetEntry("BOMBRUNWAYTTS",self.locale) elseif cat == TARGET.Category.COORDINATE or cat == TARGET.Category.ZONE then --TODO: Define Success Criteria, void of enemies? - type = AUFTRAG.Type.BOMBING + -- Current implementation - find SET of enemies in ZONE or 500m radius around coordinate, and assign as targets + local zone = Target:GetObject() + if cat == TARGET.Category.COORDINATE then + zone = ZONE_RADIUS:New("TargetZone-"..math.random(1,10000),Target:GetVec2(),self.TargetRadius) + end + -- find some enemies around there... + local enemies = self.CoalitionName == "Blue" and "red" or "blue" + local enemysetg = SET_GROUP:New():FilterCoalitions(enemies):FilterCategoryGround():FilterActive(true):FilterZones({zone}):FilterOnce() + local enemysets = SET_STATIC:New():FilterCoalitions(enemies):FilterZones({zone}):FilterOnce() + local countg = enemysetg:Count() + local counts = enemysets:Count() + if countg > 0 then + self:AddTarget(enemysetg) + end + if counts > 0 then + self:AddTarget(enemysets) + end + return self + end + + if self.UseWhiteList then + if not self:_CheckTaskTypeAllowed(type) then + return self + end + end + + if self.UseBlackList then + if self:_CheckTaskTypeDisallowed(type) then + return self + end + end + + local task = PLAYERTASK:New(type,Target,self.repeatonfailed,self.repeattimes,ttstype) + + task.coalition = self.Coalition + + if type == AUFTRAG.Type.BOMBRUNWAY then + -- task to handle event shot + task:HandleEvent(EVENTS.Shot) + function task:OnEventShot(EventData) + local data = EventData -- Core.Event#EVENTDATA EventData + local wcat = data.Weapon:getCategory() -- cat 2 or 3 + local coord = data.IniUnit:GetCoordinate() or data.IniGroup:GetCoordinate() + local vec2 = coord:GetVec2() or {x=0, y=0} + local coal = data.IniCoalition + local afbzone = AIRBASE:FindByName(Target:GetName()):GetZone() + local runways = AIRBASE:FindByName(Target:GetName()):GetRunways() or {} + local inrunwayzone = false + for _,_runway in pairs(runways) do + local runway = _runway -- Wrapper.Airbase#AIRBASE.Runway + if runway.zone:IsVec2InZone(vec2) then + inrunwayzone = true + end + end + local inzone = afbzone:IsVec2InZone(vec2) + if coal == task.coalition and (wcat == 2 or wcat == 3) and (inrunwayzone or inzone) then + -- bombing/rockets inside target AFB zone - well done! + task:__Success(-20) + end + end end - local task = PLAYERTASK:New(type,Target,self.repeatonfailed,self.repeattimes) task:_SetController(self) self.TaskQueue:Push(task) self:__TaskAdded(-1,task) + return self end @@ -886,26 +1974,39 @@ end -- @param Ops.PlayerTask#PLAYERTASK Task -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task) - self:I(self.lid.."_JoinTask") - local playername = Client:GetPlayerName() + self:T(self.lid.."_JoinTask") + local playername, ttsplayername = self:_GetPlayerName(Client) if self.TasksPerPlayer:HasUniqueID(playername) then -- Player already has a task - local m=MESSAGE:New("You already have one active task! Complete it first!","10","Info"):ToGroup(Group) + if not self.NoScreenOutput then + local text = self.gettext:GetEntry("HAVEACTIVETASK",self.locale) + local m=MESSAGE:New(text,"10","Tasking"):ToGroup(Group) + end return self end - Task:AddClient(Client) local taskstate = Task:GetState() - --self:I(self.lid.."Taskstate = "..taskstate) - if taskstate ~= "Executing" and taskstate ~= "Done" then - Task:__Requested(-1) - Task:__Executing(-2) - local text = string.format("Player %s joined task %d in state %s", playername, Task.PlayerTaskNr, taskstate) - self:I(self.lid..text) - local m=MESSAGE:New(text,"10","Info"):ToAll() + if not Task:IsDone() then + if taskstate ~= "Executing" then + Task:__Requested(-1) + Task:__Executing(-2) + end + Task:AddClient(Client) + local joined = self.gettext:GetEntry("PILOTJOINEDTASK",self.locale) + local text = string.format(joined, self.MenuName or self.Name, ttsplayername, Task.PlayerTaskNr) + self:T(self.lid..text) + if not self.NoScreenOutput then + local m=MESSAGE:New(text,"10","Tasking"):ToAll() + end + if self.UseSRS then + self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2) + end self.TasksPerPlayer:Push(Task,playername) -- clear menu - if self.PlayerMenu[playername] then - self.PlayerMenu[playername]:RemoveSubMenus() + self:_BuildMenus(Client) + end + if Task.Type == AUFTRAG.Type.PRECISIONBOMBING then + if not self.PrecisionTasks:HasUniqueID(Task.PlayerTaskNr) then + self.PrecisionTasks:Push(Task,Task.PlayerTaskNr) end end return self @@ -917,31 +2018,80 @@ end -- @param Wrapper.Client#CLIENT Client -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client) - self:I(self.lid.."_ActiveTaskInfo") - local playername = Client:GetPlayerName() + self:T(self.lid.."_ActiveTaskInfo") + local playername, ttsplayername = self:_GetPlayerName(Client) local text = "" if self.TasksPerPlayer:HasUniqueID(playername) then - -- TODO: Show multiple - local task = self.TasksPerPlayer:GetIDStack() + -- TODO: Show multiple? local task = self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK - local taskname = string.format("%s Task ID %02d",task.Type,task.PlayerTaskNr) + local tname = self.gettext:GetEntry("TASKNAME",self.locale) + local ttsname = self.gettext:GetEntry("TASKNAMETTS",self.locale) + local taskname = string.format(tname,task.Type,task.PlayerTaskNr) + local ttstaskname = string.format(ttsname,task.TTSType,task.PlayerTaskNr) local Coordinate = task.Target:GetCoordinate() - local CoordText = Coordinate:ToStringA2G(Client) + local CoordText = "" + if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then + CoordText = Coordinate:ToStringA2G(Client) + else + CoordText = Coordinate:ToStringA2A(Client) + end local ThreatLevel = task.Target:GetThreatLevelMax() + --local ThreatLevelText = "high" + local ThreatLevelText = self.gettext:GetEntry("THREATHIGH",self.locale) + if ThreatLevel > 3 and ThreatLevel < 8 then + --ThreatLevelText = "medium" + ThreatLevelText = self.gettext:GetEntry("THREATMEDIUM",self.locale) + elseif ThreatLevel <= 3 then + --ThreatLevelText = "low" + ThreatLevelText = self.gettext:GetEntry("THREATLOW",self.locale) + end local targets = task.Target:CountTargets() or 0 local clientlist = task:GetClients() local ThreatGraph = "[" .. string.rep( "■", ThreatLevel ) .. string.rep( "□", 10 - ThreatLevel ) .. "]: "..ThreatLevel - text = string.format("%s\nThreat: %s\nTargets left: %d\nCoord: %s", taskname, ThreatGraph, targets, CoordText) - local clienttxt = "\nPilot(s): " + local ThreatLocaleText = self.gettext:GetEntry("THREATTEXT",self.locale) + text = string.format(ThreatLocaleText, taskname, ThreatGraph, targets, CoordText) + if task.Type == AUFTRAG.Type.PRECISIONBOMBING and self.precisionbombing then + if self.LasingDrone and self.LasingDrone.playertask then + local yes = self.gettext:GetEntry("YES",self.locale) + local no = self.gettext:GetEntry("NO",self.locale) + local inreach = self.LasingDrone.playertask.inreach == true and yes or no + local islasing = self.LasingDrone:IsLasing() == true and yes or no + local prectext = self.gettext:GetEntry("POINTERTARGETREPORT",self.locale) + prectext = string.format(prectext,inreach,islasing) + text = text .. prectext + end + end + local clienttxt = self.gettext:GetEntry("PILOTS",self.locale) for _,_name in pairs(clientlist) do + if string.find(_name,"|") then + -- personalized flight name in player naming + _name = string.match(_name,"| ([%a]+)") + end clienttxt = clienttxt .. _name .. ", " end - clienttxt=string.gsub(clienttxt,", $",".") - text = text .. clienttxt + clienttxt=string.gsub(clienttxt,", $",".") + text = text .. clienttxt + if self.UseSRS then + if string.find(CoordText," BR, ") then + CoordText = string.gsub(CoordText," BR, "," Bee, Arr, ") + end + local ThreatLocaleTextTTS = self.gettext:GetEntry("THREATTEXTTTS",self.locale) + local ttstext = string.format(ThreatLocaleTextTTS,self.MenuName or self.Name,ttsplayername,ttstaskname,ThreatLevelText, targets, CoordText) + -- POINTERTARGETLASINGTTS = ". Pointer over target and lasing." + if task.Type == AUFTRAG.Type.PRECISIONBOMBING and self.precisionbombing then + if self.LasingDrone.playertask.inreach and self.LasingDrone:IsLasing() then + local lasingtext = self.gettext:GetEntry("POINTERTARGETLASINGTTS",self.locale) + ttstext = ttstext .. lasingtext + end + end + self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,2) + end else - text = "No active task!" + text = self.gettext:GetEntry("NOACTIVETASK",self.locale) + end + if not self.NoScreenOutput then + local m=MESSAGE:New(text,15,"Tasking"):ToGroup(Group) end - local m=MESSAGE:New(text,15,"Tasking"):ToGroup(Group) return self end @@ -951,17 +2101,26 @@ end -- @param Wrapper.Client#CLIENT Client -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_MarkTask(Group, Client) - self:I(self.lid.."_ActiveTaskInfo") - local playername = Client:GetPlayerName() + self:T(self.lid.."_MarkTask") + local playername, ttsplayername = self:_GetPlayerName(Client) local text = "" if self.TasksPerPlayer:HasUniqueID(playername) then local task = self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK - task:MarkTargetOnF10Map() - text = "Task location marked!" + local text = string.format("Task ID #%03d | Type: %s | Threat: %d",task.PlayerTaskNr,task.Type,task.Target:GetThreatLevelMax()) + task:MarkTargetOnF10Map(text) + local textmark = self.gettext:GetEntry("MARKTASK",self.locale) + --text = string.format("%s, copy pilot %s, task %03d location marked on map!", self.MenuName or self.Name, playername, task.PlayerTaskNr) + text = string.format(textmark, ttsplayername, self.MenuName or self.Name, task.PlayerTaskNr) + self:T(self.lid..text) + if self.UseSRS then + self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2) + end else - text = "No active task!" + text = self.gettext:GetEntry("NOACTIVETASK",self.locale) + end + if not self.NoScreenOutput then + local m=MESSAGE:New(text,"10","Tasking"):ToGroup(Group) end - local m=MESSAGE:New(text,15,"Info"):ToGroup(Group) return self end @@ -971,17 +2130,25 @@ end -- @param Wrapper.Client#CLIENT Client -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_SmokeTask(Group, Client) - self:I(self.lid.."_SmokeTask") - local playername = Client:GetPlayerName() + self:T(self.lid.."_SmokeTask") + local playername, ttsplayername = self:_GetPlayerName(Client) local text = "" if self.TasksPerPlayer:HasUniqueID(playername) then local task = self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK task:SmokeTarget() - text = "Task location smoked!" + local textmark = self.gettext:GetEntry("SMOKETASK",self.locale) + text = string.format(textmark, ttsplayername, self.MenuName or self.Name, task.PlayerTaskNr) + self:T(self.lid..text) + --local m=MESSAGE:New(text,"10","Tasking"):ToAll() + if self.UseSRS then + self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2) + end else - text = "No active task!" + text = self.gettext:GetEntry("NOACTIVETASK",self.locale) + end + if not self.NoScreenOutput then + local m=MESSAGE:New(text,15,"Tasking"):ToGroup(Group) end - local m=MESSAGE:New(text,15,"Info"):ToGroup(Group) return self end @@ -991,17 +2158,25 @@ end -- @param Wrapper.Client#CLIENT Client -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_FlareTask(Group, Client) - self:I(self.lid.."_FlareTask") - local playername = Client:GetPlayerName() + self:T(self.lid.."_FlareTask") + local playername, ttsplayername = self:_GetPlayerName(Client) local text = "" if self.TasksPerPlayer:HasUniqueID(playername) then local task = self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK task:FlareTarget() - text = "Task location illuminated!" + local textmark = self.gettext:GetEntry("FLARETASK",self.locale) + text = string.format(textmark, ttsplayername, self.MenuName or self.Name, task.PlayerTaskNr) + self:T(self.lid..text) + --local m=MESSAGE:New(text,"10","Tasking"):ToAll() + if self.UseSRS then + self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2) + end else - text = "No active task!" + text = self.gettext:GetEntry("NOACTIVETASK",self.locale) + end + if not self.NoScreenOutput then + local m=MESSAGE:New(text,15,"Tasking"):ToGroup(Group) end - local m=MESSAGE:New(text,15,"Info"):ToGroup(Group) return self end @@ -1011,73 +2186,126 @@ end -- @param Wrapper.Client#CLIENT Client -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_AbortTask(Group, Client) - self:I(self.lid.."_FlareTask") - local playername = Client:GetPlayerName() + self:T(self.lid.."_FlareTask") + local playername, ttsplayername = self:_GetPlayerName(Client) local text = "" if self.TasksPerPlayer:HasUniqueID(playername) then local task = self.TasksPerPlayer:PullByID(playername) -- Ops.PlayerTask#PLAYERTASK task:ClientAbort(Client) - text = "Task aborted!" + local textmark = self.gettext:GetEntry("ABORTTASK",self.locale) + text = string.format(textmark, ttsplayername, self.MenuName or self.Name, task.PlayerTaskNr) + self:T(self.lid..text) + --local m=MESSAGE:New(text,"10","Tasking"):ToAll() + if self.UseSRS then + self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2) + end else - text = "No active task!" + text = self.gettext:GetEntry("NOACTIVETASK",self.locale) end - local m=MESSAGE:New(text,15,"Info"):ToGroup(Group) + if not self.NoScreenOutput then + local m=MESSAGE:New(text,15,"Tasking"):ToGroup(Group) + end + self:_BuildMenus(Client) return self end --- [Internal] Build client menus -- @param #PLAYERTASKCONTROLLER self +-- @param Wrapper.Client#CLIENT Client (optional) build for this client name only -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:_BuildMenus() - self:I(self.lid.."_BuildMenus") +function PLAYERTASKCONTROLLER:_BuildMenus(Client) + self:T(self.lid.."_BuildMenus") local clients = self.ClientSet:GetAliveSet() + if Client then + clients = {Client} + end for _,_client in pairs(clients) do if _client then local client = _client -- Wrapper.Client#CLIENT local group = client:GetGroup() - local playername = client:GetPlayerName() or "Unknown" + local unknown = self.gettext:GetEntry("UNKNOWN",self.locale) + local playername = client:GetPlayerName() or unknown if group and client then - local topmenu = MENU_GROUP:New(group,self.Name.." Tasking "..self.Type,nil) - local active = MENU_GROUP:New(group,"Active Task",topmenu) - local info = MENU_GROUP_COMMAND:New(group,"Info",active,self._ActiveTaskInfo,self,group,client) - local mark = MENU_GROUP_COMMAND:New(group,"Mark on map",active,self._MarkTask,self,group,client) - if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then - -- no smoking/flaring here if A2A - local smoke = MENU_GROUP_COMMAND:New(group,"Smoke",active,self._SmokeTask,self,group,client) - local flare = MENU_GROUP_COMMAND:New(group,"Flare",active,self._FlareTask,self,group,client) - end - local abort = MENU_GROUP_COMMAND:New(group,"Abort",active,self._AbortTask,self,group,client) + --- + -- TOPMENU + --- + local taskings = self.gettext:GetEntry("MENUTASKING",self.locale) + local menuname = self.MenuName or self.Name..taskings..self.Type + local topmenu = MENU_GROUP:New(group,menuname,nil) if self.PlayerMenu[playername] then self.PlayerMenu[playername]:RemoveSubMenus() else - self.PlayerMenu[playername] = MENU_GROUP:New(group,"Join Task",topmenu) + self.PlayerMenu[playername] = topmenu end - local tasktypes = self:_GetAvailableTaskTypes() - local taskpertype = self:_GetTasksPerType() - - local ttypes = {} - local taskmenu = {} - for _tasktype,_data in pairs(tasktypes) do - ttypes[_tasktype] = MENU_GROUP:New(group,_tasktype,self.PlayerMenu[playername]) - local tasks = taskpertype[_tasktype] or {} - for _,_task in pairs(tasks) do - _task = _task -- Ops.PlayerTask#PLAYERTASK - local text = string.format("TaskNo %03d",_task.PlayerTaskNr) - if self.UseGroupNames then - local name = _task.Target:GetName() - if name ~= "Unknown" then - text = string.format("%s (%03d)",name,_task.PlayerTaskNr) + --- + -- ACTIVE TASK MENU + --- + if self:_CheckPlayerHasTask(playername) then + + local menuactive = self.gettext:GetEntry("MENUACTIVE",self.locale) + local menuinfo = self.gettext:GetEntry("MENUINFO",self.locale) + local menumark = self.gettext:GetEntry("MENUMARK",self.locale) + local menusmoke = self.gettext:GetEntry("MENUSMOKE",self.locale) + local menuflare = self.gettext:GetEntry("MENUFLARE",self.locale) + local menuabort = self.gettext:GetEntry("MENUABORT",self.locale) + + local active = MENU_GROUP:New(group,menuactive,topmenu) + local info = MENU_GROUP_COMMAND:New(group,menuinfo,active,self._ActiveTaskInfo,self,group,client) + local mark = MENU_GROUP_COMMAND:New(group,menumark,active,self._MarkTask,self,group,client) + if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then + -- no smoking/flaring here if A2A + local smoke = MENU_GROUP_COMMAND:New(group,menusmoke,active,self._SmokeTask,self,group,client) + local flare = MENU_GROUP_COMMAND:New(group,menuflare,active,self._FlareTask,self,group,client) + end + local abort = MENU_GROUP_COMMAND:New(group,menuabort,active,self._AbortTask,self,group,client) + elseif self.TaskQueue:Count() > 0 then + --- + -- JOIN TASK MENU + --- + local tasktypes = self:_GetAvailableTaskTypes() + local taskpertype = self:_GetTasksPerType() + local menujoin = self.gettext:GetEntry("MENUJOIN",self.locale) + local joinmenu = MENU_GROUP:New(group,menujoin,topmenu) + + local ttypes = {} + local taskmenu = {} + for _tasktype,_data in pairs(tasktypes) do + ttypes[_tasktype] = MENU_GROUP:New(group,_tasktype,joinmenu) + local tasks = taskpertype[_tasktype] or {} + for _,_task in pairs(tasks) do + _task = _task -- Ops.PlayerTask#PLAYERTASK + local pilotcount = _task:CountClients() + local newtext = "]" + local tnow = timer.getTime() + -- marker for new tasks + if tnow - _task.timestamp < 60 then + newtext = "*]" end - end - if _task:GetState() == "Planned" or (not _task:HasPlayerName(playername)) then + local menutaskno = self.gettext:GetEntry("MENUTASKNO",self.locale) + local text = string.format("%s %03d [%d%s",menutaskno,_task.PlayerTaskNr,pilotcount,newtext) + if self.UseGroupNames then + local name = _task.Target:GetName() + if name ~= "Unknown" then + text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext) + end + end + --if _task:GetState() == "Planned" or (not _task:HasPlayerName(playername)) then local taskentry = MENU_GROUP_COMMAND:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task) taskentry:SetTag(playername) taskmenu[#taskmenu+1] = taskentry - end + --end + end end + else + -- no tasks (yet) + local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale) + local joinmenu = MENU_GROUP:New(group,menunotasks,topmenu) end + --- + -- REFRESH MENU + --- self.PlayerMenu[playername]:Refresh() end end @@ -1085,6 +2313,225 @@ function PLAYERTASKCONTROLLER:_BuildMenus() return self end +--- [User] Add agent group to INTEL detection +-- @param #PLAYERTASKCONTROLLER self +-- @param Wrapper.Group#GROUP Recce Group of agents. Can also be an @{Ops.OpsGroup#OPSGROUP} object. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:AddAgent(Recce) + self:T(self.lid.."AddAgent: "..Recce:GetName()) + if self.Intel then + self.Intel:AddAgent(Recce) + end + return self +end + +--- [User] Add accept zone to INTEL detection +-- @param #PLAYERTASKCONTROLLER self +-- @param Core.Zone#ZONE AcceptZone Add a zone to the accept zone set. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:AddAcceptZone(AcceptZone) + self:T(self.lid.."AddAcceptZone") + if self.Intel then + self.Intel:AddAcceptZone(AcceptZone) + end + return self +end + +--- [User] Add reject zone to INTEL detection +-- @param #PLAYERTASKCONTROLLER self +-- @param Core.Zone#ZONE RejectZone Add a zone to the reject zone set. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:AddRejectZone(RejectZone) + self:T(self.lid.."AddRejectZone") + if self.Intel then + self.Intel:AddRejectZone(RejectZone) + end + return self +end + +--- [User] Remove accept zone from INTEL detection +-- @param #PLAYERTASKCONTROLLER self +-- @param Core.Zone#ZONE AcceptZone Add a zone to the accept zone set. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:RemoveAcceptZone(AcceptZone) + self:T(self.lid.."RemoveAcceptZone") + if self.Intel then + self.Intel:RemoveAcceptZone(AcceptZone) + end + return self +end + +--- [User] Remove reject zone from INTEL detection +-- @param #PLAYERTASKCONTROLLER self +-- @param Core.Zone#ZONE RejectZone Add a zone to the reject zone set. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:RemoveRejectZone(RejectZone) + self:T(self.lid.."RemoveRejectZone") + if self.Intel then + self.Intel:RemoveRejectZone(RejectZone) + end + return self +end + +--- [User] Set the top menu name to a custom string. +-- @param #PLAYERTASKCONTROLLER self +-- @param #string Name The name to use as the top menu designation. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetMenuName(Name) + self:T(self.lid.."SetMenuName: "..Name) + self.MenuName = Name + return self +end + +--- [User] Set up INTEL detection +-- @param #PLAYERTASKCONTROLLER self +-- @param #string RecceName This name will be used to build a detection group set. All groups with this string somewhere in their group name will be added as Recce. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetupIntel(RecceName) + self:T(self.lid.."SetupIntel: "..RecceName) + self.RecceSet = SET_GROUP:New():FilterCoalitions(self.CoalitionName):FilterPrefixes(RecceName):FilterStart() + self.Intel = INTEL:New(self.RecceSet,self.Coalition,self.Name.."-Intel") + self.Intel:SetClusterAnalysis(true,false,false) + self.Intel:SetClusterRadius(self.ClusterRadius or 500) + self.Intel.statusupdate = 25 + self.Intel:SetAcceptZones() + self.Intel:SetRejectZones() + --if self.verbose then + --self.Intel:SetDetectionTypes(true,true,false,true,true,true) + --end + if self.Type == PLAYERTASKCONTROLLER.Type.A2G or self.Type == PLAYERTASKCONTROLLER.Type.A2GS then + self.Intel:SetDetectStatics(true) + end + self.Intel:__Start(2) + + local function NewCluster(Cluster) + if not self.usecluster then return self end + local cluster = Cluster -- Ops.Intelligence#INTEL.Cluster + local type = cluster.ctype + self:T({type,self.Type}) + if (type == INTEL.Ctype.AIRCRAFT and self.Type == PLAYERTASKCONTROLLER.Type.A2A) or (type == INTEL.Ctype.NAVAL and (self.Type == PLAYERTASKCONTROLLER.Type.A2S or self.Type == PLAYERTASKCONTROLLER.Type.A2GS)) then + self:T("A2A or A2S") + local contacts = cluster.Contacts -- #table of GROUP + local targetset = SET_GROUP:New() + for _,_object in pairs(contacts) do + local contact = _object -- Ops.Intelligence#INTEL.Contact + self:T("Adding group: "..contact.groupname) + targetset:AddGroup(contact.group,true) + end + self:AddTarget(targetset) + elseif (type == INTEL.Ctype.GROUND or type == INTEL.Ctype.STRUCTURE) and (self.Type == PLAYERTASKCONTROLLER.Type.A2G or self.Type == PLAYERTASKCONTROLLER.Type.A2GS) then + self:T("A2G") + local contacts = cluster.Contacts -- #table of GROUP or STATIC + local targetset = nil -- Core.Set#SET_BASE + if type == INTEL.Ctype.GROUND then + targetset = SET_GROUP:New() + for _,_object in pairs(contacts) do + local contact = _object -- Ops.Intelligence#INTEL.Contact + self:T("Adding group: "..contact.groupname) + targetset:AddGroup(contact.group,true) + end + elseif type == INTEL.Ctype.STRUCTURE then + targetset = SET_STATIC:New() + for _,_object in pairs(contacts) do + local contact = _object -- Ops.Intelligence#INTEL.Contact + self:T("Adding static: "..contact.groupname) + targetset:AddStatic(contact.group) + end + end + if targetset then + self:AddTarget(targetset) + end + end + end + + local function NewContact(Contact) + if self.usecluster then return self end + local contact = Contact -- Ops.Intelligence#INTEL.Contact + local type = contact.ctype + self:T({type,self.Type}) + if (type == INTEL.Ctype.AIRCRAFT and self.Type == PLAYERTASKCONTROLLER.Type.A2A) or (type == INTEL.Ctype.NAVAL and (self.Type == PLAYERTASKCONTROLLER.Type.A2S or self.Type == PLAYERTASKCONTROLLER.Type.A2GS)) then + self:T("A2A or A2S") + self:T("Adding group: "..contact.groupname) + self:AddTarget(contact.group) + elseif (type == INTEL.Ctype.GROUND or type == INTEL.Ctype.STRUCTURE) and (self.Type == PLAYERTASKCONTROLLER.Type.A2G or self.Type == PLAYERTASKCONTROLLER.Type.A2GS) then + self:T("A2G") + self:T("Adding group: "..contact.groupname) + self:AddTarget(contact.group) + end + end + + function self.Intel:OnAfterNewCluster(From,Event,To,Cluster) + NewCluster(Cluster) + end + + function self.Intel:OnAfterNewContact(From,Event,To,Contact) + NewContact(Contact) + end + + return self +end + +--- [User] Set SRS TTS details - see @{Sound.SRS} for details +-- @param #PLAYERTASKCONTROLLER self +-- @param #number Frequency Frequency to be used. Can also be given as a table of multiple frequencies, e.g. 271 or {127,251}. There needs to be exactly the same number of modulations! +-- @param #number Modulation Modulation to be used. Can also be given as a table of multiple modulations, e.g. radio.modulation.AM or {radio.modulation.FM,radio.modulation.AM}. There needs to be exactly the same number of frequencies! +-- @param #string PathToSRS Defaults to "C:\\Program Files\\DCS-SimpleRadio-Standalone" +-- @param #string Gender (Optional) Defaults to "male" +-- @param #string Culture (Optional) Defaults to "en-US" +-- @param #number Port (Optional) Defaults to 5002 +-- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. +-- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. +-- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest) +-- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey) + self:T(self.lid.."SetSRS") + self.PathToSRS = PathToSRS or "C:\\Program Files\\DCS-SimpleRadio-Standalone" -- + self.Gender = Gender or "male" -- + self.Culture = Culture or "en-US" -- + self.Port = Port or 5002 -- + self.Voice = Voice -- + self.PathToGoogleKey = PathToGoogleKey -- + self.Volume = Volume or 1.0 -- + self.UseSRS = true + self.Frequency = Frequency or {127,251} -- + self.BCFrequency = self.Frequency + self.Modulation = Modulation or {radio.modulation.FM,radio.modulation.AM} -- + self.BCModulation = self.Modulation + -- set up SRS + self.SRS=MSRS:New(self.PathToSRS,self.Frequency,self.Modulation,self.Volume) + self.SRS:SetCoalition(self.Coalition) + self.SRS:SetLabel(self.MenuName or self.Name) + self.SRS:SetGender(self.Gender) + self.SRS:SetCulture(self.Culture) + self.SRS:SetPort(self.Port) + self.SRS:SetVoice(self.Voice) + if self.PathToGoogleKey then + self.SRS:SetGoogle(self.PathToGoogleKey) + end + self.SRSQueue = MSRSQUEUE:New(self.MenuName or self.Name) + return self +end + +--- [User] Set SRS Broadcast - for the announcement to joining players which SRS frequency, modulation to use. Use in case you want to set this differently to the standard SRS. +-- @param #PLAYERTASKCONTROLLER self +-- @param #number Frequency Frequency to be used. Can also be given as a table of multiple frequencies, e.g. 271 or {127,251}. There needs to be exactly the same number of modulations! +-- @param #number Modulation Modulation to be used. Can also be given as a table of multiple modulations, e.g. radio.modulation.AM or {radio.modulation.FM,radio.modulation.AM}. There needs to be exactly the same number of frequencies! +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetSRSBroadcast(Frequency,Modulation) + self:T(self.lid.."SetSRSBroadcast") + if self.SRS then + self.BCFrequency = Frequency + self.BCModulation = Modulation + end + return self +end + +------------------------------------------------------------------------------------------------------------------- +-- FSM Functions PLAYERTASKCONTROLLER +-- TODO: FSM Functions PLAYERTASKCONTROLLER +------------------------------------------------------------------------------------------------------------------- + --- [Internal] On after Status call -- @param #PLAYERTASKCONTROLLER self -- @param #string From @@ -1095,14 +2542,16 @@ function PLAYERTASKCONTROLLER:onafterStatus(From, Event, To) self:I({From, Event, To}) self:_CheckTargetQueue() self:_CheckTaskQueue() + self:_CheckPrecisionTasks() self:_BuildMenus() local targetcount = self.TargetQueue:Count() local taskcount = self.TaskQueue:Count() local playercount = self.ClientSet:CountAlive() + local assignedtasks = self.TasksPerPlayer:Count() if self.verbose then - local text = string.format("New Targets: %02d | Active Tasks: %02d | Active Players: %02d",targetcount,taskcount,playercount) + local text = string.format("New Targets: %02d | Active Tasks: %02d | Active Players: %02d | Assigned Tasks: %02d",targetcount,taskcount,playercount,assignedtasks) self:I(text) end @@ -1120,8 +2569,8 @@ end -- @param Ops.PlayerTask#PLAYERTASK Task -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:onafterTaskDone(From, Event, To, Task) - self:I({From, Event, To}) - self:I(self.lid.."TaskDone") + self:T({From, Event, To}) + self:T(self.lid.."TaskDone") return self end @@ -1133,8 +2582,18 @@ end -- @param Ops.PlayerTask#PLAYERTASK Task -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:onafterTaskCancelled(From, Event, To, Task) - self:I({From, Event, To}) - self:I(self.lid.."TaskCancelled") + self:T({From, Event, To}) + self:T(self.lid.."TaskCancelled") + local canceltxt = self.gettext:GetEntry("TASKCANCELLED",self.locale) + local canceltxttts = self.gettext:GetEntry("TASKCANCELLEDTTS",self.locale) + local taskname = string.format(canceltxt, Task.PlayerTaskNr, tostring(Task.Type)) + if not self.NoScreenOutput then + local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + end + if self.UseSRS then + taskname = string.format(canceltxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType)) + self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2) + end return self end @@ -1146,10 +2605,18 @@ end -- @param Ops.PlayerTask#PLAYERTASK Task -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:onafterTaskSuccess(From, Event, To, Task) - self:I({From, Event, To}) - self:I(self.lid.."TaskSuccess") - local taskname = string.format("Task #%d %s Success!", Task.PlayerTaskNr, tostring(Task.Type)) - local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + self:T({From, Event, To}) + self:T(self.lid.."TaskSuccess") + local succtxt = self.gettext:GetEntry("TASKSUCCESS",self.locale) + local succtxttts = self.gettext:GetEntry("TASKSUCCESSTTS",self.locale) + local taskname = string.format(succtxt, Task.PlayerTaskNr, tostring(Task.Type)) + if not self.NoScreenOutput then + local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + end + if self.UseSRS then + taskname = string.format(succtxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType)) + self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2) + end return self end @@ -1161,10 +2628,18 @@ end -- @param Ops.PlayerTask#PLAYERTASK Task -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:onafterTaskFailed(From, Event, To, Task) - self:I({From, Event, To}) - self:I(self.lid.."TaskFailed") - local taskname = string.format("Task #%d %s Failed!", Task.PlayerTaskNr, tostring(Task.Type)) - local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + self:T({From, Event, To}) + self:T(self.lid.."TaskFailed") + local failtxt = self.gettext:GetEntry("TASKFAILED",self.locale) + local failtxttts = self.gettext:GetEntry("TASKFAILEDTTS",self.locale) + local taskname = string.format(failtxt, Task.PlayerTaskNr, tostring(Task.Type)) + if not self.NoScreenOutput then + local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + end + if self.UseSRS then + taskname = string.format(failtxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType)) + self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2) + end return self end @@ -1176,10 +2651,18 @@ end -- @param Ops.PlayerTask#PLAYERTASK Task -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:onafterTaskRepeatOnFailed(From, Event, To, Task) - self:I({From, Event, To}) - self:I(self.lid.."RepeatOnFailed") - local taskname = string.format("Task #%d %s Failed! Replanning!", Task.PlayerTaskNr, tostring(Task.Type)) - local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + self:T({From, Event, To}) + self:T(self.lid.."RepeatOnFailed") + local repfailtxt = self.gettext:GetEntry("TASKFAILEDREPLAN",self.locale) + local repfailtxttts = self.gettext:GetEntry("TASKFAILEDREPLANTTS",self.locale) + local taskname = string.format(repfailtxt, Task.PlayerTaskNr, tostring(Task.Type)) + if not self.NoScreenOutput then + local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + end + if self.UseSRS then + taskname = string.format(repfailtxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType)) + self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2) + end return self end @@ -1191,27 +2674,39 @@ end -- @param Ops.PlayerTask#PLAYERTASK Task -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:onafterTaskAdded(From, Event, To, Task) - self:I({From, Event, To}) - self:I(self.lid.."TaskAdded") - local taskname = string.format("%s has a new Task %s", self.Name, tostring(Task.Type)) - local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + self:T({From, Event, To}) + self:T(self.lid.."TaskAdded") + local addtxt = self.gettext:GetEntry("TASKADDED",self.locale) + local taskname = string.format(addtxt, self.MenuName or self.Name, tostring(Task.Type)) + if not self.NoScreenOutput then + local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + end + if self.UseSRS then + taskname = string.format(addtxt, self.MenuName or self.Name, tostring(Task.TTSType)) + self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2) + end return self end --- [Internal] On after Stop call -- @param #PLAYERTASKCONTROLLER self -- @param #string From --- @param #string Event +-- @param #string Event -- @param #string To -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:onafterStop(From, Event, To) - self:I({From, Event, To}) - self:I(self.lid.."Stopped.") + self:T({From, Event, To}) + self:T(self.lid.."Stopped.") -- Player leaves self:UnHandleEvent(EVENTS.PlayerLeaveUnit) self:UnHandleEvent(EVENTS.Ejection) self:UnHandleEvent(EVENTS.Crash) self:UnHandleEvent(EVENTS.PilotDead) + self:UnHandleEvent(EVENTS.PlayerEnterAircraft) return self end +------- +-- END PLAYERTASKCONTROLLER +----- +end diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 650aaea3a..5b832317e 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1725,10 +1725,16 @@ end -- @return #number Os time in seconds. function UTILS.GetOSTime() if os then - return os.clock() + local ts = 0 + local t = os.date("*t") + local s = t.sec + local m = t.min * 60 + local h = t.hour * 3600 + ts = s+m+h + return ts + else + return nil end - - return nil end --- Shuffle a table accoring to Fisher Yeates algorithm diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 157715061..57035a324 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -500,6 +500,9 @@ AIRBASE.MarianaIslands={ -- * AIRBASE.SouthAtlantic.Punta_Arenas -- * AIRBASE.SouthAtlantic.Pampa_Guanaco -- * AIRBASE.SouthAtlantic.San_Julian +-- * AIRBASE.SouthAtlantic.Puerto_Williams +-- * AIRBASE.SouthAtlantic.Puerto_Natales +-- * AIRBASE.SouthAtlantic.El_Calafate -- --@field MarianaIslands AIRBASE.SouthAtlantic={ @@ -513,6 +516,9 @@ AIRBASE.SouthAtlantic={ ["Punta_Arenas"]="Punta Arenas", ["Pampa_Guanaco"]="Pampa Guanaco", ["San_Julian"]="San Julian", + ["Puerto_Williams"]="Puerto Williams", + ["Puerto_Natales"]="Puerto Natales", + ["El_Calafate"]="El Calafate", } --- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".