From 1b8c9367a3b7c7c46eac1c19433a16d32d43b746 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 14 Nov 2023 11:57:58 +0100 Subject: [PATCH 1/2] #MANTIS/#SEAD/#SHORAD * Added shoot and scoot for MANTIS and SHORAD * Added detection of TALD ADM-141A (all) --- Moose Development/Moose/Functional/Mantis.lua | 30 +- Moose Development/Moose/Functional/Sead.lua | 3 +- Moose Development/Moose/Functional/Shorad.lua | 263 +++++++++++------- 3 files changed, 191 insertions(+), 105 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 72de47973..1ae8604ca 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -487,7 +487,11 @@ do -- mybluemantis:Start() -- function MANTIS:New(name,samprefix,ewrprefix,hq,coalition,dynamic,awacs, EmOnOff, Padding, Zones) - + + + -- Inherit everything from BASE class. + local self = BASE:Inherit(self, FSM:New()) -- #MANTIS + -- DONE: Create some user functions for these -- DONE: Make HQ useful -- DONE: Set SAMs to auto if EWR dies @@ -549,6 +553,10 @@ do self.ShoradGroupSet = SET_GROUP:New() -- Core.Set#SET_GROUP self.FilterZones = Zones + self.SkateZones = nil + self.SkateNumber = 3 + self.shootandscoot = false + self.UseEmOnOff = true if EmOnOff == false then self.UseEmOnOff = false @@ -560,9 +568,6 @@ do self.advAwacs = false end - -- Inherit everything from BASE class. - local self = BASE:Inherit(self, FSM:New()) -- #MANTIS - -- Set the string id for output to DCS.log file. self.lid=string.format("MANTIS %s | ", self.name) @@ -787,6 +792,19 @@ do return self end + --- Add a SET_ZONE of zones for Shoot&Scoot - SHORAD units will move around + -- @param #MANTIS self + -- @param Core.Set#SET_ZONE ZoneSet Set of zones to be used. Units will move around to the next (random) zone between 100m and 3000m away. + -- @param #number Number Number of closest zones to be considered, defaults to 3. + -- @return #MANTIS self + function MANTIS:AddScootZones(ZoneSet, Number) + self:T(self.lid .. " AddScootZones") + self.SkateZones = ZoneSet + self.SkateNumber = Number or 3 + self.shootandscoot = true + return self + end + --- Function to set accept and reject zones. -- @param #MANTIS self -- @param #table AcceptZones Table of @{Core.Zone#ZONE} objects @@ -1786,6 +1804,10 @@ do self.Shorad:SetDefenseLimits(80,95) self.ShoradLink = true self.Shorad.Groupset=self.ShoradGroupSet + self.Shorad.debug = self.debug + end + if self.shootandscoot and self.SkateZones then + self.Shorad:AddScootZones(self.SkateZones,self.SkateNumber or 3) end self:__Status(-math.random(1,10)) return self diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index aaeb9afd0..d2f03703f 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -34,7 +34,7 @@ -- -- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon. -- Once a HARM attack is detected, SEAD will shut down the radars of the attacked SAM site and take evasive action by moving the SAM --- vehicles around (*if* they are drivable, that is). There's a component of randomness in detection and evasion, which is based on the +-- vehicles around (*if* they are driveable, that is). There's a component of randomness in detection and evasion, which is based on the -- skill set of the SAM set (the higher the skill, the more likely). When a missile is fired from far away, the SAM will stay active for a -- period of time to stay defensive, before it takes evasive actions. -- @@ -66,7 +66,6 @@ SEAD = { -- @field Harms SEAD.Harms = { ["AGM_88"] = "AGM_88", - --["AGM_45"] = "AGM_45", ["AGM_122"] = "AGM_122", ["AGM_84"] = "AGM_84", ["AGM_45"] = "AGM_45", diff --git a/Moose Development/Moose/Functional/Shorad.lua b/Moose Development/Moose/Functional/Shorad.lua index e08f7fa29..ff341ddbb 100644 --- a/Moose Development/Moose/Functional/Shorad.lua +++ b/Moose Development/Moose/Functional/Shorad.lua @@ -21,6 +21,7 @@ -- @image Functional.Shorad.jpg -- -- Date: Nov 2021 +-- Last Update: Nov 2023 ------------------------------------------------------------------------- --- **SHORAD** class, extends Core.Base#BASE @@ -39,8 +40,11 @@ -- @field #boolean DefendHarms Default true, intercept incoming HARMS -- @field #boolean DefendMavs Default true, intercept incoming AG-Missiles -- @field #number DefenseLowProb Default 70, minimum detection limit --- @field #number DefenseHighProb Default 90, maximim detection limit +-- @field #number DefenseHighProb Default 90, maximum detection limit -- @field #boolean UseEmOnOff Decide if we are using Emission on/off (default) or AlarmState red/green. +-- @field #boolean shootandscoot +-- @field #number SkateNumber +-- @field Core.Set#SET_ZONE SkateZones -- @extends Core.Base#BASE @@ -99,7 +103,10 @@ SHORAD = { DefendMavs = true, DefenseLowProb = 70, DefenseHighProb = 90, - UseEmOnOff = false, + UseEmOnOff = true, + shootandscoot = false, + SkateNumber = 3, + SkateZones = nil, } ----------------------------------------------------------------------- @@ -112,7 +119,6 @@ do -- @field Harms SHORAD.Harms = { ["AGM_88"] = "AGM_88", - ["AGM_45"] = "AGM_45", ["AGM_122"] = "AGM_122", ["AGM_84"] = "AGM_84", ["AGM_45"] = "AGM_45", @@ -123,6 +129,8 @@ do ["X_25"] = "X_25", ["X_31"] = "X_31", ["Kh25"] = "Kh25", + ["HY-2"] = "HY-2", + ["ADM_141A"] = "ADM_141A", } --- TODO complete list? @@ -134,7 +142,6 @@ do ["Kh29"] = "Kh29", ["Kh31"] = "Kh31", ["Kh66"] = "Kh66", - --["BGM_109"] = "BGM_109", } --- Instantiates a new SHORAD object @@ -146,7 +153,7 @@ do -- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call -- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral" -- @param #boolean UseEmOnOff Use Emissions On/Off rather than Alarm State Red/Green (default: use Emissions switch) - -- @retunr #SHORAD self + -- @return #SHORAD self function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition, UseEmOnOff) local self = BASE:Inherit( self, FSM:New() ) self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition}) @@ -165,8 +172,9 @@ do self.DefendMavs = true self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin - self.UseEmOnOff = UseEmOnOff or false -- Decide if we are using Emission on/off (default) or AlarmState red/green - self:I("*** SHORAD - Started Version 0.3.1") + self.UseEmOnOff = true -- Decide if we are using Emission on/off (default) or AlarmState red/green + if UseEmOnOff == false then self.UseEmOnOff = UseEmOnOff end + self:I("*** SHORAD - Started Version 0.3.2") -- Set the string id for output to DCS.log file. self.lid=string.format("SHORAD %s | ", self.name) self:_InitState() @@ -176,12 +184,14 @@ do self:SetStartState("Running") self:AddTransition("*", "WakeUpShorad", "*") self:AddTransition("*", "CalculateHitZone", "*") + self:AddTransition("*", "ShootAndScoot", "*") return self end --- Initially set all groups to alarm state GREEN -- @param #SHORAD self + -- @return #SHORAD self function SHORAD:_InitState() self:T(self.lid .. " _InitState") local table = {} @@ -205,21 +215,36 @@ do return self end + --- Add a SET_ZONE of zones for Shoot&Scoot + -- @param #SHORAD self + -- @param Core.Set#SET_ZONE ZoneSet Set of zones to be used. Units will move around to the next (random) zone between 100m and 3000m away. + -- @param #number Number Number of closest zones to be considered, defaults to 3. + -- @return #SHORAD self + function SHORAD:AddScootZones(ZoneSet, Number) + self:T(self.lid .. " AddScootZones") + self.SkateZones = ZoneSet + self.SkateNumber = Number or 3 + self.shootandscoot = true + return self + end + --- Switch debug state on -- @param #SHORAD self -- @param #boolean debug Switch debug on (true) or off (false) + -- @return #SHORAD self function SHORAD:SwitchDebug(onoff) self:T( { onoff } ) if onoff then self:SwitchDebugOn() else - self.SwitchDebugOff() + self:SwitchDebugOff() end return self end --- Switch debug state on -- @param #SHORAD self + -- @return #SHORAD self function SHORAD:SwitchDebugOn() self.debug = true --tracing @@ -230,6 +255,7 @@ do --- Switch debug state off -- @param #SHORAD self + -- @return #SHORAD self function SHORAD:SwitchDebugOff() self.debug = false BASE:TraceOff() @@ -239,6 +265,7 @@ do --- Switch defense for HARMs -- @param #SHORAD self -- @param #boolean onoff + -- @return #SHORAD self function SHORAD:SwitchHARMDefense(onoff) self:T( { onoff } ) local onoff = onoff or true @@ -249,6 +276,7 @@ do --- Switch defense for AGMs -- @param #SHORAD self -- @param #boolean onoff + -- @return #SHORAD self function SHORAD:SwitchAGMDefense(onoff) self:T( { onoff } ) local onoff = onoff or true @@ -260,6 +288,7 @@ do -- @param #SHORAD self -- @param #number low Minimum detection limit, integer 1-100 -- @param #number high Maximum detection limit integer 1-100 + -- @return #SHORAD self function SHORAD:SetDefenseLimits(low,high) self:T( { low, high } ) local low = low or 70 @@ -278,6 +307,7 @@ do --- Set the number of seconds a SHORAD site will stay active -- @param #SHORAD self -- @param #number seconds Number of seconds systems stay active + -- @return #SHORAD self function SHORAD:SetActiveTimer(seconds) self:T(self.lid .. " SetActiveTimer") local timer = seconds or 600 @@ -291,6 +321,7 @@ do --- Set the number of meters for the SHORAD defense zone -- @param #SHORAD self -- @param #number meters Radius of the defense search zone in meters. #SHORADs in this range around a targeted group will go active + -- @return #SHORAD self function SHORAD:SetDefenseRadius(meters) self:T(self.lid .. " SetDefenseRadius") local radius = meters or 20000 @@ -304,6 +335,7 @@ do --- Set using Emission on/off instead of changing alarm state -- @param #SHORAD self -- @param #boolean switch Decide if we are changing alarm state or AI state + -- @return #SHORAD self function SHORAD:SetUsingEmOnOff(switch) self:T(self.lid .. " SetUsingEmOnOff") self.UseEmOnOff = switch or false @@ -375,11 +407,11 @@ do local shorad = self.Groupset local shoradset = shorad:GetAliveSet() --#table local returnname = false + --local TDiff = 1 for _,_groups in pairs (shoradset) do local groupname = _groups:GetName() if string.find(groupname, tgtgrp, 1, true) then returnname = true - --_groups:RelocateGroundRandomInRadius(7,100,false,false) -- be a bit evasive end end return returnname @@ -426,6 +458,7 @@ do -- @param #number Radius Radius of the #ZONE -- @param #number ActiveTimer Number of seconds to stay active -- @param #number TargetCat (optional) Category, i.e. Object.Category.UNIT or Object.Category.STATIC + -- @return #SHORAD self -- @usage Use this function to integrate with other systems, example -- -- local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart() @@ -452,28 +485,35 @@ do local targetzone = ZONE_RADIUS:New("Shorad",targetvec2,Radius) -- create a defense zone to check local groupset = self.Groupset --Core.Set#SET_GROUP local shoradset = groupset:GetAliveSet() --#table + -- local function to switch off shorad again local function SleepShorad(group) - local groupname = group:GetName() - self.ActiveGroups[groupname] = nil - if self.UseEmOnOff then - group:EnableEmission(false) - --group:SetAIOff() - else - group:OptionAlarmStateGreen() + if group and group:IsAlive() then + local groupname = group:GetName() + self.ActiveGroups[groupname] = nil + if self.UseEmOnOff then + group:EnableEmission(false) + else + group:OptionAlarmStateGreen() + end + local text = string.format("Sleeping SHORAD %s", group:GetName()) + self:T(text) + local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug) + --Shoot and Scoot + if self.shootandscoot then + self:__ShootAndScoot(1,group) + end end - local text = string.format("Sleeping SHORAD %s", group:GetName()) - self:T(text) - local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug) end + -- go through set and find the one(s) to activate + local TDiff = 4 for _,_group in pairs (shoradset) do if _group:IsAnyInZone(targetzone) then local text = string.format("Waking up SHORAD %s", _group:GetName()) self:T(text) local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug) if self.UseEmOnOff then - --_group:SetAIOn() _group:EnableEmission(true) end _group:OptionAlarmStateRed() @@ -481,91 +521,128 @@ do if self.ActiveGroups[groupname] == nil then -- no timer yet for this group self.ActiveGroups[groupname] = { Timing = ActiveTimer } local endtime = timer.getTime() + (ActiveTimer * math.random(75,100) / 100 ) -- randomize wakeup a bit - timer.scheduleFunction(SleepShorad, _group, endtime) + self.ActiveGroups[groupname].Timer = TIMER:New(SleepShorad,_group):Start(endtime) + --Shoot and Scoot + if self.shootandscoot then + self:__ShootAndScoot(TDiff,_group) + TDiff=TDiff+1 + end end end end return self end ---- (Internal) Calculate hit zone of an AGM-88 --- @param #SHORAD self --- @param #table SEADWeapon DCS.Weapon object --- @param Core.Point#COORDINATE pos0 Position of the plane when it fired --- @param #number height Height when the missile was fired --- @param Wrapper.Group#GROUP SEADGroup Attacker group --- @return #SHORAD self -function SHORAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup) - self:T("**** Calculating hit zone") - if SEADWeapon and SEADWeapon:isExist() then - --local pos = SEADWeapon:getPoint() + --- (Internal) Calculate hit zone of an AGM-88 + -- @param #SHORAD self + -- @param #table SEADWeapon DCS.Weapon object + -- @param Core.Point#COORDINATE pos0 Position of the plane when it fired + -- @param #number height Height when the missile was fired + -- @param Wrapper.Group#GROUP SEADGroup Attacker group + -- @return #SHORAD self + function SHORAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup) + self:T("**** Calculating hit zone") + if SEADWeapon and SEADWeapon:isExist() then + --local pos = SEADWeapon:getPoint() + + -- postion and height + local position = SEADWeapon:getPosition() + local mheight = height + -- heading + local wph = math.atan2(position.x.z, position.x.x) + if wph < 0 then + wph=wph+2*math.pi + end + wph=math.deg(wph) + + -- velocity + local wpndata = SEAD.HarmData["AGM_88"] + local mveloc = math.floor(wpndata[2] * 340.29) + local c1 = (2*mheight*9.81)/(mveloc^2) + local c2 = (mveloc^2) / 9.81 + local Ropt = c2 * math.sqrt(c1+1) + if height <= 5000 then + Ropt = Ropt * 0.72 + elseif height <= 7500 then + Ropt = Ropt * 0.82 + elseif height <= 10000 then + Ropt = Ropt * 0.87 + elseif height <= 12500 then + Ropt = Ropt * 0.98 + end + + -- look at a couple of zones across the trajectory + for n=1,3 do + local dist = Ropt - ((n-1)*20000) + local predpos= pos0:Translate(dist,wph) + if predpos then - -- postion and height - local position = SEADWeapon:getPosition() - local mheight = height - -- heading - local wph = math.atan2(position.x.z, position.x.x) - if wph < 0 then - wph=wph+2*math.pi - end - wph=math.deg(wph) - - -- velocity - local wpndata = SEAD.HarmData["AGM_88"] - local mveloc = math.floor(wpndata[2] * 340.29) - local c1 = (2*mheight*9.81)/(mveloc^2) - local c2 = (mveloc^2) / 9.81 - local Ropt = c2 * math.sqrt(c1+1) - if height <= 5000 then - Ropt = Ropt * 0.72 - elseif height <= 7500 then - Ropt = Ropt * 0.82 - elseif height <= 10000 then - Ropt = Ropt * 0.87 - elseif height <= 12500 then - Ropt = Ropt * 0.98 + local targetzone = ZONE_RADIUS:New("Target Zone",predpos:GetVec2(),20000) + + if self.debug then + predpos:MarkToAll(string.format("height=%dm | heading=%d | velocity=%ddeg | Ropt=%dm",mheight,wph,mveloc,Ropt),false) + targetzone:DrawZone(coalition.side.BLUE,{0,0,1},0.2,nil,nil,3,true) + end + + local seadset = self.Groupset + local tgtcoord = targetzone:GetRandomPointVec2() + local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord) + local _targetgroup = nil + local _targetgroupname = "none" + local _targetskill = "Random" + if tgtgrp and tgtgrp:IsAlive() then + _targetgroup = tgtgrp + _targetgroupname = tgtgrp:GetName() -- group name + _targetskill = tgtgrp:GetUnit(1):GetSkill() + self:T("*** Found Target = ".. _targetgroupname) + self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT) + end + end + end end - - -- look at a couple of zones across the trajectory - for n=1,3 do - local dist = Ropt - ((n-1)*20000) - local predpos= pos0:Translate(dist,wph) - if predpos then + return self + end - local targetzone = ZONE_RADIUS:New("Target Zone",predpos:GetVec2(),20000) - - if self.debug then - predpos:MarkToAll(string.format("height=%dm | heading=%d | velocity=%ddeg | Ropt=%dm",mheight,wph,mveloc,Ropt),false) - targetzone:DrawZone(coalition.side.BLUE,{0,0,1},0.2,nil,nil,3,true) - end - - local seadset = self.Groupset - local tgtcoord = targetzone:GetRandomPointVec2() - local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord) - local _targetgroup = nil - local _targetgroupname = "none" - local _targetskill = "Random" - if tgtgrp and tgtgrp:IsAlive() then - _targetgroup = tgtgrp - _targetgroupname = tgtgrp:GetName() -- group name - _targetskill = tgtgrp:GetUnit(1):GetSkill() - self:T("*** Found Target = ".. _targetgroupname) - self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT) + --- (Internal) Shoot and Scoot + -- @param #SHORAD self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Wrapper.Group#GROUP Shorad Shorad group + -- @return #SHORAD self + function SHORAD:onafterShootAndScoot(From,Event,To,Shorad) + self:T( { From,Event,To } ) + local possibleZones = {} + local mindist = 100 + local maxdist = 3000 + if Shorad and Shorad:IsAlive() then + local NowCoord = Shorad:GetCoordinate() + for _,_zone in pairs(self.SkateZones.Set) do + local zone = _zone -- Core.Zone#ZONE_RADIUS + local dist = NowCoord:Get2DDistance(zone:GetCoordinate()) + if dist >= mindist and dist <= maxdist then + possibleZones[#possibleZones+1] = zone + if #possibleZones == self.SkateNumber then break end end end - end + if #possibleZones > 0 and Shorad:GetVelocityKMH() < 2 then + local rand = math.floor(math.random(1,#possibleZones*1000)/1000+0.5) + if rand == 0 then rand = 1 end + self:T(self.lid .. " ShootAndScoot to zone "..rand) + local ToCoordinate = possibleZones[rand]:GetCoordinate() + Shorad:RouteGroundTo(ToCoordinate,20,"Cone",1) + end + end + return self end - return self -end --- Main function - work on the EventData -- @param #SHORAD self -- @param Core.Event#EVENTDATA EventData The event details table data set + -- @return #SHORAD self function SHORAD:HandleEventShot( EventData ) self:T( { EventData } ) self:T(self.lid .. " HandleEventShot") - --local ShootingUnit = EventData.IniDCSUnit - --local ShootingUnitName = EventData.IniDCSUnitName local ShootingWeapon = EventData.Weapon -- Identify the weapon fired local ShootingWeaponName = EventData.WeaponName -- return weapon type -- get firing coalition @@ -603,20 +680,11 @@ end if targetcat == Object.Category.UNIT then -- UNIT targetunit = UNIT:Find(targetdata) elseif targetcat == Object.Category.STATIC then -- STATIC - --self:T("Static Target Data") - --self:T({targetdata:isExist()}) - --self:T({targetdata:getPoint()}) local tgtcoord = COORDINATE:NewFromVec3(targetdata:getPoint()) - --tgtcoord:MarkToAll("Missile Target",true) - - local tgtgrp1 = self.Samset:FindNearestGroupFromPointVec2(tgtcoord) + local tgtgrp1 = self.Samset:FindNearestGroupFromPointVec2(tgtcoord) local tgtcoord1 = tgtgrp1:GetCoordinate() - --tgtcoord1:MarkToAll("Close target SAM",true) - local tgtgrp2 = self.Groupset:FindNearestGroupFromPointVec2(tgtcoord) local tgtcoord2 = tgtgrp2:GetCoordinate() - --tgtcoord2:MarkToAll("Close target SHORAD",true) - local dist1 = tgtcoord:Get2DDistance(tgtcoord1) local dist2 = tgtcoord:Get2DDistance(tgtcoord2) @@ -628,10 +696,8 @@ end targetcat = Object.Category.UNIT end end - --local targetunitname = Unit.getName(targetdata) -- Unit name if targetunit and targetunit:IsAlive() then local targetunitname = targetunit:GetName() - --local targetgroup = Unit.getGroup(Weapon.getTarget(ShootingWeapon)) --targeted group local targetgroup = nil local targetgroupname = "none" if targetcat == Object.Category.UNIT then @@ -649,7 +715,6 @@ end self:T( text ) local m = MESSAGE:New(text,10,"Info"):ToAllIf(self.debug) -- check if we or a SAM site are the target - --local TargetGroup = EventData.TgtGroup -- Wrapper.Group#GROUP local shotatus = self:_CheckShotAtShorad(targetgroupname) --#boolean local shotatsams = self:_CheckShotAtSams(targetgroupname) --#boolean -- if being shot at, find closest SHORADs to activate From 5f734a0d17508bae39521692929ec874f65276a6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 15 Nov 2023 18:16:46 +0100 Subject: [PATCH 2/2] #MESSAGE * Fixes for ToSRS() for MS Desktop --- Moose Development/Moose/Core/Message.lua | 53 +++++++++++++++++------- Moose Development/Moose/Sound/SRS.lua | 12 +++--- 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 39fb11060..0feeecd2b 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -465,7 +465,7 @@ _MESSAGESRS = {} -- @param #string PathToCredentials (optional) Path to credentials file for e.g. 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 "male". +-- @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 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. @@ -480,24 +480,42 @@ _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,Modulation,Volume) - _MESSAGESRS.MSRS:SetCoalition(Coalition) + _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.MSRS:SetCoalition(Coalition or coalition.side.NEUTRAL) + _MESSAGESRS.coalition = Coalition or coalition.side.NEUTRAL + + _MESSAGESRS.coordinate = Coordinate _MESSAGESRS.MSRS:SetCoordinate(Coordinate) + _MESSAGESRS.MSRS:SetCulture(Culture) - _MESSAGESRS.Culture = Culture - --_MESSAGESRS.MSRS:SetFrequencies(Frequency) + _MESSAGESRS.Culture = Culture or "en-GB" + _MESSAGESRS.MSRS:SetGender(Gender) - _MESSAGESRS.Gender = Gender + _MESSAGESRS.Gender = Gender or "female" + _MESSAGESRS.MSRS:SetGoogle(PathToCredentials) + _MESSAGESRS.google = PathToCredentials + _MESSAGESRS.MSRS:SetLabel(Label or "MESSAGE") - --_MESSAGESRS.MSRS:SetModulations(Modulation) - --_MESSAGESRS.MSRS:SetPath(PathToSRS) - _MESSAGESRS.MSRS:SetPort(Port) - -- _MESSAGESRS.MSRS:SetVolume(Volume) - _MESSAGESRS.MSRS:SetVoice(Voice) - _MESSAGESRS.Voice = Voice + _MESSAGESRS.label = Label or "MESSAGE" + + _MESSAGESRS.MSRS:SetPort(Port or 5002) + _MESSAGESRS.port = Port or 5002 + + _MESSAGESRS.volume = Volume or 1 + _MESSAGESRS.MSRS:SetVolume(_MESSAGESRS.volume) + + if Voice then _MESSAGESRS.MSRS:SetVoice(Voice) end + + _MESSAGESRS.voice = Voice --or MSRS.Voices.Microsoft.Hedda + --if _MESSAGESRS.google and not Voice then _MESSAGESRS.Voice = MSRS.Voices.Google.Standard.en_GB_Standard_A end + --_MESSAGESRS.MSRS:SetVoice(Voice or _MESSAGESRS.voice) + _MESSAGESRS.SRSQ = MSRSQUEUE:New(Label or "MESSAGE") - env.info(_MESSAGESRS.MSRS.provider,false) end --- Sends a message via SRS. @@ -505,7 +523,7 @@ end -- @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. -- @param #string gender (optional) Gender, i.e. "male" or "female". Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. --- @param #string culture (optional) Culture, e.g. "en-US. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #string culture (optional) Culture, e.g. "en-US". Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. -- @param #string voice (optional) Voice. Will override gender and culture settings. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. -- @param #number coalition (optional) Coalition, can be coalition.side.RED, coalition.side.BLUE or coalition.side.NEUTRAL. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. -- @param #number volume (optional) Volume, can be between 0.0 and 1.0 (loudest). Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. @@ -519,13 +537,16 @@ end -- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS() -- function MESSAGE:ToSRS(frequency,modulation,gender,culture,voice,coalition,volume,coordinate) + local tgender = gender or _MESSAGESRS.Gender if _MESSAGESRS.SRSQ then - _MESSAGESRS.MSRS:SetVoice(voice or _MESSAGESRS.Voice) + if voice then + _MESSAGESRS.MSRS:SetVoice(voice or _MESSAGESRS.voice) + end if coordinate then _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,modulation,gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,voice or _MESSAGESRS.Voice,volume,category,coordinate) + _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) end return self end diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 57e3fddcc..856595d0b 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -1119,7 +1119,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp end -- Debug output. - self:T("MSRS command="..command) + self:I("MSRS command="..command) return command end @@ -1243,7 +1243,7 @@ function MSRS:LoadConfigFile(Path,Filename,ConfigLoaded) self.gender = MSRS_Config.Gender or "male" self.google = MSRS_Config.Google self.Label = MSRS_Config.Label or "MSRS" - self.voice = MSRS_Config.Voice or MSRS.Voices.Microsoft.Hazel + self.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel if MSRS_Config.GRPC then self.provider = MSRS_Config.GRPC.DefaultProvider if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then @@ -1267,7 +1267,7 @@ function MSRS:LoadConfigFile(Path,Filename,ConfigLoaded) MSRS.gender = MSRS_Config.Gender or "male" MSRS.google = MSRS_Config.Google MSRS.Label = MSRS_Config.Label or "MSRS" - MSRS.voice = MSRS_Config.Voice or MSRS.Voices.Microsoft.Hazel + MSRS.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel if MSRS_Config.GRPC then MSRS.provider = MSRS_Config.GRPC.DefaultProvider if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then @@ -1695,7 +1695,7 @@ end -- @param #MSRSQUEUE self -- @return #MSRSQUEUE self The MSRSQUEUE object. function MSRSQUEUE:Clear() - self:I(self.lid.."Clearning MSRSQUEUE") + self:I(self.lid.."Clearing MSRSQUEUE") self.queue={} return self end @@ -1796,7 +1796,7 @@ function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgr transmission.gender = gender transmission.culture = culture transmission.voice = voice - transmission.gender = volume + transmission.volume = volume transmission.label = label transmission.coordinate = coordinate @@ -1810,7 +1810,7 @@ end -- @param #MSRSQUEUE self -- @param #MSRSQUEUE.Transmission transmission The transmission. function MSRSQUEUE:Broadcast(transmission) - + 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