#Changes from Develop

This commit is contained in:
Applevangelist
2022-09-10 11:49:40 +02:00
parent c58e91b956
commit 7c22e9fe69
48 changed files with 7792 additions and 3739 deletions

View File

@@ -509,7 +509,7 @@ ENUMS.ReportingName =
Atlas = "A400",
Lancer = "B1-B",
Stratofortress = "B-52H",
Hercules = "C-130",
Hercules = "C-130",
Super_Hercules = "Hercules",
Globemaster = "C-17",
Greyhound = "C-2A",

View File

@@ -20,11 +20,11 @@ do
-- @type FIFO
-- @field #string ClassName Name of the class.
-- @field #string lid Class id string for output to DCS log file.
-- @field #string version Version of FiFo
-- @field #number counter
-- @field #number pointer
-- @field #table stackbypointer
-- @field #table stackbyid
-- @field #string version Version of FiFo.
-- @field #number counter Counter.
-- @field #number pointer Pointer.
-- @field #table stackbypointer Stack by pointer.
-- @field #table stackbyid Stack by ID.
-- @extends Core.Base#BASE
---
@@ -45,12 +45,12 @@ FIFO = {
stackbyid = {}
}
--- Instantiate a new FIFO Stack
--- Instantiate a new FIFO Stack.
-- @param #FIFO self
-- @return #FIFO self
function FIFO:New()
-- Inherit everything from BASE class.
local self=BASE:Inherit(self, BASE:New())
local self=BASE:Inherit(self, BASE:New()) --#FIFO
self.pointer = 0
self.counter = 0
self.stackbypointer = {}
@@ -62,7 +62,7 @@ function FIFO:New()
return self
end
--- Empty FIFO Stack
--- Empty FIFO Stack.
-- @param #FIFO self
-- @return #FIFO self
function FIFO:Clear()
@@ -77,7 +77,7 @@ function FIFO:Clear()
return self
end
--- FIFO Push Object to Stack
--- FIFO Push Object to Stack.
-- @param #FIFO self
-- @param #table Object
-- @param #string UniqueID (optional) - will default to current pointer + 1. Note - if you intend to use `FIFO:GetIDStackSorted()` keep the UniqueID numerical!
@@ -97,7 +97,7 @@ function FIFO:Push(Object,UniqueID)
return self
end
--- FIFO Pull Object from Stack
--- FIFO Pull Object from Stack.
-- @param #FIFO self
-- @return #table Object or nil if stack is empty
function FIFO:Pull()

View File

