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
becaf47a66
@ -1343,9 +1343,17 @@ function DATABASE:_RegisterAirbase(airbase)
|
||||
|
||||
-- Unique ID.
|
||||
local airbaseUID=airbase:GetID(true)
|
||||
|
||||
|
||||
local typename = airbase:GetTypeName()
|
||||
|
||||
local category = airbase.category
|
||||
|
||||
if category == Airbase.Category.SHIP and typename == "FARP_SINGLE_01" then
|
||||
category = Airbase.Category.HELIPAD
|
||||
end
|
||||
|
||||
-- Debug output.
|
||||
local text=string.format("Register %s: %s (UID=%d), Runways=%d, Parking=%d [", AIRBASE.CategoryName[airbase.category], tostring(DCSAirbaseName), airbaseUID, #airbase.runways, airbase.NparkingTotal)
|
||||
local text=string.format("Register %s: %s (UID=%d), Runways=%d, Parking=%d [", AIRBASE.CategoryName[category], tostring(DCSAirbaseName), airbaseUID, #airbase.runways, airbase.NparkingTotal)
|
||||
for _,terminalType in pairs(AIRBASE.TerminalType) do
|
||||
if airbase.NparkingTerminal and airbase.NparkingTerminal[terminalType] then
|
||||
text=text..string.format("%d=%d ", terminalType, airbase.NparkingTerminal[terminalType])
|
||||
|
||||
@ -459,14 +459,14 @@ end
|
||||
|
||||
_MESSAGESRS = {}
|
||||
|
||||
--- Set up MESSAGE generally to allow Text-To-Speech via SRS and TTS functions.
|
||||
-- @param #string PathToSRS Path to SRS Folder, defaults to "C:\\\\Program Files\\\\DCS-SimpleRadio-Standalone".
|
||||
-- @param #number Port Port number of SRS, defaults to 5002.
|
||||
-- @param #string PathToCredentials (optional) Path to credentials file for e.g. Google.
|
||||
--- Set up MESSAGE generally to allow Text-To-Speech via SRS and TTS functions. `SetMSRS()` will try to use as many attributes configured with @{Sound.SRS#MSRS.LoadConfigFile}() as possible.
|
||||
-- @param #string PathToSRS (optional) Path to SRS Folder, defaults to "C:\\\\Program Files\\\\DCS-SimpleRadio-Standalone" or your configuration file setting.
|
||||
-- @param #number Port Port (optional) number of SRS, defaults to 5002 or your configuration file setting.
|
||||
-- @param #string PathToCredentials (optional) Path to credentials file for Google.
|
||||
-- @param #number Frequency Frequency in MHz. Can also be given as a #table of frequencies.
|
||||
-- @param #number Modulation Modulation, i.e. radio.modulation.AM or radio.modulation.FM. Can also be given as a #table of modulations.
|
||||
-- @param #string Gender (optional) Gender, i.e. "male" or "female", defaults to "female".
|
||||
-- @param #string Culture (optional) Culture, e.g. "en-US", defaults to "en-GB"
|
||||
-- @param #string Gender (optional) Gender, i.e. "male" or "female", defaults to "female" or your configuration file setting.
|
||||
-- @param #string Culture (optional) Culture, e.g. "en-US", defaults to "en-GB" or your configuration file setting.
|
||||
-- @param #string Voice (optional) Voice. Will override gender and culture settings, e.g. MSRS.Voices.Microsoft.Hazel or MSRS.Voices.Google.Standard.de_DE_Standard_D. Hint on Microsoft voices - working voices are limited to Hedda, Hazel, David, Zira and Hortense. **Must** be installed on your Desktop or Server!
|
||||
-- @param #number Coalition (optional) Coalition, can be coalition.side.RED, coalition.side.BLUE or coalition.side.NEUTRAL. Defaults to coalition.side.NEUTRAL.
|
||||
-- @param #number Volume (optional) Volume, can be between 0.0 and 1.0 (loudest).
|
||||
@ -480,42 +480,51 @@ _MESSAGESRS = {}
|
||||
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS()
|
||||
--
|
||||
function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate)
|
||||
_MESSAGESRS.MSRS = MSRS:New(PathToSRS,Frequency or 243,Modulation or radio.modulation.AM,Volume)
|
||||
|
||||
_MESSAGESRS.frequency = Frequency
|
||||
_MESSAGESRS.modulation = Modulation or radio.modulation.AM
|
||||
_MESSAGESRS.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
||||
|
||||
_MESSAGESRS.MSRS:SetCoalition(Coalition or coalition.side.NEUTRAL)
|
||||
_MESSAGESRS.coalition = Coalition or coalition.side.NEUTRAL
|
||||
_MESSAGESRS.frequency = Frequency or MSRS.frequencies or 243
|
||||
_MESSAGESRS.modulation = Modulation or MSRS.modulations or radio.modulation.AM
|
||||
|
||||
_MESSAGESRS.MSRS = MSRS:New(_MESSAGESRS.PathToSRS,_MESSAGESRS.frequency, _MESSAGESRS.modulation)
|
||||
|
||||
_MESSAGESRS.coalition = Coalition or MSRS.coalition or coalition.side.NEUTRAL
|
||||
_MESSAGESRS.MSRS:SetCoalition(_MESSAGESRS.coalition)
|
||||
|
||||
_MESSAGESRS.coordinate = Coordinate
|
||||
_MESSAGESRS.MSRS:SetCoordinate(Coordinate)
|
||||
|
||||
if Coordinate then
|
||||
_MESSAGESRS.MSRS:SetCoordinate(Coordinate)
|
||||
end
|
||||
|
||||
_MESSAGESRS.Culture = Culture or MSRS.culture or "en-GB"
|
||||
_MESSAGESRS.MSRS:SetCulture(Culture)
|
||||
_MESSAGESRS.Culture = Culture or "en-GB"
|
||||
|
||||
_MESSAGESRS.Gender = Gender or MSRS.gender or "female"
|
||||
_MESSAGESRS.MSRS:SetGender(Gender)
|
||||
_MESSAGESRS.Gender = Gender or "female"
|
||||
|
||||
_MESSAGESRS.MSRS:SetGoogle(PathToCredentials)
|
||||
|
||||
_MESSAGESRS.MSRS:SetLabel(Label or "MESSAGE")
|
||||
_MESSAGESRS.label = Label or "MESSAGE"
|
||||
|
||||
_MESSAGESRS.MSRS:SetPort(Port or 5002)
|
||||
_MESSAGESRS.port = Port or 5002
|
||||
if PathToCredentials then
|
||||
_MESSAGESRS.MSRS:SetProviderOptionsGoogle(PathToCredentials)
|
||||
_MESSAGESRS.MSRS:SetProvider(MSRS.Provider.GOOGLE)
|
||||
end
|
||||
|
||||
_MESSAGESRS.volume = Volume or 1
|
||||
_MESSAGESRS.label = Label or MSRS.Label or "MESSAGE"
|
||||
_MESSAGESRS.MSRS:SetLabel(Label or "MESSAGE")
|
||||
|
||||
_MESSAGESRS.port = Port or MSRS.port or 5002
|
||||
_MESSAGESRS.MSRS:SetPort(Port or 5002)
|
||||
|
||||
_MESSAGESRS.volume = Volume or MSRS.volume or 1
|
||||
_MESSAGESRS.MSRS:SetVolume(_MESSAGESRS.volume)
|
||||
|
||||
if Voice then _MESSAGESRS.MSRS:SetVoice(Voice) end
|
||||
|
||||
_MESSAGESRS.voice = Voice --or MSRS.Voices.Microsoft.Hedda
|
||||
_MESSAGESRS.voice = Voice or MSRS.voice --or MSRS.Voices.Microsoft.Hedda
|
||||
|
||||
_MESSAGESRS.SRSQ = MSRSQUEUE:New(Label or "MESSAGE")
|
||||
_MESSAGESRS.SRSQ = MSRSQUEUE:New(_MESSAGESRS.label)
|
||||
end
|
||||
|
||||
--- Sends a message via SRS.
|
||||
--- Sends a message via SRS. `ToSRS()` will try to use as many attributes configured with @{Core.Message#MESSAGE.SetMSRS}() and @{Sound.SRS#MSRS.LoadConfigFile}() as possible.
|
||||
-- @param #MESSAGE self
|
||||
-- @param #number frequency (optional) Frequency in MHz. Can also be given as a #table of frequencies. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting.
|
||||
-- @param #number modulation (optional) Modulation, i.e. radio.modulation.AM or radio.modulation.FM. Can also be given as a #table of modulations. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting.
|
||||
@ -543,7 +552,7 @@ function MESSAGE:ToSRS(frequency,modulation,gender,culture,voice,coalition,volum
|
||||
_MESSAGESRS.MSRS:SetCoordinate(coordinate)
|
||||
end
|
||||
local category = string.gsub(self.MessageCategory,":","")
|
||||
_MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,nil,nil,nil,nil,frequency or _MESSAGESRS.frequency,modulation or _MESSAGESRS.modulation, gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,nil,volume or _MESSAGESRS.volume,category,coordinate or _MESSAGESRS.coordinate)
|
||||
_MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,0.5,1,nil,nil,nil,frequency or _MESSAGESRS.frequency,modulation or _MESSAGESRS.modulation, gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,nil,volume or _MESSAGESRS.volume,category,coordinate or _MESSAGESRS.coordinate)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@ -1578,7 +1578,7 @@ do
|
||||
function SET_GROUP:AddInDatabase( Event )
|
||||
self:F3( { Event } )
|
||||
|
||||
if Event.IniObjectCategory == 1 then
|
||||
if Event.IniObjectCategory == Object.Category.UNIT then
|
||||
if not self.Database[Event.IniDCSGroupName] then
|
||||
self.Database[Event.IniDCSGroupName] = GROUP:Register( Event.IniDCSGroupName )
|
||||
self:T3( self.Database[Event.IniDCSGroupName] )
|
||||
@ -2641,7 +2641,7 @@ do -- SET_UNIT
|
||||
function SET_UNIT:AddInDatabase( Event )
|
||||
self:F3( { Event } )
|
||||
|
||||
if Event.IniObjectCategory == 1 then
|
||||
if Event.IniObjectCategory == Object.Category.UNIT then
|
||||
if not self.Database[Event.IniDCSUnitName] then
|
||||
self.Database[Event.IniDCSUnitName] = UNIT:Register( Event.IniDCSUnitName )
|
||||
self:T3( self.Database[Event.IniDCSUnitName] )
|
||||
@ -4518,7 +4518,7 @@ do -- SET_CLIENT
|
||||
function SET_CLIENT:_EventPlayerEnterUnit(Event)
|
||||
self:I( "_EventPlayerEnterUnit" )
|
||||
if Event.IniDCSUnit then
|
||||
if Event.IniObjectCategory == 1 and Event.IniGroup and Event.IniGroup:IsGround() then
|
||||
if Event.IniObjectCategory == Object.Category.UNIT and Event.IniGroup and Event.IniGroup:IsGround() then
|
||||
-- CA Slot entered
|
||||
local ObjectName, Object = self:AddInDatabase( Event )
|
||||
self:I( ObjectName, UTILS.PrintTableToLog(Object) )
|
||||
@ -4537,7 +4537,7 @@ do -- SET_CLIENT
|
||||
function SET_CLIENT:_EventPlayerLeaveUnit(Event)
|
||||
self:I( "_EventPlayerLeaveUnit" )
|
||||
if Event.IniDCSUnit then
|
||||
if Event.IniObjectCategory == 1 and Event.IniGroup and Event.IniGroup:IsGround() then
|
||||
if Event.IniObjectCategory == Object.Category.UNIT and Event.IniGroup and Event.IniGroup:IsGround() then
|
||||
-- CA Slot left
|
||||
local ObjectName, Object = self:FindInDatabase( Event )
|
||||
if ObjectName then
|
||||
@ -7837,6 +7837,29 @@ do -- SET_OPSGROUP
|
||||
return self
|
||||
end
|
||||
|
||||
--- Handles the OnBirth event for the Set.
|
||||
-- @param #SET_OPSGROUP self
|
||||
-- @param Core.Event#EVENTDATA Event Event data.
|
||||
function SET_OPSGROUP:_EventOnBirth( Event )
|
||||
self:F3( { Event } )
|
||||
|
||||
if Event.IniDCSUnit and Event.IniDCSGroup then
|
||||
local DCSgroup=Event.IniDCSGroup --DCS#Group
|
||||
|
||||
if DCSgroup:getInitialSize() == DCSgroup:getSize() then -- This seems to be not a good check as even for the first birth event, getSize returns the total number of units in the group.
|
||||
|
||||
local groupname, group = self:AddInDatabase( Event )
|
||||
|
||||
if group and group:CountAliveUnits()==DCSgroup:getInitialSize() then
|
||||
if group and self:IsIncludeObject( group ) then
|
||||
self:Add( groupname, group )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnDead or OnCrash event for alive groups set.
|
||||
-- Note: The GROUP object in the SET_OPSGROUP collection will only be removed if the last unit is destroyed of the GROUP.
|
||||
-- @param #SET_OPSGROUP self
|
||||
@ -7857,12 +7880,12 @@ do -- SET_OPSGROUP
|
||||
--- Handles the Database to check on an event (birth) that the Object was added in the Database.
|
||||
-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event!
|
||||
-- @param #SET_OPSGROUP self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
-- @return #string The name of the GROUP
|
||||
-- @return #table The GROUP
|
||||
-- @param Core.Event#EVENTDATA Event Event data.
|
||||
-- @return #string The name of the GROUP.
|
||||
-- @return Wrapper.Group#GROUP The GROUP object.
|
||||
function SET_OPSGROUP:AddInDatabase( Event )
|
||||
|
||||
if Event.IniObjectCategory==1 then
|
||||
if Event.IniObjectCategory==Object.Category.UNIT then
|
||||
|
||||
if not self.Database[Event.IniDCSGroupName] then
|
||||
self.Database[Event.IniDCSGroupName] = GROUP:Register( Event.IniDCSGroupName )
|
||||
@ -7877,8 +7900,8 @@ do -- SET_OPSGROUP
|
||||
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
|
||||
-- @param #SET_OPSGROUP self
|
||||
-- @param Core.Event#EVENTDATA Event Event data table.
|
||||
-- @return #string The name of the GROUP
|
||||
-- @return #table The GROUP
|
||||
-- @return #string The name of the GROUP.
|
||||
-- @return Wrapper.Group#GROUP The GROUP object.
|
||||
function SET_OPSGROUP:FindInDatabase(Event)
|
||||
return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName]
|
||||
end
|
||||
@ -8197,7 +8220,7 @@ do -- SET_SCENERY
|
||||
end
|
||||
|
||||
--- Get a table of alive objects.
|
||||
-- @param #SET_GROUP self
|
||||
-- @param #SET_SCENERY self
|
||||
-- @return #table Table of alive objects
|
||||
-- @return Core.Set#SET_SCENERY SET of alive objects
|
||||
function SET_SCENERY:GetAliveSet()
|
||||
|
||||
@ -1458,6 +1458,7 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
||||
else
|
||||
|
||||
local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate
|
||||
local SpawnZone = self.SpawnGroups[self.SpawnIndex].SpawnZone
|
||||
self:T( SpawnTemplate.name )
|
||||
|
||||
if SpawnTemplate then
|
||||
@ -1483,6 +1484,23 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
||||
if self.SpawnRandomizeUnits then
|
||||
for UnitID = 1, #SpawnTemplate.units do
|
||||
local RandomVec2 = PointVec3:GetRandomVec2InRadius( self.SpawnOuterRadius, self.SpawnInnerRadius )
|
||||
if (SpawnZone) then
|
||||
local inZone = SpawnZone:IsVec2InZone(RandomVec2)
|
||||
local numTries = 1
|
||||
while (not inZone) and (numTries < 20) do
|
||||
if not inZone then
|
||||
RandomVec2 = PointVec3:GetRandomVec2InRadius( self.SpawnOuterRadius, self.SpawnInnerRadius )
|
||||
numTries = numTries + 1
|
||||
inZone = SpawnZone:IsVec2InZone(RandomVec2)
|
||||
self:I("Retrying " .. numTries .. "spawn " .. SpawnTemplate.name .. " in Zone " .. SpawnZone:GetName() .. "!")
|
||||
self:I(SpawnZone)
|
||||
end
|
||||
end
|
||||
if (not inZone) then
|
||||
self:I("Could not place unit within zone and within radius!")
|
||||
RandomVec2 = SpawnZone:GetRandomVec2()
|
||||
end
|
||||
end
|
||||
SpawnTemplate.units[UnitID].x = RandomVec2.x
|
||||
SpawnTemplate.units[UnitID].y = RandomVec2.y
|
||||
self:T( 'SpawnTemplate.units[' .. UnitID .. '].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units[' .. UnitID .. '].y = ' .. SpawnTemplate.units[UnitID].y )
|
||||
@ -1534,12 +1552,14 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
||||
|
||||
for UnitID = 1, #SpawnTemplate.units do
|
||||
|
||||
if UnitID > 1 then -- don't rotate position of unit #1
|
||||
local unitXOff = SpawnTemplate.units[UnitID].x - pivotX -- rotate position offset around unit #1
|
||||
local unitYOff = SpawnTemplate.units[UnitID].y - pivotY
|
||||
if not self.SpawnRandomizeUnits then
|
||||
if UnitID > 1 then -- don't rotate position of unit #1
|
||||
local unitXOff = SpawnTemplate.units[UnitID].x - pivotX -- rotate position offset around unit #1
|
||||
local unitYOff = SpawnTemplate.units[UnitID].y - pivotY
|
||||
|
||||
SpawnTemplate.units[UnitID].x = pivotX + (unitXOff * cosHeading) - (unitYOff * sinHeading)
|
||||
SpawnTemplate.units[UnitID].y = pivotY + (unitYOff * cosHeading) + (unitXOff * sinHeading)
|
||||
SpawnTemplate.units[UnitID].x = pivotX + (unitXOff * cosHeading) - (unitYOff * sinHeading)
|
||||
SpawnTemplate.units[UnitID].y = pivotY + (unitYOff * cosHeading) + (unitXOff * sinHeading)
|
||||
end
|
||||
end
|
||||
|
||||
-- adjust heading of all units, including unit #1
|
||||
@ -3591,6 +3611,7 @@ function SPAWN:_RandomizeZones( SpawnIndex )
|
||||
self:T( { SpawnVec2 = SpawnVec2 } )
|
||||
|
||||
local SpawnTemplate = self.SpawnGroups[SpawnIndex].SpawnTemplate
|
||||
self.SpawnGroups[SpawnIndex].SpawnZone = SpawnZone
|
||||
|
||||
self:T( { Route = SpawnTemplate.route } )
|
||||
|
||||
|
||||
@ -574,7 +574,7 @@ function AICSAR:SetSRSTTSRadio(OnOff,Path,Frequency,Modulation,Port,Voice,Cultur
|
||||
self.SRSModulation = Modulation or radio.modulation.AM
|
||||
self.SRSPort = Port or 5002
|
||||
if OnOff then
|
||||
self.SRS = MSRS:New(Path,Frequency,Modulation,1)
|
||||
self.SRS = MSRS:New(Path,Frequency,Modulation)
|
||||
self.SRS:SetPort(self.SRSPort)
|
||||
self.SRS:SetCoalition(self.coalition)
|
||||
self.SRS:SetLabel("ACSR")
|
||||
@ -600,7 +600,7 @@ end
|
||||
function AICSAR:SetPilotTTSVoice(Voice,Culture,Gender)
|
||||
self:T(self.lid .. "SetPilotTTSVoice")
|
||||
self.SRSPilotVoice = true
|
||||
self.SRSPilot = MSRS:New(self.SRSPath,self.SRSFrequency,self.SRSModulation,1)
|
||||
self.SRSPilot = MSRS:New(self.SRSPath,self.SRSFrequency,self.SRSModulation)
|
||||
self.SRSPilot:SetCoalition(self.coalition)
|
||||
self.SRSPilot:SetVoice(Voice)
|
||||
self.SRSPilot:SetCulture(Culture or "en-US")
|
||||
@ -624,7 +624,7 @@ end
|
||||
function AICSAR:SetOperatorTTSVoice(Voice,Culture,Gender)
|
||||
self:T(self.lid .. "SetOperatorTTSVoice")
|
||||
self.SRSOperatorVoice = true
|
||||
self.SRSOperator = MSRS:New(self.SRSPath,self.SRSFrequency,self.SRSModulation,1)
|
||||
self.SRSOperator = MSRS:New(self.SRSPath,self.SRSFrequency,self.SRSModulation)
|
||||
self.SRSOperator:SetCoalition(self.coalition)
|
||||
self.SRSOperator:SetVoice(Voice)
|
||||
self.SRSOperator:SetCulture(Culture or "en-GB")
|
||||
|
||||
@ -462,7 +462,7 @@ function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Cultu
|
||||
self.Volume = Volume or 1.0
|
||||
self.Label = Label
|
||||
-- set up SRS
|
||||
self.SRS = MSRS:New(self.SRSPath,self.SRSFreq,self.SRSMod,self.Volume)
|
||||
self.SRS = MSRS:New(self.SRSPath,self.SRSFreq,self.SRSMod)
|
||||
self.SRS:SetCoalition(self.coalition)
|
||||
self.SRS:SetLabel(self.MenuName or self.Name)
|
||||
self.SRS:SetGender(self.Gender)
|
||||
@ -470,6 +470,7 @@ function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Cultu
|
||||
self.SRS:SetPort(self.Port)
|
||||
self.SRS:SetVoice(self.Voice)
|
||||
self.SRS:SetCoalition(self.coalition)
|
||||
self.SRS:SetVolume(Volume)
|
||||
if self.PathToGoogleKey then
|
||||
self.SRS:SetGoogle(self.PathToGoogleKey)
|
||||
end
|
||||
|
||||
@ -1211,16 +1211,18 @@ function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume,
|
||||
|
||||
self.useSRS=true
|
||||
|
||||
self.controlmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 256, Modulation or radio.modulation.AM, Volume or 1.0)
|
||||
self.controlmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 256, Modulation or radio.modulation.AM)
|
||||
self.controlmsrs:SetPort(Port or MSRS.port)
|
||||
self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE)
|
||||
self.controlmsrs:SetLabel("RANGEC")
|
||||
self.controlmsrs:SetVolume(Volume or 1.0)
|
||||
self.controlsrsQ = MSRSQUEUE:New("CONTROL")
|
||||
|
||||
self.instructmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 305, Modulation or radio.modulation.AM, Volume or 1.0)
|
||||
self.instructmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 305, Modulation or radio.modulation.AM)
|
||||
self.instructmsrs:SetPort(Port or MSRS.port)
|
||||
self.instructmsrs:SetCoalition(Coalition or coalition.side.BLUE)
|
||||
self.instructmsrs:SetLabel("RANGEI")
|
||||
self.instructmsrs:SetVolume(Volume or 1.0)
|
||||
self.instructsrsQ = MSRSQUEUE:New("INSTRUCT")
|
||||
|
||||
if PathToGoogleKey then
|
||||
|
||||
@ -276,9 +276,15 @@ function SCORING:New( GameName )
|
||||
self:SetMessagesZone( true )
|
||||
|
||||
-- Scales
|
||||
|
||||
self:SetScaleDestroyScore( 10 )
|
||||
self:SetScaleDestroyPenalty( 30 )
|
||||
|
||||
-- Hitting a target multiple times before destoying it should not result in a higger score
|
||||
-- Multiple hits is typically a results of bombs/missles missing their target but still inflict some spash damage
|
||||
-- Making this configurable to anyone can enable this anyway if they want
|
||||
self:SetScoreIncrementOnHit(0)
|
||||
|
||||
-- Default fratricide penalty level (maximum penalty that can be assigned to a player before he gets kicked).
|
||||
self:SetFratricide( self.ScaleDestroyPenalty * 3 )
|
||||
self.penaltyonfratricide = true
|
||||
@ -467,6 +473,16 @@ function SCORING:SetMessagesHit( OnOff )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Configure to increment score after a target has been hit.
|
||||
-- @param #SCORING self
|
||||
-- @param #number score amount of point to inclement score on each hit
|
||||
-- @return #SCORING
|
||||
function SCORING:SetScoreIncrementOnHit( score )
|
||||
|
||||
self.ScoreIncrementOnHit = score
|
||||
return self
|
||||
end
|
||||
|
||||
--- If to send messages after a target has been hit.
|
||||
-- @param #SCORING self
|
||||
-- @return #boolean
|
||||
@ -885,6 +901,7 @@ function SCORING:OnEventBirth( Event )
|
||||
Event.IniUnit.BirthTime = timer.getTime()
|
||||
if PlayerName then
|
||||
self:_AddPlayerFromUnit( Event.IniUnit )
|
||||
self.Players[PlayerName].PlayerKills = 0
|
||||
self:SetScoringMenu( Event.IniGroup )
|
||||
end
|
||||
end
|
||||
@ -1015,7 +1032,7 @@ function SCORING:_EventOnHit( Event )
|
||||
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
|
||||
-- After an instant kill we can't compute the thread level anymore. To fix this we compute at OnEventBirth
|
||||
if PlayerHit.UNIT.ThreatType == nil then
|
||||
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
||||
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
||||
-- if this fails for some reason, set a good default value
|
||||
if PlayerHit.ThreatType == nil then
|
||||
PlayerHit.ThreatLevel = 1
|
||||
@ -1025,7 +1042,7 @@ function SCORING:_EventOnHit( Event )
|
||||
PlayerHit.ThreatLevel = PlayerHit.UNIT.ThreatLevel
|
||||
PlayerHit.ThreatType = PlayerHit.UNIT.ThreatType
|
||||
end
|
||||
|
||||
|
||||
-- Only grant hit scores if there was more than one second between the last hit.
|
||||
if timer.getTime() - PlayerHit.TimeStamp > 1 then
|
||||
PlayerHit.TimeStamp = timer.getTime()
|
||||
@ -1060,10 +1077,8 @@ function SCORING:_EventOnHit( Event )
|
||||
end
|
||||
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
||||
else
|
||||
-- Hitting a target multiple times before destoying it should not result in a higger score
|
||||
-- Multiple hits is typically a results of bombs/missles missing their target but still inflict some spash damage
|
||||
-- Player.Score = Player.Score + 1
|
||||
-- PlayerHit.Score = PlayerHit.Score + 1
|
||||
Player.Score = Player.Score + self.ScoreIncrementOnHit
|
||||
PlayerHit.Score = PlayerHit.Score + self.ScoreIncrementOnHit
|
||||
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
||||
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
|
||||
@ -1128,7 +1143,7 @@ function SCORING:_EventOnHit( Event )
|
||||
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
|
||||
-- After an instant kill we can't compute the thread level anymore. To fix this we compute at OnEventBirth
|
||||
if PlayerHit.UNIT.ThreatType == nil then
|
||||
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
||||
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
||||
-- if this fails for some reason, set a good default value
|
||||
if PlayerHit.ThreatType == nil then
|
||||
PlayerHit.ThreatLevel = 1
|
||||
@ -1163,10 +1178,8 @@ function SCORING:_EventOnHit( Event )
|
||||
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||
self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
||||
else
|
||||
-- Hitting a target multiple times before destoying it should not result in a higger score
|
||||
-- Multiple hits is typically a results of bombs/missles missing their target but still inflict some spash damage
|
||||
-- Player.Score = Player.Score + 1
|
||||
-- PlayerHit.Score = PlayerHit.Score + 1
|
||||
Player.Score = Player.Score + self.ScoreIncrementOnHit
|
||||
PlayerHit.Score = PlayerHit.Score + self.ScoreIncrementOnHit
|
||||
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
||||
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
@ -1274,13 +1287,18 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
||||
TargetDestroy.Penalty = TargetDestroy.Penalty + ThreatPenalty
|
||||
TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1
|
||||
|
||||
|
||||
self:OnKillPvP(Player, TargetPlayerName, true, TargetThreatLevel, Player.ThreatLevel, ThreatPenalty)
|
||||
|
||||
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
|
||||
self:OnKillPvP(Player, TargetPlayerName, true)
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||
"Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Information )
|
||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||
else
|
||||
self:OnKillPvE(Player, TargetUnitName, true, TargetThreatLevel, Player.ThreatLevel, ThreatPenalty)
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||
"Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Information )
|
||||
@ -1303,12 +1321,19 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
||||
TargetDestroy.Score = TargetDestroy.Score + ThreatScore
|
||||
TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1
|
||||
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
|
||||
if Player.PlayerKills ~= nil then
|
||||
Player.PlayerKills = Player.PlayerKills + 1
|
||||
else
|
||||
Player.PlayerKills = 1
|
||||
end
|
||||
self:OnKillPvP(Player, TargetPlayerName, false, TargetThreatLevel, Player.ThreatLevel, ThreatScore)
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||
"Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Information )
|
||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||
else
|
||||
self:OnKillPvE(Player, TargetUnitName, false, TargetThreatLevel, Player.ThreatLevel, ThreatScore)
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||
"Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Information )
|
||||
@ -1907,3 +1932,26 @@ function SCORING:SwitchAutoSave(OnOff)
|
||||
self.AutoSave = OnOff
|
||||
return self
|
||||
end
|
||||
|
||||
--- Handles the event when one player kill another player
|
||||
-- @param #SCORING self
|
||||
-- @param #PLAYER Player the ataching player
|
||||
-- @param #string TargetPlayerName the name of the killed player
|
||||
-- @param #bool IsTeamKill true if this kill was a team kill
|
||||
-- @param #number TargetThreatLevel Thread level of the target
|
||||
-- @param #number PlayerThreatLevelThread level of the player
|
||||
-- @param #number Score The score based on both threat levels
|
||||
function SCORING:OnKillPvP(Player, TargetPlayerName, IsTeamKill, TargetThreatLevel, PlayerThreatLevel, Score)
|
||||
|
||||
end
|
||||
--- Handles the event when one player kill another player
|
||||
-- @param #SCORING self
|
||||
-- @param #PLAYER Player the ataching player
|
||||
-- @param #string TargetUnitName the name of the killed unit
|
||||
-- @param #bool IsTeamKill true if this kill was a team kill
|
||||
-- @param #number TargetThreatLevel Thread level of the target
|
||||
-- @param #number PlayerThreatLevelThread level of the player
|
||||
-- @param #number Score The score based on both threat levels
|
||||
function SCORING:OnKillPvE(Player, TargetUnitName, IsTeamKill, TargetThreatLevel, PlayerThreatLevel, Score)
|
||||
|
||||
end
|
||||
@ -42,6 +42,7 @@
|
||||
-- @field #number CaptureUnits
|
||||
-- @field #number CaptureThreatlevel
|
||||
-- @extends Core.Base#BASE
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
--- *If you see what is right and fail to act on it, you lack courage* --- Confucius
|
||||
@ -175,7 +176,7 @@ STRATEGO = {
|
||||
debug = false,
|
||||
drawzone = false,
|
||||
markzone = false,
|
||||
version = "0.2.1",
|
||||
version = "0.2.3",
|
||||
portweight = 3,
|
||||
POIweight = 1,
|
||||
maxrunways = 3,
|
||||
@ -244,8 +245,8 @@ STRATEGO.Type = {
|
||||
-- @param #number MaxDist Maximum distance of a single route in kilometers, defaults to 150.
|
||||
-- @return #STRATEGO self
|
||||
function STRATEGO:New(Name,Coalition,MaxDist)
|
||||
-- Inherit everything from BASE class.
|
||||
local self = BASE:Inherit(self, BASE:New()) -- #STRATEGO
|
||||
-- Inherit everything from FSM class.
|
||||
local self = BASE:Inherit(self, FSM:New()) -- #STRATEGO
|
||||
|
||||
self.coalition = Coalition
|
||||
self.coalitiontext = UTILS.GetCoalitionName(Coalition)
|
||||
@ -267,14 +268,56 @@ function STRATEGO:New(Name,Coalition,MaxDist)
|
||||
[3] = {0,0,1}, -- blue
|
||||
[4] = {1,0.65,0}, -- orange
|
||||
}
|
||||
|
||||
|
||||
-- Start State.
|
||||
self:SetStartState("Stopped")
|
||||
|
||||
-- Add FSM transitions.
|
||||
-- From State --> Event --> To State
|
||||
self:AddTransition("Stopped", "Start", "Running") -- Start FSM.
|
||||
self:AddTransition("*", "Update", "*") -- Start FSM.
|
||||
self:AddTransition("*", "NodeEvent", "*") -- Start FSM.
|
||||
self:AddTransition("Running", "Stop", "Stopped") -- Start FSM.
|
||||
|
||||
------------------------
|
||||
--- Pseudo Functions ---
|
||||
------------------------
|
||||
|
||||
--- Triggers the FSM event "Start". Starts the STRATEGO. Initializes parameters and starts event handlers.
|
||||
-- @function [parent=#STRATEGO] Start
|
||||
-- @param #STRATEGO self
|
||||
|
||||
--- Triggers the FSM event "Start" after a delay. Starts the STRATEGO. Initializes parameters and starts event handlers.
|
||||
-- @function [parent=#STRATEGO] __Start
|
||||
-- @param #STRATEGO self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
--- Triggers the FSM event "Stop". Stops the STRATEGO and all its event handlers.
|
||||
-- @function [parent=#STRATEGO] Stop
|
||||
-- @param #STRATEGO self
|
||||
|
||||
--- Triggers the FSM event "Stop" after a delay. Stops the STRATEGO and all its event handlers.
|
||||
-- @function [parent=#STRATEGO] __Stop
|
||||
-- @param #STRATEGO self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
--- FSM Function OnAfterNodeEvent. A node changed coalition.
|
||||
-- @function [parent=#STRATEGO] OnAfterNodeEvent
|
||||
-- @param #STRATEGO self
|
||||
-- @param #string From State.
|
||||
-- @param #string Event Trigger.
|
||||
-- @param #string To State.
|
||||
-- @param Ops.OpsZone#OPSZONE OpsZone The OpsZone triggering the event.
|
||||
-- @param #number Coalition The coalition of the new owner.
|
||||
-- @return #STRATEGO self
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- [USER] Do initial setup and get ready.
|
||||
--- [INTERNAL] FSM function for initial setup and getting ready.
|
||||
-- @param #STRATEGO self
|
||||
-- @return #STRATEGO self
|
||||
function STRATEGO:Start()
|
||||
function STRATEGO:onafterStart(From,Event,To)
|
||||
self:T(self.lid.."Start")
|
||||
self:AnalyseBases()
|
||||
self:AnalysePOIs(self.ports,self.portweight,"PORT")
|
||||
@ -282,12 +325,27 @@ function STRATEGO:Start()
|
||||
|
||||
for i=self.maxrunways,1,-1 do
|
||||
self:AnalyseRoutes(i,i*self.routefactor,self.colors[(i%3)+1],i)
|
||||
--self:AnalyseRoutes(2,2*self.routefactor,self.colors[2],2)
|
||||
--self:AnalyseRoutes(1,1*self.routefactor,self.colors[3],3)
|
||||
end
|
||||
self:AnalyseUnconnected(self.colors[4])
|
||||
|
||||
self:I(self.lid.."Advisory ready.")
|
||||
|
||||
self:__Update(180)
|
||||
return self
|
||||
end
|
||||
|
||||
--- [INTERNAL] Update knot association
|
||||
-- @param #STRATEGO self
|
||||
-- @return #STRATEGO self
|
||||
function STRATEGO:onafterUpdate(From,Event,To)
|
||||
self:T(self.lid.."Update")
|
||||
|
||||
self:UpdateNodeCoalitions()
|
||||
|
||||
if self:GetState() == "Running" then
|
||||
self:__Update(180)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@ -308,6 +366,7 @@ end
|
||||
-- @param #boolean DrawZones If true, draw the OpsZones on the F10 map.
|
||||
-- @param #boolean MarkZones if true, mark the OpsZones on the F10 map (with further information).
|
||||
function STRATEGO:SetDebug(Debug,DrawZones,MarkZones)
|
||||
self:T(self.lid.."SetDebug")
|
||||
self.debug = Debug
|
||||
self.drawzone = DrawZones
|
||||
self.markzone = MarkZones
|
||||
@ -322,6 +381,7 @@ end
|
||||
-- @param #number RouteFactor Defines which weight each route between two defined nodes gets: Weight * RouteFactor.
|
||||
-- @return #STRATEGO self
|
||||
function STRATEGO:SetWeights(MaxRunways,PortWeight,POIWeight,RouteFactor)
|
||||
self:T(self.lid.."SetWeights")
|
||||
self.portweight = PortWeight or 3
|
||||
self.POIweight = POIWeight or 1
|
||||
self.maxrunways = MaxRunways or 3
|
||||
@ -334,6 +394,7 @@ end
|
||||
-- @param #number NeutralBenefit Pointsm defaults to 100.
|
||||
-- @return #STRATEGO self
|
||||
function STRATEGO:SetNeutralBenefit(NeutralBenefit)
|
||||
self:T(self.lid.."SetNeutralBenefit")
|
||||
self.NeutralBenefit = NeutralBenefit or 100
|
||||
return self
|
||||
end
|
||||
@ -344,6 +405,7 @@ end
|
||||
-- @param #number CaptureThreatlevel Threat level needed, can be 0..10, defaults to one.
|
||||
-- @return #STRATEGO self
|
||||
function STRATEGO:SetCaptureOptions(CaptureUnits,CaptureThreatlevel)
|
||||
self:T(self.lid.."SetCaptureOptions")
|
||||
self.CaptureUnits = CaptureUnits or 3
|
||||
self.CaptureThreatlevel = CaptureThreatlevel or 1
|
||||
return self
|
||||
@ -369,6 +431,9 @@ function STRATEGO:AnalyseBases()
|
||||
local numrwys = #runways
|
||||
if numrwys >= 1 then numrwys = numrwys * 0.5 end
|
||||
local abzone = ab:GetZone()
|
||||
if not abzone then
|
||||
abzone = ZONE_RADIUS:New(abname,ab:GetVec2(),500)
|
||||
end
|
||||
local coa = ab:GetCoalition() + 1
|
||||
local abtype = "AIRBASE"
|
||||
if ab:IsShip() then
|
||||
@ -379,7 +444,7 @@ function STRATEGO:AnalyseBases()
|
||||
numrwys = 1
|
||||
abtype = "FARP"
|
||||
end
|
||||
local coord = abzone:GetCoordinate()
|
||||
local coord = ab:GetCoordinate()
|
||||
if debug then
|
||||
abzone:DrawZone(-1,colors[coa],1,colors[coa],0.3,1)
|
||||
coord:TextToAll(tostring(numrwys),-1,{0,0,0},1,colors[coa],0.3,20)
|
||||
@ -394,6 +459,7 @@ function STRATEGO:AnalyseBases()
|
||||
zone = abzone,
|
||||
coord = coord,
|
||||
type = abtype,
|
||||
opszone = opszone,
|
||||
}
|
||||
airbasetable[abname] = tbl
|
||||
nonconnectedab[abname] = true
|
||||
@ -437,6 +503,15 @@ function STRATEGO:GetNewOpsZone(Zone,Coalition)
|
||||
opszone:SetDrawZone(self.drawzone)
|
||||
opszone:SetMarkZone(self.markzone)
|
||||
opszone:Start()
|
||||
|
||||
local function Captured(opszone,coalition)
|
||||
self:__NodeEvent(1,opszone,coalition)
|
||||
end
|
||||
|
||||
function opszone:OnBeforeCaptured(From,Event,To,Coalition)
|
||||
Captured(opszone,Coalition)
|
||||
end
|
||||
|
||||
return opszone
|
||||
end
|
||||
|
||||
@ -494,13 +569,14 @@ end
|
||||
|
||||
--- [USER] Manually add a route, for e.g. Island hopping or to connect isolated networks. Use **after** STRATEGO has been started!
|
||||
-- @param #STRATEGO self
|
||||
-- @param #string Startpoint
|
||||
-- @param #string Endpoint
|
||||
-- @param #string Startpoint Starting Point, e.g. AIRBASE.Syria.Hatay
|
||||
-- @param #string Endpoint End Point, e.g. AIRBASE.Syria.H4
|
||||
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Defaults to lila.
|
||||
-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 5.
|
||||
-- @param #number Linetype (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 5.
|
||||
-- @param #boolean Draw (Optional) If true, draw route on the F10 map. Defaukt false.
|
||||
-- @return #STRATEGO self
|
||||
function STRATEGO:AddRoutesManually(Startpoint,Endpoint,Color,Linetype,Draw)
|
||||
self:T(self.lid.."AddRoutesManually")
|
||||
local fromto,tofrom = self:GetToFrom(Startpoint,Endpoint)
|
||||
local startcoordinate = self.airbasetable[Startpoint].coord
|
||||
local targetcoordinate = self.airbasetable[Endpoint].coord
|
||||
@ -630,7 +706,7 @@ end
|
||||
-- @return #table Table of nodes.
|
||||
-- @return #number Weight The consolidated weight associated with the nodes.
|
||||
function STRATEGO:GetHighestWeightNodes()
|
||||
self:T(self.lid.."GetHighestWeightBases")
|
||||
self:T(self.lid.."GetHighestWeightNodes")
|
||||
local weight = 0
|
||||
local airbases = {}
|
||||
for _name,_data in pairs(self.airbasetable) do
|
||||
@ -648,7 +724,7 @@ end
|
||||
-- @return #table Table of nodes.
|
||||
-- @return #number Weight The consolidated weight associated with the nodes.
|
||||
function STRATEGO:GetNextHighestWeightNodes(Weight)
|
||||
self:T(self.lid.."GetNextHighestWeightBases")
|
||||
self:T(self.lid.."GetNextHighestWeightNodes")
|
||||
local weight = 0
|
||||
local airbases = {}
|
||||
for _name,_data in pairs(self.airbasetable) do
|
||||
@ -666,6 +742,7 @@ end
|
||||
-- @param #string Name.
|
||||
-- @return #number Weight The weight or 0 if not found.
|
||||
function STRATEGO:GetNodeWeight(Name)
|
||||
self:T(self.lid.."GetNodeWeight")
|
||||
if Name and self.airbasetable[Name] then
|
||||
return self.airbasetable[Name].weight or 0
|
||||
else
|
||||
@ -678,6 +755,7 @@ end
|
||||
-- @param #string Name.
|
||||
-- @return #number Weight The base weight or 0 if not found.
|
||||
function STRATEGO:GetNodeBaseWeight(Name)
|
||||
self:T(self.lid.."GetNodeBaseWeight")
|
||||
if Name and self.airbasetable[Name] then
|
||||
return self.airbasetable[Name].baseweight or 0
|
||||
else
|
||||
@ -690,6 +768,7 @@ end
|
||||
-- @param #string Name.
|
||||
-- @return #number Coalition The coalition.
|
||||
function STRATEGO:GetNodeCoalition(Name)
|
||||
self:T(self.lid.."GetNodeCoalition")
|
||||
if Name and self.airbasetable[Name] then
|
||||
return self.airbasetable[Name].coalition or coalition.side.NEUTRAL
|
||||
else
|
||||
@ -702,6 +781,7 @@ end
|
||||
-- @param #string Name.
|
||||
-- @return #string Type Type of the node, e.g. STRATEGO.Type.AIRBASE or nil if not found.
|
||||
function STRATEGO:GetNodeType(Name)
|
||||
self:T(self.lid.."GetNodeType")
|
||||
if Name and self.airbasetable[Name] then
|
||||
return self.airbasetable[Name].type
|
||||
else
|
||||
@ -714,6 +794,7 @@ end
|
||||
-- @param #string Name.
|
||||
-- @return Core.Zone#ZONE Zone The Zone of the node or nil if not found.
|
||||
function STRATEGO:GetNodeZone(Name)
|
||||
self:T(self.lid.."GetNodeZone")
|
||||
if Name and self.airbasetable[Name] then
|
||||
return self.airbasetable[Name].zone
|
||||
else
|
||||
@ -726,6 +807,7 @@ end
|
||||
-- @param #string Name.
|
||||
-- @return Ops.OpsZone#OPSZONE OpsZone The OpsZone of the node or nil if not found.
|
||||
function STRATEGO:GetNodeOpsZone(Name)
|
||||
self:T(self.lid.."GetNodeOpsZone")
|
||||
if Name and self.airbasetable[Name] then
|
||||
return self.airbasetable[Name].opszone
|
||||
else
|
||||
@ -738,6 +820,7 @@ end
|
||||
-- @param #string Name.
|
||||
-- @return Core.Point#COORDINATE Coordinate The Coordinate of the node or nil if not found.
|
||||
function STRATEGO:GetNodeCoordinate(Name)
|
||||
self:T(self.lid.."GetNodeCoordinate")
|
||||
if Name and self.airbasetable[Name] then
|
||||
return self.airbasetable[Name].coord
|
||||
else
|
||||
@ -750,6 +833,7 @@ end
|
||||
-- @param #string Name.
|
||||
-- @return #boolean Outcome
|
||||
function STRATEGO:IsAirbase(Name)
|
||||
self:T(self.lid.."IsAirbase")
|
||||
if Name and self.airbasetable[Name] then
|
||||
return self.airbasetable[Name].type == STRATEGO.Type.AIRBASE
|
||||
else
|
||||
@ -762,6 +846,7 @@ end
|
||||
-- @param #string Name.
|
||||
-- @return #boolean Outcome
|
||||
function STRATEGO:IsPort(Name)
|
||||
self:T(self.lid.."IsPort")
|
||||
if Name and self.airbasetable[Name] then
|
||||
return self.airbasetable[Name].type == STRATEGO.Type.PORT
|
||||
else
|
||||
@ -774,6 +859,7 @@ end
|
||||
-- @param #string Name.
|
||||
-- @return #boolean Outcome
|
||||
function STRATEGO:IsPOI(Name)
|
||||
self:T(self.lid.."IsPOI")
|
||||
if Name and self.airbasetable[Name] then
|
||||
return self.airbasetable[Name].type == STRATEGO.Type.POI
|
||||
else
|
||||
@ -786,6 +872,7 @@ end
|
||||
-- @param #string Name.
|
||||
-- @return #boolean Outcome
|
||||
function STRATEGO:IsFARP(Name)
|
||||
self:T(self.lid.."IsFARP")
|
||||
if Name and self.airbasetable[Name] then
|
||||
return self.airbasetable[Name].type == STRATEGO.Type.FARP
|
||||
else
|
||||
@ -798,6 +885,7 @@ end
|
||||
-- @param #string Name.
|
||||
-- @return #boolean Outcome
|
||||
function STRATEGO:IsShip(Name)
|
||||
self:T(self.lid.."IsShip")
|
||||
if Name and self.airbasetable[Name] then
|
||||
return self.airbasetable[Name].type == STRATEGO.Type.SHIP
|
||||
else
|
||||
@ -880,6 +968,7 @@ end
|
||||
-- @param #STRATEGO self
|
||||
-- @return #table of #STRATEGO.Target data points
|
||||
function STRATEGO:FindStrategicTargets()
|
||||
self:T(self.lid.."FindStrategicTargets")
|
||||
local targets = {}
|
||||
for _,_data in pairs(self.airbasetable) do
|
||||
local data = _data -- #STRATEGO.Data
|
||||
@ -923,6 +1012,7 @@ end
|
||||
-- @param #STRATEGO self
|
||||
-- @return #table of #STRATEGO.Target data points
|
||||
function STRATEGO:FindConsolidationTargets()
|
||||
self:T(self.lid.."FindConsolidationTargets")
|
||||
local targets = {}
|
||||
for _,_data in pairs(self.airbasetable) do
|
||||
local data = _data -- #STRATEGO.Data
|
||||
@ -971,6 +1061,7 @@ end
|
||||
-- @param #boolean Friends (optional) If true, find only friendly or neutral neighbors.
|
||||
-- @return #table Neighbors Table of #STRATEGO.DistData entries indexed by neighbor node names.
|
||||
function STRATEGO:FindNeighborNodes(Name,Enemies,Friends)
|
||||
self:T(self.lid.."FindNeighborNodes")
|
||||
local neighbors = {}
|
||||
local name = string.gsub(Name,"[%p%s]",".")
|
||||
for _route,_data in pairs(self.disttable) do
|
||||
@ -1002,10 +1093,13 @@ end
|
||||
-- @param #string End The name of the end node.
|
||||
-- @param #number Hops Max iterations to find a route.
|
||||
-- @param #boolean Draw If true, draw the route on the map.
|
||||
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Defaults to black.
|
||||
-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 6.
|
||||
-- @return #table Route Table of #string name entries of the route
|
||||
-- @return #boolean Complete If true, the route was found end-to-end.
|
||||
function STRATEGO:FindRoute(Start,End,Hops,Draw)
|
||||
self:I({Start,End,Hops})
|
||||
function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType)
|
||||
self:T(self.lid.."FindRoute")
|
||||
--self:I({Start,End,Hops})
|
||||
--local bases = UTILS.DeepCopy(self.airbasetable)
|
||||
local Route = {}
|
||||
local hops = Hops or 4
|
||||
@ -1046,7 +1140,9 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw)
|
||||
local p2=Route[i+1]
|
||||
local c1 = self.airbasetable[p1].coord -- Core.Point#COORDINATE
|
||||
local c2 = self.airbasetable[p2].coord -- Core.Point#COORDINATE
|
||||
c1:LineToAll(c2,-1,{0,0,0},1,6)
|
||||
local line = LineType or 6
|
||||
local color = Color or {0,0,0}
|
||||
c1:LineToAll(c2,-1,color,1,line)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1076,7 +1172,7 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw)
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.debug or Draw then DrawRoute(Route) end
|
||||
if (self.debug or Draw) then DrawRoute(Route) end
|
||||
return Route, routecomplete
|
||||
end
|
||||
|
||||
@ -1085,6 +1181,7 @@ end
|
||||
-- @param #number Number of points to add.
|
||||
-- @return #STRATEGO self
|
||||
function STRATEGO:AddBudget(Number)
|
||||
self:T(self.lid.."AddBudget")
|
||||
self.Budget = self.Budget + Number
|
||||
return self
|
||||
end
|
||||
@ -1094,6 +1191,7 @@ end
|
||||
-- @param #number Number of points to subtract.
|
||||
-- @return #STRATEGO self
|
||||
function STRATEGO:SubtractBudget(Number)
|
||||
self:T(self.lid.."SubtractBudget")
|
||||
self.Budget = self.Budget - Number
|
||||
return self
|
||||
end
|
||||
@ -1102,6 +1200,7 @@ end
|
||||
-- @param #STRATEGO self
|
||||
-- @return #number budget
|
||||
function STRATEGO:GetBudget()
|
||||
self:T(self.lid.."GetBudget")
|
||||
return self.Budget
|
||||
end
|
||||
|
||||
@ -1109,6 +1208,7 @@ end
|
||||
-- @param #STRATEGO self
|
||||
-- @return #table Target Table with #STRATEGO.Target data or nil if none found.
|
||||
function STRATEGO:FindAffordableStrategicTarget()
|
||||
self:T(self.lid.."FindAffordableStrategicTarget")
|
||||
local targets = self:FindStrategicTargets() -- #table of #STRATEGO.Target
|
||||
local budget = self.Budget
|
||||
--local leftover = self.Budget
|
||||
@ -1140,6 +1240,7 @@ end
|
||||
-- @param #STRATEGO self
|
||||
-- @return #table Target Table with #STRATEGO.Target data or nil if none found.
|
||||
function STRATEGO:FindAffordableConsolidationTarget()
|
||||
self:T(self.lid.."FindAffordableConsolidationTarget")
|
||||
local targets = self:FindConsolidationTargets() -- #table of #STRATEGO.Target
|
||||
local budget = self.Budget
|
||||
--local leftover = self.Budget
|
||||
|
||||
@ -1526,7 +1526,7 @@ function ATIS:MarkRunways( markall )
|
||||
end
|
||||
end
|
||||
|
||||
--- Use SRS Simple-Text-To-Speech for transmissions. No sound files necessary.
|
||||
--- Use SRS Simple-Text-To-Speech for transmissions. No sound files necessary.`SetSRS()` will try to use as many attributes configured with @{Sound.SRS#MSRS.LoadConfigFile}() as possible.
|
||||
-- @param #ATIS self
|
||||
-- @param #string PathToSRS Path to SRS directory (only necessary if SRS exe backend is used).
|
||||
-- @param #string Gender Gender: "male" or "female" (default).
|
||||
@ -1536,24 +1536,52 @@ end
|
||||
-- @param #string GoogleKey Path to Google JSON-Key (SRS exe backend) or Google API key (DCS-gRPC backend).
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey)
|
||||
if PathToSRS or MSRS.path then
|
||||
--if PathToSRS or MSRS.path then
|
||||
self.useSRS=true
|
||||
self.msrs=MSRS:New(PathToSRS, self.frequency, self.modulation)
|
||||
self.msrs:SetGender(Gender)
|
||||
self.msrs:SetCulture(Culture)
|
||||
self.msrs:SetVoice(Voice)
|
||||
self.msrs:SetPort(Port)
|
||||
|
||||
local path = PathToSRS or MSRS.path
|
||||
local gender = Gender or MSRS.gender
|
||||
local culture = Culture or MSRS.culture
|
||||
local voice = Voice or MSRS.voice
|
||||
local port = Port or MSRS.port or 5002
|
||||
|
||||
self.msrs=MSRS:New(path, self.frequency, self.modulation)
|
||||
self.msrs:SetGender(gender)
|
||||
self.msrs:SetCulture(culture)
|
||||
self.msrs:SetPort(port)
|
||||
self.msrs:SetCoalition(self:GetCoalition())
|
||||
self.msrs:SetLabel("ATIS")
|
||||
self.msrs:SetGoogle(GoogleKey)
|
||||
if GoogleKey then
|
||||
self.msrs:SetProviderOptionsGoogle(GoogleKey,GoogleKey)
|
||||
self.msrs:SetProvider(MSRS.Provider.GOOGLE)
|
||||
end
|
||||
-- Pre-configured Google?
|
||||
if (not GoogleKey) and self.msrs:GetProvider() == MSRS.Provider.GOOGLE then
|
||||
voice = Voice or MSRS.poptions.gcloud.voice
|
||||
end
|
||||
self.msrs:SetVoice(voice)
|
||||
self.msrs:SetCoordinate(self.airbase:GetCoordinate())
|
||||
self.msrsQ = MSRSQUEUE:New("ATIS")
|
||||
self.msrsQ:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers)
|
||||
if self.dTQueueCheck<=10 then
|
||||
self:SetQueueUpdateTime(90)
|
||||
end
|
||||
--else
|
||||
--self:E(self.lid..string.format("ERROR: No SRS path specified!"))
|
||||
--end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set an alternative provider to the one set in your MSRS configuration file.
|
||||
-- @param #ATIS self
|
||||
-- @param #string Provider The provider to use. Known providers are: `MSRS.Provider.WINDOWS` and `MSRS.Provider.GOOGLE`
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetSRSProvider(Provider)
|
||||
self:T(self.lid.."SetSRSProvider")
|
||||
if self.msrs then
|
||||
self.msrs:SetProvider(Provider)
|
||||
else
|
||||
self:E(self.lid..string.format("ERROR: No SRS path specified!"))
|
||||
MESSAGE:New(self.lid.."Set up SRS first before trying to change the provider!",30,"ATIS"):ToAll():ToLog()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@ -3072,6 +3072,7 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum
|
||||
self.SRS:SetPort(Port or 5002)
|
||||
self.SRS:SetLabel(self.AirbossRadio.alias or "AIRBOSS")
|
||||
self.SRS:SetCoordinate(self.carrier:GetCoordinate())
|
||||
self.SRS:SetVolume(Volume)
|
||||
--self.SRS:SetModulations(Modulations)
|
||||
if GoogleCreds then
|
||||
self.SRS:SetGoogle(GoogleCreds)
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **applevangelist**
|
||||
-- @date Last Update Nov 2023
|
||||
-- @date Last Update Jan 2024
|
||||
-- @module Ops.AWACS
|
||||
-- @image OPS_AWACS.jpg
|
||||
|
||||
@ -507,7 +507,7 @@ do
|
||||
-- @field #AWACS
|
||||
AWACS = {
|
||||
ClassName = "AWACS", -- #string
|
||||
version = "0.2.59", -- #string
|
||||
version = "0.2.61", -- #string
|
||||
lid = "", -- #string
|
||||
coalition = coalition.side.BLUE, -- #number
|
||||
coalitiontxt = "blue", -- #string
|
||||
@ -1405,15 +1405,18 @@ function AWACS:SetTacticalRadios(BaseFreq,Increase,Modulation,Interval,Number)
|
||||
self.TacticalFrequencies[freq] = freq
|
||||
end
|
||||
if self.AwacsSRS then
|
||||
self.TacticalSRS = MSRS:New(self.PathToSRS,self.TacticalBaseFreq,self.TacticalModulation,self.Volume)
|
||||
self.TacticalSRS = MSRS:New(self.PathToSRS,self.TacticalBaseFreq,self.TacticalModulation)
|
||||
self.TacticalSRS:SetCoalition(self.coalition)
|
||||
self.TacticalSRS:SetGender(self.Gender)
|
||||
self.TacticalSRS:SetCulture(self.Culture)
|
||||
self.TacticalSRS:SetVoice(self.Voice)
|
||||
self.TacticalSRS:SetPort(self.Port)
|
||||
self.TacticalSRS:SetLabel("AWACS")
|
||||
self.TacticalSRS:SetVolume(self.Volume)
|
||||
if self.PathToGoogleKey then
|
||||
self.TacticalSRS:SetGoogle(self.PathToGoogleKey)
|
||||
--self.TacticalSRS:SetGoogle(self.PathToGoogleKey)
|
||||
self.TacticalSRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.AccessKey)
|
||||
self.TacticalSRS:SetProvider(MSRS.Provider.GOOGLE)
|
||||
end
|
||||
self.TacticalSRSQ = MSRSQUEUE:New("Tactical AWACS")
|
||||
end
|
||||
@ -2069,7 +2072,7 @@ function AWACS:AddGroupToDetection(Group)
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Set AWACS SRS TTS details - see @{Sound.SRS} for details
|
||||
--- [User] Set AWACS SRS TTS details - see @{Sound.SRS} for details. `SetSRS()` will try to use as many attributes configured with @{Sound.SRS#MSRS.LoadConfigFile}() as possible.
|
||||
-- @param #AWACS self
|
||||
-- @param #string PathToSRS Defaults to "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
||||
-- @param #string Gender Defaults to "male"
|
||||
@ -2078,29 +2081,39 @@ end
|
||||
-- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS#SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`.
|
||||
-- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS.
|
||||
-- @param #number Volume Volume - between 0.0 (silent) and 1.0 (loudest)
|
||||
-- @param #string PathToGoogleKey Path to your google key if you want to use google TTS
|
||||
-- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS; if you use a config file for MSRS, hand in nil here.
|
||||
-- @param #string AccessKey (Optional) Your Google API access key. This is necessary if DCS-gRPC is used as backend; if you use a config file for MSRS, hand in nil here.
|
||||
-- @return #AWACS self
|
||||
function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey)
|
||||
function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey)
|
||||
self:T(self.lid.."SetSRS")
|
||||
self.PathToSRS = PathToSRS or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
||||
self.Gender = Gender or "male"
|
||||
self.Culture = Culture or "en-US"
|
||||
self.Port = Port or 5002
|
||||
self.Voice = Voice
|
||||
self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
||||
self.Gender = Gender or MSRS.gender or "male"
|
||||
self.Culture = Culture or MSRS.culture or "en-US"
|
||||
self.Port = Port or MSRS.port or 5002
|
||||
self.Voice = Voice or MSRS.voice
|
||||
self.PathToGoogleKey = PathToGoogleKey
|
||||
self.AccessKey = AccessKey
|
||||
self.Volume = Volume or 1.0
|
||||
|
||||
self.AwacsSRS = MSRS:New(self.PathToSRS,self.MultiFrequency,self.MultiModulation,self.Volume)
|
||||
self.AwacsSRS = MSRS:New(self.PathToSRS,self.MultiFrequency,self.MultiModulation)
|
||||
self.AwacsSRS:SetCoalition(self.coalition)
|
||||
self.AwacsSRS:SetGender(self.Gender)
|
||||
self.AwacsSRS:SetCulture(self.Culture)
|
||||
self.AwacsSRS:SetVoice(self.Voice)
|
||||
self.AwacsSRS:SetPort(self.Port)
|
||||
self.AwacsSRS:SetLabel("AWACS")
|
||||
self.AwacsSRS:SetVolume(Volume)
|
||||
if self.PathToGoogleKey then
|
||||
self.AwacsSRS:SetGoogle(self.PathToGoogleKey)
|
||||
--self.AwacsSRS:SetGoogle(self.PathToGoogleKey)
|
||||
self.AwacsSRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.AccessKey)
|
||||
self.AwacsSRS:SetProvider(MSRS.Provider.GOOGLE)
|
||||
end
|
||||
|
||||
-- Pre-configured Google?
|
||||
if (not PathToGoogleKey) and self.AwacsSRS:GetProvider() == MSRS.Provider.GOOGLE then
|
||||
self.PathToGoogleKey = MSRS.poptions.gcloud.credentials
|
||||
self.Voice = Voice or MSRS.poptions.gcloud.voice
|
||||
self.AccessKey = AccessKey or MSRS.poptions.gcloud.key
|
||||
end
|
||||
self.AwacsSRS:SetVoice(self.Voice)
|
||||
return self
|
||||
end
|
||||
|
||||
@ -2935,7 +2948,7 @@ function AWACS:_Picture(Group,IsGeneral)
|
||||
if not self.intel then
|
||||
-- no intel yet!
|
||||
local picclean = self.gettext:GetEntry("PICCLEAN",self.locale)
|
||||
text = string.format(picclean,self.callsigntxt, gcallsign)
|
||||
text = string.format(picclean,gcallsign,self.callsigntxt)
|
||||
textScreen = text
|
||||
|
||||
self:_NewRadioEntry(text,text,GID,false,true,true,false)
|
||||
@ -3663,7 +3676,7 @@ function AWACS:_CheckInAI(FlightGroup,Group,AuftragsNr)
|
||||
CAPVoice = self.CapVoices[math.floor(math.random(1,10))]
|
||||
end
|
||||
|
||||
FlightGroup:SetSRS(self.PathToSRS,self.CAPGender,self.CAPCulture,CAPVoice,self.Port,self.PathToGoogleKey,"FLIGHT")
|
||||
FlightGroup:SetSRS(self.PathToSRS,self.CAPGender,self.CAPCulture,CAPVoice,self.Port,self.PathToGoogleKey,"FLIGHT",1)
|
||||
|
||||
local checkai = self.gettext:GetEntry("CHECKINAI",self.locale)
|
||||
text = string.format(checkai,self.callsigntxt, managedgroup.CallSign, self.CAPTimeOnStation, self.AOName)
|
||||
@ -6595,7 +6608,7 @@ function AWACS:onafterCheckTacticalQueue(From,Event,To)
|
||||
if self.PathToGoogleKey then
|
||||
gtext = string.format("<speak><prosody rate='medium'>%s</prosody></speak>",gtext)
|
||||
end
|
||||
self.TacticalSRSQ:NewTransmission(gtext,nil,self.TacticalSRS,nil,0.5,nil,nil,nil,frequency,self.TacticalModulation,nil,nil,nil,nil,nil)
|
||||
self.TacticalSRSQ:NewTransmission(gtext,nil,self.TacticalSRS,nil,0.5,nil,nil,nil,frequency,self.TacticalModulation)
|
||||
|
||||
self:T(RadioEntry.TextTTS)
|
||||
|
||||
|
||||
@ -411,26 +411,36 @@ function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port,
|
||||
self:SetRunwayRepairtime()
|
||||
self.nosubs = false
|
||||
|
||||
-- Set SRS Port
|
||||
self:SetSRSPort(Port or 5002)
|
||||
|
||||
-- Set Callsign Options
|
||||
self:SetCallSignOptions(true,true)
|
||||
|
||||
-- Init msrs queue.
|
||||
self.msrsqueue=MSRSQUEUE:New(self.alias)
|
||||
|
||||
-- Init msrs bases
|
||||
local path = PathToSRS or MSRS.path
|
||||
local port = Port or MSRS.port or 5002
|
||||
|
||||
-- Set SRS Port
|
||||
self:SetSRSPort(port)
|
||||
|
||||
-- SRS for Tower.
|
||||
self.msrsTower=MSRS:New(PathToSRS, Frequency, Modulation)
|
||||
self.msrsTower:SetPort(self.Port)
|
||||
self.msrsTower:SetGoogle(GoogleKey)
|
||||
self.msrsTower=MSRS:New(path, Frequency, Modulation)
|
||||
self.msrsTower:SetPort(port)
|
||||
if GoogleKey then
|
||||
self.msrsTower:SetProviderOptionsGoogle(GoogleKey,GoogleKey)
|
||||
self.msrsTower:SetProvider(MSRS.Provider.GOOGLE)
|
||||
end
|
||||
self.msrsTower:SetCoordinate(self:GetCoordinate())
|
||||
self:SetSRSTower()
|
||||
|
||||
-- SRS for Pilot.
|
||||
self.msrsPilot=MSRS:New(PathToSRS, Frequency, Modulation)
|
||||
self.msrsPilot:SetPort(self.Port)
|
||||
self.msrsPilot:SetGoogle(GoogleKey)
|
||||
if GoogleKey then
|
||||
self.msrsPilot:SetProviderOptionsGoogle(GoogleKey,GoogleKey)
|
||||
self.msrsPilot:SetProvider(MSRS.Provider.GOOGLE)
|
||||
end
|
||||
self.msrsTower:SetCoordinate(self:GetCoordinate())
|
||||
self:SetSRSPilot()
|
||||
|
||||
@ -633,7 +643,6 @@ function FLIGHTCONTROL:_SetSRSOptions(msrs, Gender, Culture, Voice, Volume, Labe
|
||||
msrs:SetVoice(Voice)
|
||||
msrs:SetVolume(Volume)
|
||||
msrs:SetLabel(Label)
|
||||
msrs:SetGoogle(PathToGoogleCredentials)
|
||||
msrs:SetCoalition(self:GetCoalition())
|
||||
msrs:SetPort(Port or self.Port or 5002)
|
||||
end
|
||||
@ -648,12 +657,11 @@ end
|
||||
-- @param #string Voice Specific voice. Overrides `Gender` and `Culture`. See [Google Voices](https://cloud.google.com/text-to-speech/docs/voices).
|
||||
-- @param #number Volume Volume. Default 1.0.
|
||||
-- @param #string Label Name under which SRS transmits. Default `self.alias`.
|
||||
-- @param #string PathToGoogleCredentials Path to google credentials json file.
|
||||
-- @return #FLIGHTCONTROL self
|
||||
function FLIGHTCONTROL:SetSRSTower(Gender, Culture, Voice, Volume, Label, PathToGoogleCredentials)
|
||||
function FLIGHTCONTROL:SetSRSTower(Gender, Culture, Voice, Volume, Label)
|
||||
|
||||
if self.msrsTower then
|
||||
self:_SetSRSOptions(self.msrsTower, Gender or "female", Culture or "en-GB", Voice, Volume, Label or self.alias, PathToGoogleCredentials)
|
||||
self:_SetSRSOptions(self.msrsTower, Gender or "female", Culture or "en-GB", Voice, Volume, Label or self.alias)
|
||||
end
|
||||
|
||||
return self
|
||||
@ -666,12 +674,11 @@ end
|
||||
-- @param #string Voice Specific voice. Overrides `Gender` and `Culture`.
|
||||
-- @param #number Volume Volume. Default 1.0.
|
||||
-- @param #string Label Name under which SRS transmits. Default "Pilot".
|
||||
-- @param #string PathToGoogleCredentials Path to google credentials json file.
|
||||
-- @return #FLIGHTCONTROL self
|
||||
function FLIGHTCONTROL:SetSRSPilot(Gender, Culture, Voice, Volume, Label, PathToGoogleCredentials)
|
||||
function FLIGHTCONTROL:SetSRSPilot(Gender, Culture, Voice, Volume, Label)
|
||||
|
||||
if self.msrsPilot then
|
||||
self:_SetSRSOptions(self.msrsPilot, Gender or "male", Culture or "en-US", Voice, Volume, Label or "Pilot", PathToGoogleCredentials)
|
||||
self:_SetSRSOptions(self.msrsPilot, Gender or "male", Culture or "en-US", Voice, Volume, Label or "Pilot")
|
||||
end
|
||||
|
||||
return self
|
||||
@ -876,7 +883,7 @@ end
|
||||
|
||||
--- Set ATIS.
|
||||
-- @param #FLIGHTCONTROL self
|
||||
-- @param Ops.ATIS#ATIS ATIS ATIS.
|
||||
-- @param Ops.ATIS#ATIS Atis ATIS.
|
||||
-- @return #FLIGHTCONTROL self
|
||||
function FLIGHTCONTROL:SetATIS(Atis)
|
||||
self.atis=Atis
|
||||
|
||||
@ -3230,13 +3230,22 @@ function FLIGHTGROUP:_LandAtAirbase(airbase, SpeedTo, SpeedHold, SpeedLand)
|
||||
local TaskFinal = self.group:TaskFunction("FLIGHTGROUP._OnFinal", self)
|
||||
|
||||
-- Final approach waypoint.
|
||||
local papp=airbase:GetCoordinate():Translate(x1, runway.heading-180):SetAltitude(h1)
|
||||
local rheading
|
||||
if runway then
|
||||
rheading = runway.heading-180
|
||||
else
|
||||
-- AB HeloBase w/o runway eg Naqoura
|
||||
local wind = airbase:GetCoordinate():GetWind()
|
||||
rheading = -wind
|
||||
end
|
||||
|
||||
local papp=airbase:GetCoordinate():Translate(x1, rheading):SetAltitude(h1)
|
||||
wp[#wp+1]=papp:WaypointAirTurningPoint("BARO", UTILS.KnotsToKmph(SpeedLand), {TaskFinal}, "Final Approach")
|
||||
|
||||
-- Okay, it looks like it's best to specify the coordinates not at the airbase but a bit away. This causes a more direct landing approach.
|
||||
local pland=airbase:GetCoordinate():Translate(x2, runway.heading-180):SetAltitude(h2)
|
||||
local pland=airbase:GetCoordinate():Translate(x2, rheading):SetAltitude(h2)
|
||||
wp[#wp+1]=pland:WaypointAirLanding(UTILS.KnotsToKmph(SpeedLand), airbase, {}, "Landing")
|
||||
|
||||
|
||||
elseif airbase:IsShip() or airbase:IsHelipad() then
|
||||
|
||||
---
|
||||
|
||||
@ -2265,7 +2265,7 @@ function LEGION:RecruitAssetsForMission(Mission)
|
||||
end
|
||||
end
|
||||
|
||||
self:T(self.lid..string.format("Largest cargo bay available=%.1f", MaxWeight))
|
||||
self:T(self.lid..string.format("Largest cargo bay available=%.1f", MaxWeight or 0))
|
||||
end
|
||||
|
||||
|
||||
@ -2745,7 +2745,7 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
|
||||
end
|
||||
|
||||
-- Now we have a long list with assets.
|
||||
LEGION._OptimizeAssetSelection(Assets, MissionTypeOpt, TargetVec2, false)
|
||||
LEGION._OptimizeAssetSelection(Assets, MissionTypeOpt, TargetVec2, false, TotalWeight)
|
||||
|
||||
|
||||
-- Get payloads for air assets.
|
||||
@ -2770,7 +2770,7 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
|
||||
end
|
||||
|
||||
-- Now find the best asset for the given payloads.
|
||||
LEGION._OptimizeAssetSelection(Assets, MissionTypeOpt, TargetVec2, true)
|
||||
LEGION._OptimizeAssetSelection(Assets, MissionTypeOpt, TargetVec2, true, TotalWeight)
|
||||
|
||||
-- Number of assets. At most NreqMax.
|
||||
local Nassets=math.min(#Assets, NreqMax)
|
||||
@ -3114,8 +3114,9 @@ end
|
||||
-- @param #string MissionType Mission type for which the best assets are desired.
|
||||
-- @param DCS#Vec2 TargetVec2 Target 2D vector.
|
||||
-- @param #boolean IncludePayload If `true`, include the payload in the calulation if the asset has one attached.
|
||||
-- @param #number TotalWeight The total weight of the cargo to be transported, if applicable.
|
||||
-- @return #number Mission score.
|
||||
function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, IncludePayload)
|
||||
function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, IncludePayload, TotalWeight)
|
||||
|
||||
-- Mission score.
|
||||
local score=0
|
||||
@ -3209,8 +3210,18 @@ function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, Inclu
|
||||
|
||||
-- TRANSPORT specific.
|
||||
if MissionType==AUFTRAG.Type.OPSTRANSPORT then
|
||||
-- Add 1 score point for each 10 kg of cargo bay.
|
||||
score=score+UTILS.Round(asset.cargobaymax/10, 0)
|
||||
if TotalWeight then
|
||||
-- Add 1 score point for each 10 kg of cargo bay capacity up to the total cargo weight,
|
||||
-- and then subtract 1 score point for each excess 10kg of cargo bay capacity.
|
||||
if asset.cargobaymax < TotalWeight then
|
||||
score=score+UTILS.Round(asset.cargobaymax/10, 0)
|
||||
else
|
||||
score=score+UTILS.Round(TotalWeight/10, 0)
|
||||
end
|
||||
else
|
||||
-- Add 1 score point for each 10 kg of cargo bay.
|
||||
score=score+UTILS.Round(asset.cargobaymax/10, 0)
|
||||
end
|
||||
end
|
||||
|
||||
-- TODO: This could be vastly improved. Need to gather ideas during testing.
|
||||
@ -3231,14 +3242,15 @@ end
|
||||
-- @param #string MissionType Mission type.
|
||||
-- @param DCS#Vec2 TargetVec2 Target position as 2D vector.
|
||||
-- @param #boolean IncludePayload If `true`, include the payload in the calulation if the asset has one attached.
|
||||
function LEGION._OptimizeAssetSelection(assets, MissionType, TargetVec2, IncludePayload)
|
||||
-- @param #number TotalWeight The total weight of the cargo to be transported, if applicable.
|
||||
function LEGION._OptimizeAssetSelection(assets, MissionType, TargetVec2, IncludePayload, TotalWeight)
|
||||
|
||||
-- Calculate the mission score of all assets.
|
||||
for _,_asset in pairs(assets) do
|
||||
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||
|
||||
-- Calculate the asset score.
|
||||
asset.score=LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, IncludePayload)
|
||||
asset.score=LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, IncludePayload, TotalWeight)
|
||||
|
||||
if IncludePayload then
|
||||
|
||||
|
||||
@ -2316,14 +2316,17 @@ end
|
||||
-- @return #OPSGROUP self
|
||||
function OPSGROUP:SetSRS(PathToSRS, Gender, Culture, Voice, Port, PathToGoogleKey, Label, Volume)
|
||||
self.useSRS=true
|
||||
self.msrs=MSRS:New(PathToSRS, self.frequency, self.modulation)
|
||||
local path = PathToSRS or MSRS.path
|
||||
local port = Port or MSRS.port
|
||||
self.msrs=MSRS:New(path, self.frequency, self.modulation)
|
||||
self.msrs:SetGender(Gender)
|
||||
self.msrs:SetCulture(Culture)
|
||||
self.msrs:SetVoice(Voice)
|
||||
self.msrs:SetPort(Port)
|
||||
self.msrs:SetPort(port)
|
||||
self.msrs:SetLabel(Label)
|
||||
if PathToGoogleKey then
|
||||
self.msrs:SetGoogle(PathToGoogleKey)
|
||||
self.msrs:SetProviderOptionsGoogle(PathToGoogleKey,PathToGoogleKey)
|
||||
self.msrs:SetProvider(MSRS.Provider.GOOGLE)
|
||||
end
|
||||
self.msrs:SetCoalition(self:GetCoalition())
|
||||
self.msrs:SetVolume(Volume)
|
||||
|
||||
@ -605,6 +605,16 @@ function OPSTRANSPORT:AddCargoGroups(GroupSet, TransportZoneCombo, DisembarkActi
|
||||
self:AddCargoGroups(group, TransportZoneCombo, DisembarkActivation)
|
||||
|
||||
end
|
||||
|
||||
-- Use FSM function to keep the SET up-to-date. Note that it overwrites the user FMS function, which cannot be used any more now.
|
||||
local groupset=GroupSet --Core.Set#SET_OPSGROUP
|
||||
function groupset.OnAfterAdded(groupset, From, Event, To, ObjectName, Object)
|
||||
|
||||
self:T(self.lid..string.format("Adding Cargo Group %s", tostring(ObjectName)))
|
||||
self:AddCargoGroups(Object, TransportZoneCombo, DisembarkActivation, DisembarkZone, DisembarkCarriers)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Debug info.
|
||||
|
||||
@ -1508,15 +1508,22 @@ function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,V
|
||||
self.Modulation = Modulation or {radio.modulation.FM,radio.modulation.AM} --
|
||||
self.BCModulation = self.Modulation
|
||||
-- set up SRS
|
||||
self.SRS=MSRS:New(self.PathToSRS,self.Frequency,self.Modulation,self.Volume)
|
||||
self.SRS=MSRS:New(self.PathToSRS,self.Frequency,self.Modulation)
|
||||
self.SRS:SetCoalition(self.Coalition)
|
||||
self.SRS:SetLabel(self.MenuName or self.Name)
|
||||
self.SRS:SetGender(self.Gender)
|
||||
self.SRS:SetCulture(self.Culture)
|
||||
self.SRS:SetPort(self.Port)
|
||||
self.SRS:SetVoice(self.Voice)
|
||||
self.SRS:SetVolume(self.Volume)
|
||||
if self.PathToGoogleKey then
|
||||
self.SRS:SetGoogle(self.PathToGoogleKey)
|
||||
self.SRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.PathToGoogleKey)
|
||||
self.SRS:SetProvider(MSRS.Provider.GOOGLE)
|
||||
end
|
||||
-- Pre-configured Google?
|
||||
if (not PathToGoogleKey) and self.AwacsSRS:GetProvider() == MSRS.Provider.GOOGLE then
|
||||
self.PathToGoogleKey = MSRS.poptions.gcloud.credentials
|
||||
self.Voice = Voice or MSRS.poptions.gcloud.voice
|
||||
end
|
||||
self.SRSQueue = MSRSQUEUE:New(self.MenuName or self.Name)
|
||||
self.SRSQueue:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers)
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
-- ===
|
||||
-- @module Ops.PlayerTask
|
||||
-- @image OPS_PlayerTask.jpg
|
||||
-- @date Last Update Oct 2023
|
||||
-- @date Last Update Jan 2024
|
||||
|
||||
|
||||
do
|
||||
@ -1552,7 +1552,7 @@ PLAYERTASKCONTROLLER.Messages = {
|
||||
|
||||
--- PLAYERTASK class version.
|
||||
-- @field #string version
|
||||
PLAYERTASKCONTROLLER.version="0.1.63"
|
||||
PLAYERTASKCONTROLLER.version="0.1.64"
|
||||
|
||||
--- Create and run a new TASKCONTROLLER instance.
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
@ -3993,7 +3993,7 @@ function PLAYERTASKCONTROLLER:SetupIntel(RecceName)
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Set SRS TTS details - see @{Sound.SRS} for details
|
||||
--- [User] Set SRS TTS details - see @{Sound.SRS} for details.`SetSRS()` will try to use as many attributes configured with @{Sound.SRS#MSRS.LoadConfigFile}() as possible.
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param #number Frequency Frequency to be used. Can also be given as a table of multiple frequencies, e.g. 271 or {127,251}. There needs to be exactly the same number of modulations!
|
||||
-- @param #number Modulation Modulation to be used. Can also be given as a table of multiple modulations, e.g. radio.modulation.AM or {radio.modulation.FM,radio.modulation.AM}. There needs to be exactly the same number of frequencies!
|
||||
@ -4004,17 +4004,19 @@ end
|
||||
-- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS#SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`.
|
||||
-- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS.
|
||||
-- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest)
|
||||
-- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS
|
||||
-- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS; if you use a config file for MSRS, hand in nil here.
|
||||
-- @param #string AccessKey (Optional) Your Google API access key. This is necessary if DCS-gRPC is used as backend; if you use a config file for MSRS, hand in nil here.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate from which the controller radio is sending
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,Coordinate)
|
||||
function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Coordinate)
|
||||
self:T(self.lid.."SetSRS")
|
||||
self.PathToSRS = PathToSRS or "C:\\Program Files\\DCS-SimpleRadio-Standalone" --
|
||||
self.Gender = Gender or "male" --
|
||||
self.Culture = Culture or "en-US" --
|
||||
self.Port = Port or 5002 --
|
||||
self.Voice = Voice --
|
||||
self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" --
|
||||
self.Gender = Gender or MSRS.gender or "male" --
|
||||
self.Culture = Culture or MSRS.culture or "en-US" --
|
||||
self.Port = Port or MSRS.port or 5002 --
|
||||
self.Voice = Voice or MSRS.voice
|
||||
self.PathToGoogleKey = PathToGoogleKey --
|
||||
self.AccessKey = AccessKey
|
||||
self.Volume = Volume or 1.0 --
|
||||
self.UseSRS = true
|
||||
self.Frequency = Frequency or {127,251} --
|
||||
@ -4022,19 +4024,28 @@ function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Cultu
|
||||
self.Modulation = Modulation or {radio.modulation.FM,radio.modulation.AM} --
|
||||
self.BCModulation = self.Modulation
|
||||
-- set up SRS
|
||||
self.SRS=MSRS:New(self.PathToSRS,self.Frequency,self.Modulation,self.Volume)
|
||||
self.SRS=MSRS:New(self.PathToSRS,self.Frequency,self.Modulation)
|
||||
self.SRS:SetCoalition(self.Coalition)
|
||||
self.SRS:SetLabel(self.MenuName or self.Name)
|
||||
self.SRS:SetGender(self.Gender)
|
||||
self.SRS:SetCulture(self.Culture)
|
||||
self.SRS:SetPort(self.Port)
|
||||
self.SRS:SetVoice(self.Voice)
|
||||
self.SRS:SetVolume(self.Volume)
|
||||
if self.PathToGoogleKey then
|
||||
self.SRS:SetGoogle(self.PathToGoogleKey)
|
||||
--self.SRS:SetGoogle(self.PathToGoogleKey)
|
||||
self.SRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.AccessKey)
|
||||
self.SRS:SetProvider(MSRS.Provider.GOOGLE)
|
||||
end
|
||||
-- Pre-configured Google?
|
||||
if (not PathToGoogleKey) and self.SRS:GetProvider() == MSRS.Provider.GOOGLE then
|
||||
self.PathToGoogleKey = MSRS.poptions.gcloud.credentials
|
||||
self.Voice = Voice or MSRS.poptions.gcloud.voice
|
||||
self.AccessKey = AccessKey or MSRS.poptions.gcloud.key
|
||||
end
|
||||
if Coordinate then
|
||||
self.SRS:SetCoordinate(Coordinate)
|
||||
end
|
||||
self.SRS:SetVoice(self.Voice)
|
||||
self.SRSQueue = MSRSQUEUE:New(self.MenuName or self.Name)
|
||||
self.SRSQueue:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers)
|
||||
return self
|
||||
|
||||
@ -183,8 +183,10 @@ end
|
||||
-- @param #number Port SRS port. Default 5002.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:SetSRS(PathToSRS, Port)
|
||||
self.msrs=MSRS:New(PathToSRS, self.frequency/1000000, self.modulation)
|
||||
self.msrs:SetPort(Port)
|
||||
local path = PathToSRS or MSRS.path
|
||||
local port = Port or MSRS.port
|
||||
self.msrs=MSRS:New(path, self.frequency/1000000, self.modulation)
|
||||
self.msrs:SetPort(port)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@ -272,6 +272,13 @@ MSRS.Voices = {
|
||||
["Zira"] = "Microsoft Zira Desktop", -- en-US
|
||||
["Hortense"] = "Microsoft Hortense Desktop", --fr-FR
|
||||
},
|
||||
MicrosoftGRPC = {
|
||||
["Hedda"] = "Hedda", -- de-DE
|
||||
["Hazel"] = "Hazel", -- en-GB
|
||||
["David"] = "David", -- en-US
|
||||
["Zira"] = "Zira", -- en-US
|
||||
["Hortense"] = "Hortense", --fr-FR
|
||||
},
|
||||
Google = {
|
||||
Standard = {
|
||||
["en_AU_Standard_A"] = 'en-AU-Standard-A', -- [1] FEMALE
|
||||
@ -516,8 +523,20 @@ end
|
||||
-- @return #MSRS self
|
||||
function MSRS:SetBackend(Backend)
|
||||
self:F( {Backend=Backend} )
|
||||
self.backend=Backend or MSRS.Backend.SRSEXE
|
||||
|
||||
Backend = Backend or MSRS.Backend.SRSEXE -- avoid nil
|
||||
local function Checker(back)
|
||||
local ok = false
|
||||
for _,_backend in pairs(MSRS.Backend) do
|
||||
if tostring(back) == _backend then ok = true end
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
if Checker(Backend) then
|
||||
self.backend=Backend
|
||||
else
|
||||
MESSAGE:New("ERROR: Backend "..tostring(Backend).." is not supported!",30,"MSRS",true):ToLog():ToAll()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@ -906,12 +925,16 @@ end
|
||||
-- @param #string Provider
|
||||
-- @return #MSRS self
|
||||
function MSRS:SetProvider(Provider)
|
||||
self:F( {Provider=Provider} )
|
||||
self.provider = Provider or MSRS.Provider.WINDOWS
|
||||
return self
|
||||
BASE:F( {Provider=Provider} )
|
||||
if self then
|
||||
self.provider = Provider or MSRS.Provider.WINDOWS
|
||||
return self
|
||||
else
|
||||
MSRS.provider = Provider or MSRS.Provider.WINDOWS
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
--- Get provider.
|
||||
-- @param #MSRS self
|
||||
-- @return #MSRS self
|
||||
@ -928,7 +951,7 @@ end
|
||||
-- @param #string Region Region to use.
|
||||
-- @return #MSRS.ProviderOptions Provider optionas table.
|
||||
function MSRS:SetProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region)
|
||||
self:F( {Provider, CredentialsFile, AccessKey, SecretKey, Region} )
|
||||
BASE:F( {Provider, CredentialsFile, AccessKey, SecretKey, Region} )
|
||||
local option=MSRS._CreateProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region)
|
||||
|
||||
if self then
|
||||
@ -1184,7 +1207,7 @@ end
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate.
|
||||
-- @return #MSRS self
|
||||
function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate)
|
||||
self:F( {Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate} )
|
||||
self:T({Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate} )
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate)
|
||||
@ -1205,6 +1228,7 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture
|
||||
self:_ExecCommand(command)
|
||||
|
||||
elseif self.backend==MSRS.Backend.GRPC then
|
||||
--BASE:I("MSRS.Backend.GRPC")
|
||||
|
||||
self:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate)
|
||||
|
||||
@ -1593,7 +1617,7 @@ end
|
||||
--
|
||||
-- -- Moose MSRS default Config
|
||||
-- MSRS_Config = {
|
||||
-- Path = C:\\Program Files\\DCS-SimpleRadio-Standalone, -- Path to SRS install directory.
|
||||
-- Path = "C:\\Program Files\\DCS-SimpleRadio-Standalone", -- Path to SRS install directory.
|
||||
-- Port = 5002, -- Port of SRS server. Default 5002.
|
||||
-- Backend = "srsexe", -- Interface to SRS: "srsexe" or "grpc".
|
||||
-- Frequency = {127, 243}, -- Default frequences. Must be a table 1..n entries!
|
||||
@ -1605,8 +1629,7 @@ end
|
||||
-- Gender = "male",
|
||||
-- Voice = "Microsoft Hazel Desktop", -- Voice that is used if no explicit provider voice is specified.
|
||||
-- Label = "MSRS",
|
||||
-- Provider = "win", --Provider for generating TTS (win, gcloud, azure, aws).
|
||||
--
|
||||
-- Provider = "win", --Provider for generating TTS (win, gcloud, azure, aws).
|
||||
-- -- Windows
|
||||
-- win = {
|
||||
-- voice = "Microsoft Hazel Desktop",
|
||||
@ -1632,7 +1655,7 @@ end
|
||||
-- },
|
||||
-- }
|
||||
--
|
||||
-- 3) The config file is automatically loaded when Moose starts. YOu can also load the config into the MSRS raw class manually before you do anything else:
|
||||
-- 3) The config file is automatically loaded when Moose starts. You can also load the config into the MSRS raw class manually before you do anything else:
|
||||
--
|
||||
-- MSRS.LoadConfigFile() -- Note the "." here
|
||||
--
|
||||
@ -1648,8 +1671,7 @@ end
|
||||
-- 4) Use the config in your code like so, variable names are basically the same as in the config file, but all lower case, examples:
|
||||
--
|
||||
-- -- Needed once only
|
||||
-- MESSAGE.SetMSRS(MSRS.path,nil,MSRS.google,243,radio.modulation.AM,nil,nil,
|
||||
-- MSRS.Voices.Google.Standard.de_DE_Standard_B,coalition.side.BLUE)
|
||||
-- MESSAGE.SetMSRS(MSRS.path,MSRS.port,nil,127,rado.modulation.FM,nil,nil,nil,nil,nil,"TALK")
|
||||
--
|
||||
-- -- later on in your code
|
||||
--
|
||||
@ -1831,7 +1853,7 @@ end
|
||||
-- @param #MSRSQUEUE self
|
||||
-- @return #MSRSQUEUE self The MSRSQUEUE object.
|
||||
function MSRSQUEUE:Clear()
|
||||
self:I(self.lid.."Clearing MSRSQUEUE")
|
||||
self:T(self.lid.."Clearing MSRSQUEUE")
|
||||
self.queue={}
|
||||
return self
|
||||
end
|
||||
@ -1842,7 +1864,6 @@ end
|
||||
-- @param #MSRSQUEUE.Transmission transmission The transmission data table.
|
||||
-- @return #MSRSQUEUE self
|
||||
function MSRSQUEUE:AddTransmission(transmission)
|
||||
|
||||
-- Init.
|
||||
transmission.isplaying=false
|
||||
transmission.Tstarted=nil
|
||||
@ -1921,20 +1942,20 @@ function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgr
|
||||
transmission.Tplay=tstart or timer.getAbsTime()
|
||||
transmission.subtitle=subtitle
|
||||
transmission.interval=interval or 0
|
||||
transmission.frequency=frequency
|
||||
transmission.modulation=modulation
|
||||
transmission.frequency=frequency or msrs.frequencies
|
||||
transmission.modulation=modulation or msrs.modulations
|
||||
transmission.subgroups=subgroups
|
||||
if transmission.subtitle then
|
||||
transmission.subduration=subduration or transmission.duration
|
||||
else
|
||||
transmission.subduration=0 --nil
|
||||
end
|
||||
transmission.gender = gender
|
||||
transmission.culture = culture
|
||||
transmission.voice = voice
|
||||
transmission.volume = volume
|
||||
transmission.label = label
|
||||
transmission.coordinate = coordinate
|
||||
transmission.gender = gender or msrs.gender
|
||||
transmission.culture = culture or msrs.culture
|
||||
transmission.voice = voice or msrs.voice
|
||||
transmission.volume = volume or msrs.volume
|
||||
transmission.label = label or msrs.Label
|
||||
transmission.coordinate = coordinate or msrs.coordinate
|
||||
|
||||
-- Add transmission to queue.
|
||||
self:AddTransmission(transmission)
|
||||
@ -1946,7 +1967,8 @@ end
|
||||
-- @param #MSRSQUEUE self
|
||||
-- @param #MSRSQUEUE.Transmission transmission The transmission.
|
||||
function MSRSQUEUE:Broadcast(transmission)
|
||||
|
||||
self:T(self.lid.."Broadcast")
|
||||
|
||||
if transmission.frequency then
|
||||
transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, transmission.gender, transmission.culture, transmission.voice, transmission.volume, transmission.label, transmission.coordinate)
|
||||
else
|
||||
|
||||
@ -691,7 +691,15 @@ function GROUP:GetUnits()
|
||||
local DCSUnits = DCSGroup:getUnits() or {}
|
||||
local Units = {}
|
||||
for Index, UnitData in pairs( DCSUnits ) do
|
||||
Units[#Units+1] = UNIT:Find( UnitData )
|
||||
|
||||
local unit=UNIT:Find( UnitData )
|
||||
if unit then
|
||||
Units[#Units+1] = UNIT:Find( UnitData )
|
||||
else
|
||||
local UnitName=UnitData:getName()
|
||||
unit=_DATABASE:AddUnit(UnitName)
|
||||
Units[#Units+1]=unit
|
||||
end
|
||||
end
|
||||
self:T3( Units )
|
||||
return Units
|
||||
|
||||
@ -5,37 +5,41 @@
|
||||
-- This method is used by Moose developers and not mission builders.
|
||||
ModuleLoader = 'Scripts/Moose/Modules.lua'
|
||||
|
||||
local f=io.open(ModuleLoader,"r")
|
||||
if f~=nil then
|
||||
io.close(f)
|
||||
if io then
|
||||
local f=io.open(ModuleLoader,"r")
|
||||
if f~=nil then
|
||||
io.close(f)
|
||||
|
||||
env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' )
|
||||
env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' )
|
||||
|
||||
local base = _G
|
||||
local base = _G
|
||||
|
||||
__Moose = {}
|
||||
__Moose = {}
|
||||
|
||||
__Moose.Include = function( IncludeFile )
|
||||
if not __Moose.Includes[ IncludeFile ] then
|
||||
__Moose.Includes[IncludeFile] = IncludeFile
|
||||
local f = assert( base.loadfile( IncludeFile ) )
|
||||
if f == nil then
|
||||
error ("Moose: Could not load Moose file " .. IncludeFile )
|
||||
else
|
||||
env.info( "Moose: " .. IncludeFile .. " dynamically loaded." )
|
||||
return f()
|
||||
__Moose.Include = function( IncludeFile )
|
||||
if not __Moose.Includes[ IncludeFile ] then
|
||||
__Moose.Includes[IncludeFile] = IncludeFile
|
||||
local f = assert( base.loadfile( IncludeFile ) )
|
||||
if f == nil then
|
||||
error ("Moose: Could not load Moose file " .. IncludeFile )
|
||||
else
|
||||
env.info( "Moose: " .. IncludeFile .. " dynamically loaded." )
|
||||
return f()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
__Moose.Includes = {}
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Modules.lua' )
|
||||
BASE:TraceOnOff( true )
|
||||
env.info( '*** MOOSE INCLUDE END *** ' )
|
||||
|
||||
-- Skip the static part of this file completly
|
||||
do return end
|
||||
end
|
||||
|
||||
__Moose.Includes = {}
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Modules.lua' )
|
||||
BASE:TraceOnOff( true )
|
||||
env.info( '*** MOOSE INCLUDE END *** ' )
|
||||
|
||||
-- Skip the static part of this file completly
|
||||
do return end
|
||||
else
|
||||
env.info( '*** MOOSE DYNAMIC INCLUDE NOT POSSIBLE (Desanitize io to use it) *** ' )
|
||||
end
|
||||
|
||||
-- Individual Moose files are not found. Use the static code below.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user