Merge branch 'develop' into FF/OpsDev

This commit is contained in:
Frank
2025-04-09 22:43:24 +02:00
144 changed files with 13755 additions and 6814 deletions

View File

@@ -580,6 +580,18 @@ ENUMS.Link16Power = {
--- Enums for the STORAGE class for stores - which need to be in ""
-- @type ENUMS.Storage
-- @type ENUMS.Storage.weapons
-- @type ENUMS.Storage.weapons.missiles
-- @type ENUMS.Storage.weapons.bombs
-- @type ENUMS.Storage.weapons.nurs
-- @type ENUMS.Storage.weapons.containers
-- @type ENUMS.Storage.weapons.droptanks
-- @type ENUMS.Storage.weapons.adapters
-- @type ENUMS.Storage.weapons.torpedoes
-- @type ENUMS.Storage.weapons.Gazelle
-- @type ENUMS.Storage.weapons.CH47
-- @type ENUMS.Storage.weapons.OH58
-- @type ENUMS.Storage.weapons.UH1H
-- @type ENUMS.Storage.weapons.AH64D
ENUMS.Storage = {
weapons = {
missiles = {}, -- Missiles
@@ -589,6 +601,11 @@ ENUMS.Storage = {
droptanks = {}, -- Droptanks
adapters = {}, -- Adapter
torpedoes = {}, -- Torpedoes
Gazelle = {}, -- Gazelle specifics
CH47 = {}, -- Chinook specifics
OH58 = {}, -- Kiowa specifics
UH1H = {}, -- Huey specifics
AH64D = {}, -- Huey specifics
}
}
@@ -1148,4 +1165,197 @@ ENUMS.Storage.weapons.bombs.BDU_50LD = "weapons.bombs.BDU_50LD"
ENUMS.Storage.weapons.bombs.AGM_62 = "weapons.bombs.AGM_62"
ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_WHITE = "weapons.containers.{US_M10_SMOKE_TANK_WHITE}"
ENUMS.Storage.weapons.missiles.MICA_T = "weapons.missiles.MICA_T"
ENUMS.Storage.weapons.containers.HVAR_rocket = "weapons.containers.HVAR_rocket"
ENUMS.Storage.weapons.containers.HVAR_rocket = "weapons.containers.HVAR_rocket"
-- 2025
ENUMS.Storage.weapons.containers.LANTIRN = "weapons.containers.LANTIRN"
ENUMS.Storage.weapons.missiles.AGM_78B = "weapons.missiles.AGM_78B"
ENUMS.Storage.weapons.containers.uh_60l_pilot = "weapons.containers.uh-60l_pilot"
ENUMS.Storage.weapons.missiles.AIM_92E = "weapons.missiles.AIM-92E"
ENUMS.Storage.weapons.missiles.KD_63B = "weapons.missiles.KD_63B"
ENUMS.Storage.weapons.bombs.Type_200A = "weapons.bombs.Type_200A"
ENUMS.Storage.weapons.missiles.HB_AIM_7E_2 = "weapons.missiles.HB-AIM-7E-2"
ENUMS.Storage.weapons.containers.Spear = "weapons.containers.Spear"
ENUMS.Storage.weapons.missiles.LS_6 = "weapons.missiles.LS_6"
ENUMS.Storage.weapons.containers.HB_ALE_40_0_120 = "weapons.containers.HB_ALE_40_0_120"
ENUMS.Storage.weapons.containers.Fantasm = "weapons.containers.Fantasm"
ENUMS.Storage.weapons.nurs.FFAR_Mk61 = "weapons.nurs.FFAR_Mk61"
ENUMS.Storage.weapons.bombs.HB_F4E_GBU15V1 = "weapons.bombs.HB_F4E_GBU15V1"
ENUMS.Storage.weapons.containers.HB_F14_EXT_AN_APQ_167 = "weapons.containers.HB_F14_EXT_AN_APQ-167"
ENUMS.Storage.weapons.nurs.LWL_RP = "weapons.nurs.LWL_RP"
ENUMS.Storage.weapons.bombs.AGM_62_I = "weapons.bombs.AGM_62_I"
ENUMS.Storage.weapons.containers.ETHER = "weapons.containers.ETHER"
ENUMS.Storage.weapons.containers.TANGAZH = "weapons.containers.TANGAZH"
ENUMS.Storage.weapons.bombs.LYSBOMB_11086 = "weapons.bombs.LYSBOMB 11086"
ENUMS.Storage.weapons.containers.Stub_Wing = "weapons.containers.Stub_Wing"
ENUMS.Storage.weapons.missiles.AIM_9E = "weapons.missiles.AIM-9E"
ENUMS.Storage.weapons.missiles.C_701T = "weapons.missiles.C_701T"
ENUMS.Storage.weapons.bombs.BAP_100 = "weapons.bombs.BAP_100"
ENUMS.Storage.weapons.missiles.CM_802AKG = "weapons.missiles.CM-802AKG"
ENUMS.Storage.weapons.missiles.CM_400AKG = "weapons.missiles.CM-400AKG"
ENUMS.Storage.weapons.missiles.C_802AK = "weapons.missiles.C_802AK"
ENUMS.Storage.weapons.missiles.KD_63 = "weapons.missiles.KD_63"
ENUMS.Storage.weapons.containers.HB_ORD_Pave_Spike_Fast = "weapons.containers.HB_ORD_Pave_Spike_Fast"
ENUMS.Storage.weapons.missiles.SPIKE_ER2 = "weapons.missiles.SPIKE_ER2"
ENUMS.Storage.weapons.containers.KINGAL = "weapons.containers.KINGAL"
ENUMS.Storage.weapons.containers.LANTIRN_F14_TARGET = "weapons.containers.LANTIRN-F14-TARGET"
ENUMS.Storage.weapons.containers.SPS_141 = "weapons.containers.SPS-141"
ENUMS.Storage.weapons.bombs.BLU_3B_GROUP = "weapons.bombs.BLU-3B_GROUP"
ENUMS.Storage.weapons.containers.HB_ALE_40_30_0 = "weapons.containers.HB_ALE_40_30_0"
ENUMS.Storage.weapons.droptanks.HB_HIGH_PERFORMANCE_CENTERLINE_600_GAL = "weapons.droptanks.HB_HIGH_PERFORMANCE_CENTERLINE_600_GAL"
ENUMS.Storage.weapons.containers.ALQ_184 = "weapons.containers.ALQ-184"
ENUMS.Storage.weapons.missiles.AGM_45B = "weapons.missiles.AGM_45B"
ENUMS.Storage.weapons.bombs.BLU_3_GROUP = "weapons.bombs.BLU-3_GROUP"
ENUMS.Storage.weapons.missiles.SPIKE_ER = "weapons.missiles.SPIKE_ER"
ENUMS.Storage.weapons.nurs.ARAKM70BAPPX = "weapons.nurs.ARAKM70BAPPX"
ENUMS.Storage.weapons.bombs.LYSBOMB_11088 = "weapons.bombs.LYSBOMB 11088"
ENUMS.Storage.weapons.bombs.LYSBOMB_11087 = "weapons.bombs.LYSBOMB 11087"
ENUMS.Storage.weapons.missiles.KD_20 = "weapons.missiles.KD_20"
ENUMS.Storage.weapons.droptanks.HB_F_4E_EXT_WingTank = "weapons.droptanks.HB_F-4E_EXT_WingTank"
ENUMS.Storage.weapons.missiles.Rb_04 = "weapons.missiles.Rb_04"
ENUMS.Storage.weapons.containers.AAQ_33 = "weapons.containers.AAQ-33"
ENUMS.Storage.weapons.droptanks.HB_F_4E_EXT_Center_Fuel_Tank_EMPTY = "weapons.droptanks.HB_F-4E_EXT_Center_Fuel_Tank_EMPTY"
ENUMS.Storage.weapons.droptanks.HB_F_4E_EXT_WingTank_R_EMPTY = "weapons.droptanks.HB_F-4E_EXT_WingTank_R_EMPTY"
ENUMS.Storage.weapons.droptanks.HB_F_4E_EXT_WingTank_EMPTY = "weapons.droptanks.HB_F-4E_EXT_WingTank_EMPTY"
ENUMS.Storage.weapons.containers.uh_60l_copilot = "weapons.containers.uh-60l_copilot"
ENUMS.Storage.weapons.droptanks.JAYHAWK_80gal_Fuel_Tankv2 = "weapons.droptanks.JAYHAWK_80gal_Fuel_Tankv2"
ENUMS.Storage.weapons.containers.supply_m134 = "weapons.containers.supply_m134"
ENUMS.Storage.weapons.containers.Seahawk_Pylon = "weapons.containers.Seahawk_Pylon"
ENUMS.Storage.weapons.nurs.LWL_MPP = "weapons.nurs.LWL_MPP"
ENUMS.Storage.weapons.nurs.S_5KP = "weapons.nurs.S_5KP"
ENUMS.Storage.weapons.missiles.AIM_92J = "weapons.missiles.AIM-92J"
ENUMS.Storage.weapons.missiles.HB_AIM_7E = "weapons.missiles.HB-AIM-7E"
ENUMS.Storage.weapons.containers.ALQ_131 = "weapons.containers.ALQ-131"
ENUMS.Storage.weapons.containers.HB_F14_EXT_TARPS = "weapons.containers.HB_F14_EXT_TARPS"
ENUMS.Storage.weapons.containers.MH60_SOAR = "weapons.containers.MH60_SOAR"
ENUMS.Storage.weapons.missiles.YJ_83 = "weapons.missiles.YJ-83"
ENUMS.Storage.weapons.bombs.GBU_8_B = "weapons.bombs.GBU_8_B"
ENUMS.Storage.weapons.containers.HB_F14_EXT_ECA = "weapons.containers.HB_F14_EXT_ECA"
ENUMS.Storage.weapons.bombs.BAP_100 = "weapons.bombs.BAP-100"
ENUMS.Storage.weapons.nurs.M261_MPSM_Rocket = "weapons.nurs.M261_MPSM_Rocket"
ENUMS.Storage.weapons.droptanks.SEAHAWK_120_Fuel_Tank = "weapons.droptanks.SEAHAWK_120_Fuel_Tank"
ENUMS.Storage.weapons.containers.SHPIL = "weapons.containers.SHPIL"
ENUMS.Storage.weapons.bombs.GBU_39 = "weapons.bombs.GBU_39"
ENUMS.Storage.weapons.nurs.S_5M = "weapons.nurs.S_5M"
ENUMS.Storage.weapons.containers.HB_ALE_40_15_90 = "weapons.containers.HB_ALE_40_15_90"
ENUMS.Storage.weapons.missiles.AIM_7E = "weapons.missiles.AIM-7E"
ENUMS.Storage.weapons.missiles.AIM_9P3 = "weapons.missiles.AIM-9P3"
ENUMS.Storage.weapons.missiles.AGM_12B = "weapons.missiles.AGM_12B"
ENUMS.Storage.weapons.missiles.CM_802AKG = "weapons.missiles.CM_802AKG"
ENUMS.Storage.weapons.droptanks.JAYHAWK_120_Fuel_Dual_Tank = "weapons.droptanks.JAYHAWK_120_Fuel_Dual_Tank"
ENUMS.Storage.weapons.droptanks.HB_F_4E_EXT_Center_Fuel_Tank = "weapons.droptanks.HB_F-4E_EXT_Center_Fuel_Tank"
ENUMS.Storage.weapons.containers.PAVETACK = "weapons.containers.PAVETACK"
ENUMS.Storage.weapons.missiles.LS_6_500 = "weapons.missiles.LS_6_500"
ENUMS.Storage.weapons.bombs.LYSBOMB_11089 = "weapons.bombs.LYSBOMB 11089"
ENUMS.Storage.weapons.bombs.BLU_4B_GROUP = "weapons.bombs.BLU-4B_GROUP"
ENUMS.Storage.weapons.containers.ah_64d_radar = "weapons.containers.ah-64d_radar"
ENUMS.Storage.weapons.containers.F_18_LDT_POD = "weapons.containers.F-18-LDT-POD"
ENUMS.Storage.weapons.containers.HB_ALE_40_30_60 = "weapons.containers.HB_ALE_40_30_60"
ENUMS.Storage.weapons.bombs.LS_6_100 = "weapons.bombs.LS_6_100"
ENUMS.Storage.weapons.droptanks.HB_F_4E_EXT_WingTank_R = "weapons.droptanks.HB_F-4E_EXT_WingTank_R"
ENUMS.Storage.weapons.containers.SORBCIJA_R = "weapons.containers.SORBCIJA_R"
ENUMS.Storage.weapons.missiles.CATM_65K = "weapons.missiles.CATM_65K"
ENUMS.Storage.weapons.containers.HB_ORD_Pave_Spike = "weapons.containers.HB_ORD_Pave_Spike"
ENUMS.Storage.weapons.containers.RobbieTank1 = "weapons.containers.RobbieTank1"
ENUMS.Storage.weapons.containers.SKY_SHADOW = "weapons.containers.SKY_SHADOW"
ENUMS.Storage.weapons.containers.SORBCIJA_L = "weapons.containers.SORBCIJA_L"
ENUMS.Storage.weapons.containers.Pavehawk = "weapons.containers.Pavehawk"
ENUMS.Storage.weapons.bombs.BLG66_EG = "weapons.bombs.BLG66_EG"
ENUMS.Storage.weapons.missiles.AGM_12C_ED = "weapons.missiles.AGM_12C_ED"
ENUMS.Storage.weapons.missiles.AIM_92C = "weapons.missiles.AIM-92C"
ENUMS.Storage.weapons.containers.MPS_410 = "weapons.containers.MPS-410"
ENUMS.Storage.weapons.missiles.HJ_12 = "weapons.missiles.HJ-12"
ENUMS.Storage.weapons.containers.AAQ_28_LITENING = "weapons.containers.AAQ-28_LITENING"
ENUMS.Storage.weapons.containers.F_18_FLIR_POD = "weapons.containers.F-18-FLIR-POD"
ENUMS.Storage.weapons.bombs.BLU_3B_GROUP = "weapons.bombs.BLU_3B_GROUP"
ENUMS.Storage.weapons.containers.UH60L_Jayhawk = "weapons.containers.UH60L_Jayhawk"
ENUMS.Storage.weapons.containers.BOZ_100 = "weapons.containers.BOZ-100"
ENUMS.Storage.weapons.missiles.AGM_78A = "weapons.missiles.AGM_78A"
ENUMS.Storage.weapons.missiles.LAU_61_APKWS_M282 = "weapons.missiles.LAU_61_APKWS_M282"
ENUMS.Storage.weapons.bombs.BAP_100 = "weapons.bombs.BAP-100"
ENUMS.Storage.weapons.missiles.CM_802AKG = "weapons.missiles.CM-802AKG"
ENUMS.Storage.weapons.bombs.BLU_3B_GROUP = "weapons.bombs.BLU_3B_GROUP"
ENUMS.Storage.weapons.bombs.BLU_4B_GROUP = "weapons.bombs.BLU-4B_GROUP"
ENUMS.Storage.weapons.nurs.S_5M = "weapons.nurs.S_5M"
ENUMS.Storage.weapons.missiles.AGM_12A = "weapons.missiles.AGM_12A"
ENUMS.Storage.weapons.droptanks.JAYHAWK_120_Fuel_Tank = "weapons.droptanks.JAYHAWK_120_Fuel_Tank"
ENUMS.Storage.weapons.bombs.GBU_15_V_1_B = "weapons.bombs.GBU_15_V_1_B"
ENUMS.Storage.weapons.missiles.HYDRA_70_M151_APKWS = {4,4,8,292}
ENUMS.Storage.weapons.missiles.HYDRA_70_M282_APKWS = {4,4,8,293}
-- dupes with typos
ENUMS.Storage.weapons.bombs.BAP100 = "weapons.bombs.BAP_100"
ENUMS.Storage.weapons.bombs.BLU3B_GROUP = "weapons.bombs.BLU-3B_GROUP"
ENUMS.Storage.weapons.missiles.CM_802AKG = "weapons.missiles.CM_802AKG"
ENUMS.Storage.weapons.bombs.BLU_4B_GROUP = "weapons.bombs.BLU_4B_GROUP"
ENUMS.Storage.weapons.nurs.S5M = "weapons.nurs.S-5M"
-- Gazelle
ENUMS.Storage.weapons.Gazelle.HMP400_100RDS = {4,15,46,1771}
ENUMS.Storage.weapons.Gazelle.HMP400_200RDS = {4,15,46,1770}
ENUMS.Storage.weapons.Gazelle.HMP400_400RDS = {4,15,46,1769}
ENUMS.Storage.weapons.Gazelle.GIAT_M261_AP = {4,15,46,1768}
ENUMS.Storage.weapons.Gazelle.GIAT_M261_SAPHEI = {4,15,46,1767}
ENUMS.Storage.weapons.Gazelle.GIAT_M261_HE = {4,15,46,1766}
ENUMS.Storage.weapons.Gazelle.GIAT_M261_HEAP = {4,15,46,1765}
ENUMS.Storage.weapons.Gazelle.GIAT_M261_APHE = {4,15,46,1764}
ENUMS.Storage.weapons.Gazelle.GAZELLE_IR_DEFLECTOR = {4,15,47,680}
ENUMS.Storage.weapons.Gazelle.GAZELLE_FAS_SANDFILTER = {4,15,47,679}
-- Chinook (changed)
ENUMS.Storage.weapons.CH47.CH47_PORT_M60D = {4,15,46,2489}
ENUMS.Storage.weapons.CH47.CH47_STBD_M60D = {4,15,46,2488}
ENUMS.Storage.weapons.CH47.CH47_AFT_M60D = {4,15,46,2490}
ENUMS.Storage.weapons.CH47.CH47_PORT_M134D = {4,15,46,2494}
ENUMS.Storage.weapons.CH47.CH47_STBD_M134D = {4,15,46,2495}
ENUMS.Storage.weapons.CH47.CH47_AFT_M3M = {4,15,46,2496} --
ENUMS.Storage.weapons.CH47.CH47_PORT_M240H = {4,15,46,2492}
ENUMS.Storage.weapons.CH47.CH47_STBD_M240H = {4,15,46,2491}
ENUMS.Storage.weapons.CH47.CH47_AFT_M240H = {4,15,46,2493}
-- Huey
ENUMS.Storage.weapons.UH1H.M134_MiniGun_Right = {4,15,46,161}
ENUMS.Storage.weapons.UH1H.M134_MiniGun_Left = {4,15,46,160}
ENUMS.Storage.weapons.UH1H.M134_MiniGun_Right_Door = {4,15,46,175}
ENUMS.Storage.weapons.UH1H.M60_MG_Right_Door = {4,15,46,177}
ENUMS.Storage.weapons.UH1H.M134_MiniGun_Left_Door = {4,15,46,174}
ENUMS.Storage.weapons.UH1H.M60_MG_Left_Door = {4,15,46,176}
-- Kiowa
ENUMS.Storage.weapons.OH58.FIM92 = {4,4,7,449}
ENUMS.Storage.weapons.OH58.MG_M3P100 = {4,15,46,2611}
ENUMS.Storage.weapons.OH58.MG_M3P200 = {4,15,46,2610}
ENUMS.Storage.weapons.OH58.MG_M3P300 = {4,15,46,2609}
ENUMS.Storage.weapons.OH58.MG_M3P400 = {4,15,46,2608}
ENUMS.Storage.weapons.OH58.MG_M3P500 = {4,15,46,2607}
ENUMS.Storage.weapons.OH58.Smk_Grenade_Blue = {4,5,9,488}
ENUMS.Storage.weapons.OH58.Smk_Grenade_Green = {4,5,9,489}
ENUMS.Storage.weapons.OH58.Smk_Grenade_Red = {4,5,9,487}
ENUMS.Storage.weapons.OH58.Smk_Grenade_Violet = {4,5,9,490}
ENUMS.Storage.weapons.OH58.Smk_Grenade_White = {4,5,9,492}
ENUMS.Storage.weapons.OH58.Smk_Grenade_Yellow = {4,5,9,491}
-- Apache
ENUMS.Storage.weapons.AH64D.AN_APG78 = {4,15,44,2114}
ENUMS.Storage.weapons.AH64D.Internal_Aux_FuelTank = {1,3,43,1700}
---
-- @type ENUMS.FARPType
-- @field #string FARP
-- @field #string INVISIBLE
-- @field #string HELIPADSINGLE
-- @field #string PADSINGLE
ENUMS.FARPType = {
FARP = "FARP",
INVISIBLE = "INVISIBLE",
HELIPADSINGLE = "HELIPADSINGLE",
PADSINGLE = "PADSINGLE",
}
---
-- @type ENUMS.FARPObjectTypeNamesAndShape
-- @field #string FARP
-- @field #string INVISIBLE
-- @field #string HELIPADSINGLE
-- @field #string PADSINGLE
ENUMS.FARPObjectTypeNamesAndShape ={
[ENUMS.FARPType.FARP] = { TypeName="FARP", ShapeName="FARPS"},
[ENUMS.FARPType.INVISIBLE] = { TypeName="Invisible FARP", ShapeName="invisiblefarp"},
[ENUMS.FARPType.HELIPADSINGLE] = { TypeName="SINGLE_HELIPAD", ShapeName="FARP"},
[ENUMS.FARPType.PADSINGLE] = { TypeName="FARP_SINGLE_01", ShapeName="FARP_SINGLE_01"},
}

View File

@@ -1,259 +0,0 @@
--- **Utilities** - DCS Simple Text-To-Speech (STTS).
--
--
-- @module Utilities.STTS
-- @image MOOSE.JPG
--- [DCS Enum world](https://wiki.hoggitworld.com/view/DCS_enum_world)
-- @type STTS
-- @field #string DIRECTORY Path of the SRS directory.
--- Simple Text-To-Speech
--
-- Version 0.4 - Compatible with SRS version 1.9.6.0+
--
-- # DCS Modification Required
--
-- You will need to edit MissionScripting.lua in DCS World/Scripts/MissionScripting.lua and remove the sanitization.
-- To do this remove all the code below the comment - the line starts "local function sanitizeModule(name)"
-- Do this without DCS running to allow mission scripts to use os functions.
--
-- *You WILL HAVE TO REAPPLY AFTER EVERY DCS UPDATE*
--
-- # USAGE:
--
-- Add this script into the mission as a DO SCRIPT or DO SCRIPT FROM FILE to initialize it
-- Make sure to edit the STTS.SRS_PORT and STTS.DIRECTORY to the correct values before adding to the mission.
-- Then its as simple as calling the correct function in LUA as a DO SCRIPT or in your own scripts.
--
-- Example calls:
--
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2)
--
-- Arguments in order are:
--
-- * Message to say, make sure not to use a newline (\n) !
-- * Frequency in MHz
-- * Modulation - AM/FM
-- * Volume - 1.0 max, 0.5 half
-- * Name of the transmitter - ATC, RockFM etc
-- * Coalition - 0 spectator, 1 red 2 blue
-- * OPTIONAL - Vec3 Point i.e Unit.getByName("A UNIT"):getPoint() - needs Vec3 for Height! OR null if not needed
-- * OPTIONAL - Speed -10 to +10
-- * OPTIONAL - Gender male, female or neuter
-- * OPTIONAL - Culture - en-US, en-GB etc
-- * OPTIONAL - Voice - a specific voice by name. Run DCS-SR-ExternalAudio.exe with --help to get the ones you can use on the command line
-- * OPTIONAL - Google TTS - Switch to Google Text To Speech - Requires STTS.GOOGLE_CREDENTIALS path and Google project setup correctly
--
--
-- ## Example
--
-- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only
--
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,null,-5,"male","en-GB")
--
-- ## Example
--
-- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only centered on the position of the Unit called "A UNIT"
--
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,Unit.getByName("A UNIT"):getPoint(),-5,"male","en-GB")
--
-- Arguments in order are:
--
-- * FULL path to the MP3 OR OGG to play
-- * Frequency in MHz - to use multiple separate with a comma - Number of frequencies MUST match number of Modulations
-- * Modulation - AM/FM - to use multiple
-- * Volume - 1.0 max, 0.5 half
-- * Name of the transmitter - ATC, RockFM etc
-- * Coalition - 0 spectator, 1 red 2 blue
--
-- ## Example
--
-- This will play that MP3 on 255MHz AM & 31 FM at half volume with a client called "Multiple" and to Spectators only
--
-- STTS.PlayMP3("C:\\Users\\Ciaran\\Downloads\\PR-Music.mp3","255,31","AM,FM","0.5","Multiple",0)
--
-- @field #STTS
STTS = {
ClassName = "STTS",
DIRECTORY = "",
SRS_PORT = 5002,
GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json",
EXECUTABLE = "DCS-SR-ExternalAudio.exe"
}
--- FULL Path to the FOLDER containing DCS-SR-ExternalAudio.exe - EDIT TO CORRECT FOLDER
STTS.DIRECTORY = "D:/DCS/_SRS"
--- LOCAL SRS PORT - DEFAULT IS 5002
STTS.SRS_PORT = 5002
--- Google credentials file
STTS.GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json"
--- DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING
STTS.EXECUTABLE = "DCS-SR-ExternalAudio.exe"
--- Function for UUID.
function STTS.uuid()
local random = math.random
local template = 'yxxx-xxxxxxxxxxxx'
return string.gsub( template, '[xy]', function( c )
local v = (c == 'x') and random( 0, 0xf ) or random( 8, 0xb )
return string.format( '%x', v )
end )
end
--- Round a number.
-- @param #number x Number.
-- @param #number n Precision.
function STTS.round( x, n )
n = math.pow( 10, n or 0 )
x = x * n
if x >= 0 then
x = math.floor( x + 0.5 )
else
x = math.ceil( x - 0.5 )
end
return x / n
end
--- Function returns estimated speech time in seconds.
-- Assumptions for time calc: 100 Words per min, average of 5 letters for english word so
--
-- * 5 chars * 100wpm = 500 characters per min = 8.3 chars per second
--
-- So length of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec map function:
--
-- * (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
--
-- @param #number length can also be passed as #string
-- @param #number speed Defaults to 1.0
-- @param #boolean isGoogle We're using Google TTS
function STTS.getSpeechTime(length,speed,isGoogle)
local maxRateRatio = 3
speed = speed or 1.0
isGoogle = isGoogle or false
local speedFactor = 1.0
if isGoogle then
speedFactor = speed
else
if speed ~= 0 then
speedFactor = math.abs( speed ) * (maxRateRatio - 1) / 10 + 1
end
if speed < 0 then
speedFactor = 1 / speedFactor
end
end
local wpm = math.ceil( 100 * speedFactor )
local cps = math.floor( (wpm * 5) / 60 )
if type( length ) == "string" then
length = string.len( length )
end
return length/cps --math.ceil(length/cps)
end
--- Text to speech function.
function STTS.TextToSpeech( message, freqs, modulations, volume, name, coalition, point, speed, gender, culture, voice, googleTTS )
if os == nil or io == nil then
env.info( "[DCS-STTS] LUA modules os or io are sanitized. skipping. " )
return
end
speed = speed or 1
gender = gender or "female"
culture = culture or ""
voice = voice or ""
coalition = coalition or "0"
name = name or "ROBOT"
volume = 1
speed = 1
message = message:gsub( "\"", "\\\"" )
local cmd = string.format( "start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", STTS.DIRECTORY, STTS.EXECUTABLE, freqs or "305", modulations or "AM", coalition, STTS.SRS_PORT, name )
if voice ~= "" then
cmd = cmd .. string.format( " -V \"%s\"", voice )
else
if culture ~= "" then
cmd = cmd .. string.format( " -l %s", culture )
end
if gender ~= "" then
cmd = cmd .. string.format( " -g %s", gender )
end
end
if googleTTS == true then
cmd = cmd .. string.format( " -G \"%s\"", STTS.GOOGLE_CREDENTIALS )
end
if speed ~= 1 then
cmd = cmd .. string.format( " -s %s", speed )
end
if volume ~= 1.0 then
cmd = cmd .. string.format( " -v %s", volume )
end
if point and type( point ) == "table" and point.x then
local lat, lon, alt = coord.LOtoLL( point )
lat = STTS.round( lat, 4 )
lon = STTS.round( lon, 4 )
alt = math.floor( alt )
cmd = cmd .. string.format( " -L %s -O %s -A %s", lat, lon, alt )
end
cmd = cmd .. string.format( " -t \"%s\"", message )
if string.len( cmd ) > 255 then
local filename = os.getenv( 'TMP' ) .. "\\DCS_STTS-" .. STTS.uuid() .. ".bat"
local script = io.open( filename, "w+" )
script:write( cmd .. " && exit" )
script:close()
cmd = string.format( "\"%s\"", filename )
timer.scheduleFunction( os.remove, filename, timer.getTime() + 1 )
end
if string.len( cmd ) > 255 then
env.info( "[DCS-STTS] - cmd string too long" )
env.info( "[DCS-STTS] TextToSpeech Command :\n" .. cmd .. "\n" )
end
os.execute( cmd )
return STTS.getSpeechTime( message, speed, googleTTS )
end
--- Play mp3 function.
-- @param #string pathToMP3 Path to the sound file.
-- @param #string freqs Frequencies, e.g. "305, 256".
-- @param #string modulations Modulations, e.g. "AM, FM".
-- @param #string volume Volume, e.g. "0.5".
function STTS.PlayMP3( pathToMP3, freqs, modulations, volume, name, coalition, point )
local cmd = string.format( "start \"\" /d \"%s\" /b /min \"%s\" -i \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -v %s -h", STTS.DIRECTORY, STTS.EXECUTABLE, pathToMP3, freqs or "305", modulations or "AM", coalition or "0", STTS.SRS_PORT, name or "ROBOT", volume or "1" )
if point and type( point ) == "table" and point.x then
local lat, lon, alt = coord.LOtoLL( point )
lat = STTS.round( lat, 4 )
lon = STTS.round( lon, 4 )
alt = math.floor( alt )
cmd = cmd .. string.format( " -L %s -O %s -A %s", lat, lon, alt )
end
env.info( "[DCS-STTS] MP3/OGG Command :\n" .. cmd .. "\n" )
os.execute( cmd )
end

View File

@@ -1,612 +0,0 @@
--- **Utilities** - Templates.
--
-- DCS unit templates
--
-- @module Utilities.Templates
-- @image MOOSE.JPG
--- TEMPLATE class.
-- @type TEMPLATE
-- @field #string ClassName Name of the class.
--- *Templates*
--
-- ===
--
-- ![Banner Image](..\Presentations\Utilities\PROFILER_Main.jpg)
--
-- Get DCS templates from thin air.
--
-- # Ground Units
--
-- Ground units.
--
-- # Naval Units
--
-- Ships are not implemented yet.
--
-- # Aircraft
--
-- ## Airplanes
--
-- Airplanes are not implemented yet.
--
-- ## Helicopters
--
-- Helicopters are not implemented yet.
--
-- @field #TEMPLATE
TEMPLATE = {
ClassName = "TEMPLATE",
Ground = {},
Naval = {},
Airplane = {},
Helicopter = {},
}
--- Ground unit type names.
-- @type TEMPLATE.TypeGround
-- @param #string InfantryAK
TEMPLATE.TypeGround={
InfantryAK="Infantry AK",
ParatrooperAKS74="Paratrooper AKS-74",
ParatrooperRPG16="Paratrooper RPG-16",
SoldierWWIIUS="soldier_wwii_us",
InfantryM248="Infantry M249",
SoldierM4="Soldier M4",
}
--- Naval unit type names.
-- @type TEMPLATE.TypeNaval
-- @param #string Ticonderoga
TEMPLATE.TypeNaval={
Ticonderoga="TICONDEROG",
}
--- Rotary wing unit type names.
-- @type TEMPLATE.TypeAirplane
-- @param #string A10C
TEMPLATE.TypeAirplane={
A10C="A-10C",
}
--- Rotary wing unit type names.
-- @type TEMPLATE.TypeHelicopter
-- @param #string AH1W
TEMPLATE.TypeHelicopter={
AH1W="AH-1W",
}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Ground Template
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get template for ground units.
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
-- @param #string GroupName Name of the spawned group. **Must be unique!**
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
-- @param #number Nunits Number of units. Default 1.
-- @param #number Radius Spawn radius for additonal units in meters. Default 50 m.
-- @return #table Template Template table.
function TEMPLATE.GetGround(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
-- Defaults.
TypeName=TypeName or TEMPLATE.TypeGround.SoldierM4
GroupName=GroupName or "Ground-1"
CountryID=CountryID or country.id.USA
Vec3=Vec3 or {x=0, y=0, z=0}
Nunits=Nunits or 1
Radius=Radius or 50
-- Get generic template.
local template=UTILS.DeepCopy(TEMPLATE.GenericGround)
-- Set group name.
template.name=GroupName
-- These are additional entries required by the MOOSE _DATABASE:Spawn() function.
template.CountryID=CountryID
template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
template.CategoryID=Unit.Category.GROUND_UNIT
-- Set first unit.
template.units[1].type=TypeName
template.units[1].name=GroupName.."-1"
if Vec3 then
TEMPLATE.SetPositionFromVec3(template, Vec3)
end
TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius)
return template
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Naval Template
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get template for ground units.
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
-- @param #string GroupName Name of the spawned group. **Must be unique!**
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
-- @param #number Nunits Number of units. Default 1.
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
-- @return #table Template Template table.
function TEMPLATE.GetNaval(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
-- Defaults.
TypeName=TypeName or TEMPLATE.TypeNaval.Ticonderoga
GroupName=GroupName or "Naval-1"
CountryID=CountryID or country.id.USA
Vec3=Vec3 or {x=0, y=0, z=0}
Nunits=Nunits or 1
Radius=Radius or 500
-- Get generic template.
local template=UTILS.DeepCopy(TEMPLATE.GenericNaval)
-- Set group name.
template.name=GroupName
-- These are additional entries required by the MOOSE _DATABASE:Spawn() function.
template.CountryID=CountryID
template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
template.CategoryID=Unit.Category.SHIP
-- Set first unit.
template.units[1].type=TypeName
template.units[1].name=GroupName.."-1"
if Vec3 then
TEMPLATE.SetPositionFromVec3(template, Vec3)
end
TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius)
return template
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Aircraft Template
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get template for fixed wing units.
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
-- @param #string GroupName Name of the spawned group. **Must be unique!**
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
-- @param #number Nunits Number of units. Default 1.
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
-- @return #table Template Template table.
function TEMPLATE.GetAirplane(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
-- Defaults.
TypeName=TypeName or TEMPLATE.TypeAirplane.A10C
GroupName=GroupName or "Airplane-1"
CountryID=CountryID or country.id.USA
Vec3=Vec3 or {x=0, y=1000, z=0}
Nunits=Nunits or 1
Radius=Radius or 100
local template=TEMPLATE._GetAircraft(true, TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
return template
end
--- Get template for fixed wing units.
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
-- @param #string GroupName Name of the spawned group. **Must be unique!**
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
-- @param #number Nunits Number of units. Default 1.
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
-- @return #table Template Template table.
function TEMPLATE.GetHelicopter(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
-- Defaults.
TypeName=TypeName or TEMPLATE.TypeHelicopter.AH1W
GroupName=GroupName or "Helicopter-1"
CountryID=CountryID or country.id.USA
Vec3=Vec3 or {x=0, y=500, z=0}
Nunits=Nunits or 1
Radius=Radius or 100
-- Limit unis to 4.
Nunits=math.min(Nunits, 4)
local template=TEMPLATE._GetAircraft(false, TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
return template
end
--- Get template for aircraft units.
-- @param #boolean Airplane If true, this is a fixed wing. Else, rotary wing.
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
-- @param #string GroupName Name of the spawned group. **Must be unique!**
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
-- @param #number Nunits Number of units. Default 1.
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
-- @return #table Template Template table.
function TEMPLATE._GetAircraft(Airplane, TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
-- Defaults.
TypeName=TypeName
GroupName=GroupName or "Aircraft-1"
CountryID=CountryID or country.id.USA
Vec3=Vec3 or {x=0, y=0, z=0}
Nunits=Nunits or 1
Radius=Radius or 100
-- Get generic template.
local template=UTILS.DeepCopy(TEMPLATE.GenericAircraft)
-- Set group name.
template.name=GroupName
-- These are additional entries required by the MOOSE _DATABASE:Spawn() function.
template.CountryID=CountryID
template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
if Airplane then
template.CategoryID=Unit.Category.AIRPLANE
else
template.CategoryID=Unit.Category.HELICOPTER
end
-- Set first unit.
template.units[1].type=TypeName
template.units[1].name=GroupName.."-1"
-- Set position.
if Vec3 then
TEMPLATE.SetPositionFromVec3(template, Vec3)
end
-- Set number of units.
TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius)
return template
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Misc Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set the position of the template.
-- @param #table Template The template to be modified.
-- @param DCS#Vec2 Vec2 2D Position vector with x and y components of the group.
function TEMPLATE.SetPositionFromVec2(Template, Vec2)
Template.x=Vec2.x
Template.y=Vec2.y
for _,unit in pairs(Template.units) do
unit.x=Vec2.x
unit.y=Vec2.y
end
Template.route.points[1].x=Vec2.x
Template.route.points[1].y=Vec2.y
Template.route.points[1].alt=0 --TODO: Use land height.
end
--- Set the position of the template.
-- @param #table Template The template to be modified.
-- @param DCS#Vec3 Vec3 Position vector of the group.
function TEMPLATE.SetPositionFromVec3(Template, Vec3)
local Vec2={x=Vec3.x, y=Vec3.z}
TEMPLATE.SetPositionFromVec2(Template, Vec2)
end
--- Set the position of the template.
-- @param #table Template The template to be modified.
-- @param #number N Total number of units in the group.
-- @param Core.Point#COORDINATE Coordinate Position of the first unit.
-- @param #number Radius Radius in meters to randomly place the additional units.
function TEMPLATE.SetUnits(Template, N, Coordinate, Radius)
local units=Template.units
local unit1=units[1]
local Vec3=Coordinate:GetVec3()
unit1.x=Vec3.x
unit1.y=Vec3.z
unit1.alt=Vec3.y
for i=2,N do
units[i]=UTILS.DeepCopy(unit1)
end
for i=1,N do
local unit=units[i]
unit.name=string.format("%s-%d", Template.name, i)
if i>1 then
local vec2=Coordinate:GetRandomCoordinateInRadius(Radius, 5):GetVec2()
unit.x=vec2.x
unit.y=vec2.y
unit.alt=unit1.alt
end
end
end
--- Set the position of the template.
-- @param #table Template The template to be modified.
-- @param Wrapper.Airbase#AIRBASE AirBase The airbase where the aircraft are spawned.
-- @param #table ParkingSpots List of parking spot IDs. Every unit needs one!
-- @param #boolean EngineOn If true, aircraft are spawned hot.
function TEMPLATE.SetAirbase(Template, AirBase, ParkingSpots, EngineOn)
-- Airbase ID.
local AirbaseID=AirBase:GetID()
-- Spawn point.
local point=Template.route.points[1]
-- Set ID.
if AirBase:IsAirdrome() then
point.airdromeId=AirbaseID
else
point.helipadId=AirbaseID
point.linkUnit=AirbaseID
end
if EngineOn then
point.action=COORDINATE.WaypointAction.FromParkingAreaHot
point.type=COORDINATE.WaypointType.TakeOffParkingHot
else
point.action=COORDINATE.WaypointAction.FromParkingArea
point.type=COORDINATE.WaypointType.TakeOffParking
end
for i,unit in ipairs(Template.units) do
unit.parking_id=ParkingSpots[i]
end
end
--- Add a waypoint.
-- @param #table Template The template to be modified.
-- @param #table Waypoint Waypoint table.
function TEMPLATE.AddWaypoint(Template, Waypoint)
table.insert(Template.route.points, Waypoint)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Generic Ground Template
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
TEMPLATE.GenericGround=
{
["visible"] = false,
["tasks"] = {}, -- end of ["tasks"]
["uncontrollable"] = false,
["task"] = "Ground Nothing",
["route"] =
{
["spans"] = {}, -- end of ["spans"]
["points"] =
{
[1] =
{
["alt"] = 0,
["type"] = "Turning Point",
["ETA"] = 0,
["alt_type"] = "BARO",
["formation_template"] = "",
["y"] = 0,
["x"] = 0,
["ETA_locked"] = true,
["speed"] = 0,
["action"] = "Off Road",
["task"] =
{
["id"] = "ComboTask",
["params"] =
{
["tasks"] =
{
}, -- end of ["tasks"]
}, -- end of ["params"]
}, -- end of ["task"]
["speed_locked"] = true,
}, -- end of [1]
}, -- end of ["points"]
}, -- end of ["route"]
["groupId"] = nil,
["hidden"] = false,
["units"] =
{
[1] =
{
["transportable"] =
{
["randomTransportable"] = false,
}, -- end of ["transportable"]
["skill"] = "Average",
["type"] = "Infantry AK",
["unitId"] = nil,
["y"] = 0,
["x"] = 0,
["name"] = "Infantry AK-47 Rus",
["heading"] = 0,
["playerCanDrive"] = false,
}, -- end of [1]
}, -- end of ["units"]
["y"] = 0,
["x"] = 0,
["name"] = "Infantry AK-47 Rus",
["start_time"] = 0,
}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Generic Ship Template
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
TEMPLATE.GenericNaval=
{
["visible"] = false,
["tasks"] = {}, -- end of ["tasks"]
["uncontrollable"] = false,
["route"] =
{
["points"] =
{
[1] =
{
["alt"] = 0,
["type"] = "Turning Point",
["ETA"] = 0,
["alt_type"] = "BARO",
["formation_template"] = "",
["y"] = 0,
["x"] = 0,
["ETA_locked"] = true,
["speed"] = 0,
["action"] = "Turning Point",
["task"] =
{
["id"] = "ComboTask",
["params"] =
{
["tasks"] =
{
}, -- end of ["tasks"]
}, -- end of ["params"]
}, -- end of ["task"]
["speed_locked"] = true,
}, -- end of [1]
}, -- end of ["points"]
}, -- end of ["route"]
["groupId"] = nil,
["hidden"] = false,
["units"] =
{
[1] =
{
["transportable"] =
{
["randomTransportable"] = false,
}, -- end of ["transportable"]
["skill"] = "Average",
["type"] = "TICONDEROG",
["unitId"] = nil,
["y"] = 0,
["x"] = 0,
["name"] = "Naval-1-1",
["heading"] = 0,
["modulation"] = 0,
["frequency"] = 127500000,
}, -- end of [1]
}, -- end of ["units"]
["y"] = 0,
["x"] = 0,
["name"] = "Naval-1",
["start_time"] = 0,
}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Generic Aircraft Template
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
TEMPLATE.GenericAircraft=
{
["groupId"] = nil,
["name"] = "Rotary-1",
["uncontrolled"] = false,
["hidden"] = false,
["task"] = "Nothing",
["y"] = 0,
["x"] = 0,
["start_time"] = 0,
["communication"] = true,
["radioSet"] = false,
["frequency"] = 127.5,
["modulation"] = 0,
["taskSelected"] = true,
["tasks"] = {}, -- end of ["tasks"]
["route"] =
{
["points"] =
{
[1] =
{
["y"] = 0,
["x"] = 0,
["alt"] = 1000,
["alt_type"] = "BARO",
["action"] = "Turning Point",
["type"] = "Turning Point",
["airdromeId"] = nil,
["task"] =
{
["id"] = "ComboTask",
["params"] =
{
["tasks"] = {}, -- end of ["tasks"]
}, -- end of ["params"]
}, -- end of ["task"]
["ETA"] = 0,
["ETA_locked"] = true,
["speed"] = 100,
["speed_locked"] = true,
["formation_template"] = "",
}, -- end of [1]
}, -- end of ["points"]
}, -- end of ["route"]
["units"] =
{
[1] =
{
["name"] = "Rotary-1-1",
["unitId"] = nil,
["type"] = "AH-1W",
["onboard_num"] = "050",
["livery_id"] = "USA X Black",
["skill"] = "High",
["ropeLength"] = 15,
["speed"] = 0,
["x"] = 0,
["y"] = 0,
["alt"] = 10,
["alt_type"] = "BARO",
["heading"] = 0,
["psi"] = 0,
["parking"] = nil,
["parking_id"] = nil,
["payload"] =
{
["pylons"] = {}, -- end of ["pylons"]
["fuel"] = "1250.0",
["flare"] = 30,
["chaff"] = 30,
["gun"] = 100,
}, -- end of ["payload"]
["callsign"] =
{
[1] = 2,
[2] = 1,
[3] = 1,
["name"] = "Springfield11",
}, -- end of ["callsign"]
}, -- end of [1]
}, -- end of ["units"]
}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -56,6 +56,8 @@ BIGSMOKEPRESET = {
-- @field #string Falklands South Atlantic map.
-- @field #string Sinai Sinai map.
-- @field #string Kola Kola map.
-- @field #string Afghanistan Afghanistan map
-- @field #string Iraq Iraq map
DCSMAP = {
Caucasus="Caucasus",
NTTR="Nevada",
@@ -66,7 +68,9 @@ DCSMAP = {
MarianaIslands="MarianaIslands",
Falklands="Falklands",
Sinai="SinaiMap",
Kola="Kola"
Kola="Kola",
Afghanistan="Afghanistan",
Iraq="Iraq"
}
@@ -482,6 +486,15 @@ UTILS.BasicSerialize = function(s)
end
end
--- Counts the number of elements in a table.
-- @param #table T Table to count
-- @return #int Number of elements in the table
function UTILS.TableLength(T)
local count = 0
for _ in pairs(T or {}) do count = count + 1 end
return count
end
--- Print a table to log in a nice format
-- @param #table table The table to print
-- @param #number indent Number of indents
@@ -496,12 +509,12 @@ function UTILS.PrintTableToLog(table, indent, noprint)
if not indent then indent = 0 end
for k, v in pairs(table) do
if string.find(k," ") then k='"'..k..'"'end
if type(v) == "table" then
if type(v) == "table" and UTILS.TableLength(v) > 0 then
if not noprint then
env.info(string.rep(" ", indent) .. tostring(k) .. " = {")
end
text = text ..string.rep(" ", indent) .. tostring(k) .. " = {\n"
text = text .. tostring(UTILS.PrintTableToLog(v, indent + 1)).."\n"
text = text .. tostring(UTILS.PrintTableToLog(v, indent + 1), noprint).."\n"
if not noprint then
env.info(string.rep(" ", indent) .. "},")
end
@@ -1238,7 +1251,7 @@ function UTILS.SecondsToClock(seconds, short)
end
-- Seconds
local seconds = tonumber(seconds)
local seconds = tonumber(seconds) or 0
-- Seconds of this day.
local _seconds=seconds%(60*60*24)
@@ -1819,7 +1832,8 @@ end
-- * Mariana Islands +2 (East)
-- * Falklands +12 (East) - note there's a LOT of deviation across the map, as we're closer to the South Pole
-- * Sinai +4.8 (East)
-- * Kola +15 (East) - not there is a lot of deviation across the map (-1° to +24°), as we are close to the North pole
-- * Kola +15 (East) - note there is a lot of deviation across the map (-1° to +24°), as we are close to the North pole
-- * Afghanistan +3 (East) - actually +3.6 (NW) to +2.3 (SE)
-- @param #string map (Optional) Map for which the declination is returned. Default is from env.mission.theatre
-- @return #number Declination in degrees.
function UTILS.GetMagneticDeclination(map)
@@ -1848,6 +1862,10 @@ function UTILS.GetMagneticDeclination(map)
declination=4.8
elseif map==DCSMAP.Kola then
declination=15
elseif map==DCSMAP.Afghanistan then
declination=3
elseif map==DCSMAP.Iraq then
declination=4.4
else
declination=0
end
@@ -2079,6 +2097,8 @@ function UTILS.GMTToLocalTimeDifference()
return 2 -- Currently map is +2 but should be +3 (DCS bug?)
elseif theatre==DCSMAP.Kola then
return 3 -- Currently map is +2 but should be +3 (DCS bug?)
elseif theatre==DCSMAP.Afghanistan then
return 4.5 -- UTC +4:30
else
BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0", tostring(theatre)))
return 0
@@ -2182,9 +2202,9 @@ function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal)
local cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude))
if rising and cosH > 1 then
return "N/R" -- The sun never rises on this location on the specified date
return "N/S" -- The sun never rises on this location on the specified date
elseif cosH < -1 then
return "N/S" -- The sun never sets on this location on the specified date
return "N/R" -- The sun never sets on this location on the specified date
end
-- Finish calculating H and convert into hours
@@ -2375,12 +2395,21 @@ function UTILS.IsLoadingDoorOpen( unit_name )
return true
end
if type_name == " OH-58D" and (unit:getDrawArgumentValue(35) > 0 or unit:getDrawArgumentValue(421) == -1) then
BASE:T(unit_name .. " cargo door is open")
return true
if type_name == "OH58D" then
BASE:T(unit_name .. " front door(s) are open")
return true -- no doors on this one ;)
end
return false
if type_name == "CH-47Fbl1" and (unit:getDrawArgumentValue(86) > 0.5) then
BASE:T(unit_name .. " rear cargo door is open")
return true
end
-- ground
local UnitDescriptor = unit:getDesc()
local IsGroundResult = (UnitDescriptor.category == Unit.Category.GROUND_UNIT)
return IsGroundResult
end -- nil
@@ -2405,17 +2434,19 @@ end
--- Function to generate valid VHF frequencies in kHz for radio beacons (FM).
-- @return #table VHFrequencies
function UTILS.GenerateVHFrequencies()
-- known and sorted map-wise NDBs in kHz
local _skipFrequencies = {
214,274,291.5,295,297.5,
300.5,304,305,307,309.5,311,312,312.5,316,
320,324,328,329,330,332,336,337,
342,343,348,351,352,353,358,
363,365,368,372.5,374,
380,381,384,385,389,395,396,
414,420,430,432,435,440,450,455,462,470,485,
507,515,520,525,528,540,550,560,570,577,580,
214,243,264,273,274,288,291.5,295,297.5,
300.5,304,305,307,309.5,310,311,312,312.5,316,317,
320,323,324,325,326,328,329,330,332,335,336,337,
340,342,343,346,348,351,352,353,358,
360,363,364,365,368,372.5,373,374,
380,381,384,385,387,389,391,395,396,399,
403,404,410,412,414,418,420,423,
430,432,435,440,445,
450,455,462,470,485,490,
507,515,520,525,528,540,550,560,563,570,577,580,595,
602,625,641,662,670,680,682,690,
705,720,722,730,735,740,745,750,770,795,
822,830,862,866,
@@ -2576,7 +2607,7 @@ end
--- Function to save an object to a file
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
-- @param #string Filename The name of the file. Existing file will be overwritten.
-- @param #table Data The LUA data structure to save. This will be e.g. a table of text lines with an \\n at the end of each line.
-- @param #string Data The data structure to save. This will be e.g. a string of text lines with an \\n at the end of each line.
-- @return #boolean outcome True if saving is possible, else false.
function UTILS.SaveToFile(Path,Filename,Data)
-- Thanks to @FunkyFranky
@@ -4180,3 +4211,326 @@ function UTILS.ReadCSV(filename)
return csvdata
end
--- Seed the LCG random number generator.
-- @param #number seed Seed value. Default is a random number using math.random()
function UTILS.LCGRandomSeed(seed)
UTILS.lcg = {
seed = seed or math.random(1, 2^32 - 1),
a = 1664525,
c = 1013904223,
m = 2^32
}
end
--- Return a pseudo-random number using the LCG algorithm.
-- @return #number Random number between 0 and 1.
function UTILS.LCGRandom()
if UTILS.lcg == nil then
UTILS.LCGRandomSeed()
end
UTILS.lcg.seed = (UTILS.lcg.a * UTILS.lcg.seed + UTILS.lcg.c) % UTILS.lcg.m
return UTILS.lcg.seed / UTILS.lcg.m
end
--- Spawns a new FARP of a defined type and coalition and functional statics (fuel depot, ammo storage, tent, windsock) around that FARP to make it operational.
-- Adds vehicles from template if given. Fills the FARP warehouse with liquids and known materiels.
-- References: [DCS Forum Topic](https://forum.dcs.world/topic/282989-farp-equipment-to-run-it)
-- @param #string Name Name of this FARP installation. Must be unique.
-- @param Core.Point#COORDINATE Coordinate Where to spawn the FARP.
-- @param #string FARPType Type of FARP, can be one of the known types ENUMS.FARPType.FARP, ENUMS.FARPType.INVISIBLE, ENUMS.FARPType.HELIPADSINGLE, ENUMS.FARPType.PADSINGLE. Defaults to ENUMS.FARPType.FARP.
-- @param #number Coalition Coalition of this FARP, i.e. coalition.side.BLUE or coalition.side.RED, defaults to coalition.side.BLUE.
-- @param #number Country Country of this FARP, defaults to country.id.USA (blue) or country.id.RUSSIA (red).
-- @param #number CallSign Callsign of the FARP ATC, defaults to CALLSIGN.FARP.Berlin.
-- @param #number Frequency Frequency of the FARP ATC Radio, defaults to 127.5 (MHz).
-- @param #number Modulation Modulation of the FARP ATC Radio, defaults to radio.modulation.AM.
-- @param #number ADF ADF Beacon (FM) Frequency in KHz, e.g. 428. If not nil, creates an VHF/FM ADF Beacon for this FARP. Requires a sound called "beacon.ogg" to be in the mission (trigger "sound to" ...)
-- @param #number SpawnRadius Radius of the FARP, i.e. where the FARP objects will be placed in meters, not more than 150m away. Defaults to 100.
-- @param #string VehicleTemplate, template name for additional vehicles. Can be nil for no additional vehicles.
-- @param #number Liquids Tons of fuel to be added initially to the FARP. Defaults to 10 (tons). Set to 0 for no fill.
-- @param #number Equipment Number of equipment items per known item to be added initially to the FARP. Defaults to 10 (items). Set to 0 for no fill.
-- @return #list<Wrapper.Static#STATIC> Table of spawned objects and vehicle object (if given).
-- @return #string ADFBeaconName Name of the ADF beacon, to be able to remove/stop it later.
function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,Country,CallSign,Frequency,Modulation,ADF,SpawnRadius,VehicleTemplate,Liquids,Equipment)
-- Set Defaults
local farplocation = Coordinate
local farptype = FARPType or ENUMS.FARPType.FARP
local Coalition = Coalition or coalition.side.BLUE
local callsign = CallSign or CALLSIGN.FARP.Berlin
local freq = Frequency or 127.5
local mod = Modulation or radio.modulation.AM
local radius = SpawnRadius or 100
if radius < 0 or radius > 150 then radius = 100 end
local liquids = Liquids or 10
liquids = liquids * 1000 -- tons to kg
local equip = Equipment or 10
local statictypes = ENUMS.FARPObjectTypeNamesAndShape[farptype] or {TypeName="FARP", ShapeName="FARPS"}
local STypeName = statictypes.TypeName
local SShapeName = statictypes.ShapeName
local Country = Country or (Coalition == coalition.side.BLUE and country.id.USA or country.id.RUSSIA)
local ReturnObjects = {}
-- Spawn FARP
local newfarp = SPAWNSTATIC:NewFromType(STypeName,"Heliports",Country) -- "Invisible FARP" "FARP"
newfarp:InitShape(SShapeName) -- "invisiblefarp" "FARPS"
newfarp:InitFARP(callsign,freq,mod)
local spawnedfarp = newfarp:SpawnFromCoordinate(farplocation,0,Name)
table.insert(ReturnObjects,spawnedfarp)
-- Spawn Objects
local FARPStaticObjectsNato = {
["FUEL"] = { TypeName = "FARP Fuel Depot", ShapeName = "GSM Rus", Category = "Fortifications"},
["AMMO"] = { TypeName = "FARP Ammo Dump Coating", ShapeName = "SetkaKP", Category = "Fortifications"},
["TENT"] = { TypeName = "FARP Tent", ShapeName = "PalatkaB", Category = "Fortifications"},
["WINDSOCK"] = { TypeName = "Windsock", ShapeName = "H-Windsock_RW", Category = "Fortifications"},
}
local farpobcount = 0
for _name,_object in pairs(FARPStaticObjectsNato) do
local objloc = farplocation:Translate(radius,farpobcount*30)
local heading = objloc:HeadingTo(farplocation)
local newobject = SPAWNSTATIC:NewFromType(_object.TypeName,_object.Category,Country)
newobject:InitShape(_object.ShapeName)
newobject:InitHeading(heading)
newobject:SpawnFromCoordinate(objloc,farpobcount*30,_name.." - "..Name)
table.insert(ReturnObjects,newobject)
farpobcount = farpobcount + 1
end
-- Vehicle if any
if VehicleTemplate and type(VehicleTemplate) == "string" then
local vcoordinate = farplocation:Translate(radius,farpobcount*30)
local heading = vcoordinate:HeadingTo(farplocation)
local vehicles = SPAWN:NewWithAlias(VehicleTemplate,"FARP Vehicles - "..Name)
vehicles:InitGroupHeading(heading)
vehicles:InitCountry(Country)
vehicles:InitCoalition(Coalition)
vehicles:InitDelayOff()
local spawnedvehicle = vehicles:SpawnFromCoordinate(vcoordinate)
table.insert(ReturnObjects,spawnedvehicle)
end
local newWH = STORAGE:New(Name)
if liquids and liquids > 0 then
-- Storage fill-up
newWH:SetLiquid(STORAGE.Liquid.DIESEL,liquids) -- kgs to tons
newWH:SetLiquid(STORAGE.Liquid.GASOLINE,liquids)
newWH:SetLiquid(STORAGE.Liquid.JETFUEL,liquids)
newWH:SetLiquid(STORAGE.Liquid.MW50,liquids)
end
if equip and equip > 0 then
for cat,nitem in pairs(ENUMS.Storage.weapons) do
for name,item in pairs(nitem) do
newWH:SetItem(item,equip)
end
end
end
local ADFName
if ADF and type(ADF) == "number" then
local ADFFreq = ADF*1000 -- KHz to Hz
local Sound = "l10n/DEFAULT/beacon.ogg"
local vec3 = farplocation:GetVec3()
ADFName = Name .. " ADF "..tostring(ADF).."KHz"
--BASE:I(string.format("Adding FARP Beacon %d KHz Name %s",ADF,ADFName))
trigger.action.radioTransmission(Sound, vec3, 0, true, ADFFreq, 250, ADFName)
end
return ReturnObjects, ADFName
end
--- Converts a Vec2 to a Vec3.
-- @param vec the 2D vector
-- @param y optional new y axis (altitude) value. If omitted it's 0.
function UTILS.Vec2toVec3(vec,y)
if not vec.z then
if vec.alt and not y then
y = vec.alt
elseif not y then
y = 0
end
return {x = vec.x, y = y, z = vec.y}
else
return {x = vec.x, y = vec.y, z = vec.z} -- it was already Vec3, actually.
end
end
--- Get the correction needed for true north in radians
-- @param gPoint The map point vec2 or vec3
-- @return number correction
function UTILS.GetNorthCorrection(gPoint)
local point = UTILS.DeepCopy(gPoint)
if not point.z then --Vec2; convert to Vec3
point.z = point.y
point.y = 0
end
local lat, lon = coord.LOtoLL(point)
local north_posit = coord.LLtoLO(lat + 1, lon)
return math.atan2(north_posit.z - point.z, north_posit.x - point.x)
end
--- Convert time in seconds to a DHMS table `{d = days, h = hours, m = minutes, s = seconds}`
-- @param timeInSec Time in Seconds
-- @return #table Table with DHMS data
function UTILS.GetDHMS(timeInSec)
if timeInSec and type(timeInSec) == 'number' then
local tbl = {d = 0, h = 0, m = 0, s = 0}
if timeInSec > 86400 then
while timeInSec > 86400 do
tbl.d = tbl.d + 1
timeInSec = timeInSec - 86400
end
end
if timeInSec > 3600 then
while timeInSec > 3600 do
tbl.h = tbl.h + 1
timeInSec = timeInSec - 3600
end
end
if timeInSec > 60 then
while timeInSec > 60 do
tbl.m = tbl.m + 1
timeInSec = timeInSec - 60
end
end
tbl.s = timeInSec
return tbl
else
BASE:E("No number handed!")
return
end
end
--- Returns heading-error corrected direction in radians.
-- True-north corrected direction from point along vector vec.
-- @param vec Vec3 Starting point
-- @param point Vec2 Direction
-- @return direction corrected direction from point.
function UTILS.GetDirectionRadians(vec, point)
local dir = math.atan2(vec.z, vec.x)
if point then
dir = dir + UTILS.GetNorthCorrection(point)
end
if dir < 0 then
dir = dir + 2 * math.pi -- put dir in range of 0 to 2*pi
end
return dir
end
--- Raycasting a point in polygon. Code from http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm
-- @param point Vec2 or Vec3 to test
-- @param #table poly Polygon Table of Vec2/3 point forming the Polygon
-- @param #number maxalt Altitude limit (optional)
-- @param #boolean outcome
function UTILS.IsPointInPolygon(point, poly, maxalt)
point = UTILS.Vec2toVec3(point)
local px = point.x
local pz = point.z
local cn = 0
local newpoly = UTILS.DeepCopy(poly)
if not maxalt or (point.y <= maxalt) then
local polysize = #newpoly
newpoly[#newpoly + 1] = newpoly[1]
newpoly[1] = UTILS.Vec2toVec3(newpoly[1])
for k = 1, polysize do
newpoly[k+1] = UTILS.Vec2toVec3(newpoly[k+1])
if ((newpoly[k].z <= pz) and (newpoly[k+1].z > pz)) or ((newpoly[k].z > pz) and (newpoly[k+1].z <= pz)) then
local vt = (pz - newpoly[k].z) / (newpoly[k+1].z - newpoly[k].z)
if (px < newpoly[k].x + vt*(newpoly[k+1].x - newpoly[k].x)) then
cn = cn + 1
end
end
end
return cn%2 == 1
else
return false
end
end
--- Vector scalar multiplication.
-- @param vec Vec3 vector to multiply
-- @param #number mult scalar multiplicator
-- @return Vec3 new vector multiplied with the given scalar
function UTILS.ScalarMult(vec, mult)
return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult}
end
--- Utilities weather class for fog mainly.
-- @type UTILS.Weather
UTILS.Weather = {}
--- Returns the current fog thickness in meters. Returns zero if fog is not present.
function UTILS.Weather.GetFogThickness()
return world.weather.getFogThickness()
end
--- Sets the fog to the desired thickness in meters at sea level.
-- @param #number Thickness Thickness in meters.
-- Any fog animation will be discarded.
-- Valid range : 100 to 5000 meters
function UTILS.Weather.SetFogThickness(Thickness)
local value = Thickness
if value < 100 then value = 100
elseif value > 5000 then value = 5000 end
return world.weather.setFogThickness(value)
end
--- Removes the fog.
function UTILS.Weather.RemoveFog()
return world.weather.setFogThickness(0)
end
--- Gets the maximum visibility distance of the current fog setting.
-- Returns 0 if no fog is present.
function UTILS.Weather.GetFogVisibilityDistanceMax()
return world.weather.getFogVisibilityDistance()
end
--- Sets the maximum visibility at sea level in meters.
-- @param #number Thickness Thickness in meters.
-- Limit: 100 to 100000
function UTILS.Weather.SetFogVisibilityDistance(Thickness)
local value = Thickness
if value < 100 then value = 100
elseif value > 100000 then value = 100000 end
return world.weather.setFogVisibilityDistance(value)
end
--- Uses data from the passed table to change the fog visibility and thickness over a desired timeframe. This allows for a gradual increase/decrease of fog values rather than abruptly applying the values.
-- Animation Key Format: {time, visibility, thickness}
-- @param #table AnimationKeys Table of AnimationKey tables
-- @usage
-- Time: in seconds 0 to infinity
-- Time is relative to when the function was called. Time value for each key must be larger than the previous key. If time is set to 0 then the fog will be applied to the corresponding visibility and thickness values at that key. Any time value greater than 0 will result in the current fog being inherited and changed to the first key.
-- Visibility: in meters 100 to 100000
-- Thickness: in meters 100 to 5000
-- The speed at which the visibility and thickness changes is based on the time between keys and the values that visibility and thickness are being set to.
--
-- When the function is passed an empty table {} or nil the fog animation will be discarded and whatever the current thickness and visibility are set to will remain.
--
-- The following will set the fog in the mission to disappear in 1 minute.
--
-- UTILS.Weather.SetFogAnimation({ {60, 0, 0} })
--
-- The following will take 1 hour to get to the first fog setting, it will maintain that fog setting for another hour, then lightly removes the fog over the 2nd and 3rd hour, the completely removes the fog after 3 hours and 3 minutes from when the function was called.
--
-- UTILS.Weather.SetFogAnimation({
-- {3600, 10000, 3000}, -- one hour to get to that fog setting
-- {7200, 10000, 3000}, -- will maintain for 2 hours
-- {10800, 20000, 2000}, -- at 3 hours visibility will have been increased while thickness decreases slightly
-- {12600, 0, 0}, -- at 3:30 after the function was called the fog will be completely removed.
-- })
--
function UTILS.Weather.SetFogAnimation(AnimationKeys)
return world.weather.setFogAnimation(AnimationKeys)
end
--- The fog animation will be discarded and whatever the current thickness and visibility are set to will remain
function UTILS.Weather.StopFogAnimation()
return world.weather.setFogAnimation({})
end