@@ -7,7 +7,7 @@
-- ### Author: **TAW CougarNL**, *funkyfranky*
--
-- @module Utilities.PROFILER
-- @image MOOSE.JPG
-- @image Utils_Profiler.jpg
--- PROFILER class.
-- @type PROFILER
@@ -24,7 +24,8 @@
-- @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? *
--- *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,11 +34,24 @@
-- # 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 performance of your code.
-- With this information you can optimize the perfomance of your code.
--
-- # Prerequisites
--
-- The modules **os** and **lfs** need to be de-sanitized.
-- The modules **os**, **io** and **lfs** need to be desanizied. Comment out the lines
--
-- --sanitizeModule('os')
-- --sanitizeModule('io')
-- --sanitizeModule('lfs')
--
-- in your *"DCS World OpenBeta/Scripts/MissionScripting.lua"* file.
--
-- But be aware that these changes can make you system vulnerable to attacks.
--
-- # Disclaimer
--
-- **Profiling itself is CPU expensive!** Don't use this when you want to fly or host a mission.
--
--
-- # Start
--
@@ -123,12 +137,12 @@ function PROFILER.Start( Delay, Duration )
go = false
end
if not io then
env.error( "ERROR: Profiler needs io to be de-sanitized!" )
go = false
env.error("ERROR: Profiler needs io to be desanitized!")
go=false
end
if not lfs then
env.error( "ERROR: Profiler needs lfs to be de-sanitized!" )
go = false
env.error("ERROR: Profiler needs lfs to be desanitized!")
go=false
end
if not go then
return
@@ -139,11 +153,11 @@ function PROFILER.Start( Delay, 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 ############################' )
@@ -152,18 +166,19 @@ function PROFILER.Start( Delay, Duration )
else
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
@@ -181,20 +196,29 @@ function PROFILER.Stop( Delay )
if Delay and Delay > 0 then
BASE:ScheduleOnce( Delay, PROFILER.Stop )
end
end
function PROFILER.Stop(Delay)
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
@@ -213,34 +237,34 @@ end
--- Debug hook.
-- @param #table event Event.
function PROFILER.hook( event )
function PROFILER.hook(event)
local f = debug.getinfo( 2, "f" ).func
local f=debug.getinfo(2, "f").func
if event == 'call' then
if event=='call' then
if PROFILER.Counters[f] == nil then
if PROFILER.Counters[f]==nil then
PROFILER.Counters[f] = 1
PROFILER.dInfo[f] = debug.getinfo( 2, "Sn" )
PROFILER.Counters[f]=1
PROFILER.dInfo[f]=debug.getinfo(2,"Sn")
if PROFILER.fTimeTotal[f] == nil then
PROFILER.fTimeTotal[f] = 0
if PROFILER.fTimeTotal[f]==nil then
PROFILER.fTimeTotal[f]=0
end
else
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
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
if PROFILER.fTime[f]~=nil then
PROFILER.fTimeTotal[f]=PROFILER.fTimeTotal[f]+(os.clock()-PROFILER.fTime[f])
PROFILER.fTime[f]=nil
end
end
@@ -259,9 +283,9 @@ end
-- @return #number Function time in seconds.
function PROFILER.getData( func )
local n = PROFILER.dInfo[func]
local n=PROFILER.dInfo[func]
if n.what == "C" then
if n.what=="C" then
return n.name, "?", "?", PROFILER.fTimeTotal[func]
end
@@ -282,20 +306,20 @@ end
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 cps=t.count/runTimeGame
local threshCPS = cps >= PROFILER.ThreshCPS
local threshTot = t.tm >= PROFILER.ThreshTtot
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
@@ -312,19 +336,19 @@ function PROFILER.printCSV( data, runTimeGame )
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
-- 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
@@ -335,15 +359,15 @@ 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\]]
local dir=lfs.writedir()..[[Logs\]]
ext = ext or PROFILER.fileNameSuffix
ext=ext or PROFILER.fileNameSuffix
local file = dir .. PROFILER.fileNamePrefix .. "." .. ext
local file=dir..PROFILER.fileNamePrefix.."."..ext
if not UTILS.FileExists( file ) then
if not UTILS.FileExists(file) then
return file
end
@@ -365,34 +389,39 @@ end
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 Ttot=0
local Calls=0
local t = {}
local t={}
local tcopy = nil -- #PROFILER.Data
local tserialize = nil -- #PROFILER.Data
local tforgen = nil -- #PROFILER.Data
local tpairs = nil -- #PROFILER.Data
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 )
for func, count in pairs(PROFILER.Counters) do
if PROFILER.logUnknown == true then
if s == nil then
s = "<Unknown>"
end
local s,src,line,tm=PROFILER.getData(func)
if PROFILER.logUnknown==true then
if s==nil then s="<Unknown>" end
end
if s ~= nil then
if s~=nil then
-- Profile data.
local T = { func = s, src = src, line = line, count = count, tm = tm } -- #PROFILER.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
@@ -406,119 +435,113 @@ function PROFILER.showInfo( runTimeGame, runTimeOS )
if tserialize == nil then
tserialize = T
else
tserialize.count = tserialize.count + T.count
tserialize.tm = tserialize.tm + T.tm
tserialize.count=tserialize.count+T.count
tserialize.tm=tserialize.tm+T.tm
end
elseif s == "(for generator)" then
if tforgen == nil then
tforgen = T
elseif s=="(for generator)" then
if tforgen==nil then
tforgen=T
else
tforgen.count = tforgen.count + T.count
tforgen.tm = tforgen.tm + T.tm
tforgen.count=tforgen.count+T.count
tforgen.tm=tforgen.tm+T.tm
end
elseif s == "pairs" then
if tpairs == nil then
tpairs = T
elseif s=="pairs" then
if tpairs==nil then
tpairs=T
else
tpairs.count = tpairs.count + T.count
tpairs.tm = tpairs.tm + T.tm
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
Ttot=Ttot+tm
-- Total number of calls.
Calls = Calls + count
Calls=Calls+count
end
end
-- Add special cases.
-- Add special cases.
if tcopy then
table.insert( t, tcopy )
end
if tserialize then
table.insert( t, tserialize )
table.insert(t, tserialize)
end
if tforgen then
table.insert( t, tforgen )
end
if tpairs then
table.insert( t, tpairs )
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( "##############################################################################" )
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, "" )

