mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Merge branch 'develop' into FF/Ops
This commit is contained in:
commit
efe7673c4c
@ -315,7 +315,7 @@ do -- SETTINGS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if imperial.
|
||||
function SETTINGS:IsImperial()
|
||||
return (self.Metric ~= nil and self.Metric == false) or (self.Metric == nil and _SETTINGS:IsMetric())
|
||||
return (self.Metric ~= nil and self.Metric == false) or (self.Metric == nil and _SETTINGS:IsImperial())
|
||||
end
|
||||
|
||||
--- Sets the SETTINGS LL accuracy.
|
||||
|
||||
@ -1418,7 +1418,7 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
||||
end
|
||||
-- TODO: Need to fix this by putting an "R" in the name of the group when the group repeats.
|
||||
-- if self.Repeat then
|
||||
-- _DATABASE:SetStatusGroup( SpawnTemplate.name, "ReSpawn" )
|
||||
-- _DATABASE:SetStatusGroup( SpawnTemplate.name, "ReSpawn" )
|
||||
-- end
|
||||
end
|
||||
|
||||
@ -2971,9 +2971,9 @@ end
|
||||
function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
|
||||
self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } )
|
||||
|
||||
-- if not self.SpawnTemplate then
|
||||
-- self.SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix )
|
||||
-- end
|
||||
-- if not self.SpawnTemplate then
|
||||
-- self.SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix )
|
||||
-- end
|
||||
|
||||
local SpawnTemplate
|
||||
if self.TweakedTemplate ~= nil and self.TweakedTemplate == true then
|
||||
@ -3254,20 +3254,20 @@ function SPAWN:_OnDeadOrCrash( EventData )
|
||||
|
||||
local unit=UNIT:FindByName(EventData.IniUnitName)
|
||||
|
||||
if unit then
|
||||
|
||||
local EventPrefix = self:_GetPrefixFromGroupName(unit.GroupName)
|
||||
|
||||
if unit then
|
||||
|
||||
local EventPrefix = self:_GetPrefixFromGroupName(unit.GroupName)
|
||||
|
||||
if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group!
|
||||
self:T( { "Dead event: " .. EventPrefix } )
|
||||
|
||||
if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then
|
||||
|
||||
self.AliveUnits = self.AliveUnits - 1
|
||||
|
||||
self:T( "Alive Units: " .. self.AliveUnits )
|
||||
end
|
||||
|
||||
self.AliveUnits = self.AliveUnits - 1
|
||||
|
||||
self:T( "Alive Units: " .. self.AliveUnits )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -3365,12 +3365,16 @@ end
|
||||
-- @return #boolean True = Continue Scheduler
|
||||
function SPAWN:_SpawnCleanUpScheduler()
|
||||
self:F( { "CleanUp Scheduler:", self.SpawnTemplatePrefix } )
|
||||
|
||||
|
||||
local SpawnGroup, SpawnCursor = self:GetFirstAliveGroup()
|
||||
self:T( { "CleanUp Scheduler:", SpawnGroup, SpawnCursor } )
|
||||
|
||||
local IsHelo = false
|
||||
|
||||
while SpawnGroup do
|
||||
|
||||
|
||||
IsHelo = SpawnGroup:IsHelicopter()
|
||||
|
||||
local SpawnUnits = SpawnGroup:GetUnits()
|
||||
|
||||
for UnitID, UnitData in pairs( SpawnUnits ) do
|
||||
@ -3383,8 +3387,8 @@ function SPAWN:_SpawnCleanUpScheduler()
|
||||
self:T( { SpawnUnitName, Stamp } )
|
||||
|
||||
if Stamp.Vec2 then
|
||||
if SpawnUnit:InAir() == false and SpawnUnit:GetVelocityKMH() < 1 then
|
||||
local NewVec2 = SpawnUnit:GetVec2()
|
||||
if (SpawnUnit:InAir() == false and SpawnUnit:GetVelocityKMH() < 1) or IsHelo then
|
||||
local NewVec2 = SpawnUnit:GetVec2() or {x=0, y=0}
|
||||
if (Stamp.Vec2.x == NewVec2.x and Stamp.Vec2.y == NewVec2.y) or (SpawnUnit:GetLife() <= 1) then
|
||||
-- If the plane is not moving or dead , and is on the ground, assign it with a timestamp...
|
||||
if Stamp.Time + self.SpawnCleanUpInterval < timer.getTime() then
|
||||
@ -3402,8 +3406,8 @@ function SPAWN:_SpawnCleanUpScheduler()
|
||||
Stamp.Time = nil
|
||||
end
|
||||
else
|
||||
if SpawnUnit:InAir() == false then
|
||||
Stamp.Vec2 = SpawnUnit:GetVec2()
|
||||
if SpawnUnit:InAir() == false or (IsHelo and SpawnUnit:GetLife() <= 1) then
|
||||
Stamp.Vec2 = SpawnUnit:GetVec2() or {x=0, y=0}
|
||||
if (SpawnUnit:GetVelocityKMH() < 1) then
|
||||
Stamp.Time = timer.getTime()
|
||||
end
|
||||
|
||||
@ -111,7 +111,7 @@ AUTOLASE = {
|
||||
|
||||
--- AUTOLASE class version.
|
||||
-- @field #string version
|
||||
AUTOLASE.version = "0.1.20"
|
||||
AUTOLASE.version = "0.1.21"
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Begin Functional.Autolase.lua
|
||||
@ -430,6 +430,7 @@ function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Cultu
|
||||
self.SRS:SetCulture(self.Culture)
|
||||
self.SRS:SetPort(self.Port)
|
||||
self.SRS:SetVoice(self.Voice)
|
||||
self.SRS:SetCoalition(self.coalition)
|
||||
if self.PathToGoogleKey then
|
||||
self.SRS:SetGoogle(self.PathToGoogleKey)
|
||||
end
|
||||
@ -672,10 +673,13 @@ function AUTOLASE:ShowStatus(Group,Unit)
|
||||
if playername then
|
||||
local settings = _DATABASE:GetPlayerSettings(playername)
|
||||
if settings then
|
||||
--self:I("Get Settings ok!")
|
||||
if settings:IsA2G_MGRS() then
|
||||
locationstring = entry.coordinate:ToStringMGRS(settings)
|
||||
elseif settings:IsA2G_LL_DMS() then
|
||||
locationstring = entry.coordinate:ToStringLLDMS()
|
||||
locationstring = entry.coordinate:ToStringLLDMS(settings)
|
||||
elseif settings:IsA2G_BR() then
|
||||
locationstring = entry.coordinate:ToStringBR(Group:GetCoordinate() or Unit:GetCoordinate(),settings)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -948,7 +952,9 @@ function AUTOLASE:onafterMonitor(From, Event, To)
|
||||
settings.MGRS_Accuracy = precision
|
||||
locationstring = unit:GetCoordinate():ToStringMGRS(settings)
|
||||
elseif _SETTINGS:IsA2G_LL_DMS() then
|
||||
locationstring = unit:GetCoordinate():ToStringLLDMS()
|
||||
locationstring = unit:GetCoordinate():ToStringLLDMS(_SETTINGS)
|
||||
elseif _SETTINGS:IsA2G_BR() then
|
||||
locationstring = unit:GetCoordinate():ToStringBULLS(self.coalition,_SETTINGS)
|
||||
end
|
||||
|
||||
local laserspot = { -- #AUTOLASE.LaserSpot
|
||||
|
||||
@ -213,7 +213,7 @@ do -- DESIGNATE
|
||||
-- In order to prevent an overflow of designations due to many Detected Targets, there is a
|
||||
-- Maximum Designations scope that is set in the DesignationObject.
|
||||
--
|
||||
-- The method @{#DESIGNATE.SetMaximumDesignations}() will put a limit on the amount of designations put in scope of the DesignationObject.
|
||||
-- The method @{#DESIGNATE.SetMaximumDesignations}() will put a limit on the amount of designations (target groups) put in scope of the DesignationObject.
|
||||
-- Using the menu system, the player can "forget" a designation, so that gradually a new designation can be put in scope when detected.
|
||||
--
|
||||
-- # 4. Laser codes
|
||||
@ -562,7 +562,8 @@ do -- DESIGNATE
|
||||
end
|
||||
|
||||
|
||||
--- Set the maximum amount of designations.
|
||||
--- Set the maximum amount of designations (target groups). This will put a limit on the amount of designations in scope.
|
||||
-- Using the menu system, the player can "forget" a designation, so that gradually a new designation can be put in scope when detected.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param #number MaximumDesignations
|
||||
-- @return #DESIGNATE
|
||||
@ -602,7 +603,7 @@ do -- DESIGNATE
|
||||
end
|
||||
|
||||
|
||||
--- Set the maximum amount of markings FACs will do, per designated target group.
|
||||
--- Set the maximum amount of markings FACs will do, per designated target group. This will limit the number of parallelly marked units of a target group.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param #number MaximumMarkings Maximum markings FACs will do, per designated target group.
|
||||
-- @return #DESIGNATE
|
||||
@ -911,8 +912,8 @@ do -- DESIGNATE
|
||||
for DesignateIndex, Designating in pairs( self.Designating ) do
|
||||
local DetectedItem = DetectedItems[DesignateIndex]
|
||||
if DetectedItem then
|
||||
local Report = self.Detection:DetectedItemReportSummary( DetectedItem, AttackGroup ):Text( ", " )
|
||||
DetectedReport:Add( string.rep( "-", 140 ) )
|
||||
local Report = self.Detection:DetectedItemReportSummary( DetectedItem, AttackGroup, nil, true ):Text( ", " )
|
||||
DetectedReport:Add( string.rep( "-", 40 ) )
|
||||
DetectedReport:Add( " - " .. Report )
|
||||
if string.find( Designating, "L" ) then
|
||||
DetectedReport:Add( " - " .. "Lasing Targets" )
|
||||
@ -1192,8 +1193,8 @@ do -- DESIGNATE
|
||||
|
||||
local MarkingCount = 0
|
||||
local MarkedTypes = {}
|
||||
local ReportTypes = REPORT:New()
|
||||
local ReportLaserCodes = REPORT:New()
|
||||
--local ReportTypes = REPORT:New()
|
||||
--local ReportLaserCodes = REPORT:New()
|
||||
|
||||
TargetSetUnit:Flush( self )
|
||||
|
||||
@ -1243,8 +1244,8 @@ do -- DESIGNATE
|
||||
if not Recce then
|
||||
|
||||
self:F( "Lasing..." )
|
||||
self.RecceSet:Flush( self)
|
||||
|
||||
--self.RecceSet:Flush( self)
|
||||
|
||||
for RecceGroupID, RecceGroup in pairs( self.RecceSet:GetSet() ) do
|
||||
for UnitID, UnitData in pairs( RecceGroup:GetUnits() or {} ) do
|
||||
|
||||
@ -1282,13 +1283,13 @@ do -- DESIGNATE
|
||||
-- OK. We have assigned for the Recce a TargetUnit. We can exit the function.
|
||||
MarkingCount = MarkingCount + 1
|
||||
local TargetUnitType = TargetUnit:GetTypeName()
|
||||
--RecceUnit:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.",
|
||||
-- 5, self.AttackSet, DesignateName )
|
||||
RecceUnit:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.",
|
||||
10, self.AttackSet, DesignateName )
|
||||
if not MarkedTypes[TargetUnitType] then
|
||||
MarkedTypes[TargetUnitType] = true
|
||||
ReportTypes:Add(TargetUnitType)
|
||||
--ReportTypes:Add(TargetUnitType)
|
||||
end
|
||||
ReportLaserCodes:Add(RecceUnit.LaserCode)
|
||||
--ReportLaserCodes:Add(RecceUnit.LaserCode)
|
||||
return
|
||||
end
|
||||
else
|
||||
@ -1303,16 +1304,16 @@ do -- DESIGNATE
|
||||
|
||||
if Recce then
|
||||
Recce:LaseOff()
|
||||
Recce:MessageToSetGroup( "Target " .. TargetUnit:GetTypeName() "out of LOS. Cancelling lase!", 5, self.AttackSet, self.DesignateName )
|
||||
Recce:MessageToSetGroup( "Target " .. TargetUnit:GetTypeName() "out of LOS. Cancelling lase!", 10, self.AttackSet, self.DesignateName )
|
||||
end
|
||||
else
|
||||
--MarkingCount = MarkingCount + 1
|
||||
local TargetUnitType = TargetUnit:GetTypeName()
|
||||
if not MarkedTypes[TargetUnitType] then
|
||||
MarkedTypes[TargetUnitType] = true
|
||||
ReportTypes:Add(TargetUnitType)
|
||||
--ReportTypes:Add(TargetUnitType)
|
||||
end
|
||||
ReportLaserCodes:Add(RecceUnit.LaserCode)
|
||||
--ReportLaserCodes:Add(RecceUnit.LaserCode)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1322,19 +1323,19 @@ do -- DESIGNATE
|
||||
local TargetUnitType = TargetUnit:GetTypeName()
|
||||
if not MarkedTypes[TargetUnitType] then
|
||||
MarkedTypes[TargetUnitType] = true
|
||||
ReportTypes:Add(TargetUnitType)
|
||||
--ReportTypes:Add(TargetUnitType)
|
||||
end
|
||||
ReportLaserCodes:Add(Recce.LaserCode)
|
||||
--Recce:MessageToSetGroup( self.DesignateName .. ": Marking " .. TargetUnit:GetTypeName() .. " with laser " .. Recce.LaserCode .. ".", 5, self.AttackSet )
|
||||
--ReportLaserCodes:Add(Recce.LaserCode)
|
||||
Recce:MessageToSetGroup( self.DesignateName .. ": Marking " .. TargetUnit:GetTypeName() .. " with laser " .. Recce.LaserCode .. ".", 10, self.AttackSet )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
local MarkedTypesText = ReportTypes:Text(', ')
|
||||
local MarkedLaserCodesText = ReportLaserCodes:Text(', ')
|
||||
self.CC:GetPositionable():MessageToSetGroup( "Marking " .. MarkingCount .. " x " .. MarkedTypesText .. ", code " .. MarkedLaserCodesText .. ".", 5, self.AttackSet, self.DesignateName )
|
||||
--local MarkedTypesText = ReportTypes:Text(', ')
|
||||
--local MarkedLaserCodesText = ReportLaserCodes:Text(', ')
|
||||
--self.CC:GetPositionable():MessageToSetGroup( "Marking " .. MarkingCount .. " x " .. MarkedTypesText .. ", code " .. MarkedLaserCodesText .. ".", 5, self.AttackSet, self.DesignateName )
|
||||
|
||||
self:__Lasing( -self.LaseDuration, Index, Duration, LaserCodeRequested )
|
||||
|
||||
|
||||
@ -2152,8 +2152,9 @@ do -- DETECTION_UNITS
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
||||
-- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for.
|
||||
-- @param Core.Settings#SETTINGS Settings Message formatting settings to use.
|
||||
-- @param #boolean ForceA2GCoordinate Set creation of A2G coordinate
|
||||
-- @return Core.Report#REPORT The report of the detection items.
|
||||
function DETECTION_UNITS:DetectedItemReportSummary( DetectedItem, AttackGroup, Settings )
|
||||
function DETECTION_UNITS:DetectedItemReportSummary( DetectedItem, AttackGroup, Settings, ForceA2GCoordinate )
|
||||
self:F( { DetectedItem = DetectedItem } )
|
||||
|
||||
local DetectedItemID = self:GetDetectedItemID( DetectedItem )
|
||||
@ -2188,7 +2189,11 @@ do -- DETECTION_UNITS
|
||||
-- TODO: solve Index reference
|
||||
local DetectedItemCoordinate = self:GetDetectedItemCoordinate( DetectedItem )
|
||||
local DetectedItemCoordText = DetectedItemCoordinate:ToString( AttackGroup, Settings )
|
||||
|
||||
|
||||
if ForceA2GCoordinate then
|
||||
DetectedItemCoordText = DetectedItemCoordinate:ToStringA2G(AttackGroup,Settings)
|
||||
end
|
||||
|
||||
local ThreatLevelA2G = self:GetDetectedItemThreatLevel( DetectedItem )
|
||||
|
||||
local Report = REPORT:New()
|
||||
|
||||
@ -492,7 +492,6 @@ end
|
||||
|
||||
--- Disable F10 menu for all players.
|
||||
-- @param #FOX self
|
||||
-- @param #boolean switch If true debug mode on. If false/nil debug mode off
|
||||
-- @return #FOX self
|
||||
function FOX:SetDisableF10Menu()
|
||||
|
||||
@ -501,6 +500,16 @@ function FOX:SetDisableF10Menu()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Enable F10 menu for all players.
|
||||
-- @param #FOX self
|
||||
-- @return #FOX self
|
||||
function FOX:SetEnableF10Menu()
|
||||
|
||||
self.menudisabled=false
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set default player setting for missile destruction.
|
||||
-- @param #FOX self
|
||||
-- @param #boolean switch If true missiles are destroyed. If false/nil missiles are not destroyed.
|
||||
@ -1162,7 +1171,7 @@ function FOX:OnEventBirth(EventData)
|
||||
|
||||
-- Add F10 radio menu for player.
|
||||
if not self.menudisabled then
|
||||
self:ScheduleOnce(0.1, FOX._AddF10Commands, self, _unitname)
|
||||
self:ScheduleOnce(0.1, self._AddF10Commands, self, _unitName)
|
||||
end
|
||||
|
||||
-- Player data.
|
||||
@ -1429,10 +1438,10 @@ function FOX:_AddF10Commands(_unitName)
|
||||
|
||||
end
|
||||
else
|
||||
self:E(self.lid..string.format("ERROR: Could not find group or group ID in AddF10Menu() function. Unit name: %s.", _unitName))
|
||||
self:E(self.lid..string.format("ERROR: Could not find group or group ID in AddF10Menu() function. Unit name: %s.", _unitName or "unknown"))
|
||||
end
|
||||
else
|
||||
self:E(self.lid..string.format("ERROR: Player unit does not exist in AddF10Menu() function. Unit name: %s.", _unitName))
|
||||
self:E(self.lid..string.format("ERROR: Player unit does not exist in AddF10Menu() function. Unit name: %s.", _unitName or "unknown"))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -45,7 +45,8 @@
|
||||
-- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)**
|
||||
--
|
||||
-- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536), [Ciribob](https://forums.eagle.ru/member.php?u=112175)
|
||||
--
|
||||
-- ### SRS Additions: Applevangelist
|
||||
--
|
||||
-- ===
|
||||
-- @module Functional.Range
|
||||
-- @image Range.JPG
|
||||
@ -101,6 +102,10 @@
|
||||
-- @field #boolean targetsheet If true, players can save their target sheets. Rangeboss will not work if targetsheets do not save.
|
||||
-- @field #string targetpath Path where to save the target sheets.
|
||||
-- @field #string targetprefix File prefix for target sheet files.
|
||||
-- @field Sound.SRS#MSRS controlmsrs
|
||||
-- @field Sound.SRS#MSRSQUEUE controlsrsQ
|
||||
-- @field Sound.SRS#MSRS instructmsrs
|
||||
-- @field Sound.SRS#MSRSQUEUE instructsrsQ
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven
|
||||
@ -225,6 +230,11 @@
|
||||
--
|
||||
-- By default, the sound files are placed in the "Range Soundfiles/" folder inside the mission (.miz) file. Another folder can be specified via the @{#RANGE.SetSoundfilesPath}(*path*) function.
|
||||
--
|
||||
-- ## Voice output via SRS
|
||||
--
|
||||
-- Alternatively, the voice output can be fully done via SRS, **no sound file additions needed**. Set up SRS with @{#RANGE.SetSRS}(). Range control and instructor frequencies and voices can then be
|
||||
-- set via @{#RANGE.SetSRSRangeControl}() and @{#RANGE.SetSRSRangeInstructor}()
|
||||
--
|
||||
-- # Persistence
|
||||
--
|
||||
-- To automatically save bombing results to disk, use the @{#RANGE.SetAutosave}() function. Bombing results will be saved as csv file in your "Saved Games\DCS.openbeta\Logs" directory.
|
||||
@ -568,7 +578,7 @@ RANGE.MenuF10Root = nil
|
||||
|
||||
--- Range script version.
|
||||
-- @field #string version
|
||||
RANGE.version = "2.4.0"
|
||||
RANGE.version = "2.5.0"
|
||||
|
||||
-- TODO list:
|
||||
-- TODO: Verbosity level for messages.
|
||||
@ -833,7 +843,7 @@ function RANGE:onafterStart()
|
||||
end
|
||||
|
||||
-- Init range control.
|
||||
if self.rangecontrolfreq then
|
||||
if self.rangecontrolfreq and not self.useSRS then
|
||||
|
||||
-- Radio queue.
|
||||
self.rangecontrol = RADIOQUEUE:New( self.rangecontrolfreq, nil, self.rangename )
|
||||
@ -859,7 +869,7 @@ function RANGE:onafterStart()
|
||||
self.rangecontrol:Start( 1, 0.1 )
|
||||
|
||||
-- Init range control.
|
||||
if self.instructorfreq then
|
||||
if self.instructorfreq and not self.useSRS then
|
||||
|
||||
-- Radio queue.
|
||||
self.instructor = RADIOQUEUE:New( self.instructorfreq, nil, self.rangename )
|
||||
@ -1177,7 +1187,91 @@ function RANGE:TrackMissilesOFF()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Enable range control and set frequency.
|
||||
--- Use SRS Simple-Text-To-Speech for transmissions. No sound files necessary.
|
||||
-- @param #RANGE self
|
||||
-- @param #string PathToSRS Path to SRS directory.
|
||||
-- @param #number Port SRS port. Default 5002.
|
||||
-- @param #number Coalition Coalition side, e.g. coalition.side.BLUE or coalition.side.RED
|
||||
-- @param #number Frequency Frequency to use, defaults to 256 (same as rangecontrol)
|
||||
-- @param #number Modulation Modulation to use, defaults to radio.modulation.AM
|
||||
-- @param #number Volume Volume, between 0.0 and 1.0. Defaults to 1.0
|
||||
-- @param #string PathToGoogleKey Path to Google TTS credentials.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey)
|
||||
if PathToSRS then
|
||||
|
||||
self.useSRS=true
|
||||
|
||||
self.controlmsrs=MSRS:New(PathToSRS, Frequency or 256, Modulation or radio.modulation.AM, Volume or 1.0)
|
||||
self.controlmsrs:SetPort(Port)
|
||||
self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE)
|
||||
self.controlmsrs:SetLabel("RANGEC")
|
||||
self.controlsrsQ = MSRSQUEUE:New("CONTROL")
|
||||
|
||||
self.instructmsrs=MSRS:New(PathToSRS, Frequency or 305, Modulation or radio.modulation.AM, Volume or 1.0)
|
||||
self.instructmsrs:SetPort(Port)
|
||||
self.instructmsrs:SetCoalition(Coalition or coalition.side.BLUE)
|
||||
self.instructmsrs:SetLabel("RANGEI")
|
||||
self.instructsrsQ = MSRSQUEUE:New("INSTRUCT")
|
||||
|
||||
else
|
||||
self:E(self.lid..string.format("ERROR: No SRS path specified!"))
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- (SRS) Set range control frequency and voice.
|
||||
-- @param #RANGE self
|
||||
-- @param #number frequency Frequency in MHz. Default 256 MHz.
|
||||
-- @param #number modulation Modulation, defaults to radio.modulation.AM.
|
||||
-- @param #string voice Voice.
|
||||
-- @param #string culture Culture, defaults to "en-US".
|
||||
-- @param #string gender Gender, defaults to "female".
|
||||
-- @param #string relayunitname Name of the unit used for transmission location.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetSRSRangeControl( frequency, modulation, voice, culture, gender, relayunitname )
|
||||
self.rangecontrolfreq = frequency or 256
|
||||
self.controlmsrs:SetFrequencies(self.rangecontrolfreq)
|
||||
self.controlmsrs:SetModulations(modulation or radio.modulation.AM)
|
||||
self.controlmsrs:SetVoice(voice)
|
||||
self.controlmsrs:SetCulture(culture or "en-US")
|
||||
self.controlmsrs:SetGender(gender or "female")
|
||||
self.rangecontrol = true
|
||||
if relayunitname then
|
||||
local unit = UNIT:FindByName(relayunitname)
|
||||
local Coordinate = unit:GetCoordinate()
|
||||
self.rangecontrolrelayname = relayunitname
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- (SRS) Set range instructor frequency and voice.
|
||||
-- @param #RANGE self
|
||||
-- @param #number frequency Frequency in MHz. Default 305 MHz.
|
||||
-- @param #number modulation Modulation, defaults to radio.modulation.AM.
|
||||
-- @param #string voice Voice.
|
||||
-- @param #string culture Culture, defaults to "en-US".
|
||||
-- @param #string gender Gender, defaults to "male".
|
||||
-- @param #string relayunitname Name of the unit used for transmission location.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetSRSRangeInstructor( frequency, modulation, voice, culture, gender, relayunitname )
|
||||
self.instructorfreq = frequency or 305
|
||||
self.instructmsrs:SetFrequencies(self.instructorfreq)
|
||||
self.instructmsrs:SetModulations(modulation or radio.modulation.AM)
|
||||
self.instructmsrs:SetVoice(voice)
|
||||
self.instructmsrs:SetCulture(culture or "en-US")
|
||||
self.instructmsrs:SetGender(gender or "male")
|
||||
self.instructor = true
|
||||
if relayunitname then
|
||||
local unit = UNIT:FindByName(relayunitname)
|
||||
local Coordinate = unit:GetCoordinate()
|
||||
self.instructmsrs:SetCoordinate(Coordinate)
|
||||
self.instructorrelayname = relayunitname
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Enable range control and set frequency (non-SRS).
|
||||
-- @param #RANGE self
|
||||
-- @param #number frequency Frequency in MHz. Default 256 MHz.
|
||||
-- @param #string relayunitname Name of the unit used for transmission.
|
||||
@ -1188,7 +1282,7 @@ function RANGE:SetRangeControl( frequency, relayunitname )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Enable instructor radio and set frequency.
|
||||
--- Enable instructor radio and set frequency (non-SRS).
|
||||
-- @param #RANGE self
|
||||
-- @param #number frequency Frequency in MHz. Default 305 MHz.
|
||||
-- @param #string relayunitname Name of the unit used for transmission.
|
||||
@ -1755,7 +1849,12 @@ function RANGE:OnEventHit( EventData )
|
||||
-- Too close to the target.
|
||||
if _currentTarget.pastfoulline == false and _unit and _playername then
|
||||
local _d = _currentTarget.zone.foulline
|
||||
-- DONE - SRS output
|
||||
local text = string.format( "%s, Invalid hit!\nYou already passed foul line distance of %d m for target %s.", self:_myname( _unitName ), _d, targetname )
|
||||
if self.useSRS then
|
||||
local ttstext = string.format( "%s, Invalid hit! You already passed foul line distance of %d meters for target %s.", self:_myname( _unitName ), _d, targetname )
|
||||
self.controlsrsQ:NewTransmission(ttstext,nil,self.controlmsrs,nil,2)
|
||||
end
|
||||
self:_DisplayMessageToGroup( _unit, text )
|
||||
self:T2( self.id .. text )
|
||||
_currentTarget.pastfoulline = true
|
||||
@ -1810,7 +1909,11 @@ function RANGE:OnEventShot( EventData )
|
||||
if EventData.IniDCSUnit == nil then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if EventData.IniPlayerName == nil then
|
||||
return
|
||||
end
|
||||
|
||||
-- Weapon data.
|
||||
local _weapon = EventData.Weapon:getTypeName() -- should be the same as Event.WeaponTypeName
|
||||
local _weaponStrArray = UTILS.Split( _weapon, "%." )
|
||||
@ -1998,11 +2101,21 @@ function RANGE:OnEventShot( EventData )
|
||||
elseif insidezone then
|
||||
|
||||
-- Send message.
|
||||
-- DONE SRS message
|
||||
local _message = string.format( "%s, weapon impacted too far from nearest range target (>%.1f km). No score!", _callsign, self.scorebombdistance / 1000 )
|
||||
if self.useSRS then
|
||||
local ttstext = string.format( "%s, weapon impacted too far from nearest range target, mor than %.1f kilometer. No score!", _callsign, self.scorebombdistance / 1000 )
|
||||
self.controlsrsQ:NewTransmission(ttstext,nil,self.controlmsrs,nil,2)
|
||||
end
|
||||
self:_DisplayMessageToGroup( _unit, _message, nil, false )
|
||||
|
||||
if self.rangecontrol then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCWeaponImpactedTooFar.filename, RANGE.Sound.RCWeaponImpactedTooFar.duration, self.soundpath, nil, nil, _message, self.subduration )
|
||||
-- weapon impacted too far from the nearest target! No Score!
|
||||
if self.useSRS then
|
||||
self.controlsrsQ:NewTransmission(_message,nil,self.controlmsrs,nil,1)
|
||||
else
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCWeaponImpactedTooFar.filename, RANGE.Sound.RCWeaponImpactedTooFar.duration, self.soundpath, nil, nil, _message, self.subduration )
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
@ -2085,18 +2198,26 @@ end
|
||||
function RANGE:onafterEnterRange( From, Event, To, player )
|
||||
|
||||
if self.instructor and self.rangecontrol then
|
||||
|
||||
-- Range control radio frequency split.
|
||||
local RF = UTILS.Split( string.format( "%.3f", self.rangecontrolfreq ), "." )
|
||||
|
||||
-- Radio message that player entered the range
|
||||
self.instructor:NewTransmission( RANGE.Sound.IREnterRange.filename, RANGE.Sound.IREnterRange.duration, self.soundpath )
|
||||
self.instructor:Number2Transmission( RF[1] )
|
||||
if tonumber( RF[2] ) > 0 then
|
||||
self.instructor:NewTransmission( RANGE.Sound.IRDecimal.filename, RANGE.Sound.IRDecimal.duration, self.soundpath )
|
||||
self.instructor:Number2Transmission( RF[2] )
|
||||
|
||||
if self.useSRS then
|
||||
local text = string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f MHz", self.rangecontrolfreq)
|
||||
local ttstext = string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f mega hertz.", self.rangecontrolfreq)
|
||||
local group = player.client:GetGroup()
|
||||
self.instructsrsQ:NewTransmission(ttstext,nil,self.instructmsrs,nil,1,{group},text,10)
|
||||
else
|
||||
-- Range control radio frequency split.
|
||||
local RF = UTILS.Split( string.format( "%.3f", self.rangecontrolfreq ), "." )
|
||||
|
||||
-- Radio message that player entered the range
|
||||
-- You entered the bombing range. For hit assessment, contact the range controller at xy MHz
|
||||
self.instructor:NewTransmission( RANGE.Sound.IREnterRange.filename, RANGE.Sound.IREnterRange.duration, self.soundpath )
|
||||
self.instructor:Number2Transmission( RF[1] )
|
||||
if tonumber( RF[2] ) > 0 then
|
||||
self.instructor:NewTransmission( RANGE.Sound.IRDecimal.filename, RANGE.Sound.IRDecimal.duration, self.soundpath )
|
||||
self.instructor:Number2Transmission( RF[2] )
|
||||
end
|
||||
self.instructor:NewTransmission( RANGE.Sound.IRMegaHertz.filename, RANGE.Sound.IRMegaHertz.duration, self.soundpath )
|
||||
end
|
||||
self.instructor:NewTransmission( RANGE.Sound.IRMegaHertz.filename, RANGE.Sound.IRMegaHertz.duration, self.soundpath )
|
||||
end
|
||||
|
||||
end
|
||||
@ -2110,7 +2231,14 @@ end
|
||||
function RANGE:onafterExitRange( From, Event, To, player )
|
||||
|
||||
if self.instructor then
|
||||
self.instructor:NewTransmission( RANGE.Sound.IRExitRange.filename, RANGE.Sound.IRExitRange.duration, self.soundpath )
|
||||
-- You left the bombing range zone. Have a nice day!
|
||||
if self.useSRS then
|
||||
local text = "You left the bombing range zone. Have a nice day!"
|
||||
local group = player.client:GetGroup()
|
||||
self.instructsrsQ:NewTransmission(text,nil,self.instructmsrs,nil,1,{group},text,10)
|
||||
else
|
||||
self.instructor:NewTransmission( RANGE.Sound.IRExitRange.filename, RANGE.Sound.IRExitRange.duration, self.soundpath )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@ -2140,32 +2268,37 @@ function RANGE:onafterImpact( From, Event, To, result, player )
|
||||
text = text .. string.format( " %s hit.", result.quality )
|
||||
|
||||
if self.rangecontrol then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCImpact.filename, RANGE.Sound.RCImpact.duration, self.soundpath, nil, nil, text, self.subduration )
|
||||
self.rangecontrol:Number2Transmission( string.format( "%03d", result.radial ), nil, 0.1 )
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCDegrees.filename, RANGE.Sound.RCDegrees.duration, self.soundpath )
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCFor.filename, RANGE.Sound.RCFor.duration, self.soundpath )
|
||||
self.rangecontrol:Number2Transmission( string.format( "%d", UTILS.MetersToFeet( result.distance ) ) )
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCFeet.filename, RANGE.Sound.RCFeet.duration, self.soundpath )
|
||||
if result.quality == "POOR" then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCPoorHit.filename, RANGE.Sound.RCPoorHit.duration, self.soundpath, nil, 0.5 )
|
||||
elseif result.quality == "INEFFECTIVE" then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCIneffectiveHit.filename, RANGE.Sound.RCIneffectiveHit.duration, self.soundpath, nil, 0.5 )
|
||||
elseif result.quality == "GOOD" then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCGoodHit.filename, RANGE.Sound.RCGoodHit.duration, self.soundpath, nil, 0.5 )
|
||||
elseif result.quality == "EXCELLENT" then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCExcellentHit.filename, RANGE.Sound.RCExcellentHit.duration, self.soundpath, nil, 0.5 )
|
||||
|
||||
if self.useSRS then
|
||||
local group = player.client:GetGroup()
|
||||
self.controlsrsQ:NewTransmission(text,nil,self.controlmsrs,nil,1,{group},text,10)
|
||||
else
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCImpact.filename, RANGE.Sound.RCImpact.duration, self.soundpath, nil, nil, text, self.subduration )
|
||||
self.rangecontrol:Number2Transmission( string.format( "%03d", result.radial ), nil, 0.1 )
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCDegrees.filename, RANGE.Sound.RCDegrees.duration, self.soundpath )
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCFor.filename, RANGE.Sound.RCFor.duration, self.soundpath )
|
||||
self.rangecontrol:Number2Transmission( string.format( "%d", UTILS.MetersToFeet( result.distance ) ) )
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCFeet.filename, RANGE.Sound.RCFeet.duration, self.soundpath )
|
||||
if result.quality == "POOR" then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCPoorHit.filename, RANGE.Sound.RCPoorHit.duration, self.soundpath, nil, 0.5 )
|
||||
elseif result.quality == "INEFFECTIVE" then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCIneffectiveHit.filename, RANGE.Sound.RCIneffectiveHit.duration, self.soundpath, nil, 0.5 )
|
||||
elseif result.quality == "GOOD" then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCGoodHit.filename, RANGE.Sound.RCGoodHit.duration, self.soundpath, nil, 0.5 )
|
||||
elseif result.quality == "EXCELLENT" then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCExcellentHit.filename, RANGE.Sound.RCExcellentHit.duration, self.soundpath, nil, 0.5 )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Unit.
|
||||
if player.unitname then
|
||||
if player.unitname and not self.useSRS then
|
||||
|
||||
-- Get unit.
|
||||
local unit = UNIT:FindByName( player.unitname )
|
||||
|
||||
-- Send message.
|
||||
self:_DisplayMessageToGroup( unit, text, nil, true )
|
||||
self:_DisplayMessageToGroup( unit, text, nil, true )
|
||||
self:T( self.id .. text )
|
||||
end
|
||||
|
||||
@ -2677,7 +2810,7 @@ function RANGE:_DisplayRangeInfo( _unitname )
|
||||
|
||||
-- Check if we have a player.
|
||||
if unit and playername then
|
||||
|
||||
self:I(playername)
|
||||
-- Message text.
|
||||
local text = ""
|
||||
|
||||
@ -2746,7 +2879,8 @@ function RANGE:_DisplayRangeInfo( _unitname )
|
||||
if self.instructorrelayname then
|
||||
local relay = UNIT:FindByName( self.instructorrelayname )
|
||||
if relay then
|
||||
alive = tostring( relay:IsAlive() )
|
||||
--alive = tostring( relay:IsAlive() )
|
||||
alive = relay:IsAlive() and "ok" or "N/A"
|
||||
end
|
||||
end
|
||||
text = text .. string.format( "Instructor %.3f MHz (Relay=%s)\n", self.instructorfreq, alive )
|
||||
@ -2757,6 +2891,7 @@ function RANGE:_DisplayRangeInfo( _unitname )
|
||||
local relay = UNIT:FindByName( self.rangecontrolrelayname )
|
||||
if relay then
|
||||
alive = tostring( relay:IsAlive() )
|
||||
alive = relay:IsAlive() and "ok" or "N/A"
|
||||
end
|
||||
end
|
||||
text = text .. string.format( "Control %.3f MHz (Relay=%s)\n", self.rangecontrolfreq, alive )
|
||||
@ -3041,11 +3176,18 @@ function RANGE:_CheckInZone( _unitName )
|
||||
|
||||
-- Send message.
|
||||
self:_DisplayMessageToGroup( _unit, _msg, nil, true )
|
||||
|
||||
|
||||
if self.rangecontrol then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCLeftStrafePitTooQuickly.filename, RANGE.Sound.RCLeftStrafePitTooQuickly.duration, self.soundpath )
|
||||
if self.useSRS then
|
||||
local group = _unit:GetGroup()
|
||||
local text = "You left the strafing zone too quickly! No score!"
|
||||
--self.controlsrsQ:NewTransmission(text,nil,self.controlmsrs,nil,1,{group},text,10)
|
||||
self.controlsrsQ:NewTransmission(text,nil,self.controlmsrs,nil,1)
|
||||
else
|
||||
-- You left the strafing zone too quickly! No score!
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCLeftStrafePitTooQuickly.filename, RANGE.Sound.RCLeftStrafePitTooQuickly.duration, self.soundpath )
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- Get current ammo.
|
||||
@ -3056,23 +3198,6 @@ function RANGE:_CheckInZone( _unitName )
|
||||
|
||||
local _sound = nil -- #RANGE.Soundfile
|
||||
|
||||
--[[ --RangeBoss commented out in order to implement strafe quality based on accuracy percentage, not the number of rounds on target
|
||||
-- Judge this pass. Text is displayed on summary.
|
||||
if _result.hits >= _result.zone.goodPass*2 then
|
||||
_result.text = "EXCELLENT PASS"
|
||||
_sound=RANGE.Sound.RCExcellentPass
|
||||
elseif _result.hits >= _result.zone.goodPass then
|
||||
_result.text = "GOOD PASS"
|
||||
_sound=RANGE.Sound.RCGoodPass
|
||||
elseif _result.hits >= _result.zone.goodPass/2 then
|
||||
_result.text = "INEFFECTIVE PASS"
|
||||
_sound=RANGE.Sound.RCIneffectivePass
|
||||
else
|
||||
_result.text = "POOR PASS"
|
||||
_sound=RANGE.Sound.RCPoorPass
|
||||
end
|
||||
]]
|
||||
|
||||
-- Calculate accuracy of run. Number of hits wrt number of rounds fired.
|
||||
local shots = _result.ammo - _ammo
|
||||
local accur = 0
|
||||
@ -3109,10 +3234,13 @@ function RANGE:_CheckInZone( _unitName )
|
||||
|
||||
-- Message text.
|
||||
local _text = string.format( "%s, hits on target %s: %d", self:_myname( _unitName ), _result.zone.name, _result.hits )
|
||||
local ttstext = string.format( "%s, hits on target %s: %d.", self:_myname( _unitName ), _result.zone.name, _result.hits )
|
||||
if shots and accur then
|
||||
_text = _text .. string.format( "\nTotal rounds fired %d. Accuracy %.1f %%.", shots, accur )
|
||||
ttstext = ttstext .. string.format( ". Total rounds fired %d. Accuracy %.1f percent.", shots, accur )
|
||||
end
|
||||
_text = _text .. string.format( "\n%s", resulttext )
|
||||
ttstext = ttstext .. string.format( " %s", resulttext )
|
||||
|
||||
-- Send message.
|
||||
self:_DisplayMessageToGroup( _unit, _text )
|
||||
@ -3144,16 +3272,20 @@ function RANGE:_CheckInZone( _unitName )
|
||||
|
||||
-- Voice over.
|
||||
if self.rangecontrol then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCHitsOnTarget.filename, RANGE.Sound.RCHitsOnTarget.duration, self.soundpath )
|
||||
self.rangecontrol:Number2Transmission( string.format( "%d", _result.hits ) )
|
||||
if shots and accur then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCTotalRoundsFired.filename, RANGE.Sound.RCTotalRoundsFired.duration, self.soundpath, nil, 0.2 )
|
||||
self.rangecontrol:Number2Transmission( string.format( "%d", shots ), nil, 0.2 )
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCAccuracy.filename, RANGE.Sound.RCAccuracy.duration, self.soundpath, nil, 0.2 )
|
||||
self.rangecontrol:Number2Transmission( string.format( "%d", UTILS.Round( accur, 0 ) ) )
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCPercent.filename, RANGE.Sound.RCPercent.duration, self.soundpath )
|
||||
if self.useSRS then
|
||||
self.controlsrsQ:NewTransmission(ttstext,nil,self.controlmsrs,nil,1)
|
||||
else
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCHitsOnTarget.filename, RANGE.Sound.RCHitsOnTarget.duration, self.soundpath )
|
||||
self.rangecontrol:Number2Transmission( string.format( "%d", _result.hits ) )
|
||||
if shots and accur then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCTotalRoundsFired.filename, RANGE.Sound.RCTotalRoundsFired.duration, self.soundpath, nil, 0.2 )
|
||||
self.rangecontrol:Number2Transmission( string.format( "%d", shots ), nil, 0.2 )
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCAccuracy.filename, RANGE.Sound.RCAccuracy.duration, self.soundpath, nil, 0.2 )
|
||||
self.rangecontrol:Number2Transmission( string.format( "%d", UTILS.Round( accur, 0 ) ) )
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCPercent.filename, RANGE.Sound.RCPercent.duration, self.soundpath )
|
||||
end
|
||||
self.rangecontrol:NewTransmission( _sound.filename, _sound.duration, self.soundpath, nil, 0.5 )
|
||||
end
|
||||
self.rangecontrol:NewTransmission( _sound.filename, _sound.duration, self.soundpath, nil, 0.5 )
|
||||
end
|
||||
|
||||
-- Set strafe status to nil.
|
||||
@ -3192,7 +3324,11 @@ function RANGE:_CheckInZone( _unitName )
|
||||
local _msg = string.format( "%s, rolling in on strafe pit %s.", self:_myname( _unitName ), target.name )
|
||||
|
||||
if self.rangecontrol then
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCRollingInOnStrafeTarget.filename, RANGE.Sound.RCRollingInOnStrafeTarget.duration, self.soundpath )
|
||||
if self.useSRS then
|
||||
self.controlsrsQ:NewTransmission(_msg,nil,self.controlmsrs,nil,1)
|
||||
else
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCRollingInOnStrafeTarget.filename, RANGE.Sound.RCRollingInOnStrafeTarget.duration, self.soundpath )
|
||||
end
|
||||
end
|
||||
|
||||
-- Send message.
|
||||
@ -3312,10 +3448,10 @@ function RANGE:_AddF10Commands( _unitName )
|
||||
local _StrPits = MENU_GROUP_COMMAND:New( group, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName ):Refresh()
|
||||
end
|
||||
else
|
||||
self:E( self.id .. "Could not find group or group ID in AddF10Menu() function. Unit name: " .. _unitName )
|
||||
self:E( self.id .. "Could not find group or group ID in AddF10Menu() function. Unit name: " .. _unitName or "N/A")
|
||||
end
|
||||
else
|
||||
self:E( self.id .. "Player unit does not exist in AddF10Menu() function. Unit name: " .. _unitName )
|
||||
self:E( self.id .. "Player unit does not exist in AddF10Menu() function. Unit name: " .. _unitName or "N/A")
|
||||
end
|
||||
|
||||
end
|
||||
@ -3919,6 +4055,7 @@ function RANGE:_GetPlayerUnitAndName( _unitName )
|
||||
|
||||
self:T2( { DCSunit = DCSunit, unit = unit, playername = playername } )
|
||||
if DCSunit and unit and playername then
|
||||
self:F2(playername)
|
||||
return unit, playername
|
||||
end
|
||||
|
||||
@ -3935,13 +4072,15 @@ end
|
||||
-- @param #string unitname Name of the player unit.
|
||||
function RANGE:_myname( unitname )
|
||||
self:F2( unitname )
|
||||
|
||||
local pname = "Ghost 1 1"
|
||||
local unit = UNIT:FindByName( unitname )
|
||||
local pname = unit:GetPlayerName()
|
||||
-- local csign = unit:GetCallsign()
|
||||
|
||||
-- return string.format("%s (%s)", csign, pname)
|
||||
return string.format( "%s", pname )
|
||||
if unit and unit:IsAlive() then
|
||||
local grp = unit:GetGroup()
|
||||
if grp and grp:IsAlive() then
|
||||
pname = grp:GetCustomCallSign(true,true)
|
||||
end
|
||||
end
|
||||
return pname
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -91,6 +91,7 @@
|
||||
-- @field #boolean useSRS If true, use SRS for transmission.
|
||||
-- @field Sound.SRS#MSRS msrs Moose SRS object.
|
||||
-- @field #number dTQueueCheck Time interval to check the radio queue. Default 5 sec or 90 sec if SRS is used.
|
||||
-- @field #boolean ReportmBar Report mBar/hpa even if not metric, i.e. for Mirage flights
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
|
||||
@ -344,6 +345,7 @@ ATIS = {
|
||||
usemarker = nil,
|
||||
markerid = nil,
|
||||
relHumidity = nil,
|
||||
ReportmBar = false,
|
||||
}
|
||||
|
||||
--- NATO alphabet.
|
||||
@ -586,15 +588,18 @@ _ATIS = {}
|
||||
|
||||
--- ATIS class version.
|
||||
-- @field #string version
|
||||
ATIS.version = "0.9.6"
|
||||
ATIS.version = "0.9.9"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: Add new Normany airfields.
|
||||
-- TODO: Add new Normandy airfields.
|
||||
-- TODO: Zulu time --> Zulu in output.
|
||||
-- TODO: Correct fog for elevation.
|
||||
-- DONE: Use new AIRBASE system to set start/landing runway
|
||||
-- DONE: SetILS doesn't work
|
||||
-- DONE: Visibility reported twice over SRS
|
||||
-- DONE: Add text report for output.
|
||||
-- DONE: Add stop FMS functions.
|
||||
-- NOGO: Use local time. Not realisitc!
|
||||
@ -620,8 +625,6 @@ function ATIS:New(AirbaseName, Frequency, Modulation)
|
||||
-- Inherit everything from FSM class.
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #ATIS
|
||||
|
||||
local self=BASE:Inherit(self, FSM:New()) -- #ATIS
|
||||
|
||||
self.airbasename=AirbaseName
|
||||
self.airbase=AIRBASE:FindByName(AirbaseName)
|
||||
|
||||
@ -653,6 +656,7 @@ function ATIS:New(AirbaseName, Frequency, Modulation)
|
||||
self:SetMapMarks( false )
|
||||
self:SetRelativeHumidity()
|
||||
self:SetQueueUpdateTime()
|
||||
self:SetReportmBar(false)
|
||||
|
||||
-- Start State.
|
||||
self:SetStartState( "Stopped" )
|
||||
@ -747,7 +751,7 @@ end
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetSoundfilesPath( path )
|
||||
self.soundpath = tostring( path or "ATIS Soundfiles/" )
|
||||
self:I( self.lid .. string.format( "Setting sound files path to %s", self.soundpath ) )
|
||||
self:T( self.lid .. string.format( "Setting sound files path to %s", self.soundpath ) )
|
||||
return self
|
||||
end
|
||||
|
||||
@ -758,7 +762,7 @@ end
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetRadioRelayUnitName( unitname )
|
||||
self.relayunitname = unitname
|
||||
self:I( self.lid .. string.format( "Setting radio relay unit to %s", self.relayunitname ) )
|
||||
self:T( self.lid .. string.format( "Setting radio relay unit to %s", self.relayunitname ) )
|
||||
return self
|
||||
end
|
||||
|
||||
@ -776,13 +780,40 @@ function ATIS:SetTowerFrequencies( freqs )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set active runway. This can be used if the automatic runway determination via the wind direction gives incorrect results.
|
||||
--- Set active runway for **landing** operations. This can be used if the automatic runway determination via the wind direction gives incorrect results.
|
||||
-- For example, use this if there are two runways with the same directions.
|
||||
-- @param #ATIS self
|
||||
-- @param #string runway Active runway, *e.g.* "31L".
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetActiveRunway( runway )
|
||||
self.activerunway = tostring( runway )
|
||||
local prefer = nil
|
||||
if string.find(string.lower(runway),"l") then
|
||||
prefer = true
|
||||
elseif string.find(string.lower(runway),"r") then
|
||||
prefer = false
|
||||
end
|
||||
self.airbase:SetActiveRunway(runway,prefer)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the active runway for landing.
|
||||
-- @param #ATIS self
|
||||
-- @param #string runway : Name of the runway, e.g. "31" or "02L" or "90R". If not given, the runway is determined from the wind direction.
|
||||
-- @param #boolean preferleft : If true, perfer the left runway. If false, prefer the right runway. If nil (default), do not care about left or right.
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetActiveRunwayLanding(runway, preferleft)
|
||||
self.airbase:SetActiveRunwayLanding(runway,preferleft)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the active runway for take-off.
|
||||
-- @param #ATIS self
|
||||
-- @param #string runway : Name of the runway, e.g. "31" or "02L" or "90R". If not given, the runway is determined from the wind direction.
|
||||
-- @param #boolean preferleft : If true, perfer the left runway. If false, prefer the right runway. If nil (default), do not care about left or right.
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetActiveRunwayTakeoff(runway,preferleft)
|
||||
self.airbase:SetActiveRunwayTakeoff(runway,preferleft)
|
||||
return self
|
||||
end
|
||||
|
||||
@ -853,7 +884,7 @@ function ATIS:SetRunwayHeadingsMagnetic( headings )
|
||||
end
|
||||
|
||||
-- Add runway heading to table.
|
||||
self:I( self.lid .. string.format( "Adding user specified magnetic runway heading %s", heading ) )
|
||||
self:T( self.lid .. string.format( "Adding user specified magnetic runway heading %s", heading ) )
|
||||
table.insert( self.runwaymag, heading )
|
||||
|
||||
local h = self:GetRunwayWithoutLR( heading )
|
||||
@ -875,7 +906,7 @@ function ATIS:SetRunwayHeadingsMagnetic( headings )
|
||||
end
|
||||
|
||||
-- Add inverse runway heading to table.
|
||||
self:I( self.lid .. string.format( "Adding user specified magnetic runway heading %s (inverse)", head2 ) )
|
||||
self:T( self.lid .. string.format( "Adding user specified magnetic runway heading %s (inverse)", head2 ) )
|
||||
table.insert( self.runwaymag, head2 )
|
||||
end
|
||||
|
||||
@ -949,6 +980,28 @@ function ATIS:SetAltimeterQNH( switch )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Additionally report altimeter QNH/QFE in hPa, even if not set to metric.
|
||||
-- @param #ATIS self
|
||||
-- @param #boolean switch If true or nil, report mBar/hPa in addition.
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetReportmBar(switch)
|
||||
if switch == true or switch == nil then
|
||||
self.ReportmBar = true
|
||||
else
|
||||
self.ReportmBar = false
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Additionally report free text, only working with SRS(!)
|
||||
-- @param #ATIS self
|
||||
-- @param #string text The text to report at the end of the ATIS message, e.g. runway closure, warnings, etc.
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetAdditionalInformation(text)
|
||||
self.AdditionalInformation = text
|
||||
return self
|
||||
end
|
||||
|
||||
--- Suppresses QFE readout. Default is to report both QNH and QFE.
|
||||
-- @param #ATIS self
|
||||
-- @return #ATIS self
|
||||
@ -1128,8 +1181,9 @@ end
|
||||
-- @param #string Culture Culture, e.g. "en-GB" (default).
|
||||
-- @param #string Voice Specific voice. Overrides `Gender` and `Culture`.
|
||||
-- @param #number Port SRS port. Default 5002.
|
||||
-- @param #string GoogleKey Path to Google JSON-Key.
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port)
|
||||
function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey)
|
||||
if PathToSRS then
|
||||
self.useSRS=true
|
||||
self.msrs=MSRS:New(PathToSRS, self.frequency, self.modulation)
|
||||
@ -1139,6 +1193,8 @@ function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port)
|
||||
self.msrs:SetPort(Port)
|
||||
self.msrs:SetCoalition(self:GetCoalition())
|
||||
self.msrs:SetLabel("ATIS")
|
||||
self.msrs:SetGoogle(GoogleKey)
|
||||
self.msrsQ = MSRSQUEUE:New("ATIS")
|
||||
if self.dTQueueCheck<=10 then
|
||||
self:SetQueueUpdateTime(90)
|
||||
end
|
||||
@ -1185,32 +1241,34 @@ function ATIS:onafterStart( From, Event, To )
|
||||
self:I( self.lid .. string.format( "Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d", ATIS.version, self.airbasename, self.frequency, self.modulation ) )
|
||||
|
||||
-- Start radio queue.
|
||||
self.radioqueue = RADIOQUEUE:New( self.frequency, self.modulation, string.format( "ATIS %s", self.airbasename ) )
|
||||
|
||||
-- Send coordinate is airbase coord.
|
||||
self.radioqueue:SetSenderCoordinate( self.airbase:GetCoordinate() )
|
||||
|
||||
-- Set relay unit if we have one.
|
||||
self.radioqueue:SetSenderUnitName( self.relayunitname )
|
||||
|
||||
-- Set radio power.
|
||||
self.radioqueue:SetRadioPower( self.power )
|
||||
|
||||
-- Init numbers.
|
||||
self.radioqueue:SetDigit( 0, ATIS.Sound.N0.filename, ATIS.Sound.N0.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 1, ATIS.Sound.N1.filename, ATIS.Sound.N1.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 2, ATIS.Sound.N2.filename, ATIS.Sound.N2.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 3, ATIS.Sound.N3.filename, ATIS.Sound.N3.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 4, ATIS.Sound.N4.filename, ATIS.Sound.N4.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 5, ATIS.Sound.N5.filename, ATIS.Sound.N5.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 6, ATIS.Sound.N6.filename, ATIS.Sound.N6.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 7, ATIS.Sound.N7.filename, ATIS.Sound.N7.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 8, ATIS.Sound.N8.filename, ATIS.Sound.N8.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 9, ATIS.Sound.N9.filename, ATIS.Sound.N9.duration, self.soundpath )
|
||||
|
||||
-- Start radio queue.
|
||||
self.radioqueue:Start( 1, 0.1 )
|
||||
|
||||
if not self.useSRS then
|
||||
self.radioqueue = RADIOQUEUE:New( self.frequency, self.modulation, string.format( "ATIS %s", self.airbasename ) )
|
||||
|
||||
-- Send coordinate is airbase coord.
|
||||
self.radioqueue:SetSenderCoordinate( self.airbase:GetCoordinate() )
|
||||
|
||||
-- Set relay unit if we have one.
|
||||
self.radioqueue:SetSenderUnitName( self.relayunitname )
|
||||
|
||||
-- Set radio power.
|
||||
self.radioqueue:SetRadioPower( self.power )
|
||||
|
||||
-- Init numbers.
|
||||
self.radioqueue:SetDigit( 0, ATIS.Sound.N0.filename, ATIS.Sound.N0.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 1, ATIS.Sound.N1.filename, ATIS.Sound.N1.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 2, ATIS.Sound.N2.filename, ATIS.Sound.N2.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 3, ATIS.Sound.N3.filename, ATIS.Sound.N3.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 4, ATIS.Sound.N4.filename, ATIS.Sound.N4.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 5, ATIS.Sound.N5.filename, ATIS.Sound.N5.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 6, ATIS.Sound.N6.filename, ATIS.Sound.N6.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 7, ATIS.Sound.N7.filename, ATIS.Sound.N7.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 8, ATIS.Sound.N8.filename, ATIS.Sound.N8.duration, self.soundpath )
|
||||
self.radioqueue:SetDigit( 9, ATIS.Sound.N9.filename, ATIS.Sound.N9.duration, self.soundpath )
|
||||
|
||||
-- Start radio queue.
|
||||
self.radioqueue:Start( 1, 0.1 )
|
||||
end
|
||||
|
||||
-- Handle airbase capture
|
||||
-- Handle events.
|
||||
self:HandleEvent( EVENTS.BaseCaptured )
|
||||
@ -1245,7 +1303,7 @@ function ATIS:onafterStatus( From, Event, To )
|
||||
else
|
||||
text = text .. string.format( ", Relay unit=%s (alive=%s)", tostring( self.relayunitname ), relayunitstatus )
|
||||
end
|
||||
self:I( self.lid .. text )
|
||||
self:T( self.lid .. text )
|
||||
|
||||
self:__Status( -60 )
|
||||
end
|
||||
@ -1324,7 +1382,10 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
qnh = Q / 100
|
||||
|
||||
end
|
||||
|
||||
|
||||
local mBarqnh = qnh
|
||||
local mBarqfe = qfe
|
||||
|
||||
-- Convert to inHg.
|
||||
if self.PmmHg then
|
||||
qfe = UTILS.hPa2mmHg( qfe )
|
||||
@ -1775,7 +1836,9 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
end
|
||||
end
|
||||
alltext = alltext .. ";\n" .. subtitle
|
||||
|
||||
--self:I("Line 1811")
|
||||
--self:I(alltext)
|
||||
|
||||
-- Visibility
|
||||
if self.metric then
|
||||
subtitle = string.format( "Visibility %s km", VISIBILITY )
|
||||
@ -1792,7 +1855,10 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
end
|
||||
end
|
||||
alltext = alltext .. ";\n" .. subtitle
|
||||
|
||||
--self:I("Line 1830")
|
||||
--self:I(alltext)
|
||||
|
||||
subtitle = ""
|
||||
-- Weather phenomena
|
||||
local wp = false
|
||||
local wpsub = ""
|
||||
@ -1892,8 +1958,11 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
end
|
||||
end
|
||||
end
|
||||
alltext = alltext .. ";\n" .. subtitle
|
||||
--self:I("Line 1932")
|
||||
|
||||
alltext = alltext .. ";\n" .. subtitle
|
||||
--self:I(alltext)
|
||||
subtitle = ""
|
||||
-- Temperature
|
||||
if self.TDegF then
|
||||
if temperature < 0 then
|
||||
@ -1921,8 +1990,10 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
self:Transmission( ATIS.Sound.DegreesCelsius, 0.2 )
|
||||
end
|
||||
end
|
||||
--self:I("Line 1962")
|
||||
alltext = alltext .. ";\n" .. subtitle
|
||||
|
||||
--self:I(alltext)
|
||||
|
||||
-- Dew point
|
||||
if self.TDegF then
|
||||
if dewpoint < 0 then
|
||||
@ -1950,6 +2021,8 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
self:Transmission( ATIS.Sound.DegreesCelsius, 0.2 )
|
||||
end
|
||||
end
|
||||
--self:I("Line 1992")
|
||||
--self:I(alltext)
|
||||
alltext = alltext .. ";\n" .. subtitle
|
||||
|
||||
-- Altimeter QNH/QFE.
|
||||
@ -1974,6 +2047,15 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.ReportmBar and not self.metric then
|
||||
if self.qnhonly then
|
||||
subtitle = string.format( "%s;\nAltimeter %d hPa", subtitle, mBarqnh )
|
||||
else
|
||||
subtitle = string.format( "%s;\nAltimeter: QNH %d, QFE %d hPa", subtitle, mBarqnh, mBarqfe)
|
||||
end
|
||||
end
|
||||
|
||||
local _ALTIMETER = subtitle
|
||||
if not self.useSRS then
|
||||
self:Transmission( ATIS.Sound.Altimeter, 1.0, subtitle )
|
||||
@ -2006,6 +2088,8 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
end
|
||||
end
|
||||
end
|
||||
--self:I("Line 2049")
|
||||
--self:I(alltext)
|
||||
alltext = alltext .. ";\n" .. subtitle
|
||||
|
||||
-- Active runway.
|
||||
@ -2133,7 +2217,9 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
end
|
||||
|
||||
-- ILS
|
||||
--self:I({ils=self.ils})
|
||||
local ils=self:GetNavPoint(self.ils, runwayLanding, rwyLandingLeft)
|
||||
--self:I({ils=ils,runwayLanding=runwayLanding, rwyLandingLeft=rwyLandingLeft})
|
||||
if ils then
|
||||
subtitle = string.format( "ILS frequency %.2f MHz", ils.frequency )
|
||||
if not self.useSRS then
|
||||
@ -2148,6 +2234,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
self:Transmission( ATIS.Sound.MegaHertz, 0.2 )
|
||||
end
|
||||
alltext = alltext .. ";\n" .. subtitle
|
||||
--self:I(alltext)
|
||||
end
|
||||
|
||||
-- Outer NDB
|
||||
@ -2237,7 +2324,12 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
end
|
||||
alltext = alltext .. ";\n" .. subtitle
|
||||
end
|
||||
|
||||
|
||||
-- additional info, if any
|
||||
if self.useSRS and self.AdditionalInformation then
|
||||
alltext = alltext .. ";\n"..self.AdditionalInformation
|
||||
end
|
||||
|
||||
-- Advice on initial...
|
||||
subtitle = string.format( "Advise on initial contact, you have information %s", NATO )
|
||||
if not self.useSRS then
|
||||
@ -2285,8 +2377,10 @@ function ATIS:onafterReport( From, Event, To, Text )
|
||||
-- Debug output.
|
||||
self:T( "SRS TTS: " .. text )
|
||||
|
||||
-- Play text-to-speech report.
|
||||
self.msrs:PlayText( text )
|
||||
-- Play text-to-speech report.
|
||||
local duration = STTS.getSpeechTime(text,0.95)
|
||||
self.msrsQ:NewTransmission(text,duration,self.msrs,nil,2)
|
||||
--self.msrs:PlayText( text )
|
||||
|
||||
end
|
||||
|
||||
@ -2433,7 +2527,7 @@ end
|
||||
-- @return #string Runway heading without left or right, *e.g.* "31".
|
||||
function ATIS:GetRunwayWithoutLR( runway )
|
||||
local rwywo = runway:gsub( "%D+", "" )
|
||||
-- self:I(string.format("FF runway=%s ==> rwywo=%s", runway, rwywo))
|
||||
-- self:T(string.format("FF runway=%s ==> rwywo=%s", runway, rwywo))
|
||||
return rwywo
|
||||
end
|
||||
|
||||
|
||||
@ -2766,9 +2766,9 @@ function AIRBOSS:SetRefuelAI( LowFuelThreshold )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set max alitude to register flights in the initial zone. Aircraft above this altitude will not be registerered.
|
||||
--- Set max altitude to register flights in the initial zone. Aircraft above this altitude will not be registerered.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #number MaxAltitude Max alitude in feet. Default 1300 ft.
|
||||
-- @param #number MaxAltitude Max altitude in feet. Default 1300 ft.
|
||||
-- @return #AIRBOSS self
|
||||
function AIRBOSS:SetInitialMaxAlt( MaxAltitude )
|
||||
self.initialmaxalt = UTILS.FeetToMeters( MaxAltitude or 1300 )
|
||||
@ -4268,7 +4268,7 @@ function AIRBOSS:_InitStennis()
|
||||
|
||||
-- Carrier Parameters.
|
||||
self.carrierparam.sterndist = -153
|
||||
self.carrierparam.deckheight = 19.06
|
||||
self.carrierparam.deckheight = 18.30
|
||||
|
||||
-- Total size of the carrier (approx as rectangle).
|
||||
self.carrierparam.totlength = 310 -- Wiki says 332.8 meters overall length.
|
||||
@ -5913,7 +5913,7 @@ function AIRBOSS:_WaitAI( flight, respawn )
|
||||
-- Heading from carrier to flight group
|
||||
local hdgto = cv:HeadingTo( fc )
|
||||
|
||||
-- Holding alitude between angels 6 and 10 (random).
|
||||
-- Holding altitude between angels 6 and 10 (random).
|
||||
local angels = math.random( 6, 10 )
|
||||
local altitude = UTILS.FeetToMeters( angels * 1000 )
|
||||
|
||||
@ -8913,7 +8913,7 @@ function AIRBOSS:_Initial( playerData )
|
||||
-- Relative heading to carrier direction.
|
||||
local relheading = self:_GetRelativeHeading( playerData.unit, false )
|
||||
|
||||
-- Alitude of player in feet.
|
||||
-- altitude of player in feet.
|
||||
local altitude = playerData.unit:GetAltitude()
|
||||
|
||||
-- Check if player is in zone and flying roughly in the right direction.
|
||||
@ -11112,7 +11112,7 @@ function AIRBOSS:_Lineup( unit, runway )
|
||||
return lineup
|
||||
end
|
||||
|
||||
--- Get alitude of aircraft wrt carrier deck. Should give zero when the aircraft touched down.
|
||||
--- Get altitude of aircraft wrt carrier deck. Should give zero when the aircraft touched down.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param Wrapper.Unit#UNIT unit Aircraft unit.
|
||||
-- @return #number Altitude in meters wrt carrier height.
|
||||
@ -14103,7 +14103,7 @@ end
|
||||
|
||||
--- Convert altitude from meters to angels (thousands of feet).
|
||||
-- @param #AIRBOSS self
|
||||
-- @param alt Alitude in meters.
|
||||
-- @param alt altitude in meters.
|
||||
-- @return #number Altitude in Anglels = thousands of feet using math.floor().
|
||||
function AIRBOSS:_GetAngels( alt )
|
||||
|
||||
@ -15415,7 +15415,7 @@ function AIRBOSS:_MarshalCallNewFinalBearing( FB )
|
||||
|
||||
end
|
||||
|
||||
--- Compile a radio call when Marshal tells a flight the holding alitude.
|
||||
--- Compile a radio call when Marshal tells a flight the holding altitude.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #number hdg Heading in degrees.
|
||||
function AIRBOSS:_MarshalCallCarrierTurnTo( hdg )
|
||||
@ -15438,7 +15438,7 @@ function AIRBOSS:_MarshalCallCarrierTurnTo( hdg )
|
||||
|
||||
end
|
||||
|
||||
--- Compile a radio call when Marshal tells a flight the holding alitude.
|
||||
--- Compile a radio call when Marshal tells a flight the holding altitude.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #string modex Tail number.
|
||||
-- @param #number nwaiting Number of flights already waiting.
|
||||
@ -15464,7 +15464,7 @@ function AIRBOSS:_MarshalCallStackFull( modex, nwaiting )
|
||||
self:RadioTransmission( self.MarshalRadio, call, nil, nil, nil, true )
|
||||
end
|
||||
|
||||
--- Compile a radio call when Marshal tells a flight the holding alitude.
|
||||
--- Compile a radio call when Marshal tells a flight the holding altitude.
|
||||
-- @param #AIRBOSS self
|
||||
function AIRBOSS:_MarshalCallRecoveryStart( case )
|
||||
|
||||
@ -15504,12 +15504,12 @@ function AIRBOSS:_MarshalCallRecoveryStart( case )
|
||||
|
||||
end
|
||||
|
||||
--- Compile a radio call when Marshal tells a flight the holding alitude.
|
||||
--- Compile a radio call when Marshal tells a flight the holding altitude.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #string modex Tail number.
|
||||
-- @param #number case Recovery case.
|
||||
-- @param #number brc Base recovery course.
|
||||
-- @param #number altitude Holding alitude.
|
||||
-- @param #number altitude Holding altitude.
|
||||
-- @param #string charlie Charlie Time estimate.
|
||||
-- @param #number qfe Alitmeter inHg.
|
||||
function AIRBOSS:_MarshalCallArrived( modex, case, brc, altitude, charlie, qfe )
|
||||
@ -17362,7 +17362,7 @@ function AIRBOSS:_MarkMarshalZone( _unitName, flare )
|
||||
-- Get Case I commence zone at three position.
|
||||
local zoneThree = self:_GetZoneCommence( case, stack )
|
||||
|
||||
-- Pattern alitude.
|
||||
-- Pattern altitude.
|
||||
local patternalt = self:_GetMarshalAltitude( stack, case )
|
||||
|
||||
-- Flare and smoke at the ground.
|
||||
|
||||
@ -91,6 +91,8 @@ do
|
||||
-- @field #boolean PlayerGuidance if true additional callouts to guide/warn players
|
||||
-- @field #boolean ModernEra if true we get more intel on targets, and EPLR on the AIC
|
||||
-- @field #boolean callsignshort if true use short (group) callsigns, e.g. "Ghost 1", else "Ghost 1 1"
|
||||
-- @field #boolean keepnumber if true, use the full string after # for a player custom callsign
|
||||
-- @field #table callsignTranslations optional translations for callsigns
|
||||
-- @field #number MeldDistance 25nm - distance for "Meld" Call , usually shortly before the actual engagement
|
||||
-- @field #number TacDistance 30nm - distance for "TAC" Call
|
||||
-- @field #number ThreatDistance 15nm - distance to declare untargeted (new) threats
|
||||
@ -325,7 +327,7 @@ do
|
||||
-- * @{#AWACS.SetRadarBlur}() : Set the radar blur faktor in percent.
|
||||
-- * @{#AWACS.SetColdWar}() : Set to cold war - no fill-ins, no EPLRS, VID as standard.
|
||||
-- * @{#AWACS.SetModernEraDefensive}() : Set to modern, EPLRS, BVR/IFF engagement, fill-ins.
|
||||
-- * @{#AWACS.SetModernEraAgressive}() : Set to modern, EPLRS, BVR/IFF engagement, fill-ins.
|
||||
-- * @{#AWACS.SetModernEraAggressive}() : Set to modern, EPLRS, BVR/IFF engagement, fill-ins.
|
||||
-- * @{#AWACS.SetPolicingModern}() : Set to modern, EPLRS, VID engagement, fill-ins.
|
||||
-- * @{#AWACS.SetPolicingColdWar}() : Set to cold war, no EPLRS, VID engagement, no fill-ins.
|
||||
-- * @{#AWACS.SetInterceptTimeline}() : Set distances for TAC, Meld and Threat range calls.
|
||||
@ -495,7 +497,7 @@ do
|
||||
-- @field #AWACS
|
||||
AWACS = {
|
||||
ClassName = "AWACS", -- #string
|
||||
version = "0.2.41", -- #string
|
||||
version = "0.2.43", -- #string
|
||||
lid = "", -- #string
|
||||
coalition = coalition.side.BLUE, -- #number
|
||||
coalitiontxt = "blue", -- #string
|
||||
@ -561,6 +563,8 @@ AWACS = {
|
||||
PlayerGuidance = true,
|
||||
ModernEra = true,
|
||||
callsignshort = true,
|
||||
keepnumber = true,
|
||||
callsignTranslations = nil,
|
||||
TacDistance = 45,
|
||||
MeldDistance = 35,
|
||||
ThreatDistance = 25,
|
||||
@ -1710,7 +1714,7 @@ end
|
||||
--- [User] Set AWACS to Modern Era standards - ROE to BVR, ROT to return fire. Radar blur 15%.
|
||||
-- @param #AWACS self
|
||||
-- @return #AWACS self
|
||||
function AWACS:SetModernEraAgressive()
|
||||
function AWACS:SetModernEraAggressive()
|
||||
self.ModernEra = true
|
||||
self.AwacsROT = AWACS.ROT.RETURNFIRE
|
||||
self.AwacsROE = AWACS.ROE.BVR
|
||||
@ -1896,6 +1900,7 @@ function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey
|
||||
self.Volume = Volume or 1.0
|
||||
|
||||
self.AwacsSRS = MSRS:New(self.PathToSRS,self.MultiFrequency,self.MultiModulation,self.Volume)
|
||||
self.AwacsSRS:SetCoalition(self.coalition)
|
||||
self.AwacsSRS:SetGender(self.Gender)
|
||||
self.AwacsSRS:SetCulture(self.Culture)
|
||||
self.AwacsSRS:SetVoice(self.Voice)
|
||||
@ -2068,7 +2073,7 @@ function AWACS:_StartSettings(FlightGroup,Mission)
|
||||
|
||||
--self.AwacsFG:SetSRS(self.PathToSRS,self.Gender,self.Culture,self.Voice,self.Port,self.PathToGoogleKey,"AWACS",self.Volume)
|
||||
|
||||
self.callsigntxt = string.format("%s",AWACS.CallSignClear[self.CallSign])
|
||||
self.callsigntxt = string.format("%s",self.CallSignClear[self.CallSign])
|
||||
|
||||
self:__CheckRadioQueue(10)
|
||||
|
||||
@ -2110,7 +2115,7 @@ function AWACS:_StartSettings(FlightGroup,Mission)
|
||||
|
||||
--AwacsFG:SetSRS(self.PathToSRS,self.Gender,self.Culture,self.Voice,self.Port,nil,"AWACS")
|
||||
|
||||
self.callsigntxt = string.format("%s",AWACS.CallSignClear[self.CallSign])
|
||||
self.callsigntxt = string.format("%s",self.CallSignClear[self.CallSign])
|
||||
|
||||
local shifting = self.gettext:GetEntry("SHIFTCHANGE",self.locale)
|
||||
|
||||
@ -2222,39 +2227,29 @@ function AWACS:_GetCallSign(Group,GID, IsPlayer)
|
||||
|
||||
local callsign = "Ghost 1"
|
||||
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+)')
|
||||
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))
|
||||
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
|
||||
callsign = string.gsub(shortcallsign,callnumber,"").." "..callnumbermajor.." "..callnumberminor
|
||||
end
|
||||
self:T("Generated Callsign for TTS = " .. callsign)
|
||||
end
|
||||
|
||||
callsign = Group:GetCustomCallSign(self.callsignshort,self.keepnumber,self.callsignTranslations)
|
||||
end
|
||||
return callsign
|
||||
end
|
||||
|
||||
--- [User] Set player callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns.
|
||||
-- @param #AWACS self
|
||||
-- @param #boolean ShortCallsign If true, only call out the major flight number
|
||||
-- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name as-is, no amendments or numbers.
|
||||
-- @param #table CallsignTranslations (optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized
|
||||
-- callsigns from playername or group name.
|
||||
-- @return #AWACS self
|
||||
function AWACS:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||
if not ShortCallsign or ShortCallsign == false then
|
||||
self.callsignshort = false
|
||||
else
|
||||
self.callsignshort = true
|
||||
end
|
||||
self.keepnumber = Keepnumber or false
|
||||
self.callsignTranslations = CallsignTranslations
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Update contact from cluster data
|
||||
-- @param #AWACS self
|
||||
-- @param #number CID Contact ID
|
||||
@ -2971,7 +2966,7 @@ function AWACS:_ShowAwacsInfo(Group)
|
||||
local report = REPORT:New("Info")
|
||||
report:Add("====================")
|
||||
report:Add(string.format("AWACS %s",self.callsigntxt))
|
||||
report:Add(string.format("Radio: %d %s",self.Frequency,UTILS.GetModulationName(self.Modulation)))
|
||||
report:Add(string.format("Radio: %.3f %s",self.Frequency,UTILS.GetModulationName(self.Modulation)))
|
||||
report:Add(string.format("Bulls Alias: %s",self.AOName))
|
||||
report:Add(string.format("Coordinate: %s",self.AOCoordinate:ToStringLLDDM()))
|
||||
report:Add("====================")
|
||||
@ -4303,55 +4298,7 @@ function AWACS:_ReadAssignedGroupFromTID(TaskID)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- [Internal] Create new idle task from contact to pick up later
|
||||
-- @param #AWACS self
|
||||
-- @param #string Description Task Type
|
||||
-- @param #table Object Object of TARGET
|
||||
-- @param Ops.Intelligence#INTEL.Contact Contact
|
||||
-- @return #AWACS self
|
||||
function AWACS:_CreateIdleTaskForContact(Description,Object,Contact)
|
||||
self:T(self.lid.."_CreateIdleTaskForContact "..Description)
|
||||
local task = {} -- #AWACS.ManagedTask
|
||||
self.ManagedTaskID = self.ManagedTaskID + 1
|
||||
task.TID = self.ManagedTaskID
|
||||
task.AssignedGroupID = 0
|
||||
task.Status = AWACS.TaskStatus.IDLE
|
||||
task.ToDo = Description
|
||||
task.Target = TARGET:New(Object)
|
||||
task.Contact = Contact
|
||||
task.ScreenText = Description
|
||||
if Description == AWACS.TaskDescription.ANCHOR or Description == AWACS.TaskDescription.REANCHOR then
|
||||
task.Target.Type = TARGET.ObjectType.ZONE
|
||||
end
|
||||
self.ManagedTasks:Push(task,task.TID)
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Create new idle task from cluster to pick up later
|
||||
-- @param #AWACS self
|
||||
-- @param #string Description Task Type
|
||||
-- @param #table Object Object of TARGET
|
||||
-- @param Ops.Intelligence#INTEL.Cluster Cluster
|
||||
-- @return #AWACS self
|
||||
function AWACS:_CreateIdleTaskForCluster(Description,Object,Cluster)
|
||||
self:T(self.lid.."_CreateIdleTaskForCluster "..Description)
|
||||
local task = {} -- #AWACS.ManagedTask
|
||||
self.ManagedTaskID = self.ManagedTaskID + 1
|
||||
task.TID = self.ManagedTaskID
|
||||
task.AssignedGroupID = 0
|
||||
task.Status = AWACS.TaskStatus.IDLE
|
||||
task.ToDo = Description
|
||||
task.Target = TARGET:New(self.intel:GetClusterCoordinate(Cluster))
|
||||
task.Cluster = Cluster
|
||||
task.ScreenText = Description
|
||||
if Description == AWACS.TaskDescription.ANCHOR or Description == AWACS.TaskDescription.REANCHOR then
|
||||
task.Target.Type = TARGET.ObjectType.ZONE
|
||||
end
|
||||
self.ManagedTasks:Push(task,task.TID)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Create radio entry to tell players that CAP is on station in Anchor
|
||||
-- @param #AWACS self
|
||||
-- @param #number GID Group ID
|
||||
@ -5711,7 +5658,7 @@ function AWACS:onafterStart(From, Event, To)
|
||||
end
|
||||
|
||||
--self.AwacsFG:SetSRS(self.PathToSRS,self.Gender,self.Culture,self.Voice,self.Port,self.PathToGoogleKey,"AWACS",self.Volume)
|
||||
self.callsigntxt = string.format("%s",AWACS.CallSignClear[self.CallSign])
|
||||
self.callsigntxt = string.format("%s",self.CallSignClear[self.CallSign])
|
||||
self:__CheckRadioQueue(-10)
|
||||
|
||||
local sunrise = self.gettext:GetEntry("SUNRISE",self.locale)
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
-- @field #number verbose Verbosity level.
|
||||
-- @field #string lid Class id string for output to DCS log file.
|
||||
-- @field #number coalition Coalition side number, e.g. `coalition.side.RED`.
|
||||
-- @field Core.Set#SET_GROUP allheligroupset Set of CSAR heli groups.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- *Combat search and rescue (CSAR) are search and rescue operations that are carried out during war that are within or near combat zones.* (Wikipedia)
|
||||
@ -97,23 +98,22 @@
|
||||
-- mycsar.useprefix = true -- Requires CSAR helicopter #GROUP names to have the prefix(es) defined below.
|
||||
-- mycsar.csarPrefix = { "helicargo", "MEDEVAC"} -- #GROUP name prefixes used for useprefix=true - DO NOT use # in helicopter names in the Mission Editor!
|
||||
-- mycsar.verbose = 0 -- set to > 1 for stats output for debugging.
|
||||
-- -- (added 0.1.4) limit amount of downed pilots spawned by **ejection** events
|
||||
-- -- limit amount of downed pilots spawned by **ejection** events
|
||||
-- mycsar.limitmaxdownedpilots = true
|
||||
-- mycsar.maxdownedpilots = 10
|
||||
-- -- (added 0.1.8) - allow to set far/near distance for approach and optionally pilot must open doors
|
||||
-- -- allow to set far/near distance for approach and optionally pilot must open doors
|
||||
-- mycsar.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters
|
||||
-- mycsar.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters
|
||||
-- mycsar.pilotmustopendoors = false -- switch to true to enable check of open doors
|
||||
-- -- (added 0.1.9)
|
||||
-- mycsar.suppressmessages = false -- switch off all messaging if you want to do your own
|
||||
-- -- (added 0.1.11)
|
||||
-- mycsar.rescuehoverheight = 20 -- max height for a hovering rescue in meters
|
||||
-- mycsar.rescuehoverdistance = 10 -- max distance for a hovering rescue in meters
|
||||
-- -- (added 0.1.12)
|
||||
-- -- Country codes for spawned pilots
|
||||
-- mycsar.countryblue= country.id.USA
|
||||
-- mycsar.countryred = country.id.RUSSIA
|
||||
-- mycsar.countryneutral = country.id.UN_PEACEKEEPERS
|
||||
-- mycsar.topmenuname = "CSAR" -- set the menu entry name
|
||||
-- mycsar.ADFRadioPwr = 1000 -- ADF Beacons sending with 1KW as default
|
||||
--
|
||||
-- ## 2.1 Experimental Features
|
||||
--
|
||||
@ -129,9 +129,11 @@
|
||||
-- mycsar.SRSVoice = nil -- SRS voice, relevant for Google TTS
|
||||
-- mycsar.SRSGPathToCredentials = nil -- Path to your Google credentials json file, set this if you want to use Google TTS
|
||||
-- mycsar.SRSVolume = 1 -- Volume, between 0 and 1
|
||||
-- mycsar.SRSGender = "male" -- male or female voice
|
||||
-- --
|
||||
-- mycsar.csarUsePara = false -- If set to true, will use the LandingAfterEjection Event instead of Ejection. Requires mycsar.enableForAI to be set to true. --shagrat
|
||||
-- mycsar.wetfeettemplate = "man in floating thingy" -- if you use a mod to have a pilot in a rescue float, put the template name in here for wet feet spawns. Note: in conjunction with csarUsePara this might create dual ejected pilots in edge cases.
|
||||
-- mycsar.allowbronco = false -- set to true to use the Bronco mod as a CSAR plane
|
||||
--
|
||||
-- ## 3. Results
|
||||
--
|
||||
@ -215,7 +217,7 @@ CSAR = {
|
||||
smokeMarkers = {}, -- tracks smoke markers for groups
|
||||
heliVisibleMessage = {}, -- tracks if the first message has been sent of the heli being visible
|
||||
heliCloseMessage = {}, -- tracks heli close message ie heli < 500m distance
|
||||
max_units = 6, --number of pilots that can be carried
|
||||
max_units = 6, -- number of pilots that can be carried
|
||||
hoverStatus = {}, -- tracks status of a helis hover above a downed pilot
|
||||
pilotDisabled = {}, -- tracks what aircraft a pilot is disabled for
|
||||
pilotLives = {}, -- tracks how many lives a pilot has
|
||||
@ -228,6 +230,9 @@ CSAR = {
|
||||
rescuedpilots = 0,
|
||||
limitmaxdownedpilots = true,
|
||||
maxdownedpilots = 10,
|
||||
allheligroupset = nil,
|
||||
topmenuname = "CSAR",
|
||||
ADFRadioPwr = 1000,
|
||||
}
|
||||
|
||||
--- Downed pilots info.
|
||||
@ -260,11 +265,12 @@ CSAR.AircraftType["Mi-24P"] = 8
|
||||
CSAR.AircraftType["Mi-24V"] = 8
|
||||
CSAR.AircraftType["Bell-47"] = 2
|
||||
CSAR.AircraftType["UH-60L"] = 10
|
||||
CSAR.AircraftType["AH-64D_BLK_II"] = 2
|
||||
CSAR.AircraftType["AH-64D_BLK_II"] = 2
|
||||
CSAR.AircraftType["Bronco-OV-10A"] = 2
|
||||
|
||||
--- CSAR class version.
|
||||
-- @field #string version
|
||||
CSAR.version="1.0.6"
|
||||
CSAR.version="1.0.11"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
@ -411,19 +417,25 @@ function CSAR:New(Coalition, Template, Alias)
|
||||
-- added 0.1.4
|
||||
self.wetfeettemplate = nil
|
||||
self.usewetfeet = false
|
||||
|
||||
-- added 0.1.8
|
||||
self.allowbronco = false -- set to true to use the Bronco mod as a CSAR plane
|
||||
|
||||
self.ADFRadioPwr = 1000
|
||||
|
||||
-- WARNING - here\'ll be dragons
|
||||
-- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua
|
||||
-- needs SRS => 1.9.6 to work (works on the *server* side)
|
||||
self.useSRS = false -- Use FF\'s SRS integration
|
||||
self.SRSPath = "E:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your server(!)
|
||||
self.SRSPath = "E:\\Program Files\\DCS-SimpleRadio-Standalone" -- adjust your own path in your server(!)
|
||||
self.SRSchannel = 300 -- radio channel
|
||||
self.SRSModulation = radio.modulation.AM -- modulation
|
||||
self.SRSport = 5002 -- port
|
||||
self.SRSCulture = "en-GB"
|
||||
self.SRSVoice = nil
|
||||
self.SRSGPathToCredentials = nil
|
||||
self.SRSVolume = 1
|
||||
self.SRSVolume = 1.0 -- volume 0.0 to 1.0
|
||||
self.SRSGender = "male" -- male or female
|
||||
|
||||
------------------------
|
||||
--- Pseudo Functions ---
|
||||
@ -925,7 +937,16 @@ function CSAR:_EventHandler(EventData)
|
||||
|
||||
local _unit = _event.IniUnit
|
||||
local _group = _event.IniGroup
|
||||
if _unit:IsHelicopter() or _group:IsHelicopter() then
|
||||
|
||||
local function IsBronco(Group)
|
||||
local grp = Group -- Wrapper.Group#GROUP
|
||||
local typename = grp:GetTypeName()
|
||||
self:T(typename)
|
||||
if typename == "Bronco-OV-10A" then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
if _unit:IsHelicopter() or _group:IsHelicopter() or IsBronco(_group) then
|
||||
self:_AddMedevacMenuItem()
|
||||
end
|
||||
|
||||
@ -1580,21 +1601,7 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak, _overrid
|
||||
end
|
||||
-- integrate SRS
|
||||
if _speak and self.useSRS then
|
||||
local srstext = SOUNDTEXT:New(_text)
|
||||
local path = self.SRSPath
|
||||
local modulation = self.SRSModulation
|
||||
local channel = self.SRSchannel
|
||||
local msrs = MSRS:New(path,channel,modulation)
|
||||
msrs:SetPort(self.SRSport)
|
||||
msrs:SetLabel("CSAR")
|
||||
msrs:SetCulture(self.SRSCulture)
|
||||
msrs:SetCoalition(self.coalition)
|
||||
msrs:SetVoice(self.SRSVoice)
|
||||
if self.SRSGPathToCredentials then
|
||||
msrs:SetGoogle(self.SRSGPathToCredentials)
|
||||
end
|
||||
msrs:SetVolume(self.SRSVolume)
|
||||
msrs:PlaySoundText(srstext, 2)
|
||||
self.SRSQueue:NewTransmission(_text,nil,self.msrs,nil,2)
|
||||
end
|
||||
return self
|
||||
end
|
||||
@ -1894,7 +1901,7 @@ function CSAR:_AddMedevacMenuItem()
|
||||
self:T(self.lid .. " _AddMedevacMenuItem")
|
||||
|
||||
local coalition = self.coalition
|
||||
local allheligroupset = self.allheligroupset
|
||||
local allheligroupset = self.allheligroupset -- Core.Set#SET_GROUP
|
||||
local _allHeliGroups = allheligroupset:GetSetObjects()
|
||||
|
||||
-- rebuild units table
|
||||
@ -1919,7 +1926,8 @@ function CSAR:_AddMedevacMenuItem()
|
||||
local groupname = _group:GetName()
|
||||
if self.addedTo[groupname] == nil then
|
||||
self.addedTo[groupname] = true
|
||||
local _rootPath = MENU_GROUP:New(_group,"CSAR")
|
||||
local menuname = self.topmenuname or "CSAR"
|
||||
local _rootPath = MENU_GROUP:New(_group,menuname)
|
||||
local _rootMenu1 = MENU_GROUP_COMMAND:New(_group,"List Active CSAR",_rootPath, self._DisplayActiveSAR,self,_unitName)
|
||||
local _rootMenu2 = MENU_GROUP_COMMAND:New(_group,"Check Onboard",_rootPath, self._CheckOnboard,self,_unitName)
|
||||
local _rootMenu3 = MENU_GROUP_COMMAND:New(_group,"Request Signal Flare",_rootPath, self._SignalFlare,self,_unitName)
|
||||
@ -2025,10 +2033,13 @@ function CSAR:_AddBeaconToGroup(_group, _freq)
|
||||
end
|
||||
|
||||
if _group:IsAlive() then
|
||||
local _radioUnit = _group:GetUnit(1)
|
||||
local Frequency = _freq -- Freq in Hertz
|
||||
local Sound = "l10n/DEFAULT/"..self.radioSound
|
||||
trigger.action.radioTransmission(Sound, _radioUnit:GetPositionVec3(), 0, false, Frequency, 1000) -- Beacon in MP only runs for exactly 30secs straight
|
||||
local _radioUnit = _group:GetUnit(1)
|
||||
if _radioUnit then
|
||||
local Frequency = _freq -- Freq in Hertz
|
||||
local Sound = "l10n/DEFAULT/"..self.radioSound
|
||||
local vec3 = _radioUnit:GetVec3() or _radioUnit:GetPositionVec3() or {x=0,y=0,z=0}
|
||||
trigger.action.radioTransmission(Sound, vec3, 0, false, Frequency, self.ADFRadioPwr or 1000) -- Beacon in MP only runs for exactly 30secs straight
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
@ -2103,7 +2114,11 @@ function CSAR:onafterStart(From, Event, To)
|
||||
self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.PilotDead, self._EventHandler)
|
||||
if self.useprefix then
|
||||
|
||||
if self.allowbronco then
|
||||
local prefixes = self.csarPrefix or {}
|
||||
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefixes):FilterStart()
|
||||
elseif self.useprefix then
|
||||
local prefixes = self.csarPrefix or {}
|
||||
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefixes):FilterCategoryHelicopter():FilterStart()
|
||||
else
|
||||
@ -2113,6 +2128,24 @@ function CSAR:onafterStart(From, Event, To)
|
||||
if self.wetfeettemplate then
|
||||
self.usewetfeet = true
|
||||
end
|
||||
if self.useSRS then
|
||||
local path = self.SRSPath
|
||||
local modulation = self.SRSModulation
|
||||
local channel = self.SRSchannel
|
||||
self.msrs = MSRS:New(path,channel,modulation)
|
||||
self.msrs:SetPort(self.SRSport)
|
||||
self.msrs:SetLabel("CSAR")
|
||||
self.msrs:SetCulture(self.SRSCulture)
|
||||
self.msrs:SetCoalition(self.coalition)
|
||||
self.msrs:SetVoice(self.SRSVoice)
|
||||
self.msrs:SetGender(self.SRSGender)
|
||||
if self.SRSGPathToCredentials then
|
||||
self.msrs:SetGoogle(self.SRSGPathToCredentials)
|
||||
end
|
||||
self.msrs:SetVolume(self.SRSVolume)
|
||||
self.msrs:SetLabel("CSAR")
|
||||
self.SRSQueue = MSRSQUEUE:New("CSAR")
|
||||
end
|
||||
self:__Status(-10)
|
||||
return self
|
||||
end
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
-- @image OPS_CTLD.jpg
|
||||
|
||||
-- Date: Feb 2022
|
||||
-- Last Update Sep 2022
|
||||
|
||||
do
|
||||
|
||||
@ -74,29 +75,13 @@ CTLD_ENGINEERING = {
|
||||
self.Name = Name or "Engineer Squad" -- #string
|
||||
self.Group = GROUP:FindByName(GroupName) -- Wrapper.Group#GROUP
|
||||
self.Unit = self.Group:GetUnit(1) -- Wrapper.Unit#UNIT
|
||||
--self.C_Ops = C_Ops -- Ops.CTLD#CTLD
|
||||
self.HeliGroup = HeliGroup -- Wrapper.Group#GROUP
|
||||
self.HeliUnit = HeliUnit -- Wrapper.Unit#UNIT
|
||||
--self.distance = Distance or UTILS.NMToMeters(1)
|
||||
self.currwpt = nil -- Core.Point#COORDINATE
|
||||
self.lid = string.format("%s (%s) | ",self.Name, self.Version)
|
||||
-- Start State.
|
||||
self.State = "Stopped"
|
||||
self.marktimer = 300 -- wait this many secs before trying a crate again
|
||||
|
||||
--[[ Add FSM transitions.
|
||||
-- From State --> Event --> To State
|
||||
self:AddTransition("Stopped", "Start", "Running") -- Start FSM.
|
||||
self:AddTransition("*", "Status", "*")
|
||||
self:AddTransition("*", "Search", "Searching")
|
||||
self:AddTransition("*", "Move", "Moving")
|
||||
self:AddTransition("*", "Arrive", "Arrived")
|
||||
self:AddTransition("*", "Build", "Building")
|
||||
self:AddTransition("*", "Done", "Running")
|
||||
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
||||
|
||||
self:__Start(5)
|
||||
--]]
|
||||
self:Start()
|
||||
local parent = self:GetParent(self)
|
||||
return self
|
||||
@ -200,10 +185,8 @@ CTLD_ENGINEERING = {
|
||||
-- have we tried this cargo recently?
|
||||
local tag = chalk.tag or "none"
|
||||
local timestamp = chalk.timestamp or 0
|
||||
--self:I({chalk})
|
||||
-- enough time gone?
|
||||
local gone = timer.getAbsTime() - timestamp
|
||||
--self:I({time=gone})
|
||||
if gone >= self.marktimer then
|
||||
ok = true
|
||||
_cargo:WipeMark()
|
||||
@ -287,7 +270,6 @@ CTLD_ENGINEERING = {
|
||||
if _point1 and _point2 then
|
||||
local distance1 = _point1:Get2DDistance(_point2)
|
||||
local distance2 = _point1:DistanceFromPointVec2(_point2)
|
||||
--self:I({dist1=distance1, dist2=distance2})
|
||||
if distance1 and type(distance1) == "number" then
|
||||
return distance1
|
||||
elseif distance2 and type(distance2) == "number" then
|
||||
@ -306,7 +288,8 @@ CTLD_ENGINEERING = {
|
||||
|
||||
end
|
||||
|
||||
do
|
||||
do
|
||||
|
||||
------------------------------------------------------
|
||||
--- **CTLD_CARGO** class, extends Core.Base#BASE
|
||||
-- @type CTLD_CARGO
|
||||
@ -314,7 +297,7 @@ do
|
||||
-- @field #number ID ID of this cargo.
|
||||
-- @field #string Name Name for menu.
|
||||
-- @field #table Templates Table of #POSITIONABLE objects.
|
||||
-- @field #CTLD_CARGO.Enum CargoType Enumerator of Type.
|
||||
-- @field #string CargoType Enumerator of Type.
|
||||
-- @field #boolean HasBeenMoved Flag for moving.
|
||||
-- @field #boolean LoadDirectly Flag for direct loading.
|
||||
-- @field #number CratesNeeded Crates needed to build.
|
||||
@ -325,8 +308,9 @@ do
|
||||
-- @field #string Subcategory Sub-category name.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
---
|
||||
-- @field #CTLD_CARGO
|
||||
-- @field CTLD_CARGO
|
||||
CTLD_CARGO = {
|
||||
ClassName = "CTLD_CARGO",
|
||||
ID = 0,
|
||||
@ -343,9 +327,15 @@ CTLD_CARGO = {
|
||||
Mark = nil,
|
||||
}
|
||||
|
||||
---
|
||||
--- Define cargo types.
|
||||
-- @field Enum
|
||||
-- @type CTLD_CARGO.Enum
|
||||
-- @field #string VEHICLE
|
||||
-- @field #string TROOPS
|
||||
-- @field #string FOB
|
||||
-- @field #string CRATE
|
||||
-- @field #string REPAIR
|
||||
-- @field #string ENGINEERS
|
||||
-- @field #string STATIC
|
||||
CTLD_CARGO.Enum = {
|
||||
VEHICLE = "Vehicle", -- #string vehicles
|
||||
TROOPS = "Troops", -- #string troops
|
||||
@ -542,7 +532,7 @@ CTLD_CARGO = {
|
||||
|
||||
--- Query crate type for STATIC
|
||||
-- @param #CTLD_CARGO self
|
||||
-- @param #boolean
|
||||
-- @return #boolean
|
||||
function CTLD_CARGO:IsStatic()
|
||||
if self.CargoType == "Static" then
|
||||
return true
|
||||
@ -551,19 +541,35 @@ CTLD_CARGO = {
|
||||
end
|
||||
end
|
||||
|
||||
--- Add mark
|
||||
-- @param #CTLD_CARGO self
|
||||
-- @return #CTLD_CARGO self
|
||||
function CTLD_CARGO:AddMark(Mark)
|
||||
self.Mark = Mark
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get mark
|
||||
-- @param #CTLD_CARGO self
|
||||
-- @return #string Mark
|
||||
function CTLD_CARGO:GetMark(Mark)
|
||||
return self.Mark
|
||||
end
|
||||
|
||||
--- Wipe mark
|
||||
-- @param #CTLD_CARGO self
|
||||
-- @return #CTLD_CARGO self
|
||||
function CTLD_CARGO:WipeMark()
|
||||
self.Mark = nil
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get overall mass of a cargo object, i.e. crates needed x mass per crate
|
||||
-- @param #CTLD_CARGO self
|
||||
-- @return #number mass
|
||||
function CTLD_CARGO:GetNetMass()
|
||||
return self.CratesNeeded * self.PerCrateMass
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -1062,7 +1068,7 @@ CTLD.UnitTypes = {
|
||||
|
||||
--- CTLD class version.
|
||||
-- @field #string version
|
||||
CTLD.version="1.0.10"
|
||||
CTLD.version="1.0.11"
|
||||
|
||||
--- Instantiate a new CTLD.
|
||||
-- @param #CTLD self
|
||||
@ -1178,7 +1184,6 @@ function CTLD:New(Coalition, Prefixes, Alias)
|
||||
self.CrateDistance = 35 -- list/load crates in this radius
|
||||
self.ExtractFactor = 3.33 -- factor for troops extraction, i.e. CrateDistance * Extractfactor
|
||||
self.prefixes = Prefixes or {"Cargoheli"}
|
||||
--self.I({prefixes = self.prefixes})
|
||||
self.useprefix = true
|
||||
|
||||
self.maximumHoverHeight = 15
|
||||
@ -1491,7 +1496,6 @@ function CTLD:_EventHandler(EventData)
|
||||
self:_RefreshF10Menus()
|
||||
end
|
||||
-- Herc support
|
||||
--self:T_unit:GetTypeName())
|
||||
if _unit:GetTypeName() == "Hercules" and self.enableHercules then
|
||||
local unitname = event.IniUnitName or "none"
|
||||
self.Loaded_Cargo[unitname] = nil
|
||||
@ -1532,6 +1536,8 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype)
|
||||
local instock = Cargotype:GetStock()
|
||||
local cgoname = Cargotype:GetName()
|
||||
local cgotype = Cargotype:GetType()
|
||||
local cgonetmass = Cargotype:GetNetMass()
|
||||
local maxloadable = self:_GetMaxLoadableMass(Unit)
|
||||
if type(instock) == "number" and tonumber(instock) <= 0 and tonumber(instock) ~= -1 then
|
||||
-- nothing left over
|
||||
self:_SendMessage(string.format("Sorry, all %s are gone!", cgoname), 10, false, Group)
|
||||
@ -1583,6 +1589,9 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype)
|
||||
if troopsize + numberonboard > trooplimit then
|
||||
self:_SendMessage("Sorry, we\'re crammed already!", 10, false, Group)
|
||||
return
|
||||
elseif maxloadable < cgonetmass then
|
||||
self:_SendMessage("Sorry, that\'s too heavy to load!", 10, false, Group)
|
||||
return
|
||||
else
|
||||
self.CargoCounter = self.CargoCounter + 1
|
||||
local loadcargotype = CTLD_CARGO:New(self.CargoCounter, Cargotype.Name, Cargotype.Templates, cgotype, true, true, Cargotype.CratesNeeded,nil,nil,Cargotype.PerCrateMass)
|
||||
@ -1610,7 +1619,6 @@ function CTLD:_FindRepairNearby(Group, Unit, Repairtype)
|
||||
local distance = self:_GetDistance(v:GetCoordinate(),unitcoord)
|
||||
local unit = v:GetUnit(1) -- Wrapper.Unit#UNIT
|
||||
local desc = unit:GetDesc() or nil
|
||||
--self:I({desc = desc.attributes})
|
||||
if distance < nearestDistance and distance ~= -1 and not desc.attributes.Infantry then
|
||||
nearestGroup = v
|
||||
nearestGroupIndex = k
|
||||
@ -1647,7 +1655,6 @@ function CTLD:_FindRepairNearby(Group, Unit, Repairtype)
|
||||
-- walk through generics and find matching type
|
||||
local Cargotype = nil
|
||||
for k,v in pairs(self.Cargo_Crates) do
|
||||
--self:I({groupname,v.Templates})
|
||||
if matchstring(groupname,v.Templates) and matchstring(groupname,Repairtype) then
|
||||
Cargotype = v -- #CTLD_CARGO
|
||||
break
|
||||
@ -1655,7 +1662,6 @@ function CTLD:_FindRepairNearby(Group, Unit, Repairtype)
|
||||
end
|
||||
|
||||
if Cargotype == nil then
|
||||
--self:_SendMessage("Can't find a matching group for " .. Repairtype, 10, false, Group)
|
||||
return nil, nil
|
||||
else
|
||||
return nearestGroup, Cargotype
|
||||
@ -1674,17 +1680,14 @@ end
|
||||
function CTLD:_RepairObjectFromCrates(Group,Unit,Crates,Build,Number,Engineering)
|
||||
self:T(self.lid .. " _RepairObjectFromCrates")
|
||||
local build = Build -- -- #CTLD.Buildable
|
||||
--self:I({Build=Build})
|
||||
local Repairtype = build.Template -- #string
|
||||
local NearestGroup, CargoType = self:_FindRepairNearby(Group,Unit,Repairtype) -- Wrapper.Group#GROUP, #CTLD_CARGO
|
||||
--self:I({Repairtype=Repairtype, CargoType=CargoType, NearestGroup=NearestGroup})
|
||||
if NearestGroup ~= nil then
|
||||
if self.repairtime < 2 then self.repairtime = 30 end -- noob catch
|
||||
if not Engineering then
|
||||
self:_SendMessage(string.format("Repair started using %s taking %d secs", build.Name, self.repairtime), 10, false, Group)
|
||||
end
|
||||
-- now we can build ....
|
||||
--NearestGroup:Destroy(false)
|
||||
local name = CargoType:GetName()
|
||||
local required = CargoType:GetCratesNeeded()
|
||||
local template = CargoType:GetTemplates()
|
||||
@ -1701,7 +1704,6 @@ function CTLD:_RepairObjectFromCrates(Group,Unit,Crates,Build,Number,Engineering
|
||||
desttimer:Start(self.repairtime - 1)
|
||||
local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,object,true,NearestGroup:GetCoordinate())
|
||||
buildtimer:Start(self.repairtime)
|
||||
--self:_BuildObjectFromCrates(Group,Unit,object)
|
||||
else
|
||||
if not Engineering then
|
||||
self:_SendMessage("Can't repair this unit with " .. build.Name, 10, false, Group)
|
||||
@ -1813,9 +1815,7 @@ end
|
||||
self:__TroopsExtracted(1,Group, Unit, nearestGroup)
|
||||
|
||||
-- clean up:
|
||||
--table.remove(self.DroppedTroops, nearestGroupIndex)
|
||||
if type(Cargotype.Templates) == "table" and Cargotype.Templates[2] then
|
||||
--self:I("*****This CargoType has multiple templates: "..Cargotype.Name)
|
||||
for _,_key in pairs (Cargotype.Templates) do
|
||||
table.insert(secondarygroups,_key)
|
||||
end
|
||||
@ -1966,7 +1966,6 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop)
|
||||
local width = width / 2
|
||||
local Offy = math.random(-width,width)
|
||||
self.Spawned_Crates[self.CrateCounter] = SPAWNSTATIC:NewFromType(basetype,"Cargos",self.cratecountry)
|
||||
--:InitCoordinate(cratecoord)
|
||||
:InitCargoMass(cgomass)
|
||||
:InitCargo(self.enableslingload)
|
||||
:InitLinkToUnit(Ship,dist,Offy,0)
|
||||
@ -1976,7 +1975,6 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop)
|
||||
:InitCoordinate(cratecoord)
|
||||
:InitCargoMass(cgomass)
|
||||
:InitCargo(self.enableslingload)
|
||||
--:InitLinkToUnit(Unit,OffsetX,OffsetY,OffsetAngle)
|
||||
:Spawn(270,cratealias)
|
||||
end
|
||||
local templ = cargotype:GetTemplates()
|
||||
@ -2105,7 +2103,6 @@ function CTLD:_GetDistance(_point1, _point2)
|
||||
if _point1 and _point2 then
|
||||
local distance1 = _point1:Get2DDistance(_point2)
|
||||
local distance2 = _point1:DistanceFromPointVec2(_point2)
|
||||
--self:I({dist1=distance1, dist2=distance2})
|
||||
if distance1 and type(distance1) == "number" then
|
||||
return distance1
|
||||
elseif distance2 and type(distance2) == "number" then
|
||||
@ -2144,11 +2141,7 @@ function CTLD:_FindCratesNearby( _group, _unit, _dist, _ignoreweight)
|
||||
local maxmass = 2000
|
||||
local maxloadable = 2000
|
||||
if not _ignoreweight then
|
||||
loadedmass = self:_GetUnitCargoMass(_unit)
|
||||
unittype = _unit:GetTypeName()
|
||||
capabilities = self:_GetUnitCapabilities(_unit) -- #CTLD.UnitCapabilities
|
||||
maxmass = capabilities.cargoweightlimit or 2000
|
||||
maxloadable = maxmass - loadedmass
|
||||
maxloadable = self:_GetMaxLoadableMass(_unit)
|
||||
end
|
||||
self:T(self.lid .. " Max loadable mass: " .. maxloadable)
|
||||
for _,_cargoobject in pairs (existingcrates) do
|
||||
@ -2326,6 +2319,21 @@ function CTLD:_GetUnitCargoMass(Unit)
|
||||
return loadedmass
|
||||
end
|
||||
|
||||
--- (Internal) Function to calculate max loadable mass left over.
|
||||
-- @param #CTLD self
|
||||
-- @param Wrapper.Unit#UNIT Unit
|
||||
-- @return #number maxloadable Max loadable mass in kg
|
||||
function CTLD:_GetMaxLoadableMass(Unit)
|
||||
self:T(self.lid .. " _GetMaxLoadableMass")
|
||||
if not Unit then return 0 end
|
||||
local loadable = 0
|
||||
local loadedmass = self:_GetUnitCargoMass(Unit)
|
||||
local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities
|
||||
local maxmass = capabilities.cargoweightlimit or 2000 -- max 2 tons
|
||||
loadable = maxmass - loadedmass
|
||||
return loadable
|
||||
end
|
||||
|
||||
--- (Internal) Function to calculate and set Unit internal cargo mass
|
||||
-- @param #CTLD self
|
||||
-- @param Wrapper.Unit#UNIT Unit
|
||||
@ -2333,9 +2341,6 @@ function CTLD:_UpdateUnitCargoMass(Unit)
|
||||
self:T(self.lid .. " _UpdateUnitCargoMass")
|
||||
local calculatedMass = self:_GetUnitCargoMass(Unit)
|
||||
Unit:SetUnitInternalCargo(calculatedMass)
|
||||
--local report = REPORT:New("Loadmaster report")
|
||||
--report:Add("Carrying " .. calculatedMass .. "Kg")
|
||||
--self:_SendMessage(report:Text(),10,false,Unit:GetGroup())
|
||||
return self
|
||||
end
|
||||
|
||||
@ -2353,6 +2358,7 @@ function CTLD:_ListCargo(Group, Unit)
|
||||
local cratelimit = capabilities.cratelimit -- #number
|
||||
local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo
|
||||
local loadedmass = self:_GetUnitCargoMass(Unit) -- #number
|
||||
local maxloadable = self:_GetMaxLoadableMass(Unit)
|
||||
if self.Loaded_Cargo[unitname] then
|
||||
local no_troops = loadedcargo.Troopsloaded or 0
|
||||
local no_crates = loadedcargo.Cratesloaded or 0
|
||||
@ -2387,11 +2393,11 @@ function CTLD:_ListCargo(Group, Unit)
|
||||
report:Add(" N O N E")
|
||||
end
|
||||
report:Add("------------------------------------------------------------")
|
||||
report:Add("Total Mass: ".. loadedmass .. " kg")
|
||||
report:Add("Total Mass: ".. loadedmass .. " kg. Loadable: "..maxloadable.." kg.")
|
||||
local text = report:Text()
|
||||
self:_SendMessage(text, 30, true, Group)
|
||||
else
|
||||
self:_SendMessage(string.format("Nothing loaded!\nTroop limit: %d | Crate limit %d",trooplimit,cratelimit), 10, false, Group)
|
||||
self:_SendMessage(string.format("Nothing loaded!\nTroop limit: %d | Crate limit %d | Weight limit %d kgs",trooplimit,cratelimit,maxloadable), 10, false, Group)
|
||||
end
|
||||
return self
|
||||
end
|
||||
@ -2567,7 +2573,6 @@ function CTLD:_UnloadTroops(Group, Unit)
|
||||
end -- template loop
|
||||
cargo:SetWasDropped(true)
|
||||
-- engineering group?
|
||||
--self:I("Dropped Troop Type: "..type)
|
||||
if type == CTLD_CARGO.Enum.ENGINEERS then
|
||||
self.Engineers = self.Engineers + 1
|
||||
local grpname = self.DroppedTroops[self.TroopCounter]:GetName()
|
||||
@ -2943,7 +2948,6 @@ function CTLD:_MoveGroupToZone(Group)
|
||||
local groupcoord = Group:GetCoordinate()
|
||||
-- Get closest zone of type
|
||||
local outcome, name, zone, distance = self:IsUnitInZone(Group,CTLD.CargoZoneType.MOVE)
|
||||
--self:Tstring.format("Closest WP zone %s is %d meters",name,distance))
|
||||
if (distance <= self.movetroopsdistance) and zone then
|
||||
-- yes, we can ;)
|
||||
local groupname = Group:GetName()
|
||||
@ -3125,7 +3129,7 @@ function CTLD:_RefreshF10Menus()
|
||||
--- [Internal] Function to check if a template exists in the mission.
|
||||
-- @param #CTLD self
|
||||
-- @param #table temptable Table of string names
|
||||
-- @return #boolen outcome
|
||||
-- @return #boolean outcome
|
||||
function CTLD:_CheckTemplates(temptable)
|
||||
self:T(self.lid .. " _CheckTemplates")
|
||||
local outcome = true
|
||||
@ -3889,8 +3893,7 @@ end
|
||||
self:_SendMessage(text, 10, false, Group)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- (Internal) Check if a unit is in a load zone and is hovering in parameters.
|
||||
-- @param #CTLD self
|
||||
-- @param Wrapper.Unit#UNIT Unit
|
||||
@ -4160,9 +4163,6 @@ end
|
||||
self.Engineers = self.Engineers + 1
|
||||
local grpname = self.DroppedTroops[self.TroopCounter]:GetName()
|
||||
self.EngineersInField[self.Engineers] = CTLD_ENGINEERING:New(name, grpname)
|
||||
--self:I(string.format("%s Injected Engineers %s into action!",self.lid, name))
|
||||
else
|
||||
--self:I(string.format("%s Injected Troops %s into action!",self.lid, name))
|
||||
end
|
||||
if self.eventoninject then
|
||||
self:__TroopsDeployed(1,nil,nil,self.DroppedTroops[self.TroopCounter])
|
||||
@ -4533,7 +4533,6 @@ end
|
||||
local cargo = _cargo -- #CTLD_CARGO
|
||||
local object = cargo:GetPositionable() -- Wrapper.Static#STATIC
|
||||
if object and object:IsAlive() and cargo:WasDropped() then
|
||||
self:I({_cargo})
|
||||
statics[#statics+1] = cargo
|
||||
end
|
||||
end
|
||||
@ -4550,7 +4549,6 @@ end
|
||||
template = { template }
|
||||
end
|
||||
for _,_name in pairs (template) do
|
||||
--self:I(string.format("*** Saving CTLD: Matching %s with %s",name,_name))
|
||||
if string.find(name,_name) and _cargo:GetType() ~= CTLD_CARGO.Enum.REPAIR then
|
||||
match = true
|
||||
cargo = thiscargo
|
||||
@ -4745,7 +4743,6 @@ end
|
||||
|
||||
local loadeddata = {}
|
||||
for line in file:lines() do
|
||||
--self:I({line=type(line)})
|
||||
loadeddata[#loadeddata+1] = line
|
||||
end
|
||||
file:close()
|
||||
@ -4769,7 +4766,6 @@ end
|
||||
cargotemplates = UTILS.Split(cargotemplates,";")
|
||||
local size = tonumber(dataset[8])
|
||||
local mass = tonumber(dataset[9])
|
||||
--self:I({groupname,vec3,cargoname,cargotemplates,cargotype,size,mass})
|
||||
-- inject at Vec2
|
||||
local dropzone = ZONE_RADIUS:New("DropZone",vec2,20)
|
||||
if cargotype == CTLD_CARGO.Enum.VEHICLE or cargotype == CTLD_CARGO.Enum.FOB then
|
||||
@ -4784,8 +4780,6 @@ end
|
||||
local size = tonumber(dataset[8])
|
||||
local mass = tonumber(dataset[9])
|
||||
local dropzone = ZONE_RADIUS:New("DropZone",vec2,20)
|
||||
-- STATIC,-84037,154,834021,Humvee,{Humvee;},Vehicle,1,100
|
||||
-- STATIC,-84036,154,834018,Ammunition-1,ammo_cargo,Static,1,500
|
||||
local injectstatic = nil
|
||||
if cargotype == CTLD_CARGO.Enum.VEHICLE or cargotype == CTLD_CARGO.Enum.FOB then
|
||||
cargotemplates = string.gsub(cargotemplates,"{","")
|
||||
@ -4898,7 +4892,6 @@ CTLD_HERCULES.Types = {
|
||||
["ART GVOZDIKA [34720lb]"] = {['name'] = "SAU Gvozdika", ['container'] = false},
|
||||
["APC MTLB Air [26400lb]"] = {['name'] = "MTLB", ['container'] = true},
|
||||
["APC MTLB Skid [26290lb]"] = {['name'] = "MTLB", ['container'] = false},
|
||||
--["Generic Crate [20000lb]"] = {['name'] = "Hercules_Container_Parachute", ['container'] = true} --nothing generic in Moose CTLD
|
||||
}
|
||||
|
||||
--- Cargo Object
|
||||
@ -5000,8 +4993,7 @@ function CTLD_HERCULES:New(Coalition, Alias, CtldObject)
|
||||
|
||||
-- Set some string id for output to DCS.log file.
|
||||
self.lid=string.format("%s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown")
|
||||
|
||||
--self:HandleEvent(EVENTS.Birth,self._HandleBirth)
|
||||
|
||||
self:HandleEvent(EVENTS.Shot, self._HandleShot)
|
||||
|
||||
self:I(self.lid .. "Started")
|
||||
@ -5115,13 +5107,8 @@ function CTLD_HERCULES:Cargo_SpawnStatic(Cargo_Drop_initiator,Cargo_Drop_Positio
|
||||
local position = Cargo_Drop_Position:GetVec2()
|
||||
local Zone = ZONE_RADIUS:New("Cargo Static " .. math.random(1,10000),position,100)
|
||||
if not dead then
|
||||
-- CTLD_CARGO:New(ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped, PerCrateMass, Stock)
|
||||
local injectstatic = CTLD_CARGO:New(nil,"Cargo Static Group "..math.random(1,10000),"iso_container",CTLD_CARGO.Enum.STATIC,true,false,1,nil,true,4500,1)
|
||||
self.CTLD:InjectStatics(Zone,injectstatic,true)
|
||||
else
|
||||
--local static = SPAWNSTATIC:NewFromType("iso_container","Cargos",Cargo_Country)
|
||||
--static.InitDead = true
|
||||
--static:SpawnFromZone(Zone,CargoHeading)
|
||||
end
|
||||
return self
|
||||
end
|
||||
@ -5145,7 +5132,6 @@ function CTLD_HERCULES:Cargo_SpawnObjects(Cargo_Drop_initiator,Cargo_Drop_Direct
|
||||
self:T(self.lid .. 'Cargo_SpawnObjects')
|
||||
|
||||
local CargoHeading = self.CargoHeading
|
||||
--local Cargo_Drop_Position = {}
|
||||
|
||||
if offload_cargo == true or ParatrooperGroupSpawn == true then
|
||||
if ParatrooperGroupSpawn == true then
|
||||
@ -5157,14 +5143,7 @@ function CTLD_HERCULES:Cargo_SpawnObjects(Cargo_Drop_initiator,Cargo_Drop_Direct
|
||||
end
|
||||
else
|
||||
if all_cargo_gets_destroyed == true or Cargo_over_water == true then
|
||||
if Container_Enclosed == true then
|
||||
--self:Cargo_SpawnStatic(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, true, Cargo_Country)
|
||||
if ParatrooperGroupSpawn == false then
|
||||
--self:Cargo_SpawnStatic(Cargo_Drop_initiator,Cargo_Content_position, "Hercules_Container_Parachute_Static", CargoHeading, true, Cargo_Country)
|
||||
end
|
||||
else
|
||||
--self:Cargo_SpawnStatic(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, true, Cargo_Country)
|
||||
end
|
||||
|
||||
else
|
||||
if all_cargo_survive_to_the_ground == true then
|
||||
if ParatrooperGroupSpawn == true then
|
||||
@ -5209,14 +5188,12 @@ function CTLD_HERCULES:Calculate_Object_Height_AGL(group)
|
||||
return height - lheight
|
||||
else
|
||||
-- DCS object
|
||||
--self:T({group})
|
||||
if group:isExist() then
|
||||
local dcsposition = group:getPosition().p
|
||||
local dcsvec2 = {x = dcsposition.x, y = dcsposition.z} -- Vec2
|
||||
local height = math.floor(group:getPosition().p.y - land.getHeight(dcsvec2))
|
||||
self.ObjectTracker[group.id_] = dcsposition -- Vec3
|
||||
self:T(self.lid .. "Height " .. height)
|
||||
--self:T({group.id_,self.ObjectTracker[group.id_]})
|
||||
return height
|
||||
else
|
||||
return 0
|
||||
@ -5437,27 +5414,6 @@ end
|
||||
function CTLD_HERCULES:_HandleBirth(event)
|
||||
-- not sure what this is needed for? I think this for setting generic crates "content" setting.
|
||||
self:T(self.lid .. "Birth Event ID:" .. event.id)
|
||||
--[[
|
||||
if event.id == EVENTS.Birth then
|
||||
local desc = event.initiator:getDesc()
|
||||
if desc["displayName"] == "Hercules" then
|
||||
local grpTab = {}
|
||||
grpTab['object'] = event.IniGroup
|
||||
grpTab['name'] = event.IniGroupName
|
||||
grpTab['cargoType'] = 'Container red 1'
|
||||
grpTab['cargoNum'] = 1
|
||||
grpTab['key'] = #self.carrierGroups + 1
|
||||
|
||||
table.insert(self.carrierGroups,grpTab)
|
||||
|
||||
local hercCargoMenu = MENU_GROUP:New(event.IniGroup,"CargoTypes",nil)
|
||||
local mlrs = MENU_GROUP_COMMAND:New(event.IniGroup,"MLRS",hercCargoMenu,self.SetType,self,grpTab['key'],'MLRS',1)
|
||||
local mlrs = MENU_GROUP_COMMAND:New(event.IniGroup,"Mortar",hercCargoMenu,self.SetType,self,grpTab['key'],'2B11 mortar',8)
|
||||
local mlrs = MENU_GROUP_COMMAND:New(event.IniGroup,"M-109",hercCargoMenu,self.SetType,self,grpTab['key'],'M-109',1)
|
||||
local mlrs = MENU_GROUP_COMMAND:New(event.IniGroup,"FOB Crate",hercCargoMenu,self.SetType,self,grpTab['key'],'Container red 1',1)
|
||||
end
|
||||
end
|
||||
--]]
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@ -327,7 +327,7 @@ FLIGHTCONTROL.FlightStatus={
|
||||
|
||||
--- FlightControl class version.
|
||||
-- @field #string version
|
||||
FLIGHTCONTROL.version="0.7.2"
|
||||
FLIGHTCONTROL.version="0.7.3"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@ -357,8 +357,10 @@ FLIGHTCONTROL.version="0.7.2"
|
||||
-- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a `#table` of multiple frequencies.
|
||||
-- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a `#table` of multiple modulations.
|
||||
-- @param #string PathToSRS Path to the directory, where SRS is located.
|
||||
-- @param #number Port Port of SRS Server, defaults to 5002
|
||||
-- @param #string GoogleKey Path to the Google JSON-Key.
|
||||
-- @return #FLIGHTCONTROL self
|
||||
function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS)
|
||||
function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port, GoogleKey)
|
||||
|
||||
-- Inherit everything from FSM class.
|
||||
local self=BASE:Inherit(self, FSM:New()) -- #FLIGHTCONTROL
|
||||
@ -406,15 +408,25 @@ function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS)
|
||||
self:SetMarkHoldingPattern(true)
|
||||
self:SetRunwayRepairtime()
|
||||
|
||||
-- Set SRS Port
|
||||
self:SetSRSPort(Port or 5002)
|
||||
|
||||
-- Set Callsign Options
|
||||
self:SetCallSignOptions(true,true)
|
||||
|
||||
-- Init msrs queue.
|
||||
self.msrsqueue=MSRSQUEUE:New(self.alias)
|
||||
|
||||
-- SRS for Tower.
|
||||
self.msrsTower=MSRS:New(PathToSRS, Frequency, Modulation)
|
||||
self.msrsTower:SetPort(self.Port)
|
||||
self.msrsTower:SetGoogle(GoogleKey)
|
||||
self:SetSRSTower()
|
||||
|
||||
-- SRS for Pilot.
|
||||
self.msrsPilot=MSRS:New(PathToSRS, Frequency, Modulation)
|
||||
self.msrsPilot:SetPort(self.Port)
|
||||
self.msrsPilot:SetGoogle(GoogleKey)
|
||||
self:SetSRSPilot()
|
||||
|
||||
-- Wait at least 10 seconds after last radio message before calling the next status update.
|
||||
@ -567,6 +579,15 @@ function FLIGHTCONTROL:SetFrequency(Frequency, Modulation)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the SRS server port.
|
||||
-- @param #FLIGHTCONTROL self
|
||||
-- @param #number Port Port to be used. Defaults to 5002.
|
||||
-- @return #FLIGHTCONTROL self
|
||||
function FLIGHTCONTROL:SetSRSPort(Port)
|
||||
self.Port = Port or 5002
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set SRS options for a given MSRS object.
|
||||
-- @param #FLIGHTCONTROL self
|
||||
-- @param Sound.SRS#MSRS msrs Moose SRS object.
|
||||
@ -576,8 +597,9 @@ end
|
||||
-- @param #number Volume Volume. Default 1.0.
|
||||
-- @param #string Label Name under which SRS transmitts.
|
||||
-- @param #string PathToGoogleCredentials Path to google credentials json file.
|
||||
-- @param #number Port Server port for SRS
|
||||
-- @return #FLIGHTCONTROL self
|
||||
function FLIGHTCONTROL:_SetSRSOptions(msrs, Gender, Culture, Voice, Volume, Label, PathToGoogleCredentials)
|
||||
function FLIGHTCONTROL:_SetSRSOptions(msrs, Gender, Culture, Voice, Volume, Label, PathToGoogleCredentials, Port)
|
||||
|
||||
-- Defaults:
|
||||
Gender=Gender or "female"
|
||||
@ -592,6 +614,7 @@ function FLIGHTCONTROL:_SetSRSOptions(msrs, Gender, Culture, Voice, Volume, Labe
|
||||
msrs:SetLabel(Label)
|
||||
msrs:SetGoogle(PathToGoogleCredentials)
|
||||
msrs:SetCoalition(self:GetCoalition())
|
||||
msrs:SetPort(Port or self.Port or 5002)
|
||||
end
|
||||
|
||||
return self
|
||||
@ -981,33 +1004,6 @@ end
|
||||
--- On Before Update status.
|
||||
-- @param #FLIGHTCONTROL self
|
||||
function FLIGHTCONTROL:onbeforeStatusUpdate()
|
||||
|
||||
--[[
|
||||
if self.Tlastmessage then
|
||||
local Tnow=timer.getAbsTime()
|
||||
|
||||
-- Time interval between last radio message.
|
||||
local dT=Tnow-self.Tlastmessage
|
||||
|
||||
if dT<self.dTmessage then
|
||||
|
||||
-- Time
|
||||
local dt=self.dTmessage-dT+1
|
||||
|
||||
-- Debug info.
|
||||
local text=string.format("Last message sent %d sec ago. Will call status again in %d sec", dT, dt)
|
||||
self:T(self.lid..text)
|
||||
|
||||
-- Call status again in dt seconds.
|
||||
self:__StatusUpdate(-dt)
|
||||
|
||||
-- Deny transition.
|
||||
return false
|
||||
else
|
||||
self:T2(self.lid..string.format("Last radio sent %d>%d sec ago. Status update allowed", dT, self.dTmessage))
|
||||
end
|
||||
end
|
||||
]]
|
||||
|
||||
local Tqueue=self.msrsqueue:CalcTransmisstionDuration()
|
||||
|
||||
@ -2633,8 +2629,18 @@ function FLIGHTCONTROL:_PlayerRadioCheck(groupname)
|
||||
local callsign=self:_GetCallsignName(flight)
|
||||
|
||||
-- Pilot radio check.
|
||||
local text=string.format("%s, %s, radio check %.3f", self.alias, callsign, self.frequency)
|
||||
local text = ""
|
||||
|
||||
if type(self.frequency) == "table" then
|
||||
local multifreq = ""
|
||||
for _,_entry in pairs(self.frequency) do
|
||||
multifreq = string.format("%s%.2f, ",multifreq,_entry)
|
||||
end
|
||||
multifreq = string.gsub(multifreq,", $","")
|
||||
text=string.format("%s, %s, radio check %s", self.alias, callsign, multifreq)
|
||||
else
|
||||
text=string.format("%s, %s, radio check %.3f", self.alias, callsign, self.frequency)
|
||||
end
|
||||
-- Radio message.
|
||||
self:TransmissionPilot(text, flight)
|
||||
|
||||
@ -2713,7 +2719,17 @@ function FLIGHTCONTROL:_PlayerInfoAirbase(groupname)
|
||||
|
||||
local text=string.format("Airbase %s Info:", self.airbasename)
|
||||
text=text..string.format("\nATC Status: %s", self:GetState())
|
||||
text=text..string.format("\nFrequency: %.3f %s", self.frequency, UTILS.GetModulationName(self.modulation))
|
||||
|
||||
if type(self.frequency) == "table" then
|
||||
local multifreq = ""
|
||||
for i=1,#self.frequency do
|
||||
multifreq=string.format("%s%.2f %s, ",multifreq,self.frequency[i],UTILS.GetModulationName(self.modulation[i] or 0))
|
||||
end
|
||||
text=string.gsub(text,", $","")
|
||||
text=text..string.format("\nFrequencies: %s", multifreq)
|
||||
else
|
||||
text=text..string.format("\nFrequency: %.3f %s", self.frequency, UTILS.GetModulationName(self.modulation))
|
||||
end
|
||||
text=text..string.format("\nRunway Landing: %s", self:GetActiveRunwayText())
|
||||
text=text..string.format("\nRunway Takeoff: %s", self:GetActiveRunwayText(true))
|
||||
|
||||
@ -4458,13 +4474,31 @@ function FLIGHTCONTROL:_IsFlightOnRunway(flight)
|
||||
return nil
|
||||
end
|
||||
|
||||
--- [User] Set callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns.
|
||||
-- @param #FLIGHTCONTROL self
|
||||
-- @param #boolean ShortCallsign If true, only call out the major flight number. Default = `true`.
|
||||
-- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name for players as-is, no amendments or numbers. Default = `true`.
|
||||
-- @param #table CallsignTranslations (optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized
|
||||
-- callsigns from playername or group name.
|
||||
-- @return #FLIGHTCONTROL self
|
||||
function FLIGHTCONTROL:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||
if not ShortCallsign or ShortCallsign == false then
|
||||
self.ShortCallsign = false
|
||||
else
|
||||
self.ShortCallsign = true
|
||||
end
|
||||
self.Keepnumber = Keepnumber or false
|
||||
self.CallsignTranslations = CallsignTranslations
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get callsign name of a given flight.
|
||||
-- @param #FLIGHTCONTROL self
|
||||
-- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group.
|
||||
-- @return #string Callsign or "Ghostrider 1-1".
|
||||
function FLIGHTCONTROL:_GetCallsignName(flight)
|
||||
|
||||
local callsign=flight:GetCallsignName()
|
||||
local callsign=flight:GetCallsignName(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
||||
|
||||
--local name=string.match(callsign, "%a+")
|
||||
--local number=string.match(callsign, "%d+")
|
||||
|
||||
@ -4495,8 +4495,10 @@ function FLIGHTGROUP:_PlayerSubtitles()
|
||||
-- Switch setting.
|
||||
playerData.subtitles=not playerData.subtitles
|
||||
|
||||
local onoff = playerData.subtitles == true and "ON" or "OFF"
|
||||
|
||||
-- Display message.
|
||||
MESSAGE:New(string.format("%s, subtitles are now %s", playerData.name, tostring(playerData.subtitles)), 10, nil, true):ToGroup(self.group)
|
||||
MESSAGE:New(string.format("%s, subtitles are now %s", playerData.name, onoff), 10, nil, true):ToGroup(self.group)
|
||||
|
||||
else
|
||||
--TODO: Error
|
||||
|
||||
@ -10123,7 +10123,7 @@ function OPSGROUP:_CheckDamage()
|
||||
for _,_element in pairs(self.elements) do
|
||||
local element=_element --Ops.OpsGroup#OPSGROUP.Element
|
||||
|
||||
if element.status~=OPSGROUP.ElementStatus.DEAD and element.status~=OPSGROUP.ElementStatus.INUTERO then
|
||||
if element.status~=OPSGROUP.ElementStatus.DEAD and element.status~=OPSGROUP.ElementStatus.INUTERO then
|
||||
|
||||
-- Current life points.
|
||||
local life=element.unit:GetLife()
|
||||
@ -10135,8 +10135,8 @@ function OPSGROUP:_CheckDamage()
|
||||
self:ElementDamaged(element)
|
||||
damaged=true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -11748,8 +11748,11 @@ end
|
||||
|
||||
--- Get callsign of the first element alive.
|
||||
-- @param #OPSGROUP self
|
||||
-- @param #boolean ShortCallsign If true, append major flight number only
|
||||
-- @param #boolean Keepnumber (Player only) If true, and using a customized callsign in the #GROUP name after an #-sign, use all of that information.
|
||||
-- @param #table CallsignTranslations (optional) Translation table between callsigns
|
||||
-- @return #string Callsign name, e.g. Uzi11, or "Ghostrider11".
|
||||
function OPSGROUP:GetCallsignName()
|
||||
function OPSGROUP:GetCallsignName(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||
|
||||
local element=self:GetElementAlive()
|
||||
|
||||
@ -11757,6 +11760,9 @@ function OPSGROUP:GetCallsignName()
|
||||
self:T2(self.lid..string.format("Callsign %s", tostring(element.callsign)))
|
||||
local name=element.callsign or "Ghostrider11"
|
||||
name=name:gsub("-", "")
|
||||
if self.group:IsPlayer() or CallsignTranslations then
|
||||
name=self.group:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||
end
|
||||
return name
|
||||
end
|
||||
|
||||
@ -11778,7 +11784,7 @@ function OPSGROUP:_UpdatePosition()
|
||||
self.positionLast=self.position or self:GetVec3()
|
||||
self.headingLast=self.heading or self:GetHeading()
|
||||
self.orientXLast=self.orientX or self:GetOrientationX()
|
||||
self.velocityLast=self.velocity or self.group:GetVelocityMPS()
|
||||
self.velocityLast=self.velocity or self.group:GetVelocityMPS()
|
||||
|
||||
-- Current state.
|
||||
self.position=self:GetVec3()
|
||||
@ -12432,18 +12438,18 @@ function OPSGROUP:GetAmmoUnit(unit, display)
|
||||
if ammotable then
|
||||
|
||||
local weapons=#ammotable
|
||||
|
||||
--self:I(ammotable)
|
||||
|
||||
--self:I(ammotable)
|
||||
|
||||
-- Loop over all weapons.
|
||||
for w=1,weapons do
|
||||
|
||||
-- Number of current weapon.
|
||||
local Nammo=ammotable[w]["count"]
|
||||
|
||||
-- Range in meters. Seems only to exist for missiles (not shells).
|
||||
local rmin=ammotable[w]["desc"]["rangeMin"] or 0
|
||||
local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0
|
||||
|
||||
-- Range in meters. Seems only to exist for missiles (not shells).
|
||||
local rmin=ammotable[w]["desc"]["rangeMin"] or 0
|
||||
local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0
|
||||
|
||||
-- Type name of current weapon.
|
||||
local Tammo=ammotable[w]["desc"]["typeName"]
|
||||
@ -12707,7 +12713,7 @@ function OPSGROUP:_AddElementByName(unitname)
|
||||
element.gid=element.DCSunit:getNumber()
|
||||
element.uid=element.DCSunit:getID()
|
||||
--element.group=unit:GetGroup()
|
||||
element.controller=element.DCSunit:getController()
|
||||
element.controller=element.DCSunit:getController()
|
||||
element.Nhit=0
|
||||
element.opsgroup=self
|
||||
|
||||
|
||||
@ -80,7 +80,7 @@ PLAYERTASK = {
|
||||
|
||||
--- PLAYERTASK class version.
|
||||
-- @field #string version
|
||||
PLAYERTASK.version="0.1.1"
|
||||
PLAYERTASK.version="0.1.2"
|
||||
|
||||
--- Generic task condition.
|
||||
-- @type PLAYERTASK.Condition
|
||||
@ -110,7 +110,7 @@ function PLAYERTASK:New(Type, Target, Repeat, Times, TTSType)
|
||||
self.conditionSuccess = {}
|
||||
self.conditionFailure = {}
|
||||
self.TaskController = nil -- Ops.PlayerTask#PLAYERTASKCONTROLLER
|
||||
self.timestamp = timer.getTime()
|
||||
self.timestamp = timer.getAbsTime()
|
||||
self.TTSType = TTSType or "close air support"
|
||||
|
||||
if Repeat then
|
||||
@ -262,7 +262,7 @@ function PLAYERTASK:IsDone()
|
||||
return IsDone
|
||||
end
|
||||
|
||||
--- [User] Get clients assigned list as table
|
||||
--- [User] Get client names assigned as table of #strings
|
||||
-- @param #PLAYERTASK self
|
||||
-- @return #table clients
|
||||
-- @return #number clientcount
|
||||
@ -273,6 +273,17 @@ function PLAYERTASK:GetClients()
|
||||
return clientlist, count
|
||||
end
|
||||
|
||||
--- [User] Get #CLIENT objects assigned as table
|
||||
-- @param #PLAYERTASK self
|
||||
-- @return #table clients
|
||||
-- @return #number clientcount
|
||||
function PLAYERTASK:GetClientObjects()
|
||||
self:T(self.lid.."GetClientObjects")
|
||||
local clientlist = self.Clients:GetDataTable() or {}
|
||||
local count = self.Clients:Count()
|
||||
return clientlist, count
|
||||
end
|
||||
|
||||
--- [User] Count clients
|
||||
-- @param #PLAYERTASK self
|
||||
-- @return #number clientcount
|
||||
@ -548,6 +559,7 @@ end
|
||||
-- @return #PLAYERTASK self
|
||||
function PLAYERTASK:onafterPlanned(From, Event, To)
|
||||
self:T({From, Event, To})
|
||||
self.timestamp = timer.getAbsTime()
|
||||
return self
|
||||
end
|
||||
|
||||
@ -559,6 +571,7 @@ end
|
||||
-- @return #PLAYERTASK self
|
||||
function PLAYERTASK:onafterRequested(From, Event, To)
|
||||
self:T({From, Event, To})
|
||||
self.timestamp = timer.getAbsTime()
|
||||
return self
|
||||
end
|
||||
|
||||
@ -570,6 +583,7 @@ end
|
||||
-- @return #PLAYERTASK self
|
||||
function PLAYERTASK:onafterExecuting(From, Event, To)
|
||||
self:T({From, Event, To})
|
||||
self.timestamp = timer.getAbsTime()
|
||||
return self
|
||||
end
|
||||
|
||||
@ -581,6 +595,7 @@ end
|
||||
-- @return #PLAYERTASK self
|
||||
function PLAYERTASK:onafterStop(From, Event, To)
|
||||
self:T({From, Event, To})
|
||||
self.timestamp = timer.getAbsTime()
|
||||
return self
|
||||
end
|
||||
|
||||
@ -597,6 +612,7 @@ function PLAYERTASK:onafterClientAdded(From, Event, To, Client)
|
||||
local text = string.format("Player %s joined task %03d!",Client:GetPlayerName() or "Generic",self.PlayerTaskNr)
|
||||
self:I(self.lid..text)
|
||||
end
|
||||
self.timestamp = timer.getAbsTime()
|
||||
return self
|
||||
end
|
||||
|
||||
@ -611,6 +627,7 @@ function PLAYERTASK:onafterDone(From, Event, To)
|
||||
if self.TaskController then
|
||||
self.TaskController:__TaskDone(-1,self)
|
||||
end
|
||||
self.timestamp = timer.getAbsTime()
|
||||
self:__Stop(-1)
|
||||
return self
|
||||
end
|
||||
@ -626,6 +643,7 @@ function PLAYERTASK:onafterCancel(From, Event, To)
|
||||
if self.TaskController then
|
||||
self.TaskController:__TaskCancelled(-1,self)
|
||||
end
|
||||
self.timestamp = timer.getAbsTime()
|
||||
self:__Done(-1)
|
||||
return self
|
||||
end
|
||||
@ -644,6 +662,7 @@ function PLAYERTASK:onafterSuccess(From, Event, To)
|
||||
if self.TargetMarker then
|
||||
self.TargetMarker:Remove()
|
||||
end
|
||||
self.timestamp = timer.getAbsTime()
|
||||
self:__Done(-1)
|
||||
return self
|
||||
end
|
||||
@ -673,6 +692,7 @@ function PLAYERTASK:onafterFailed(From, Event, To)
|
||||
end
|
||||
self:__Done(-1)
|
||||
end
|
||||
self.timestamp = timer.getAbsTime()
|
||||
return self
|
||||
end
|
||||
-------------------------------------------------------------------------------------------------------------------
|
||||
@ -684,6 +704,11 @@ do
|
||||
-------------------------------------------------------------------------------------------------------------------
|
||||
-- PLAYERTASKCONTROLLER
|
||||
-- TODO: PLAYERTASKCONTROLLER
|
||||
-- DONE Playername customized
|
||||
-- DONE Coalition-level screen info to SET based
|
||||
-- DONE Flash directions
|
||||
-- DONE less rebuilds menu, Task info menu available after join
|
||||
-- DONE Limit menu entries
|
||||
-------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- PLAYERTASKCONTROLLER class.
|
||||
@ -715,8 +740,20 @@ do
|
||||
-- @field #boolean precisionbombing
|
||||
-- @field Ops.FlightGroup#FLIGHTGROUP LasingDrone
|
||||
-- @field Core.MarkerOps_BASE#MARKEROPS_BASE MarkerOps
|
||||
-- @field #boolean askinfomenu
|
||||
-- @field #boolean taskinfomenu
|
||||
-- @field #boolean MarkerReadOnly
|
||||
-- @field #table FlashPlayer List of player who switched Flashing Direction Info on
|
||||
-- @field #boolean AllowFlash Flashing directions for players allowed
|
||||
-- @field #number menuitemlimit
|
||||
-- @field #boolean activehasinfomenu
|
||||
-- @field #number holdmenutime
|
||||
-- @field #table customcallsigns
|
||||
-- @field #boolean ShortCallsign
|
||||
-- @field #boolean Keepnumber
|
||||
-- @field #table CallsignTranslations
|
||||
-- @field #table PlayerFlashMenu
|
||||
-- @field #table PlayerJoinMenu
|
||||
-- @field #table PlayerInfoMenu
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
---
|
||||
@ -893,6 +930,10 @@ do
|
||||
-- POINTEROVERTARGET = "%s, %s, pointer in reach for task %03d, lasing!",
|
||||
-- POINTERTARGETREPORT = "\nPointer in reach: %s\nLasing: %s",
|
||||
-- POINTERTARGETLASINGTTS = ". Pointer in reach and lasing.",
|
||||
-- TARGET = "Target",
|
||||
-- FLASHON = "%s - Flashing directions is now ON!",
|
||||
-- FLASHOFF = "%s - Flashing directions is now OFF!",
|
||||
-- FLASHMENU = "Flash Directions Switch",
|
||||
-- },
|
||||
--
|
||||
-- e.g.
|
||||
@ -1004,8 +1045,16 @@ PLAYERTASKCONTROLLER = {
|
||||
gettext = nil,
|
||||
locale = "en",
|
||||
precisionbombing = false,
|
||||
taskinfomenu = true,
|
||||
taskinfomenu = false,
|
||||
activehasinfomenu = false,
|
||||
MarkerReadOnly = false,
|
||||
customcallsigns = {},
|
||||
ShortCallsign = true,
|
||||
Keepnumber = false,
|
||||
CallsignTranslations = nil,
|
||||
PlayerFlashMenu = {},
|
||||
PlayerJoinMenu = {},
|
||||
PlayerInfoMenu = {},
|
||||
}
|
||||
|
||||
---
|
||||
@ -1030,9 +1079,9 @@ AUFTRAG.Type.PRECISIONBOMBING = "Precision Bombing"
|
||||
-- @field #number AAA GROUP.Attribute.GROUND_AAA
|
||||
-- @field #number EWR GROUP.Attribute.GROUND_EWR
|
||||
PLAYERTASKCONTROLLER.SeadAttributes = {
|
||||
SAM = GROUP.Attribute.GROUND_SAM,
|
||||
AAA = GROUP.Attribute.GROUND_AAA,
|
||||
EWR = GROUP.Attribute.GROUND_EWR,
|
||||
SAM = GROUP.Attribute.GROUND_SAM,
|
||||
AAA = GROUP.Attribute.GROUND_AAA,
|
||||
EWR = GROUP.Attribute.GROUND_EWR,
|
||||
}
|
||||
|
||||
---
|
||||
@ -1094,6 +1143,10 @@ PLAYERTASKCONTROLLER.Messages = {
|
||||
POINTEROVERTARGET = "%s, %s, pointer in reach for task %03d, lasing!",
|
||||
POINTERTARGETREPORT = "\nPointer in reach: %s\nLasing: %s",
|
||||
POINTERTARGETLASINGTTS = ". Pointer in reach and lasing.",
|
||||
TARGET = "Target",
|
||||
FLASHON = "%s - Flashing directions is now ON!",
|
||||
FLASHOFF = "%s - Flashing directions is now OFF!",
|
||||
FLASHMENU = "Flash Directions Switch",
|
||||
},
|
||||
DE = {
|
||||
TASKABORT = "Auftrag abgebrochen!",
|
||||
@ -1151,12 +1204,16 @@ PLAYERTASKCONTROLLER.Messages = {
|
||||
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.",
|
||||
TARGET = "Ziel",
|
||||
FLASHON = "%s - Richtungsangaben einblenden ist EIN!",
|
||||
FLASHOFF = "%s - Richtungsangaben einblenden ist AUS!",
|
||||
FLASHMENU = "Richtungsangaben Schalter",
|
||||
},
|
||||
}
|
||||
|
||||
--- PLAYERTASK class version.
|
||||
-- @field #string version
|
||||
PLAYERTASKCONTROLLER.version="0.1.30"
|
||||
PLAYERTASKCONTROLLER.version="0.1.36"
|
||||
|
||||
--- Constructor
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
@ -1190,16 +1247,26 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter)
|
||||
self.TasksPerPlayer = FIFO:New() -- Utilities.FiFo#FIFO
|
||||
self.PrecisionTasks = FIFO:New() -- Utilities.FiFo#FIFO
|
||||
self.PlayerMenu = {} -- #table
|
||||
self.FlashPlayer = {} -- #table
|
||||
self.AllowFlash = false
|
||||
self.lasttaskcount = 0
|
||||
|
||||
self.taskinfomenu = false
|
||||
self.activehasinfomenu = false
|
||||
self.MenuName = nil
|
||||
self.menuitemlimit = 5
|
||||
self.holdmenutime = 30
|
||||
|
||||
self.MarkerReadOnly = false
|
||||
|
||||
self.repeatonfailed = true
|
||||
self.repeattimes = 5
|
||||
self.UseGroupNames = true
|
||||
|
||||
self.customcallsigns = {}
|
||||
self.ShortCallsign = true
|
||||
self.Keepnumber = false
|
||||
self.CallsignTranslations = nil
|
||||
|
||||
if ClientFilter then
|
||||
self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterPrefixes(ClientFilter):FilterStart()
|
||||
@ -1311,6 +1378,50 @@ function PLAYERTASKCONTROLLER:_InitLocalization()
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Set flash directions option for player (player based info)
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param #boolean OnOff Set to `true` to switch on and `false` to switch off. Default is OFF.
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:SetAllowFlashDirection(OnOff)
|
||||
self:T(self.lid.."SetAllowFlashDirection")
|
||||
self.AllowFlash = OnOff
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Set callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns.
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param #boolean ShortCallsign If true, only call out the major flight number
|
||||
-- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name for players as-is, no amendments or numbers.
|
||||
-- @param #table CallsignTranslations (optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized
|
||||
-- callsigns from playername or group name.
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||
if not ShortCallsign or ShortCallsign == false then
|
||||
self.ShortCallsign = false
|
||||
else
|
||||
self.ShortCallsign = true
|
||||
end
|
||||
self.Keepnumber = Keepnumber or false
|
||||
self.CallsignTranslations = CallsignTranslations
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Get text for text-to-speech.
|
||||
-- Numbers are spaced out, e.g. "Heading 180" becomes "Heading 1 8 0 ".
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param #string text Original text.
|
||||
-- @return #string Spoken text.
|
||||
function PLAYERTASKCONTROLLER:_GetTextForSpeech(text)
|
||||
|
||||
-- Space out numbers.
|
||||
text=string.gsub(text,"%d","%1 ")
|
||||
-- get rid of leading or trailing spaces
|
||||
text=string.gsub(text,"^%s*","")
|
||||
text=string.gsub(text,"%s*$","")
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
--- [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)
|
||||
@ -1329,6 +1440,22 @@ function PLAYERTASKCONTROLLER:SetTaskRepetition(OnOff, Repeats)
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Send message to SET_CLIENT of players
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param #string Text the text to be send
|
||||
-- @param #number Seconds (optional) Seconds to show, default 10
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:_SendMessageToClients(Text,Seconds)
|
||||
self:T(self.lid.."_SendMessageToClients")
|
||||
local seconds = Seconds or 10
|
||||
self.ClientSet:ForEachClient(
|
||||
function (Client)
|
||||
local m = MESSAGE:New(Text,seconds,"Tasking"):ToClient(Client)
|
||||
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).
|
||||
@ -1416,14 +1543,15 @@ end
|
||||
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]+)")
|
||||
local ttsplayername = nil
|
||||
if not self.customcallsigns[playername] then
|
||||
local playergroup = Client:GetGroup()
|
||||
ttsplayername = playergroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
||||
local newplayername = self:_GetTextForSpeech(ttsplayername)
|
||||
self.customcallsigns[playername] = newplayername
|
||||
ttsplayername = newplayername
|
||||
else
|
||||
ttsplayername = self.customcallsigns[playername]
|
||||
end
|
||||
return playername, ttsplayername
|
||||
end
|
||||
@ -1456,6 +1584,24 @@ function PLAYERTASKCONTROLLER:DisableTaskInfoMenu()
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Set menu build fine-tuning options
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param #boolean InfoMenu If `true` this option will allow to show the Task Info-Menu also when a player has an active task.
|
||||
-- Since the menu isn't refreshed if a player holds an active task, the info in there might be stale.
|
||||
-- @param #number ItemLimit Number of items per task type to show, default 5.
|
||||
-- @param #number HoldTime Minimum number of seconds between menu refreshes (called every 30 secs) if a player has **no active task**.
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:SetMenuOptions(InfoMenu,ItemLimit,HoldTime)
|
||||
self:T(self.lid.."SetMenuOptions")
|
||||
self.activehasinfomenu = InfoMenu or false
|
||||
if self.activehasinfomenu then
|
||||
self:EnableTaskInfoMenu()
|
||||
end
|
||||
self.menuitemlimit = ItemLimit or 5
|
||||
self.holdmenutime = HoldTime or 30
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Forbid F10 markers to be deleted by pilots. Note: Marker will auto-delete when the undelying task is done.
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
@ -1519,11 +1665,15 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData)
|
||||
modulation = UTILS.GetModulationName(modulation)
|
||||
local switchtext = self.gettext:GetEntry("BROADCAST",self.locale)
|
||||
|
||||
local playername = EventData.IniPlayerName
|
||||
if string.find(playername,"|") then
|
||||
local playername = EventData.IniPlayerName
|
||||
if EventData.IniGroup then
|
||||
-- personalized flight name in player naming
|
||||
playername = string.match(playername,"| ([%a]+)")
|
||||
if self.customcallsigns[playername] then
|
||||
self.customcallsigns[playername] = nil
|
||||
end
|
||||
playername = EventData.IniGroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber)
|
||||
end
|
||||
playername = self:_GetTextForSpeech(playername)
|
||||
--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)
|
||||
@ -1532,11 +1682,6 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData)
|
||||
return self
|
||||
end
|
||||
|
||||
function PLAYERTASKCONTROLLER:_DummyMenu(group)
|
||||
self:T(self.lid.."_DummyMenu")
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Set locale for localization. Defaults to "en"
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param #string Locale The locale to use
|
||||
@ -1634,12 +1779,13 @@ function PLAYERTASKCONTROLLER:_GetTasksPerType()
|
||||
for _,_task in pairs(datatable) do
|
||||
local task = _task -- Ops.PlayerTask#PLAYERTASK
|
||||
local threat = task.Target:GetThreatLevelMax()
|
||||
threattable[#threattable+1]={task=task,threat=threat}
|
||||
if not task:IsDone() then
|
||||
threattable[#threattable+1]={task=task,threat=threat}
|
||||
end
|
||||
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
|
||||
@ -1684,8 +1830,11 @@ function PLAYERTASKCONTROLLER:_CheckTaskQueue()
|
||||
self:T("*****Removing player " .. _id)
|
||||
self.TasksPerPlayer:PullByID(_id)
|
||||
end
|
||||
local task = self.TaskQueue:PullByID(_id) -- Ops.PlayerTask#PLAYERTASK
|
||||
task = nil
|
||||
local TNow = timer.getAbsTime()
|
||||
if TNow - task.timestamp > 10 then
|
||||
local task = self.TaskQueue:PullByID(_id) -- Ops.PlayerTask#PLAYERTASK
|
||||
task = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1789,8 +1938,12 @@ function PLAYERTASKCONTROLLER:_CheckPrecisionTasks()
|
||||
local text = ""
|
||||
for _,playername in pairs(clients) do
|
||||
local pointertext = self.gettext:GetEntry("POINTEROVERTARGET",self.locale)
|
||||
local ttsplayername = playername
|
||||
if self.customcallsigns[playername] then
|
||||
ttsplayername = self.customcallsigns[playername]
|
||||
end
|
||||
--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)
|
||||
text = string.format(pointertext, ttsplayername, self.MenuName or self.Name, task.PlayerTaskNr)
|
||||
if not self.NoScreenOutput then
|
||||
local client = nil
|
||||
self.ClientSet:ForEachClient(
|
||||
@ -1921,15 +2074,15 @@ end
|
||||
-- Default attribute types are: GROUP.Attribute.GROUND_SAM, GROUP.Attribute.GROUND_AAA, and GROUP.Attribute.GROUND_EWR.
|
||||
-- If you want to e.g. exclude AAA, so target groups with this attribute are assigned CAS or BAI tasks, and not SEAD, use this function as follows:
|
||||
--
|
||||
-- `mycontroller:SetSEADAttributes({GROUP.Attribute.GROUND_SAM, GROUP.Attribute.GROUND_EWR})`
|
||||
-- `mycontroller:SetSEADAttributes({GROUP.Attribute.GROUND_SAM, GROUP.Attribute.GROUND_EWR})`
|
||||
--
|
||||
function PLAYERTASKCONTROLLER:SetSEADAttributes(Attributes)
|
||||
self:T(self.lid.."SetSEADAttributes")
|
||||
if type(Attributes) ~= "table" then
|
||||
Attributes = {Attributes}
|
||||
end
|
||||
self.SeadAttributes = Attributes
|
||||
return self
|
||||
self:T(self.lid.."SetSEADAttributes")
|
||||
if type(Attributes) ~= "table" then
|
||||
Attributes = {Attributes}
|
||||
end
|
||||
self.SeadAttributes = Attributes
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Function the check against SeadAttributes
|
||||
@ -1937,15 +2090,15 @@ end
|
||||
-- @param #string Attribute
|
||||
-- @return #boolean IsSead
|
||||
function PLAYERTASKCONTROLLER:_IsAttributeSead(Attribute)
|
||||
self:T(self.lid.."_IsAttributeSead?")
|
||||
local IsSead = false
|
||||
for _,_attribute in pairs(self.SeadAttributes) do
|
||||
if Attribute == _attribute then
|
||||
IsSead = true
|
||||
break
|
||||
end
|
||||
end
|
||||
return IsSead
|
||||
self:T(self.lid.."_IsAttributeSead?")
|
||||
local IsSead = false
|
||||
for _,_attribute in pairs(self.SeadAttributes) do
|
||||
if Attribute == _attribute then
|
||||
IsSead = true
|
||||
break
|
||||
end
|
||||
end
|
||||
return IsSead
|
||||
end
|
||||
|
||||
--- [Internal] Add a task to the task queue
|
||||
@ -1975,22 +2128,22 @@ function PLAYERTASKCONTROLLER:_AddTask(Target)
|
||||
elseif targetobject:IsInstanceOf("GROUP") then
|
||||
self:T("SEAD Check GROUP")
|
||||
local attribute = targetobject:GetAttribute()
|
||||
if self:_IsAttributeSead(attribute) then
|
||||
type = AUFTRAG.Type.SEAD
|
||||
--ttstype = "suppress air defense"
|
||||
ttstype = self.gettext:GetEntry("SEADTTS",self.locale)
|
||||
end
|
||||
if self:_IsAttributeSead(attribute) then
|
||||
type = AUFTRAG.Type.SEAD
|
||||
--ttstype = "suppress air defense"
|
||||
ttstype = self.gettext:GetEntry("SEADTTS",self.locale)
|
||||
end
|
||||
elseif targetobject:IsInstanceOf("SET_GROUP") then
|
||||
self:T("SEAD Check SET_GROUP")
|
||||
targetobject:ForEachGroup(
|
||||
function (group)
|
||||
local attribute = group:GetAttribute()
|
||||
if self:_IsAttributeSead(attribute) then
|
||||
type = AUFTRAG.Type.SEAD
|
||||
--ttstype = "suppress air defense"
|
||||
ttstype = self.gettext:GetEntry("SEADTTS",self.locale)
|
||||
end
|
||||
end
|
||||
if self:_IsAttributeSead(attribute) 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:T("SEAD Check SET_UNIT")
|
||||
@ -2135,7 +2288,7 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task)
|
||||
-- Player already has a task
|
||||
if not self.NoScreenOutput then
|
||||
local text = self.gettext:GetEntry("HAVEACTIVETASK",self.locale)
|
||||
local m=MESSAGE:New(text,"10","Tasking"):ToGroup(Group)
|
||||
local m=MESSAGE:New(text,"10","Tasking"):ToClient(Client)
|
||||
end
|
||||
return self
|
||||
end
|
||||
@ -2151,9 +2304,11 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task)
|
||||
local text = string.format(joined,ttsplayername, self.MenuName or self.Name, Task.TTSType, Task.PlayerTaskNr)
|
||||
self:T(self.lid..text)
|
||||
if not self.NoScreenOutput then
|
||||
local m=MESSAGE:New(text,"10","Tasking"):ToAll()
|
||||
self:_SendMessageToClients(text)
|
||||
--local m=MESSAGE:New(text,"10","Tasking"):ToAll()
|
||||
end
|
||||
if self.UseSRS then
|
||||
self:I(self.lid..text)
|
||||
self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2)
|
||||
end
|
||||
self.TasksPerPlayer:Push(Task,playername)
|
||||
@ -2168,6 +2323,55 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task)
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Switch flashing info for a client
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param Wrapper.Group#GROUP Group
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:_SwitchFlashing(Group, Client)
|
||||
self:T(self.lid.."_SwitchFlashing")
|
||||
local playername, ttsplayername = self:_GetPlayerName(Client)
|
||||
if (not self.FlashPlayer[playername]) or (self.FlashPlayer[playername] == false) then
|
||||
-- Switch on
|
||||
self.FlashPlayer[playername] = Client
|
||||
local flashtext = self.gettext:GetEntry("FLASHON",self.locale)
|
||||
local text = string.format(flashtext,ttsplayername)
|
||||
local m = MESSAGE:New(text,10,"Tasking"):ToClient(Client)
|
||||
else
|
||||
-- Switch off
|
||||
self.FlashPlayer[playername] = false
|
||||
local flashtext = self.gettext:GetEntry("FLASHOFF",self.locale)
|
||||
local text = string.format(flashtext,ttsplayername)
|
||||
local m = MESSAGE:New(text,10,"Tasking"):ToClient(Client)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Flashing directional info for a client
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:_FlashInfo()
|
||||
self:T(self.lid.."_FlashInfo")
|
||||
for _playername,_client in pairs(self.FlashPlayer) do
|
||||
if _client and _client:IsAlive() then
|
||||
if self.TasksPerPlayer:HasUniqueID(_playername) then
|
||||
local task = self.TasksPerPlayer:ReadByID(_playername) -- Ops.PlayerTask#PLAYERTASK
|
||||
local Coordinate = task.Target:GetCoordinate()
|
||||
local CoordText = ""
|
||||
if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then
|
||||
CoordText = Coordinate:ToStringA2G(_client)
|
||||
else
|
||||
CoordText = Coordinate:ToStringA2A(_client)
|
||||
end
|
||||
local targettxt = self.gettext:GetEntry("TARGET",self.locale)
|
||||
local text = "Target: "..CoordText
|
||||
local m = MESSAGE:New(text,10,"Tasking"):ToClient(_client)
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Show active task info
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param Wrapper.Group#GROUP Group
|
||||
@ -2221,9 +2425,10 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task)
|
||||
local clienttxt = self.gettext:GetEntry("PILOTS",self.locale)
|
||||
if clientcount > 0 then
|
||||
for _,_name in pairs(clientlist) do
|
||||
if string.find(_name,"|") then
|
||||
if self.customcallsigns[_name] then
|
||||
-- personalized flight name in player naming
|
||||
_name = string.match(_name,"| ([%a]+)")
|
||||
--_name = string.match(_name,"| ([%a]+)")
|
||||
_name = self.customcallsigns[_name]
|
||||
end
|
||||
clienttxt = clienttxt .. _name .. ", "
|
||||
end
|
||||
@ -2252,7 +2457,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task)
|
||||
text = self.gettext:GetEntry("NOACTIVETASK",self.locale)
|
||||
end
|
||||
if not self.NoScreenOutput then
|
||||
local m=MESSAGE:New(text,15,"Tasking"):ToGroup(Group)
|
||||
local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client)
|
||||
end
|
||||
return self
|
||||
end
|
||||
@ -2281,7 +2486,7 @@ function PLAYERTASKCONTROLLER:_MarkTask(Group, Client)
|
||||
text = self.gettext:GetEntry("NOACTIVETASK",self.locale)
|
||||
end
|
||||
if not self.NoScreenOutput then
|
||||
local m=MESSAGE:New(text,"10","Tasking"):ToGroup(Group)
|
||||
local m=MESSAGE:New(text,"10","Tasking"):ToClient(Client)
|
||||
end
|
||||
return self
|
||||
end
|
||||
@ -2309,7 +2514,7 @@ function PLAYERTASKCONTROLLER:_SmokeTask(Group, Client)
|
||||
text = self.gettext:GetEntry("NOACTIVETASK",self.locale)
|
||||
end
|
||||
if not self.NoScreenOutput then
|
||||
local m=MESSAGE:New(text,15,"Tasking"):ToGroup(Group)
|
||||
local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client)
|
||||
end
|
||||
return self
|
||||
end
|
||||
@ -2337,7 +2542,7 @@ function PLAYERTASKCONTROLLER:_FlareTask(Group, Client)
|
||||
text = self.gettext:GetEntry("NOACTIVETASK",self.locale)
|
||||
end
|
||||
if not self.NoScreenOutput then
|
||||
local m=MESSAGE:New(text,15,"Tasking"):ToGroup(Group)
|
||||
local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client)
|
||||
end
|
||||
return self
|
||||
end
|
||||
@ -2366,25 +2571,83 @@ function PLAYERTASKCONTROLLER:_AbortTask(Group, Client)
|
||||
text = self.gettext:GetEntry("NOACTIVETASK",self.locale)
|
||||
end
|
||||
if not self.NoScreenOutput then
|
||||
local m=MESSAGE:New(text,15,"Tasking"):ToGroup(Group)
|
||||
local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client)
|
||||
end
|
||||
self:_BuildMenus(Client,true)
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Build Task Info Menu
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param Wrapper.Group#GROUP group
|
||||
-- @param Wrapper.Client#CLIENT client
|
||||
-- @param #string playername
|
||||
-- @param Core.Menu#MENU_BASE topmenu
|
||||
-- @param #table tasktypes
|
||||
-- @param #table taskpertype
|
||||
-- @return #table taskinfomenu
|
||||
function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype)
|
||||
self:T(self.lid.."_BuildTaskInfoMenu")
|
||||
local taskinfomenu = nil
|
||||
if self.taskinfomenu then
|
||||
local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale)
|
||||
local taskinfomenu = MENU_GROUP_DELAYED:New(group,menutaskinfo,topmenu)
|
||||
local ittypes = {}
|
||||
local itaskmenu = {}
|
||||
|
||||
for _tasktype,_data in pairs(tasktypes) do
|
||||
ittypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,taskinfomenu)
|
||||
local tasks = taskpertype[_tasktype] or {}
|
||||
local n = 0
|
||||
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
|
||||
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
|
||||
local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ittypes[_tasktype],self._ActiveTaskInfo,self,group,client,_task)
|
||||
--taskentry:SetTag(playername)
|
||||
itaskmenu[#itaskmenu+1] = taskentry
|
||||
-- keep max items limit
|
||||
n = n + 1
|
||||
if n >= self.menuitemlimit then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return taskinfomenu
|
||||
end
|
||||
|
||||
--- [Internal] Build client menus
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param Wrapper.Client#CLIENT Client (optional) build for this client name only
|
||||
-- @param #boolean enforced
|
||||
-- @param #boolean fromsuccess
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced)
|
||||
function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess)
|
||||
self:T(self.lid.."_BuildMenus")
|
||||
|
||||
local clients = self.ClientSet:GetAliveSet()
|
||||
|
||||
local joinorabort = false
|
||||
local timedbuild = false
|
||||
|
||||
if Client then
|
||||
-- client + enforced -- join task or abort
|
||||
clients = {Client}
|
||||
enforced = true
|
||||
joinorabort = true
|
||||
end
|
||||
|
||||
for _,_client in pairs(clients) do
|
||||
@ -2394,11 +2657,6 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced)
|
||||
local unknown = self.gettext:GetEntry("UNKNOWN",self.locale)
|
||||
local playername = client:GetPlayerName() or unknown
|
||||
if group and client then
|
||||
---
|
||||
-- Conditions for menu rebuild
|
||||
-- 1) Player has no menu
|
||||
-- 2) Player has no running task
|
||||
-- 3) enforced
|
||||
---
|
||||
-- TOPMENU
|
||||
---
|
||||
@ -2407,26 +2665,47 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced)
|
||||
local menuname = self.MenuName or longname
|
||||
local playerhastask = false
|
||||
|
||||
if self:_CheckPlayerHasTask(playername) then playerhastask = true end
|
||||
if self:_CheckPlayerHasTask(playername) and not fromsuccess then playerhastask = true end
|
||||
local topmenu = nil
|
||||
|
||||
self:T("Playerhastask = "..tostring(playerhastask).." Enforced = "..tostring(enforced))
|
||||
self:T("Playerhastask = "..tostring(playerhastask).." Enforced = "..tostring(enforced).." Join or Abort = "..tostring(joinorabort))
|
||||
|
||||
-- Cases to rebuild menu
|
||||
-- 1) new player
|
||||
-- 2) player joined a task, joinorabort = true
|
||||
-- 3) player left a task, joinorabort = true
|
||||
-- 4) player has no task, but number of tasks changed, and last build > 30 secs ago
|
||||
if self.PlayerMenu[playername] then
|
||||
if enforced or not playerhastask then
|
||||
-- NOT a new player
|
||||
-- 2)+3) Join or abort?
|
||||
if joinorabort then
|
||||
self.PlayerMenu[playername]:RemoveSubMenus()
|
||||
self.PlayerMenu[playername]:SetTag(timer.getAbsTime())
|
||||
topmenu = self.PlayerMenu[playername]
|
||||
elseif (not playerhastask) or enforced then
|
||||
-- 4) last build > 30 secs?
|
||||
local T0 = timer.getAbsTime()
|
||||
local TDiff = T0-self.PlayerMenu[playername].MenuTag
|
||||
self:T("TDiff = "..string.format("%.2d",TDiff))
|
||||
if TDiff >= self.holdmenutime then
|
||||
self.PlayerMenu[playername]:RemoveSubMenus()
|
||||
self.PlayerMenu[playername]:SetTag(timer.getAbsTime())
|
||||
timedbuild = true
|
||||
end
|
||||
topmenu = self.PlayerMenu[playername]
|
||||
end
|
||||
topmenu = self.PlayerMenu[playername]
|
||||
else
|
||||
-- 1) new player#
|
||||
topmenu = MENU_GROUP_DELAYED:New(group,menuname,nil)
|
||||
self.PlayerMenu[playername] = topmenu
|
||||
self.PlayerMenu[playername]:SetTag(timer.getAbsTime())
|
||||
end
|
||||
|
||||
---
|
||||
-- ACTIVE TASK MENU
|
||||
---
|
||||
if playerhastask and enforced then
|
||||
|
||||
--self:T("Building Active Task Menus for "..playername)
|
||||
local menuactive = self.gettext:GetEntry("MENUACTIVE",self.locale)
|
||||
local menuinfo = self.gettext:GetEntry("MENUINFO",self.locale)
|
||||
local menumark = self.gettext:GetEntry("MENUMARK",self.locale)
|
||||
@ -2443,24 +2722,31 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced)
|
||||
local flare = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._FlareTask,self,group,client)
|
||||
end
|
||||
local abort = MENU_GROUP_COMMAND_DELAYED:New(group,menuabort,active,self._AbortTask,self,group,client)
|
||||
|
||||
elseif (self.TaskQueue:Count() > 0 and enforced) or (not playerhastask) then
|
||||
if self.activehasinfomenu and self.taskinfomenu then
|
||||
--self:T("Building Active-Info Menus for "..playername)
|
||||
local tasktypes = self:_GetAvailableTaskTypes()
|
||||
local taskpertype = self:_GetTasksPerType()
|
||||
if self.PlayerInfoMenu[playername] then
|
||||
self.PlayerInfoMenu[playername]:RemoveSubMenus()
|
||||
end
|
||||
self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype)
|
||||
end
|
||||
elseif (self.TaskQueue:Count() > 0 and enforced) or (not playerhastask and (timedbuild or joinorabort)) then
|
||||
--self:T("Building Join Menus for "..playername)
|
||||
---
|
||||
-- JOIN TASK MENU
|
||||
---
|
||||
local tasktypes = self:_GetAvailableTaskTypes()
|
||||
local taskpertype = self:_GetTasksPerType()
|
||||
local menujoin = self.gettext:GetEntry("MENUJOIN",self.locale)
|
||||
local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale)
|
||||
local joinmenu = MENU_GROUP_DELAYED:New(group,menujoin,topmenu)
|
||||
|
||||
local ttypes = {}
|
||||
local taskmenu = {}
|
||||
local ittypes = {}
|
||||
local itaskmenu = {}
|
||||
for _tasktype,_data in pairs(tasktypes) do
|
||||
ttypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,joinmenu)
|
||||
local tasks = taskpertype[_tasktype] or {}
|
||||
local n = 0
|
||||
for _,_task in pairs(tasks) do
|
||||
_task = _task -- Ops.PlayerTask#PLAYERTASK
|
||||
local pilotcount = _task:CountClients()
|
||||
@ -2478,45 +2764,21 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced)
|
||||
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_DELAYED:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task)
|
||||
taskentry:SetTag(playername)
|
||||
--taskentry:SetTag(playername)
|
||||
taskmenu[#taskmenu+1] = taskentry
|
||||
--end
|
||||
n = n + 1
|
||||
if n >= self.menuitemlimit then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
--joinmenu:Set()
|
||||
|
||||
if self.taskinfomenu then
|
||||
local taskinfomenu = MENU_GROUP_DELAYED:New(group,menutaskinfo,topmenu)
|
||||
for _tasktype,_data in pairs(tasktypes) do
|
||||
ittypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,taskinfomenu)
|
||||
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
|
||||
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_DELAYED:New(group,text,ittypes[_tasktype],self._ActiveTaskInfo,self,group,client,_task)
|
||||
taskentry:SetTag(playername)
|
||||
itaskmenu[#itaskmenu+1] = taskentry
|
||||
--end
|
||||
end
|
||||
--self:T("Building Join-Info Menus for "..playername)
|
||||
if self.PlayerInfoMenu[playername] then
|
||||
self.PlayerInfoMenu[playername]:RemoveSubMenus()
|
||||
end
|
||||
--taskinfomenu:Set()
|
||||
self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype)
|
||||
end
|
||||
elseif self.TaskQueue:Count() == 0 then
|
||||
-- no tasks (yet)
|
||||
@ -2525,7 +2787,11 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced)
|
||||
end
|
||||
---
|
||||
-- REFRESH MENU
|
||||
---
|
||||
---
|
||||
if self.AllowFlash then
|
||||
local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale)
|
||||
local flashmenu = MENU_GROUP_COMMAND_DELAYED:New(group,flashtext,self.PlayerMenu[playername],self._SwitchFlashing,self,group,client)
|
||||
end
|
||||
self.PlayerMenu[playername]:Set()
|
||||
end
|
||||
end
|
||||
@ -2769,11 +3035,14 @@ end
|
||||
-- @param #string To
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:onafterStatus(From, Event, To)
|
||||
self:I({From, Event, To})
|
||||
self:T({From, Event, To})
|
||||
|
||||
self:_CheckTargetQueue()
|
||||
self:_CheckTaskQueue()
|
||||
self:_CheckPrecisionTasks()
|
||||
if self.AllowFlash then
|
||||
self:_FlashInfo()
|
||||
end
|
||||
|
||||
local targetcount = self.TargetQueue:Count()
|
||||
local taskcount = self.TaskQueue:Count()
|
||||
@ -2826,7 +3095,8 @@ function PLAYERTASKCONTROLLER:onafterTaskCancelled(From, Event, To, Task)
|
||||
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)
|
||||
self:_SendMessageToClients(taskname,15)
|
||||
--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))
|
||||
@ -2849,12 +3119,17 @@ function PLAYERTASKCONTROLLER:onafterTaskSuccess(From, Event, To, Task)
|
||||
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)
|
||||
self:_SendMessageToClients(taskname,15)
|
||||
--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
|
||||
local clients=Task:GetClientObjects()
|
||||
for _,client in pairs(clients) do
|
||||
self:_BuildMenus(client,true,true)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@ -2872,7 +3147,8 @@ function PLAYERTASKCONTROLLER:onafterTaskFailed(From, Event, To, Task)
|
||||
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)
|
||||
self:_SendMessageToClients(taskname,15)
|
||||
--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))
|
||||
@ -2895,7 +3171,8 @@ function PLAYERTASKCONTROLLER:onafterTaskRepeatOnFailed(From, Event, To, Task)
|
||||
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)
|
||||
self:_SendMessageToClients(taskname,15)
|
||||
--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))
|
||||
@ -2917,7 +3194,8 @@ function PLAYERTASKCONTROLLER:onafterTaskAdded(From, Event, To, Task)
|
||||
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)
|
||||
self:_SendMessageToClients(taskname,15)
|
||||
--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))
|
||||
|
||||
@ -143,6 +143,107 @@ MSRS = {
|
||||
-- @field #string version
|
||||
MSRS.version="0.1.0"
|
||||
|
||||
--- Voices
|
||||
-- @type Voices
|
||||
MSRS.Voices = {
|
||||
Microsoft = {
|
||||
["Hedda"] = "Microsoft Hedda Desktop", -- de-DE
|
||||
["Hazel"] = "Microsoft Hazel Desktop", -- en-GB
|
||||
["David"] = "Microsoft David Desktop", -- en-US
|
||||
["Zira"] = "Microsoft Zira Desktop", -- en-US
|
||||
["Hortense"] = "Microsoft Hortense Desktop", --fr-FR
|
||||
},
|
||||
Google = {
|
||||
Standard = {
|
||||
["en_AU_Standard_A"] = 'en-AU-Standard-A', -- [1] FEMALE
|
||||
["en_AU_Standard_B"] = 'en-AU-Standard-B', -- [2] MALE
|
||||
["en_AU_Standard_C"] = 'en-AU-Standard-C', -- [3] FEMALE
|
||||
["en_AU_Standard_D"] = 'en-AU-Standard-D', -- [4] MALE
|
||||
["en_IN_Standard_A"] = 'en-IN-Standard-A', -- [5] FEMALE
|
||||
["en_IN_Standard_B"] = 'en-IN-Standard-B', -- [6] MALE
|
||||
["en_IN_Standard_C"] = 'en-IN-Standard-C', -- [7] MALE
|
||||
["en_IN_Standard_D"] = 'en-IN-Standard-D', -- [8] FEMALE
|
||||
["en_GB_Standard_A"] = 'en-GB-Standard-A', -- [9] FEMALE
|
||||
["en_GB_Standard_B"] = 'en-GB-Standard-B', -- [10] MALE
|
||||
["en_GB_Standard_C"] = 'en-GB-Standard-C', -- [11] FEMALE
|
||||
["en_GB_Standard_D"] = 'en-GB-Standard-D', -- [12] MALE
|
||||
["en_GB_Standard_F"] = 'en-GB-Standard-F', -- [13] FEMALE
|
||||
["en_US_Standard_A"] = 'en-US-Standard-A', -- [14] MALE
|
||||
["en_US_Standard_B"] = 'en-US-Standard-B', -- [15] MALE
|
||||
["en_US_Standard_C"] = 'en-US-Standard-C', -- [16] FEMALE
|
||||
["en_US_Standard_D"] = 'en-US-Standard-D', -- [17] MALE
|
||||
["en_US_Standard_E"] = 'en-US-Standard-E', -- [18] FEMALE
|
||||
["en_US_Standard_F"] = 'en-US-Standard-F', -- [19] FEMALE
|
||||
["en_US_Standard_G"] = 'en-US-Standard-G', -- [20] FEMALE
|
||||
["en_US_Standard_H"] = 'en-US-Standard-H', -- [21] FEMALE
|
||||
["en_US_Standard_I"] = 'en-US-Standard-I', -- [22] MALE
|
||||
["en_US_Standard_J"] = 'en-US-Standard-J', -- [23] MALE
|
||||
["fr_FR_Standard_A"] = "fr-FR-Standard-A", -- Female
|
||||
["fr_FR_Standard_B"] = "fr-FR-Standard-B", -- Male
|
||||
["fr_FR_Standard_C"] = "fr-FR-Standard-C", -- Female
|
||||
["fr_FR_Standard_D"] = "fr-FR-Standard-D", -- Male
|
||||
["fr_FR_Standard_E"] = "fr-FR-Standard-E", -- Female
|
||||
["de_DE_Standard_A"] = "de-DE-Standard-A", -- Female
|
||||
["de_DE_Standard_B"] = "de-DE-Standard-B", -- Male
|
||||
["de_DE_Standard_C"] = "de-DE-Standard-C", -- Female
|
||||
["de_DE_Standard_D"] = "de-DE-Standard-D", -- Male
|
||||
["de_DE_Standard_E"] = "de-DE-Standard-E", -- Male
|
||||
["de_DE_Standard_F"] = "de-DE-Standard-F", -- Female
|
||||
["es_ES_Standard_A"] = "es-ES-Standard-A", -- Female
|
||||
["es_ES_Standard_B"] = "es-ES-Standard-B", -- Male
|
||||
["es_ES_Standard_C"] = "es-ES-Standard-C", -- Female
|
||||
["es_ES_Standard_D"] = "es-ES-Standard-D", -- Female
|
||||
["it_IT_Standard_A"] = "it-IT-Standard-A", -- Female
|
||||
["it_IT_Standard_B"] = "it-IT-Standard-B", -- Female
|
||||
["it_IT_Standard_C"] = "it-IT-Standard-C", -- Male
|
||||
["it_IT_Standard_D"] = "it-IT-Standard-D", -- Male
|
||||
},
|
||||
Wavenet = {
|
||||
["en_AU_Wavenet_A"] = 'en-AU-Wavenet-A', -- [1] FEMALE
|
||||
["en_AU_Wavenet_B"] = 'en-AU-Wavenet-B', -- [2] MALE
|
||||
["en_AU_Wavenet_C"] = 'en-AU-Wavenet-C', -- [3] FEMALE
|
||||
["en_AU_Wavenet_D"] = 'en-AU-Wavenet-D', -- [4] MALE
|
||||
["en_IN_Wavenet_A"] = 'en-IN-Wavenet-A', -- [5] FEMALE
|
||||
["en_IN_Wavenet_B"] = 'en-IN-Wavenet-B', -- [6] MALE
|
||||
["en_IN_Wavenet_C"] = 'en-IN-Wavenet-C', -- [7] MALE
|
||||
["en_IN_Wavenet_D"] = 'en-IN-Wavenet-D', -- [8] FEMALE
|
||||
["en_GB_Wavenet_A"] = 'en-GB-Wavenet-A', -- [9] FEMALE
|
||||
["en_GB_Wavenet_B"] = 'en-GB-Wavenet-B', -- [10] MALE
|
||||
["en_GB_Wavenet_C"] = 'en-GB-Wavenet-C', -- [11] FEMALE
|
||||
["en_GB_Wavenet_D"] = 'en-GB-Wavenet-D', -- [12] MALE
|
||||
["en_GB_Wavenet_F"] = 'en-GB-Wavenet-F', -- [13] FEMALE
|
||||
["en_US_Wavenet_A"] = 'en-US-Wavenet-A', -- [14] MALE
|
||||
["en_US_Wavenet_B"] = 'en-US-Wavenet-B', -- [15] MALE
|
||||
["en_US_Wavenet_C"] = 'en-US-Wavenet-C', -- [16] FEMALE
|
||||
["en_US_Wavenet_D"] = 'en-US-Wavenet-D', -- [17] MALE
|
||||
["en_US_Wavenet_E"] = 'en-US-Wavenet-E', -- [18] FEMALE
|
||||
["en_US_Wavenet_F"] = 'en-US-Wavenet-F', -- [19] FEMALE
|
||||
["en_US_Wavenet_G"] = 'en-US-Wavenet-G', -- [20] FEMALE
|
||||
["en_US_Wavenet_H"] = 'en-US-Wavenet-H', -- [21] FEMALE
|
||||
["en_US_Wavenet_I"] = 'en-US-Wavenet-I', -- [22] MALE
|
||||
["en_US_Wavenet_J"] = 'en-US-Wavenet-J', -- [23] MALE
|
||||
["fr_FR_Wavenet_A"] = "fr-FR-Wavenet-A", -- Female
|
||||
["fr_FR_Wavenet_B"] = "fr-FR-Wavenet-B", -- Male
|
||||
["fr_FR_Wavenet_C"] = "fr-FR-Wavenet-C", -- Female
|
||||
["fr_FR_Wavenet_D"] = "fr-FR-Wavenet-D", -- Male
|
||||
["fr_FR_Wavenet_E"] = "fr-FR-Wavenet-E", -- Female
|
||||
["de_DE_Wavenet_A"] = "de-DE-Wavenet-A", -- Female
|
||||
["de_DE_Wavenet_B"] = "de-DE-Wavenet-B", -- Male
|
||||
["de_DE_Wavenet_C"] = "de-DE-Wavenet-C", -- Female
|
||||
["de_DE_Wavenet_D"] = "de-DE-Wavenet-D", -- Male
|
||||
["de_DE_Wavenet_E"] = "de-DE-Wavenet-E", -- Male
|
||||
["de_DE_Wavenet_F"] = "de-DE-Wavenet-F", -- Female
|
||||
["es_ES_Wavenet_B"] = "es-ES-Wavenet-B", -- Male
|
||||
["es_ES_Wavenet_C"] = "es-ES-Wavenet-C", -- Female
|
||||
["es_ES_Wavenet_D"] = "es-ES-Wavenet-D", -- Female
|
||||
["it_IT_Wavenet_A"] = "it-IT-Wavenet-A", -- Female
|
||||
["it_IT_Wavenet_B"] = "it-IT-Wavenet-B", -- Female
|
||||
["it_IT_Wavenet_C"] = "it-IT-Wavenet-C", -- Male
|
||||
["it_IT_Wavenet_D"] = "it-IT-Wavenet-D", -- Male
|
||||
} ,
|
||||
},
|
||||
}
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@ -705,16 +806,8 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
||||
modus=modus:gsub("0", "AM")
|
||||
modus=modus:gsub("1", "FM")
|
||||
|
||||
-- This did not work well. Stopped if the transmission was a bit longer with no apparent error.
|
||||
--local command=string.format("%s --freqs=%s --modulations=%s --coalition=%d --port=%d --volume=%.2f --speed=%d", exe, freqs, modus, coal, port, volume, speed)
|
||||
|
||||
-- Command from orig STTS script. Works better for some unknown reason!
|
||||
--local command=string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", path, exe, freqs, modus, coal, port, "ROBOT")
|
||||
|
||||
--local command=string.format('start /b "" /d "%s" "%s" -f %s -m %s -c %s -p %s -n "%s" > bla.txt', path, exe, freqs, modus, coal, port, "ROBOT")
|
||||
|
||||
-- Command.
|
||||
local command=string.format('"%s\\%s" -f %s -m %s -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume)
|
||||
local command=string.format('"%s\\%s" -f "%s" -m "%s" -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume)
|
||||
|
||||
-- Set voice or gender/culture.
|
||||
if voice then
|
||||
@ -1098,5 +1191,3 @@ end
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
@ -489,7 +489,7 @@ UTILS.hPa2inHg = function( hPa )
|
||||
return hPa * 0.0295299830714
|
||||
end
|
||||
|
||||
--- Convert knots to alitude corrected KIAS, e.g. for tankers.
|
||||
--- Convert knots to altitude corrected KIAS, e.g. for tankers.
|
||||
-- @param #number knots Speed in knots.
|
||||
-- @param #number altitude Altitude in feet
|
||||
-- @return #number Corrected KIAS
|
||||
@ -1849,6 +1849,11 @@ function UTILS.IsLoadingDoorOpen( unit_name )
|
||||
return true -- no doors on this one ;)
|
||||
end
|
||||
|
||||
if type_name == "Bronco-OV-10A" then
|
||||
BASE:T(unit_name .. " front door(s) are open")
|
||||
return true -- no doors on this one ;)
|
||||
end
|
||||
|
||||
return false
|
||||
|
||||
end -- nil
|
||||
@ -2317,7 +2322,7 @@ end
|
||||
-- @param #string Filename The name of the file.
|
||||
-- @param #boolean Spawn If set to false, do not re-spawn the groups loaded in location and reduce to size.
|
||||
-- @return Core.Set#SET_GROUP Set of GROUP objects.
|
||||
-- Returns nil when file cannot be read. Returns a table of data entries if Spawn is false: `{ groupname=groupname, size=size, coordinate=coordinate }`
|
||||
-- Returns nil when file cannot be read. Returns a table of data entries if Spawn is false: `{ groupname=groupname, size=size, coordinate=coordinate, template=template }`
|
||||
function UTILS.LoadSetOfGroups(Path,Filename,Spawn)
|
||||
local spawn = true
|
||||
if Spawn == false then spawn = false end
|
||||
@ -2340,10 +2345,10 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn)
|
||||
local posz = tonumber(dataset[6])
|
||||
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
|
||||
local group=nil
|
||||
local data = { groupname=groupname, size=size, coordinate=coordinate }
|
||||
local data = { groupname=groupname, size=size, coordinate=coordinate, template=template }
|
||||
table.insert(datatable,data)
|
||||
if spawn then
|
||||
local group = SPAWN:New(groupname)
|
||||
local group = SPAWN:New(template)
|
||||
:InitDelayOff()
|
||||
:OnSpawnGroup(
|
||||
function(spwndgrp)
|
||||
|
||||
@ -1455,14 +1455,14 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
|
||||
--_spot:MarkToAll(string.format("Parking spot %d free=%s", parkingspot.TerminalID, tostring(not occupied)))
|
||||
if occupied then
|
||||
self:I(string.format("%s: Parking spot id %d occupied.", airport, _termid))
|
||||
self:T(string.format("%s: Parking spot id %d occupied.", airport, _termid))
|
||||
else
|
||||
self:I(string.format("%s: Parking spot id %d free.", airport, _termid))
|
||||
self:T(string.format("%s: Parking spot id %d free.", airport, _termid))
|
||||
if nvalid<_nspots then
|
||||
table.insert(validspots, {Coordinate=_spot, TerminalID=_termid})
|
||||
end
|
||||
nvalid=nvalid+1
|
||||
self:I(string.format("%s: Parking spot id %d free. Nfree=%d/%d.", airport, _termid, nvalid,_nspots))
|
||||
self:T(string.format("%s: Parking spot id %d free. Nfree=%d/%d.", airport, _termid, nvalid,_nspots))
|
||||
end
|
||||
|
||||
end -- loop over units
|
||||
@ -1980,7 +1980,7 @@ function AIRBASE:SetActiveRunwayLanding(Name, PreferLeft)
|
||||
end
|
||||
|
||||
if runway then
|
||||
self:I(string.format("%s: Setting active runway for landing as %s", self.AirbaseName, self:GetRunwayName(runway)))
|
||||
self:T(string.format("%s: Setting active runway for landing as %s", self.AirbaseName, self:GetRunwayName(runway)))
|
||||
else
|
||||
self:E("ERROR: Could not set the runway for landing!")
|
||||
end
|
||||
@ -2028,7 +2028,7 @@ function AIRBASE:SetActiveRunwayTakeoff(Name, PreferLeft)
|
||||
end
|
||||
|
||||
if runway then
|
||||
self:I(string.format("%s: Setting active runway for takeoff as %s", self.AirbaseName, self:GetRunwayName(runway)))
|
||||
self:T(string.format("%s: Setting active runway for takeoff as %s", self.AirbaseName, self:GetRunwayName(runway)))
|
||||
else
|
||||
self:E("ERROR: Could not set the runway for takeoff!")
|
||||
end
|
||||
|
||||
@ -673,24 +673,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean If true, group is associated with a client or player slot.
|
||||
function GROUP:IsPlayer()
|
||||
|
||||
-- Get group.
|
||||
-- local group=self:GetGroup()
|
||||
|
||||
-- Units of template group.
|
||||
local units=self:GetTemplate().units
|
||||
|
||||
-- Get numbers.
|
||||
for _,unit in pairs(units) do
|
||||
|
||||
-- Check if unit name matach and skill is Client or Player.
|
||||
if unit.name==self:GetName() and (unit.skill=="Client" or unit.skill=="Player") then
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return false
|
||||
return self:GetUnit(1):IsPlayer()
|
||||
end
|
||||
|
||||
--- Returns the UNIT wrapper class with number UnitNumber.
|
||||
@ -725,29 +708,6 @@ function GROUP:GetUnit( UnitNumber )
|
||||
|
||||
end
|
||||
|
||||
--- Check if an (air) group is a client or player slot. Information is retrieved from the group template.
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean If true, group is associated with a client or player slot.
|
||||
function GROUP:IsPlayer()
|
||||
|
||||
-- Get group.
|
||||
-- local group=self:GetGroup()
|
||||
|
||||
-- Units of template group.
|
||||
local units=self:GetTemplate().units
|
||||
|
||||
-- Get numbers.
|
||||
for _,unit in pairs(units) do
|
||||
|
||||
-- Check if unit name matach and skill is Client or Player.
|
||||
if unit.name==self:GetName() and (unit.skill=="Client" or unit.skill=="Player") then
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--- Returns the DCS Unit with number UnitNumber.
|
||||
-- If the underlying DCS Unit does not exist, the method will return nil. .
|
||||
@ -2767,94 +2727,85 @@ function GROUP:GetHighestThreat()
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
--do -- Smoke
|
||||
--- Get TTS friendly, optionally customized callsign mainly for **player groups**. A customized callsign is taken from the #GROUP name, after an optional '#' sign, e.g. "Aerial 1-1#Ghostrider" resulting in "Ghostrider 9", or,
|
||||
-- if that isn't available, from the playername, as set in the mission editor main screen under Logbook, after an optional '|' sign (actually, more of a personal call sign), e.g. "Apple|Moose" results in "Moose 9 1". Options see below.
|
||||
-- @param #GROUP self
|
||||
-- @param #boolean ShortCallsign Return a shortened customized callsign, i.e. "Ghostrider 9" and not "Ghostrider 9 1"
|
||||
-- @param #boolean Keepnumber (Player only) Return customized callsign, incl optional numbers at the end, e.g. "Aerial 1-1#Ghostrider 109" results in "Ghostrider 109", if you want to e.g. use historical US Navy Callsigns
|
||||
-- @param #table CallsignTranslations Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized
|
||||
-- callsigns from playername or group name.
|
||||
-- @return #string Callsign
|
||||
-- @usage
|
||||
-- -- Set Custom CAP Flight Callsigns for use with TTS
|
||||
-- mygroup:GetCustomCallSign(true,false,{
|
||||
-- Devil = 'Bengal',
|
||||
-- Snake = 'Winder',
|
||||
-- Colt = 'Camelot',
|
||||
-- Enfield = 'Victory',
|
||||
-- Uzi = 'Evil Eye'
|
||||
-- })
|
||||
--
|
||||
----- Signal a flare at the position of the GROUP.
|
||||
---- @param #GROUP self
|
||||
---- @param Utilities.Utils#FLARECOLOR FlareColor
|
||||
--function GROUP:Flare( FlareColor )
|
||||
-- self:F2()
|
||||
-- trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 )
|
||||
--end
|
||||
-- results in this outcome if the group has Callsign "Enfield 9 1" on the 1st #UNIT of the group:
|
||||
--
|
||||
----- Signal a white flare at the position of the GROUP.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:FlareWhite()
|
||||
-- self:F2()
|
||||
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 )
|
||||
--end
|
||||
-- 'Victory 9'
|
||||
--
|
||||
----- Signal a yellow flare at the position of the GROUP.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:FlareYellow()
|
||||
-- self:F2()
|
||||
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 )
|
||||
--end
|
||||
--
|
||||
----- Signal a green flare at the position of the GROUP.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:FlareGreen()
|
||||
-- self:F2()
|
||||
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 )
|
||||
--end
|
||||
--
|
||||
----- Signal a red flare at the position of the GROUP.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:FlareRed()
|
||||
-- self:F2()
|
||||
-- local Vec3 = self:GetVec3()
|
||||
-- if Vec3 then
|
||||
-- trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 )
|
||||
-- end
|
||||
--end
|
||||
--
|
||||
----- Smoke the GROUP.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:Smoke( SmokeColor, Range )
|
||||
-- self:F2()
|
||||
-- if Range then
|
||||
-- trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor )
|
||||
-- else
|
||||
-- trigger.action.smoke( self:GetVec3(), SmokeColor )
|
||||
-- end
|
||||
--
|
||||
--end
|
||||
--
|
||||
----- Smoke the GROUP Green.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:SmokeGreen()
|
||||
-- self:F2()
|
||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green )
|
||||
--end
|
||||
--
|
||||
----- Smoke the GROUP Red.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:SmokeRed()
|
||||
-- self:F2()
|
||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red )
|
||||
--end
|
||||
--
|
||||
----- Smoke the GROUP White.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:SmokeWhite()
|
||||
-- self:F2()
|
||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White )
|
||||
--end
|
||||
--
|
||||
----- Smoke the GROUP Orange.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:SmokeOrange()
|
||||
-- self:F2()
|
||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange )
|
||||
--end
|
||||
--
|
||||
----- Smoke the GROUP Blue.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:SmokeBlue()
|
||||
-- self:F2()
|
||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
|
||||
--end
|
||||
--
|
||||
--
|
||||
--
|
||||
--end
|
||||
--
|
||||
function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||
--self:I("GetCustomCallSign")
|
||||
|
||||
local callsign = "Ghost 1"
|
||||
if self:IsAlive() then
|
||||
local IsPlayer = self:IsPlayer()
|
||||
local shortcallsign = self:GetCallsign() or "unknown91" -- e.g.Uzi91, but we want Uzi 9 1
|
||||
local callsignroot = string.match(shortcallsign, '(%a+)') -- Uzi
|
||||
--self:I("CallSign = " .. callsignroot)
|
||||
local groupname = self:GetName()
|
||||
local callnumber = string.match(shortcallsign, "(%d+)$" ) or "91" -- 91
|
||||
local callnumbermajor = string.char(string.byte(callnumber,1)) -- 9
|
||||
local callnumberminor = string.char(string.byte(callnumber,2)) -- 1
|
||||
local personalized = false
|
||||
if IsPlayer and string.find(groupname,"#") then
|
||||
-- personalized flight name in group naming
|
||||
if Keepnumber then
|
||||
shortcallsign = string.match(groupname,"#(.+)") or "Ghost 111" -- Ghostrider 219
|
||||
else
|
||||
shortcallsign = string.match(groupname,"#%s*([%a]+)") or "Ghost" -- Ghostrider
|
||||
end
|
||||
personalized = true
|
||||
elseif IsPlayer and string.find(self:GetPlayerName(),"|") then
|
||||
-- personalized flight name in group naming
|
||||
shortcallsign = string.match(self:GetPlayerName(),"|%s*([%a]+)") or string.match(self:GetPlayerName(),"|%s*([%d]+)") or "Ghost" -- Ghostrider
|
||||
personalized = true
|
||||
end
|
||||
|
||||
if (not personalized) and CallsignTranslations and CallsignTranslations[callsignroot] then
|
||||
callsignroot = CallsignTranslations[callsignroot]
|
||||
end
|
||||
|
||||
if personalized then
|
||||
-- player personalized callsign
|
||||
-- remove trailing/leading spaces
|
||||
shortcallsign=string.gsub(shortcallsign,"^%s*","")
|
||||
shortcallsign=string.gsub(shortcallsign,"%s*$","")
|
||||
if Keepnumber then
|
||||
return shortcallsign -- Ghostrider 219
|
||||
elseif ShortCallsign then
|
||||
callsign = shortcallsign.." "..callnumbermajor -- Ghostrider 9
|
||||
else
|
||||
callsign = shortcallsign.." "..callnumbermajor.." "..callnumberminor -- Ghostrider 9 1
|
||||
end
|
||||
return callsign
|
||||
end
|
||||
|
||||
-- AI or not personalized
|
||||
if ShortCallsign then
|
||||
callsign = callsignroot.." "..callnumbermajor -- Uzi/Victory 9
|
||||
else
|
||||
callsign = callsignroot.." "..callnumbermajor.." "..callnumberminor -- Uzi/Victory 9 1
|
||||
end
|
||||
|
||||
--self:I("Generated Callsign = " .. callsign)
|
||||
end
|
||||
|
||||
return callsign
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user