From 32deb160efecc330ce50dd57b3f0e32ff82899d7 Mon Sep 17 00:00:00 2001 From: TommyC81 Date: Sat, 4 Dec 2021 21:49:47 +0400 Subject: [PATCH] Formatting and typos (#1652) * Formatting and typo fixes. General formatting and typo fixes. * Update STTS.lua Keep class table on separate lines. --- Moose Development/Moose/Utilities/Enums.lua | 124 +- .../Moose/Utilities/Profiler.lua | 598 ++- .../Moose/Utilities/Routines.lua | 3397 ++++++++--------- Moose Development/Moose/Utilities/STTS.lua | 236 +- 4 files changed, 2095 insertions(+), 2260 deletions(-) diff --git a/Moose Development/Moose/Utilities/Enums.lua b/Moose Development/Moose/Utilities/Enums.lua index 757b67ea1..f4de6e4d7 100644 --- a/Moose Development/Moose/Utilities/Enums.lua +++ b/Moose Development/Moose/Utilities/Enums.lua @@ -1,18 +1,18 @@ --- **Utilities** Enumerators. --- +-- -- An enumerator is a variable that holds a constant value. Enumerators are very useful because they make the code easier to read and to change in general. --- +-- -- For example, instead of using the same value at multiple different places in your code, you should use a variable set to that value. -- If, for whatever reason, the value needs to be changed, you only have to change the variable once and do not have to search through you code and reset -- every value by hand. --- +-- -- Another big advantage is that the LDT intellisense "knows" the enumerators. So you can use the autocompletion feature and do not have to keep all the --- values in your head or look them up in the docs. --- +-- values in your head or look them up in the docs. +-- -- DCS itself provides a lot of enumerators for various things. See [Enumerators](https://wiki.hoggitworld.com/view/Category:Enumerators) on Hoggit. --- --- Other Moose classe also have enumerators. For example, the AIRBASE class has enumerators for airbase names. --- +-- +-- Other Moose classes also have enumerators. For example, the AIRBASE class has enumerators for airbase names. +-- -- @module ENUMS -- @image MOOSE.JPG @@ -20,7 +20,7 @@ -- @type ENUMS --- Because ENUMS are just better practice. --- +-- -- The ENUMS class adds some handy variables, which help you to make your code better and more general. -- -- @field #ENUMS @@ -30,16 +30,16 @@ ENUMS = {} -- @type ENUMS.ROE -- @field #number WeaponFree AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target. -- @field #number OpenFireWeaponFree AI will engage any enemy group it detects, but will prioritize targets specified in the groups tasking. --- @field #number OpenFire AI will engage only targets specified in its taskings. +-- @field #number OpenFire AI will engage only targets specified in its tasking. -- @field #number ReturnFire AI will only engage threats that shoot first. -- @field #number WeaponHold AI will hold fire under all circumstances. ENUMS.ROE = { - WeaponFree=0, - OpenFireWeaponFree=1, - OpenFire=2, - ReturnFire=3, - WeaponHold=4, - } + WeaponFree = 0, + OpenFireWeaponFree = 1, + OpenFire = 2, + ReturnFire = 3, + WeaponHold = 4, +} --- Reaction On Threat. -- @type ENUMS.ROT @@ -49,11 +49,11 @@ ENUMS.ROE = { -- @field #number BypassAndEscape AI will attempt to avoid enemy threat zones all together. This includes attempting to fly above or around threats. -- @field #number AllowAbortMission If a threat is deemed severe enough the AI will abort its mission and return to base. ENUMS.ROT = { - NoReaction=0, - PassiveDefense=1, - EvadeFire=2, - BypassAndEscape=3, - AllowAbortMission=4, + NoReaction = 0, + PassiveDefense = 1, + EvadeFire = 2, + BypassAndEscape = 3, + AllowAbortMission = 4, } --- Alarm state. @@ -62,12 +62,12 @@ ENUMS.ROT = { -- @field #number Green Group is not combat ready. Sensors are stowed if possible. -- @field #number Red Group is combat ready and actively searching for targets. Some groups like infantry will not move in this state. ENUMS.AlarmState = { - Auto=0, - Green=1, - Red=2, + Auto = 0, + Green = 1, + Red = 2, } ---- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerotor on hoggit wiki. +--- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerator on Hoggit wiki. -- @type ENUMS.WeaponFlag ENUMS.WeaponFlag={ -- Bombs @@ -111,7 +111,7 @@ ENUMS.WeaponFlag={ -- -- Bombs GuidedBomb = 14, -- (LGB + TvGB + SNSGB) - AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb) + AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispenser + CandleBomb + ParachuteBomb) AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb) --- Rockets AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket @@ -123,7 +123,7 @@ ENUMS.WeaponFlag={ --- Air-To-Air Missiles AnyAAM = 264241152, -- IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM AnyAutonomousMissile = 36012032, -- IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile - AnyMissile = 268402688, -- AnyASM + AnyAAM + AnyMissile = 268402688, -- AnyASM + AnyAAM --- Guns Cannons = 805306368, -- GUN_POD + BuiltInCannon --- @@ -133,7 +133,7 @@ ENUMS.WeaponFlag={ AnyAG = 2956984318, -- Any Air-To-Ground Weapon AnyAA = 264241152, -- Any Air-To-Air Weapon AnyUnguided = 2952822768, -- Any Unguided Weapon - AnyGuided = 268402702, -- Any Guided Weapon + AnyGuided = 268402702, -- Any Guided Weapon } --- Mission tasks. @@ -173,7 +173,7 @@ ENUMS.MissionTask={ TRANSPORT="Transport", } ---- Formations (new). See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki. +--- Formations (new). See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on Hoggit wiki. -- @type ENUMS.Formation ENUMS.Formation={} ENUMS.Formation.FixedWing={} @@ -216,23 +216,23 @@ ENUMS.Formation.FixedWing.FighterVic.Close = 917505 ENUMS.Formation.FixedWing.FighterVic.Open = 917506 ENUMS.Formation.RotaryWing={} ENUMS.Formation.RotaryWing.Column={} -ENUMS.Formation.RotaryWing.Column.D70=720896 +ENUMS.Formation.RotaryWing.Column.D70 = 720896 ENUMS.Formation.RotaryWing.Wedge={} -ENUMS.Formation.RotaryWing.Wedge.D70=8 +ENUMS.Formation.RotaryWing.Wedge.D70 = 8 ENUMS.Formation.RotaryWing.FrontRight={} -ENUMS.Formation.RotaryWing.FrontRight.D300=655361 -ENUMS.Formation.RotaryWing.FrontRight.D600=655362 +ENUMS.Formation.RotaryWing.FrontRight.D300 = 655361 +ENUMS.Formation.RotaryWing.FrontRight.D600 = 655362 ENUMS.Formation.RotaryWing.FrontLeft={} -ENUMS.Formation.RotaryWing.FrontLeft.D300=655617 -ENUMS.Formation.RotaryWing.FrontLeft.D600=655618 +ENUMS.Formation.RotaryWing.FrontLeft.D300 = 655617 +ENUMS.Formation.RotaryWing.FrontLeft.D600 = 655618 ENUMS.Formation.RotaryWing.EchelonRight={} -ENUMS.Formation.RotaryWing.EchelonRight.D70 =589825 -ENUMS.Formation.RotaryWing.EchelonRight.D300=589826 -ENUMS.Formation.RotaryWing.EchelonRight.D600=589827 +ENUMS.Formation.RotaryWing.EchelonRight.D70 = 589825 +ENUMS.Formation.RotaryWing.EchelonRight.D300 = 589826 +ENUMS.Formation.RotaryWing.EchelonRight.D600 = 589827 ENUMS.Formation.RotaryWing.EchelonLeft={} -ENUMS.Formation.RotaryWing.EchelonLeft.D70 =590081 -ENUMS.Formation.RotaryWing.EchelonLeft.D300=590082 -ENUMS.Formation.RotaryWing.EchelonLeft.D600=590083 +ENUMS.Formation.RotaryWing.EchelonLeft.D70 = 590081 +ENUMS.Formation.RotaryWing.EchelonLeft.D300 = 590082 +ENUMS.Formation.RotaryWing.EchelonLeft.D600 = 590083 ENUMS.Formation.Vehicle={} ENUMS.Formation.Vehicle.Vee="Vee" ENUMS.Formation.Vehicle.EchelonRight="EchelonR" @@ -244,34 +244,34 @@ ENUMS.Formation.Vehicle.Cone="Cone" ENUMS.Formation.Vehicle.Diamond="Diamond" --- Formations (old). The old format is a simplified version of the new formation enums, which allow more sophisticated settings. --- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki. +-- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on Hoggit wiki. -- @type ENUMS.FormationOld ENUMS.FormationOld={} ENUMS.FormationOld.FixedWing={} -ENUMS.FormationOld.FixedWing.LineAbreast=1 -ENUMS.FormationOld.FixedWing.Trail=2 -ENUMS.FormationOld.FixedWing.Wedge=3 -ENUMS.FormationOld.FixedWing.EchelonRight=4 -ENUMS.FormationOld.FixedWing.EchelonLeft=5 -ENUMS.FormationOld.FixedWing.FingerFour=6 -ENUMS.FormationOld.FixedWing.SpreadFour=7 -ENUMS.FormationOld.FixedWing.BomberElement=12 -ENUMS.FormationOld.FixedWing.BomberElementHeight=13 -ENUMS.FormationOld.FixedWing.FighterVic=14 +ENUMS.FormationOld.FixedWing.LineAbreast = 1 +ENUMS.FormationOld.FixedWing.Trail = 2 +ENUMS.FormationOld.FixedWing.Wedge = 3 +ENUMS.FormationOld.FixedWing.EchelonRight = 4 +ENUMS.FormationOld.FixedWing.EchelonLeft = 5 +ENUMS.FormationOld.FixedWing.FingerFour = 6 +ENUMS.FormationOld.FixedWing.SpreadFour = 7 +ENUMS.FormationOld.FixedWing.BomberElement = 12 +ENUMS.FormationOld.FixedWing.BomberElementHeight = 13 +ENUMS.FormationOld.FixedWing.FighterVic = 14 ENUMS.FormationOld.RotaryWing={} -ENUMS.FormationOld.RotaryWing.Wedge=8 -ENUMS.FormationOld.RotaryWing.Echelon=9 -ENUMS.FormationOld.RotaryWing.Front=10 -ENUMS.FormationOld.RotaryWing.Column=11 +ENUMS.FormationOld.RotaryWing.Wedge = 8 +ENUMS.FormationOld.RotaryWing.Echelon = 9 +ENUMS.FormationOld.RotaryWing.Front = 10 +ENUMS.FormationOld.RotaryWing.Column = 11 --- Morse Code. See the [Wikipedia](https://en.wikipedia.org/wiki/Morse_code). --- +-- -- * Short pulse "*" -- * Long pulse "-" --- +-- -- Pulses are separated by a blank character " ". --- +-- -- @type ENUMS.Morse ENUMS.Morse={} ENUMS.Morse.A="* -" @@ -313,9 +313,9 @@ ENUMS.Morse.N0="- - - - -" ENUMS.Morse[" "]=" " --- ISO (639-1) 2-letter Language Codes. See the [Wikipedia](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). --- +-- -- @type ENUMS.ISOLang -ENUMS.ISOLang = +ENUMS.ISOLang = { Arabic = 'AR', Chinese = 'ZH', @@ -329,7 +329,7 @@ ENUMS.ISOLang = } --- Phonetic Alphabet (NATO). See the [Wikipedia](https://en.wikipedia.org/wiki/NATO_phonetic_alphabet). --- +-- -- @type ENUMS.Phonetic ENUMS.Phonetic = { diff --git a/Moose Development/Moose/Utilities/Profiler.lua b/Moose Development/Moose/Utilities/Profiler.lua index a4bce95b5..7df211b0c 100644 --- a/Moose Development/Moose/Utilities/Profiler.lua +++ b/Moose Development/Moose/Utilities/Profiler.lua @@ -5,11 +5,10 @@ -- === -- -- ### Author: **TAW CougarNL**, *funkyfranky* --- +-- -- @module Utilities.PROFILER -- @image MOOSE.JPG - --- PROFILER class. -- @type PROFILER -- @field #string ClassName Name of the class. @@ -25,7 +24,6 @@ -- @field #number ThreshTtot Total time threshold. Only write output if total function CPU time is more than this value. -- @field #string fileNamePrefix Output file name prefix, e.g. "MooseProfiler". -- @field #string fileNameSuffix Output file name prefix, e.g. "txt" - --- *The emperor counsels simplicity. First principles. Of each particular thing, ask: What is it in itself, in its own constitution? What is its causal nature? * -- -- === @@ -33,60 +31,59 @@ -- ![Banner Image](..\Presentations\Utilities\PROFILER_Main.jpg) -- -- # The PROFILER Concept --- +-- -- Profile your lua code. This tells you, which functions are called very often and which consume most real time. --- With this information you can optimize the perfomance of your code. --- +-- With this information you can optimize the performance of your code. +-- -- # Prerequisites --- --- The modules **os** and **lfs** need to be desanizied. --- --- +-- +-- The modules **os** and **lfs** need to be de-sanitized. +-- -- # Start --- +-- -- The profiler can simply be started with the @{#PROFILER.Start}(*Delay, Duration*) function --- +-- -- PROFILER.Start() --- +-- -- The optional parameter *Delay* can be used to delay the start by a certain amount of seconds and the optional parameter *Duration* can be used to -- stop the profiler after a certain amount of seconds. --- +-- -- # Stop --- +-- -- The profiler automatically stops when the mission ends. But it can be stopped any time with the @{#PROFILER.Stop}(*Delay*) function --- +-- -- PROFILER.Stop() --- +-- -- The optional parameter *Delay* can be used to specify a delay after which the profiler is stopped. --- +-- -- When the profiler is stopped, the output is written to a file. --- +-- -- # Output --- +-- -- The profiler output is written to a file in your DCS home folder --- +-- -- X:\User\\Saved Games\DCS OpenBeta\Logs --- +-- -- The default file name is "MooseProfiler.txt". If that file exists, the file name is "MooseProfiler-001.txt" etc. --- +-- -- ## Data --- +-- -- The data in the output file provides information on the functions that were called in the mission. --- +-- -- It will tell you how many times a function was called in total, how many times per second, how much time in total and the percentage of time. --- +-- -- If you only want output for functions that are called more than *X* times per second, you can set --- +-- -- PROFILER.ThreshCPS=1.5 --- +-- -- With this setting, only functions which are called more than 1.5 times per second are displayed. The default setting is PROFILER.ThreshCPS=0.0 (no threshold). --- +-- -- Furthermore, you can limit the output for functions that consumed a certain amount of CPU time in total by --- +-- -- PROFILER.ThreshTtot=0.005 --- +-- -- With this setting, which is also the default, only functions which in total used more than 5 milliseconds CPU time. --- +-- -- @field #PROFILER PROFILER = { ClassName = "PROFILER", @@ -117,97 +114,95 @@ PROFILER = { --- Start profiler. -- @param #number Delay Delay in seconds before profiler is stated. Default is immediately. -- @param #number Duration Duration in (game) seconds before the profiler is stopped. Default is when mission ends. -function PROFILER.Start(Delay, Duration) +function PROFILER.Start( Delay, Duration ) -- Check if os, io and lfs are available. - local go=true + local go = true if not os then - env.error("ERROR: Profiler needs os to be desanitized!") - go=false + env.error( "ERROR: Profiler needs os to be de-sanitized!" ) + go = false end if not io then - env.error("ERROR: Profiler needs io to be desanitized!") - go=false - end + env.error( "ERROR: Profiler needs io to be de-sanitized!" ) + go = false + end if not lfs then - env.error("ERROR: Profiler needs lfs to be desanitized!") - go=false - end + env.error( "ERROR: Profiler needs lfs to be de-sanitized!" ) + go = false + end if not go then return end - if Delay and Delay>0 then - BASE:ScheduleOnce(Delay, PROFILER.Start, 0, Duration) + if Delay and Delay > 0 then + BASE:ScheduleOnce( Delay, PROFILER.Start, 0, Duration ) else - + -- Set start time. - PROFILER.TstartGame=timer.getTime() - PROFILER.TstartOS=os.clock() - + PROFILER.TstartGame = timer.getTime() + PROFILER.TstartOS = os.clock() + -- Add event handler. - world.addEventHandler(PROFILER.eventHandler) - + world.addEventHandler( PROFILER.eventHandler ) + -- Info in log. - env.info('############################ Profiler Started ############################') + env.info( '############################ Profiler Started ############################' ) if Duration then - env.info(string.format("- Will be running for %d seconds", Duration)) + env.info( string.format( "- Will be running for %d seconds", Duration ) ) else - env.info(string.format("- Will be stopped when mission ends")) + env.info( string.format( "- Will be stopped when mission ends" ) ) end - env.info(string.format("- Calls per second threshold %.3f/sec", PROFILER.ThreshCPS)) - env.info(string.format("- Total function time threshold %.3f sec", PROFILER.ThreshTtot)) - env.info(string.format("- Output file \"%s\" in your DCS log file folder", PROFILER.getfilename(PROFILER.fileNameSuffix))) - env.info(string.format("- Output file \"%s\" in CSV format", PROFILER.getfilename("csv"))) - env.info('###############################################################################') - - + env.info( string.format( "- Calls per second threshold %.3f/sec", PROFILER.ThreshCPS ) ) + env.info( string.format( "- Total function time threshold %.3f sec", PROFILER.ThreshTtot ) ) + env.info( string.format( "- Output file \"%s\" in your DCS log file folder", PROFILER.getfilename( PROFILER.fileNameSuffix ) ) ) + env.info( string.format( "- Output file \"%s\" in CSV format", PROFILER.getfilename( "csv" ) ) ) + env.info( '###############################################################################' ) + -- Message on screen - local duration=Duration or 600 - trigger.action.outText("### Profiler running ###", duration) - + local duration = Duration or 600 + trigger.action.outText( "### Profiler running ###", duration ) + -- Set hook. - debug.sethook(PROFILER.hook, "cr") - + debug.sethook( PROFILER.hook, "cr" ) + -- Auto stop profiler. if Duration then - PROFILER.Stop(Duration) + PROFILER.Stop( Duration ) end - + end - + end --- Stop profiler. -- @param #number Delay Delay before stop in seconds. -function PROFILER.Stop(Delay) +function PROFILER.Stop( Delay ) + + if Delay and Delay > 0 then + + BASE:ScheduleOnce( Delay, PROFILER.Stop ) - if Delay and Delay>0 then - - BASE:ScheduleOnce(Delay, PROFILER.Stop) - else -- Remove hook. debug.sethook() - - + -- Run time game. - local runTimeGame=timer.getTime()-PROFILER.TstartGame - + local runTimeGame = timer.getTime() - PROFILER.TstartGame + -- Run time real OS. - local runTimeOS=os.clock()-PROFILER.TstartOS - + local runTimeOS = os.clock() - PROFILER.TstartOS + -- Show info. - PROFILER.showInfo(runTimeGame, runTimeOS) - + PROFILER.showInfo( runTimeGame, runTimeOS ) + end end --- Event handler. -function PROFILER.eventHandler:onEvent(event) - if event.id==world.event.S_EVENT_MISSION_END then +function PROFILER.eventHandler:onEvent( event ) + if event.id == world.event.S_EVENT_MISSION_END then PROFILER.Stop() end end @@ -218,38 +213,38 @@ end --- Debug hook. -- @param #table event Event. -function PROFILER.hook(event) +function PROFILER.hook( event ) - local f=debug.getinfo(2, "f").func - - if event=='call' then - - if PROFILER.Counters[f]==nil then - - PROFILER.Counters[f]=1 - PROFILER.dInfo[f]=debug.getinfo(2,"Sn") - - if PROFILER.fTimeTotal[f]==nil then - PROFILER.fTimeTotal[f]=0 + local f = debug.getinfo( 2, "f" ).func + + if event == 'call' then + + if PROFILER.Counters[f] == nil then + + PROFILER.Counters[f] = 1 + PROFILER.dInfo[f] = debug.getinfo( 2, "Sn" ) + + if PROFILER.fTimeTotal[f] == nil then + PROFILER.fTimeTotal[f] = 0 end - + else - PROFILER.Counters[f]=PROFILER.Counters[f]+1 + PROFILER.Counters[f] = PROFILER.Counters[f] + 1 end - - if PROFILER.fTime[f]==nil then - PROFILER.fTime[f]=os.clock() + + if PROFILER.fTime[f] == nil then + PROFILER.fTime[f] = os.clock() end - - elseif (event=='return') then - - if PROFILER.fTime[f]~=nil then - PROFILER.fTimeTotal[f]=PROFILER.fTimeTotal[f]+(os.clock()-PROFILER.fTime[f]) - PROFILER.fTime[f]=nil + + elseif (event == 'return') then + + if PROFILER.fTime[f] ~= nil then + PROFILER.fTimeTotal[f] = PROFILER.fTimeTotal[f] + (os.clock() - PROFILER.fTime[f]) + PROFILER.fTime[f] = nil end - + end - + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -262,105 +257,104 @@ end -- @return #string Source file name. -- @return #string Line number. -- @return #number Function time in seconds. -function PROFILER.getData(func) +function PROFILER.getData( func ) - local n=PROFILER.dInfo[func] - - if n.what=="C" then + local n = PROFILER.dInfo[func] + + if n.what == "C" then return n.name, "?", "?", PROFILER.fTimeTotal[func] end - - return n.name, n.short_src, n.linedefined, PROFILER.fTimeTotal[func] + + return n.name, n.short_src, n.linedefined, PROFILER.fTimeTotal[func] end --- Write text to log file. -- @param #function f The file. -- @param #string txt The text. -function PROFILER._flog(f, txt) - f:write(txt.."\r\n") +function PROFILER._flog( f, txt ) + f:write( txt .. "\r\n" ) end --- Show table. -- @param #table data Data table. -- @param #function f The file. -- @param #number runTimeGame Game run time in seconds. -function PROFILER.showTable(data, f, runTimeGame) +function PROFILER.showTable( data, f, runTimeGame ) -- Loop over data. - for i=1, #data do - local t=data[i] --#PROFILER.Data - + for i = 1, #data do + local t = data[i] -- #PROFILER.Data + -- Calls per second. - local cps=t.count/runTimeGame - - local threshCPS=cps>=PROFILER.ThreshCPS - local threshTot=t.tm>=PROFILER.ThreshTtot - + local cps = t.count / runTimeGame + + local threshCPS = cps >= PROFILER.ThreshCPS + local threshTot = t.tm >= PROFILER.ThreshTtot + if threshCPS and threshTot then - + -- Output - local text=string.format("%30s: %8d calls %8.1f/sec - Time Total %8.3f sec (%.3f %%) %5.3f sec/call %s line %s", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line)) - PROFILER._flog(f, text) - + local text = string.format( "%30s: %8d calls %8.1f/sec - Time Total %8.3f sec (%.3f %%) %5.3f sec/call %s line %s", t.func, t.count, cps, t.tm, t.tm / runTimeGame * 100, t.tm / t.count, tostring( t.src ), tostring( t.line ) ) + PROFILER._flog( f, text ) + end end - + end --- Print csv file. -- @param #table data Data table. -- @param #number runTimeGame Game run time in seconds. -function PROFILER.printCSV(data, runTimeGame) +function PROFILER.printCSV( data, runTimeGame ) -- Output file. - local file=PROFILER.getfilename("csv") - local g=io.open(file, 'w') + local file = PROFILER.getfilename( "csv" ) + local g = io.open( file, 'w' ) -- Header. - local text="Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number," - g:write(text.."\r\n") - + local text = "Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number," + g:write( text .. "\r\n" ) + -- Loop over data. - for i=1, #data do - local t=data[i] --#PROFILER.Data - + for i = 1, #data do + local t = data[i] -- #PROFILER.Data + -- Calls per second. - local cps=t.count/runTimeGame + local cps = t.count / runTimeGame -- Output - local txt=string.format("%s,%d,%.1f,%.3f,%.3f,%.3f,%s,%s,", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line)) - g:write(txt.."\r\n") - + local txt = string.format( "%s,%d,%.1f,%.3f,%.3f,%.3f,%s,%s,", t.func, t.count, cps, t.tm, t.tm / runTimeGame * 100, t.tm / t.count, tostring( t.src ), tostring( t.line ) ) + g:write( txt .. "\r\n" ) + end - + -- Close file. g:close() end - --- Write info to output file. -- @param #string ext Extension. -- @return #string File name. -function PROFILER.getfilename(ext) +function PROFILER.getfilename( ext ) - local dir=lfs.writedir()..[[Logs\]] - - ext=ext or PROFILER.fileNameSuffix - - local file=dir..PROFILER.fileNamePrefix.."."..ext - - if not UTILS.FileExists(file) then + local dir = lfs.writedir() .. [[Logs\]] + + ext = ext or PROFILER.fileNameSuffix + + local file = dir .. PROFILER.fileNamePrefix .. "." .. ext + + if not UTILS.FileExists( file ) then return file end - - for i=1,999 do - - local file=string.format("%s%s-%03d.%s", dir,PROFILER.fileNamePrefix, i, ext) - if not UTILS.FileExists(file) then + for i = 1, 999 do + + local file = string.format( "%s%s-%03d.%s", dir, PROFILER.fileNamePrefix, i, ext ) + + if not UTILS.FileExists( file ) then return file end - + end end @@ -368,172 +362,172 @@ end --- Write info to output file. -- @param #number runTimeGame Game time in seconds. -- @param #number runTimeOS OS time in seconds. -function PROFILER.showInfo(runTimeGame, runTimeOS) +function PROFILER.showInfo( runTimeGame, runTimeOS ) -- Output file. - local file=PROFILER.getfilename(PROFILER.fileNameSuffix) - local f=io.open(file, 'w') - + local file = PROFILER.getfilename( PROFILER.fileNameSuffix ) + local f = io.open( file, 'w' ) + -- Gather data. - local Ttot=0 - local Calls=0 - - local t={} - - local tcopy=nil --#PROFILER.Data - local tserialize=nil --#PROFILER.Data - local tforgen=nil --#PROFILER.Data - local tpairs=nil --#PROFILER.Data - - - for func, count in pairs(PROFILER.Counters) do - - local s,src,line,tm=PROFILER.getData(func) - - if PROFILER.logUnknown==true then - if s==nil then s="" end - end - - if s~=nil then - - -- Profile data. - local T= - { func=s, - src=src, - line=line, - count=count, - tm=tm, - } --#PROFILER.Data - - -- Collect special cases. Somehow, e.g. "_copy" appears multiple times so we try to gather all data. - if s=="_copy" then - if tcopy==nil then - tcopy=T - else - tcopy.count=tcopy.count+T.count - tcopy.tm=tcopy.tm+T.tm - end - elseif s=="_Serialize" then - if tserialize==nil then - tserialize=T - else - tserialize.count=tserialize.count+T.count - tserialize.tm=tserialize.tm+T.tm - end - elseif s=="(for generator)" then - if tforgen==nil then - tforgen=T - else - tforgen.count=tforgen.count+T.count - tforgen.tm=tforgen.tm+T.tm - end - elseif s=="pairs" then - if tpairs==nil then - tpairs=T - else - tpairs.count=tpairs.count+T.count - tpairs.tm=tpairs.tm+T.tm - end - else - table.insert(t, T) + local Ttot = 0 + local Calls = 0 + + local t = {} + + local tcopy = nil -- #PROFILER.Data + local tserialize = nil -- #PROFILER.Data + local tforgen = nil -- #PROFILER.Data + local tpairs = nil -- #PROFILER.Data + + for func, count in pairs( PROFILER.Counters ) do + + local s, src, line, tm = PROFILER.getData( func ) + + if PROFILER.logUnknown == true then + if s == nil then + s = "" end - - -- Total function time. - Ttot=Ttot+tm - - -- Total number of calls. - Calls=Calls+count - end - + + if s ~= nil then + + -- Profile data. + local T = { func = s, src = src, line = line, count = count, tm = tm } -- #PROFILER.Data + + -- Collect special cases. Somehow, e.g. "_copy" appears multiple times so we try to gather all data. + if s == "_copy" then + if tcopy == nil then + tcopy = T + else + tcopy.count = tcopy.count + T.count + tcopy.tm = tcopy.tm + T.tm + end + elseif s == "_Serialize" then + if tserialize == nil then + tserialize = T + else + tserialize.count = tserialize.count + T.count + tserialize.tm = tserialize.tm + T.tm + end + elseif s == "(for generator)" then + if tforgen == nil then + tforgen = T + else + tforgen.count = tforgen.count + T.count + tforgen.tm = tforgen.tm + T.tm + end + elseif s == "pairs" then + if tpairs == nil then + tpairs = T + else + tpairs.count = tpairs.count + T.count + tpairs.tm = tpairs.tm + T.tm + end + else + table.insert( t, T ) + end + + -- Total function time. + Ttot = Ttot + tm + + -- Total number of calls. + Calls = Calls + count + + end + end -- Add special cases. if tcopy then - table.insert(t, tcopy) + table.insert( t, tcopy ) end if tserialize then - table.insert(t, tserialize) + table.insert( t, tserialize ) end if tforgen then - table.insert(t, tforgen) + table.insert( t, tforgen ) end if tpairs then - table.insert(t, tpairs) - end - - env.info('############################ Profiler Stopped ############################') - env.info(string.format("* Runtime Game : %s = %d sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame)) - env.info(string.format("* Runtime Real : %s = %d sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS)) - env.info(string.format("* Function time : %s = %.1f sec (%.1f percent of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/runTimeGame*100)) - env.info(string.format("* Total functions : %d", #t)) - env.info(string.format("* Total func calls : %d", Calls)) - env.info(string.format("* Writing to file : \"%s\"", file)) - env.info(string.format("* Writing to file : \"%s\"", PROFILER.getfilename("csv"))) - env.info("##############################################################################") - + table.insert( t, tpairs ) + end + + env.info( '############################ Profiler Stopped ############################' ) + env.info( string.format( "* Runtime Game : %s = %d sec", UTILS.SecondsToClock( runTimeGame, true ), runTimeGame ) ) + env.info( string.format( "* Runtime Real : %s = %d sec", UTILS.SecondsToClock( runTimeOS, true ), runTimeOS ) ) + env.info( string.format( "* Function time : %s = %.1f sec (%.1f percent of runtime game)", UTILS.SecondsToClock( Ttot, true ), Ttot, Ttot / runTimeGame * 100 ) ) + env.info( string.format( "* Total functions : %d", #t ) ) + env.info( string.format( "* Total func calls : %d", Calls ) ) + env.info( string.format( "* Writing to file : \"%s\"", file ) ) + env.info( string.format( "* Writing to file : \"%s\"", PROFILER.getfilename( "csv" ) ) ) + env.info( "##############################################################################" ) + -- Sort by total time. - table.sort(t, function(a,b) return a.tm>b.tm end) - + table.sort( t, function( a, b ) + return a.tm > b.tm + end ) + -- Write data. - PROFILER._flog(f,"") - PROFILER._flog(f,"************************************************************************************************************************") - PROFILER._flog(f,"************************************************************************************************************************") - PROFILER._flog(f,"************************************************************************************************************************") - PROFILER._flog(f,"") - PROFILER._flog(f,"-------------------------") - PROFILER._flog(f,"---- Profiler Report ----") - PROFILER._flog(f,"-------------------------") - PROFILER._flog(f,"") - PROFILER._flog(f,string.format("* Runtime Game : %s = %.1f sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame)) - PROFILER._flog(f,string.format("* Runtime Real : %s = %.1f sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS)) - PROFILER._flog(f,string.format("* Function time : %s = %.1f sec (%.1f %% of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/runTimeGame*100)) - PROFILER._flog(f,"") - PROFILER._flog(f,string.format("* Total functions = %d", #t)) - PROFILER._flog(f,string.format("* Total func calls = %d", Calls)) - PROFILER._flog(f,"") - PROFILER._flog(f,string.format("* Calls per second threshold = %.3f/sec", PROFILER.ThreshCPS)) - PROFILER._flog(f,string.format("* Total func time threshold = %.3f sec", PROFILER.ThreshTtot)) - PROFILER._flog(f,"") - PROFILER._flog(f,"************************************************************************************************************************") - PROFILER._flog(f,"") - PROFILER.showTable(t, f, runTimeGame) + PROFILER._flog( f, "" ) + PROFILER._flog( f, "************************************************************************************************************************" ) + PROFILER._flog( f, "************************************************************************************************************************" ) + PROFILER._flog( f, "************************************************************************************************************************" ) + PROFILER._flog( f, "" ) + PROFILER._flog( f, "-------------------------" ) + PROFILER._flog( f, "---- Profiler Report ----" ) + PROFILER._flog( f, "-------------------------" ) + PROFILER._flog( f, "" ) + PROFILER._flog( f, string.format( "* Runtime Game : %s = %.1f sec", UTILS.SecondsToClock( runTimeGame, true ), runTimeGame ) ) + PROFILER._flog( f, string.format( "* Runtime Real : %s = %.1f sec", UTILS.SecondsToClock( runTimeOS, true ), runTimeOS ) ) + PROFILER._flog( f, string.format( "* Function time : %s = %.1f sec (%.1f %% of runtime game)", UTILS.SecondsToClock( Ttot, true ), Ttot, Ttot / runTimeGame * 100 ) ) + PROFILER._flog( f, "" ) + PROFILER._flog( f, string.format( "* Total functions = %d", #t ) ) + PROFILER._flog( f, string.format( "* Total func calls = %d", Calls ) ) + PROFILER._flog( f, "" ) + PROFILER._flog( f, string.format( "* Calls per second threshold = %.3f/sec", PROFILER.ThreshCPS ) ) + PROFILER._flog( f, string.format( "* Total func time threshold = %.3f sec", PROFILER.ThreshTtot ) ) + PROFILER._flog( f, "" ) + PROFILER._flog( f, "************************************************************************************************************************" ) + PROFILER._flog( f, "" ) + PROFILER.showTable( t, f, runTimeGame ) -- Sort by number of calls. - table.sort(t, function(a,b) return a.tm/a.count>b.tm/b.count end) - + table.sort( t, function( a, b ) + return a.tm / a.count > b.tm / b.count + end ) + -- Detailed data. - PROFILER._flog(f,"") - PROFILER._flog(f,"************************************************************************************************************************") - PROFILER._flog(f,"") - PROFILER._flog(f,"--------------------------------------") - PROFILER._flog(f,"---- Data Sorted by Time per Call ----") - PROFILER._flog(f,"--------------------------------------") - PROFILER._flog(f,"") - PROFILER.showTable(t, f, runTimeGame) - + PROFILER._flog( f, "" ) + PROFILER._flog( f, "************************************************************************************************************************" ) + PROFILER._flog( f, "" ) + PROFILER._flog( f, "--------------------------------------" ) + PROFILER._flog( f, "---- Data Sorted by Time per Call ----" ) + PROFILER._flog( f, "--------------------------------------" ) + PROFILER._flog( f, "" ) + PROFILER.showTable( t, f, runTimeGame ) + -- Sort by number of calls. - table.sort(t, function(a,b) return a.count>b.count end) - + table.sort( t, function( a, b ) + return a.count > b.count + end ) + -- Detailed data. - PROFILER._flog(f,"") - PROFILER._flog(f,"************************************************************************************************************************") - PROFILER._flog(f,"") - PROFILER._flog(f,"------------------------------------") - PROFILER._flog(f,"---- Data Sorted by Total Calls ----") - PROFILER._flog(f,"------------------------------------") - PROFILER._flog(f,"") - PROFILER.showTable(t, f, runTimeGame) - + PROFILER._flog( f, "" ) + PROFILER._flog( f, "************************************************************************************************************************" ) + PROFILER._flog( f, "" ) + PROFILER._flog( f, "------------------------------------" ) + PROFILER._flog( f, "---- Data Sorted by Total Calls ----" ) + PROFILER._flog( f, "------------------------------------" ) + PROFILER._flog( f, "" ) + PROFILER.showTable( t, f, runTimeGame ) + -- Closing. - PROFILER._flog(f,"") - PROFILER._flog(f,"************************************************************************************************************************") - PROFILER._flog(f,"************************************************************************************************************************") - PROFILER._flog(f,"************************************************************************************************************************") + PROFILER._flog( f, "" ) + PROFILER._flog( f, "************************************************************************************************************************" ) + PROFILER._flog( f, "************************************************************************************************************************" ) + PROFILER._flog( f, "************************************************************************************************************************" ) -- Close file. f:close() - - -- Print csv file. - PROFILER.printCSV(t, runTimeGame) -end + -- Print csv file. + PROFILER.printCSV( t, runTimeGame ) +end diff --git a/Moose Development/Moose/Utilities/Routines.lua b/Moose Development/Moose/Utilities/Routines.lua index 15e4cc1c8..83d52180d 100644 --- a/Moose Development/Moose/Utilities/Routines.lua +++ b/Moose Development/Moose/Utilities/Routines.lua @@ -1,15 +1,13 @@ --- Various routines -- @module routines -- @image MOOSE.JPG - -env.setErrorMessageBoxEnabled(false) +env.setErrorMessageBoxEnabled( false ) --- Extract of MIST functions. -- @author Grimes routines = {} - -- don't change these routines.majorVersion = 3 routines.minorVersion = 3 @@ -21,291 +19,281 @@ routines.build = 22 -- Utils- conversion, Lua utils, etc. routines.utils = {} -routines.utils.round = function(number, decimals) - local power = 10^decimals - return math.floor(number * power) / power +routines.utils.round = function( number, decimals ) + local power = 10 ^ decimals + return math.floor( number * power ) / power end ---from http://lua-users.org/wiki/CopyTable -routines.utils.deepCopy = function(object) - local lookup_table = {} - local function _copy(object) - if type(object) ~= "table" then - return object - elseif lookup_table[object] then - return lookup_table[object] - end - local new_table = {} - lookup_table[object] = new_table - for index, value in pairs(object) do - new_table[_copy(index)] = _copy(value) - end - return setmetatable(new_table, getmetatable(object)) - end - local objectreturn = _copy(object) - return objectreturn +-- from http://lua-users.org/wiki/CopyTable +routines.utils.deepCopy = function( object ) + local lookup_table = {} + local function _copy( object ) + if type( object ) ~= "table" then + return object + elseif lookup_table[object] then + return lookup_table[object] + end + local new_table = {} + lookup_table[object] = new_table + for index, value in pairs( object ) do + new_table[_copy( index )] = _copy( value ) + end + return setmetatable( new_table, getmetatable( object ) ) + end + local objectreturn = _copy( object ) + return objectreturn end - -- porting in Slmod's serialize_slmod2 -routines.utils.oneLineSerialize = function(tbl) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function +routines.utils.oneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function - lookup_table = {} - - local function _Serialize( tbl ) + lookup_table = {} - if type(tbl) == 'table' then --function only works for tables! - - if lookup_table[tbl] then - return lookup_table[object] - end + local function _Serialize( tbl ) - local tbl_str = {} - - lookup_table[tbl] = tbl_str - - tbl_str[#tbl_str + 1] = '{' + if type( tbl ) == 'table' then -- function only works for tables! - for ind,val in pairs(tbl) do -- serialize its fields - local ind_str = {} - if type(ind) == "number" then - ind_str[#ind_str + 1] = '[' - ind_str[#ind_str + 1] = tostring(ind) - ind_str[#ind_str + 1] = ']=' - else --must be a string - ind_str[#ind_str + 1] = '[' - ind_str[#ind_str + 1] = routines.utils.basicSerialize(ind) - ind_str[#ind_str + 1] = ']=' - end + if lookup_table[tbl] then + return lookup_table[object] + end - local val_str = {} - if ((type(val) == 'number') or (type(val) == 'boolean')) then - val_str[#val_str + 1] = tostring(val) - val_str[#val_str + 1] = ',' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'string' then - val_str[#val_str + 1] = routines.utils.basicSerialize(val) - val_str[#val_str + 1] = ',' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'nil' then -- won't ever happen, right? - val_str[#val_str + 1] = 'nil,' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'table' then - if ind == "__index" then - -- tbl_str[#tbl_str + 1] = "__index" - -- tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it - else + local tbl_str = {} - val_str[#val_str + 1] = _Serialize(val) - val_str[#val_str + 1] = ',' --I think this is right, I just added it - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - end - elseif type(val) == 'function' then - -- tbl_str[#tbl_str + 1] = "function " .. tostring(ind) - -- tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it - else --- env.info('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) --- env.info( debug.traceback() ) - end - - end - tbl_str[#tbl_str + 1] = '}' - return table.concat(tbl_str) - else - if type(tbl) == 'string' then - return tbl - else - return tostring(tbl) - end - end - end - - local objectreturn = _Serialize(tbl) - return objectreturn + lookup_table[tbl] = tbl_str + + tbl_str[#tbl_str + 1] = '{' + + for ind, val in pairs( tbl ) do -- serialize its fields + local ind_str = {} + if type( ind ) == "number" then + ind_str[#ind_str + 1] = '[' + ind_str[#ind_str + 1] = tostring( ind ) + ind_str[#ind_str + 1] = ']=' + else -- must be a string + ind_str[#ind_str + 1] = '[' + ind_str[#ind_str + 1] = routines.utils.basicSerialize( ind ) + ind_str[#ind_str + 1] = ']=' + end + + local val_str = {} + if ((type( val ) == 'number') or (type( val ) == 'boolean')) then + val_str[#val_str + 1] = tostring( val ) + val_str[#val_str + 1] = ',' + tbl_str[#tbl_str + 1] = table.concat( ind_str ) + tbl_str[#tbl_str + 1] = table.concat( val_str ) + elseif type( val ) == 'string' then + val_str[#val_str + 1] = routines.utils.basicSerialize( val ) + val_str[#val_str + 1] = ',' + tbl_str[#tbl_str + 1] = table.concat( ind_str ) + tbl_str[#tbl_str + 1] = table.concat( val_str ) + elseif type( val ) == 'nil' then -- won't ever happen, right? + val_str[#val_str + 1] = 'nil,' + tbl_str[#tbl_str + 1] = table.concat( ind_str ) + tbl_str[#tbl_str + 1] = table.concat( val_str ) + elseif type( val ) == 'table' then + if ind == "__index" then + -- tbl_str[#tbl_str + 1] = "__index" + -- tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it + else + + val_str[#val_str + 1] = _Serialize( val ) + val_str[#val_str + 1] = ',' -- I think this is right, I just added it + tbl_str[#tbl_str + 1] = table.concat( ind_str ) + tbl_str[#tbl_str + 1] = table.concat( val_str ) + end + elseif type( val ) == 'function' then + -- tbl_str[#tbl_str + 1] = "function " .. tostring(ind) + -- tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it + else + -- env.info('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) + -- env.info( debug.traceback() ) + end + + end + tbl_str[#tbl_str + 1] = '}' + return table.concat( tbl_str ) + else + if type( tbl ) == 'string' then + return tbl + else + return tostring( tbl ) + end + end + end + + local objectreturn = _Serialize( tbl ) + return objectreturn end ---porting in Slmod's "safestring" basic serialize -routines.utils.basicSerialize = function(s) - if s == nil then - return "\"\"" - else - if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then - return tostring(s) - elseif type(s) == 'string' then - s = string.format('%s', s:gsub( "%%", "%%%%" ) ) - return s - end - end +-- porting in Slmod's "safestring" basic serialize +routines.utils.basicSerialize = function( s ) + if s == nil then + return "\"\"" + else + if ((type( s ) == 'number') or (type( s ) == 'boolean') or (type( s ) == 'function') or (type( s ) == 'table') or (type( s ) == 'userdata')) then + return tostring( s ) + elseif type( s ) == 'string' then + s = string.format( '%s', s:gsub( "%%", "%%%%" ) ) + return s + end + end end - -routines.utils.toDegree = function(angle) - return angle*180/math.pi +routines.utils.toDegree = function( angle ) + return angle * 180 / math.pi end -routines.utils.toRadian = function(angle) - return angle*math.pi/180 +routines.utils.toRadian = function( angle ) + return angle * math.pi / 180 end -routines.utils.metersToNM = function(meters) - return meters/1852 +routines.utils.metersToNM = function( meters ) + return meters / 1852 end -routines.utils.metersToFeet = function(meters) - return meters/0.3048 +routines.utils.metersToFeet = function( meters ) + return meters / 0.3048 end -routines.utils.NMToMeters = function(NM) - return NM*1852 +routines.utils.NMToMeters = function( NM ) + return NM * 1852 end -routines.utils.feetToMeters = function(feet) - return feet*0.3048 +routines.utils.feetToMeters = function( feet ) + return feet * 0.3048 end -routines.utils.mpsToKnots = function(mps) - return mps*3600/1852 +routines.utils.mpsToKnots = function( mps ) + return mps * 3600 / 1852 end -routines.utils.mpsToKmph = function(mps) - return mps*3.6 +routines.utils.mpsToKmph = function( mps ) + return mps * 3.6 end -routines.utils.knotsToMps = function(knots) - return knots*1852/3600 +routines.utils.knotsToMps = function( knots ) + return knots * 1852 / 3600 end -routines.utils.kmphToMps = function(kmph) - return kmph/3.6 +routines.utils.kmphToMps = function( kmph ) + return kmph / 3.6 end -function routines.utils.makeVec2(Vec3) - if Vec3.z then - return {x = Vec3.x, y = Vec3.z} - else - return {x = Vec3.x, y = Vec3.y} -- it was actually already vec2. - end +function routines.utils.makeVec2( Vec3 ) + if Vec3.z then + return { x = Vec3.x, y = Vec3.z } + else + return { x = Vec3.x, y = Vec3.y } -- it was actually already vec2. + end end -function routines.utils.makeVec3(Vec2, y) - if not Vec2.z then - if not y then - y = 0 - end - return {x = Vec2.x, y = y, z = Vec2.y} - else - return {x = Vec2.x, y = Vec2.y, z = Vec2.z} -- it was already Vec3, actually. - end +function routines.utils.makeVec3( Vec2, y ) + if not Vec2.z then + if not y then + y = 0 + end + return { x = Vec2.x, y = y, z = Vec2.y } + else + return { x = Vec2.x, y = Vec2.y, z = Vec2.z } -- it was already Vec3, actually. + end end -function routines.utils.makeVec3GL(Vec2, offset) - local adj = offset or 0 +function routines.utils.makeVec3GL( Vec2, offset ) + local adj = offset or 0 - if not Vec2.z then - return {x = Vec2.x, y = (land.getHeight(Vec2) + adj), z = Vec2.y} - else - return {x = Vec2.x, y = (land.getHeight({x = Vec2.x, y = Vec2.z}) + adj), z = Vec2.z} - end + if not Vec2.z then + return { x = Vec2.x, y = (land.getHeight( Vec2 ) + adj), z = Vec2.y } + else + return { x = Vec2.x, y = (land.getHeight( { x = Vec2.x, y = Vec2.z } ) + adj), z = Vec2.z } + end end -routines.utils.zoneToVec3 = function(zone) - local new = {} - if type(zone) == 'table' and zone.point then - new.x = zone.point.x - new.y = zone.point.y - new.z = zone.point.z - return new - elseif type(zone) == 'string' then - zone = trigger.misc.getZone(zone) - if zone then - new.x = zone.point.x - new.y = zone.point.y - new.z = zone.point.z - return new - end - end +routines.utils.zoneToVec3 = function( zone ) + local new = {} + if type( zone ) == 'table' and zone.point then + new.x = zone.point.x + new.y = zone.point.y + new.z = zone.point.z + return new + elseif type( zone ) == 'string' then + zone = trigger.misc.getZone( zone ) + if zone then + new.x = zone.point.x + new.y = zone.point.y + new.z = zone.point.z + return new + end + end end -- gets heading-error corrected direction from point along vector vec. -function routines.utils.getDir(vec, point) - local dir = math.atan2(vec.z, vec.x) - dir = dir + routines.getNorthCorrection(point) - if dir < 0 then - dir = dir + 2*math.pi -- put dir in range of 0 to 2*pi - end - return dir +function routines.utils.getDir( vec, point ) + local dir = math.atan2( vec.z, vec.x ) + dir = dir + routines.getNorthCorrection( point ) + if dir < 0 then + dir = dir + 2 * math.pi -- put dir in range of 0 to 2*pi + end + return dir end -- gets distance in meters between two points (2 dimensional) -function routines.utils.get2DDist(point1, point2) - point1 = routines.utils.makeVec3(point1) - point2 = routines.utils.makeVec3(point2) - return routines.vec.mag({x = point1.x - point2.x, y = 0, z = point1.z - point2.z}) +function routines.utils.get2DDist( point1, point2 ) + point1 = routines.utils.makeVec3( point1 ) + point2 = routines.utils.makeVec3( point2 ) + return routines.vec.mag( { x = point1.x - point2.x, y = 0, z = point1.z - point2.z } ) end -- gets distance in meters between two points (3 dimensional) -function routines.utils.get3DDist(point1, point2) - return routines.vec.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z}) +function routines.utils.get3DDist( point1, point2 ) + return routines.vec.mag( { x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z } ) end - - - - ---3D Vector manipulation +-- 3D Vector manipulation routines.vec = {} -routines.vec.add = function(vec1, vec2) - return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z} +routines.vec.add = function( vec1, vec2 ) + return { x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z } end -routines.vec.sub = function(vec1, vec2) - return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z} +routines.vec.sub = function( vec1, vec2 ) + return { x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z } end -routines.vec.scalarMult = function(vec, mult) - return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult} +routines.vec.scalarMult = function( vec, mult ) + return { x = vec.x * mult, y = vec.y * mult, z = vec.z * mult } end routines.vec.scalar_mult = routines.vec.scalarMult -routines.vec.dp = function(vec1, vec2) - return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z +routines.vec.dp = function( vec1, vec2 ) + return vec1.x * vec2.x + vec1.y * vec2.y + vec1.z * vec2.z end -routines.vec.cp = function(vec1, vec2) - return { x = vec1.y*vec2.z - vec1.z*vec2.y, y = vec1.z*vec2.x - vec1.x*vec2.z, z = vec1.x*vec2.y - vec1.y*vec2.x} +routines.vec.cp = function( vec1, vec2 ) + return { x = vec1.y * vec2.z - vec1.z * vec2.y, y = vec1.z * vec2.x - vec1.x * vec2.z, z = vec1.x * vec2.y - vec1.y * vec2.x } end -routines.vec.mag = function(vec) - return (vec.x^2 + vec.y^2 + vec.z^2)^0.5 +routines.vec.mag = function( vec ) + return (vec.x ^ 2 + vec.y ^ 2 + vec.z ^ 2) ^ 0.5 end -routines.vec.getUnitVec = function(vec) - local mag = routines.vec.mag(vec) - return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag } +routines.vec.getUnitVec = function( vec ) + local mag = routines.vec.mag( vec ) + return { x = vec.x / mag, y = vec.y / mag, z = vec.z / mag } end -routines.vec.rotateVec2 = function(vec2, theta) - return { x = vec2.x*math.cos(theta) - vec2.y*math.sin(theta), y = vec2.x*math.sin(theta) + vec2.y*math.cos(theta)} +routines.vec.rotateVec2 = function( vec2, theta ) + return { x = vec2.x * math.cos( theta ) - vec2.y * math.sin( theta ), y = vec2.x * math.sin( theta ) + vec2.y * math.cos( theta ) } end --------------------------------------------------------------------------------------------------------------------------- - - - -- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5. -routines.tostringMGRS = function(MGRS, acc) - if acc == 0 then - return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph - else - return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', routines.utils.round(MGRS.Easting/(10^(5-acc)), 0)) - .. ' ' .. string.format('%0' .. acc .. 'd', routines.utils.round(MGRS.Northing/(10^(5-acc)), 0)) - end +routines.tostringMGRS = function( MGRS, acc ) + if acc == 0 then + return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph + else + return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format( '%0' .. acc .. 'd', routines.utils.round( MGRS.Easting / (10 ^ (5 - acc)), 0 ) ) .. ' ' .. string.format( '%0' .. acc .. 'd', routines.utils.round( MGRS.Northing / (10 ^ (5 - acc)), 0 ) ) + end end --[[acc: @@ -315,86 +303,84 @@ position after the decimal of the least significant digit: So: 42.32 - acc of 2. ]] -routines.tostringLL = function(lat, lon, acc, DMS) +routines.tostringLL = function( lat, lon, acc, DMS ) - local latHemi, lonHemi - if lat > 0 then - latHemi = 'N' - else - latHemi = 'S' - end + local latHemi, lonHemi + if lat > 0 then + latHemi = 'N' + else + latHemi = 'S' + end - if lon > 0 then - lonHemi = 'E' - else - lonHemi = 'W' - end + if lon > 0 then + lonHemi = 'E' + else + lonHemi = 'W' + end - lat = math.abs(lat) - lon = math.abs(lon) + lat = math.abs( lat ) + lon = math.abs( lon ) - local latDeg = math.floor(lat) - local latMin = (lat - latDeg)*60 + local latDeg = math.floor( lat ) + local latMin = (lat - latDeg) * 60 - local lonDeg = math.floor(lon) - local lonMin = (lon - lonDeg)*60 + local lonDeg = math.floor( lon ) + local lonMin = (lon - lonDeg) * 60 - if DMS then -- degrees, minutes, and seconds. - local oldLatMin = latMin - latMin = math.floor(latMin) - local latSec = routines.utils.round((oldLatMin - latMin)*60, acc) + if DMS then -- degrees, minutes, and seconds. + local oldLatMin = latMin + latMin = math.floor( latMin ) + local latSec = routines.utils.round( (oldLatMin - latMin) * 60, acc ) - local oldLonMin = lonMin - lonMin = math.floor(lonMin) - local lonSec = routines.utils.round((oldLonMin - lonMin)*60, acc) + local oldLonMin = lonMin + lonMin = math.floor( lonMin ) + local lonSec = routines.utils.round( (oldLonMin - lonMin) * 60, acc ) - if latSec == 60 then - latSec = 0 - latMin = latMin + 1 - end + if latSec == 60 then + latSec = 0 + latMin = latMin + 1 + end - if lonSec == 60 then - lonSec = 0 - lonMin = lonMin + 1 - end + if lonSec == 60 then + lonSec = 0 + lonMin = lonMin + 1 + end - local secFrmtStr -- create the formatting string for the seconds place - if acc <= 0 then -- no decimal place. - secFrmtStr = '%02d' - else - local width = 3 + acc -- 01.310 - that's a width of 6, for example. - secFrmtStr = '%0' .. width .. '.' .. acc .. 'f' - end + local secFrmtStr -- create the formatting string for the seconds place + if acc <= 0 then -- no decimal place. + secFrmtStr = '%02d' + else + local width = 3 + acc -- 01.310 - that's a width of 6, for example. + secFrmtStr = '%0' .. width .. '.' .. acc .. 'f' + end - return string.format('%02d', latDeg) .. ' ' .. string.format('%02d', latMin) .. '\' ' .. string.format(secFrmtStr, latSec) .. '"' .. latHemi .. ' ' - .. string.format('%02d', lonDeg) .. ' ' .. string.format('%02d', lonMin) .. '\' ' .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi + return string.format( '%02d', latDeg ) .. ' ' .. string.format( '%02d', latMin ) .. '\' ' .. string.format( secFrmtStr, latSec ) .. '"' .. latHemi .. ' ' .. string.format( '%02d', lonDeg ) .. ' ' .. string.format( '%02d', lonMin ) .. '\' ' .. string.format( secFrmtStr, lonSec ) .. '"' .. lonHemi - else -- degrees, decimal minutes. - latMin = routines.utils.round(latMin, acc) - lonMin = routines.utils.round(lonMin, acc) + else -- degrees, decimal minutes. + latMin = routines.utils.round( latMin, acc ) + lonMin = routines.utils.round( lonMin, acc ) - if latMin == 60 then - latMin = 0 - latDeg = latDeg + 1 - end + if latMin == 60 then + latMin = 0 + latDeg = latDeg + 1 + end - if lonMin == 60 then - lonMin = 0 - lonDeg = lonDeg + 1 - end + if lonMin == 60 then + lonMin = 0 + lonDeg = lonDeg + 1 + end - local minFrmtStr -- create the formatting string for the minutes place - if acc <= 0 then -- no decimal place. - minFrmtStr = '%02d' - else - local width = 3 + acc -- 01.310 - that's a width of 6, for example. - minFrmtStr = '%0' .. width .. '.' .. acc .. 'f' - end + local minFrmtStr -- create the formatting string for the minutes place + if acc <= 0 then -- no decimal place. + minFrmtStr = '%02d' + else + local width = 3 + acc -- 01.310 - that's a width of 6, for example. + minFrmtStr = '%0' .. width .. '.' .. acc .. 'f' + end - return string.format('%02d', latDeg) .. ' ' .. string.format(minFrmtStr, latMin) .. '\'' .. latHemi .. ' ' - .. string.format('%02d', lonDeg) .. ' ' .. string.format(minFrmtStr, lonMin) .. '\'' .. lonHemi + return string.format( '%02d', latDeg ) .. ' ' .. string.format( minFrmtStr, latMin ) .. '\'' .. latHemi .. ' ' .. string.format( '%02d', lonDeg ) .. ' ' .. string.format( minFrmtStr, lonMin ) .. '\'' .. lonHemi - end + end end --[[ required: az - radian @@ -402,471 +388,449 @@ end optional: alt - meters (set to false or nil if you don't want to use it). optional: metric - set true to get dist and alt in km and m. precision will always be nearest degree and NM or km.]] -routines.tostringBR = function(az, dist, alt, metric) - az = routines.utils.round(routines.utils.toDegree(az), 0) +routines.tostringBR = function( az, dist, alt, metric ) + az = routines.utils.round( routines.utils.toDegree( az ), 0 ) - if metric then - dist = routines.utils.round(dist/1000, 2) - else - dist = routines.utils.round(routines.utils.metersToNM(dist), 2) - end + if metric then + dist = routines.utils.round( dist / 1000, 2 ) + else + dist = routines.utils.round( routines.utils.metersToNM( dist ), 2 ) + end - local s = string.format('%03d', az) .. ' for ' .. dist + local s = string.format( '%03d', az ) .. ' for ' .. dist - if alt then - if metric then - s = s .. ' at ' .. routines.utils.round(alt, 0) - else - s = s .. ' at ' .. routines.utils.round(routines.utils.metersToFeet(alt), 0) - end - end - return s + if alt then + if metric then + s = s .. ' at ' .. routines.utils.round( alt, 0 ) + else + s = s .. ' at ' .. routines.utils.round( routines.utils.metersToFeet( alt ), 0 ) + end + end + return s end -routines.getNorthCorrection = function(point) --gets the correction needed for true north - 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) +routines.getNorthCorrection = function( point ) -- gets the correction needed for true north + 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 - do - local idNum = 0 + local idNum = 0 - --Simplified event handler - routines.addEventHandler = function(f) --id is optional! - local handler = {} - idNum = idNum + 1 - handler.id = idNum - handler.f = f - handler.onEvent = function(self, event) - self.f(event) - end - world.addEventHandler(handler) - end + -- Simplified event handler + routines.addEventHandler = function( f ) -- id is optional! + local handler = {} + idNum = idNum + 1 + handler.id = idNum + handler.f = f + handler.onEvent = function( self, event ) + self.f( event ) + end + world.addEventHandler( handler ) + end - routines.removeEventHandler = function(id) - for key, handler in pairs(world.eventHandlers) do - if handler.id and handler.id == id then - world.eventHandlers[key] = nil - return true - end - end - return false - end + routines.removeEventHandler = function( id ) + for key, handler in pairs( world.eventHandlers ) do + if handler.id and handler.id == id then + world.eventHandlers[key] = nil + return true + end + end + return false + end end -- need to return a Vec3 or Vec2? -function routines.getRandPointInCircle(point, radius, innerRadius) - local theta = 2*math.pi*math.random() - local rad = math.random() + math.random() - if rad > 1 then - rad = 2 - rad - end +function routines.getRandPointInCircle( point, radius, innerRadius ) + local theta = 2 * math.pi * math.random() + local rad = math.random() + math.random() + if rad > 1 then + rad = 2 - rad + end - local radMult - if innerRadius and innerRadius <= radius then - radMult = (radius - innerRadius)*rad + innerRadius - else - radMult = radius*rad - end + local radMult + if innerRadius and innerRadius <= radius then + radMult = (radius - innerRadius) * rad + innerRadius + else + radMult = radius * rad + end - if not point.z then --might as well work with vec2/3 - point.z = point.y - end + if not point.z then -- might as well work with vec2/3 + point.z = point.y + end - local rndCoord - if radius > 0 then - rndCoord = {x = math.cos(theta)*radMult + point.x, y = math.sin(theta)*radMult + point.z} - else - rndCoord = {x = point.x, y = point.z} - end - return rndCoord + local rndCoord + if radius > 0 then + rndCoord = { x = math.cos( theta ) * radMult + point.x, y = math.sin( theta ) * radMult + point.z } + else + rndCoord = { x = point.x, y = point.z } + end + return rndCoord end -routines.goRoute = function(group, path) - local misTask = { - id = 'Mission', - params = { - route = { - points = routines.utils.deepCopy(path), - }, - }, - } - if type(group) == 'string' then - group = Group.getByName(group) - end - local groupCon = group:getController() - if groupCon then - groupCon:setTask(misTask) - return true - end +routines.goRoute = function( group, path ) + local misTask = { id = 'Mission', params = { route = { points = routines.utils.deepCopy( path ) } } } + if type( group ) == 'string' then + group = Group.getByName( group ) + end + local groupCon = group:getController() + if groupCon then + groupCon:setTask( misTask ) + return true + end - Controller.setTask(groupCon, misTask) - return false + Controller.setTask( groupCon, misTask ) + return false end - -- Useful atomic functions from mist, ported. routines.ground = {} routines.fixedWing = {} routines.heli = {} -routines.ground.buildWP = function(point, overRideForm, overRideSpeed) +routines.ground.buildWP = function( point, overRideForm, overRideSpeed ) - local wp = {} - wp.x = point.x + local wp = {} + wp.x = point.x - if point.z then - wp.y = point.z - else - wp.y = point.y - end - local form, speed + if point.z then + wp.y = point.z + else + wp.y = point.y + end + local form, speed - if point.speed and not overRideSpeed then - wp.speed = point.speed - elseif type(overRideSpeed) == 'number' then - wp.speed = overRideSpeed - else - wp.speed = routines.utils.kmphToMps(20) - end + if point.speed and not overRideSpeed then + wp.speed = point.speed + elseif type( overRideSpeed ) == 'number' then + wp.speed = overRideSpeed + else + wp.speed = routines.utils.kmphToMps( 20 ) + end - if point.form and not overRideForm then - form = point.form - else - form = overRideForm - end + if point.form and not overRideForm then + form = point.form + else + form = overRideForm + end - if not form then - wp.action = 'Cone' - else - form = string.lower(form) - if form == 'off_road' or form == 'off road' then - wp.action = 'Off Road' - elseif form == 'on_road' or form == 'on road' then - wp.action = 'On Road' - elseif form == 'rank' or form == 'line_abrest' or form == 'line abrest' or form == 'lineabrest'then - wp.action = 'Rank' - elseif form == 'cone' then - wp.action = 'Cone' - elseif form == 'diamond' then - wp.action = 'Diamond' - elseif form == 'vee' then - wp.action = 'Vee' - elseif form == 'echelon_left' or form == 'echelon left' or form == 'echelonl' then - wp.action = 'EchelonL' - elseif form == 'echelon_right' or form == 'echelon right' or form == 'echelonr' then - wp.action = 'EchelonR' - else - wp.action = 'Cone' -- if nothing matched - end - end - - wp.type = 'Turning Point' - - return wp + if not form then + wp.action = 'Cone' + else + form = string.lower( form ) + if form == 'off_road' or form == 'off road' then + wp.action = 'Off Road' + elseif form == 'on_road' or form == 'on road' then + wp.action = 'On Road' + elseif form == 'rank' or form == 'line_abrest' or form == 'line abrest' or form == 'lineabrest' then + wp.action = 'Rank' + elseif form == 'cone' then + wp.action = 'Cone' + elseif form == 'diamond' then + wp.action = 'Diamond' + elseif form == 'vee' then + wp.action = 'Vee' + elseif form == 'echelon_left' or form == 'echelon left' or form == 'echelonl' then + wp.action = 'EchelonL' + elseif form == 'echelon_right' or form == 'echelon right' or form == 'echelonr' then + wp.action = 'EchelonR' + else + wp.action = 'Cone' -- if nothing matched + end + end + wp.type = 'Turning Point' + return wp end -routines.fixedWing.buildWP = function(point, WPtype, speed, alt, altType) +routines.fixedWing.buildWP = function( point, WPtype, speed, alt, altType ) - local wp = {} - wp.x = point.x + local wp = {} + wp.x = point.x - if point.z then - wp.y = point.z - else - wp.y = point.y - end + if point.z then + wp.y = point.z + else + wp.y = point.y + end - if alt and type(alt) == 'number' then - wp.alt = alt - else - wp.alt = 2000 - end + if alt and type( alt ) == 'number' then + wp.alt = alt + else + wp.alt = 2000 + end - if altType then - altType = string.lower(altType) - if altType == 'radio' or 'agl' then - wp.alt_type = 'RADIO' - elseif altType == 'baro' or 'asl' then - wp.alt_type = 'BARO' - end - else - wp.alt_type = 'RADIO' - end + if altType then + altType = string.lower( altType ) + if altType == 'radio' or 'agl' then + wp.alt_type = 'RADIO' + elseif altType == 'baro' or 'asl' then + wp.alt_type = 'BARO' + end + else + wp.alt_type = 'RADIO' + end - if point.speed then - speed = point.speed - end + if point.speed then + speed = point.speed + end - if point.type then - WPtype = point.type - end + if point.type then + WPtype = point.type + end - if not speed then - wp.speed = routines.utils.kmphToMps(500) - else - wp.speed = speed - end + if not speed then + wp.speed = routines.utils.kmphToMps( 500 ) + else + wp.speed = speed + end - if not WPtype then - wp.action = 'Turning Point' - else - WPtype = string.lower(WPtype) - if WPtype == 'flyover' or WPtype == 'fly over' or WPtype == 'fly_over' then - wp.action = 'Fly Over Point' - elseif WPtype == 'turningpoint' or WPtype == 'turning point' or WPtype == 'turning_point' then - wp.action = 'Turning Point' - else - wp.action = 'Turning Point' - end - end + if not WPtype then + wp.action = 'Turning Point' + else + WPtype = string.lower( WPtype ) + if WPtype == 'flyover' or WPtype == 'fly over' or WPtype == 'fly_over' then + wp.action = 'Fly Over Point' + elseif WPtype == 'turningpoint' or WPtype == 'turning point' or WPtype == 'turning_point' then + wp.action = 'Turning Point' + else + wp.action = 'Turning Point' + end + end - wp.type = 'Turning Point' - return wp + wp.type = 'Turning Point' + return wp end -routines.heli.buildWP = function(point, WPtype, speed, alt, altType) +routines.heli.buildWP = function( point, WPtype, speed, alt, altType ) - local wp = {} - wp.x = point.x + local wp = {} + wp.x = point.x - if point.z then - wp.y = point.z - else - wp.y = point.y - end + if point.z then + wp.y = point.z + else + wp.y = point.y + end - if alt and type(alt) == 'number' then - wp.alt = alt - else - wp.alt = 500 - end + if alt and type( alt ) == 'number' then + wp.alt = alt + else + wp.alt = 500 + end - if altType then - altType = string.lower(altType) - if altType == 'radio' or 'agl' then - wp.alt_type = 'RADIO' - elseif altType == 'baro' or 'asl' then - wp.alt_type = 'BARO' - end - else - wp.alt_type = 'RADIO' - end + if altType then + altType = string.lower( altType ) + if altType == 'radio' or 'agl' then + wp.alt_type = 'RADIO' + elseif altType == 'baro' or 'asl' then + wp.alt_type = 'BARO' + end + else + wp.alt_type = 'RADIO' + end - if point.speed then - speed = point.speed - end + if point.speed then + speed = point.speed + end - if point.type then - WPtype = point.type - end + if point.type then + WPtype = point.type + end - if not speed then - wp.speed = routines.utils.kmphToMps(200) - else - wp.speed = speed - end + if not speed then + wp.speed = routines.utils.kmphToMps( 200 ) + else + wp.speed = speed + end - if not WPtype then - wp.action = 'Turning Point' - else - WPtype = string.lower(WPtype) - if WPtype == 'flyover' or WPtype == 'fly over' or WPtype == 'fly_over' then - wp.action = 'Fly Over Point' - elseif WPtype == 'turningpoint' or WPtype == 'turning point' or WPtype == 'turning_point' then - wp.action = 'Turning Point' - else - wp.action = 'Turning Point' - end - end + if not WPtype then + wp.action = 'Turning Point' + else + WPtype = string.lower( WPtype ) + if WPtype == 'flyover' or WPtype == 'fly over' or WPtype == 'fly_over' then + wp.action = 'Fly Over Point' + elseif WPtype == 'turningpoint' or WPtype == 'turning point' or WPtype == 'turning_point' then + wp.action = 'Turning Point' + else + wp.action = 'Turning Point' + end + end - wp.type = 'Turning Point' - return wp + wp.type = 'Turning Point' + return wp end -routines.groupToRandomPoint = function(vars) - local group = vars.group --Required - local point = vars.point --required - local radius = vars.radius or 0 - local innerRadius = vars.innerRadius - local form = vars.form or 'Cone' - local heading = vars.heading or math.random()*2*math.pi - local headingDegrees = vars.headingDegrees - local speed = vars.speed or routines.utils.kmphToMps(20) +routines.groupToRandomPoint = function( vars ) + local group = vars.group -- Required + local point = vars.point -- required + local radius = vars.radius or 0 + local innerRadius = vars.innerRadius + local form = vars.form or 'Cone' + local heading = vars.heading or math.random() * 2 * math.pi + local headingDegrees = vars.headingDegrees + local speed = vars.speed or routines.utils.kmphToMps( 20 ) + local useRoads + if not vars.disableRoads then + useRoads = true + else + useRoads = false + end - local useRoads - if not vars.disableRoads then - useRoads = true - else - useRoads = false - end + local path = {} - local path = {} + if headingDegrees then + heading = headingDegrees * math.pi / 180 + end - if headingDegrees then - heading = headingDegrees*math.pi/180 - end + if heading >= 2 * math.pi then + heading = heading - 2 * math.pi + end - if heading >= 2*math.pi then - heading = heading - 2*math.pi - end + local rndCoord = routines.getRandPointInCircle( point, radius, innerRadius ) - local rndCoord = routines.getRandPointInCircle(point, radius, innerRadius) + local offset = {} + local posStart = routines.getLeadPos( group ) - local offset = {} - local posStart = routines.getLeadPos(group) + offset.x = routines.utils.round( math.sin( heading - (math.pi / 2) ) * 50 + rndCoord.x, 3 ) + offset.z = routines.utils.round( math.cos( heading + (math.pi / 2) ) * 50 + rndCoord.y, 3 ) + path[#path + 1] = routines.ground.buildWP( posStart, form, speed ) - offset.x = routines.utils.round(math.sin(heading - (math.pi/2)) * 50 + rndCoord.x, 3) - offset.z = routines.utils.round(math.cos(heading + (math.pi/2)) * 50 + rndCoord.y, 3) - path[#path + 1] = routines.ground.buildWP(posStart, form, speed) + if useRoads == true and ((point.x - posStart.x) ^ 2 + (point.z - posStart.z) ^ 2) ^ 0.5 > radius * 1.3 then + path[#path + 1] = routines.ground.buildWP( { ['x'] = posStart.x + 11, ['z'] = posStart.z + 11 }, 'off_road', speed ) + path[#path + 1] = routines.ground.buildWP( posStart, 'on_road', speed ) + path[#path + 1] = routines.ground.buildWP( offset, 'on_road', speed ) + else + path[#path + 1] = routines.ground.buildWP( { ['x'] = posStart.x + 25, ['z'] = posStart.z + 25 }, form, speed ) + end + path[#path + 1] = routines.ground.buildWP( offset, form, speed ) + path[#path + 1] = routines.ground.buildWP( rndCoord, form, speed ) - if useRoads == true and ((point.x - posStart.x)^2 + (point.z - posStart.z)^2)^0.5 > radius * 1.3 then - path[#path + 1] = routines.ground.buildWP({['x'] = posStart.x + 11, ['z'] = posStart.z + 11}, 'off_road', speed) - path[#path + 1] = routines.ground.buildWP(posStart, 'on_road', speed) - path[#path + 1] = routines.ground.buildWP(offset, 'on_road', speed) - else - path[#path + 1] = routines.ground.buildWP({['x'] = posStart.x + 25, ['z'] = posStart.z + 25}, form, speed) - end - - path[#path + 1] = routines.ground.buildWP(offset, form, speed) - path[#path + 1] = routines.ground.buildWP(rndCoord, form, speed) - - routines.goRoute(group, path) - - return + routines.goRoute( group, path ) end -routines.groupRandomDistSelf = function(gpData, dist, form, heading, speed) - local pos = routines.getLeadPos(gpData) - local fakeZone = {} - fakeZone.radius = dist or math.random(300, 1000) - fakeZone.point = {x = pos.x, y, pos.y, z = pos.z} - routines.groupToRandomZone(gpData, fakeZone, form, heading, speed) - - return +routines.groupRandomDistSelf = function( gpData, dist, form, heading, speed ) + local pos = routines.getLeadPos( gpData ) + local fakeZone = {} + fakeZone.radius = dist or math.random( 300, 1000 ) + fakeZone.point = { x = pos.x, y, pos.y, z = pos.z } + routines.groupToRandomZone( gpData, fakeZone, form, heading, speed ) end -routines.groupToRandomZone = function(gpData, zone, form, heading, speed) - if type(gpData) == 'string' then - gpData = Group.getByName(gpData) - end +routines.groupToRandomZone = function( gpData, zone, form, heading, speed ) + if type( gpData ) == 'string' then + gpData = Group.getByName( gpData ) + end - if type(zone) == 'string' then - zone = trigger.misc.getZone(zone) - elseif type(zone) == 'table' and not zone.radius then - zone = trigger.misc.getZone(zone[math.random(1, #zone)]) - end + if type( zone ) == 'string' then + zone = trigger.misc.getZone( zone ) + elseif type( zone ) == 'table' and not zone.radius then + zone = trigger.misc.getZone( zone[math.random( 1, #zone )] ) + end - if speed then - speed = routines.utils.kmphToMps(speed) - end + if speed then + speed = routines.utils.kmphToMps( speed ) + end - local vars = {} - vars.group = gpData - vars.radius = zone.radius - vars.form = form - vars.headingDegrees = heading - vars.speed = speed - vars.point = routines.utils.zoneToVec3(zone) + local vars = {} + vars.group = gpData + vars.radius = zone.radius + vars.form = form + vars.headingDegrees = heading + vars.speed = speed + vars.point = routines.utils.zoneToVec3( zone ) - routines.groupToRandomPoint(vars) - - return + routines.groupToRandomPoint( vars ) end -routines.isTerrainValid = function(coord, terrainTypes) -- vec2/3 and enum or table of acceptable terrain types - if coord.z then - coord.y = coord.z - end - local typeConverted = {} +routines.isTerrainValid = function( coord, terrainTypes ) -- vec2/3 and enum or table of acceptable terrain types + if coord.z then + coord.y = coord.z + end + local typeConverted = {} - if type(terrainTypes) == 'string' then -- if its a string it does this check - for constId, constData in pairs(land.SurfaceType) do - if string.lower(constId) == string.lower(terrainTypes) or string.lower(constData) == string.lower(terrainTypes) then - table.insert(typeConverted, constId) - end - end - elseif type(terrainTypes) == 'table' then -- if its a table it does this check - for typeId, typeData in pairs(terrainTypes) do - for constId, constData in pairs(land.SurfaceType) do - if string.lower(constId) == string.lower(typeData) or string.lower(constData) == string.lower(typeId) then - table.insert(typeConverted, constId) - end - end - end - end - for validIndex, validData in pairs(typeConverted) do - if land.getSurfaceType(coord) == land.SurfaceType[validData] then - return true - end - end - return false + if type( terrainTypes ) == 'string' then -- if its a string it does this check + for constId, constData in pairs( land.SurfaceType ) do + if string.lower( constId ) == string.lower( terrainTypes ) or string.lower( constData ) == string.lower( terrainTypes ) then + table.insert( typeConverted, constId ) + end + end + elseif type( terrainTypes ) == 'table' then -- if its a table it does this check + for typeId, typeData in pairs( terrainTypes ) do + for constId, constData in pairs( land.SurfaceType ) do + if string.lower( constId ) == string.lower( typeData ) or string.lower( constData ) == string.lower( typeId ) then + table.insert( typeConverted, constId ) + end + end + end + end + for validIndex, validData in pairs( typeConverted ) do + if land.getSurfaceType( coord ) == land.SurfaceType[validData] then + return true + end + end + return false end -routines.groupToPoint = function(gpData, point, form, heading, speed, useRoads) - if type(point) == 'string' then - point = trigger.misc.getZone(point) - end - if speed then - speed = routines.utils.kmphToMps(speed) - end +routines.groupToPoint = function( gpData, point, form, heading, speed, useRoads ) + if type( point ) == 'string' then + point = trigger.misc.getZone( point ) + end + if speed then + speed = routines.utils.kmphToMps( speed ) + end - local vars = {} - vars.group = gpData - vars.form = form - vars.headingDegrees = heading - vars.speed = speed - vars.disableRoads = useRoads - vars.point = routines.utils.zoneToVec3(point) - routines.groupToRandomPoint(vars) - - return + local vars = {} + vars.group = gpData + vars.form = form + vars.headingDegrees = heading + vars.speed = speed + vars.disableRoads = useRoads + vars.point = routines.utils.zoneToVec3( point ) + routines.groupToRandomPoint( vars ) end +routines.getLeadPos = function( group ) + if type( group ) == 'string' then -- group name + group = Group.getByName( group ) + end -routines.getLeadPos = function(group) - if type(group) == 'string' then -- group name - group = Group.getByName(group) - end + local units = group:getUnits() - local units = group:getUnits() - - local leader = units[1] - if not leader then -- SHOULD be good, but if there is a bug, this code future-proofs it then. - local lowestInd = math.huge - for ind, unit in pairs(units) do - if ind < lowestInd then - lowestInd = ind - leader = unit - end - end - end - if leader and Unit.isExist(leader) then -- maybe a little too paranoid now... - return leader:getPosition().p - end + local leader = units[1] + if not leader then -- SHOULD be good, but if there is a bug, this code future-proofs it then. + local lowestInd = math.huge + for ind, unit in pairs( units ) do + if ind < lowestInd then + lowestInd = ind + leader = unit + end + end + end + if leader and Unit.isExist( leader ) then -- maybe a little too paranoid now... + return leader:getPosition().p + end end --[[ vars for routines.getMGRSString: vars.units - table of unit names (NOT unitNameTable- maybe this should change). vars.acc - integer between 0 and 5, inclusive ]] -routines.getMGRSString = function(vars) - local units = vars.units - local acc = vars.acc or 5 - local avgPos = routines.getAvgPos(units) - if avgPos then - return routines.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(avgPos)), acc) - end +routines.getMGRSString = function( vars ) + local units = vars.units + local acc = vars.acc or 5 + local avgPos = routines.getAvgPos( units ) + if avgPos then + return routines.tostringMGRS( coord.LLtoMGRS( coord.LOtoLL( avgPos ) ), acc ) + end end --[[ vars for routines.getLLString @@ -876,15 +840,15 @@ vars.DMS - if true, output in degrees, minutes, seconds. Otherwise, output in d ]] -routines.getLLString = function(vars) - local units = vars.units - local acc = vars.acc or 3 - local DMS = vars.DMS - local avgPos = routines.getAvgPos(units) - if avgPos then - local lat, lon = coord.LOtoLL(avgPos) - return routines.tostringLL(lat, lon, acc, DMS) - end +routines.getLLString = function( vars ) + local units = vars.units + local acc = vars.acc or 3 + local DMS = vars.DMS + local avgPos = routines.getAvgPos( units ) + if avgPos then + local lat, lon = coord.LOtoLL( avgPos ) + return routines.tostringLL( lat, lon, acc, DMS ) + end end --[[ @@ -893,22 +857,22 @@ vars.ref - vec3 ref point, maybe overload for vec2 as well? vars.alt - boolean, if used, includes altitude in string vars.metric - boolean, gives distance in km instead of NM. ]] -routines.getBRStringZone = function(vars) - local zone = trigger.misc.getZone( vars.zone ) - local ref = routines.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already. - local alt = vars.alt - local metric = vars.metric - if zone then - local vec = {x = zone.point.x - ref.x, y = zone.point.y - ref.y, z = zone.point.z - ref.z} - local dir = routines.utils.getDir(vec, ref) - local dist = routines.utils.get2DDist(zone.point, ref) - if alt then - alt = zone.y - end - return routines.tostringBR(dir, dist, alt, metric) - else - env.info( 'routines.getBRStringZone: error: zone is nil' ) - end +routines.getBRStringZone = function( vars ) + local zone = trigger.misc.getZone( vars.zone ) + local ref = routines.utils.makeVec3( vars.ref, 0 ) -- turn it into Vec3 if it is not already. + local alt = vars.alt + local metric = vars.metric + if zone then + local vec = { x = zone.point.x - ref.x, y = zone.point.y - ref.y, z = zone.point.z - ref.z } + local dir = routines.utils.getDir( vec, ref ) + local dist = routines.utils.get2DDist( zone.point, ref ) + if alt then + alt = zone.y + end + return routines.tostringBR( dir, dist, alt, metric ) + else + env.info( 'routines.getBRStringZone: error: zone is nil' ) + end end --[[ @@ -917,24 +881,23 @@ vars.ref - vec3 ref point, maybe overload for vec2 as well? vars.alt - boolean, if used, includes altitude in string vars.metric - boolean, gives distance in km instead of NM. ]] -routines.getBRString = function(vars) - local units = vars.units - local ref = routines.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already. - local alt = vars.alt - local metric = vars.metric - local avgPos = routines.getAvgPos(units) - if avgPos then - local vec = {x = avgPos.x - ref.x, y = avgPos.y - ref.y, z = avgPos.z - ref.z} - local dir = routines.utils.getDir(vec, ref) - local dist = routines.utils.get2DDist(avgPos, ref) - if alt then - alt = avgPos.y - end - return routines.tostringBR(dir, dist, alt, metric) - end +routines.getBRString = function( vars ) + local units = vars.units + local ref = routines.utils.makeVec3( vars.ref, 0 ) -- turn it into Vec3 if it is not already. + local alt = vars.alt + local metric = vars.metric + local avgPos = routines.getAvgPos( units ) + if avgPos then + local vec = { x = avgPos.x - ref.x, y = avgPos.y - ref.y, z = avgPos.z - ref.z } + local dir = routines.utils.getDir( vec, ref ) + local dist = routines.utils.get2DDist( avgPos, ref ) + if alt then + alt = avgPos.y + end + return routines.tostringBR( dir, dist, alt, metric ) + end end - -- Returns the Vec3 coordinates of the average position of the concentration of units most in the heading direction. --[[ vars for routines.getLeadingPos: vars.units - table of unit names @@ -942,57 +905,56 @@ vars.heading - direction vars.radius - number vars.headingDegrees - boolean, switches heading to degrees ]] -routines.getLeadingPos = function(vars) - local units = vars.units - local heading = vars.heading - local radius = vars.radius - if vars.headingDegrees then - heading = routines.utils.toRadian(vars.headingDegrees) - end +routines.getLeadingPos = function( vars ) + local units = vars.units + local heading = vars.heading + local radius = vars.radius + if vars.headingDegrees then + heading = routines.utils.toRadian( vars.headingDegrees ) + end - local unitPosTbl = {} - for i = 1, #units do - local unit = Unit.getByName(units[i]) - if unit and unit:isExist() then - unitPosTbl[#unitPosTbl + 1] = unit:getPosition().p - end - end - if #unitPosTbl > 0 then -- one more more units found. - -- first, find the unit most in the heading direction - local maxPos = -math.huge + local unitPosTbl = {} + for i = 1, #units do + local unit = Unit.getByName( units[i] ) + if unit and unit:isExist() then + unitPosTbl[#unitPosTbl + 1] = unit:getPosition().p + end + end + if #unitPosTbl > 0 then -- one more more units found. + -- first, find the unit most in the heading direction + local maxPos = -math.huge - local maxPosInd -- maxPos - the furthest in direction defined by heading; maxPosInd = - for i = 1, #unitPosTbl do - local rotatedVec2 = routines.vec.rotateVec2(routines.utils.makeVec2(unitPosTbl[i]), heading) - if (not maxPos) or maxPos < rotatedVec2.x then - maxPos = rotatedVec2.x - maxPosInd = i - end - end + local maxPosInd -- maxPos - the furthest in direction defined by heading; maxPosInd = + for i = 1, #unitPosTbl do + local rotatedVec2 = routines.vec.rotateVec2( routines.utils.makeVec2( unitPosTbl[i] ), heading ) + if (not maxPos) or maxPos < rotatedVec2.x then + maxPos = rotatedVec2.x + maxPosInd = i + end + end - --now, get all the units around this unit... - local avgPos - if radius then - local maxUnitPos = unitPosTbl[maxPosInd] - local avgx, avgy, avgz, totNum = 0, 0, 0, 0 - for i = 1, #unitPosTbl do - if routines.utils.get2DDist(maxUnitPos, unitPosTbl[i]) <= radius then - avgx = avgx + unitPosTbl[i].x - avgy = avgy + unitPosTbl[i].y - avgz = avgz + unitPosTbl[i].z - totNum = totNum + 1 - end - end - avgPos = { x = avgx/totNum, y = avgy/totNum, z = avgz/totNum} - else - avgPos = unitPosTbl[maxPosInd] - end + -- now, get all the units around this unit... + local avgPos + if radius then + local maxUnitPos = unitPosTbl[maxPosInd] + local avgx, avgy, avgz, totNum = 0, 0, 0, 0 + for i = 1, #unitPosTbl do + if routines.utils.get2DDist( maxUnitPos, unitPosTbl[i] ) <= radius then + avgx = avgx + unitPosTbl[i].x + avgy = avgy + unitPosTbl[i].y + avgz = avgz + unitPosTbl[i].z + totNum = totNum + 1 + end + end + avgPos = { x = avgx / totNum, y = avgy / totNum, z = avgz / totNum } + else + avgPos = unitPosTbl[maxPosInd] + end - return avgPos - end + return avgPos + end end - --[[ vars for routines.getLeadingMGRSString: vars.units - table of unit names vars.heading - direction @@ -1000,12 +962,12 @@ vars.radius - number vars.headingDegrees - boolean, switches heading to degrees vars.acc - number, 0 to 5. ]] -routines.getLeadingMGRSString = function(vars) - local pos = routines.getLeadingPos(vars) - if pos then - local acc = vars.acc or 5 - return routines.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(pos)), acc) - end +routines.getLeadingMGRSString = function( vars ) + local pos = routines.getLeadingPos( vars ) + if pos then + local acc = vars.acc or 5 + return routines.tostringMGRS( coord.LLtoMGRS( coord.LOtoLL( pos ) ), acc ) + end end --[[ vars for routines.getLeadingLLString: @@ -1016,18 +978,16 @@ vars.headingDegrees - boolean, switches heading to degrees vars.acc - number of digits after decimal point (can be negative) vars.DMS - boolean, true if you want DMS. ]] -routines.getLeadingLLString = function(vars) - local pos = routines.getLeadingPos(vars) - if pos then - local acc = vars.acc or 3 - local DMS = vars.DMS - local lat, lon = coord.LOtoLL(pos) - return routines.tostringLL(lat, lon, acc, DMS) - end +routines.getLeadingLLString = function( vars ) + local pos = routines.getLeadingPos( vars ) + if pos then + local acc = vars.acc or 3 + local DMS = vars.DMS + local lat, lon = coord.LOtoLL( pos ) + return routines.tostringLL( lat, lon, acc, DMS ) + end end - - --[[ vars for routines.getLeadingBRString: vars.units - table of unit names vars.heading - direction, number @@ -1037,21 +997,21 @@ vars.metric - boolean, if true, use km instead of NM. vars.alt - boolean, if true, include altitude. vars.ref - vec3/vec2 reference point. ]] -routines.getLeadingBRString = function(vars) - local pos = routines.getLeadingPos(vars) - if pos then - local ref = vars.ref - local alt = vars.alt - local metric = vars.metric +routines.getLeadingBRString = function( vars ) + local pos = routines.getLeadingPos( vars ) + if pos then + local ref = vars.ref + local alt = vars.alt + local metric = vars.metric - local vec = {x = pos.x - ref.x, y = pos.y - ref.y, z = pos.z - ref.z} - local dir = routines.utils.getDir(vec, ref) - local dist = routines.utils.get2DDist(pos, ref) - if alt then - alt = pos.y - end - return routines.tostringBR(dir, dist, alt, metric) - end + local vec = { x = pos.x - ref.x, y = pos.y - ref.y, z = pos.z - ref.z } + local dir = routines.utils.getDir( vec, ref ) + local dist = routines.utils.get2DDist( pos, ref ) + if alt then + alt = pos.y + end + return routines.tostringBR( dir, dist, alt, metric ) + end end --[[ vars for routines.message.add @@ -1068,26 +1028,22 @@ vars.text - text in the message vars.displayTime - self explanatory vars.msgFor - scope ]] -routines.msgMGRS = function(vars) - local units = vars.units - local acc = vars.acc - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor +routines.msgMGRS = function( vars ) + local units = vars.units + local acc = vars.acc + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor - local s = routines.getMGRSString{units = units, acc = acc} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end + local s = routines.getMGRSString { units = units, acc = acc } + local newText + if string.find( text, '%%s' ) then -- look for %s + newText = string.format( text, s ) -- insert the coordinates into the message + else -- else, just append to the end. + newText = text .. s + end - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + routines.message.add { text = newText, displayTime = displayTime, msgFor = msgFor } end --[[ vars for routines.msgLL @@ -1098,31 +1054,26 @@ vars.text - text in the message vars.displayTime - self explanatory vars.msgFor - scope ]] -routines.msgLL = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local acc = vars.acc - local DMS = vars.DMS - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor +routines.msgLL = function( vars ) + local units = vars.units -- technically, I don't really need to do this, but it helps readability. + local acc = vars.acc + local DMS = vars.DMS + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor - local s = routines.getLLString{units = units, acc = acc, DMS = DMS} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end + local s = routines.getLLString { units = units, acc = acc, DMS = DMS } + local newText + if string.find( text, '%%s' ) then -- look for %s + newText = string.format( text, s ) -- insert the coordinates into the message + else -- else, just append to the end. + newText = text .. s + end - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + routines.message.add { text = newText, displayTime = displayTime, msgFor = msgFor } end - --[[ vars.units- table of unit names (NOT unitNameTable- maybe this should change). vars.ref - vec3 ref point, maybe overload for vec2 as well? @@ -1132,32 +1083,27 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -routines.msgBR = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local ref = vars.ref -- vec2/vec3 will be handled in routines.getBRString - local alt = vars.alt - local metric = vars.metric - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor +routines.msgBR = function( vars ) + local units = vars.units -- technically, I don't really need to do this, but it helps readability. + local ref = vars.ref -- vec2/vec3 will be handled in routines.getBRString + local alt = vars.alt + local metric = vars.metric + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor - local s = routines.getBRString{units = units, ref = ref, alt = alt, metric = metric} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end + local s = routines.getBRString { units = units, ref = ref, alt = alt, metric = metric } + local newText + if string.find( text, '%%s' ) then -- look for %s + newText = string.format( text, s ) -- insert the coordinates into the message + else -- else, just append to the end. + newText = text .. s + end - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + routines.message.add { text = newText, displayTime = displayTime, msgFor = msgFor } end - -------------------------------------------------------------------------------------------- -- basically, just sub-types of routines.msgBR... saves folks the work of getting the ref point. --[[ @@ -1169,14 +1115,14 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -routines.msgBullseye = function(vars) - if string.lower(vars.ref) == 'red' then - vars.ref = routines.DBs.missionData.bullseye.red - routines.msgBR(vars) - elseif string.lower(vars.ref) == 'blue' then - vars.ref = routines.DBs.missionData.bullseye.blue - routines.msgBR(vars) - end +routines.msgBullseye = function( vars ) + if string.lower( vars.ref ) == 'red' then + vars.ref = routines.DBs.missionData.bullseye.red + routines.msgBR( vars ) + elseif string.lower( vars.ref ) == 'blue' then + vars.ref = routines.DBs.missionData.bullseye.blue + routines.msgBR( vars ) + end end --[[ @@ -1189,14 +1135,14 @@ vars.displayTime vars.msgFor - scope ]] -routines.msgBRA = function(vars) - if Unit.getByName(vars.ref) then - vars.ref = Unit.getByName(vars.ref):getPosition().p - if not vars.alt then - vars.alt = true - end - routines.msgBR(vars) - end +routines.msgBRA = function( vars ) + if Unit.getByName( vars.ref ) then + vars.ref = Unit.getByName( vars.ref ):getPosition().p + if not vars.alt then + vars.alt = true + end + routines.msgBR( vars ) + end end -------------------------------------------------------------------------------------------- @@ -1210,32 +1156,27 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -routines.msgLeadingMGRS = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local heading = vars.heading - local radius = vars.radius - local headingDegrees = vars.headingDegrees - local acc = vars.acc - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = routines.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end - - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } +routines.msgLeadingMGRS = function( vars ) + local units = vars.units -- technically, I don't really need to do this, but it helps readability. + local heading = vars.heading + local radius = vars.radius + local headingDegrees = vars.headingDegrees + local acc = vars.acc + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor + local s = routines.getLeadingMGRSString { units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc } + local newText + if string.find( text, '%%s' ) then -- look for %s + newText = string.format( text, s ) -- insert the coordinates into the message + else -- else, just append to the end. + newText = text .. s + end + routines.message.add { text = newText, displayTime = displayTime, msgFor = msgFor } end + --[[ vars for routines.msgLeadingLL: vars.units - table of unit names vars.heading - direction, number @@ -1247,31 +1188,26 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -routines.msgLeadingLL = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local heading = vars.heading - local radius = vars.radius - local headingDegrees = vars.headingDegrees - local acc = vars.acc - local DMS = vars.DMS - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor +routines.msgLeadingLL = function( vars ) + local units = vars.units -- technically, I don't really need to do this, but it helps readability. + local heading = vars.heading + local radius = vars.radius + local headingDegrees = vars.headingDegrees + local acc = vars.acc + local DMS = vars.DMS + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor - local s = routines.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end - - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + local s = routines.getLeadingLLString { units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS } + local newText + if string.find( text, '%%s' ) then -- look for %s + newText = string.format( text, s ) -- insert the coordinates into the message + else -- else, just append to the end. + newText = text .. s + end + routines.message.add { text = newText, displayTime = displayTime, msgFor = msgFor } end --[[ @@ -1286,137 +1222,93 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -routines.msgLeadingBR = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local heading = vars.heading - local radius = vars.radius - local headingDegrees = vars.headingDegrees - local metric = vars.metric - local alt = vars.alt - local ref = vars.ref -- vec2/vec3 will be handled in routines.getBRString - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor +routines.msgLeadingBR = function( vars ) + local units = vars.units -- technically, I don't really need to do this, but it helps readability. + local heading = vars.heading + local radius = vars.radius + local headingDegrees = vars.headingDegrees + local metric = vars.metric + local alt = vars.alt + local ref = vars.ref -- vec2/vec3 will be handled in routines.getBRString + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor - local s = routines.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end + local s = routines.getLeadingBRString { units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref } + local newText + if string.find( text, '%%s' ) then -- look for %s + newText = string.format( text, s ) -- insert the coordinates into the message + else -- else, just append to the end. + newText = text .. s + end - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + routines.message.add { text = newText, displayTime = displayTime, msgFor = msgFor } end +function spairs( t, order ) + -- collect the keys + local keys = {} + for k in pairs( t ) do + keys[#keys + 1] = k + end -function spairs(t, order) - -- collect the keys - local keys = {} - for k in pairs(t) do keys[#keys+1] = k end + -- if order function given, sort by it by passing the table and keys a, b, + -- otherwise just sort the keys + if order then + table.sort( keys, function( a, b ) + return order( t, a, b ) + end ) + else + table.sort( keys ) + end - -- if order function given, sort by it by passing the table and keys a, b, - -- otherwise just sort the keys - if order then - table.sort(keys, function(a,b) return order(t, a, b) end) - else - table.sort(keys) - end - - -- return the iterator function - local i = 0 - return function() - i = i + 1 - if keys[i] then - return keys[i], t[keys[i]] - end + -- return the iterator function + local i = 0 + return function() + i = i + 1 + if keys[i] then + return keys[i], t[keys[i]] end + end end - function routines.IsPartOfGroupInZones( CargoGroup, LandingZones ) ---trace.f() + -- trace.f() - local CurrentZoneID = nil + local CurrentZoneID = nil - if CargoGroup then - local CargoUnits = CargoGroup:getUnits() - for CargoUnitID, CargoUnit in pairs( CargoUnits ) do - if CargoUnit and CargoUnit:getLife() >= 1.0 then - CurrentZoneID = routines.IsUnitInZones( CargoUnit, LandingZones ) - if CurrentZoneID then - break - end - end - end - end + if CargoGroup then + local CargoUnits = CargoGroup:getUnits() + for CargoUnitID, CargoUnit in pairs( CargoUnits ) do + if CargoUnit and CargoUnit:getLife() >= 1.0 then + CurrentZoneID = routines.IsUnitInZones( CargoUnit, LandingZones ) + if CurrentZoneID then + break + end + end + end + end ---trace.r( "", "", { CurrentZoneID } ) - return CurrentZoneID + -- trace.r( "", "", { CurrentZoneID } ) + return CurrentZoneID end - - function routines.IsUnitInZones( TransportUnit, LandingZones ) ---trace.f("", "routines.IsUnitInZones" ) - - local TransportZoneResult = nil - local TransportZonePos = nil - local TransportZone = nil - - -- fill-up some local variables to support further calculations to determine location of units within the zone. - if TransportUnit then - local TransportUnitPos = TransportUnit:getPosition().p - if type( LandingZones ) == "table" then - for LandingZoneID, LandingZoneName in pairs( LandingZones ) do - TransportZone = trigger.misc.getZone( LandingZoneName ) - if TransportZone then - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportUnitPos.x - TransportZonePos.x)^2 + (TransportUnitPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then - TransportZoneResult = LandingZoneID - break - end - end - end - else - TransportZone = trigger.misc.getZone( LandingZones ) - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportUnitPos.x - TransportZonePos.x)^2 + (TransportUnitPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then - TransportZoneResult = 1 - end - end - if TransportZoneResult then - --trace.i( "routines", "TransportZone:" .. TransportZoneResult ) - else - --trace.i( "routines", "TransportZone:nil logic" ) - end - return TransportZoneResult - else - --trace.i( "routines", "TransportZone:nil hard" ) - return nil - end -end - -function routines.IsUnitNearZonesRadius( TransportUnit, LandingZones, ZoneRadius ) ---trace.f("", "routines.IsUnitInZones" ) + -- trace.f("", "routines.IsUnitInZones" ) local TransportZoneResult = nil local TransportZonePos = nil local TransportZone = nil - -- fill-up some local variables to support further calculations to determine location of units within the zone. + -- fill-up some local variables to support further calculations to determine location of units within the zone. if TransportUnit then local TransportUnitPos = TransportUnit:getPosition().p if type( LandingZones ) == "table" then for LandingZoneID, LandingZoneName in pairs( LandingZones ) do TransportZone = trigger.misc.getZone( LandingZoneName ) if TransportZone then - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportUnitPos.x - TransportZonePos.x)^2 + (TransportUnitPos.z - TransportZonePos.z)^2)^0.5 <= ZoneRadius ) then + TransportZonePos = { radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z } + if (((TransportUnitPos.x - TransportZonePos.x) ^ 2 + (TransportUnitPos.z - TransportZonePos.z) ^ 2) ^ 0.5 <= TransportZonePos.radius) then TransportZoneResult = LandingZoneID break end @@ -1424,59 +1316,97 @@ function routines.IsUnitNearZonesRadius( TransportUnit, LandingZones, ZoneRadius end else TransportZone = trigger.misc.getZone( LandingZones ) - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportUnitPos.x - TransportZonePos.x)^2 + (TransportUnitPos.z - TransportZonePos.z)^2)^0.5 <= ZoneRadius ) then + TransportZonePos = { radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z } + if (((TransportUnitPos.x - TransportZonePos.x) ^ 2 + (TransportUnitPos.z - TransportZonePos.z) ^ 2) ^ 0.5 <= TransportZonePos.radius) then TransportZoneResult = 1 end end if TransportZoneResult then - --trace.i( "routines", "TransportZone:" .. TransportZoneResult ) + -- trace.i( "routines", "TransportZone:" .. TransportZoneResult ) else - --trace.i( "routines", "TransportZone:nil logic" ) + -- trace.i( "routines", "TransportZone:nil logic" ) end return TransportZoneResult else - --trace.i( "routines", "TransportZone:nil hard" ) + -- trace.i( "routines", "TransportZone:nil hard" ) return nil end end +function routines.IsUnitNearZonesRadius( TransportUnit, LandingZones, ZoneRadius ) + -- trace.f("", "routines.IsUnitInZones" ) -function routines.IsStaticInZones( TransportStatic, LandingZones ) ---trace.f() + local TransportZoneResult = nil + local TransportZonePos = nil + local TransportZone = nil - local TransportZoneResult = nil - local TransportZonePos = nil - local TransportZone = nil - - -- fill-up some local variables to support further calculations to determine location of units within the zone. - local TransportStaticPos = TransportStatic:getPosition().p - if type( LandingZones ) == "table" then - for LandingZoneID, LandingZoneName in pairs( LandingZones ) do - TransportZone = trigger.misc.getZone( LandingZoneName ) - if TransportZone then - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportStaticPos.x - TransportZonePos.x)^2 + (TransportStaticPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then - TransportZoneResult = LandingZoneID - break - end - end - end - else - TransportZone = trigger.misc.getZone( LandingZones ) - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportStaticPos.x - TransportZonePos.x)^2 + (TransportStaticPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then - TransportZoneResult = 1 - end - end - ---trace.r( "", "", { TransportZoneResult } ) + -- fill-up some local variables to support further calculations to determine location of units within the zone. + if TransportUnit then + local TransportUnitPos = TransportUnit:getPosition().p + if type( LandingZones ) == "table" then + for LandingZoneID, LandingZoneName in pairs( LandingZones ) do + TransportZone = trigger.misc.getZone( LandingZoneName ) + if TransportZone then + TransportZonePos = { radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z } + if (((TransportUnitPos.x - TransportZonePos.x) ^ 2 + (TransportUnitPos.z - TransportZonePos.z) ^ 2) ^ 0.5 <= ZoneRadius) then + TransportZoneResult = LandingZoneID + break + end + end + end + else + TransportZone = trigger.misc.getZone( LandingZones ) + TransportZonePos = { radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z } + if (((TransportUnitPos.x - TransportZonePos.x) ^ 2 + (TransportUnitPos.z - TransportZonePos.z) ^ 2) ^ 0.5 <= ZoneRadius) then + TransportZoneResult = 1 + end + end + if TransportZoneResult then + -- trace.i( "routines", "TransportZone:" .. TransportZoneResult ) + else + -- trace.i( "routines", "TransportZone:nil logic" ) + end return TransportZoneResult + else + -- trace.i( "routines", "TransportZone:nil hard" ) + return nil + end end +function routines.IsStaticInZones( TransportStatic, LandingZones ) + -- trace.f() + + local TransportZoneResult = nil + local TransportZonePos = nil + local TransportZone = nil + + -- fill-up some local variables to support further calculations to determine location of units within the zone. + local TransportStaticPos = TransportStatic:getPosition().p + if type( LandingZones ) == "table" then + for LandingZoneID, LandingZoneName in pairs( LandingZones ) do + TransportZone = trigger.misc.getZone( LandingZoneName ) + if TransportZone then + TransportZonePos = { radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z } + if (((TransportStaticPos.x - TransportZonePos.x) ^ 2 + (TransportStaticPos.z - TransportZonePos.z) ^ 2) ^ 0.5 <= TransportZonePos.radius) then + TransportZoneResult = LandingZoneID + break + end + end + end + else + TransportZone = trigger.misc.getZone( LandingZones ) + TransportZonePos = { radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z } + if (((TransportStaticPos.x - TransportZonePos.x) ^ 2 + (TransportStaticPos.z - TransportZonePos.z) ^ 2) ^ 0.5 <= TransportZonePos.radius) then + TransportZoneResult = 1 + end + end + + -- trace.r( "", "", { TransportZoneResult } ) + return TransportZoneResult +end function routines.IsUnitInRadius( CargoUnit, ReferencePosition, Radius ) ---trace.f() + -- trace.f() local Valid = true @@ -1484,7 +1414,7 @@ function routines.IsUnitInRadius( CargoUnit, ReferencePosition, Radius ) local CargoPos = CargoUnit:getPosition().p local ReferenceP = ReferencePosition.p - if (((CargoPos.x - ReferenceP.x)^2 + (CargoPos.z - ReferenceP.z)^2)^0.5 <= Radius) then + if (((CargoPos.x - ReferenceP.x) ^ 2 + (CargoPos.z - ReferenceP.z) ^ 2) ^ 0.5 <= Radius) then else Valid = false end @@ -1493,7 +1423,7 @@ function routines.IsUnitInRadius( CargoUnit, ReferencePosition, Radius ) end function routines.IsPartOfGroupInRadius( CargoGroup, ReferencePosition, Radius ) ---trace.f() + -- trace.f() local Valid = true @@ -1503,11 +1433,11 @@ function routines.IsPartOfGroupInRadius( CargoGroup, ReferencePosition, Radius ) local CargoUnits = CargoGroup:getUnits() for CargoUnitId, CargoUnit in pairs( CargoUnits ) do local CargoUnitPos = CargoUnit:getPosition().p --- env.info( 'routines.IsPartOfGroupInRadius: CargoUnitPos.x = ' .. CargoUnitPos.x .. ' CargoUnitPos.z = ' .. CargoUnitPos.z ) + -- env.info( 'routines.IsPartOfGroupInRadius: CargoUnitPos.x = ' .. CargoUnitPos.x .. ' CargoUnitPos.z = ' .. CargoUnitPos.z ) local ReferenceP = ReferencePosition.p --- env.info( 'routines.IsPartOfGroupInRadius: ReferenceGroupPos.x = ' .. ReferenceGroupPos.x .. ' ReferenceGroupPos.z = ' .. ReferenceGroupPos.z ) + -- env.info( 'routines.IsPartOfGroupInRadius: ReferenceGroupPos.x = ' .. ReferenceGroupPos.x .. ' ReferenceGroupPos.z = ' .. ReferenceGroupPos.z ) - if ((( CargoUnitPos.x - ReferenceP.x)^2 + (CargoUnitPos.z - ReferenceP.z)^2)^0.5 <= Radius) then + if (((CargoUnitPos.x - ReferenceP.x) ^ 2 + (CargoUnitPos.z - ReferenceP.z) ^ 2) ^ 0.5 <= Radius) then else Valid = false break @@ -1517,11 +1447,10 @@ function routines.IsPartOfGroupInRadius( CargoGroup, ReferencePosition, Radius ) return Valid end - function routines.ValidateString( Variable, VariableName, Valid ) ---trace.f() + -- trace.f() - if type( Variable ) == "string" then + if type( Variable ) == "string" then if Variable == "" then error( "routines.ValidateString: error: " .. VariableName .. " must be filled out!" ) Valid = false @@ -1531,65 +1460,64 @@ function routines.ValidateString( Variable, VariableName, Valid ) Valid = false end ---trace.r( "", "", { Valid } ) + -- trace.r( "", "", { Valid } ) return Valid end function routines.ValidateNumber( Variable, VariableName, Valid ) ---trace.f() + -- trace.f() - if type( Variable ) == "number" then + if type( Variable ) == "number" then else error( "routines.ValidateNumber: error: " .. VariableName .. " is not a number." ) Valid = false end ---trace.r( "", "", { Valid } ) + -- trace.r( "", "", { Valid } ) return Valid - end function routines.ValidateGroup( Variable, VariableName, Valid ) ---trace.f() + -- trace.f() - if Variable == nil then - error( "routines.ValidateGroup: error: " .. VariableName .. " is a nil value!" ) - Valid = false - end + if Variable == nil then + error( "routines.ValidateGroup: error: " .. VariableName .. " is a nil value!" ) + Valid = false + end ---trace.r( "", "", { Valid } ) - return Valid + -- trace.r( "", "", { Valid } ) + return Valid end function routines.ValidateZone( LandingZones, VariableName, Valid ) ---trace.f() + -- trace.f() - if LandingZones == nil then - error( "routines.ValidateGroup: error: " .. VariableName .. " is a nil value!" ) - Valid = false - end + if LandingZones == nil then + error( "routines.ValidateGroup: error: " .. VariableName .. " is a nil value!" ) + Valid = false + end - if type( LandingZones ) == "table" then - for LandingZoneID, LandingZoneName in pairs( LandingZones ) do - if trigger.misc.getZone( LandingZoneName ) == nil then - error( "routines.ValidateGroup: error: Zone " .. LandingZoneName .. " does not exist!" ) - Valid = false - break - end - end - else - if trigger.misc.getZone( LandingZones ) == nil then - error( "routines.ValidateGroup: error: Zone " .. LandingZones .. " does not exist!" ) - Valid = false - end - end + if type( LandingZones ) == "table" then + for LandingZoneID, LandingZoneName in pairs( LandingZones ) do + if trigger.misc.getZone( LandingZoneName ) == nil then + error( "routines.ValidateGroup: error: Zone " .. LandingZoneName .. " does not exist!" ) + Valid = false + break + end + end + else + if trigger.misc.getZone( LandingZones ) == nil then + error( "routines.ValidateGroup: error: Zone " .. LandingZones .. " does not exist!" ) + Valid = false + end + end ---trace.r( "", "", { Valid } ) - return Valid + -- trace.r( "", "", { Valid } ) + return Valid end function routines.ValidateEnumeration( Variable, VariableName, Enum, Valid ) ---trace.f() + -- trace.f() local ValidVariable = false @@ -1600,214 +1528,191 @@ function routines.ValidateEnumeration( Variable, VariableName, Enum, Valid ) end end - if ValidVariable then + if ValidVariable then else error( 'TransportValidateEnum: " .. VariableName .. " is not a valid type.' .. Variable ) Valid = false end ---trace.r( "", "", { Valid } ) + -- trace.r( "", "", { Valid } ) return Valid end -function routines.getGroupRoute(groupIdent, task) -- same as getGroupPoints but returns speed and formation type along with vec2 of point} - -- refactor to search by groupId and allow groupId and groupName as inputs - local gpId = groupIdent - if type(groupIdent) == 'string' and not tonumber(groupIdent) then - gpId = _DATABASE.Templates.Groups[groupIdent].groupId - end - - for coa_name, coa_data in pairs(env.mission.coalition) do - if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - for obj_type_name, obj_type_data in pairs(cntry_data) do - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - for group_num, group_data in pairs(obj_type_data.group) do - if group_data and group_data.groupId == gpId then -- this is the group we are looking for - if group_data.route and group_data.route.points and #group_data.route.points > 0 then - local points = {} - - for point_num, point in pairs(group_data.route.points) do - local routeData = {} - if env.mission.version > 7 then - routeData.name = env.getValueDictByKey(point.name) - else - routeData.name = point.name - end - if not point.point then - routeData.x = point.x - routeData.y = point.y - else - routeData.point = point.point --it's possible that the ME could move to the point = Vec2 notation. - end - routeData.form = point.action - routeData.speed = point.speed - routeData.alt = point.alt - routeData.alt_type = point.alt_type - routeData.airdromeId = point.airdromeId - routeData.helipadId = point.helipadId - routeData.type = point.type - routeData.action = point.action - if task then - routeData.task = point.task - end - points[point_num] = routeData - end - - return points - end - return - end --if group_data and group_data.name and group_data.name == 'groupname' - end --for group_num, group_data in pairs(obj_type_data.group) do - end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then - end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then - end --for obj_type_name, obj_type_data in pairs(cntry_data) do - end --for cntry_id, cntry_data in pairs(coa_data.country) do - end --if coa_data.country then --there is a country table - end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then - end --for coa_name, coa_data in pairs(mission.coalition) do +function routines.getGroupRoute( groupIdent, task ) -- same as getGroupPoints but returns speed and formation type along with vec2 of point} + -- refactor to search by groupId and allow groupId and groupName as inputs + local gpId = groupIdent + if type( groupIdent ) == 'string' and not tonumber( groupIdent ) then + gpId = _DATABASE.Templates.Groups[groupIdent].groupId + end + + for coa_name, coa_data in pairs( env.mission.coalition ) do + if (coa_name == 'red' or coa_name == 'blue') and type( coa_data ) == 'table' then + if coa_data.country then -- there is a country table + for cntry_id, cntry_data in pairs( coa_data.country ) do + for obj_type_name, obj_type_data in pairs( cntry_data ) do + if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points + if ((type( obj_type_data ) == 'table') and obj_type_data.group and (type( obj_type_data.group ) == 'table') and (#obj_type_data.group > 0)) then -- there's a group! + for group_num, group_data in pairs( obj_type_data.group ) do + if group_data and group_data.groupId == gpId then -- this is the group we are looking for + if group_data.route and group_data.route.points and #group_data.route.points > 0 then + local points = {} + + for point_num, point in pairs( group_data.route.points ) do + local routeData = {} + if env.mission.version > 7 then + routeData.name = env.getValueDictByKey( point.name ) + else + routeData.name = point.name + end + if not point.point then + routeData.x = point.x + routeData.y = point.y + else + routeData.point = point.point -- it's possible that the ME could move to the point = Vec2 notation. + end + routeData.form = point.action + routeData.speed = point.speed + routeData.alt = point.alt + routeData.alt_type = point.alt_type + routeData.airdromeId = point.airdromeId + routeData.helipadId = point.helipadId + routeData.type = point.type + routeData.action = point.action + if task then + routeData.task = point.task + end + points[point_num] = routeData + end + + return points + end + return + end -- if group_data and group_data.name and group_data.name == 'groupname' + end -- for group_num, group_data in pairs(obj_type_data.group) do + end -- if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then + end -- if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then + end -- for obj_type_name, obj_type_data in pairs(cntry_data) do + end -- for cntry_id, cntry_data in pairs(coa_data.country) do + end -- if coa_data.country then --there is a country table + end -- if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then + end -- for coa_name, coa_data in pairs(mission.coalition) do end -routines.ground.patrolRoute = function(vars) - - - local tempRoute = {} - local useRoute = {} - local gpData = vars.gpData - if type(gpData) == 'string' then - gpData = Group.getByName(gpData) - end - - local useGroupRoute - if not vars.useGroupRoute then - useGroupRoute = vars.gpData - else - useGroupRoute = vars.useGroupRoute - end - local routeProvided = false - if not vars.route then - if useGroupRoute then - tempRoute = routines.getGroupRoute(useGroupRoute) - end - else - useRoute = vars.route - local posStart = routines.getLeadPos(gpData) - useRoute[1] = routines.ground.buildWP(posStart, useRoute[1].action, useRoute[1].speed) - routeProvided = true - end - - - local overRideSpeed = vars.speed or 'default' - local pType = vars.pType - local offRoadForm = vars.offRoadForm or 'default' - local onRoadForm = vars.onRoadForm or 'default' - - if routeProvided == false and #tempRoute > 0 then - local posStart = routines.getLeadPos(gpData) - - - useRoute[#useRoute + 1] = routines.ground.buildWP(posStart, offRoadForm, overRideSpeed) - for i = 1, #tempRoute do - local tempForm = tempRoute[i].action - local tempSpeed = tempRoute[i].speed - - if offRoadForm == 'default' then - tempForm = tempRoute[i].action - end - if onRoadForm == 'default' then - onRoadForm = 'On Road' - end - if (string.lower(tempRoute[i].action) == 'on road' or string.lower(tempRoute[i].action) == 'onroad' or string.lower(tempRoute[i].action) == 'on_road') then - tempForm = onRoadForm - else - tempForm = offRoadForm - end - - if type(overRideSpeed) == 'number' then - tempSpeed = overRideSpeed - end - - - useRoute[#useRoute + 1] = routines.ground.buildWP(tempRoute[i], tempForm, tempSpeed) - end - - if pType and string.lower(pType) == 'doubleback' then - local curRoute = routines.utils.deepCopy(useRoute) - for i = #curRoute, 2, -1 do - useRoute[#useRoute + 1] = routines.ground.buildWP(curRoute[i], curRoute[i].action, curRoute[i].speed) - end - end - - useRoute[1].action = useRoute[#useRoute].action -- make it so the first WP matches the last WP - end - - local cTask3 = {} - local newPatrol = {} - newPatrol.route = useRoute - newPatrol.gpData = gpData:getName() - cTask3[#cTask3 + 1] = 'routines.ground.patrolRoute(' - cTask3[#cTask3 + 1] = routines.utils.oneLineSerialize(newPatrol) - cTask3[#cTask3 + 1] = ')' - cTask3 = table.concat(cTask3) - local tempTask = { - id = 'WrappedAction', - params = { - action = { - id = 'Script', - params = { - command = cTask3, - - }, - }, - }, - } +routines.ground.patrolRoute = function( vars ) - - useRoute[#useRoute].task = tempTask - routines.goRoute(gpData, useRoute) - - return + local tempRoute = {} + local useRoute = {} + local gpData = vars.gpData + if type( gpData ) == 'string' then + gpData = Group.getByName( gpData ) + end + + local useGroupRoute + if not vars.useGroupRoute then + useGroupRoute = vars.gpData + else + useGroupRoute = vars.useGroupRoute + end + local routeProvided = false + if not vars.route then + if useGroupRoute then + tempRoute = routines.getGroupRoute( useGroupRoute ) + end + else + useRoute = vars.route + local posStart = routines.getLeadPos( gpData ) + useRoute[1] = routines.ground.buildWP( posStart, useRoute[1].action, useRoute[1].speed ) + routeProvided = true + end + + local overRideSpeed = vars.speed or 'default' + local pType = vars.pType + local offRoadForm = vars.offRoadForm or 'default' + local onRoadForm = vars.onRoadForm or 'default' + + if routeProvided == false and #tempRoute > 0 then + local posStart = routines.getLeadPos( gpData ) + + useRoute[#useRoute + 1] = routines.ground.buildWP( posStart, offRoadForm, overRideSpeed ) + for i = 1, #tempRoute do + local tempForm = tempRoute[i].action + local tempSpeed = tempRoute[i].speed + + if offRoadForm == 'default' then + tempForm = tempRoute[i].action + end + if onRoadForm == 'default' then + onRoadForm = 'On Road' + end + if (string.lower( tempRoute[i].action ) == 'on road' or string.lower( tempRoute[i].action ) == 'onroad' or string.lower( tempRoute[i].action ) == 'on_road') then + tempForm = onRoadForm + else + tempForm = offRoadForm + end + + if type( overRideSpeed ) == 'number' then + tempSpeed = overRideSpeed + end + + useRoute[#useRoute + 1] = routines.ground.buildWP( tempRoute[i], tempForm, tempSpeed ) + end + + if pType and string.lower( pType ) == 'doubleback' then + local curRoute = routines.utils.deepCopy( useRoute ) + for i = #curRoute, 2, -1 do + useRoute[#useRoute + 1] = routines.ground.buildWP( curRoute[i], curRoute[i].action, curRoute[i].speed ) + end + end + + useRoute[1].action = useRoute[#useRoute].action -- make it so the first WP matches the last WP + end + + local cTask3 = {} + local newPatrol = {} + newPatrol.route = useRoute + newPatrol.gpData = gpData:getName() + cTask3[#cTask3 + 1] = 'routines.ground.patrolRoute(' + cTask3[#cTask3 + 1] = routines.utils.oneLineSerialize( newPatrol ) + cTask3[#cTask3 + 1] = ')' + cTask3 = table.concat( cTask3 ) + local tempTask = { id = 'WrappedAction', params = { action = { id = 'Script', params = { command = cTask3 } } } } + + useRoute[#useRoute].task = tempTask + routines.goRoute( gpData, useRoute ) end -routines.ground.patrol = function(gpData, pType, form, speed) - local vars = {} - - if type(gpData) == 'table' and gpData:getName() then - gpData = gpData:getName() - end - - vars.useGroupRoute = gpData - vars.gpData = gpData - vars.pType = pType - vars.offRoadForm = form - vars.speed = speed - - routines.ground.patrolRoute(vars) +routines.ground.patrol = function( gpData, pType, form, speed ) + local vars = {} - return + if type( gpData ) == 'table' and gpData:getName() then + gpData = gpData:getName() + end + + vars.useGroupRoute = gpData + vars.gpData = gpData + vars.pType = pType + vars.offRoadForm = form + vars.speed = speed + + routines.ground.patrolRoute( vars ) end function routines.GetUnitHeight( CheckUnit ) ---trace.f( "routines" ) + -- trace.f( "routines" ) - local UnitPoint = CheckUnit:getPoint() - local UnitPosition = { x = UnitPoint.x, y = UnitPoint.z } - local UnitHeight = UnitPoint.y + local UnitPoint = CheckUnit:getPoint() + local UnitPosition = { x = UnitPoint.x, y = UnitPoint.z } + local UnitHeight = UnitPoint.y - local LandHeight = land.getHeight( UnitPosition ) + local LandHeight = land.getHeight( UnitPosition ) - --env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) + -- env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) - --trace.f( "routines", "Unit Height = " .. UnitHeight - LandHeight ) - - return UnitHeight - LandHeight + -- trace.f( "routines", "Unit Height = " .. UnitHeight - LandHeight ) + return UnitHeight - LandHeight end - - Su34Status = { status = {} } boardMsgRed = { statusMsg = "" } boardMsgAll = { timeMsg = "" } @@ -1815,688 +1720,626 @@ SpawnSettings = {} Su34MenuPath = {} Su34Menus = 0 - -function Su34AttackCarlVinson(groupName) ---trace.menu("", "Su34AttackCarlVinson") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34.getController(groupSu34) - local groupCarlVinson = Group.getByName("US Carl Vinson #001") - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) - if groupCarlVinson ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupCarlVinson:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true}}) - end - Su34Status.status[groupName] = 1 - MessageToRed( string.format('%s: ',groupName) .. 'Attacking carrier Carl Vinson. ', 10, 'RedStatus' .. groupName ) +function Su34AttackCarlVinson( groupName ) + -- trace.menu("", "Su34AttackCarlVinson") + local groupSu34 = Group.getByName( groupName ) + local controllerSu34 = groupSu34.getController( groupSu34 ) + local groupCarlVinson = Group.getByName( "US Carl Vinson #001" ) + controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) + controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) + if groupCarlVinson ~= nil then + controllerSu34.pushTask( controllerSu34, { id = 'AttackGroup', params = { groupId = groupCarlVinson:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true } } ) + end + Su34Status.status[groupName] = 1 + MessageToRed( string.format( '%s: ', groupName ) .. 'Attacking carrier Carl Vinson. ', 10, 'RedStatus' .. groupName ) end -function Su34AttackWest(groupName) ---trace.f("","Su34AttackWest") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34.getController(groupSu34) - local groupShipWest1 = Group.getByName("US Ship West #001") - local groupShipWest2 = Group.getByName("US Ship West #002") - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) - if groupShipWest1 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipWest1:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true}}) - end - if groupShipWest2 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipWest2:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true}}) - end - Su34Status.status[groupName] = 2 - MessageToRed( string.format('%s: ',groupName) .. 'Attacking invading ships in the west. ', 10, 'RedStatus' .. groupName ) +function Su34AttackWest( groupName ) + -- trace.f("","Su34AttackWest") + local groupSu34 = Group.getByName( groupName ) + local controllerSu34 = groupSu34.getController( groupSu34 ) + local groupShipWest1 = Group.getByName( "US Ship West #001" ) + local groupShipWest2 = Group.getByName( "US Ship West #002" ) + controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) + controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) + if groupShipWest1 ~= nil then + controllerSu34.pushTask( controllerSu34, { id = 'AttackGroup', params = { groupId = groupShipWest1:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true } } ) + end + if groupShipWest2 ~= nil then + controllerSu34.pushTask( controllerSu34, { id = 'AttackGroup', params = { groupId = groupShipWest2:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true } } ) + end + Su34Status.status[groupName] = 2 + MessageToRed( string.format( '%s: ', groupName ) .. 'Attacking invading ships in the west. ', 10, 'RedStatus' .. groupName ) end -function Su34AttackNorth(groupName) ---trace.menu("","Su34AttackNorth") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34.getController(groupSu34) - local groupShipNorth1 = Group.getByName("US Ship North #001") - local groupShipNorth2 = Group.getByName("US Ship North #002") - local groupShipNorth3 = Group.getByName("US Ship North #003") - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) - if groupShipNorth1 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipNorth1:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false}}) - end - if groupShipNorth2 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipNorth2:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false}}) - end - if groupShipNorth3 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipNorth3:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false}}) - end - Su34Status.status[groupName] = 3 - MessageToRed( string.format('%s: ',groupName) .. 'Attacking invading ships in the north. ', 10, 'RedStatus' .. groupName ) +function Su34AttackNorth( groupName ) + -- trace.menu("","Su34AttackNorth") + local groupSu34 = Group.getByName( groupName ) + local controllerSu34 = groupSu34.getController( groupSu34 ) + local groupShipNorth1 = Group.getByName( "US Ship North #001" ) + local groupShipNorth2 = Group.getByName( "US Ship North #002" ) + local groupShipNorth3 = Group.getByName( "US Ship North #003" ) + controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) + controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) + if groupShipNorth1 ~= nil then + controllerSu34.pushTask( controllerSu34, { id = 'AttackGroup', params = { groupId = groupShipNorth1:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false } } ) + end + if groupShipNorth2 ~= nil then + controllerSu34.pushTask( controllerSu34, { id = 'AttackGroup', params = { groupId = groupShipNorth2:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false } } ) + end + if groupShipNorth3 ~= nil then + controllerSu34.pushTask( controllerSu34, { id = 'AttackGroup', params = { groupId = groupShipNorth3:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false } } ) + end + Su34Status.status[groupName] = 3 + MessageToRed( string.format( '%s: ', groupName ) .. 'Attacking invading ships in the north. ', 10, 'RedStatus' .. groupName ) end -function Su34Orbit(groupName) ---trace.menu("","Su34Orbit") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34:getController() - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) - controllerSu34:pushTask( {id = 'ControlledTask', params = { task = { id = 'Orbit', params = { pattern = AI.Task.OrbitPattern.RACE_TRACK } }, stopCondition = { duration = 600 } } } ) - Su34Status.status[groupName] = 4 - MessageToRed( string.format('%s: ',groupName) .. 'In orbit and awaiting further instructions. ', 10, 'RedStatus' .. groupName ) +function Su34Orbit( groupName ) + -- trace.menu("","Su34Orbit") + local groupSu34 = Group.getByName( groupName ) + local controllerSu34 = groupSu34:getController() + controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) + controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) + controllerSu34:pushTask( { id = 'ControlledTask', params = { task = { id = 'Orbit', params = { pattern = AI.Task.OrbitPattern.RACE_TRACK } }, stopCondition = { duration = 600 } } } ) + Su34Status.status[groupName] = 4 + MessageToRed( string.format( '%s: ', groupName ) .. 'In orbit and awaiting further instructions. ', 10, 'RedStatus' .. groupName ) end -function Su34TakeOff(groupName) ---trace.menu("","Su34TakeOff") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34:getController() - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) - Su34Status.status[groupName] = 8 - MessageToRed( string.format('%s: ',groupName) .. 'Take-Off. ', 10, 'RedStatus' .. groupName ) +function Su34TakeOff( groupName ) + -- trace.menu("","Su34TakeOff") + local groupSu34 = Group.getByName( groupName ) + local controllerSu34 = groupSu34:getController() + controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) + controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) + Su34Status.status[groupName] = 8 + MessageToRed( string.format( '%s: ', groupName ) .. 'Take-Off. ', 10, 'RedStatus' .. groupName ) end -function Su34Hold(groupName) ---trace.menu("","Su34Hold") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34:getController() - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) - Su34Status.status[groupName] = 5 - MessageToRed( string.format('%s: ',groupName) .. 'Holding Weapons. ', 10, 'RedStatus' .. groupName ) +function Su34Hold( groupName ) + -- trace.menu("","Su34Hold") + local groupSu34 = Group.getByName( groupName ) + local controllerSu34 = groupSu34:getController() + controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) + controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) + Su34Status.status[groupName] = 5 + MessageToRed( string.format( '%s: ', groupName ) .. 'Holding Weapons. ', 10, 'RedStatus' .. groupName ) end -function Su34RTB(groupName) ---trace.menu("","Su34RTB") - Su34Status.status[groupName] = 6 - MessageToRed( string.format('%s: ',groupName) .. 'Return to Krasnodar. ', 10, 'RedStatus' .. groupName ) +function Su34RTB( groupName ) + -- trace.menu("","Su34RTB") + Su34Status.status[groupName] = 6 + MessageToRed( string.format( '%s: ', groupName ) .. 'Return to Krasnodar. ', 10, 'RedStatus' .. groupName ) end -function Su34Destroyed(groupName) ---trace.menu("","Su34Destroyed") - Su34Status.status[groupName] = 7 - MessageToRed( string.format('%s: ',groupName) .. 'Destroyed. ', 30, 'RedStatus' .. groupName ) +function Su34Destroyed( groupName ) + -- trace.menu("","Su34Destroyed") + Su34Status.status[groupName] = 7 + MessageToRed( string.format( '%s: ', groupName ) .. 'Destroyed. ', 30, 'RedStatus' .. groupName ) end function GroupAlive( groupName ) ---trace.menu("","GroupAlive") - local groupTest = Group.getByName( groupName ) + -- trace.menu("","GroupAlive") + local groupTest = Group.getByName( groupName ) - local groupExists = false + local groupExists = false - if groupTest then - groupExists = groupTest:isExist() - end + if groupTest then + groupExists = groupTest:isExist() + end - --trace.r( "", "", { groupExists } ) - return groupExists + -- trace.r( "", "", { groupExists } ) + return groupExists end function Su34IsDead() ---trace.f() + -- trace.f() end function Su34OverviewStatus() ---trace.menu("","Su34OverviewStatus") - local msg = "" - local currentStatus = 0 - local Exists = false + -- trace.menu("","Su34OverviewStatus") + local msg = "" + local currentStatus = 0 + local Exists = false - for groupName, currentStatus in pairs(Su34Status.status) do + for groupName, currentStatus in pairs( Su34Status.status ) do - env.info(('Su34 Overview Status: GroupName = ' .. groupName )) - Alive = GroupAlive( groupName ) + env.info( ('Su34 Overview Status: GroupName = ' .. groupName) ) + Alive = GroupAlive( groupName ) - if Alive then - if currentStatus == 1 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Attacking carrier Carl Vinson. " - elseif currentStatus == 2 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Attacking supporting ships in the west. " - elseif currentStatus == 3 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Attacking invading ships in the north. " - elseif currentStatus == 4 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "In orbit and awaiting further instructions. " - elseif currentStatus == 5 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Holding Weapons. " - elseif currentStatus == 6 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Return to Krasnodar. " - elseif currentStatus == 7 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Destroyed. " - elseif currentStatus == 8 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Take-Off. " - end - else - if currentStatus == 7 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Destroyed. " - else - Su34Destroyed(groupName) - end - end - end + if Alive then + if currentStatus == 1 then + msg = msg .. string.format( "%s: ", groupName ) + msg = msg .. "Attacking carrier Carl Vinson. " + elseif currentStatus == 2 then + msg = msg .. string.format( "%s: ", groupName ) + msg = msg .. "Attacking supporting ships in the west. " + elseif currentStatus == 3 then + msg = msg .. string.format( "%s: ", groupName ) + msg = msg .. "Attacking invading ships in the north. " + elseif currentStatus == 4 then + msg = msg .. string.format( "%s: ", groupName ) + msg = msg .. "In orbit and awaiting further instructions. " + elseif currentStatus == 5 then + msg = msg .. string.format( "%s: ", groupName ) + msg = msg .. "Holding Weapons. " + elseif currentStatus == 6 then + msg = msg .. string.format( "%s: ", groupName ) + msg = msg .. "Return to Krasnodar. " + elseif currentStatus == 7 then + msg = msg .. string.format( "%s: ", groupName ) + msg = msg .. "Destroyed. " + elseif currentStatus == 8 then + msg = msg .. string.format( "%s: ", groupName ) + msg = msg .. "Take-Off. " + end + else + if currentStatus == 7 then + msg = msg .. string.format( "%s: ", groupName ) + msg = msg .. "Destroyed. " + else + Su34Destroyed( groupName ) + end + end + end - boardMsgRed.statusMsg = msg + boardMsgRed.statusMsg = msg end - function UpdateBoardMsg() ---trace.f() - Su34OverviewStatus() - MessageToRed( boardMsgRed.statusMsg, 15, 'RedStatus' ) + -- trace.f() + Su34OverviewStatus() + MessageToRed( boardMsgRed.statusMsg, 15, 'RedStatus' ) end function MusicReset( flg ) ---trace.f() - trigger.action.setUserFlag(95,flg) + -- trace.f() + trigger.action.setUserFlag( 95, flg ) end -function PlaneActivate(groupNameFormat, flg) ---trace.f() - local groupName = groupNameFormat .. string.format("#%03d", trigger.misc.getUserFlag(flg)) - --trigger.action.outText(groupName,10) - trigger.action.activateGroup(Group.getByName(groupName)) +function PlaneActivate( groupNameFormat, flg ) + -- trace.f() + local groupName = groupNameFormat .. string.format( "#%03d", trigger.misc.getUserFlag( flg ) ) + -- trigger.action.outText(groupName,10) + trigger.action.activateGroup( Group.getByName( groupName ) ) end -function Su34Menu(groupName) ---trace.f() +function Su34Menu( groupName ) + -- trace.f() - --env.info(( 'Su34Menu(' .. groupName .. ')' )) - local groupSu34 = Group.getByName( groupName ) + -- env.info(( 'Su34Menu(' .. groupName .. ')' )) + local groupSu34 = Group.getByName( groupName ) - if Su34Status.status[groupName] == 1 or - Su34Status.status[groupName] == 2 or - Su34Status.status[groupName] == 3 or - Su34Status.status[groupName] == 4 or - Su34Status.status[groupName] == 5 then - if Su34MenuPath[groupName] == nil then - if planeMenuPath == nil then - planeMenuPath = missionCommands.addSubMenuForCoalition( - coalition.side.RED, - "SU-34 anti-ship flights", - nil - ) - end - Su34MenuPath[groupName] = missionCommands.addSubMenuForCoalition( - coalition.side.RED, - "Flight " .. groupName, - planeMenuPath - ) + if Su34Status.status[groupName] == 1 or Su34Status.status[groupName] == 2 or Su34Status.status[groupName] == 3 or Su34Status.status[groupName] == 4 or Su34Status.status[groupName] == 5 then + if Su34MenuPath[groupName] == nil then + if planeMenuPath == nil then + planeMenuPath = missionCommands.addSubMenuForCoalition( coalition.side.RED, "SU-34 anti-ship flights", nil ) + end + Su34MenuPath[groupName] = missionCommands.addSubMenuForCoalition( coalition.side.RED, "Flight " .. groupName, planeMenuPath ) - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Attack carrier Carl Vinson", - Su34MenuPath[groupName], - Su34AttackCarlVinson, - groupName - ) + missionCommands.addCommandForCoalition( coalition.side.RED, "Attack carrier Carl Vinson", Su34MenuPath[groupName], Su34AttackCarlVinson, groupName ) - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Attack ships in the west", - Su34MenuPath[groupName], - Su34AttackWest, - groupName - ) + missionCommands.addCommandForCoalition( coalition.side.RED, "Attack ships in the west", Su34MenuPath[groupName], Su34AttackWest, groupName ) - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Attack ships in the north", - Su34MenuPath[groupName], - Su34AttackNorth, - groupName - ) + missionCommands.addCommandForCoalition( coalition.side.RED, "Attack ships in the north", Su34MenuPath[groupName], Su34AttackNorth, groupName ) - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Hold position and await instructions", - Su34MenuPath[groupName], - Su34Orbit, - groupName - ) + missionCommands.addCommandForCoalition( coalition.side.RED, "Hold position and await instructions", Su34MenuPath[groupName], Su34Orbit, groupName ) - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Report status", - Su34MenuPath[groupName], - Su34OverviewStatus - ) - end - else - if Su34MenuPath[groupName] then - missionCommands.removeItemForCoalition(coalition.side.RED, Su34MenuPath[groupName]) - end - end + missionCommands.addCommandForCoalition( coalition.side.RED, "Report status", Su34MenuPath[groupName], Su34OverviewStatus ) + end + else + if Su34MenuPath[groupName] then + missionCommands.removeItemForCoalition( coalition.side.RED, Su34MenuPath[groupName] ) + end + end end --- Obsolete function, but kept to rework in framework. -function ChooseInfantry ( TeleportPrefixTable, TeleportMax ) ---trace.f("Spawn") - --env.info(( 'ChooseInfantry: ' )) +function ChooseInfantry( TeleportPrefixTable, TeleportMax ) + -- trace.f("Spawn") + -- env.info(( 'ChooseInfantry: ' )) - TeleportPrefixTableCount = #TeleportPrefixTable - TeleportPrefixTableIndex = math.random( 1, TeleportPrefixTableCount ) + TeleportPrefixTableCount = #TeleportPrefixTable + TeleportPrefixTableIndex = math.random( 1, TeleportPrefixTableCount ) - --env.info(( 'ChooseInfantry: TeleportPrefixTableIndex = ' .. TeleportPrefixTableIndex .. ' TeleportPrefixTableCount = ' .. TeleportPrefixTableCount .. ' TeleportMax = ' .. TeleportMax )) + -- env.info(( 'ChooseInfantry: TeleportPrefixTableIndex = ' .. TeleportPrefixTableIndex .. ' TeleportPrefixTableCount = ' .. TeleportPrefixTableCount .. ' TeleportMax = ' .. TeleportMax )) - local TeleportFound = false - local TeleportLoop = true - local Index = TeleportPrefixTableIndex - local TeleportPrefix = '' + local TeleportFound = false + local TeleportLoop = true + local Index = TeleportPrefixTableIndex + local TeleportPrefix = '' - while TeleportLoop do - TeleportPrefix = TeleportPrefixTable[Index] - if SpawnSettings[TeleportPrefix] then - if SpawnSettings[TeleportPrefix]['SpawnCount'] - 1 < TeleportMax then - SpawnSettings[TeleportPrefix]['SpawnCount'] = SpawnSettings[TeleportPrefix]['SpawnCount'] + 1 - TeleportFound = true - else - TeleportFound = false - end - else - SpawnSettings[TeleportPrefix] = {} - SpawnSettings[TeleportPrefix]['SpawnCount'] = 0 - TeleportFound = true - end - if TeleportFound then - TeleportLoop = false - else - if Index < TeleportPrefixTableCount then - Index = Index + 1 - else - TeleportLoop = false - end - end - --env.info(( 'ChooseInfantry: Loop 1 - TeleportPrefix = ' .. TeleportPrefix .. ' Index = ' .. Index )) - end + while TeleportLoop do + TeleportPrefix = TeleportPrefixTable[Index] + if SpawnSettings[TeleportPrefix] then + if SpawnSettings[TeleportPrefix]['SpawnCount'] - 1 < TeleportMax then + SpawnSettings[TeleportPrefix]['SpawnCount'] = SpawnSettings[TeleportPrefix]['SpawnCount'] + 1 + TeleportFound = true + else + TeleportFound = false + end + else + SpawnSettings[TeleportPrefix] = {} + SpawnSettings[TeleportPrefix]['SpawnCount'] = 0 + TeleportFound = true + end + if TeleportFound then + TeleportLoop = false + else + if Index < TeleportPrefixTableCount then + Index = Index + 1 + else + TeleportLoop = false + end + end + -- env.info(( 'ChooseInfantry: Loop 1 - TeleportPrefix = ' .. TeleportPrefix .. ' Index = ' .. Index )) + end - if TeleportFound == false then - TeleportLoop = true - Index = 1 - while TeleportLoop do - TeleportPrefix = TeleportPrefixTable[Index] - if SpawnSettings[TeleportPrefix] then - if SpawnSettings[TeleportPrefix]['SpawnCount'] - 1 < TeleportMax then - SpawnSettings[TeleportPrefix]['SpawnCount'] = SpawnSettings[TeleportPrefix]['SpawnCount'] + 1 - TeleportFound = true - else - TeleportFound = false - end - else - SpawnSettings[TeleportPrefix] = {} - SpawnSettings[TeleportPrefix]['SpawnCount'] = 0 - TeleportFound = true - end - if TeleportFound then - TeleportLoop = false - else - if Index < TeleportPrefixTableIndex then - Index = Index + 1 - else - TeleportLoop = false - end - end - --env.info(( 'ChooseInfantry: Loop 2 - TeleportPrefix = ' .. TeleportPrefix .. ' Index = ' .. Index )) - end - end + if TeleportFound == false then + TeleportLoop = true + Index = 1 + while TeleportLoop do + TeleportPrefix = TeleportPrefixTable[Index] + if SpawnSettings[TeleportPrefix] then + if SpawnSettings[TeleportPrefix]['SpawnCount'] - 1 < TeleportMax then + SpawnSettings[TeleportPrefix]['SpawnCount'] = SpawnSettings[TeleportPrefix]['SpawnCount'] + 1 + TeleportFound = true + else + TeleportFound = false + end + else + SpawnSettings[TeleportPrefix] = {} + SpawnSettings[TeleportPrefix]['SpawnCount'] = 0 + TeleportFound = true + end + if TeleportFound then + TeleportLoop = false + else + if Index < TeleportPrefixTableIndex then + Index = Index + 1 + else + TeleportLoop = false + end + end + -- env.info(( 'ChooseInfantry: Loop 2 - TeleportPrefix = ' .. TeleportPrefix .. ' Index = ' .. Index )) + end + end - local TeleportGroupName = '' - if TeleportFound == true then - TeleportGroupName = TeleportPrefix .. string.format("#%03d", SpawnSettings[TeleportPrefix]['SpawnCount'] ) - else - TeleportGroupName = '' - end + local TeleportGroupName = '' + if TeleportFound == true then + TeleportGroupName = TeleportPrefix .. string.format( "#%03d", SpawnSettings[TeleportPrefix]['SpawnCount'] ) + else + TeleportGroupName = '' + end - --env.info(('ChooseInfantry: TeleportGroupName = ' .. TeleportGroupName )) - --env.info(('ChooseInfantry: return')) + -- env.info(('ChooseInfantry: TeleportGroupName = ' .. TeleportGroupName )) + -- env.info(('ChooseInfantry: return')) - return TeleportGroupName + return TeleportGroupName end SpawnedInfantry = 0 -function LandCarrier ( CarrierGroup, LandingZonePrefix ) ---trace.f() - --env.info(( 'LandCarrier: ' )) - --env.info(( 'LandCarrier: CarrierGroup = ' .. CarrierGroup:getName() )) - --env.info(( 'LandCarrier: LandingZone = ' .. LandingZonePrefix )) +function LandCarrier( CarrierGroup, LandingZonePrefix ) + -- trace.f() + -- env.info(( 'LandCarrier: ' )) + -- env.info(( 'LandCarrier: CarrierGroup = ' .. CarrierGroup:getName() )) + -- env.info(( 'LandCarrier: LandingZone = ' .. LandingZonePrefix )) - local controllerGroup = CarrierGroup:getController() + local controllerGroup = CarrierGroup:getController() - local LandingZone = trigger.misc.getZone(LandingZonePrefix) - local LandingZonePos = {} - LandingZonePos.x = LandingZone.point.x + math.random(LandingZone.radius * -1, LandingZone.radius) - LandingZonePos.y = LandingZone.point.z + math.random(LandingZone.radius * -1, LandingZone.radius) + local LandingZone = trigger.misc.getZone( LandingZonePrefix ) + local LandingZonePos = {} + LandingZonePos.x = LandingZone.point.x + math.random( LandingZone.radius * -1, LandingZone.radius ) + LandingZonePos.y = LandingZone.point.z + math.random( LandingZone.radius * -1, LandingZone.radius ) - controllerGroup:pushTask( { id = 'Land', params = { point = LandingZonePos, durationFlag = true, duration = 10 } } ) + controllerGroup:pushTask( { id = 'Land', params = { point = LandingZonePos, durationFlag = true, duration = 10 } } ) - --env.info(( 'LandCarrier: end' )) + -- env.info(( 'LandCarrier: end' )) end EscortCount = 0 -function EscortCarrier ( CarrierGroup, EscortPrefix, EscortLastWayPoint, EscortEngagementDistanceMax, EscortTargetTypes ) ---trace.f() - --env.info(( 'EscortCarrier: ' )) - --env.info(( 'EscortCarrier: CarrierGroup = ' .. CarrierGroup:getName() )) - --env.info(( 'EscortCarrier: EscortPrefix = ' .. EscortPrefix )) +function EscortCarrier( CarrierGroup, EscortPrefix, EscortLastWayPoint, EscortEngagementDistanceMax, EscortTargetTypes ) + -- trace.f() + -- env.info(( 'EscortCarrier: ' )) + -- env.info(( 'EscortCarrier: CarrierGroup = ' .. CarrierGroup:getName() )) + -- env.info(( 'EscortCarrier: EscortPrefix = ' .. EscortPrefix )) - local CarrierName = CarrierGroup:getName() + local CarrierName = CarrierGroup:getName() - local EscortMission = {} - local CarrierMission = {} + local EscortMission = {} + local CarrierMission = {} - local EscortMission = SpawnMissionGroup( EscortPrefix ) - local CarrierMission = SpawnMissionGroup( CarrierGroup:getName() ) + local EscortMission = SpawnMissionGroup( EscortPrefix ) + local CarrierMission = SpawnMissionGroup( CarrierGroup:getName() ) - if EscortMission ~= nil and CarrierMission ~= nil then + if EscortMission ~= nil and CarrierMission ~= nil then - EscortCount = EscortCount + 1 - EscortMissionName = string.format( EscortPrefix .. '#Escort %s', CarrierName ) - EscortMission.name = EscortMissionName - EscortMission.groupId = nil - EscortMission.lateActivation = false - EscortMission.taskSelected = false + EscortCount = EscortCount + 1 + EscortMissionName = string.format( EscortPrefix .. '#Escort %s', CarrierName ) + EscortMission.name = EscortMissionName + EscortMission.groupId = nil + EscortMission.lateActivation = false + EscortMission.taskSelected = false - local EscortUnits = #EscortMission.units - for u = 1, EscortUnits do - EscortMission.units[u].name = string.format( EscortPrefix .. '#Escort %s %02d', CarrierName, u ) - EscortMission.units[u].unitId = nil - end + local EscortUnits = #EscortMission.units + for u = 1, EscortUnits do + EscortMission.units[u].name = string.format( EscortPrefix .. '#Escort %s %02d', CarrierName, u ) + EscortMission.units[u].unitId = nil + end + EscortMission.route.points[1].task = { + id = "ComboTask", + params = { + tasks = { + [1] = { + enabled = true, + auto = false, + id = "Escort", + number = 1, + params = { + lastWptIndexFlagChangedManually = false, + groupId = CarrierGroup:getID(), + lastWptIndex = nil, + lastWptIndexFlag = false, + engagementDistMax = EscortEngagementDistanceMax, + targetTypes = EscortTargetTypes, + pos = { y = 20, x = 20, z = 0 } -- end of ["pos"] + } -- end of ["params"] + } -- end of [1] + } -- end of ["tasks"] + } -- end of ["params"] + } -- end of ["task"] - EscortMission.route.points[1].task = { id = "ComboTask", - params = - { - tasks = - { - [1] = - { - enabled = true, - auto = false, - id = "Escort", - number = 1, - params = - { - lastWptIndexFlagChangedManually = false, - groupId = CarrierGroup:getID(), - lastWptIndex = nil, - lastWptIndexFlag = false, - engagementDistMax = EscortEngagementDistanceMax, - targetTypes = EscortTargetTypes, - pos = - { - y = 20, - x = 20, - z = 0, - } -- end of ["pos"] - } -- end of ["params"] - } -- end of [1] - } -- end of ["tasks"] - } -- end of ["params"] - } -- end of ["task"] + SpawnGroupAdd( EscortPrefix, EscortMission ) - SpawnGroupAdd( EscortPrefix, EscortMission ) - - end + end end function SendMessageToCarrier( CarrierGroup, CarrierMessage ) ---trace.f() + -- trace.f() - if CarrierGroup ~= nil then - MessageToGroup( CarrierGroup, CarrierMessage, 30, 'Carrier/' .. CarrierGroup:getName() ) - end + if CarrierGroup ~= nil then + MessageToGroup( CarrierGroup, CarrierMessage, 30, 'Carrier/' .. CarrierGroup:getName() ) + end end function MessageToGroup( MsgGroup, MsgText, MsgTime, MsgName ) ---trace.f() + -- trace.f() - if type(MsgGroup) == 'string' then - --env.info( 'MessageToGroup: Converted MsgGroup string "' .. MsgGroup .. '" into a Group structure.' ) - MsgGroup = Group.getByName( MsgGroup ) - end + if type( MsgGroup ) == 'string' then + -- env.info( 'MessageToGroup: Converted MsgGroup string "' .. MsgGroup .. '" into a Group structure.' ) + MsgGroup = Group.getByName( MsgGroup ) + end - if MsgGroup ~= nil then - local MsgTable = {} - MsgTable.text = MsgText - MsgTable.displayTime = MsgTime - MsgTable.msgFor = { units = { MsgGroup:getUnits()[1]:getName() } } - MsgTable.name = MsgName - --routines.message.add( MsgTable ) - --env.info(('MessageToGroup: Message sent to ' .. MsgGroup:getUnits()[1]:getName() .. ' -> ' .. MsgText )) - end + if MsgGroup ~= nil then + local MsgTable = {} + MsgTable.text = MsgText + MsgTable.displayTime = MsgTime + MsgTable.msgFor = { units = { MsgGroup:getUnits()[1]:getName() } } + MsgTable.name = MsgName + -- routines.message.add( MsgTable ) + -- env.info(('MessageToGroup: Message sent to ' .. MsgGroup:getUnits()[1]:getName() .. ' -> ' .. MsgText )) + end end function MessageToUnit( UnitName, MsgText, MsgTime, MsgName ) ---trace.f() + -- trace.f() - if UnitName ~= nil then - local MsgTable = {} - MsgTable.text = MsgText - MsgTable.displayTime = MsgTime - MsgTable.msgFor = { units = { UnitName } } - MsgTable.name = MsgName - --routines.message.add( MsgTable ) - end + if UnitName ~= nil then + local MsgTable = {} + MsgTable.text = MsgText + MsgTable.displayTime = MsgTime + MsgTable.msgFor = { units = { UnitName } } + MsgTable.name = MsgName + -- routines.message.add( MsgTable ) + end end function MessageToAll( MsgText, MsgTime, MsgName ) ---trace.f() + -- trace.f() - MESSAGE:New( MsgText, MsgTime, "Message" ):ToCoalition( coalition.side.RED ):ToCoalition( coalition.side.BLUE ) + MESSAGE:New( MsgText, MsgTime, "Message" ):ToCoalition( coalition.side.RED ):ToCoalition( coalition.side.BLUE ) end function MessageToRed( MsgText, MsgTime, MsgName ) ---trace.f() + -- trace.f() - MESSAGE:New( MsgText, MsgTime, "To Red Coalition" ):ToCoalition( coalition.side.RED ) + MESSAGE:New( MsgText, MsgTime, "To Red Coalition" ):ToCoalition( coalition.side.RED ) end function MessageToBlue( MsgText, MsgTime, MsgName ) ---trace.f() + -- trace.f() - MESSAGE:New( MsgText, MsgTime, "To Blue Coalition" ):ToCoalition( coalition.side.BLUE ) + MESSAGE:New( MsgText, MsgTime, "To Blue Coalition" ):ToCoalition( coalition.side.BLUE ) end function getCarrierHeight( CarrierGroup ) ---trace.f() + -- trace.f() - if CarrierGroup ~= nil then - if table.getn(CarrierGroup:getUnits()) == 1 then - local CarrierUnit = CarrierGroup:getUnits()[1] - local CurrentPoint = CarrierUnit:getPoint() + if CarrierGroup ~= nil then + if table.getn( CarrierGroup:getUnits() ) == 1 then + local CarrierUnit = CarrierGroup:getUnits()[1] + local CurrentPoint = CarrierUnit:getPoint() - local CurrentPosition = { x = CurrentPoint.x, y = CurrentPoint.z } - local CarrierHeight = CurrentPoint.y + local CurrentPosition = { x = CurrentPoint.x, y = CurrentPoint.z } + local CarrierHeight = CurrentPoint.y - local LandHeight = land.getHeight( CurrentPosition ) + local LandHeight = land.getHeight( CurrentPosition ) - --env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) - - return CarrierHeight - LandHeight - else - return 999999 - end - else - return 999999 - end + -- env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) + return CarrierHeight - LandHeight + else + return 999999 + end + else + return 999999 + end end function GetUnitHeight( CheckUnit ) ---trace.f() + -- trace.f() - local UnitPoint = CheckUnit:getPoint() - local UnitPosition = { x = CurrentPoint.x, y = CurrentPoint.z } - local UnitHeight = CurrentPoint.y + local UnitPoint = CheckUnit:getPoint() + local UnitPosition = { x = CurrentPoint.x, y = CurrentPoint.z } + local UnitHeight = CurrentPoint.y - local LandHeight = land.getHeight( CurrentPosition ) + local LandHeight = land.getHeight( CurrentPosition ) - --env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) - - return UnitHeight - LandHeight + -- env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) + return UnitHeight - LandHeight end - _MusicTable = {} _MusicTable.Files = {} _MusicTable.Queue = {} _MusicTable.FileCnt = 0 - function MusicRegister( SndRef, SndFile, SndTime ) ---trace.f() + -- trace.f() - env.info(( 'MusicRegister: SndRef = ' .. SndRef )) - env.info(( 'MusicRegister: SndFile = ' .. SndFile )) - env.info(( 'MusicRegister: SndTime = ' .. SndTime )) + env.info( ('MusicRegister: SndRef = ' .. SndRef) ) + env.info( ('MusicRegister: SndFile = ' .. SndFile) ) + env.info( ('MusicRegister: SndTime = ' .. SndTime) ) + _MusicTable.FileCnt = _MusicTable.FileCnt + 1 - _MusicTable.FileCnt = _MusicTable.FileCnt + 1 - - _MusicTable.Files[_MusicTable.FileCnt] = {} - _MusicTable.Files[_MusicTable.FileCnt].Ref = SndRef - _MusicTable.Files[_MusicTable.FileCnt].File = SndFile - _MusicTable.Files[_MusicTable.FileCnt].Time = SndTime - - if not _MusicTable.Function then - _MusicTable.Function = routines.scheduleFunction( MusicScheduler, { }, timer.getTime() + 10, 10) - end + _MusicTable.Files[_MusicTable.FileCnt] = {} + _MusicTable.Files[_MusicTable.FileCnt].Ref = SndRef + _MusicTable.Files[_MusicTable.FileCnt].File = SndFile + _MusicTable.Files[_MusicTable.FileCnt].Time = SndTime + if not _MusicTable.Function then + _MusicTable.Function = routines.scheduleFunction( MusicScheduler, {}, timer.getTime() + 10, 10 ) + end end function MusicToPlayer( SndRef, PlayerName, SndContinue ) ---trace.f() + -- trace.f() - --env.info(( 'MusicToPlayer: SndRef = ' .. SndRef )) + -- env.info(( 'MusicToPlayer: SndRef = ' .. SndRef )) - local PlayerUnits = AlivePlayerUnits() - for PlayerUnitIdx, PlayerUnit in pairs(PlayerUnits) do - local PlayerUnitName = PlayerUnit:getPlayerName() - --env.info(( 'MusicToPlayer: PlayerUnitName = ' .. PlayerUnitName )) - if PlayerName == PlayerUnitName then - PlayerGroup = PlayerUnit:getGroup() - if PlayerGroup then - --env.info(( 'MusicToPlayer: PlayerGroup = ' .. PlayerGroup:getName() )) - MusicToGroup( SndRef, PlayerGroup, SndContinue ) - end - break - end - end - - --env.info(( 'MusicToPlayer: end' )) + local PlayerUnits = AlivePlayerUnits() + for PlayerUnitIdx, PlayerUnit in pairs( PlayerUnits ) do + local PlayerUnitName = PlayerUnit:getPlayerName() + -- env.info(( 'MusicToPlayer: PlayerUnitName = ' .. PlayerUnitName )) + if PlayerName == PlayerUnitName then + PlayerGroup = PlayerUnit:getGroup() + if PlayerGroup then + -- env.info(( 'MusicToPlayer: PlayerGroup = ' .. PlayerGroup:getName() )) + MusicToGroup( SndRef, PlayerGroup, SndContinue ) + end + break + end + end + -- env.info(( 'MusicToPlayer: end' )) end function MusicToGroup( SndRef, SndGroup, SndContinue ) ---trace.f() + -- trace.f() - --env.info(( 'MusicToGroup: SndRef = ' .. SndRef )) + -- env.info(( 'MusicToGroup: SndRef = ' .. SndRef )) - if SndGroup ~= nil then - if _MusicTable and _MusicTable.FileCnt > 0 then - if SndGroup:isExist() then - if MusicCanStart(SndGroup:getUnit(1):getPlayerName()) then - --env.info(( 'MusicToGroup: OK for Sound.' )) - local SndIdx = 0 - if SndRef == '' then - --env.info(( 'MusicToGroup: SndRef as empty. Queueing at random.' )) - SndIdx = math.random( 1, _MusicTable.FileCnt ) - else - for SndIdx = 1, _MusicTable.FileCnt do - if _MusicTable.Files[SndIdx].Ref == SndRef then - break - end - end - end - --env.info(( 'MusicToGroup: SndIdx = ' .. SndIdx )) - --env.info(( 'MusicToGroup: Queueing Music ' .. _MusicTable.Files[SndIdx].File .. ' for Group ' .. SndGroup:getID() )) - trigger.action.outSoundForGroup( SndGroup:getID(), _MusicTable.Files[SndIdx].File ) - MessageToGroup( SndGroup, 'Playing ' .. _MusicTable.Files[SndIdx].File, 15, 'Music-' .. SndGroup:getUnit(1):getPlayerName() ) + if SndGroup ~= nil then + if _MusicTable and _MusicTable.FileCnt > 0 then + if SndGroup:isExist() then + if MusicCanStart( SndGroup:getUnit( 1 ):getPlayerName() ) then + -- env.info(( 'MusicToGroup: OK for Sound.' )) + local SndIdx = 0 + if SndRef == '' then + -- env.info(( 'MusicToGroup: SndRef as empty. Queueing at random.' )) + SndIdx = math.random( 1, _MusicTable.FileCnt ) + else + for SndIdx = 1, _MusicTable.FileCnt do + if _MusicTable.Files[SndIdx].Ref == SndRef then + break + end + end + end + -- env.info(( 'MusicToGroup: SndIdx = ' .. SndIdx )) + -- env.info(( 'MusicToGroup: Queueing Music ' .. _MusicTable.Files[SndIdx].File .. ' for Group ' .. SndGroup:getID() )) + trigger.action.outSoundForGroup( SndGroup:getID(), _MusicTable.Files[SndIdx].File ) + MessageToGroup( SndGroup, 'Playing ' .. _MusicTable.Files[SndIdx].File, 15, 'Music-' .. SndGroup:getUnit( 1 ):getPlayerName() ) - local SndQueueRef = SndGroup:getUnit(1):getPlayerName() - if _MusicTable.Queue[SndQueueRef] == nil then - _MusicTable.Queue[SndQueueRef] = {} - end - _MusicTable.Queue[SndQueueRef].Start = timer.getTime() - _MusicTable.Queue[SndQueueRef].PlayerName = SndGroup:getUnit(1):getPlayerName() - _MusicTable.Queue[SndQueueRef].Group = SndGroup - _MusicTable.Queue[SndQueueRef].ID = SndGroup:getID() - _MusicTable.Queue[SndQueueRef].Ref = SndIdx - _MusicTable.Queue[SndQueueRef].Continue = SndContinue - _MusicTable.Queue[SndQueueRef].Type = Group - end - end - end - end + local SndQueueRef = SndGroup:getUnit( 1 ):getPlayerName() + if _MusicTable.Queue[SndQueueRef] == nil then + _MusicTable.Queue[SndQueueRef] = {} + end + _MusicTable.Queue[SndQueueRef].Start = timer.getTime() + _MusicTable.Queue[SndQueueRef].PlayerName = SndGroup:getUnit( 1 ):getPlayerName() + _MusicTable.Queue[SndQueueRef].Group = SndGroup + _MusicTable.Queue[SndQueueRef].ID = SndGroup:getID() + _MusicTable.Queue[SndQueueRef].Ref = SndIdx + _MusicTable.Queue[SndQueueRef].Continue = SndContinue + _MusicTable.Queue[SndQueueRef].Type = Group + end + end + end + end end -function MusicCanStart(PlayerName) ---trace.f() +function MusicCanStart( PlayerName ) + -- trace.f() - --env.info(( 'MusicCanStart:' )) + -- env.info(( 'MusicCanStart:' )) - local MusicOut = false + local MusicOut = false - if _MusicTable['Queue'] ~= nil and _MusicTable.FileCnt > 0 then - --env.info(( 'MusicCanStart: PlayerName = ' .. PlayerName )) - local PlayerFound = false - local MusicStart = 0 - local MusicTime = 0 - for SndQueueIdx, SndQueue in pairs( _MusicTable.Queue ) do - if SndQueue.PlayerName == PlayerName then - PlayerFound = true - MusicStart = SndQueue.Start - MusicTime = _MusicTable.Files[SndQueue.Ref].Time - break - end - end - if PlayerFound then - --env.info(( 'MusicCanStart: MusicStart = ' .. MusicStart )) - --env.info(( 'MusicCanStart: MusicTime = ' .. MusicTime )) - --env.info(( 'MusicCanStart: timer.getTime() = ' .. timer.getTime() )) + if _MusicTable['Queue'] ~= nil and _MusicTable.FileCnt > 0 then + -- env.info(( 'MusicCanStart: PlayerName = ' .. PlayerName )) + local PlayerFound = false + local MusicStart = 0 + local MusicTime = 0 + for SndQueueIdx, SndQueue in pairs( _MusicTable.Queue ) do + if SndQueue.PlayerName == PlayerName then + PlayerFound = true + MusicStart = SndQueue.Start + MusicTime = _MusicTable.Files[SndQueue.Ref].Time + break + end + end + if PlayerFound then + -- env.info(( 'MusicCanStart: MusicStart = ' .. MusicStart )) + -- env.info(( 'MusicCanStart: MusicTime = ' .. MusicTime )) + -- env.info(( 'MusicCanStart: timer.getTime() = ' .. timer.getTime() )) - if MusicStart + MusicTime <= timer.getTime() then - MusicOut = true - end - else - MusicOut = true - end - end + if MusicStart + MusicTime <= timer.getTime() then + MusicOut = true + end + else + MusicOut = true + end + end - if MusicOut then - --env.info(( 'MusicCanStart: true' )) - else - --env.info(( 'MusicCanStart: false' )) - end + if MusicOut then + -- env.info(( 'MusicCanStart: true' )) + else + -- env.info(( 'MusicCanStart: false' )) + end - return MusicOut + return MusicOut end function MusicScheduler() ---trace.scheduled("", "MusicScheduler") - - --env.info(( 'MusicScheduler:' )) - if _MusicTable['Queue'] ~= nil and _MusicTable.FileCnt > 0 then - --env.info(( 'MusicScheduler: Walking Sound Queue.')) - for SndQueueIdx, SndQueue in pairs( _MusicTable.Queue ) do - if SndQueue.Continue then - if MusicCanStart(SndQueue.PlayerName) then - --env.info(('MusicScheduler: MusicToGroup')) - MusicToPlayer( '', SndQueue.PlayerName, true ) - end - end - end - end + -- trace.scheduled("", "MusicScheduler") + -- env.info(( 'MusicScheduler:' )) + if _MusicTable['Queue'] ~= nil and _MusicTable.FileCnt > 0 then + -- env.info(( 'MusicScheduler: Walking Sound Queue.')) + for SndQueueIdx, SndQueue in pairs( _MusicTable.Queue ) do + if SndQueue.Continue then + if MusicCanStart( SndQueue.PlayerName ) then + -- env.info(('MusicScheduler: MusicToGroup')) + MusicToPlayer( '', SndQueue.PlayerName, true ) + end + end + end + end end - -env.info(( 'Init: Scripts Loaded v1.1' )) - +env.info( ('Init: Scripts Loaded v1.1') ) diff --git a/Moose Development/Moose/Utilities/STTS.lua b/Moose Development/Moose/Utilities/STTS.lua index 97836a1ee..37a6b3035 100644 --- a/Moose Development/Moose/Utilities/STTS.lua +++ b/Moose Development/Moose/Utilities/STTS.lua @@ -1,38 +1,35 @@ --- **Utilities** DCS Simple Text-To-Speech (STTS). --- --- --- +-- +-- -- @module Utils.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 sanitisation. +-- +-- 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 initialise it +-- +-- 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 @@ -50,37 +47,37 @@ -- ## 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" +-- +-- 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", +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 @@ -95,139 +92,141 @@ STTS.GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json" --- DONT 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) + 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) +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 + 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, avarage of 5 letters for english word so --- +-- -- * 5 chars * 100wpm = 500 characters per min = 8.3 chars per second --- +-- -- So lengh 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 --- -function STTS.getSpeechTime(length,speed,isGoogle) +-- +function STTS.getSpeechTime( length, speed, isGoogle ) - local maxRateRatio = 3 + local maxRateRatio = 3 speed = speed or 1.0 isGoogle = isGoogle or false local speedFactor = 1.0 if isGoogle then - speedFactor = speed + speedFactor = speed else - if speed ~= 0 then - speedFactor = math.abs(speed) * (maxRateRatio - 1) / 10 + 1 - end - if speed < 0 then - speedFactor = 1/speedFactor - end + 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) + local wpm = math.ceil( 100 * speedFactor ) + local cps = math.floor( (wpm * 5) / 60 ) - if type(length) == "string" then - length = string.len(length) + if type( length ) == "string" then + length = string.len( length ) end - return math.ceil(length/cps) + return 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 +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 + 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 ) - 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) + cmd = cmd .. string.format( " -V \"%s\"", voice ) else - if culture ~= "" then - cmd = cmd .. string.format(" -l %s",culture) - end + if culture ~= "" then + cmd = cmd .. string.format( " -l %s", culture ) + end - if gender ~= "" then - cmd = cmd .. string.format(" -g %s",gender) - 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) + cmd = cmd .. string.format( " -G \"%s\"", STTS.GOOGLE_CREDENTIALS ) end if speed ~= 1 then - cmd = cmd .. string.format(" -s %s",speed) + cmd = cmd .. string.format( " -s %s", speed ) end if volume ~= 1.0 then - cmd = cmd .. string.format(" -v %s",volume) + 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) + 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) + 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) + cmd = cmd .. string.format( " -L %s -O %s -A %s", lat, lon, alt ) end - cmd = cmd ..string.format(" -t \"%s\"",message) + 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) + 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") + 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) + os.execute( cmd ) - return STTS.getSpeechTime(message,speed,googleTTS) + return STTS.getSpeechTime( message, speed, googleTTS ) end --- Play mp3 function. @@ -235,22 +234,21 @@ end -- @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) +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) + 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" ) - lat = STTS.round(lat,4) - lon = STTS.round(lon,4) - alt = math.floor(alt) + if point and type( point ) == "table" and point.x then + local lat, lon, alt = coord.LOtoLL( point ) - cmd = cmd .. string.format(" -L %s -O %s -A %s",lat,lon,alt) - end + lat = STTS.round( lat, 4 ) + lon = STTS.round( lon, 4 ) + alt = math.floor( alt ) - env.info("[DCS-STTS] MP3/OGG Command :\n" .. cmd.."\n") - os.execute(cmd) + cmd = cmd .. string.format( " -L %s -O %s -A %s", lat, lon, alt ) + end -end \ No newline at end of file + env.info( "[DCS-STTS] MP3/OGG Command :\n" .. cmd .. "\n" ) + os.execute( cmd ) + +end