View File

@@ -494,330 +494,343 @@ 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
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
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 path = {}
local useRoads
if not vars.disableRoads then
useRoads = true
else
useRoads = false
end
if headingDegrees then
heading = headingDegrees * math.pi / 180
end
local path = {}
if heading >= 2 * math.pi then
heading = heading - 2 * math.pi
end
if headingDegrees then
heading = headingDegrees*math.pi/180
end
local rndCoord = routines.getRandPointInCircle( point, radius, innerRadius )
if heading >= 2*math.pi then
heading = heading - 2*math.pi
end
local offset = {}
local posStart = routines.getLeadPos( group )
local rndCoord = routines.getRandPointInCircle(point, radius, innerRadius)
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 )
local offset = {}
local posStart = routines.getLeadPos(group)
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
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)
path[#path + 1] = routines.ground.buildWP( offset, form, speed )
path[#path + 1] = routines.ground.buildWP( rndCoord, form, speed )
routines.goRoute( group, path )
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
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 )
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
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 )
routines.groupToRandomPoint(vars)
return
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 )
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
end
routines.getLeadPos = function( group )
if type( group ) == 'string' then -- group name
group = Group.getByName( group )
end
local units = group:getUnits()
routines.getLeadPos = function(group)
if type(group) == 'string' then -- group name
group = Group.getByName(group)
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
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
end
--[[ vars for routines.getMGRSString:

View File

@@ -126,8 +126,11 @@ end
-- So length of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec map function:
--
-- * (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
--
function STTS.getSpeechTime( length, speed, isGoogle )
--
-- @param #number length can also be passed as #string
-- @param #number speed Defaults to 1.0
-- @param #boolean isGoogle We're using Google TTS
function STTS.getSpeechTime(length,speed,isGoogle)
local maxRateRatio = 3
@@ -153,7 +156,7 @@ function STTS.getSpeechTime( length, speed, isGoogle )
length = string.len( length )
end
return math.ceil( length / cps )
return length/cps --math.ceil(length/cps)
end
--- Text to speech function.

View File

@@ -0,0 +1,152 @@
--- **Utilities** - Socket.
--
-- **Main Features:**
--
-- * Creates UDP Sockets
-- * Send messages to Discord
-- * Compatible with [FunkMan](https://github.com/funkyfranky/FunkMan)
-- * Compatible with [DCSServerBot](https://github.com/Special-K-s-Flightsim-Bots/DCSServerBot)
--
-- ===
--
-- ### Author: **funkyfranky**
-- @module Utilities.Socket
-- @image Utilities_Socket.png
--- SOCKET class.
-- @type SOCKET
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity level.
-- @field #string lid Class id string for output to DCS log file.
-- @field #table socket The socket.
-- @field #number port The port.
-- @field #string host The host.
-- @field #table json JSON.
-- @extends Core.Fsm#FSM
--- **At times I feel like a socket that remembers its tooth.** -- Saul Bellow
--
-- ===
--
-- # The SOCKET Concept
--
-- Create a UDP socket server. It enables you to send messages to discord servers via discord bots.
--
-- **Note** that you have to **de-sanitize** `require` and `package` in your `MissionScripting.lua` file, which is in your `DCS/Scripts` folder.
--
--
-- @field #SOCKET
SOCKET = {
ClassName = "SOCKET",
verbose = 0,
lid = nil,
}
--- Data type. This is the keyword the socket listener uses.
-- @field #string TEXT Plain text.
-- @field #string BOMBRESULT Range bombing.
-- @field #string STRAFERESULT Range strafeing result.
-- @field #string LSOGRADE Airboss LSO grade.
SOCKET.DataType={
TEXT="moose_text",
BOMBRESULT="moose_bomb_result",
STRAFERESULT="moose_strafe_result",
LSOGRADE="moose_lso_grade",
}
--- SOCKET class version.
-- @field #string version
SOCKET.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot!
-- TODO: Messages as spoiler.
-- TODO: Send images?
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new SOCKET object.
-- @param #SOCKET self
-- @param #number Port UDP port. Default `10042`.
-- @param #string Host Host. Default `"127.0.0.1"`.
-- @return #SOCKET self
function SOCKET:New(Port, Host)
-- Inherit everything from FSM class.
local self=BASE:Inherit(self, FSM:New()) --#SOCKET
package.path = package.path..";.\\LuaSocket\\?.lua;"
package.cpath = package.cpath..";.\\LuaSocket\\?.dll;"
self.socket = require("socket")
self.port=Port or 10042
self.host=Host or "127.0.0.1"
self.json=loadfile("Scripts\\JSON.lua")()
self.UDPSendSocket=self.socket.udp()
self.UDPSendSocket:settimeout(0)
return self
end
--- Set port.
-- @param #SOCKET self
-- @param #number Port Port. Default 10042.
-- @return #SOCKET self
function SOCKET:SetPort(Port)
self.port=Port or 10042
end
--- Set host.
-- @param #SOCKET self
-- @param #string Host Host. Default `"127.0.0.1"`.
-- @return #SOCKET self
function SOCKET:SetHost(Host)
self.host=Host or "127.0.0.1"
end
--- Send a table.
-- @param #SOCKET self
-- @param #table Table Table to send.
-- @return #SOCKET self
function SOCKET:SendTable(Table)
local json= self.json:encode(Table)
-- Debug info.
self:T("Json table:")
self:T(json)
-- Send data.
self.socket.try(self.UDPSendSocket:sendto(json, self.host, self.port))
return self
end
--- Send a text message.
-- @param #SOCKET self
-- @param #string Text Test message.
-- @return #SOCKET self
function SOCKET:SendText(Text)
local message={}
message.command = SOCKET.DataType.TEXT
message.text = Text
self:SendTable(message)
return self
end

View File

@@ -1155,7 +1155,7 @@ function UTILS.VecHdg(a)
end
--- Calculate "heading" of a 2D vector in the X-Y plane.
-- @param DCS#Vec2 a Vector in "D with x, y components.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @return #number Heading in degrees in [0,360).
function UTILS.Vec2Hdg(a)
local h=math.deg(math.atan2(a.y, a.x))
@@ -1724,17 +1724,17 @@ end
--- Get OS time. Needs os to be desanitized!
-- @return #number Os time in seconds.
function UTILS.GetOSTime()
if os then
local ts = 0
local t = os.date("*t")
local s = t.sec
local m = t.min * 60
local h = t.hour * 3600
ts = s+m+h
return ts
else
return nil
end
if os then
local ts = 0
local t = os.date("*t")
local s = t.sec
local m = t.min * 60
local h = t.hour * 3600
ts = s+m+h
return ts
else
return nil
end
end
--- Shuffle a table accoring to Fisher Yeates algorithm