diff --git a/.gitignore b/.gitignore index c8cd38031..3496f3e86 100644 --- a/.gitignore +++ b/.gitignore @@ -222,3 +222,4 @@ _gsdata_/ #GITHUB .gitattributes .gitignore +Moose Test Missions/MOOSE_Test_Template.miz diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lauxlib.h b/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lauxlib.h new file mode 100644 index 000000000..34258235d --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lauxlib.h @@ -0,0 +1,174 @@ +/* +** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lauxlib_h +#define lauxlib_h + + +#include +#include + +#include "lua.h" + + +#if defined(LUA_COMPAT_GETN) +LUALIB_API int (luaL_getn) (lua_State *L, int t); +LUALIB_API void (luaL_setn) (lua_State *L, int t, int n); +#else +#define luaL_getn(L,i) ((int)lua_objlen(L, i)) +#define luaL_setn(L,i,j) ((void)0) /* no op! */ +#endif + +#if defined(LUA_COMPAT_OPENLIB) +#define luaI_openlib luaL_openlib +#endif + + +/* extra error code for `luaL_load' */ +#define LUA_ERRFILE (LUA_ERRERR+1) + + +typedef struct luaL_Reg { + const char *name; + lua_CFunction func; +} luaL_Reg; + + + +LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname, + const luaL_Reg *l, int nup); +LUALIB_API void (luaL_register) (lua_State *L, const char *libname, + const luaL_Reg *l); +LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname); +LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg, + size_t *l); +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg, + const char *def, size_t *l); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def); + +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg, + lua_Integer def); + +LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); +LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int narg); + +LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); +LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); + +LUALIB_API void (luaL_where) (lua_State *L, int lvl); +LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); + +LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def, + const char *const lst[]); + +LUALIB_API int (luaL_ref) (lua_State *L, int t); +LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); + +LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename); +LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, + const char *name); +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +LUALIB_API lua_State *(luaL_newstate) (void); + + +LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, + const char *r); + +LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx, + const char *fname, int szhint); + + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define luaL_argcheck(L, cond,numarg,extramsg) \ + ((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) + +#define luaL_dofile(L, fn) \ + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_dostring(L, s) \ + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) + +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + + +typedef struct luaL_Buffer { + char *p; /* current position in buffer */ + int lvl; /* number of strings in the stack (level) */ + lua_State *L; + char buffer[LUAL_BUFFERSIZE]; +} luaL_Buffer; + +#define luaL_addchar(B,c) \ + ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \ + (*(B)->p++ = (char)(c))) + +/* compatibility only */ +#define luaL_putchar(B,c) luaL_addchar(B,c) + +#define luaL_addsize(B,n) ((B)->p += (n)) + +LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); +LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B); +LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); +LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); + + +/* }====================================================== */ + + +/* compatibility with ref system */ + +/* pre-defined references */ +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + +#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \ + (lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0)) + +#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref)) + +#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref)) + + +#define luaL_reg luaL_Reg + +#endif + + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lua.h b/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lua.h new file mode 100644 index 000000000..a4b73e743 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lua.h @@ -0,0 +1,388 @@ +/* +** $Id: lua.h,v 1.218.1.7 2012/01/13 20:36:20 roberto Exp $ +** Lua - An Extensible Extension Language +** Lua.org, PUC-Rio, Brazil (http://www.lua.org) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lua_h +#define lua_h + +#include +#include + + +#include "luaconf.h" + + +#define LUA_VERSION "Lua 5.1" +#define LUA_RELEASE "Lua 5.1.5" +#define LUA_VERSION_NUM 501 +#define LUA_COPYRIGHT "Copyright (C) 1994-2012 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes" + + +/* mark for precompiled code (`Lua') */ +#define LUA_SIGNATURE "\033Lua" + +/* option for multiple returns in `lua_pcall' and `lua_call' */ +#define LUA_MULTRET (-1) + + +/* +** pseudo-indices +*/ +#define LUA_REGISTRYINDEX (-10000) +#define LUA_ENVIRONINDEX (-10001) +#define LUA_GLOBALSINDEX (-10002) +#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i)) + + +/* thread status; 0 is OK */ +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRERR 5 + + +typedef struct lua_State lua_State; + +typedef int (*lua_CFunction) (lua_State *L); + + +/* +** functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); + + +/* +** prototype for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + +/* +** basic types +*/ +#define LUA_TNONE (-1) + +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 + + + +/* minimum Lua stack available to a C function */ +#define LUA_MINSTACK 20 + + +/* +** generic extra include file +*/ +#if defined(LUA_USER_H) +#include LUA_USER_H +#endif + + +/* type of numbers in Lua */ +typedef LUA_NUMBER lua_Number; + + +/* type for integer functions */ +typedef LUA_INTEGER lua_Integer; + + + +/* +** state manipulation +*/ +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); +LUA_API void (lua_close) (lua_State *L); +LUA_API lua_State *(lua_newthread) (lua_State *L); + +LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); + + +/* +** basic stack manipulation +*/ +LUA_API int (lua_gettop) (lua_State *L); +LUA_API void (lua_settop) (lua_State *L, int idx); +LUA_API void (lua_pushvalue) (lua_State *L, int idx); +LUA_API void (lua_remove) (lua_State *L, int idx); +LUA_API void (lua_insert) (lua_State *L, int idx); +LUA_API void (lua_replace) (lua_State *L, int idx); +LUA_API int (lua_checkstack) (lua_State *L, int sz); + +LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); + + +/* +** access functions (stack -> C) +*/ + +LUA_API int (lua_isnumber) (lua_State *L, int idx); +LUA_API int (lua_isstring) (lua_State *L, int idx); +LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isuserdata) (lua_State *L, int idx); +LUA_API int (lua_type) (lua_State *L, int idx); +LUA_API const char *(lua_typename) (lua_State *L, int tp); + +LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2); + +LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx); +LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx); +LUA_API int (lua_toboolean) (lua_State *L, int idx); +LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); +LUA_API size_t (lua_objlen) (lua_State *L, int idx); +LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); +LUA_API void *(lua_touserdata) (lua_State *L, int idx); +LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); +LUA_API const void *(lua_topointer) (lua_State *L, int idx); + + +/* +** push functions (C -> stack) +*/ +LUA_API void (lua_pushnil) (lua_State *L); +LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); +LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); +LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l); +LUA_API void (lua_pushstring) (lua_State *L, const char *s); +LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, + va_list argp); +LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); +LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +LUA_API void (lua_pushboolean) (lua_State *L, int b); +LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); +LUA_API int (lua_pushthread) (lua_State *L); + + +/* +** get functions (Lua -> stack) +*/ +LUA_API void (lua_gettable) (lua_State *L, int idx); +LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawget) (lua_State *L, int idx); +LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n); +LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); +LUA_API int (lua_getmetatable) (lua_State *L, int objindex); +LUA_API void (lua_getfenv) (lua_State *L, int idx); + + +/* +** set functions (stack -> Lua) +*/ +LUA_API void (lua_settable) (lua_State *L, int idx); +LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawset) (lua_State *L, int idx); +LUA_API void (lua_rawseti) (lua_State *L, int idx, int n); +LUA_API int (lua_setmetatable) (lua_State *L, int objindex); +LUA_API int (lua_setfenv) (lua_State *L, int idx); + + +/* +** `load' and `call' functions (load and run Lua code) +*/ +LUA_API void (lua_call) (lua_State *L, int nargs, int nresults); +LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); +LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud); +LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname); + +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); + + +/* +** coroutine functions +*/ +LUA_API int (lua_yield) (lua_State *L, int nresults); +LUA_API int (lua_resume) (lua_State *L, int narg); +LUA_API int (lua_status) (lua_State *L); + +/* +** garbage-collection function and options +*/ + +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCSETPAUSE 6 +#define LUA_GCSETSTEPMUL 7 + +LUA_API int (lua_gc) (lua_State *L, int what, int data); + + +/* +** miscellaneous functions +*/ + +LUA_API int (lua_error) (lua_State *L); + +LUA_API int (lua_next) (lua_State *L, int idx); + +LUA_API void (lua_concat) (lua_State *L, int n); + +LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define lua_pop(L,n) lua_settop(L, -(n)-1) + +#define lua_newtable(L) lua_createtable(L, 0, 0) + +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) + +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) + +#define lua_strlen(L,i) lua_objlen(L, (i)) + +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) + +#define lua_pushliteral(L, s) \ + lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) + +#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s)) +#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s)) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + + +/* +** compatibility macros and functions +*/ + +#define lua_open() luaL_newstate() + +#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX) + +#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0) + +#define lua_Chunkreader lua_Reader +#define lua_Chunkwriter lua_Writer + + +/* hack */ +LUA_API void lua_setlevel (lua_State *from, lua_State *to); + + +/* +** {====================================================================== +** Debug API +** ======================================================================= +*/ + + +/* +** Event codes +*/ +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 +#define LUA_HOOKTAILRET 4 + + +/* +** Event masks +*/ +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) + +typedef struct lua_Debug lua_Debug; /* activation record */ + + +/* Functions to be called by the debuger in specific events */ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar); +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n); +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n); + +LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count); +LUA_API lua_Hook lua_gethook (lua_State *L); +LUA_API int lua_gethookmask (lua_State *L); +LUA_API int lua_gethookcount (lua_State *L); + + +struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) `global', `local', `field', `method' */ + const char *what; /* (S) `Lua', `C', `main', `tail' */ + const char *source; /* (S) */ + int currentline; /* (l) */ + int nups; /* (u) number of upvalues */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + int i_ci; /* active function */ +}; + +/* }====================================================================== */ + + +/****************************************************************************** +* Copyright (C) 1994-2012 Lua.org, PUC-Rio. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + + +#endif diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lua.hpp b/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lua.hpp new file mode 100644 index 000000000..ec417f594 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lua.hpp @@ -0,0 +1,9 @@ +// lua.hpp +// Lua header files for C++ +// <> not supplied automatically because Lua also compiles as C++ + +extern "C" { +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/luaconf.h b/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/luaconf.h new file mode 100644 index 000000000..e2cb26163 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/luaconf.h @@ -0,0 +1,763 @@ +/* +** $Id: luaconf.h,v 1.82.1.7 2008/02/11 16:25:08 roberto Exp $ +** Configuration file for Lua +** See Copyright Notice in lua.h +*/ + + +#ifndef lconfig_h +#define lconfig_h + +#include +#include + + +/* +** ================================================================== +** Search for "@@" to find all configurable definitions. +** =================================================================== +*/ + + +/* +@@ LUA_ANSI controls the use of non-ansi features. +** CHANGE it (define it) if you want Lua to avoid the use of any +** non-ansi feature or library. +*/ +#if defined(__STRICT_ANSI__) +#define LUA_ANSI +#endif + + +#if !defined(LUA_ANSI) && defined(_WIN32) +#define LUA_WIN +#endif + +#if defined(LUA_USE_LINUX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#define LUA_USE_READLINE /* needs some extra libraries */ +#endif + +#if defined(LUA_USE_MACOSX) +#define LUA_USE_POSIX +#define LUA_DL_DYLD /* does not need extra library */ +#endif + + + +/* +@@ LUA_USE_POSIX includes all functionallity listed as X/Open System +@* Interfaces Extension (XSI). +** CHANGE it (define it) if your system is XSI compatible. +*/ +#if defined(LUA_USE_POSIX) +#define LUA_USE_MKSTEMP +#define LUA_USE_ISATTY +#define LUA_USE_POPEN +#define LUA_USE_ULONGJMP +#endif + + +/* +@@ LUA_PATH and LUA_CPATH are the names of the environment variables that +@* Lua check to set its paths. +@@ LUA_INIT is the name of the environment variable that Lua +@* checks for initialization code. +** CHANGE them if you want different names. +*/ +#define LUA_PATH "LUA_PATH" +#define LUA_CPATH "LUA_CPATH" +#define LUA_INIT "LUA_INIT" + + +/* +@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for +@* Lua libraries. +@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for +@* C libraries. +** CHANGE them if your machine has a non-conventional directory +** hierarchy or if you want to install your libraries in +** non-conventional directories. +*/ +#if defined(_WIN32) +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_PATH_DEFAULT \ + ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua" +#define LUA_CPATH_DEFAULT \ + ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll" + +#else +#define LUA_ROOT "/usr/local/" +#define LUA_LDIR LUA_ROOT "share/lua/5.1/" +#define LUA_CDIR LUA_ROOT "lib/lua/5.1/" +#define LUA_PATH_DEFAULT \ + "./?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua" +#define LUA_CPATH_DEFAULT \ + "./?.so;" LUA_CDIR"?.so;" LUA_CDIR"loadall.so" +#endif + + +/* +@@ LUA_DIRSEP is the directory separator (for submodules). +** CHANGE it if your machine does not use "/" as the directory separator +** and is not Windows. (On Windows Lua automatically uses "\".) +*/ +#if defined(_WIN32) +#define LUA_DIRSEP "\\" +#else +#define LUA_DIRSEP "/" +#endif + + +/* +@@ LUA_PATHSEP is the character that separates templates in a path. +@@ LUA_PATH_MARK is the string that marks the substitution points in a +@* template. +@@ LUA_EXECDIR in a Windows path is replaced by the executable's +@* directory. +@@ LUA_IGMARK is a mark to ignore all before it when bulding the +@* luaopen_ function name. +** CHANGE them if for some reason your system cannot use those +** characters. (E.g., if one of those characters is a common character +** in file/directory names.) Probably you do not need to change them. +*/ +#define LUA_PATHSEP ";" +#define LUA_PATH_MARK "?" +#define LUA_EXECDIR "!" +#define LUA_IGMARK "-" + + +/* +@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger. +** CHANGE that if ptrdiff_t is not adequate on your machine. (On most +** machines, ptrdiff_t gives a good choice between int or long.) +*/ +#define LUA_INTEGER ptrdiff_t + + +/* +@@ LUA_API is a mark for all core API functions. +@@ LUALIB_API is a mark for all standard library functions. +** CHANGE them if you need to define those functions in some special way. +** For instance, if you want to create one Windows DLL with the core and +** the libraries, you may want to use the following definition (define +** LUA_BUILD_AS_DLL to get it). +*/ +#if defined(LUA_BUILD_AS_DLL) + +#if defined(LUA_CORE) || defined(LUA_LIB) +#define LUA_API __declspec(dllexport) +#else +#define LUA_API __declspec(dllimport) +#endif + +#else + +#define LUA_API extern + +#endif + +/* more often than not the libs go together with the core */ +#define LUALIB_API LUA_API + + +/* +@@ LUAI_FUNC is a mark for all extern functions that are not to be +@* exported to outside modules. +@@ LUAI_DATA is a mark for all extern (const) variables that are not to +@* be exported to outside modules. +** CHANGE them if you need to mark them in some special way. Elf/gcc +** (versions 3.2 and later) mark them as "hidden" to optimize access +** when Lua is compiled as a shared library. +*/ +#if defined(luaall_c) +#define LUAI_FUNC static +#define LUAI_DATA /* empty */ + +#elif defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ + defined(__ELF__) +#define LUAI_FUNC __attribute__((visibility("hidden"))) extern +#define LUAI_DATA LUAI_FUNC + +#else +#define LUAI_FUNC extern +#define LUAI_DATA extern +#endif + + + +/* +@@ LUA_QL describes how error messages quote program elements. +** CHANGE it if you want a different appearance. +*/ +#define LUA_QL(x) "'" x "'" +#define LUA_QS LUA_QL("%s") + + +/* +@@ LUA_IDSIZE gives the maximum size for the description of the source +@* of a function in debug information. +** CHANGE it if you want a different size. +*/ +#define LUA_IDSIZE 60 + + +/* +** {================================================================== +** Stand-alone configuration +** =================================================================== +*/ + +#if defined(lua_c) || defined(luaall_c) + +/* +@@ lua_stdin_is_tty detects whether the standard input is a 'tty' (that +@* is, whether we're running lua interactively). +** CHANGE it if you have a better definition for non-POSIX/non-Windows +** systems. +*/ +#if defined(LUA_USE_ISATTY) +#include +#define lua_stdin_is_tty() isatty(0) +#elif defined(LUA_WIN) +#include +#include +#define lua_stdin_is_tty() _isatty(_fileno(stdin)) +#else +#define lua_stdin_is_tty() 1 /* assume stdin is a tty */ +#endif + + +/* +@@ LUA_PROMPT is the default prompt used by stand-alone Lua. +@@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua. +** CHANGE them if you want different prompts. (You can also change the +** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.) +*/ +#define LUA_PROMPT "> " +#define LUA_PROMPT2 ">> " + + +/* +@@ LUA_PROGNAME is the default name for the stand-alone Lua program. +** CHANGE it if your stand-alone interpreter has a different name and +** your system is not able to detect that name automatically. +*/ +#define LUA_PROGNAME "lua" + + +/* +@@ LUA_MAXINPUT is the maximum length for an input line in the +@* stand-alone interpreter. +** CHANGE it if you need longer lines. +*/ +#define LUA_MAXINPUT 512 + + +/* +@@ lua_readline defines how to show a prompt and then read a line from +@* the standard input. +@@ lua_saveline defines how to "save" a read line in a "history". +@@ lua_freeline defines how to free a line read by lua_readline. +** CHANGE them if you want to improve this functionality (e.g., by using +** GNU readline and history facilities). +*/ +#if defined(LUA_USE_READLINE) +#include +#include +#include +#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) +#define lua_saveline(L,idx) \ + if (lua_strlen(L,idx) > 0) /* non-empty line? */ \ + add_history(lua_tostring(L, idx)); /* add it to history */ +#define lua_freeline(L,b) ((void)L, free(b)) +#else +#define lua_readline(L,b,p) \ + ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ + fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ +#define lua_saveline(L,idx) { (void)L; (void)idx; } +#define lua_freeline(L,b) { (void)L; (void)b; } +#endif + +#endif + +/* }================================================================== */ + + +/* +@@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles +@* as a percentage. +** CHANGE it if you want the GC to run faster or slower (higher values +** mean larger pauses which mean slower collection.) You can also change +** this value dynamically. +*/ +#define LUAI_GCPAUSE 200 /* 200% (wait memory to double before next GC) */ + + +/* +@@ LUAI_GCMUL defines the default speed of garbage collection relative to +@* memory allocation as a percentage. +** CHANGE it if you want to change the granularity of the garbage +** collection. (Higher values mean coarser collections. 0 represents +** infinity, where each step performs a full collection.) You can also +** change this value dynamically. +*/ +#define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */ + + + +/* +@@ LUA_COMPAT_GETN controls compatibility with old getn behavior. +** CHANGE it (define it) if you want exact compatibility with the +** behavior of setn/getn in Lua 5.0. +*/ +#undef LUA_COMPAT_GETN + +/* +@@ LUA_COMPAT_LOADLIB controls compatibility about global loadlib. +** CHANGE it to undefined as soon as you do not need a global 'loadlib' +** function (the function is still available as 'package.loadlib'). +*/ +#undef LUA_COMPAT_LOADLIB + +/* +@@ LUA_COMPAT_VARARG controls compatibility with old vararg feature. +** CHANGE it to undefined as soon as your programs use only '...' to +** access vararg parameters (instead of the old 'arg' table). +*/ +#define LUA_COMPAT_VARARG + +/* +@@ LUA_COMPAT_MOD controls compatibility with old math.mod function. +** CHANGE it to undefined as soon as your programs use 'math.fmod' or +** the new '%' operator instead of 'math.mod'. +*/ +#define LUA_COMPAT_MOD + +/* +@@ LUA_COMPAT_LSTR controls compatibility with old long string nesting +@* facility. +** CHANGE it to 2 if you want the old behaviour, or undefine it to turn +** off the advisory error when nesting [[...]]. +*/ +#define LUA_COMPAT_LSTR 1 + +/* +@@ LUA_COMPAT_GFIND controls compatibility with old 'string.gfind' name. +** CHANGE it to undefined as soon as you rename 'string.gfind' to +** 'string.gmatch'. +*/ +#define LUA_COMPAT_GFIND + +/* +@@ LUA_COMPAT_OPENLIB controls compatibility with old 'luaL_openlib' +@* behavior. +** CHANGE it to undefined as soon as you replace to 'luaL_register' +** your uses of 'luaL_openlib' +*/ +#define LUA_COMPAT_OPENLIB + + + +/* +@@ luai_apicheck is the assert macro used by the Lua-C API. +** CHANGE luai_apicheck if you want Lua to perform some checks in the +** parameters it gets from API calls. This may slow down the interpreter +** a bit, but may be quite useful when debugging C code that interfaces +** with Lua. A useful redefinition is to use assert.h. +*/ +#if defined(LUA_USE_APICHECK) +#include +#define luai_apicheck(L,o) { (void)L; assert(o); } +#else +#define luai_apicheck(L,o) { (void)L; } +#endif + + +/* +@@ LUAI_BITSINT defines the number of bits in an int. +** CHANGE here if Lua cannot automatically detect the number of bits of +** your machine. Probably you do not need to change this. +*/ +/* avoid overflows in comparison */ +#if INT_MAX-20 < 32760 +#define LUAI_BITSINT 16 +#elif INT_MAX > 2147483640L +/* int has at least 32 bits */ +#define LUAI_BITSINT 32 +#else +#error "you must define LUA_BITSINT with number of bits in an integer" +#endif + + +/* +@@ LUAI_UINT32 is an unsigned integer with at least 32 bits. +@@ LUAI_INT32 is an signed integer with at least 32 bits. +@@ LUAI_UMEM is an unsigned integer big enough to count the total +@* memory used by Lua. +@@ LUAI_MEM is a signed integer big enough to count the total memory +@* used by Lua. +** CHANGE here if for some weird reason the default definitions are not +** good enough for your machine. (The definitions in the 'else' +** part always works, but may waste space on machines with 64-bit +** longs.) Probably you do not need to change this. +*/ +#if LUAI_BITSINT >= 32 +#define LUAI_UINT32 unsigned int +#define LUAI_INT32 int +#define LUAI_MAXINT32 INT_MAX +#define LUAI_UMEM size_t +#define LUAI_MEM ptrdiff_t +#else +/* 16-bit ints */ +#define LUAI_UINT32 unsigned long +#define LUAI_INT32 long +#define LUAI_MAXINT32 LONG_MAX +#define LUAI_UMEM unsigned long +#define LUAI_MEM long +#endif + + +/* +@@ LUAI_MAXCALLS limits the number of nested calls. +** CHANGE it if you need really deep recursive calls. This limit is +** arbitrary; its only purpose is to stop infinite recursion before +** exhausting memory. +*/ +#define LUAI_MAXCALLS 20000 + + +/* +@@ LUAI_MAXCSTACK limits the number of Lua stack slots that a C function +@* can use. +** CHANGE it if you need lots of (Lua) stack space for your C +** functions. This limit is arbitrary; its only purpose is to stop C +** functions to consume unlimited stack space. (must be smaller than +** -LUA_REGISTRYINDEX) +*/ +#define LUAI_MAXCSTACK 8000 + + + +/* +** {================================================================== +** CHANGE (to smaller values) the following definitions if your system +** has a small C stack. (Or you may want to change them to larger +** values if your system has a large C stack and these limits are +** too rigid for you.) Some of these constants control the size of +** stack-allocated arrays used by the compiler or the interpreter, while +** others limit the maximum number of recursive calls that the compiler +** or the interpreter can perform. Values too large may cause a C stack +** overflow for some forms of deep constructs. +** =================================================================== +*/ + + +/* +@@ LUAI_MAXCCALLS is the maximum depth for nested C calls (short) and +@* syntactical nested non-terminals in a program. +*/ +#define LUAI_MAXCCALLS 200 + + +/* +@@ LUAI_MAXVARS is the maximum number of local variables per function +@* (must be smaller than 250). +*/ +#define LUAI_MAXVARS 200 + + +/* +@@ LUAI_MAXUPVALUES is the maximum number of upvalues per function +@* (must be smaller than 250). +*/ +#define LUAI_MAXUPVALUES 60 + + +/* +@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. +*/ +#define LUAL_BUFFERSIZE BUFSIZ + +/* }================================================================== */ + + + + +/* +** {================================================================== +@@ LUA_NUMBER is the type of numbers in Lua. +** CHANGE the following definitions only if you want to build Lua +** with a number type different from double. You may also need to +** change lua_number2int & lua_number2integer. +** =================================================================== +*/ + +#define LUA_NUMBER_DOUBLE +#define LUA_NUMBER double + +/* +@@ LUAI_UACNUMBER is the result of an 'usual argument conversion' +@* over a number. +*/ +#define LUAI_UACNUMBER double + + +/* +@@ LUA_NUMBER_SCAN is the format for reading numbers. +@@ LUA_NUMBER_FMT is the format for writing numbers. +@@ lua_number2str converts a number to a string. +@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. +@@ lua_str2number converts a string to a number. +*/ +#define LUA_NUMBER_SCAN "%lf" +#define LUA_NUMBER_FMT "%.14g" +#define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n)) +#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ +#define lua_str2number(s,p) strtod((s), (p)) + + +/* +@@ The luai_num* macros define the primitive operations over numbers. +*/ +#if defined(LUA_CORE) +#include +#define luai_numadd(a,b) ((a)+(b)) +#define luai_numsub(a,b) ((a)-(b)) +#define luai_nummul(a,b) ((a)*(b)) +#define luai_numdiv(a,b) ((a)/(b)) +#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b)) +#define luai_numpow(a,b) (pow(a,b)) +#define luai_numunm(a) (-(a)) +#define luai_numeq(a,b) ((a)==(b)) +#define luai_numlt(a,b) ((a)<(b)) +#define luai_numle(a,b) ((a)<=(b)) +#define luai_numisnan(a) (!luai_numeq((a), (a))) +#endif + + +/* +@@ lua_number2int is a macro to convert lua_Number to int. +@@ lua_number2integer is a macro to convert lua_Number to lua_Integer. +** CHANGE them if you know a faster way to convert a lua_Number to +** int (with any rounding method and without throwing errors) in your +** system. In Pentium machines, a naive typecast from double to int +** in C is extremely slow, so any alternative is worth trying. +*/ + +/* On a Pentium, resort to a trick */ +#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \ + (defined(__i386) || defined (_M_IX86) || defined(__i386__)) + +/* On a Microsoft compiler, use assembler */ +#if defined(_MSC_VER) + +#define lua_number2int(i,d) __asm fld d __asm fistp i +#define lua_number2integer(i,n) lua_number2int(i, n) + +/* the next trick should work on any Pentium, but sometimes clashes + with a DirectX idiosyncrasy */ +#else + +union luai_Cast { double l_d; long l_l; }; +#define lua_number2int(i,d) \ + { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; } +#define lua_number2integer(i,n) lua_number2int(i, n) + +#endif + + +/* this option always works, but may be slow */ +#else +#define lua_number2int(i,d) ((i)=(int)(d)) +#define lua_number2integer(i,d) ((i)=(lua_Integer)(d)) + +#endif + +/* }================================================================== */ + + +/* +@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment. +** CHANGE it if your system requires alignments larger than double. (For +** instance, if your system supports long doubles and they must be +** aligned in 16-byte boundaries, then you should add long double in the +** union.) Probably you do not need to change this. +*/ +#define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; } + + +/* +@@ LUAI_THROW/LUAI_TRY define how Lua does exception handling. +** CHANGE them if you prefer to use longjmp/setjmp even with C++ +** or if want/don't to use _longjmp/_setjmp instead of regular +** longjmp/setjmp. By default, Lua handles errors with exceptions when +** compiling as C++ code, with _longjmp/_setjmp when asked to use them, +** and with longjmp/setjmp otherwise. +*/ +#if defined(__cplusplus) +/* C++ exceptions */ +#define LUAI_THROW(L,c) throw(c) +#define LUAI_TRY(L,c,a) try { a } catch(...) \ + { if ((c)->status == 0) (c)->status = -1; } +#define luai_jmpbuf int /* dummy variable */ + +#elif defined(LUA_USE_ULONGJMP) +/* in Unix, try _longjmp/_setjmp (more efficient) */ +#define LUAI_THROW(L,c) _longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#else +/* default handling with long jumps */ +#define LUAI_THROW(L,c) longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#endif + + +/* +@@ LUA_MAXCAPTURES is the maximum number of captures that a pattern +@* can do during pattern-matching. +** CHANGE it if you need more captures. This limit is arbitrary. +*/ +#define LUA_MAXCAPTURES 32 + + +/* +@@ lua_tmpnam is the function that the OS library uses to create a +@* temporary name. +@@ LUA_TMPNAMBUFSIZE is the maximum size of a name created by lua_tmpnam. +** CHANGE them if you have an alternative to tmpnam (which is considered +** insecure) or if you want the original tmpnam anyway. By default, Lua +** uses tmpnam except when POSIX is available, where it uses mkstemp. +*/ +#if defined(loslib_c) || defined(luaall_c) + +#if defined(LUA_USE_MKSTEMP) +#include +#define LUA_TMPNAMBUFSIZE 32 +#define lua_tmpnam(b,e) { \ + strcpy(b, "/tmp/lua_XXXXXX"); \ + e = mkstemp(b); \ + if (e != -1) close(e); \ + e = (e == -1); } + +#else +#define LUA_TMPNAMBUFSIZE L_tmpnam +#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } +#endif + +#endif + + +/* +@@ lua_popen spawns a new process connected to the current one through +@* the file streams. +** CHANGE it if you have a way to implement it in your system. +*/ +#if defined(LUA_USE_POPEN) + +#define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m)) +#define lua_pclose(L,file) ((void)L, (pclose(file) != -1)) + +#elif defined(LUA_WIN) + +#define lua_popen(L,c,m) ((void)L, _popen(c,m)) +#define lua_pclose(L,file) ((void)L, (_pclose(file) != -1)) + +#else + +#define lua_popen(L,c,m) ((void)((void)c, m), \ + luaL_error(L, LUA_QL("popen") " not supported"), (FILE*)0) +#define lua_pclose(L,file) ((void)((void)L, file), 0) + +#endif + +/* +@@ LUA_DL_* define which dynamic-library system Lua should use. +** CHANGE here if Lua has problems choosing the appropriate +** dynamic-library system for your platform (either Windows' DLL, Mac's +** dyld, or Unix's dlopen). If your system is some kind of Unix, there +** is a good chance that it has dlopen, so LUA_DL_DLOPEN will work for +** it. To use dlopen you also need to adapt the src/Makefile (probably +** adding -ldl to the linker options), so Lua does not select it +** automatically. (When you change the makefile to add -ldl, you must +** also add -DLUA_USE_DLOPEN.) +** If you do not want any kind of dynamic library, undefine all these +** options. +** By default, _WIN32 gets LUA_DL_DLL and MAC OS X gets LUA_DL_DYLD. +*/ +#if defined(LUA_USE_DLOPEN) +#define LUA_DL_DLOPEN +#endif + +#if defined(LUA_WIN) +#define LUA_DL_DLL +#endif + + +/* +@@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State +@* (the data goes just *before* the lua_State pointer). +** CHANGE (define) this if you really need that. This value must be +** a multiple of the maximum alignment required for your machine. +*/ +#define LUAI_EXTRASPACE 0 + + +/* +@@ luai_userstate* allow user-specific actions on threads. +** CHANGE them if you defined LUAI_EXTRASPACE and need to do something +** extra when a thread is created/deleted/resumed/yielded. +*/ +#define luai_userstateopen(L) ((void)L) +#define luai_userstateclose(L) ((void)L) +#define luai_userstatethread(L,L1) ((void)L) +#define luai_userstatefree(L) ((void)L) +#define luai_userstateresume(L,n) ((void)L) +#define luai_userstateyield(L,n) ((void)L) + + +/* +@@ LUA_INTFRMLEN is the length modifier for integer conversions +@* in 'string.format'. +@@ LUA_INTFRM_T is the integer type correspoding to the previous length +@* modifier. +** CHANGE them if your system supports long long or does not support long. +*/ + +#if defined(LUA_USELONGLONG) + +#define LUA_INTFRMLEN "ll" +#define LUA_INTFRM_T long long + +#else + +#define LUA_INTFRMLEN "l" +#define LUA_INTFRM_T long + +#endif + + + +/* =================================================================== */ + +/* +** Local configuration. You can use this space to add your redefinitions +** without modifying the main part of the file. +*/ + + + +#endif + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lualib.h b/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lualib.h new file mode 100644 index 000000000..469417f67 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lualib.h @@ -0,0 +1,53 @@ +/* +** $Id: lualib.h,v 1.36.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua standard libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lualib_h +#define lualib_h + +#include "lua.h" + + +/* Key to file-handle type */ +#define LUA_FILEHANDLE "FILE*" + + +#define LUA_COLIBNAME "coroutine" +LUALIB_API int (luaopen_base) (lua_State *L); + +#define LUA_TABLIBNAME "table" +LUALIB_API int (luaopen_table) (lua_State *L); + +#define LUA_IOLIBNAME "io" +LUALIB_API int (luaopen_io) (lua_State *L); + +#define LUA_OSLIBNAME "os" +LUALIB_API int (luaopen_os) (lua_State *L); + +#define LUA_STRLIBNAME "string" +LUALIB_API int (luaopen_string) (lua_State *L); + +#define LUA_MATHLIBNAME "math" +LUALIB_API int (luaopen_math) (lua_State *L); + +#define LUA_DBLIBNAME "debug" +LUALIB_API int (luaopen_debug) (lua_State *L); + +#define LUA_LOADLIBNAME "package" +LUALIB_API int (luaopen_package) (lua_State *L); + + +/* open all previous libraries */ +LUALIB_API void (luaL_openlibs) (lua_State *L); + + + +#ifndef lua_assert +#define lua_assert(x) ((void)0) +#endif + + +#endif diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/liblua.a b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/liblua.a new file mode 100644 index 000000000..f4d65be64 Binary files /dev/null and b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/liblua.a differ diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/lua/5.1/checks.dll b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/lua/5.1/checks.dll new file mode 100644 index 000000000..98254c4f4 Binary files /dev/null and b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/lua/5.1/checks.dll differ diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/lua/5.1/lfs.dll b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/lua/5.1/lfs.dll new file mode 100644 index 000000000..6e73ea2c2 Binary files /dev/null and b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/lua/5.1/lfs.dll differ diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/checks/1.0-1/checks-1.0-1.rockspec b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/checks/1.0-1/checks-1.0-1.rockspec new file mode 100644 index 000000000..9f9cae083 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/checks/1.0-1/checks-1.0-1.rockspec @@ -0,0 +1,36 @@ +--*-lua-*- +package = "checks" +version = "1.0-1" +source = { + url = "https://github.com/fab13n/checks/raw/master/checks-1.0-1.tar.gz", + dir = "checks", +} + +description = { + summary = "Easy, terse, readable and fast function arguments type checking", + detailed = [[ + This library declares a `checks()` function and a + `checkers` table, which allow to check the parameters + passed to a Lua function in a fast and unobtrusive way. + + Althought provided here as a standalone library, it is + part of Sierra Wireless' Aleos Applicaion Framework, + available under the Eclipse Public License, currently at: + + https://github.com/SierraWireless/luasched + + ]], + homepage = "https://github.com/SierraWireless/luasched", + license = "Eclipse public license" +} + +dependencies = { + "lua >= 5.1" +} + +build = { + type = 'builtin', + modules = { + checks = 'checks.c' + } +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/checks/1.0-1/rock_manifest b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/checks/1.0-1/rock_manifest new file mode 100644 index 000000000..07ae2133a --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/checks/1.0-1/rock_manifest @@ -0,0 +1,6 @@ +rock_manifest = { + ["checks-1.0-1.rockspec"] = "70af2f5dd173774a16b9e7cfdfd12ecd", + lib = { + ["checks.dll"] = "5342726d76176ca95ebe25c7b07ca6ac" + } +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/doc/LICENSE b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/doc/LICENSE new file mode 100644 index 000000000..11ecb7958 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/doc/LICENSE @@ -0,0 +1,198 @@ +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM +CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation + distributed under this Agreement, and +b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + + where such changes and/or additions to the Program originate from and are + distributed by that particular Contributor. A Contribution 'originates' from + a Contributor if it was added to the Program by such Contributor itself or + anyone acting on such Contributor's behalf. Contributions do not include + additions to the Program which: (i) are separate modules of software + distributed in conjunction with the Program under their own license + agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are +necessarily infringed by the use or sale of its Contribution alone or when +combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + +2. GRANT OF RIGHTS + a) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free copyright license to + reproduce, prepare derivative works of, publicly display, publicly perform, + distribute and sublicense the Contribution of such Contributor, if any, and + such derivative works, in source code and object code form. + b) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free patent license under + Licensed Patents to make, use, sell, offer to sell, import and otherwise + transfer the Contribution of such Contributor, if any, in source code and + object code form. This patent license shall apply to the combination of the + Contribution and the Program if, at the time the Contribution is added by + the Contributor, such addition of the Contribution causes such combination + to be covered by the Licensed Patents. The patent license shall not apply + to any other combinations which include the Contribution. No hardware per + se is licensed hereunder. + c) Recipient understands that although each Contributor grants the licenses to + its Contributions set forth herein, no assurances are provided by any + Contributor that the Program does not infringe the patent or other + intellectual property rights of any other entity. Each Contributor + disclaims any liability to Recipient for claims brought by any other entity + based on infringement of intellectual property rights or otherwise. As a + condition to exercising the rights and licenses granted hereunder, each + Recipient hereby assumes sole responsibility to secure any other + intellectual property rights needed, if any. For example, if a third party + patent license is required to allow Recipient to distribute the Program, it + is Recipient's responsibility to acquire that license before distributing + the Program. + d) Each Contributor represents that to its knowledge it has sufficient + copyright rights in its Contribution, if any, to grant the copyright + license set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its +own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + b) its license agreement: + i) effectively disclaims on behalf of all Contributors all warranties and + conditions, express and implied, including warranties or conditions of + title and non-infringement, and implied warranties or conditions of + merchantability and fitness for a particular purpose; + ii) effectively excludes on behalf of all Contributors all liability for + damages, including direct, indirect, special, incidental and + consequential damages, such as lost profits; + iii) states that any provisions which differ from this Agreement are offered + by that Contributor alone and not by any other party; and + iv) states that source code for the Program is available from such + Contributor, and informs licensees how to obtain it in a reasonable + manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + b) a copy of this Agreement must be included with each copy of the Program. + Contributors may not remove or alter any copyright notices contained within + the Program. + +Each Contributor must identify itself as the originator of its Contribution, if +any, in a manner that reasonably allows subsequent Recipients to identify the +originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with +respect to end users, business partners and the like. While this license is +intended to facilitate the commercial use of the Program, the Contributor who +includes the Program in a commercial product offering should do so in a manner +which does not create potential liability for other Contributors. Therefore, if +a Contributor includes the Program in a commercial product offering, such +Contributor ("Commercial Contributor") hereby agrees to defend and indemnify +every other Contributor ("Indemnified Contributor") against any losses, damages +and costs (collectively "Losses") arising from claims, lawsuits and other legal +actions brought by a third party against the Indemnified Contributor to the +extent caused by the acts or omissions of such Commercial Contributor in +connection with its distribution of the Program in a commercial product +offering. The obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In order +to qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial Contributor to +control, and cooperate with the Commercial Contributor in, the defense and any +related settlement negotiations. The Indemnified Contributor may participate in +any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product +offering, Product X. That Contributor is then a Commercial Contributor. If that +Commercial Contributor then makes performance claims, or offers warranties +related to Product X, those performance claims and warranties are such +Commercial Contributor's responsibility alone. Under this section, the +Commercial Contributor would have to defend claims against the other +Contributors related to those performance claims and warranties, and if a court +requires any other Contributor to pay any damages as a result, the Commercial +Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each +Recipient is solely responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its exercise of +rights under this Agreement , including but not limited to the risks and costs +of program errors, compliance with applicable laws, damage to or loss of data, +programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY +CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST +PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS +GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable +law, it shall not affect the validity or enforceability of the remainder of the +terms of this Agreement, and without further action by the parties hereto, such +provision shall be reformed to the minimum extent necessary to make such +provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Program itself +(excluding combinations of the Program with other software or hardware) +infringes such Recipient's patent(s), then such Recipient's rights granted under +Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to +comply with any of the material terms or conditions of this Agreement and does +not cure such failure in a reasonable period of time after becoming aware of +such noncompliance. If all Recipient's rights under this Agreement terminate, +Recipient agrees to cease use and distribution of the Program as soon as +reasonably practicable. However, Recipient's obligations under this Agreement +and any licenses granted by Recipient relating to the Program shall continue and +survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in +order to avoid inconsistency the Agreement is copyrighted and may only be +modified in the following manner. The Agreement Steward reserves the right to +publish new versions (including revisions) of this Agreement from time to time. +No one other than the Agreement Steward has the right to modify this Agreement. +The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation +may assign the responsibility to serve as the Agreement Steward to a suitable +separate entity. Each new version of the Agreement will be given a +distinguishing version number. The Program (including Contributions) may always +be distributed subject to the version of the Agreement under which it was +received. In addition, after a new version of the Agreement is published, +Contributor may elect to distribute the Program (including its Contributions) +under the new version. Except as expressly stated in Sections 2(a) and 2(b) +above, Recipient receives no rights or licenses to the intellectual property of +any Contributor under this Agreement, whether expressly, by implication, +estoppel or otherwise. All rights in the Program not expressly granted under +this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to this +Agreement will bring a legal action under this Agreement more than one year +after the cause of action arose. Each party waives its rights to a jury trial in +any resulting litigation. diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/doc/README.md b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/doc/README.md new file mode 100644 index 000000000..03611d630 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/doc/README.md @@ -0,0 +1,7 @@ +# Lua Documentor + +LuaDocumentor allow users to generate HTML and API files from code documented +using Lua documentation language. + +Documentation is +[available here](http://wiki.eclipse.org/Koneki/LDT/User_Area/LuaDocumentor). diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/luadocumentor-0.1.5-1.rockspec b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/luadocumentor-0.1.5-1.rockspec new file mode 100644 index 000000000..9ed686c3f --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/luadocumentor-0.1.5-1.rockspec @@ -0,0 +1,57 @@ +package = 'LuaDocumentor' +version = '0.1.5-1' +description = { + summary = 'LuaDocumentor allow users to generate HTML and API files from code documented using Lua documentation language.', + detailed = [[ + This is an example for the LuaRocks tutorial. + Here we would put a detailed, typically + paragraph-long description. + ]], + homepage = 'http://wiki.eclipse.org/Koneki/LDT/User_Area/LuaDocumentor', + license = 'EPL' +} +source = { + url = 'git://github.com/LuaDevelopmentTools/luadocumentor.git', + tag = 'v0.1.5-1' +} +dependencies = { + 'lua ~> 5.1', + 'luafilesystem ~> 1.6', + 'markdown ~> 0.32', + 'metalua-compiler ~> 0.7', + 'penlight ~> 0.9' +} +build = { + type = 'builtin', + install = { + bin = { + luadocumentor = 'luadocumentor.lua' + }, + lua = { + ['models.internalmodelbuilder'] = 'models/internalmodelbuilder.mlua' + } + }, + modules = { + defaultcss = 'defaultcss.lua', + docgenerator = 'docgenerator.lua', + extractors = 'extractors.lua', + lddextractor = 'lddextractor.lua', + templateengine = 'templateengine.lua', + + ['fs.lfs'] = 'fs/lfs.lua', + + ['models.apimodel'] = 'models/apimodel.lua', + ['models.apimodelbuilder'] = 'models/apimodelbuilder.lua', + ['models.internalmodel'] = 'models/internalmodel.lua', + ['models.ldparser'] = 'models/ldparser.lua', + + ['template.file'] = 'template/file.lua', + ['template.index'] = 'template/index.lua', + ['template.index.recordtypedef'] = 'template/index/recordtypedef.lua', + ['template.item'] = 'template/item.lua', + ['template.page'] = 'template/page.lua', + ['template.recordtypedef'] = 'template/recordtypedef.lua', + ['template.usage'] = 'template/usage.lua', + ['template.utils'] = 'template/utils.lua', + } +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/rock_manifest b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/rock_manifest new file mode 100644 index 000000000..c286d43bd --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/rock_manifest @@ -0,0 +1,39 @@ +rock_manifest = { + bin = { + luadocumentor = "bc5cc07f56db2cf1dbe80f0827332873" + }, + doc = { + LICENSE = "52a21f73ac77fd790dc40dc5acda0fc2", + ["README.md"] = "fcef1f43c69f3559b347d854b2626deb" + }, + lua = { + ["defaultcss.lua"] = "dd9b2b89e5080972bbb52056247c0c65", + ["docgenerator.lua"] = "92d0a3947d88226340014d2f033be37f", + ["extractors.lua"] = "74191695e5217706ee355925e5ca40fa", + fs = { + ["lfs.lua"] = "4d00f9bc942b02a86ccea16544d3e85d" + }, + ["lddextractor.lua"] = "56edde775a5d57818aa0a07b4f723536", + models = { + ["apimodel.lua"] = "3c401de18691b1222b0ad253958260ee", + ["apimodelbuilder.lua"] = "4c4a3c0b48b404973542dd99f994eb2c", + ["internalmodel.lua"] = "a1a21e50af8db0f0a0b9d164ccc08853", + ["internalmodelbuilder.mlua"] = "ff95dfca573ccc1c19a79434e96a492d", + ["ldparser.lua"] = "538904a3adbfff4ff83deda029847323" + }, + template = { + ["file.lua"] = "41f095bc049ef161060d8e3b4ac9de63", + index = { + ["recordtypedef.lua"] = "0977ff0048a837389c2ac10285eb1ce1" + }, + ["index.lua"] = "5a3b3cface3b1fd9cb2d56f1edd5487b", + ["item.lua"] = "5d5a6d9bffd8935c4ed283105ede331b", + ["page.lua"] = "351f4a7215272f7e448faeece4945bc0", + ["recordtypedef.lua"] = "69938e1d60e94eed7f95b0999f1386ca", + ["usage.lua"] = "979503deb84877cb221130a5be7c1535", + ["utils.lua"] = "ad97fb4e3de9fb6480b25cdd877b50d9" + }, + ["templateengine.lua"] = "09bfc6350e14f4ab509d14fb0fb295c0" + }, + ["luadocumentor-0.1.5-1.rockspec"] = "4ba1b88898dce89e7fd8fb6a700496a4" +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/doc.css b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/doc.css new file mode 100644 index 000000000..e816a7e2c --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/doc.css @@ -0,0 +1,212 @@ +body { + margin-left: 1em; + margin-right: 1em; + font-family: arial, helvetica, geneva, sans-serif; + background-color:#ffffff; margin:0px; +} + +code { + font-family: "Andale Mono", monospace; +} + +tt { + font-family: "Andale Mono", monospace; +} + +body, td, th { font-size: 11pt; } + +h1, h2, h3, h4 { margin-left: 0em; } + +textarea, pre, tt { font-size:10pt; } +body, td, th { color:#000000; } +small { font-size:0.85em; } +h1 { font-size:1.5em; } +h2 { font-size:1.25em; } +h3 { font-size:1.15em; } +h4 { font-size:1.06em; } + +a:link { font-weight:bold; color: #004080; text-decoration: none; } +a:visited { font-weight:bold; color: #006699; text-decoration: none; } +a:link:hover { text-decoration:underline; } +hr { color:#cccccc } +img { border-width: 0px; } + +h3 { padding-top: 1em; } + +p { margin-left: 1em; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; + margin-left: 0em; +} + +blockquote { margin-left: 3em; } + +.example { + background-color: rgb(245, 245, 245); + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-style: solid; + border-right-style: solid; + border-bottom-style: solid; + border-left-style: solid; + border-top-color: silver; + border-right-color: silver; + border-bottom-color: silver; + border-left-color: silver; + padding: 1em; + margin-left: 1em; + margin-right: 1em; + font-family: "Andale Mono", monospace; + font-size: smaller; +} + +hr { + margin-left: 0em; + background: #00007f; + border: 0px; + height: 1px; +} + +ul { list-style-type: disc; } + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } +table.index ul { padding-top: 0em; margin-top: 0em; } + +table { + border: 1px solid black; + border-collapse: collapse; + margin-left: auto; + margin-right: auto; +} + +th { + border: 1px solid black; + padding: 0.5em; +} + +td { + border: 1px solid black; + padding: 0.5em; +} +div.header, div.footer { margin-left: 0em; } + +#container { + margin-left: 1em; + margin-right: 1em; + background-color: #f0f0f0; +} + +#product { + text-align: center; + border-bottom: 1px solid #cccccc; + background-color: #ffffff; +} + +#product big { + font-size: 2em; +} + +#product_logo { +} + +#product_name { +} + +#product_description { +} + +#main { + background-color: #f0f0f0; + border-left: 2px solid #cccccc; +} + +#navigation { + float: left; + width: 12em; + margin: 0; + vertical-align: top; + background-color: #f0f0f0; + overflow:visible; +} + +#navigation h1 { + background-color:#e7e7e7; + font-size:1.1em; + color:#000000; + text-align:left; + margin:0px; + padding:0.2em; + border-top:1px solid #dddddd; + border-bottom:1px solid #dddddd; +} + +#navigation ul { + font-size:1em; + list-style-type: none; + padding: 0; + margin: 1px; +} + +#navigation li { + text-indent: -1em; + margin: 0em 0em 0em 0.5em; + display: block; + padding: 3px 0px 0px 12px; +} + +#navigation li li a { + padding: 0px 3px 0px -1em; +} + +#content { + margin-left: 12em; + padding: 1em; + border-left: 2px solid #cccccc; + border-right: 2px solid #cccccc; + background-color: #ffffff; +} + +#about { + clear: both; + margin: 0; + padding: 5px; + border-top: 2px solid #cccccc; + background-color: #ffffff; +} + +@media print { + body { + font: 10pt "Times New Roman", "TimeNR", Times, serif; + } + a { + font-weight:bold; color: #004080; text-decoration: underline; + } + #main { + background-color: #ffffff; border-left: 0px; + } + #container { + margin-left: 2%; margin-right: 2%; background-color: #ffffff; + } + #content { + margin-left: 0px; padding: 1em; border-left: 0px; border-right: 0px; background-color: #ffffff; + } + #navigation { + display: none; + } + #product_logo { + display: none; + } + #about img { + display: none; + } + .example { + font-family: "Andale Mono", monospace; + font-size: 8pt; + page-break-inside: avoid; + } +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/examples.html b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/examples.html new file mode 100644 index 000000000..2c1644cb8 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/examples.html @@ -0,0 +1,103 @@ + + + + LuaFileSystem + + + + + + +
+ +
+ +
LuaFileSystem
+
File System Library for the Lua Programming Language
+
+ +
+ + + +
+ +

Examples

+ +

Directory iterator

+ +

The following example iterates over a directory and recursively lists the +attributes for each file inside it.

+ +
+local lfs = require"lfs"
+
+function attrdir (path)
+    for file in lfs.dir(path) do
+        if file ~= "." and file ~= ".." then
+            local f = path..'/'..file
+            print ("\t "..f)
+            local attr = lfs.attributes (f)
+            assert (type(attr) == "table")
+            if attr.mode == "directory" then
+                attrdir (f)
+            else
+                for name, value in pairs(attr) do
+                    print (name, value)
+                end
+            end
+        end
+    end
+end
+
+attrdir (".")
+
+ +
+ +
+ +
+

Valid XHTML 1.0!

+

$Id: examples.html,v 1.8 2007/12/14 15:28:04 carregal Exp $

+
+ +
+ + + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/index.html b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/index.html new file mode 100644 index 000000000..2bb7f5d2c --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/index.html @@ -0,0 +1,218 @@ + + + + LuaFileSystem + + + + + + +
+ +
+ +
LuaFileSystem
+
File System Library for the Lua Programming Language
+
+ +
+ + + +
+ +

Overview

+ +

LuaFileSystem is a Lua library +developed to complement the set of functions related to file +systems offered by the standard Lua distribution.

+ +

LuaFileSystem offers a portable way to access +the underlying directory structure and file attributes.

+ +

LuaFileSystem is free software and uses the same +license as Lua 5.1.

+ +

Status

+ +

Current version is 1.6.3. It works with Lua 5.1, 5.2 and 5.3.

+ +

Download

+ +

LuaFileSystem source can be downloaded from its +Github +page.

+ +

History

+ +
+
Version 1.6.3 [15/Jan/2015]
+
    +
  • Lua 5.3 support.
  • +
  • Assorted bugfixes.
  • +
+ +
Version 1.6.2 [??/Oct/2012]
+
    +
  • Full Lua 5.2 compatibility (with Lua 5.1 fallbacks)
  • +
+ +
Version 1.6.1 [01/Oct/2012]
+
    +
  • fix build for Lua 5.2
  • +
+ +
Version 1.6.0 [26/Sep/2012]
+
    +
  • getcwd fix for Android
  • +
  • support for Lua 5.2
  • +
  • add lfs.link
  • +
  • other bug fixes
  • +
+ +
Version 1.5.0 [20/Oct/2009]
+
    +
  • Added explicit next and close methods to second return value of lfs.dir +(the directory object), for explicit iteration or explicit closing.
  • +
  • Added directory locking via lfs.lock_dir function (see the manual).
  • +
+
Version 1.4.2 [03/Feb/2009]
+
+
    +
  • fixed bug [#13198] + lfs.attributes(filename, 'size') overflow on files > 2 Gb again (bug report and patch by KUBO Takehiro).
  • +
  • fixed bug [#39794] + Compile error on Solaris 10 (bug report and patch by Aaron B).
  • +
  • fixed compilation problems with Borland C.
  • +
+
+ +
Version 1.4.1 [07/May/2008]
+
+
    +
  • documentation review
  • +
  • fixed Windows compilation issues
  • +
  • fixed bug in the Windows tests (patch by Shmuel Zeigerman)
  • +
  • fixed bug [#2185] + lfs.attributes(filename, 'size') overflow on files > 2 Gb +
  • +
+
+ +
Version 1.4.0 [13/Feb/2008]
+
+
    +
  • added function + lfs.setmode + (works only in Windows systems).
  • +
  • lfs.attributes + raises an error if attribute does not exist
  • +
+
+ +
Version 1.3.0 [26/Oct/2007]
+
+ +
+ +
Version 1.2.1 [08/May/2007]
+
+
    +
  • compatible only with Lua 5.1 (Lua 5.0 support was dropped)
  • +
+
+ +
Version 1.2 [15/Mar/2006]
+
+ +
+ +
Version 1.1 [30/May/2005]
+
+ +
+ +
Version 1.0 [21/Jan/2005]
+
+ +
Version 1.0 Beta [10/Nov/2004]
+
+
+ +

Credits

+ +

LuaFileSystem was designed by Roberto Ierusalimschy, +André Carregal and Tomás Guisasola as part of the +Kepler Project, +which holds its copyright. LuaFileSystem is currently maintained by Fábio Mascarenhas.

+ +

Contact us

+ +

For more information please +contact us. +Comments are welcome!

+ +

You can also reach other Kepler developers and users on the Kepler Project +mailing list.

+ +
+ +
+ +
+

Valid XHTML 1.0!

+

$Id: index.html,v 1.44 2009/02/04 21:21:33 carregal Exp $

+
+ +
+ + + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/license.html b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/license.html new file mode 100644 index 000000000..300338172 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/license.html @@ -0,0 +1,122 @@ + + + + LuaFileSystem + + + + + + +
+ +
+ +
LuaFileSystem
+
File System Library for the Lua Programming Language
+
+ +
+ + + +
+ +

License

+ +

+LuaFileSystem is free software: it can be used for both academic +and commercial purposes at absolutely no cost. There are no +royalties or GNU-like "copyleft" restrictions. LuaFileSystem +qualifies as +Open Source +software. +Its licenses are compatible with +GPL. +LuaFileSystem is not in the public domain and the +Kepler Project +keep its copyright. +The legal details are below. +

+ +

The spirit of the license is that you are free to use +LuaFileSystem for any purpose at no cost without having to ask us. +The only requirement is that if you do use LuaFileSystem, then you +should give us credit by including the appropriate copyright notice +somewhere in your product or its documentation.

+ +

The LuaFileSystem library is designed and implemented by Roberto +Ierusalimschy, André Carregal and Tomás Guisasola. +The implementation is not derived from licensed software.

+ +
+

Copyright © 2003 Kepler Project.

+ +

Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions:

+ +

The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software.

+ +

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.

+ +
+ +
+ +
+

Valid XHTML 1.0!

+

$Id: license.html,v 1.13 2008/02/11 22:42:21 carregal Exp $

+
+ +
+ + + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/luafilesystem.png b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/luafilesystem.png new file mode 100644 index 000000000..e1dd8c65b Binary files /dev/null and b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/luafilesystem.png differ diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/manual.html b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/manual.html new file mode 100644 index 000000000..33c1cbea5 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/manual.html @@ -0,0 +1,280 @@ + + + + LuaFileSystem + + + + + + +
+ +
+ +
LuaFileSystem
+
File System Library for the Lua Programming Language
+
+ +
+ + + +
+ +

Introduction

+ +

LuaFileSystem is a Lua library +developed to complement the set of functions related to file +systems offered by the standard Lua distribution.

+ +

LuaFileSystem offers a portable way to access +the underlying directory structure and file attributes.

+ +

Building

+ +

+LuaFileSystem should be built with Lua 5.1 so the language library +and header files for the target version must be installed properly. +

+ +

+LuaFileSystem offers a Makefile and a separate configuration file, +config, +which should be edited to suit your installation before running +make. +The file has some definitions like paths to the external libraries, +compiler options and the like. +

+ +

On Windows, the C runtime used to compile LuaFileSystem must be the same +runtime that Lua uses, or some LuaFileSystem functions will not work.

+ +

Installation

+ +

The easiest way to install LuaFileSystem is to use LuaRocks:

+ +
+luarocks install luafilesystem
+
+ +

If you prefer to install LuaFileSystem manually, the compiled binary should be copied to a directory in your +C path.

+ +

Reference

+ +

+LuaFileSystem offers the following functions: +

+ +
+
lfs.attributes (filepath [, aname])
+
Returns a table with the file attributes corresponding to + filepath (or nil followed by an error message + in case of error). + If the second optional argument is given, then only the value of the + named attribute is returned (this use is equivalent to + lfs.attributes(filepath).aname, but the table is not created + and only one attribute is retrieved from the O.S.). + The attributes are described as follows; + attribute mode is a string, all the others are numbers, + and the time related attributes use the same time reference of + os.time: +
+
dev
+
on Unix systems, this represents the device that the inode resides on. On Windows systems, + represents the drive number of the disk containing the file
+ +
ino
+
on Unix systems, this represents the inode number. On Windows systems this has no meaning
+ +
mode
+
string representing the associated protection mode (the values could be + file, directory, link, socket, + named pipe, char device, block device or + other)
+ +
nlink
+
number of hard links to the file
+ +
uid
+
user-id of owner (Unix only, always 0 on Windows)
+ +
gid
+
group-id of owner (Unix only, always 0 on Windows)
+ +
rdev
+
on Unix systems, represents the device type, for special file inodes. + On Windows systems represents the same as dev
+ +
access
+
time of last access
+ +
modification
+
time of last data modification
+ +
change
+
time of last file status change
+ +
size
+
file size, in bytes
+ +
blocks
+
block allocated for file; (Unix only)
+ +
blksize
+
optimal file system I/O blocksize; (Unix only)
+
+ This function uses stat internally thus if the given + filepath is a symbolic link, it is followed (if it points to + another link the chain is followed recursively) and the information + is about the file it refers to. + To obtain information about the link itself, see function + lfs.symlinkattributes. +
+ +
lfs.chdir (path)
+
Changes the current working directory to the given + path.
+ Returns true in case of success or nil plus an + error string.
+ +
lfs.lock_dir(path, [seconds_stale])
+
Creates a lockfile (called lockfile.lfs) in path if it does not + exist and returns the lock. If the lock already exists checks if + it's stale, using the second parameter (default for the second + parameter is INT_MAX, which in practice means the lock will never + be stale. To free the the lock call lock:free().
+ In case of any errors it returns nil and the error message. In + particular, if the lock exists and is not stale it returns the + "File exists" message.
+ +
lfs.currentdir ()
+
Returns a string with the current working directory or nil + plus an error string.
+ +
iter, dir_obj = lfs.dir (path)
+
+ Lua iterator over the entries of a given directory. + Each time the iterator is called with dir_obj it returns a directory entry's name as a string, or + nil if there are no more entries. You can also iterate by calling dir_obj:next(), and + explicitly close the directory before the iteration finished with dir_obj:close(). + Raises an error if path is not a directory. +
+ +
lfs.lock (filehandle, mode[, start[, length]])
+
Locks a file or a part of it. This function works on open files; the + file handle should be specified as the first argument. + The string mode could be either + r (for a read/shared lock) or w (for a + write/exclusive lock). The optional arguments start + and length can be used to specify a starting point and + its length; both should be numbers.
+ Returns true if the operation was successful; in + case of error, it returns nil plus an error string. +
+ +
lfs.link (old, new[, symlink])
+
Creates a link. The first argument is the object to link to + and the second is the name of the link. If the optional third + argument is true, the link will by a symbolic link (by default, a + hard link is created). +
+ +
lfs.mkdir (dirname)
+
Creates a new directory. The argument is the name of the new + directory.
+ Returns true if the operation was successful; + in case of error, it returns nil plus an error string. +
+ +
lfs.rmdir (dirname)
+
Removes an existing directory. The argument is the name of the directory.
+ Returns true if the operation was successful; + in case of error, it returns nil plus an error string.
+ +
lfs.setmode (file, mode)
+
Sets the writing mode for a file. The mode string can be either "binary" or "text". + Returns true followed the previous mode string for the file, or + nil followed by an error string in case of errors. + On non-Windows platforms, where the two modes are identical, + setting the mode has no effect, and the mode is always returned as binary. +
+ +
lfs.symlinkattributes (filepath [, aname])
+
Identical to lfs.attributes except that + it obtains information about the link itself (not the file it refers to). + On Windows this function does not yet support links, and is identical to + lfs.attributes. +
+ +
lfs.touch (filepath [, atime [, mtime]])
+
Set access and modification times of a file. This function is + a bind to utime function. The first argument is the + filename, the second argument (atime) is the access time, + and the third argument (mtime) is the modification time. + Both times are provided in seconds (which should be generated with + Lua standard function os.time). + If the modification time is omitted, the access time provided is used; + if both times are omitted, the current time is used.
+ Returns true if the operation was successful; + in case of error, it returns nil plus an error string. +
+ +
lfs.unlock (filehandle[, start[, length]])
+
Unlocks a file or a part of it. This function works on + open files; the file handle should be specified as the first + argument. The optional arguments start and + length can be used to specify a starting point and its + length; both should be numbers.
+ Returns true if the operation was successful; + in case of error, it returns nil plus an error string. +
+
+ +
+ +
+ +
+

Valid XHTML 1.0!

+

$Id: manual.html,v 1.45 2009/06/03 20:53:55 mascarenhas Exp $

+
+ +
+ + + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/luafilesystem-1.6.3-2.rockspec b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/luafilesystem-1.6.3-2.rockspec new file mode 100644 index 000000000..c27e2b711 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/luafilesystem-1.6.3-2.rockspec @@ -0,0 +1,29 @@ +package = "LuaFileSystem" +version = "1.6.3-2" +source = { + url = "git://github.com/keplerproject/luafilesystem", + tag = "v_1_6_3" +} +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]], + homepage = "http://keplerproject.github.io/luafilesystem", + license = "MIT/X11" +} +dependencies = { + "lua >= 5.1" +} +build = { + type = "builtin", + modules = { + lfs = "src/lfs.c" + }, + copy_directories = { + "doc", "tests" + } +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/rock_manifest b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/rock_manifest new file mode 100644 index 000000000..b0f7b6332 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/rock_manifest @@ -0,0 +1,19 @@ +rock_manifest = { + doc = { + us = { + ["doc.css"] = "d0a913514fb190240b3b4033d105cbc0", + ["examples.html"] = "5832f72021728374cf57b621d62ce0ff", + ["index.html"] = "96885bdda963939f0a363b5fa6b16b59", + ["license.html"] = "e3a756835cb7c8ae277d5e513c8e32ee", + ["luafilesystem.png"] = "81e923e976e99f894ea0aa8b52baff29", + ["manual.html"] = "d6473799b73ce486c3ea436586cb3b34" + } + }, + lib = { + ["lfs.dll"] = "165694685cffa6014be4ca8cbb7d920b" + }, + ["luafilesystem-1.6.3-2.rockspec"] = "eb0ef7c190516892eb8357af799eea5f", + tests = { + ["test.lua"] = "7b4ddb5bdb7e0b1b1ed0150d473535c9" + } +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/tests/test.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/tests/test.lua new file mode 100644 index 000000000..abfbd4d96 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/tests/test.lua @@ -0,0 +1,175 @@ +#!/usr/bin/env lua5.1 + +local tmp = "/tmp" +local sep = string.match (package.config, "[^\n]+") +local upper = ".." + +local lfs = require"lfs" +print (lfs._VERSION) + +io.write(".") +io.flush() + +function attrdir (path) + for file in lfs.dir(path) do + if file ~= "." and file ~= ".." then + local f = path..sep..file + print ("\t=> "..f.." <=") + local attr = lfs.attributes (f) + assert (type(attr) == "table") + if attr.mode == "directory" then + attrdir (f) + else + for name, value in pairs(attr) do + print (name, value) + end + end + end + end +end + +-- Checking changing directories +local current = assert (lfs.currentdir()) +local reldir = string.gsub (current, "^.*%"..sep.."([^"..sep.."])$", "%1") +assert (lfs.chdir (upper), "could not change to upper directory") +assert (lfs.chdir (reldir), "could not change back to current directory") +assert (lfs.currentdir() == current, "error trying to change directories") +assert (lfs.chdir ("this couldn't be an actual directory") == nil, "could change to a non-existent directory") + +io.write(".") +io.flush() + +-- Changing creating and removing directories +local tmpdir = current..sep.."lfs_tmp_dir" +local tmpfile = tmpdir..sep.."tmp_file" +-- Test for existence of a previous lfs_tmp_dir +-- that may have resulted from an interrupted test execution and remove it +if lfs.chdir (tmpdir) then + assert (lfs.chdir (upper), "could not change to upper directory") + assert (os.remove (tmpfile), "could not remove file from previous test") + assert (lfs.rmdir (tmpdir), "could not remove directory from previous test") +end + +io.write(".") +io.flush() + +-- tries to create a directory +assert (lfs.mkdir (tmpdir), "could not make a new directory") +local attrib, errmsg = lfs.attributes (tmpdir) +if not attrib then + error ("could not get attributes of file `"..tmpdir.."':\n"..errmsg) +end +local f = io.open(tmpfile, "w") +f:close() + +io.write(".") +io.flush() + +-- Change access time +local testdate = os.time({ year = 2007, day = 10, month = 2, hour=0}) +assert (lfs.touch (tmpfile, testdate)) +local new_att = assert (lfs.attributes (tmpfile)) +assert (new_att.access == testdate, "could not set access time") +assert (new_att.modification == testdate, "could not set modification time") + +io.write(".") +io.flush() + +-- Change access and modification time +local testdate1 = os.time({ year = 2007, day = 10, month = 2, hour=0}) +local testdate2 = os.time({ year = 2007, day = 11, month = 2, hour=0}) + +assert (lfs.touch (tmpfile, testdate2, testdate1)) +local new_att = assert (lfs.attributes (tmpfile)) +assert (new_att.access == testdate2, "could not set access time") +assert (new_att.modification == testdate1, "could not set modification time") + +io.write(".") +io.flush() + +-- Checking link (does not work on Windows) +if lfs.link (tmpfile, "_a_link_for_test_", true) then + assert (lfs.attributes"_a_link_for_test_".mode == "file") + assert (lfs.symlinkattributes"_a_link_for_test_".mode == "link") + assert (lfs.link (tmpfile, "_a_hard_link_for_test_")) + assert (lfs.attributes (tmpfile, "nlink") == 2) + assert (os.remove"_a_link_for_test_") + assert (os.remove"_a_hard_link_for_test_") +end + +io.write(".") +io.flush() + +-- Checking text/binary modes (only has an effect in Windows) +local f = io.open(tmpfile, "w") +local result, mode = lfs.setmode(f, "binary") +assert(result) -- on non-Windows platforms, mode is always returned as "binary" +result, mode = lfs.setmode(f, "text") +assert(result and mode == "binary") +f:close() + +io.write(".") +io.flush() + +-- Restore access time to current value +assert (lfs.touch (tmpfile, attrib.access, attrib.modification)) +new_att = assert (lfs.attributes (tmpfile)) +assert (new_att.access == attrib.access) +assert (new_att.modification == attrib.modification) + +io.write(".") +io.flush() + +-- Check consistency of lfs.attributes values +local attr = lfs.attributes (tmpfile) +for key, value in pairs(attr) do + assert (value == lfs.attributes (tmpfile, key), + "lfs.attributes values not consistent") +end + +-- Remove new file and directory +assert (os.remove (tmpfile), "could not remove new file") +assert (lfs.rmdir (tmpdir), "could not remove new directory") +assert (lfs.mkdir (tmpdir..sep.."lfs_tmp_dir") == nil, "could create a directory inside a non-existent one") + +io.write(".") +io.flush() + +-- Trying to get attributes of a non-existent file +assert (lfs.attributes ("this couldn't be an actual file") == nil, "could get attributes of a non-existent file") +assert (type(lfs.attributes (upper)) == "table", "couldn't get attributes of upper directory") + +io.write(".") +io.flush() + +-- Stressing directory iterator +count = 0 +for i = 1, 4000 do + for file in lfs.dir (tmp) do + count = count + 1 + end +end + +io.write(".") +io.flush() + +-- Stressing directory iterator, explicit version +count = 0 +for i = 1, 4000 do + local iter, dir = lfs.dir(tmp) + local file = dir:next() + while file do + count = count + 1 + file = dir:next() + end + assert(not pcall(dir.next, dir)) +end + +io.write(".") +io.flush() + +-- directory explicit close +local iter, dir = lfs.dir(tmp) +dir:close() +assert(not pcall(dir.next, dir)) +print"Ok!" diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/manifest b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/manifest new file mode 100644 index 000000000..3b022bd4f --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/manifest @@ -0,0 +1,599 @@ +commands = { + luadocumentor = { + "luadocumentor/0.1.5-1" + } +} +dependencies = { + checks = { + ["1.0-1"] = { + { + constraints = { + { + op = ">=", + version = { + 5, 1, string = "5.1" + } + } + }, + name = "lua" + } + } + }, + luadocumentor = { + ["0.1.5-1"] = { + { + constraints = { + { + op = "~>", + version = { + 5, 1, string = "5.1" + } + } + }, + name = "lua" + }, { + constraints = { + { + op = "~>", + version = { + 1, 6, string = "1.6" + } + } + }, + name = "luafilesystem" + }, { + constraints = { + { + op = "~>", + version = { + 0, 32, string = "0.32" + } + } + }, + name = "markdown" + }, { + constraints = { + { + op = "~>", + version = { + 0, 7, string = "0.7" + } + } + }, + name = "metalua-compiler" + }, { + constraints = { + { + op = "~>", + version = { + 0, 9, string = "0.9" + } + } + }, + name = "penlight" + } + } + }, + luafilesystem = { + ["1.6.3-2"] = { + { + constraints = { + { + op = ">=", + version = { + 5, 1, string = "5.1" + } + } + }, + name = "lua" + } + } + }, + markdown = { + ["0.32-2"] = { + { + constraints = { + { + op = ">=", + version = { + 5, 1, string = "5.1" + } + } + }, + name = "lua" + } + } + }, + ["metalua-compiler"] = { + ["0.7.3-1"] = { + { + constraints = { + { + op = "~>", + version = { + 5, 1, string = "5.1" + } + } + }, + name = "lua" + }, { + constraints = { + { + op = "~>", + version = { + 1, 6, string = "1.6" + } + } + }, + name = "luafilesystem" + }, { + constraints = { + { + op = ">=", + version = { + 0, 7, 3, string = "0.7.3" + } + } + }, + name = "metalua-parser" + } + } + }, + ["metalua-parser"] = { + ["0.7.3-2"] = { + { + constraints = { + { + op = ">=", + version = { + 5, 1, string = "5.1" + } + } + }, + name = "lua" + } + } + }, + penlight = { + ["0.9.8-1"] = { + { + constraints = {}, + name = "luafilesystem" + } + } + } +} +modules = { + checks = { + "checks/1.0-1" + }, + defaultcss = { + "luadocumentor/0.1.5-1" + }, + docgenerator = { + "luadocumentor/0.1.5-1" + }, + extractors = { + "luadocumentor/0.1.5-1" + }, + ["fs.lfs"] = { + "luadocumentor/0.1.5-1" + }, + lddextractor = { + "luadocumentor/0.1.5-1" + }, + lfs = { + "luafilesystem/1.6.3-2" + }, + markdown = { + "markdown/0.32-2" + }, + ["metalua.compiler"] = { + "metalua-parser/0.7.3-2" + }, + ["metalua.compiler.bytecode"] = { + "metalua-compiler/0.7.3-1" + }, + ["metalua.compiler.bytecode.compile"] = { + "metalua-compiler/0.7.3-1" + }, + ["metalua.compiler.bytecode.lcode"] = { + "metalua-compiler/0.7.3-1" + }, + ["metalua.compiler.bytecode.ldump"] = { + "metalua-compiler/0.7.3-1" + }, + ["metalua.compiler.bytecode.lopcodes"] = { + "metalua-compiler/0.7.3-1" + }, + ["metalua.compiler.globals"] = { + "metalua-compiler/0.7.3-1" + }, + ["metalua.compiler.parser"] = { + "metalua-parser/0.7.3-2" + }, + ["metalua.compiler.parser.annot.generator"] = { + "metalua-parser/0.7.3-2" + }, + ["metalua.compiler.parser.annot.grammar"] = { + "metalua-parser/0.7.3-2" + }, + ["metalua.compiler.parser.expr"] = { + "metalua-parser/0.7.3-2" + }, + ["metalua.compiler.parser.ext"] = { + "metalua-parser/0.7.3-2" + }, + ["metalua.compiler.parser.lexer"] = { + "metalua-parser/0.7.3-2" + }, + ["metalua.compiler.parser.meta"] = { + "metalua-parser/0.7.3-2" + }, + ["metalua.compiler.parser.misc"] = { + "metalua-parser/0.7.3-2" + }, + ["metalua.compiler.parser.stat"] = { + "metalua-parser/0.7.3-2" + }, + ["metalua.compiler.parser.table"] = { + "metalua-parser/0.7.3-2" + }, + ["metalua.grammar.generator"] = { + "metalua-parser/0.7.3-2" + }, + ["metalua.grammar.lexer"] = { + "metalua-parser/0.7.3-2" + }, + ["metalua.loader"] = { + "metalua-compiler/0.7.3-1" + }, + ["metalua.pprint"] = { + "metalua-parser/0.7.3-2" + }, + ["metalua/compiler/ast_to_src.mlua"] = { + "metalua-compiler/0.7.3-1" + }, + ["metalua/extension/comprehension.mlua"] = { + "metalua-compiler/0.7.3-1" + }, + ["metalua/extension/match.mlua"] = { + "metalua-compiler/0.7.3-1" + }, + ["metalua/repl.mlua"] = { + "metalua-compiler/0.7.3-1" + }, + ["metalua/treequery.mlua"] = { + "metalua-compiler/0.7.3-1" + }, + ["metalua/treequery/walk.mlua"] = { + "metalua-compiler/0.7.3-1" + }, + ["models.apimodel"] = { + "luadocumentor/0.1.5-1" + }, + ["models.apimodelbuilder"] = { + "luadocumentor/0.1.5-1" + }, + ["models.internalmodel"] = { + "luadocumentor/0.1.5-1" + }, + ["models.ldparser"] = { + "luadocumentor/0.1.5-1" + }, + ["models/internalmodelbuilder.mlua"] = { + "luadocumentor/0.1.5-1" + }, + pl = { + "penlight/0.9.8-1" + }, + ["pl.Date"] = { + "penlight/0.9.8-1" + }, + ["pl.List"] = { + "penlight/0.9.8-1" + }, + ["pl.Map"] = { + "penlight/0.9.8-1" + }, + ["pl.MultiMap"] = { + "penlight/0.9.8-1" + }, + ["pl.OrderedMap"] = { + "penlight/0.9.8-1" + }, + ["pl.Set"] = { + "penlight/0.9.8-1" + }, + ["pl.app"] = { + "penlight/0.9.8-1" + }, + ["pl.array2d"] = { + "penlight/0.9.8-1" + }, + ["pl.class"] = { + "penlight/0.9.8-1" + }, + ["pl.comprehension"] = { + "penlight/0.9.8-1" + }, + ["pl.config"] = { + "penlight/0.9.8-1" + }, + ["pl.data"] = { + "penlight/0.9.8-1" + }, + ["pl.dir"] = { + "penlight/0.9.8-1" + }, + ["pl.file"] = { + "penlight/0.9.8-1" + }, + ["pl.func"] = { + "penlight/0.9.8-1" + }, + ["pl.input"] = { + "penlight/0.9.8-1" + }, + ["pl.lapp"] = { + "penlight/0.9.8-1" + }, + ["pl.lexer"] = { + "penlight/0.9.8-1" + }, + ["pl.luabalanced"] = { + "penlight/0.9.8-1" + }, + ["pl.operator"] = { + "penlight/0.9.8-1" + }, + ["pl.path"] = { + "penlight/0.9.8-1" + }, + ["pl.permute"] = { + "penlight/0.9.8-1" + }, + ["pl.platf.luajava"] = { + "penlight/0.9.8-1" + }, + ["pl.pretty"] = { + "penlight/0.9.8-1" + }, + ["pl.seq"] = { + "penlight/0.9.8-1" + }, + ["pl.sip"] = { + "penlight/0.9.8-1" + }, + ["pl.strict"] = { + "penlight/0.9.8-1" + }, + ["pl.stringio"] = { + "penlight/0.9.8-1" + }, + ["pl.stringx"] = { + "penlight/0.9.8-1" + }, + ["pl.tablex"] = { + "penlight/0.9.8-1" + }, + ["pl.template"] = { + "penlight/0.9.8-1" + }, + ["pl.test"] = { + "penlight/0.9.8-1" + }, + ["pl.text"] = { + "penlight/0.9.8-1" + }, + ["pl.utils"] = { + "penlight/0.9.8-1" + }, + ["pl.xml"] = { + "penlight/0.9.8-1" + }, + ["template.file"] = { + "luadocumentor/0.1.5-1" + }, + ["template.index"] = { + "luadocumentor/0.1.5-1" + }, + ["template.index.recordtypedef"] = { + "luadocumentor/0.1.5-1" + }, + ["template.item"] = { + "luadocumentor/0.1.5-1" + }, + ["template.page"] = { + "luadocumentor/0.1.5-1" + }, + ["template.recordtypedef"] = { + "luadocumentor/0.1.5-1" + }, + ["template.usage"] = { + "luadocumentor/0.1.5-1" + }, + ["template.utils"] = { + "luadocumentor/0.1.5-1" + }, + templateengine = { + "luadocumentor/0.1.5-1" + } +} +repository = { + checks = { + ["1.0-1"] = { + { + arch = "installed", + commands = {}, + dependencies = {}, + modules = { + checks = "checks.dll" + } + } + } + }, + luadocumentor = { + ["0.1.5-1"] = { + { + arch = "installed", + commands = { + luadocumentor = "luadocumentor" + }, + dependencies = { + luafilesystem = "1.6.3-2", + markdown = "0.32-2", + ["metalua-compiler"] = "0.7.3-1", + ["metalua-parser"] = "0.7.3-2", + penlight = "0.9.8-1" + }, + modules = { + defaultcss = "defaultcss.lua", + docgenerator = "docgenerator.lua", + extractors = "extractors.lua", + ["fs.lfs"] = "fs/lfs.lua", + lddextractor = "lddextractor.lua", + ["models.apimodel"] = "models/apimodel.lua", + ["models.apimodelbuilder"] = "models/apimodelbuilder.lua", + ["models.internalmodel"] = "models/internalmodel.lua", + ["models.ldparser"] = "models/ldparser.lua", + ["models/internalmodelbuilder.mlua"] = "models/internalmodelbuilder.mlua", + ["template.file"] = "template/file.lua", + ["template.index"] = "template/index.lua", + ["template.index.recordtypedef"] = "template/index/recordtypedef.lua", + ["template.item"] = "template/item.lua", + ["template.page"] = "template/page.lua", + ["template.recordtypedef"] = "template/recordtypedef.lua", + ["template.usage"] = "template/usage.lua", + ["template.utils"] = "template/utils.lua", + templateengine = "templateengine.lua" + } + } + } + }, + luafilesystem = { + ["1.6.3-2"] = { + { + arch = "installed", + commands = {}, + dependencies = {}, + modules = { + lfs = "lfs.dll" + } + } + } + }, + markdown = { + ["0.32-2"] = { + { + arch = "installed", + commands = {}, + dependencies = {}, + modules = { + markdown = "markdown.lua" + } + } + } + }, + ["metalua-compiler"] = { + ["0.7.3-1"] = { + { + arch = "installed", + commands = {}, + dependencies = { + luafilesystem = "1.6.3-2", + ["metalua-parser"] = "0.7.3-2" + }, + modules = { + ["metalua.compiler.bytecode"] = "metalua/compiler/bytecode.lua", + ["metalua.compiler.bytecode.compile"] = "metalua/compiler/bytecode/compile.lua", + ["metalua.compiler.bytecode.lcode"] = "metalua/compiler/bytecode/lcode.lua", + ["metalua.compiler.bytecode.ldump"] = "metalua/compiler/bytecode/ldump.lua", + ["metalua.compiler.bytecode.lopcodes"] = "metalua/compiler/bytecode/lopcodes.lua", + ["metalua.compiler.globals"] = "metalua/compiler/globals.lua", + ["metalua.loader"] = "metalua/loader.lua", + ["metalua/compiler/ast_to_src.mlua"] = "metalua/compiler/ast_to_src.mlua", + ["metalua/extension/comprehension.mlua"] = "metalua/extension/comprehension.mlua", + ["metalua/extension/match.mlua"] = "metalua/extension/match.mlua", + ["metalua/repl.mlua"] = "metalua/repl.mlua", + ["metalua/treequery.mlua"] = "metalua/treequery.mlua", + ["metalua/treequery/walk.mlua"] = "metalua/treequery/walk.mlua" + } + } + } + }, + ["metalua-parser"] = { + ["0.7.3-2"] = { + { + arch = "installed", + commands = {}, + dependencies = {}, + modules = { + ["metalua.compiler"] = "metalua/compiler.lua", + ["metalua.compiler.parser"] = "metalua/compiler/parser.lua", + ["metalua.compiler.parser.annot.generator"] = "metalua/compiler/parser/annot/generator.lua", + ["metalua.compiler.parser.annot.grammar"] = "metalua/compiler/parser/annot/grammar.lua", + ["metalua.compiler.parser.expr"] = "metalua/compiler/parser/expr.lua", + ["metalua.compiler.parser.ext"] = "metalua/compiler/parser/ext.lua", + ["metalua.compiler.parser.lexer"] = "metalua/compiler/parser/lexer.lua", + ["metalua.compiler.parser.meta"] = "metalua/compiler/parser/meta.lua", + ["metalua.compiler.parser.misc"] = "metalua/compiler/parser/misc.lua", + ["metalua.compiler.parser.stat"] = "metalua/compiler/parser/stat.lua", + ["metalua.compiler.parser.table"] = "metalua/compiler/parser/table.lua", + ["metalua.grammar.generator"] = "metalua/grammar/generator.lua", + ["metalua.grammar.lexer"] = "metalua/grammar/lexer.lua", + ["metalua.pprint"] = "metalua/pprint.lua" + } + } + } + }, + penlight = { + ["0.9.8-1"] = { + { + arch = "installed", + commands = {}, + dependencies = { + luafilesystem = "1.6.3-2" + }, + modules = { + pl = "pl/init.lua", + ["pl.Date"] = "pl/Date.lua", + ["pl.List"] = "pl/List.lua", + ["pl.Map"] = "pl/Map.lua", + ["pl.MultiMap"] = "pl/MultiMap.lua", + ["pl.OrderedMap"] = "pl/OrderedMap.lua", + ["pl.Set"] = "pl/Set.lua", + ["pl.app"] = "pl/app.lua", + ["pl.array2d"] = "pl/array2d.lua", + ["pl.class"] = "pl/class.lua", + ["pl.comprehension"] = "pl/comprehension.lua", + ["pl.config"] = "pl/config.lua", + ["pl.data"] = "pl/data.lua", + ["pl.dir"] = "pl/dir.lua", + ["pl.file"] = "pl/file.lua", + ["pl.func"] = "pl/func.lua", + ["pl.input"] = "pl/input.lua", + ["pl.lapp"] = "pl/lapp.lua", + ["pl.lexer"] = "pl/lexer.lua", + ["pl.luabalanced"] = "pl/luabalanced.lua", + ["pl.operator"] = "pl/operator.lua", + ["pl.path"] = "pl/path.lua", + ["pl.permute"] = "pl/permute.lua", + ["pl.platf.luajava"] = "pl/platf/luajava.lua", + ["pl.pretty"] = "pl/pretty.lua", + ["pl.seq"] = "pl/seq.lua", + ["pl.sip"] = "pl/sip.lua", + ["pl.strict"] = "pl/strict.lua", + ["pl.stringio"] = "pl/stringio.lua", + ["pl.stringx"] = "pl/stringx.lua", + ["pl.tablex"] = "pl/tablex.lua", + ["pl.template"] = "pl/template.lua", + ["pl.test"] = "pl/test.lua", + ["pl.text"] = "pl/text.lua", + ["pl.utils"] = "pl/utils.lua", + ["pl.xml"] = "pl/xml.lua" + } + } + } + } +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/markdown/0.32-2/markdown-0.32-2.rockspec b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/markdown/0.32-2/markdown-0.32-2.rockspec new file mode 100644 index 000000000..abbfc89e1 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/markdown/0.32-2/markdown-0.32-2.rockspec @@ -0,0 +1,23 @@ +package = "Markdown" +version = "0.32-2" +source = { + url = "http://www.frykholm.se/files/markdown-0.32.tar.gz", + dir = "." +} +description = { + summary = "Markdown text-to-html markup system.", + detailed = [[ + A pure-lua implementation of the Markdown text-to-html markup system. + ]], + license = "MIT", + homepage = "http://www.frykholm.se/files/markdown.lua" +} +dependencies = { + "lua >= 5.1", +} +build = { + type = "none", + install = { + lua = { "markdown.lua" }, + } +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/markdown/0.32-2/rock_manifest b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/markdown/0.32-2/rock_manifest new file mode 100644 index 000000000..8f3d633ca --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/markdown/0.32-2/rock_manifest @@ -0,0 +1,6 @@ +rock_manifest = { + lua = { + ["markdown.lua"] = "0ea5f9d6d22a6c9aa4fdf63cf1d7d066" + }, + ["markdown-0.32-2.rockspec"] = "83f0335058d8fbd078d4f2c1ce941df0" +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README-compiler.md b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README-compiler.md new file mode 100644 index 000000000..b2679cdb5 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README-compiler.md @@ -0,0 +1,104 @@ +Metalua Compiler +================ + +## Metalua compiler + +This module `metalua-compiler` depends on `metalua-parser`. Its main +feature is to compile ASTs into Lua 5.1 bytecode, allowing to convert +them into bytecode files and executable functions. This opens the +following possibilities: + +* compiler objects generated with `require 'metalua.compiler'.new()` + support methods `:xxx_to_function()` and `:xxx_to_bytecode()`; + +* Compile-time meta-programming: use of `-{...}` splices in source + code, to generate code during compilation; + +* Some syntax extensions, such as structural pattern matching and + lists by comprehension; + +* Some AST manipulation facilities such as `treequery`, which are + implemented with Metalua syntax extensions. + +## What's new in Metalua 0.7 + +This is a major overhaul of the compiler's architecture. Some of the +most noteworthy changes are: + +* No more installation or bootstrap script. Some Metalua source files + have been rewritten in plain Lua, and module sources have been + refactored, so that if you just drop the `metalua` folder somewhere + in your `LUA_PATH`, it works. + +* The compiler can be cut in two parts: + + * a parser which generates ASTs out of Lua sources, and should be + either portable or easily ported to Lua 5.2; + + * a compiler, which can turn sources and AST into executable + Lua 5.1 bytecode and run it. It also supports compile-time + meta-programming, i.e. code included between `-{ ... }` is + executed during compilation, and the ASTs it produces are + included in the resulting bytecode. + +* Both parts are packaged as separate LuaRocks, `metalua-parser` and + `metalua-compiler` respectively, so that you can install the former + without the latter. + +* The parser is not a unique object anymore. Instead, + `require "metalua.compiler".new()` returns a different compiler + instance every time it's called. Compiler instances can be reused on + as many source files as wanted, but extending one instance's grammar + doesn't affect other compiler instances. + +* Included standard library has been shed. There are too many standard + libs in Lua, and none of them is standard enough, offering + yet-another-one, coupled with a specific compiler can only add to + confusion. + +* Many syntax extensions, which either were arguably more code samples + than actual production-ready tools, or relied too heavily on the + removed runtime standard libraries, have been removed. + +* The remaining libraries and samples are: + + * `metalua.compiler` converts sources into ASTs, bytecode, + functions, and ASTs back into sources. + + * `metalua` compiles and/or executes files from the command line, + can start an interactive REPL session. + + * `metalua.loader` adds a package loader which allows to use modules + written in Metalua, even from a plain Lua program. + + * `metalua.treequery` is an advanced DSL allowing to search ASTs in + a smart way, e.g. "_search `return` statements which return a + `local` variable but aren't in a nested `function`_". + + * `metalua.extension.comprehension` is a language extension which + supports lists by comprehension + (`even = { i for i=1, 100 if i%2==0 }`) and improved loops + (`for i=1, 10 for j=1,10 if i~=j do print(i,j) end`). + + * `metalua.extension.match` is a language extension which offers + Haskell/ML structural pattern matching + (``match AST with `Function{ args, body } -> ... | `Number{ 0 } -> ...end``) + + * **TODO Move basic extensions in a separate module.** + +* To remove the compilation speed penalty associated with + metaprogramming, when environment variable `LUA_MCACHE` or Lua + variable `package.mcache` is defined and LuaFileSystem is available, + the results of Metalua source compilations is cached. Unless the + source file is more recent than the latest cached bytecode file, the + latter is loaded instead of the former. + +* The Luarock install for the full compiler lists dependencies towards + Readline, LuaFileSytem, and Alt-Getopts. Those projects are + optional, but having them automatically installed by LuaRocks offers + a better user experience. + +* The license has changed from MIT to double license MIT + EPL. This + has been done in order to provide the IP guarantees expected by the + Eclipse Foundation, to include Metalua in Eclipse's + [Lua Development Tools](http://www.eclipse.org/koneki/ldt/). diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README-parser.md b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README-parser.md new file mode 100644 index 000000000..98e34ee43 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README-parser.md @@ -0,0 +1,177 @@ +Metalua Parser +============== + +`metalua-parser` is a subset of the Metalua compiler, which turns +valid Lua source files and strings into abstract syntax trees +(AST). This README includes a description of this AST format. People +interested by Lua code analysis and generation are encouraged to +produce and/or consume this format to represent ASTs. + +It has been designed for Lua 5.1. It hasn't been tested against +Lua 5.2, but should be easily ported. + +## Usage + +Module `metalua.compiler` has a `new()` function, which returns a +compiler instance. This instance has a set of methods of the form +`:xxx_to_yyy(input)`, where `xxx` and `yyy` must be one of the +following: + +* `srcfile` the name of a Lua source file; +* `src` a string containing the Lua sources of a list of statements; +* `lexstream` a lexical tokens stream; +* `ast` an abstract syntax tree; +* `bytecode` a chunk of Lua bytecode that can be loaded in a Lua 5.1 + VM (not available if you only installed the parser); +* `function` an executable Lua function. + +Compiling into bytecode or executable functions requires the whole +Metalua compiler, not only the parser. The most frequently used +functions are `:src_to_ast(source_string)` and +`:srcfile_to_ast("path/to/source/file.lua")`. + + mlc = require 'metalua.compiler'.new() + ast = mlc :src_to_ast[[ return 123 ]] + +A compiler instance can be reused as much as you want; it's only +interesting to work with more than one compiler instance when you +start extending their grammars. + +## Abstract Syntax Trees definition + +### Notation + +Trees are written below with some Metalua syntax sugar, which +increases their readability. the backquote symbol introduces a `tag`, +i.e. a string stored in the `"tag"` field of a table: + +* `` `Foo{ 1, 2, 3 }`` is a shortcut for `{tag="Foo", 1, 2, 3}`; +* `` `Foo`` is a shortcut for `{tag="Foo"}`; +* `` `Foo 123`` is a shortcut for `` `Foo{ 123 }``, and therefore + `{tag="Foo", 123 }`; the expression after the tag must be a literal + number or string. + +When using a Metalua interpreter or compiler, the backtick syntax is +supported and can be used directly. Metalua's pretty-printing helpers +also try to use backtick syntax whenever applicable. + +### Tree elements + +Tree elements are mainly categorized into statements `stat`, +expressions `expr` and lists of statements `block`. Auxiliary +definitions include function applications/method invocation `apply`, +are both valid statements and expressions, expressions admissible on +the left-hand-side of an assignment statement `lhs`. + + block: { stat* } + + stat: + `Do{ stat* } + | `Set{ {lhs+} {expr+} } -- lhs1, lhs2... = e1, e2... + | `While{ expr block } -- while e do b end + | `Repeat{ block expr } -- repeat b until e + | `If{ (expr block)+ block? } -- if e1 then b1 [elseif e2 then b2] ... [else bn] end + | `Fornum{ ident expr expr expr? block } -- for ident = e, e[, e] do b end + | `Forin{ {ident+} {expr+} block } -- for i1, i2... in e1, e2... do b end + | `Local{ {ident+} {expr+}? } -- local i1, i2... = e1, e2... + | `Localrec{ ident expr } -- only used for 'local function' + | `Goto{ } -- goto str + | `Label{ } -- ::str:: + | `Return{ } -- return e1, e2... + | `Break -- break + | apply + + expr: + `Nil | `Dots | `True | `False + | `Number{ } + | `String{ } + | `Function{ { ident* `Dots? } block } + | `Table{ ( `Pair{ expr expr } | expr )* } + | `Op{ opid expr expr? } + | `Paren{ expr } -- significant to cut multiple values returns + | apply + | lhs + + apply: + `Call{ expr expr* } + | `Invoke{ expr `String{ } expr* } + + ident: `Id{ } + + lhs: ident | `Index{ expr expr } + + opid: 'add' | 'sub' | 'mul' | 'div' + | 'mod' | 'pow' | 'concat'| 'eq' + | 'lt' | 'le' | 'and' | 'or' + | 'not' | 'len' + +### Meta-data (lineinfo) + + +ASTs also embed some metadata, allowing to map them to their source +representation. Those informations are stored in a `"lineinfo"` field +in each tree node, which points to the range of characters in the +source string which represents it, and to the content of any comment +that would appear immediately before or after that node. + +Lineinfo objects have two fields, `"first"` and `"last"`, describing +respectively the beginning and the end of the subtree in the +sources. For instance, the sub-node ``Number{123}` produced by parsing +`[[return 123]]` will have `lineinfo.first` describing offset 8, and +`lineinfo.last` describing offset 10: + + + > mlc = require 'metalua.compiler'.new() + > ast = mlc :src_to_ast "return 123 -- comment" + > print(ast[1][1].lineinfo) + + > + +A lineinfo keeps track of character offsets relative to the beginning +of the source string/file ("K8-10" above), line numbers (L1 above; a +lineinfo spanning on several lines would read something like "L1-10"), +columns i.e. offset within the line ("C8-10" above), and a filename if +available (the "?" mark above indicating that we have no file name, as +the AST comes from a string). The final "|C>" indicates that there's a +comment immediately after the node; an initial " 5.1", -- Lua 5.2 bytecode not supported + "luafilesystem ~> 1.6", -- Cached compilation based on file timestamps + "metalua-parser >= 0.7.3", -- AST production +} + +build = { + type="builtin", + modules={ + ["metalua.compiler.bytecode"] = "metalua/compiler/bytecode.lua", + ["metalua.compiler.globals"] = "metalua/compiler/globals.lua", + ["metalua.compiler.bytecode.compile"] = "metalua/compiler/bytecode/compile.lua", + ["metalua.compiler.bytecode.lcode"] = "metalua/compiler/bytecode/lcode.lua", + ["metalua.compiler.bytecode.lopcodes"] = "metalua/compiler/bytecode/lopcodes.lua", + ["metalua.compiler.bytecode.ldump"] = "metalua/compiler/bytecode/ldump.lua", + ["metalua.loader"] = "metalua/loader.lua", + }, + install={ + lua={ + ["metalua.treequery"] = "metalua/treequery.mlua", + ["metalua.compiler.ast_to_src"] = "metalua/compiler/ast_to_src.mlua", + ["metalua.treequery.walk"] = "metalua/treequery/walk.mlua", + ["metalua.extension.match"] = "metalua/extension/match.mlua", + ["metalua.extension.comprehension"] = "metalua/extension/comprehension.mlua", + ["metalua.repl"] = "metalua/repl.mlua", + } + } +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/rock_manifest b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/rock_manifest new file mode 100644 index 000000000..7452a8f94 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/rock_manifest @@ -0,0 +1,33 @@ +rock_manifest = { + doc = { + ["README-compiler.md"] = "292523d759247d210d32fb2f6153e0f4", + ["README-parser.md"] = "b44e3673d96dd296f2c0e92a6c87ee18", + ["README.md"] = "20bfb490cddef9e101e44688791abcda" + }, + lua = { + metalua = { + compiler = { + ["ast_to_src.mlua"] = "1309f76df37585ef8e1f67f748b07b22", + bytecode = { + ["compile.lua"] = "430e4a6fac8b64b5ebb3ae585ebae75a", + ["lcode.lua"] = "3ad8755ebe8ea8eca6b1d2846eec92c4", + ["ldump.lua"] = "295e1d9657fb0126ce3471b3366da694", + ["lopcodes.lua"] = "a0f15cfc93b026b0a868466d066f1d21" + }, + ["bytecode.lua"] = "1032e5233455fd4e504daf5d2893527b", + ["globals.lua"] = "80ae19c6e640de0746348c91633c4c55" + }, + extension = { + ["comprehension.mlua"] = "426f5856896bda4c3763bd5f61410685", + ["match.mlua"] = "79960265331e8b2f46199c2411a103de" + }, + ["loader.lua"] = "1cdbf6cdf6ca97c55540d068474f1d8a", + ["repl.mlua"] = "729456f3a8cc073788acee564a0495f0", + treequery = { + ["walk.mlua"] = "5159aaddbec55936f91ea4236f6451d3" + }, + ["treequery.mlua"] = "97ffcee0825ac3bc776d01566767b2e8" + } + }, + ["metalua-compiler-0.7.3-1.rockspec"] = "b3883b25641d862db6828300bb755d51" +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README-compiler.md b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README-compiler.md new file mode 100644 index 000000000..b2679cdb5 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README-compiler.md @@ -0,0 +1,104 @@ +Metalua Compiler +================ + +## Metalua compiler + +This module `metalua-compiler` depends on `metalua-parser`. Its main +feature is to compile ASTs into Lua 5.1 bytecode, allowing to convert +them into bytecode files and executable functions. This opens the +following possibilities: + +* compiler objects generated with `require 'metalua.compiler'.new()` + support methods `:xxx_to_function()` and `:xxx_to_bytecode()`; + +* Compile-time meta-programming: use of `-{...}` splices in source + code, to generate code during compilation; + +* Some syntax extensions, such as structural pattern matching and + lists by comprehension; + +* Some AST manipulation facilities such as `treequery`, which are + implemented with Metalua syntax extensions. + +## What's new in Metalua 0.7 + +This is a major overhaul of the compiler's architecture. Some of the +most noteworthy changes are: + +* No more installation or bootstrap script. Some Metalua source files + have been rewritten in plain Lua, and module sources have been + refactored, so that if you just drop the `metalua` folder somewhere + in your `LUA_PATH`, it works. + +* The compiler can be cut in two parts: + + * a parser which generates ASTs out of Lua sources, and should be + either portable or easily ported to Lua 5.2; + + * a compiler, which can turn sources and AST into executable + Lua 5.1 bytecode and run it. It also supports compile-time + meta-programming, i.e. code included between `-{ ... }` is + executed during compilation, and the ASTs it produces are + included in the resulting bytecode. + +* Both parts are packaged as separate LuaRocks, `metalua-parser` and + `metalua-compiler` respectively, so that you can install the former + without the latter. + +* The parser is not a unique object anymore. Instead, + `require "metalua.compiler".new()` returns a different compiler + instance every time it's called. Compiler instances can be reused on + as many source files as wanted, but extending one instance's grammar + doesn't affect other compiler instances. + +* Included standard library has been shed. There are too many standard + libs in Lua, and none of them is standard enough, offering + yet-another-one, coupled with a specific compiler can only add to + confusion. + +* Many syntax extensions, which either were arguably more code samples + than actual production-ready tools, or relied too heavily on the + removed runtime standard libraries, have been removed. + +* The remaining libraries and samples are: + + * `metalua.compiler` converts sources into ASTs, bytecode, + functions, and ASTs back into sources. + + * `metalua` compiles and/or executes files from the command line, + can start an interactive REPL session. + + * `metalua.loader` adds a package loader which allows to use modules + written in Metalua, even from a plain Lua program. + + * `metalua.treequery` is an advanced DSL allowing to search ASTs in + a smart way, e.g. "_search `return` statements which return a + `local` variable but aren't in a nested `function`_". + + * `metalua.extension.comprehension` is a language extension which + supports lists by comprehension + (`even = { i for i=1, 100 if i%2==0 }`) and improved loops + (`for i=1, 10 for j=1,10 if i~=j do print(i,j) end`). + + * `metalua.extension.match` is a language extension which offers + Haskell/ML structural pattern matching + (``match AST with `Function{ args, body } -> ... | `Number{ 0 } -> ...end``) + + * **TODO Move basic extensions in a separate module.** + +* To remove the compilation speed penalty associated with + metaprogramming, when environment variable `LUA_MCACHE` or Lua + variable `package.mcache` is defined and LuaFileSystem is available, + the results of Metalua source compilations is cached. Unless the + source file is more recent than the latest cached bytecode file, the + latter is loaded instead of the former. + +* The Luarock install for the full compiler lists dependencies towards + Readline, LuaFileSytem, and Alt-Getopts. Those projects are + optional, but having them automatically installed by LuaRocks offers + a better user experience. + +* The license has changed from MIT to double license MIT + EPL. This + has been done in order to provide the IP guarantees expected by the + Eclipse Foundation, to include Metalua in Eclipse's + [Lua Development Tools](http://www.eclipse.org/koneki/ldt/). diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README-parser.md b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README-parser.md new file mode 100644 index 000000000..98e34ee43 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README-parser.md @@ -0,0 +1,177 @@ +Metalua Parser +============== + +`metalua-parser` is a subset of the Metalua compiler, which turns +valid Lua source files and strings into abstract syntax trees +(AST). This README includes a description of this AST format. People +interested by Lua code analysis and generation are encouraged to +produce and/or consume this format to represent ASTs. + +It has been designed for Lua 5.1. It hasn't been tested against +Lua 5.2, but should be easily ported. + +## Usage + +Module `metalua.compiler` has a `new()` function, which returns a +compiler instance. This instance has a set of methods of the form +`:xxx_to_yyy(input)`, where `xxx` and `yyy` must be one of the +following: + +* `srcfile` the name of a Lua source file; +* `src` a string containing the Lua sources of a list of statements; +* `lexstream` a lexical tokens stream; +* `ast` an abstract syntax tree; +* `bytecode` a chunk of Lua bytecode that can be loaded in a Lua 5.1 + VM (not available if you only installed the parser); +* `function` an executable Lua function. + +Compiling into bytecode or executable functions requires the whole +Metalua compiler, not only the parser. The most frequently used +functions are `:src_to_ast(source_string)` and +`:srcfile_to_ast("path/to/source/file.lua")`. + + mlc = require 'metalua.compiler'.new() + ast = mlc :src_to_ast[[ return 123 ]] + +A compiler instance can be reused as much as you want; it's only +interesting to work with more than one compiler instance when you +start extending their grammars. + +## Abstract Syntax Trees definition + +### Notation + +Trees are written below with some Metalua syntax sugar, which +increases their readability. the backquote symbol introduces a `tag`, +i.e. a string stored in the `"tag"` field of a table: + +* `` `Foo{ 1, 2, 3 }`` is a shortcut for `{tag="Foo", 1, 2, 3}`; +* `` `Foo`` is a shortcut for `{tag="Foo"}`; +* `` `Foo 123`` is a shortcut for `` `Foo{ 123 }``, and therefore + `{tag="Foo", 123 }`; the expression after the tag must be a literal + number or string. + +When using a Metalua interpreter or compiler, the backtick syntax is +supported and can be used directly. Metalua's pretty-printing helpers +also try to use backtick syntax whenever applicable. + +### Tree elements + +Tree elements are mainly categorized into statements `stat`, +expressions `expr` and lists of statements `block`. Auxiliary +definitions include function applications/method invocation `apply`, +are both valid statements and expressions, expressions admissible on +the left-hand-side of an assignment statement `lhs`. + + block: { stat* } + + stat: + `Do{ stat* } + | `Set{ {lhs+} {expr+} } -- lhs1, lhs2... = e1, e2... + | `While{ expr block } -- while e do b end + | `Repeat{ block expr } -- repeat b until e + | `If{ (expr block)+ block? } -- if e1 then b1 [elseif e2 then b2] ... [else bn] end + | `Fornum{ ident expr expr expr? block } -- for ident = e, e[, e] do b end + | `Forin{ {ident+} {expr+} block } -- for i1, i2... in e1, e2... do b end + | `Local{ {ident+} {expr+}? } -- local i1, i2... = e1, e2... + | `Localrec{ ident expr } -- only used for 'local function' + | `Goto{ } -- goto str + | `Label{ } -- ::str:: + | `Return{ } -- return e1, e2... + | `Break -- break + | apply + + expr: + `Nil | `Dots | `True | `False + | `Number{ } + | `String{ } + | `Function{ { ident* `Dots? } block } + | `Table{ ( `Pair{ expr expr } | expr )* } + | `Op{ opid expr expr? } + | `Paren{ expr } -- significant to cut multiple values returns + | apply + | lhs + + apply: + `Call{ expr expr* } + | `Invoke{ expr `String{ } expr* } + + ident: `Id{ } + + lhs: ident | `Index{ expr expr } + + opid: 'add' | 'sub' | 'mul' | 'div' + | 'mod' | 'pow' | 'concat'| 'eq' + | 'lt' | 'le' | 'and' | 'or' + | 'not' | 'len' + +### Meta-data (lineinfo) + + +ASTs also embed some metadata, allowing to map them to their source +representation. Those informations are stored in a `"lineinfo"` field +in each tree node, which points to the range of characters in the +source string which represents it, and to the content of any comment +that would appear immediately before or after that node. + +Lineinfo objects have two fields, `"first"` and `"last"`, describing +respectively the beginning and the end of the subtree in the +sources. For instance, the sub-node ``Number{123}` produced by parsing +`[[return 123]]` will have `lineinfo.first` describing offset 8, and +`lineinfo.last` describing offset 10: + + + > mlc = require 'metalua.compiler'.new() + > ast = mlc :src_to_ast "return 123 -- comment" + > print(ast[1][1].lineinfo) + + > + +A lineinfo keeps track of character offsets relative to the beginning +of the source string/file ("K8-10" above), line numbers (L1 above; a +lineinfo spanning on several lines would read something like "L1-10"), +columns i.e. offset within the line ("C8-10" above), and a filename if +available (the "?" mark above indicating that we have no file name, as +the AST comes from a string). The final "|C>" indicates that there's a +comment immediately after the node; an initial "= 5.1" +} +build = { + type="builtin", + modules={ + ["metalua.grammar.generator"] = "metalua/grammar/generator.lua", + ["metalua.grammar.lexer"] = "metalua/grammar/lexer.lua", + ["metalua.compiler.parser"] = "metalua/compiler/parser.lua", + ["metalua.compiler.parser.table"] = "metalua/compiler/parser/table.lua", + ["metalua.compiler.parser.ext"] = "metalua/compiler/parser/ext.lua", + ["metalua.compiler.parser.annot.generator"] = "metalua/compiler/parser/annot/generator.lua", + ["metalua.compiler.parser.annot.grammar"] = "metalua/compiler/parser/annot/grammar.lua", + ["metalua.compiler.parser.stat"] = "metalua/compiler/parser/stat.lua", + ["metalua.compiler.parser.misc"] = "metalua/compiler/parser/misc.lua", + ["metalua.compiler.parser.lexer"] = "metalua/compiler/parser/lexer.lua", + ["metalua.compiler.parser.meta"] = "metalua/compiler/parser/meta.lua", + ["metalua.compiler.parser.expr"] = "metalua/compiler/parser/expr.lua", + ["metalua.compiler"] = "metalua/compiler.lua", + ["metalua.pprint"] = "metalua/pprint.lua", + } +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/rock_manifest b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/rock_manifest new file mode 100644 index 000000000..4640e3d33 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/rock_manifest @@ -0,0 +1,34 @@ +rock_manifest = { + doc = { + ["README-compiler.md"] = "292523d759247d210d32fb2f6153e0f4", + ["README-parser.md"] = "b44e3673d96dd296f2c0e92a6c87ee18", + ["README.md"] = "20bfb490cddef9e101e44688791abcda" + }, + lua = { + metalua = { + compiler = { + parser = { + annot = { + ["generator.lua"] = "d86f7507d66ba6a3692a6f8611e9939b", + ["grammar.lua"] = "7d195bde7992efd9923771751b67b18f" + }, + ["expr.lua"] = "3a0b1984a6f92280e2e63b074fdcec10", + ["ext.lua"] = "a99e31a07bc390b826f6653bcc47d89b", + ["lexer.lua"] = "eac0f9d475d9dae4ea5a2724014cebec", + ["meta.lua"] = "12870bceda6395695020b739196e2a92", + ["misc.lua"] = "49d59f4fc1bfb77b36f78d4f87ae258f", + ["stat.lua"] = "83f10ac899be12ca4df58bbe8645299f", + ["table.lua"] = "5d2389e89603b7f78c731e6918aa1a9b" + }, + ["parser.lua"] = "e6ae68ce200de8071bb0fefad97f9b79" + }, + ["compiler.lua"] = "ca65ee9a3053581f4315821a31d0c1fd", + grammar = { + ["generator.lua"] = "b8a29e817d6798c12f40a230a0f6d0af", + ["lexer.lua"] = "7cb7c835479a9be884130eaacb9be60a" + }, + ["pprint.lua"] = "0b9bd8757b45c2d4be30106abcbd45b2" + } + }, + ["metalua-parser-0.7.3-2.rockspec"] = "a56680900b0b51701db7cd7abf49af92" +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/penlight/0.9.8-1/penlight-0.9.8-1.rockspec b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/penlight/0.9.8-1/penlight-0.9.8-1.rockspec new file mode 100644 index 000000000..d1d8b0f85 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/penlight/0.9.8-1/penlight-0.9.8-1.rockspec @@ -0,0 +1,66 @@ +package = "penlight" +version = "0.9.8-1" + +source = { + dir = "penlight-0.9.8", + url = "http://stevedonovan.github.com/files/penlight-0.9.8-core.zip", +} + +description = { + summary = "Lua utility libraries loosely based on the Python standard libraries", + homepage = "http://stevedonovan.github.com/Penlight", + license = "MIT/X11", + maintainer = "steve.j.donovan@gmail.com", + detailed = [[ +Penlight is a set of pure Lua libraries for making it easier to work with common tasks like +iterating over directories, reading configuration files and the like. Provides functional operations +on tables and sequences. +]] +} + +dependencies = { + "luafilesystem", +} + +build = { + type = "builtin", + modules = { + ["pl.strict"] = "lua/pl/strict.lua", + ["pl.dir"] = "lua/pl/dir.lua", + ["pl.operator"] = "lua/pl/operator.lua", + ["pl.input"] = "lua/pl/input.lua", + ["pl.config"] = "lua/pl/config.lua", + ["pl.seq"] = "lua/pl/seq.lua", + ["pl.stringio"] = "lua/pl/stringio.lua", + ["pl.text"] = "lua/pl/text.lua", + ["pl.test"] = "lua/pl/test.lua", + ["pl.tablex"] = "lua/pl/tablex.lua", + ["pl.app"] = "lua/pl/app.lua", + ["pl.stringx"] = "lua/pl/stringx.lua", + ["pl.lexer"] = "lua/pl/lexer.lua", + ["pl.utils"] = "lua/pl/utils.lua", + ["pl.sip"] = "lua/pl/sip.lua", + ["pl.permute"] = "lua/pl/permute.lua", + ["pl.pretty"] = "lua/pl/pretty.lua", + ["pl.class"] = "lua/pl/class.lua", + ["pl.List"] = "lua/pl/List.lua", + ["pl.data"] = "lua/pl/data.lua", + ["pl.Date"] = "lua/pl/Date.lua", + ["pl"] = "lua/pl/init.lua", + ["pl.luabalanced"] = "lua/pl/luabalanced.lua", + ["pl.comprehension"] = "lua/pl/comprehension.lua", + ["pl.path"] = "lua/pl/path.lua", + ["pl.array2d"] = "lua/pl/array2d.lua", + ["pl.func"] = "lua/pl/func.lua", + ["pl.lapp"] = "lua/pl/lapp.lua", + ["pl.file"] = "lua/pl/file.lua", + ['pl.template'] = "lua/pl/template.lua", + ["pl.Map"] = "lua/pl/Map.lua", + ["pl.MultiMap"] = "lua/pl/MultiMap.lua", + ["pl.OrderedMap"] = "lua/pl/OrderedMap.lua", + ["pl.Set"] = "lua/pl/Set.lua", + ["pl.xml"] = "lua/pl/xml.lua", + ["pl.platf.luajava"] = "lua/pl/platf/luajava.lua" + }, +} + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/penlight/0.9.8-1/rock_manifest b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/penlight/0.9.8-1/rock_manifest new file mode 100644 index 000000000..8565ebbd3 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/penlight/0.9.8-1/rock_manifest @@ -0,0 +1,45 @@ +rock_manifest = { + lua = { + pl = { + ["Date.lua"] = "d2131d59151ce978c4db6a648fcd275a", + ["List.lua"] = "1236c5eb08956619daacd25a462a9682", + ["Map.lua"] = "0297a536ac0595ac59e8828f8c867f53", + ["MultiMap.lua"] = "e5f898fe2443e51c38825e9bc3d1aee5", + ["OrderedMap.lua"] = "bd8e39c59e22c582a33e2f025d3ae914", + ["Set.lua"] = "346ff7392fd4aeda418fb832e8da7a7f", + ["app.lua"] = "23ffb79e69a3fd679013cf82d95ed792", + ["array2d.lua"] = "77618ec2e2de4d6d237484dfd742cd73", + ["class.lua"] = "6f58bf39e7f90711b6840ad6955d258e", + ["comprehension.lua"] = "f8600ba945dde5d959194500a687c69f", + ["config.lua"] = "9ea3ce0ac3cdf2ce0e17f1353f32abb6", + ["data.lua"] = "be446ff813b5bcf30b4063601165df6a", + ["dir.lua"] = "3d60d4c1caeaabe199fe361e4e9b14a4", + ["file.lua"] = "f5c9527ea14b511d2cb9af80b219c562", + ["func.lua"] = "cc50d73512b6d0518f6587b82844de8c", + ["init.lua"] = "9232be7d8790d4f907972a00dec7949d", + ["input.lua"] = "bab7c64ca9a740df5e9fb9909610bbc4", + ["lapp.lua"] = "1cc81f048bc3fcd775c40cd9a2d601a7", + ["lexer.lua"] = "da0db5e323a2d37545ccb02592d0d3c8", + ["luabalanced.lua"] = "00b94a997a9ea4d73f54c10893f3b35f", + ["operator.lua"] = "e606629c738966cf497bb938457adebd", + ["path.lua"] = "b0714bc337c068b7252f64250fe59604", + ["permute.lua"] = "b0ed9ba2787119ef99468329a54ea16a", + platf = { + ["luajava.lua"] = "9c2898667281ad9501cc05a8e31a6f53" + }, + ["pretty.lua"] = "3ece64317ce05916eaba91fa96d9e7c0", + ["seq.lua"] = "e99e420345ab11120a7b741d8184920a", + ["sip.lua"] = "bde74f65e7246017d3ef034d178100ea", + ["strict.lua"] = "720e939931dbbe42fad8fd4e7736435e", + ["stringio.lua"] = "a8f4c786ea1b62f16ed05e6b09840044", + ["stringx.lua"] = "43f57755969c6b4001316226506a3744", + ["tablex.lua"] = "dec027cc3a3901766bd933c5fc0f3e93", + ["template.lua"] = "f358175bbb84c401c6213c953ce295a4", + ["test.lua"] = "1c45f7b1c438673f1eb668e2ca592f1c", + ["text.lua"] = "c30f90cab2d00186a6432e408ba1fe14", + ["utils.lua"] = "68cd38638a29b4ab5f1cc0eae38dce77", + ["xml.lua"] = "e13ed468c450fccb9a8e858a0f787eef" + } + }, + ["penlight-0.9.8-1.rockspec"] = "96edac3ff1d0ac57cb45d6551a56a775" +} diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/man/man1/lua.1 b/Moose Development Evironment Setup/LuaFiles/lua/5.1/man/man1/lua.1 new file mode 100644 index 000000000..24809cc6c --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/man/man1/lua.1 @@ -0,0 +1,163 @@ +.\" $Id: lua.man,v 1.11 2006/01/06 16:03:34 lhf Exp $ +.TH LUA 1 "$Date: 2006/01/06 16:03:34 $" +.SH NAME +lua \- Lua interpreter +.SH SYNOPSIS +.B lua +[ +.I options +] +[ +.I script +[ +.I args +] +] +.SH DESCRIPTION +.B lua +is the stand-alone Lua interpreter. +It loads and executes Lua programs, +either in textual source form or +in precompiled binary form. +(Precompiled binaries are output by +.BR luac , +the Lua compiler.) +.B lua +can be used as a batch interpreter and also interactively. +.LP +The given +.I options +(see below) +are executed and then +the Lua program in file +.I script +is loaded and executed. +The given +.I args +are available to +.I script +as strings in a global table named +.BR arg . +If these arguments contain spaces or other characters special to the shell, +then they should be quoted +(but note that the quotes will be removed by the shell). +The arguments in +.B arg +start at 0, +which contains the string +.RI ' script '. +The index of the last argument is stored in +.BR arg.n . +The arguments given in the command line before +.IR script , +including the name of the interpreter, +are available in negative indices in +.BR arg . +.LP +At the very start, +before even handling the command line, +.B lua +executes the contents of the environment variable +.BR LUA_INIT , +if it is defined. +If the value of +.B LUA_INIT +is of the form +.RI '@ filename ', +then +.I filename +is executed. +Otherwise, the string is assumed to be a Lua statement and is executed. +.LP +Options start with +.B '\-' +and are described below. +You can use +.B "'\--'" +to signal the end of options. +.LP +If no arguments are given, +then +.B "\-v \-i" +is assumed when the standard input is a terminal; +otherwise, +.B "\-" +is assumed. +.LP +In interactive mode, +.B lua +prompts the user, +reads lines from the standard input, +and executes them as they are read. +If a line does not contain a complete statement, +then a secondary prompt is displayed and +lines are read until a complete statement is formed or +a syntax error is found. +So, one way to interrupt the reading of an incomplete statement is +to force a syntax error: +adding a +.B ';' +in the middle of a statement is a sure way of forcing a syntax error +(except inside multiline strings and comments; these must be closed explicitly). +If a line starts with +.BR '=' , +then +.B lua +displays the values of all the expressions in the remainder of the +line. The expressions must be separated by commas. +The primary prompt is the value of the global variable +.BR _PROMPT , +if this value is a string; +otherwise, the default prompt is used. +Similarly, the secondary prompt is the value of the global variable +.BR _PROMPT2 . +So, +to change the prompts, +set the corresponding variable to a string of your choice. +You can do that after calling the interpreter +or on the command line +(but in this case you have to be careful with quotes +if the prompt string contains a space; otherwise you may confuse the shell.) +The default prompts are "> " and ">> ". +.SH OPTIONS +.TP +.B \- +load and execute the standard input as a file, +that is, +not interactively, +even when the standard input is a terminal. +.TP +.BI \-e " stat" +execute statement +.IR stat . +You need to quote +.I stat +if it contains spaces, quotes, +or other characters special to the shell. +.TP +.B \-i +enter interactive mode after +.I script +is executed. +.TP +.BI \-l " name" +call +.BI require(' name ') +before executing +.IR script . +Typically used to load libraries. +.TP +.B \-v +show version information. +.SH "SEE ALSO" +.BR luac (1) +.br +http://www.lua.org/ +.SH DIAGNOSTICS +Error messages should be self explanatory. +.SH AUTHORS +R. Ierusalimschy, +L. H. de Figueiredo, +and +W. Celes +.\" EOF diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/man/man1/luac.1 b/Moose Development Evironment Setup/LuaFiles/lua/5.1/man/man1/luac.1 new file mode 100644 index 000000000..d8146782d --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/man/man1/luac.1 @@ -0,0 +1,136 @@ +.\" $Id: luac.man,v 1.28 2006/01/06 16:03:34 lhf Exp $ +.TH LUAC 1 "$Date: 2006/01/06 16:03:34 $" +.SH NAME +luac \- Lua compiler +.SH SYNOPSIS +.B luac +[ +.I options +] [ +.I filenames +] +.SH DESCRIPTION +.B luac +is the Lua compiler. +It translates programs written in the Lua programming language +into binary files that can be later loaded and executed. +.LP +The main advantages of precompiling chunks are: +faster loading, +protecting source code from accidental user changes, +and +off-line syntax checking. +.LP +Pre-compiling does not imply faster execution +because in Lua chunks are always compiled into bytecodes before being executed. +.B luac +simply allows those bytecodes to be saved in a file for later execution. +.LP +Pre-compiled chunks are not necessarily smaller than the corresponding source. +The main goal in pre-compiling is faster loading. +.LP +The binary files created by +.B luac +are portable only among architectures with the same word size and byte order. +.LP +.B luac +produces a single output file containing the bytecodes +for all source files given. +By default, +the output file is named +.BR luac.out , +but you can change this with the +.B \-o +option. +.LP +In the command line, +you can mix +text files containing Lua source and +binary files containing precompiled chunks. +This is useful to combine several precompiled chunks, +even from different (but compatible) platforms, +into a single precompiled chunk. +.LP +You can use +.B "'\-'" +to indicate the standard input as a source file +and +.B "'\--'" +to signal the end of options +(that is, +all remaining arguments will be treated as files even if they start with +.BR "'\-'" ). +.LP +The internal format of the binary files produced by +.B luac +is likely to change when a new version of Lua is released. +So, +save the source files of all Lua programs that you precompile. +.LP +.SH OPTIONS +Options must be separate. +.TP +.B \-l +produce a listing of the compiled bytecode for Lua's virtual machine. +Listing bytecodes is useful to learn about Lua's virtual machine. +If no files are given, then +.B luac +loads +.B luac.out +and lists its contents. +.TP +.BI \-o " file" +output to +.IR file , +instead of the default +.BR luac.out . +(You can use +.B "'\-'" +for standard output, +but not on platforms that open standard output in text mode.) +The output file may be a source file because +all files are loaded before the output file is written. +Be careful not to overwrite precious files. +.TP +.B \-p +load files but do not generate any output file. +Used mainly for syntax checking and for testing precompiled chunks: +corrupted files will probably generate errors when loaded. +Lua always performs a thorough integrity test on precompiled chunks. +Bytecode that passes this test is completely safe, +in the sense that it will not break the interpreter. +However, +there is no guarantee that such code does anything sensible. +(None can be given, because the halting problem is unsolvable.) +If no files are given, then +.B luac +loads +.B luac.out +and tests its contents. +No messages are displayed if the file passes the integrity test. +.TP +.B \-s +strip debug information before writing the output file. +This saves some space in very large chunks, +but if errors occur when running a stripped chunk, +then the error messages may not contain the full information they usually do. +For instance, +line numbers and names of local variables are lost. +.TP +.B \-v +show version information. +.SH FILES +.TP 15 +.B luac.out +default output file +.SH "SEE ALSO" +.BR lua (1) +.br +http://www.lua.org/ +.SH DIAGNOSTICS +Error messages should be self explanatory. +.SH AUTHORS +L. H. de Figueiredo, +R. Ierusalimschy and +W. Celes +.\" EOF diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/defaultcss.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/defaultcss.lua new file mode 100644 index 000000000..2c2b331cc --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/defaultcss.lua @@ -0,0 +1,270 @@ +return [[html { + color: #000; + background: #FFF; +} +body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td { + margin: 0; + padding: 0; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +fieldset,img { + border: 0; +} +address,caption,cite,code,dfn,em,strong,th,var,optgroup { + font-style: inherit; + font-weight: inherit; +} +del,ins { + text-decoration: none; +} +li { + list-style: bullet; + margin-left: 20px; +} +caption,th { + text-align: left; +} +h1,h2,h3,h4,h5,h6 { + font-size: 100%; + font-weight: bold; +} +q:before,q:after { + content: ''; +} +abbr,acronym { + border: 0; + font-variant: normal; +} +sup { + vertical-align: baseline; +} +sub { + vertical-align: baseline; +} +legend { + color: #000; +} +input,button,textarea,select,optgroup,option { + font-family: inherit; + font-size: inherit; + font-style: inherit; + font-weight: inherit; +} +input,button,textarea,select {*font-size:100%; +} +/* END RESET */ + +body { + margin-left: 1em; + margin-right: 1em; + font-family: arial, helvetica, geneva, sans-serif; + background-color: #ffffff; margin: 0px; +} + +code, tt { font-family: monospace; } + +body, p, td, th { font-size: .95em; line-height: 1.2em;} + +p, ul { margin: 10px 0 0 10px;} + +strong { font-weight: bold;} + +em { font-style: italic;} + +h1 { + font-size: 1.5em; + margin: 25px 0 20px 0; +} +h2, h3, h4 { margin: 15px 0 10px 0; } +h2 { font-size: 1.25em; } +h3 { font-size: 1.15em; } +h4 { font-size: 1.06em; } + +a:link { font-weight: bold; color: #004080; text-decoration: none; } +a:visited { font-weight: bold; color: #006699; text-decoration: none; } +a:link:hover { text-decoration: underline; } + +hr { + color:#cccccc; + background: #00007f; + height: 1px; +} + +blockquote { margin-left: 3em; } + +ul { list-style-type: disc; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; +} + +p:first-child { + margin-top: 0px; +} + +pre.example { + background-color: rgb(245, 245, 245); + border: 1px solid silver; + padding: 10px; + margin: 10px 0 10px 0; + font-family: "Andale Mono", monospace; + font-size: .85em; +} + +pre { + background-color: rgb(245, 245, 245); + border: 1px solid silver; + padding: 10px; + margin: 10px 0 10px 0; + font-family: "Andale Mono", monospace; +} + + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } + +#container { + margin-left: 1em; + margin-right: 1em; + background-color: #f0f0f0; +} + +#product { + text-align: center; + border-bottom: 1px solid #cccccc; + background-color: #ffffff; +} + +#product big { + font-size: 2em; +} + +#main { + background-color: #f0f0f0; + border-left: 2px solid #cccccc; +} + +#navigation { + float: left; + width: 18em; + vertical-align: top; + background-color: #f0f0f0; + overflow: scroll; + position: fixed; + height:100%; +} + +#navigation h2 { + background-color:#e7e7e7; + font-size:1.1em; + color:#000000; + text-align: left; + padding:0.2em; + border-top:1px solid #dddddd; + border-bottom:1px solid #dddddd; +} + +#navigation ul +{ + font-size:1em; + list-style-type: none; + margin: 1px 1px 10px 1px; +} + +#navigation li { + text-indent: -1em; + display: block; + margin: 3px 0px 0px 22px; +} + +#navigation li li a { + margin: 0px 3px 0px -1em; +} + +#content { + margin-left: 18em; + padding: 1em; + border-left: 2px solid #cccccc; + border-right: 2px solid #cccccc; + background-color: #ffffff; +} + +#about { + clear: both; + padding: 5px; + border-top: 2px solid #cccccc; + background-color: #ffffff; +} + +@media print { + body { + font: 12pt "Times New Roman", "TimeNR", Times, serif; + } + a { font-weight: bold; color: #004080; text-decoration: underline; } + + #main { + background-color: #ffffff; + border-left: 0px; + } + + #container { + margin-left: 2%; + margin-right: 2%; + background-color: #ffffff; + } + + #content { + padding: 1em; + background-color: #ffffff; + } + + #navigation { + display: none; + } + pre.example { + font-family: "Andale Mono", monospace; + font-size: 10pt; + page-break-inside: avoid; + } +} + +table.module_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.module_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.module_list td.name { background-color: #f0f0f0; } +table.module_list td.summary { width: 100%; } + + +table.function_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.function_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.function_list td.name { background-color: #f0f0f0; } +table.function_list td.summary { width: 100%; } + +dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} +dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} +dl.table h3, dl.function h3 {font-size: .95em;} + +]] diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/docgenerator.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/docgenerator.lua new file mode 100644 index 000000000..2a4d88812 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/docgenerator.lua @@ -0,0 +1,87 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2012-2014 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Kevin KIN-FOO +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- + +-- +-- Load documentation generator and update its path +-- +local templateengine = require 'templateengine' +for name, def in pairs( require 'template.utils' ) do + templateengine.env [ name ] = def +end + +-- Load documentation extractor and set handled languages +local lddextractor = require 'lddextractor' + +local M = {} +M.defaultsitemainpagename = 'index' + +function M.generatedocforfiles(filenames, cssname,noheuristic) + if not filenames then return nil, 'No files provided.' end + -- + -- Generate API model elements for all files + -- + local generatedfiles = {} + local wrongfiles = {} + for _, filename in pairs( filenames ) do + -- Load file content + local file, error = io.open(filename, 'r') + if not file then return nil, 'Unable to read "'..filename..'"\n'..err end + local code = file:read('*all') + file:close() + -- Get module for current file + local apimodule, err = lddextractor.generateapimodule(filename, code,noheuristic) + + -- Handle modules with module name + if apimodule and apimodule.name then + generatedfiles[ apimodule.name ] = apimodule + elseif not apimodule then + -- Track faulty files + table.insert(wrongfiles, 'Unable to extract comments from "'..filename..'".\n'..err) + elseif not apimodule.name then + -- Do not generate documentation for unnamed modules + table.insert(wrongfiles, 'Unable to create documentation for "'..filename..'", no module name provided.') + end + end + -- + -- Defining index, which will summarize all modules + -- + local index = { + modules = generatedfiles, + name = M.defaultsitemainpagename, + tag='index' + } + generatedfiles[ M.defaultsitemainpagename ] = index + + -- + -- Define page cursor + -- + local page = { + currentmodule = nil, + headers = { [[]] }, + modules = generatedfiles, + tag = 'page' + } + + -- + -- Iterate over modules, generating complete doc pages + -- + for _, module in pairs( generatedfiles ) do + -- Update current cursor page + page.currentmodule = module + -- Generate page + local content, error = templateengine.applytemplate(page) + if not content then return nil, error end + module.body = content + end + return generatedfiles, wrongfiles +end +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/extractors.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/extractors.lua new file mode 100644 index 000000000..aa5235ea9 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/extractors.lua @@ -0,0 +1,102 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2012-2014 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Kevin KIN-FOO +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +local M = {} +require 'metalua.loader' +local compiler = require 'metalua.compiler' +local mlc = compiler.new() +local Q = require 'metalua.treequery' + +-- Enable to retrieve all Javadoc-like comments from C code +function M.c(code) + if not code then return nil, 'No code provided' end + local comments = {} + -- Loop over comments stripping cosmetic '*' + for comment in code:gmatch('%s*/%*%*+(.-)%*+/') do + -- All Lua special comment are prefixed with an '-', + -- so we also comment C comment to make them compliant + table.insert(comments, '-'..comment) + end + return comments +end + +-- Enable to retrieve "---" comments from Lua code +function M.lua( code ) + if not code then return nil, 'No code provided' end + + -- manage shebang + if code then code = code:gsub("^(#.-\n)", function (s) return string.rep(' ',string.len(s)) end) end + + -- check for errors + local f, err = loadstring(code,'source_to_check') + if not f then + return nil, 'Syntax error.\n' .. err + end + + -- Get ast from file + local status, ast = pcall(mlc.src_to_ast, mlc, code) + -- + -- Detect parsing errors + -- + if not status then + return nil, 'There might be a syntax error.\n' .. ast + end + + -- + -- Extract commented nodes from AST + -- + + -- Function enabling commented node selection + local function acceptcommentednode(node) + return node.lineinfo and ( node.lineinfo.last.comments or node.lineinfo.first.comments ) + end + + -- Fetch commented node from AST + local commentednodes = Q(ast):filter( acceptcommentednode ):list() + + -- Comment cache to avoid selecting same comment twice + local commentcache = {} + -- Will contain selected comments + local comments = {} + + -- Loop over commented nodes + for _, node in ipairs( commentednodes ) do + + -- A node can is relateds to comment before and after itself, + -- the following gathers them. + local commentlists = {} + if node.lineinfo and node.lineinfo.first.comments then + table.insert(commentlists, node.lineinfo.first.comments) + end + if node.lineinfo and node.lineinfo.last.comments then + table.insert(commentlists, node.lineinfo.last.comments) + end + -- Now that we have comments before and fater the node, + -- collect them in a single table + for _, list in ipairs( commentlists ) do + for _, commenttable in ipairs(list) do + -- Only select special comments + local firstcomment = #commenttable > 0 and #commenttable[1] > 0 and commenttable[1] + if firstcomment:sub(1, 1) == '-' then + for _, comment in ipairs( commenttable ) do + -- Only comments which were not already collected + if not commentcache[comment] then + commentcache[comment] = true + table.insert(comments, comment) + end + end + end + end + end + end + return comments +end +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/fs/lfs.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/fs/lfs.lua new file mode 100644 index 000000000..3fc513542 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/fs/lfs.lua @@ -0,0 +1,130 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2012-2014 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Kevin KIN-FOO +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +local lfs = require 'lfs' +local M = {} +local function iswindows() + local p = io.popen("echo %os%") + if not p then + return false + end + local result =p:read("*l") + p:close() + return result == "Windows_NT" +end +M.separator = iswindows() and [[\]] or [[/]] +--- +-- Will recursively browse given directories and list files encountered +-- @param tab Table, list where files will be added +-- @param dirorfiles list of path to browse in order to build list. +-- Files from this list will be added to tab list. +-- @return tab list, table containing all files from directories +-- and files contained in dirorfile +local function appendfiles(tab, dirorfile) + + -- Nothing to process + if #dirorfile < 1 then return tab end + + -- Append all files to list + local dirs = {} + for _, path in ipairs( dirorfile ) do + -- Determine element nature + local elementnature = lfs.attributes (path, "mode") + + -- Handle files + if elementnature == 'file' then + table.insert(tab, path) + else if elementnature == 'directory' then + + -- Check if folder is accessible + local status, error = pcall(lfs.dir, path) + if not status then return nil, error end + + -- + -- Handle folders + -- + for diskelement in lfs.dir(path) do + + -- Format current file name + local currentfilename + if path:sub(#path) == M.separator then + currentfilename = path .. diskelement + else + currentfilename = path .. M.separator .. diskelement + end + + -- Handle folder elements + local nature, err = lfs.attributes (currentfilename, "mode") + -- Append file to current list + if nature == 'file' then + table.insert(tab, currentfilename) + elseif nature == 'directory' then + -- Avoid current and parent directory in order to avoid + -- endless recursion + if diskelement ~= '.' and diskelement ~= '..' then + -- Handle subfolders + table.insert(dirs, currentfilename) + end + end + end + end + end + end + -- If we only encountered files, going deeper is useless + if #dirs == 0 then return tab end + -- Append files from encountered directories + return appendfiles(tab, dirs) +end +--- +-- Provide a list of files from a directory +-- @param list Table of directories to browse +-- @return table of string, path to files contained in given directories +function M.filelist(list) + if not list then return nil, 'No directory list provided' end + return appendfiles({}, list) +end +function M.checkdirectory( dirlist ) + if not dirlist then return false end + local missingdirs = {} + for _, filename in ipairs( dirlist ) do + if not lfs.attributes(filename, 'mode') then + table.insert(missingdirs, filename) + end + end + if #missingdirs > 0 then + return false, missingdirs + end + return true +end +function M.fill(filename, content) + -- + -- Ensure parent directory exists + -- + local parent = filename:gmatch([[(.*)]] .. M.separator ..[[(.+)]])() + local parentnature = lfs.attributes(parent, 'mode') + -- Create parent directory while absent + if not parentnature then + lfs.mkdir( parent ) + elseif parentnature ~= 'directory' then + -- Notify that disk element already exists + return nil, parent..' is a '..parentnature..'.' + end + + -- Create actual file + local file, error = io.open(filename, 'w') + if not file then + return nil, error + end + file:write( content ) + file:close() + return true +end +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/lddextractor.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/lddextractor.lua new file mode 100644 index 000000000..b2cea2ea3 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/lddextractor.lua @@ -0,0 +1,113 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2012-2014 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Kevin KIN-FOO +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +require 'metalua.loader' +local compiler = require 'metalua.compiler' +local mlc = compiler.new() +local M = {} + +-- +-- Define default supported languages +-- +M.supportedlanguages = {} +local extractors = require 'extractors' + +-- Support Lua comment extracting +M.supportedlanguages['lua'] = extractors.lua + +-- Support C comment extracting +for _,c in ipairs({'c', 'cpp', 'c++'}) do + M.supportedlanguages[c] = extractors.c +end + +-- Extract comment from code, +-- type of code is deduced from filename extension +function M.extract(filename, code) + -- Check parameters + if not code then return nil, 'No code provided' end + if type(filename) ~= "string" then + return nil, 'No string for file name provided' + end + + -- Extract file extension + local fileextension = filename:gmatch('.*%.(.*)')() + if not fileextension then + return nil, 'File '..filename..' has no extension, could not determine how to extract documentation.' + end + + -- Check if it is possible to extract documentation from these files + local extractor = M.supportedlanguages[ fileextension ] + if not extractor then + return nil, 'Unable to extract documentation from '.. fileextension .. ' file.' + end + return extractor( code ) +end +-- Generate a file gathering only comments from given code +function M.generatecommentfile(filename, code) + local comments, error = M.extract(filename, code) + if not comments then + return nil, 'Unable to generate comment file.\n'..error + end + local filecontent = {} + for _, comment in ipairs( comments ) do + table.insert(filecontent, "--[[") + table.insert(filecontent, comment) + table.insert(filecontent, "\n]]\n\n") + end + return table.concat(filecontent)..'return nil\n' +end +-- Create API Model module from a 'comment only' lua file +function M.generateapimodule(filename, code,noheuristic) + if not filename then return nil, 'No file name given.' end + if not code then return nil, 'No code provided.' end + if type(filename) ~= "string" then return nil, 'No string for file name provided' end + + -- for non lua file get comment file + if filename:gmatch('.*%.(.*)')() ~= 'lua' then + local err + code, err = M.generatecommentfile(filename, code) + if not code then + return nil, 'Unable to create api module for "'..filename..'".\n'..err + end + else + + -- manage shebang + if code then code = code:gsub("^(#.-\n)", function (s) return string.rep(' ',string.len(s)) end) end + + -- check for errors + local f, err = loadstring(code,'source_to_check') + if not f then + return nil, 'File'..filename..'contains syntax error.\n' .. err + end + end + + local status, ast = pcall(mlc.src_to_ast, mlc, code) + if not status then + return nil, 'Unable to compute ast for "'..filename..'".\n'..ast + end + + -- Extract module name as the filename without extension + local modulename + local matcher = string.gmatch(filename,'.*/(.*)%..*$') + if matcher then modulename = matcher() end + + -- Create api model + local apimodelbuilder = require 'models.apimodelbuilder' + local _file, comment2apiobj = apimodelbuilder.createmoduleapi(ast, modulename) + + -- Create internal model + if not noheuristic then + local internalmodelbuilder = require "models.internalmodelbuilder" + local _internalcontent = internalmodelbuilder.createinternalcontent(ast,_file,comment2apiobj, modulename) + end + return _file +end +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/markdown.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/markdown.lua new file mode 100644 index 000000000..b16d43b3d --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/markdown.lua @@ -0,0 +1,1359 @@ +#!/usr/bin/env lua + +--[[ +# markdown.lua -- version 0.32 + + + +**Author:** Niklas Frykholm, +**Date:** 31 May 2008 + +This is an implementation of the popular text markup language Markdown in pure Lua. +Markdown can convert documents written in a simple and easy to read text format +to well-formatted HTML. For a more thourough description of Markdown and the Markdown +syntax, see . + +The original Markdown source is written in Perl and makes heavy use of advanced +regular expression techniques (such as negative look-ahead, etc) which are not available +in Lua's simple regex engine. Therefore this Lua port has been rewritten from the ground +up. It is probably not completely bug free. If you notice any bugs, please report them to +me. A unit test that exposes the error is helpful. + +## Usage + + require "markdown" + markdown(source) + +``markdown.lua`` exposes a single global function named ``markdown(s)`` which applies the +Markdown transformation to the specified string. + +``markdown.lua`` can also be used directly from the command line: + + lua markdown.lua test.md + +Creates a file ``test.html`` with the converted content of ``test.md``. Run: + + lua markdown.lua -h + +For a description of the command-line options. + +``markdown.lua`` uses the same license as Lua, the MIT license. + +## License + +Copyright © 2008 Niklas Frykholm. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +## Version history + +- **0.32** -- 31 May 2008 + - Fix for links containing brackets +- **0.31** -- 1 Mar 2008 + - Fix for link definitions followed by spaces +- **0.30** -- 25 Feb 2008 + - Consistent behavior with Markdown when the same link reference is reused +- **0.29** -- 24 Feb 2008 + - Fix for
 blocks with spaces in them
+-	**0.28** -- 18 Feb 2008
+	-	Fix for link encoding
+-	**0.27** -- 14 Feb 2008
+	-	Fix for link database links with ()
+-	**0.26** -- 06 Feb 2008
+	-	Fix for nested italic and bold markers
+-	**0.25** -- 24 Jan 2008
+	-	Fix for encoding of naked <
+-	**0.24** -- 21 Jan 2008
+	-	Fix for link behavior.
+-	**0.23** -- 10 Jan 2008
+	-	Fix for a regression bug in longer expressions in italic or bold.
+-	**0.22** -- 27 Dec 2007
+	-	Fix for crash when processing blocks with a percent sign in them.
+-	**0.21** -- 27 Dec 2007
+	- 	Fix for combined strong and emphasis tags
+-	**0.20** -- 13 Oct 2007
+	-	Fix for < as well in image titles, now matches Dingus behavior
+-	**0.19** -- 28 Sep 2007
+	-	Fix for quotation marks " and ampersands & in link and image titles.
+-	**0.18** -- 28 Jul 2007
+	-	Does not crash on unmatched tags (behaves like standard markdown)
+-	**0.17** -- 12 Apr 2007
+	-	Fix for links with %20 in them.
+-	**0.16** -- 12 Apr 2007
+	-	Do not require arg global to exist.
+-	**0.15** -- 28 Aug 2006
+	-	Better handling of links with underscores in them.
+-	**0.14** -- 22 Aug 2006
+	-	Bug for *`foo()`*
+-	**0.13** -- 12 Aug 2006
+	-	Added -l option for including stylesheet inline in document.
+	-	Fixed bug in -s flag.
+	-	Fixed emphasis bug.
+-	**0.12** -- 15 May 2006
+	-	Fixed several bugs to comply with MarkdownTest 1.0 
+-	**0.11** -- 12 May 2006
+	-	Fixed bug for escaping `*` and `_` inside code spans.
+	-	Added license terms.
+	-	Changed join() to table.concat().
+-	**0.10** -- 3 May 2006
+	-	Initial public release.
+
+// Niklas
+]]
+
+
+-- Set up a table for holding local functions to avoid polluting the global namespace
+local M = {}
+local MT = {__index = _G}
+setmetatable(M, MT)
+setfenv(1, M)
+
+----------------------------------------------------------------------
+-- Utility functions
+----------------------------------------------------------------------
+
+-- Locks table t from changes, writes an error if someone attempts to change the table.
+-- This is useful for detecting variables that have "accidently" been made global. Something
+-- I tend to do all too much.
+function lock(t)
+	function lock_new_index(t, k, v)
+		error("module has been locked -- " .. k .. " must be declared local", 2)
+	end
+
+	local mt = {__newindex = lock_new_index}
+	if getmetatable(t) then mt.__index = getmetatable(t).__index end
+	setmetatable(t, mt)
+end
+
+-- Returns the result of mapping the values in table t through the function f
+function map(t, f)
+	local out = {}
+	for k,v in pairs(t) do out[k] = f(v,k) end
+	return out
+end
+
+-- The identity function, useful as a placeholder.
+function identity(text) return text end
+
+-- Functional style if statement. (NOTE: no short circuit evaluation)
+function iff(t, a, b) if t then return a else return b end end
+
+-- Splits the text into an array of separate lines.
+function split(text, sep)
+	sep = sep or "\n"
+	local lines = {}
+	local pos = 1
+	while true do
+		local b,e = text:find(sep, pos)
+		if not b then table.insert(lines, text:sub(pos)) break end
+		table.insert(lines, text:sub(pos, b-1))
+		pos = e + 1
+	end
+	return lines
+end
+
+-- Converts tabs to spaces
+function detab(text)
+	local tab_width = 4
+	local function rep(match)
+		local spaces = -match:len()
+		while spaces<1 do spaces = spaces + tab_width end
+		return match .. string.rep(" ", spaces)
+	end
+	text = text:gsub("([^\n]-)\t", rep)
+	return text
+end
+
+-- Applies string.find for every pattern in the list and returns the first match
+function find_first(s, patterns, index)
+	local res = {}
+	for _,p in ipairs(patterns) do
+		local match = {s:find(p, index)}
+		if #match>0 and (#res==0 or match[1] < res[1]) then res = match end
+	end
+	return unpack(res)
+end
+
+-- If a replacement array is specified, the range [start, stop] in the array is replaced
+-- with the replacement array and the resulting array is returned. Without a replacement
+-- array the section of the array between start and stop is returned.
+function splice(array, start, stop, replacement)
+	if replacement then
+		local n = stop - start + 1
+		while n > 0 do
+			table.remove(array, start)
+			n = n - 1
+		end
+		for i,v in ipairs(replacement) do
+			table.insert(array, start, v)
+		end
+		return array
+	else
+		local res = {}
+		for i = start,stop do
+			table.insert(res, array[i])
+		end
+		return res
+	end
+end
+
+-- Outdents the text one step.
+function outdent(text)
+	text = "\n" .. text
+	text = text:gsub("\n  ? ? ?", "\n")
+	text = text:sub(2)
+	return text
+end
+
+-- Indents the text one step.
+function indent(text)
+	text = text:gsub("\n", "\n    ")
+	return text
+end
+
+-- Does a simple tokenization of html data. Returns the data as a list of tokens. 
+-- Each token is a table with a type field (which is either "tag" or "text") and
+-- a text field (which contains the original token data).
+function tokenize_html(html)
+	local tokens = {}
+	local pos = 1
+	while true do
+		local start = find_first(html, {"", start)
+		elseif html:match("^<%?", start) then
+			_,stop = html:find("?>", start)
+		else
+			_,stop = html:find("%b<>", start)
+		end
+		if not stop then
+			-- error("Could not match html tag " .. html:sub(start,start+30)) 
+		 	table.insert(tokens, {type="text", text=html:sub(start, start)})
+			pos = start + 1
+		else
+			table.insert(tokens, {type="tag", text=html:sub(start, stop)})
+			pos = stop + 1
+		end
+	end
+	return tokens
+end
+
+----------------------------------------------------------------------
+-- Hash
+----------------------------------------------------------------------
+
+-- This is used to "hash" data into alphanumeric strings that are unique
+-- in the document. (Note that this is not cryptographic hash, the hash
+-- function is not one-way.) The hash procedure is used to protect parts
+-- of the document from further processing.
+
+local HASH = {
+	-- Has the hash been inited.
+	inited = false,
+	
+	-- The unique string prepended to all hash values. This is to ensure
+	-- that hash values do not accidently coincide with an actual existing
+	-- string in the document.
+	identifier = "",
+	
+	-- Counter that counts up for each new hash instance.
+	counter = 0,
+	
+	-- Hash table.
+	table = {}
+}
+
+-- Inits hashing. Creates a hash_identifier that doesn't occur anywhere
+-- in the text.
+function init_hash(text)
+	HASH.inited = true
+	HASH.identifier = ""
+	HASH.counter = 0
+	HASH.table = {}
+	
+	local s = "HASH"
+	local counter = 0
+	local id
+	while true do
+		id  = s .. counter
+		if not text:find(id, 1, true) then break end
+		counter = counter + 1
+	end
+	HASH.identifier = id
+end
+
+-- Returns the hashed value for s.
+function hash(s)
+	assert(HASH.inited)
+	if not HASH.table[s] then
+		HASH.counter = HASH.counter + 1
+		local id = HASH.identifier .. HASH.counter .. "X"
+		HASH.table[s] = id
+	end
+	return HASH.table[s]
+end
+
+----------------------------------------------------------------------
+-- Protection
+----------------------------------------------------------------------
+
+-- The protection module is used to "protect" parts of a document
+-- so that they are not modified by subsequent processing steps. 
+-- Protected parts are saved in a table for later unprotection
+
+-- Protection data
+local PD = {
+	-- Saved blocks that have been converted
+	blocks = {},
+
+	-- Block level tags that will be protected
+	tags = {"p", "div", "h1", "h2", "h3", "h4", "h5", "h6", "blockquote",
+	"pre", "table", "dl", "ol", "ul", "script", "noscript", "form", "fieldset",
+	"iframe", "math", "ins", "del"}
+}
+
+-- Pattern for matching a block tag that begins and ends in the leftmost
+-- column and may contain indented subtags, i.e.
+-- 
+-- A nested block. +--
+-- Nested data. +--
+--
+function block_pattern(tag) + return "\n<" .. tag .. ".-\n[ \t]*\n" +end + +-- Pattern for matching a block tag that begins and ends with a newline +function line_pattern(tag) + return "\n<" .. tag .. ".-[ \t]*\n" +end + +-- Protects the range of characters from start to stop in the text and +-- returns the protected string. +function protect_range(text, start, stop) + local s = text:sub(start, stop) + local h = hash(s) + PD.blocks[h] = s + text = text:sub(1,start) .. h .. text:sub(stop) + return text +end + +-- Protect every part of the text that matches any of the patterns. The first +-- matching pattern is protected first, etc. +function protect_matches(text, patterns) + while true do + local start, stop = find_first(text, patterns) + if not start then break end + text = protect_range(text, start, stop) + end + return text +end + +-- Protects blocklevel tags in the specified text +function protect(text) + -- First protect potentially nested block tags + text = protect_matches(text, map(PD.tags, block_pattern)) + -- Then protect block tags at the line level. + text = protect_matches(text, map(PD.tags, line_pattern)) + -- Protect
and comment tags + text = protect_matches(text, {"\n]->[ \t]*\n"}) + text = protect_matches(text, {"\n[ \t]*\n"}) + return text +end + +-- Returns true if the string s is a hash resulting from protection +function is_protected(s) + return PD.blocks[s] +end + +-- Unprotects the specified text by expanding all the nonces +function unprotect(text) + for k,v in pairs(PD.blocks) do + v = v:gsub("%%", "%%%%") + text = text:gsub(k, v) + end + return text +end + + +---------------------------------------------------------------------- +-- Block transform +---------------------------------------------------------------------- + +-- The block transform functions transform the text on the block level. +-- They work with the text as an array of lines rather than as individual +-- characters. + +-- Returns true if the line is a ruler of (char) characters. +-- The line must contain at least three char characters and contain only spaces and +-- char characters. +function is_ruler_of(line, char) + if not line:match("^[ %" .. char .. "]*$") then return false end + if not line:match("%" .. char .. ".*%" .. char .. ".*%" .. char) then return false end + return true +end + +-- Identifies the block level formatting present in the line +function classify(line) + local info = {line = line, text = line} + + if line:match("^ ") then + info.type = "indented" + info.outdented = line:sub(5) + return info + end + + for _,c in ipairs({'*', '-', '_', '='}) do + if is_ruler_of(line, c) then + info.type = "ruler" + info.ruler_char = c + return info + end + end + + if line == "" then + info.type = "blank" + return info + end + + if line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") then + local m1, m2 = line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") + info.type = "header" + info.level = m1:len() + info.text = m2 + return info + end + + if line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") then + local number, text = line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") + info.type = "list_item" + info.list_type = "numeric" + info.number = 0 + number + info.text = text + return info + end + + if line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") then + local bullet, text = line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") + info.type = "list_item" + info.list_type = "bullet" + info.bullet = bullet + info.text= text + return info + end + + if line:match("^>[ \t]?(.*)") then + info.type = "blockquote" + info.text = line:match("^>[ \t]?(.*)") + return info + end + + if is_protected(line) then + info.type = "raw" + info.html = unprotect(line) + return info + end + + info.type = "normal" + return info +end + +-- Find headers constisting of a normal line followed by a ruler and converts them to +-- header entries. +function headers(array) + local i = 1 + while i <= #array - 1 do + if array[i].type == "normal" and array[i+1].type == "ruler" and + (array[i+1].ruler_char == "-" or array[i+1].ruler_char == "=") then + local info = {line = array[i].line} + info.text = info.line + info.type = "header" + info.level = iff(array[i+1].ruler_char == "=", 1, 2) + table.remove(array, i+1) + array[i] = info + end + i = i + 1 + end + return array +end + +-- Find list blocks and convert them to protected data blocks +function lists(array, sublist) + local function process_list(arr) + local function any_blanks(arr) + for i = 1, #arr do + if arr[i].type == "blank" then return true end + end + return false + end + + local function split_list_items(arr) + local acc = {arr[1]} + local res = {} + for i=2,#arr do + if arr[i].type == "list_item" then + table.insert(res, acc) + acc = {arr[i]} + else + table.insert(acc, arr[i]) + end + end + table.insert(res, acc) + return res + end + + local function process_list_item(lines, block) + while lines[#lines].type == "blank" do + table.remove(lines) + end + + local itemtext = lines[1].text + for i=2,#lines do + itemtext = itemtext .. "\n" .. outdent(lines[i].line) + end + if block then + itemtext = block_transform(itemtext, true) + if not itemtext:find("
") then itemtext = indent(itemtext) end
+				return "    
  • " .. itemtext .. "
  • " + else + local lines = split(itemtext) + lines = map(lines, classify) + lines = lists(lines, true) + lines = blocks_to_html(lines, true) + itemtext = table.concat(lines, "\n") + if not itemtext:find("
    ") then itemtext = indent(itemtext) end
    +				return "    
  • " .. itemtext .. "
  • " + end + end + + local block_list = any_blanks(arr) + local items = split_list_items(arr) + local out = "" + for _, item in ipairs(items) do + out = out .. process_list_item(item, block_list) .. "\n" + end + if arr[1].list_type == "numeric" then + return "
      \n" .. out .. "
    " + else + return "
      \n" .. out .. "
    " + end + end + + -- Finds the range of lines composing the first list in the array. A list + -- starts with (^ list_item) or (blank list_item) and ends with + -- (blank* $) or (blank normal). + -- + -- A sublist can start with just (list_item) does not need a blank... + local function find_list(array, sublist) + local function find_list_start(array, sublist) + if array[1].type == "list_item" then return 1 end + if sublist then + for i = 1,#array do + if array[i].type == "list_item" then return i end + end + else + for i = 1, #array-1 do + if array[i].type == "blank" and array[i+1].type == "list_item" then + return i+1 + end + end + end + return nil + end + local function find_list_end(array, start) + local pos = #array + for i = start, #array-1 do + if array[i].type == "blank" and array[i+1].type ~= "list_item" + and array[i+1].type ~= "indented" and array[i+1].type ~= "blank" then + pos = i-1 + break + end + end + while pos > start and array[pos].type == "blank" do + pos = pos - 1 + end + return pos + end + + local start = find_list_start(array, sublist) + if not start then return nil end + return start, find_list_end(array, start) + end + + while true do + local start, stop = find_list(array, sublist) + if not start then break end + local text = process_list(splice(array, start, stop)) + local info = { + line = text, + type = "raw", + html = text + } + array = splice(array, start, stop, {info}) + end + + -- Convert any remaining list items to normal + for _,line in ipairs(array) do + if line.type == "list_item" then line.type = "normal" end + end + + return array +end + +-- Find and convert blockquote markers. +function blockquotes(lines) + local function find_blockquote(lines) + local start + for i,line in ipairs(lines) do + if line.type == "blockquote" then + start = i + break + end + end + if not start then return nil end + + local stop = #lines + for i = start+1, #lines do + if lines[i].type == "blank" or lines[i].type == "blockquote" then + elseif lines[i].type == "normal" then + if lines[i-1].type == "blank" then stop = i-1 break end + else + stop = i-1 break + end + end + while lines[stop].type == "blank" do stop = stop - 1 end + return start, stop + end + + local function process_blockquote(lines) + local raw = lines[1].text + for i = 2,#lines do + raw = raw .. "\n" .. lines[i].text + end + local bt = block_transform(raw) + if not bt:find("
    ") then bt = indent(bt) end
    +		return "
    \n " .. bt .. + "\n
    " + end + + while true do + local start, stop = find_blockquote(lines) + if not start then break end + local text = process_blockquote(splice(lines, start, stop)) + local info = { + line = text, + type = "raw", + html = text + } + lines = splice(lines, start, stop, {info}) + end + return lines +end + +-- Find and convert codeblocks. +function codeblocks(lines) + local function find_codeblock(lines) + local start + for i,line in ipairs(lines) do + if line.type == "indented" then start = i break end + end + if not start then return nil end + + local stop = #lines + for i = start+1, #lines do + if lines[i].type ~= "indented" and lines[i].type ~= "blank" then + stop = i-1 + break + end + end + while lines[stop].type == "blank" do stop = stop - 1 end + return start, stop + end + + local function process_codeblock(lines) + local raw = detab(encode_code(outdent(lines[1].line))) + for i = 2,#lines do + raw = raw .. "\n" .. detab(encode_code(outdent(lines[i].line))) + end + return "
    " .. raw .. "\n
    " + end + + while true do + local start, stop = find_codeblock(lines) + if not start then break end + local text = process_codeblock(splice(lines, start, stop)) + local info = { + line = text, + type = "raw", + html = text + } + lines = splice(lines, start, stop, {info}) + end + return lines +end + +-- Convert lines to html code +function blocks_to_html(lines, no_paragraphs) + local out = {} + local i = 1 + while i <= #lines do + local line = lines[i] + if line.type == "ruler" then + table.insert(out, "
    ") + elseif line.type == "raw" then + table.insert(out, line.html) + elseif line.type == "normal" then + local s = line.line + + while i+1 <= #lines and lines[i+1].type == "normal" do + i = i + 1 + s = s .. "\n" .. lines[i].line + end + + if no_paragraphs then + table.insert(out, span_transform(s)) + else + table.insert(out, "

    " .. span_transform(s) .. "

    ") + end + elseif line.type == "header" then + local s = "" .. span_transform(line.text) .. "" + table.insert(out, s) + else + table.insert(out, line.line) + end + i = i + 1 + end + return out +end + +-- Perform all the block level transforms +function block_transform(text, sublist) + local lines = split(text) + lines = map(lines, classify) + lines = headers(lines) + lines = lists(lines, sublist) + lines = codeblocks(lines) + lines = blockquotes(lines) + lines = blocks_to_html(lines) + local text = table.concat(lines, "\n") + return text +end + +-- Debug function for printing a line array to see the result +-- of partial transforms. +function print_lines(lines) + for i, line in ipairs(lines) do + print(i, line.type, line.text or line.line) + end +end + +---------------------------------------------------------------------- +-- Span transform +---------------------------------------------------------------------- + +-- Functions for transforming the text at the span level. + +-- These characters may need to be escaped because they have a special +-- meaning in markdown. +escape_chars = "'\\`*_{}[]()>#+-.!'" +escape_table = {} + +function init_escape_table() + escape_table = {} + for i = 1,#escape_chars do + local c = escape_chars:sub(i,i) + escape_table[c] = hash(c) + end +end + +-- Adds a new escape to the escape table. +function add_escape(text) + if not escape_table[text] then + escape_table[text] = hash(text) + end + return escape_table[text] +end + +-- Escape characters that should not be disturbed by markdown. +function escape_special_chars(text) + local tokens = tokenize_html(text) + + local out = "" + for _, token in ipairs(tokens) do + local t = token.text + if token.type == "tag" then + -- In tags, encode * and _ so they don't conflict with their use in markdown. + t = t:gsub("%*", escape_table["*"]) + t = t:gsub("%_", escape_table["_"]) + else + t = encode_backslash_escapes(t) + end + out = out .. t + end + return out +end + +-- Encode backspace-escaped characters in the markdown source. +function encode_backslash_escapes(t) + for i=1,escape_chars:len() do + local c = escape_chars:sub(i,i) + t = t:gsub("\\%" .. c, escape_table[c]) + end + return t +end + +-- Unescape characters that have been encoded. +function unescape_special_chars(t) + local tin = t + for k,v in pairs(escape_table) do + k = k:gsub("%%", "%%%%") + t = t:gsub(v,k) + end + if t ~= tin then t = unescape_special_chars(t) end + return t +end + +-- Encode/escape certain characters inside Markdown code runs. +-- The point is that in code, these characters are literals, +-- and lose their special Markdown meanings. +function encode_code(s) + s = s:gsub("%&", "&") + s = s:gsub("<", "<") + s = s:gsub(">", ">") + for k,v in pairs(escape_table) do + s = s:gsub("%"..k, v) + end + return s +end + +-- Handle backtick blocks. +function code_spans(s) + s = s:gsub("\\\\", escape_table["\\"]) + s = s:gsub("\\`", escape_table["`"]) + + local pos = 1 + while true do + local start, stop = s:find("`+", pos) + if not start then return s end + local count = stop - start + 1 + -- Find a matching numbert of backticks + local estart, estop = s:find(string.rep("`", count), stop+1) + local brstart = s:find("\n", stop+1) + if estart and (not brstart or estart < brstart) then + local code = s:sub(stop+1, estart-1) + code = code:gsub("^[ \t]+", "") + code = code:gsub("[ \t]+$", "") + code = code:gsub(escape_table["\\"], escape_table["\\"] .. escape_table["\\"]) + code = code:gsub(escape_table["`"], escape_table["\\"] .. escape_table["`"]) + code = "" .. encode_code(code) .. "" + code = add_escape(code) + s = s:sub(1, start-1) .. code .. s:sub(estop+1) + pos = start + code:len() + else + pos = stop + 1 + end + end + return s +end + +-- Encode alt text... enodes &, and ". +function encode_alt(s) + if not s then return s end + s = s:gsub('&', '&') + s = s:gsub('"', '"') + s = s:gsub('<', '<') + return s +end + +-- Handle image references +function images(text) + local function reference_link(alt, id) + alt = encode_alt(alt:match("%b[]"):sub(2,-2)) + id = id:match("%[(.*)%]"):lower() + if id == "" then id = text:lower() end + link_database[id] = link_database[id] or {} + if not link_database[id].url then return nil end + local url = link_database[id].url or id + url = encode_alt(url) + local title = encode_alt(link_database[id].title) + if title then title = " title=\"" .. title .. "\"" else title = "" end + return add_escape ('' .. alt .. '") + end + + local function inline_link(alt, link) + alt = encode_alt(alt:match("%b[]"):sub(2,-2)) + local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") + url = url or link:match("%(?%)") + url = encode_alt(url) + title = encode_alt(title) + if title then + return add_escape('' .. alt .. '') + else + return add_escape('' .. alt .. '') + end + end + + text = text:gsub("!(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) + text = text:gsub("!(%b[])(%b())", inline_link) + return text +end + +-- Handle anchor references +function anchors(text) + local function reference_link(text, id) + text = text:match("%b[]"):sub(2,-2) + id = id:match("%b[]"):sub(2,-2):lower() + if id == "" then id = text:lower() end + link_database[id] = link_database[id] or {} + if not link_database[id].url then return nil end + local url = link_database[id].url or id + url = encode_alt(url) + local title = encode_alt(link_database[id].title) + if title then title = " title=\"" .. title .. "\"" else title = "" end + return add_escape("") .. text .. add_escape("") + end + + local function inline_link(text, link) + text = text:match("%b[]"):sub(2,-2) + local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") + title = encode_alt(title) + url = url or link:match("%(?%)") or "" + url = encode_alt(url) + if title then + return add_escape("") .. text .. "" + else + return add_escape("") .. text .. add_escape("") + end + end + + text = text:gsub("(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) + text = text:gsub("(%b[])(%b())", inline_link) + return text +end + +-- Handle auto links, i.e. . +function auto_links(text) + local function link(s) + return add_escape("") .. s .. "" + end + -- Encode chars as a mix of dec and hex entitites to (perhaps) fool + -- spambots. + local function encode_email_address(s) + -- Use a deterministic encoding to make unit testing possible. + -- Code 45% hex, 45% dec, 10% plain. + local hex = {code = function(c) return "&#x" .. string.format("%x", c:byte()) .. ";" end, count = 1, rate = 0.45} + local dec = {code = function(c) return "&#" .. c:byte() .. ";" end, count = 0, rate = 0.45} + local plain = {code = function(c) return c end, count = 0, rate = 0.1} + local codes = {hex, dec, plain} + local function swap(t,k1,k2) local temp = t[k2] t[k2] = t[k1] t[k1] = temp end + + local out = "" + for i = 1,s:len() do + for _,code in ipairs(codes) do code.count = code.count + code.rate end + if codes[1].count < codes[2].count then swap(codes,1,2) end + if codes[2].count < codes[3].count then swap(codes,2,3) end + if codes[1].count < codes[2].count then swap(codes,1,2) end + + local code = codes[1] + local c = s:sub(i,i) + -- Force encoding of "@" to make email address more invisible. + if c == "@" and code == plain then code = codes[2] end + out = out .. code.code(c) + code.count = code.count - 1 + end + return out + end + local function mail(s) + s = unescape_special_chars(s) + local address = encode_email_address("mailto:" .. s) + local text = encode_email_address(s) + return add_escape("") .. text .. "" + end + -- links + text = text:gsub("<(https?:[^'\">%s]+)>", link) + text = text:gsub("<(ftp:[^'\">%s]+)>", link) + + -- mail + text = text:gsub("%s]+)>", mail) + text = text:gsub("<([-.%w]+%@[-.%w]+)>", mail) + return text +end + +-- Encode free standing amps (&) and angles (<)... note that this does not +-- encode free >. +function amps_and_angles(s) + -- encode amps not part of &..; expression + local pos = 1 + while true do + local amp = s:find("&", pos) + if not amp then break end + local semi = s:find(";", amp+1) + local stop = s:find("[ \t\n&]", amp+1) + if not semi or (stop and stop < semi) or (semi - amp) > 15 then + s = s:sub(1,amp-1) .. "&" .. s:sub(amp+1) + pos = amp+1 + else + pos = amp+1 + end + end + + -- encode naked <'s + s = s:gsub("<([^a-zA-Z/?$!])", "<%1") + s = s:gsub("<$", "<") + + -- what about >, nothing done in the original markdown source to handle them + return s +end + +-- Handles emphasis markers (* and _) in the text. +function emphasis(text) + for _, s in ipairs {"%*%*", "%_%_"} do + text = text:gsub(s .. "([^%s][%*%_]?)" .. s, "%1") + text = text:gsub(s .. "([^%s][^<>]-[^%s][%*%_]?)" .. s, "%1") + end + for _, s in ipairs {"%*", "%_"} do + text = text:gsub(s .. "([^%s_])" .. s, "%1") + text = text:gsub(s .. "([^%s_])" .. s, "%1") + text = text:gsub(s .. "([^%s_][^<>_]-[^%s_])" .. s, "%1") + text = text:gsub(s .. "([^<>_]-[^<>_]-[^<>_]-)" .. s, "%1") + end + return text +end + +-- Handles line break markers in the text. +function line_breaks(text) + return text:gsub(" +\n", "
    \n") +end + +-- Perform all span level transforms. +function span_transform(text) + text = code_spans(text) + text = escape_special_chars(text) + text = images(text) + text = anchors(text) + text = auto_links(text) + text = amps_and_angles(text) + text = emphasis(text) + text = line_breaks(text) + return text +end + +---------------------------------------------------------------------- +-- Markdown +---------------------------------------------------------------------- + +-- Cleanup the text by normalizing some possible variations to make further +-- processing easier. +function cleanup(text) + -- Standardize line endings + text = text:gsub("\r\n", "\n") -- DOS to UNIX + text = text:gsub("\r", "\n") -- Mac to UNIX + + -- Convert all tabs to spaces + text = detab(text) + + -- Strip lines with only spaces and tabs + while true do + local subs + text, subs = text:gsub("\n[ \t]+\n", "\n\n") + if subs == 0 then break end + end + + return "\n" .. text .. "\n" +end + +-- Strips link definitions from the text and stores the data in a lookup table. +function strip_link_definitions(text) + local linkdb = {} + + local function link_def(id, url, title) + id = id:match("%[(.+)%]"):lower() + linkdb[id] = linkdb[id] or {} + linkdb[id].url = url or linkdb[id].url + linkdb[id].title = title or linkdb[id].title + return "" + end + + local def_no_title = "\n ? ? ?(%b[]):[ \t]*\n?[ \t]*]+)>?[ \t]*" + local def_title1 = def_no_title .. "[ \t]+\n?[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" + local def_title2 = def_no_title .. "[ \t]*\n[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" + local def_title3 = def_no_title .. "[ \t]*\n?[ \t]+[\"'(]([^\n]+)[\"')][ \t]*" + + text = text:gsub(def_title1, link_def) + text = text:gsub(def_title2, link_def) + text = text:gsub(def_title3, link_def) + text = text:gsub(def_no_title, link_def) + return text, linkdb +end + +link_database = {} + +-- Main markdown processing function +function markdown(text) + init_hash(text) + init_escape_table() + + text = cleanup(text) + text = protect(text) + text, link_database = strip_link_definitions(text) + text = block_transform(text) + text = unescape_special_chars(text) + return text +end + +---------------------------------------------------------------------- +-- End of module +---------------------------------------------------------------------- + +setfenv(1, _G) +M.lock(M) + +-- Expose markdown function to the world +markdown = M.markdown + +-- Class for parsing command-line options +local OptionParser = {} +OptionParser.__index = OptionParser + +-- Creates a new option parser +function OptionParser:new() + local o = {short = {}, long = {}} + setmetatable(o, self) + return o +end + +-- Calls f() whenever a flag with specified short and long name is encountered +function OptionParser:flag(short, long, f) + local info = {type = "flag", f = f} + if short then self.short[short] = info end + if long then self.long[long] = info end +end + +-- Calls f(param) whenever a parameter flag with specified short and long name is encountered +function OptionParser:param(short, long, f) + local info = {type = "param", f = f} + if short then self.short[short] = info end + if long then self.long[long] = info end +end + +-- Calls f(v) for each non-flag argument +function OptionParser:arg(f) + self.arg = f +end + +-- Runs the option parser for the specified set of arguments. Returns true if all arguments +-- where successfully parsed and false otherwise. +function OptionParser:run(args) + local pos = 1 + while pos <= #args do + local arg = args[pos] + if arg == "--" then + for i=pos+1,#args do + if self.arg then self.arg(args[i]) end + return true + end + end + if arg:match("^%-%-") then + local info = self.long[arg:sub(3)] + if not info then print("Unknown flag: " .. arg) return false end + if info.type == "flag" then + info.f() + pos = pos + 1 + else + param = args[pos+1] + if not param then print("No parameter for flag: " .. arg) return false end + info.f(param) + pos = pos+2 + end + elseif arg:match("^%-") then + for i=2,arg:len() do + local c = arg:sub(i,i) + local info = self.short[c] + if not info then print("Unknown flag: -" .. c) return false end + if info.type == "flag" then + info.f() + else + if i == arg:len() then + param = args[pos+1] + if not param then print("No parameter for flag: -" .. c) return false end + info.f(param) + pos = pos + 1 + else + param = arg:sub(i+1) + info.f(param) + end + break + end + end + pos = pos + 1 + else + if self.arg then self.arg(arg) end + pos = pos + 1 + end + end + return true +end + +-- Handles the case when markdown is run from the command line +local function run_command_line(arg) + -- Generate output for input s given options + local function run(s, options) + s = markdown(s) + if not options.wrap_header then return s end + local header = "" + if options.header then + local f = io.open(options.header) or error("Could not open file: " .. options.header) + header = f:read("*a") + f:close() + else + header = [[ + + + + + TITLE + + + +]] + local title = options.title or s:match("

    (.-)

    ") or s:match("

    (.-)

    ") or + s:match("

    (.-)

    ") or "Untitled" + header = header:gsub("TITLE", title) + if options.inline_style then + local style = "" + local f = io.open(options.stylesheet) + if f then + style = f:read("*a") f:close() + else + error("Could not include style sheet " .. options.stylesheet .. ": File not found") + end + header = header:gsub('', + "") + else + header = header:gsub("STYLESHEET", options.stylesheet) + end + header = header:gsub("CHARSET", options.charset) + end + local footer = "" + if options.footer then + local f = io.open(options.footer) or error("Could not open file: " .. options.footer) + footer = f:read("*a") + f:close() + end + return header .. s .. footer + end + + -- Generate output path name from input path name given options. + local function outpath(path, options) + if options.append then return path .. ".html" end + local m = path:match("^(.+%.html)[^/\\]+$") if m then return m end + m = path:match("^(.+%.)[^/\\]*$") if m and path ~= m .. "html" then return m .. "html" end + return path .. ".html" + end + + -- Default commandline options + local options = { + wrap_header = true, + header = nil, + footer = nil, + charset = "utf-8", + title = nil, + stylesheet = "default.css", + inline_style = false + } + local help = [[ +Usage: markdown.lua [OPTION] [FILE] +Runs the markdown text markup to HTML converter on each file specified on the +command line. If no files are specified, runs on standard input. + +No header: + -n, --no-wrap Don't wrap the output in ... tags. +Custom header: + -e, --header FILE Use content of FILE for header. + -f, --footer FILE Use content of FILE for footer. +Generated header: + -c, --charset SET Specifies charset (default utf-8). + -i, --title TITLE Specifies title (default from first

    tag). + -s, --style STYLE Specifies style sheet file (default default.css). + -l, --inline-style Include the style sheet file inline in the header. +Generated files: + -a, --append Append .html extension (instead of replacing). +Other options: + -h, --help Print this help text. + -t, --test Run the unit tests. +]] + + local run_stdin = true + local op = OptionParser:new() + op:flag("n", "no-wrap", function () options.wrap_header = false end) + op:param("e", "header", function (x) options.header = x end) + op:param("f", "footer", function (x) options.footer = x end) + op:param("c", "charset", function (x) options.charset = x end) + op:param("i", "title", function(x) options.title = x end) + op:param("s", "style", function(x) options.stylesheet = x end) + op:flag("l", "inline-style", function(x) options.inline_style = true end) + op:flag("a", "append", function() options.append = true end) + op:flag("t", "test", function() + local n = arg[0]:gsub("markdown.lua", "markdown-tests.lua") + local f = io.open(n) + if f then + f:close() dofile(n) + else + error("Cannot find markdown-tests.lua") + end + run_stdin = false + end) + op:flag("h", "help", function() print(help) run_stdin = false end) + op:arg(function(path) + local file = io.open(path) or error("Could not open file: " .. path) + local s = file:read("*a") + file:close() + s = run(s, options) + file = io.open(outpath(path, options), "w") or error("Could not open output file: " .. outpath(path, options)) + file:write(s) + file:close() + run_stdin = false + end + ) + + if not op:run(arg) then + print(help) + run_stdin = false + end + + if run_stdin then + local s = io.read("*a") + s = run(s, options) + io.write(s) + end +end + +-- If we are being run from the command-line, act accordingly +if arg and arg[0]:find("markdown%.lua$") then + run_command_line(arg) +else + return markdown +end \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler.lua new file mode 100644 index 000000000..202b254fd --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler.lua @@ -0,0 +1,181 @@ +--------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- +-- Convert between various code representation formats. Atomic +-- converters are written in extenso, others are composed automatically +-- by chaining the atomic ones together in a closure. +-- +-- Supported formats are: +-- +-- * srcfile: the name of a file containing sources. +-- * src: these sources as a single string. +-- * lexstream: a stream of lexemes. +-- * ast: an abstract syntax tree. +-- * proto: a (Yueliang) struture containing a high level +-- representation of bytecode. Largely based on the +-- Proto structure in Lua's VM +-- * bytecode: a string dump of the function, as taken by +-- loadstring() and produced by string.dump(). +-- * function: an executable lua function in RAM. +-- +-------------------------------------------------------------------------------- + +local checks = require 'checks' + +local M = { } + +-------------------------------------------------------------------------------- +-- Order of the transformations. if 'a' is on the left of 'b', then a 'a' can +-- be transformed into a 'b' (but not the other way around). +-- M.sequence goes for numbers to format names, M.order goes from format +-- names to numbers. +-------------------------------------------------------------------------------- +M.sequence = { + 'srcfile', 'src', 'lexstream', 'ast', 'proto', 'bytecode', 'function' } + +local arg_types = { + srcfile = { 'string', '?string' }, + src = { 'string', '?string' }, + lexstream = { 'lexer.stream', '?string' }, + ast = { 'table', '?string' }, + proto = { 'table', '?string' }, + bytecode = { 'string', '?string' }, +} + +if false then + -- if defined, runs on every newly-generated AST + function M.check_ast(ast) + local function rec(x, n, parent) + if not x.lineinfo and parent.lineinfo then + local pp = require 'metalua.pprint' + pp.printf("WARNING: Missing lineinfo in child #%s `%s{...} of node at %s", + n, x.tag or '', tostring(parent.lineinfo)) + end + for i, child in ipairs(x) do + if type(child)=='table' then rec(child, i, x) end + end + end + rec(ast, -1, { }) + end +end + + +M.order= { }; for a,b in pairs(M.sequence) do M.order[b]=a end + +local CONV = { } -- conversion metatable __index + +function CONV :srcfile_to_src(x, name) + checks('metalua.compiler', 'string', '?string') + name = name or '@'..x + local f, msg = io.open (x, 'rb') + if not f then error(msg) end + local r, msg = f :read '*a' + if not r then error("Cannot read file '"..x.."': "..msg) end + f :close() + return r, name +end + +function CONV :src_to_lexstream(src, name) + checks('metalua.compiler', 'string', '?string') + local r = self.parser.lexer :newstream (src, name) + return r, name +end + +function CONV :lexstream_to_ast(lx, name) + checks('metalua.compiler', 'lexer.stream', '?string') + local r = self.parser.chunk(lx) + r.source = name + if M.check_ast then M.check_ast (r) end + return r, name +end + +local bytecode_compiler = nil -- cache to avoid repeated `pcall(require(...))` +local function get_bytecode_compiler() + if bytecode_compiler then return bytecode_compiler else + local status, result = pcall(require, 'metalua.compiler.bytecode') + if status then + bytecode_compiler = result + return result + elseif string.match(result, "not found") then + error "Compilation only available with full Metalua" + else error (result) end + end +end + +function CONV :ast_to_proto(ast, name) + checks('metalua.compiler', 'table', '?string') + return get_bytecode_compiler().ast_to_proto(ast, name), name +end + +function CONV :proto_to_bytecode(proto, name) + return get_bytecode_compiler().proto_to_bytecode(proto), name +end + +function CONV :bytecode_to_function(bc, name) + checks('metalua.compiler', 'string', '?string') + return loadstring(bc, name) +end + +-- Create all sensible combinations +for i=1,#M.sequence do + local src = M.sequence[i] + for j=i+2, #M.sequence do + local dst = M.sequence[j] + local dst_name = src.."_to_"..dst + local my_arg_types = arg_types[src] + local functions = { } + for k=i, j-1 do + local name = M.sequence[k].."_to_"..M.sequence[k+1] + local f = assert(CONV[name], name) + table.insert (functions, f) + end + CONV[dst_name] = function(self, a, b) + checks('metalua.compiler', unpack(my_arg_types)) + for _, f in ipairs(functions) do + a, b = f(self, a, b) + end + return a, b + end + --printf("Created M.%s out of %s", dst_name, table.concat(n, ', ')) + end +end + + +-------------------------------------------------------------------------------- +-- This one goes in the "wrong" direction, cannot be composed. +-------------------------------------------------------------------------------- +function CONV :function_to_bytecode(...) return string.dump(...) end + +function CONV :ast_to_src(...) + require 'metalua.loader' -- ast_to_string isn't written in plain lua + return require 'metalua.compiler.ast_to_src' (...) +end + +local MT = { __index=CONV, __type='metalua.compiler' } + +function M.new() + local parser = require 'metalua.compiler.parser' .new() + local self = { parser = parser } + setmetatable(self, MT) + return self +end + +return M \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/ast_to_src.mlua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/ast_to_src.mlua new file mode 100644 index 000000000..ca80a12d2 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/ast_to_src.mlua @@ -0,0 +1,682 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +------------------------------------------------------------------------------- + +-{ extension ('match', ...) } + +local M = { } +M.__index = M +M.__call = |self, ...| self:run(...) + +local pp=require 'metalua.pprint' + +-------------------------------------------------------------------------------- +-- Instanciate a new AST->source synthetizer +-------------------------------------------------------------------------------- +function M.new () + local self = { + _acc = { }, -- Accumulates pieces of source as strings + current_indent = 0, -- Current level of line indentation + indent_step = " " -- Indentation symbol, normally spaces or '\t' + } + return setmetatable (self, M) +end + +-------------------------------------------------------------------------------- +-- Run a synthetizer on the `ast' arg and return the source as a string. +-- Can also be used as a static method `M.run (ast)'; in this case, +-- a temporary Metizer is instanciated on the fly. +-------------------------------------------------------------------------------- +function M:run (ast) + if not ast then + self, ast = M.new(), self + end + self._acc = { } + self:node (ast) + return table.concat (self._acc) +end + +-------------------------------------------------------------------------------- +-- Accumulate a piece of source file in the synthetizer. +-------------------------------------------------------------------------------- +function M:acc (x) + if x then table.insert (self._acc, x) end +end + +-------------------------------------------------------------------------------- +-- Accumulate an indented newline. +-- Jumps an extra line if indentation is 0, so that +-- toplevel definitions are separated by an extra empty line. +-------------------------------------------------------------------------------- +function M:nl () + if self.current_indent == 0 then self:acc "\n" end + self:acc ("\n" .. self.indent_step:rep (self.current_indent)) +end + +-------------------------------------------------------------------------------- +-- Increase indentation and accumulate a new line. +-------------------------------------------------------------------------------- +function M:nlindent () + self.current_indent = self.current_indent + 1 + self:nl () +end + +-------------------------------------------------------------------------------- +-- Decrease indentation and accumulate a new line. +-------------------------------------------------------------------------------- +function M:nldedent () + self.current_indent = self.current_indent - 1 + self:acc ("\n" .. self.indent_step:rep (self.current_indent)) +end + +-------------------------------------------------------------------------------- +-- Keywords, which are illegal as identifiers. +-------------------------------------------------------------------------------- +local keywords_list = { + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "if", + "in", "local", "nil", "not", "or", + "repeat", "return", "then", "true", "until", + "while" } +local keywords = { } +for _, kw in pairs(keywords_list) do keywords[kw]=true end + +-------------------------------------------------------------------------------- +-- Return true iff string `id' is a legal identifier name. +-------------------------------------------------------------------------------- +local function is_ident (id) + return string['match'](id, "^[%a_][%w_]*$") and not keywords[id] +end + +-------------------------------------------------------------------------------- +-- Return true iff ast represents a legal function name for +-- syntax sugar ``function foo.bar.gnat() ... end'': +-- a series of nested string indexes, with an identifier as +-- the innermost node. +-------------------------------------------------------------------------------- +local function is_idx_stack (ast) + match ast with + | `Id{ _ } -> return true + | `Index{ left, `String{ _ } } -> return is_idx_stack (left) + | _ -> return false + end +end + +-------------------------------------------------------------------------------- +-- Operator precedences, in increasing order. +-- This is not directly used, it's used to generate op_prec below. +-------------------------------------------------------------------------------- +local op_preprec = { + { "or", "and" }, + { "lt", "le", "eq", "ne" }, + { "concat" }, + { "add", "sub" }, + { "mul", "div", "mod" }, + { "unary", "not", "len" }, + { "pow" }, + { "index" } } + +-------------------------------------------------------------------------------- +-- operator --> precedence table, generated from op_preprec. +-------------------------------------------------------------------------------- +local op_prec = { } + +for prec, ops in ipairs (op_preprec) do + for _, op in ipairs (ops) do + op_prec[op] = prec + end +end + +-------------------------------------------------------------------------------- +-- operator --> source representation. +-------------------------------------------------------------------------------- +local op_symbol = { + add = " + ", sub = " - ", mul = " * ", + div = " / ", mod = " % ", pow = " ^ ", + concat = " .. ", eq = " == ", ne = " ~= ", + lt = " < ", le = " <= ", ["and"] = " and ", + ["or"] = " or ", ["not"] = "not ", len = "# " } + +-------------------------------------------------------------------------------- +-- Accumulate the source representation of AST `node' in +-- the synthetizer. Most of the work is done by delegating to +-- the method having the name of the AST tag. +-- If something can't be converted to normal sources, it's +-- instead dumped as a `-{ ... }' splice in the source accumulator. +-------------------------------------------------------------------------------- +function M:node (node) + assert (self~=M and self._acc) + if node==nil then self:acc'<>' + elseif not self.custom_printer or not self.custom_printer (self, node) then + if not node.tag then -- tagless (henceunindented) block. + self:list (node, self.nl) + else + local f = M[node.tag] + if type (f) == "function" then -- Delegate to tag method. + f (self, node, unpack (node)) + elseif type (f) == "string" then -- tag string. + self:acc (f) + else -- No appropriate method, fall back to splice dumping. + -- This cannot happen in a plain Lua AST. + self:acc " -{ " + self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1}), 80) + self:acc " }" + end + end + end +end + +function M:block(body) + if not self.custom_printer or not self.custom_printer (self, body) then + self:nlindent () + self:list (body, self.nl) + self:nldedent () + end +end + +-------------------------------------------------------------------------------- +-- Convert every node in the AST list `list' passed as 1st arg. +-- `sep' is an optional separator to be accumulated between each list element, +-- it can be a string or a synth method. +-- `start' is an optional number (default == 1), indicating which is the +-- first element of list to be converted, so that we can skip the begining +-- of a list. +-------------------------------------------------------------------------------- +function M:list (list, sep, start) + for i = start or 1, # list do + self:node (list[i]) + if list[i + 1] then + if not sep then + elseif type (sep) == "function" then sep (self) + elseif type (sep) == "string" then self:acc (sep) + else error "Invalid list separator" end + end + end +end + +-------------------------------------------------------------------------------- +-- +-- Tag methods. +-- ------------ +-- +-- Specific AST node dumping methods, associated to their node kinds +-- by their name, which is the corresponding AST tag. +-- synth:node() is in charge of delegating a node's treatment to the +-- appropriate tag method. +-- +-- Such tag methods are called with the AST node as 1st arg. +-- As a convenience, the n node's children are passed as args #2 ... n+1. +-- +-- There are several things that could be refactored into common subroutines +-- here: statement blocks dumping, function dumping... +-- However, given their small size and linear execution +-- (they basically perform series of :acc(), :node(), :list(), +-- :nl(), :nlindent() and :nldedent() calls), it seems more readable +-- to avoid multiplication of such tiny functions. +-- +-- To make sense out of these, you need to know metalua's AST syntax, as +-- found in the reference manual or in metalua/doc/ast.txt. +-- +-------------------------------------------------------------------------------- + +function M:Do (node) + self:acc "do" + self:block (node) + self:acc "end" +end + +function M:Set (node) + match node with + | `Set{ { `Index{ lhs, `String{ method } } }, + { `Function{ { `Id "self", ... } == params, body } } } + if is_idx_stack (lhs) and is_ident (method) -> + -- ``function foo:bar(...) ... end'' -- + self:acc "function " + self:node (lhs) + self:acc ":" + self:acc (method) + self:acc " (" + self:list (params, ", ", 2) + self:acc ")" + self:block (body) + self:acc "end" + + | `Set{ { lhs }, { `Function{ params, body } } } if is_idx_stack (lhs) -> + -- ``function foo(...) ... end'' -- + self:acc "function " + self:node (lhs) + self:acc " (" + self:list (params, ", ") + self:acc ")" + self:block (body) + self:acc "end" + + | `Set{ { `Id{ lhs1name } == lhs1, ... } == lhs, rhs } + if not is_ident (lhs1name) -> + -- ``foo, ... = ...'' when foo is *not* a valid identifier. + -- In that case, the spliced 1st variable must get parentheses, + -- to be distinguished from a statement splice. + -- This cannot happen in a plain Lua AST. + self:acc "(" + self:node (lhs1) + self:acc ")" + if lhs[2] then -- more than one lhs variable + self:acc ", " + self:list (lhs, ", ", 2) + end + self:acc " = " + self:list (rhs, ", ") + + | `Set{ lhs, rhs } -> + -- ``... = ...'', no syntax sugar -- + self:list (lhs, ", ") + self:acc " = " + self:list (rhs, ", ") + | `Set{ lhs, rhs, annot } -> + -- ``... = ...'', no syntax sugar, annotation -- + local n = #lhs + for i=1,n do + local ell, a = lhs[i], annot[i] + self:node (ell) + if a then + self:acc ' #' + self:node(a) + end + if i~=n then self:acc ', ' end + end + self:acc " = " + self:list (rhs, ", ") + end +end + +function M:While (node, cond, body) + self:acc "while " + self:node (cond) + self:acc " do" + self:block (body) + self:acc "end" +end + +function M:Repeat (node, body, cond) + self:acc "repeat" + self:block (body) + self:acc "until " + self:node (cond) +end + +function M:If (node) + for i = 1, #node-1, 2 do + -- for each ``if/then'' and ``elseif/then'' pair -- + local cond, body = node[i], node[i+1] + self:acc (i==1 and "if " or "elseif ") + self:node (cond) + self:acc " then" + self:block (body) + end + -- odd number of children --> last one is an `else' clause -- + if #node%2 == 1 then + self:acc "else" + self:block (node[#node]) + end + self:acc "end" +end + +function M:Fornum (node, var, first, last) + local body = node[#node] + self:acc "for " + self:node (var) + self:acc " = " + self:node (first) + self:acc ", " + self:node (last) + if #node==5 then -- 5 children --> child #4 is a step increment. + self:acc ", " + self:node (node[4]) + end + self:acc " do" + self:block (body) + self:acc "end" +end + +function M:Forin (node, vars, generators, body) + self:acc "for " + self:list (vars, ", ") + self:acc " in " + self:list (generators, ", ") + self:acc " do" + self:block (body) + self:acc "end" +end + +function M:Local (node, lhs, rhs, annots) + if next (lhs) then + self:acc "local " + if annots then + local n = #lhs + for i=1, n do + self:node (lhs) + local a = annots[i] + if a then + self:acc ' #' + self:node (a) + end + if i~=n then self:acc ', ' end + end + else + self:list (lhs, ", ") + end + if rhs[1] then + self:acc " = " + self:list (rhs, ", ") + end + else -- Can't create a local statement with 0 variables in plain Lua + self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1, fix_indent=2})) + end +end + +function M:Localrec (node, lhs, rhs) + match node with + | `Localrec{ { `Id{name} }, { `Function{ params, body } } } + if is_ident (name) -> + -- ``local function name() ... end'' -- + self:acc "local function " + self:acc (name) + self:acc " (" + self:list (params, ", ") + self:acc ")" + self:block (body) + self:acc "end" + + | _ -> + -- Other localrec are unprintable ==> splice them -- + -- This cannot happen in a plain Lua AST. -- + self:acc "-{ " + self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1, fix_indent=2})) + self:acc " }" + end +end + +function M:Call (node, f) + -- single string or table literal arg ==> no need for parentheses. -- + local parens + match node with + | `Call{ _, `String{_} } + | `Call{ _, `Table{...}} -> parens = false + | _ -> parens = true + end + self:node (f) + self:acc (parens and " (" or " ") + self:list (node, ", ", 2) -- skip `f'. + self:acc (parens and ")") +end + +function M:Invoke (node, f, method) + -- single string or table literal arg ==> no need for parentheses. -- + local parens + match node with + | `Invoke{ _, _, `String{_} } + | `Invoke{ _, _, `Table{...}} -> parens = false + | _ -> parens = true + end + self:node (f) + self:acc ":" + self:acc (method[1]) + self:acc (parens and " (" or " ") + self:list (node, ", ", 3) -- Skip args #1 and #2, object and method name. + self:acc (parens and ")") +end + +function M:Return (node) + self:acc "return " + self:list (node, ", ") +end + +M.Break = "break" +M.Nil = "nil" +M.False = "false" +M.True = "true" +M.Dots = "..." + +function M:Number (node, n) + self:acc (tostring (n)) +end + +function M:String (node, str) + -- format "%q" prints '\n' in an umpractical way IMO, + -- so this is fixed with the :gsub( ) call. + self:acc (string.format ("%q", str):gsub ("\\\n", "\\n")) +end + +function M:Function (node, params, body, annots) + self:acc "function (" + if annots then + local n = #params + for i=1,n do + local p, a = params[i], annots[i] + self:node(p) + if annots then + self:acc " #" + self:node(a) + end + if i~=n then self:acc ', ' end + end + else + self:list (params, ", ") + end + self:acc ")" + self:block (body) + self:acc "end" +end + +function M:Table (node) + if not node[1] then self:acc "{ }" else + self:acc "{" + if #node > 1 then self:nlindent () else self:acc " " end + for i, elem in ipairs (node) do + match elem with + | `Pair{ `String{ key }, value } if is_ident (key) -> + -- ``key = value''. -- + self:acc (key) + self:acc " = " + self:node (value) + + | `Pair{ key, value } -> + -- ``[key] = value''. -- + self:acc "[" + self:node (key) + self:acc "] = " + self:node (value) + + | _ -> + -- ``value''. -- + self:node (elem) + end + if node [i+1] then + self:acc "," + self:nl () + end + end + if #node > 1 then self:nldedent () else self:acc " " end + self:acc "}" + end +end + +function M:Op (node, op, a, b) + -- Transform ``not (a == b)'' into ``a ~= b''. -- + match node with + | `Op{ "not", `Op{ "eq", _a, _b } } + | `Op{ "not", `Paren{ `Op{ "eq", _a, _b } } } -> + op, a, b = "ne", _a, _b + | _ -> + end + + if b then -- binary operator. + local left_paren, right_paren + match a with + | `Op{ op_a, ...} if op_prec[op] >= op_prec[op_a] -> left_paren = true + | _ -> left_paren = false + end + + match b with -- FIXME: might not work with right assoc operators ^ and .. + | `Op{ op_b, ...} if op_prec[op] >= op_prec[op_b] -> right_paren = true + | _ -> right_paren = false + end + + self:acc (left_paren and "(") + self:node (a) + self:acc (left_paren and ")") + + self:acc (op_symbol [op]) + + self:acc (right_paren and "(") + self:node (b) + self:acc (right_paren and ")") + + else -- unary operator. + local paren + match a with + | `Op{ op_a, ... } if op_prec[op] >= op_prec[op_a] -> paren = true + | _ -> paren = false + end + self:acc (op_symbol[op]) + self:acc (paren and "(") + self:node (a) + self:acc (paren and ")") + end +end + +function M:Paren (node, content) + self:acc "(" + self:node (content) + self:acc ")" +end + +function M:Index (node, table, key) + local paren_table + -- Check precedence, see if parens are needed around the table -- + match table with + | `Op{ op, ... } if op_prec[op] < op_prec.index -> paren_table = true + | _ -> paren_table = false + end + + self:acc (paren_table and "(") + self:node (table) + self:acc (paren_table and ")") + + match key with + | `String{ field } if is_ident (field) -> + -- ``table.key''. -- + self:acc "." + self:acc (field) + | _ -> + -- ``table [key]''. -- + self:acc "[" + self:node (key) + self:acc "]" + end +end + +function M:Id (node, name) + if is_ident (name) then + self:acc (name) + else -- Unprintable identifier, fall back to splice representation. + -- This cannot happen in a plain Lua AST. + self:acc "-{`Id " + self:String (node, name) + self:acc "}" + end +end + + +M.TDyn = '*' +M.TDynbar = '**' +M.TPass = 'pass' +M.TField = 'field' +M.TIdbar = M.TId +M.TReturn = M.Return + + +function M:TId (node, name) self:acc(name) end + + +function M:TCatbar(node, te, tebar) + self:acc'(' + self:node(te) + self:acc'|' + self:tebar(tebar) + self:acc')' +end + +function M:TFunction(node, p, r) + self:tebar(p) + self:acc '->' + self:tebar(r) +end + +function M:TTable (node, default, pairs) + self:acc '[' + self:list (pairs, ', ') + if default.tag~='TField' then + self:acc '|' + self:node (default) + end + self:acc ']' +end + +function M:TPair (node, k, v) + self:node (k) + self:acc '=' + self:node (v) +end + +function M:TIdbar (node, name) + self :acc (name) +end + +function M:TCatbar (node, a, b) + self:node(a) + self:acc ' ++ ' + self:node(b) +end + +function M:tebar(node) + if node.tag then self:node(node) else + self:acc '(' + self:list(node, ', ') + self:acc ')' + end +end + +function M:TUnkbar(node, name) + self:acc '~~' + self:acc (name) +end + +function M:TUnk(node, name) + self:acc '~' + self:acc (name) +end + +for name, tag in pairs{ const='TConst', var='TVar', currently='TCurrently', just='TJust' } do + M[tag] = function(self, node, te) + self:acc (name..' ') + self:node(te) + end +end + +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode.lua new file mode 100644 index 000000000..b3afbdb73 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode.lua @@ -0,0 +1,29 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +-------------------------------------------------------------------------------- + +local compile = require 'metalua.compiler.bytecode.compile' +local ldump = require 'metalua.compiler.bytecode.ldump' + +local M = { } + +M.ast_to_proto = compile.ast_to_proto +M.proto_to_bytecode = ldump.dump_string +M.proto_to_file = ldump.dump_file + +return M \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/compile.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/compile.lua new file mode 100644 index 000000000..011517f3d --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/compile.lua @@ -0,0 +1,1263 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Kein-Hong Man, Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Kein-Hong Man - Initial implementation for Lua 5.0, part of Yueliang +-- Fabien Fleutot - Port to Lua 5.1, integration with Metalua +-- +------------------------------------------------------------------------------- + +---------------------------------------------------------------------- +-- +-- This code mainly results from the borrowing, then ruthless abuse, of +-- Yueliang's implementation of Lua 5.0 compiler. +-- +--------------------------------------------------------------------- + +local pp = require 'metalua.pprint' + +local luaK = require 'metalua.compiler.bytecode.lcode' +local luaP = require 'metalua.compiler.bytecode.lopcodes' + +local debugf = function() end +--local debugf=printf + +local stat = { } +local expr = { } + +local M = { } + +M.MAX_INT = 2147483645 -- INT_MAX-2 for 32-bit systems (llimits.h) +M.MAXVARS = 200 -- (llimits.h) +M.MAXUPVALUES = 32 -- (llimits.h) +M.MAXPARAMS = 100 -- (llimits.h) +M.LUA_MAXPARSERLEVEL = 200 -- (llimits.h) + +-- from lobject.h +M.VARARG_HASARG = 1 +M.VARARG_ISVARARG = 2 +M.VARARG_NEEDSARG = 4 + +local function hasmultret (k) + return k=="VCALL" or k=="VVARARG" +end + +----------------------------------------------------------------------- +-- Some ASTs take expression lists as children; it should be +-- acceptible to give an expression instead, and to automatically +-- interpret it as a single element list. That's what does this +-- function, adding a surrounding list iff needed. +-- +-- WARNING: "Do" is the tag for chunks, which are essentially lists. +-- Therefore, we don't listify stuffs with a "Do" tag. +----------------------------------------------------------------------- +local function ensure_list (ast) + return ast.tag and ast.tag ~= "Do" and {ast} or ast end + +----------------------------------------------------------------------- +-- Get a localvar structure { varname, startpc, endpc } from a +-- (zero-based) index of active variable. The catch is: don't get +-- confused between local index and active index. +-- +-- locvars[x] contains { varname, startpc, endpc }; +-- actvar[i] contains the index of the variable in locvars +----------------------------------------------------------------------- +local function getlocvar (fs, i) + return fs.f.locvars[fs.actvar[i]] +end + +local function removevars (fs, tolevel) + while fs.nactvar > tolevel do + fs.nactvar = fs.nactvar - 1 + -- There may be dummy locvars due to expr.Stat + -- FIXME: strange that they didn't disappear?! + local locvar = getlocvar (fs, fs.nactvar) + --printf("[REMOVEVARS] removing var #%i = %s", fs.nactvar, + -- locvar and tostringv(locvar) or "") + if locvar then locvar.endpc = fs.pc end + end +end + +----------------------------------------------------------------------- +-- [f] has a list of all its local variables, active and inactive. +-- Some local vars can correspond to the same register, if they exist +-- in different scopes. +-- [fs.nlocvars] is the total number of local variables, not to be +-- confused with [fs.nactvar] the numebr of variables active at the +-- current PC. +-- At this stage, the existence of the variable is not yet aknowledged, +-- since [fs.nactvar] and [fs.freereg] aren't updated. +----------------------------------------------------------------------- +local function registerlocalvar (fs, varname) + --debugf("[locvar: %s = reg %i]", varname, fs.nlocvars) + local f = fs.f + f.locvars[fs.nlocvars] = { } -- LocVar + f.locvars[fs.nlocvars].varname = varname + local nlocvars = fs.nlocvars + fs.nlocvars = fs.nlocvars + 1 + return nlocvars +end + +----------------------------------------------------------------------- +-- update the active vars counter in [fs] by adding [nvars] of them, +-- and sets those variables' [startpc] to the current [fs.pc]. +-- These variables were allready created, but not yet counted, by +-- new_localvar. +----------------------------------------------------------------------- +local function adjustlocalvars (fs, nvars) + --debugf("adjustlocalvars, nvars=%i, previous fs.nactvar=%i,".. + -- " #locvars=%i, #actvar=%i", + -- nvars, fs.nactvar, #fs.f.locvars, #fs.actvar) + + fs.nactvar = fs.nactvar + nvars + for i = nvars, 1, -1 do + --printf ("adjusting actvar #%i", fs.nactvar - i) + getlocvar (fs, fs.nactvar - i).startpc = fs.pc + end +end + +------------------------------------------------------------------------ +-- check whether, in an assignment to a local variable, the local variable +-- is needed in a previous assignment (to a table). If so, save original +-- local value in a safe place and use this safe copy in the previous +-- assignment. +------------------------------------------------------------------------ +local function check_conflict (fs, lh, v) + local extra = fs.freereg -- eventual position to save local variable + local conflict = false + while lh do + if lh.v.k == "VINDEXED" then + if lh.v.info == v.info then -- conflict? + conflict = true + lh.v.info = extra -- previous assignment will use safe copy + end + if lh.v.aux == v.info then -- conflict? + conflict = true + lh.v.aux = extra -- previous assignment will use safe copy + end + end + lh = lh.prev + end + if conflict then + luaK:codeABC (fs, "OP_MOVE", fs.freereg, v.info, 0) -- make copy + luaK:reserveregs (fs, 1) + end +end + +----------------------------------------------------------------------- +-- Create an expdesc. To be updated when expdesc is lua-ified. +----------------------------------------------------------------------- +local function init_exp (e, k, i) + e.f, e.t, e.k, e.info = luaK.NO_JUMP, luaK.NO_JUMP, k, i end + +----------------------------------------------------------------------- +-- Reserve the string in tthe constant pool, and return an expdesc +-- referring to it. +----------------------------------------------------------------------- +local function codestring (fs, e, str) + --printf( "codestring(%s)", disp.ast(str)) + init_exp (e, "VK", luaK:stringK (fs, str)) +end + +----------------------------------------------------------------------- +-- search for a local variable named [name] in the function being +-- built by [fs]. Doesn't try to visit upvalues. +----------------------------------------------------------------------- +local function searchvar (fs, name) + for i = fs.nactvar - 1, 0, -1 do + -- Because of expr.Stat, there can be some actvars which don't + -- correspond to any locvar. Hence the checking for locvar's + -- nonnilness before getting the varname. + local locvar = getlocvar(fs, i) + if locvar and name == locvar.varname then + --printf("Found local var: %s; i = %i", tostringv(locvar), i) + return i + end + end + return -1 -- not found +end + +----------------------------------------------------------------------- +-- create and return a new proto [f] +----------------------------------------------------------------------- +local function newproto () + local f = {} + f.k = {} + f.sizek = 0 + f.p = {} + f.sizep = 0 + f.code = {} + f.sizecode = 0 + f.sizelineinfo = 0 + f.sizeupvalues = 0 + f.nups = 0 + f.upvalues = {} + f.numparams = 0 + f.is_vararg = 0 + f.maxstacksize = 0 + f.lineinfo = {} + f.sizelocvars = 0 + f.locvars = {} + f.lineDefined = 0 + f.source = nil + return f +end + +------------------------------------------------------------------------ +-- create and return a function state [new_fs] as a sub-funcstate of [fs]. +------------------------------------------------------------------------ +local function open_func (old_fs) + local new_fs = { } + new_fs.upvalues = { } + new_fs.actvar = { } + local f = newproto () + new_fs.f = f + new_fs.prev = old_fs -- linked list of funcstates + new_fs.pc = 0 + new_fs.lasttarget = -1 + new_fs.jpc = luaK.NO_JUMP + new_fs.freereg = 0 + new_fs.nk = 0 + new_fs.h = {} -- constant table; was luaH_new call + new_fs.np = 0 + new_fs.nlocvars = 0 + new_fs.nactvar = 0 + new_fs.bl = nil + new_fs.nestlevel = old_fs and old_fs.nestlevel or 0 + f.maxstacksize = 2 -- registers 0/1 are always valid + new_fs.lastline = 0 + new_fs.forward_gotos = { } + new_fs.labels = { } + return new_fs +end + +------------------------------------------------------------------------ +-- Finish to set up [f] according to final state of [fs] +------------------------------------------------------------------------ +local function close_func (fs) + local f = fs.f + --printf("[CLOSE_FUNC] remove any remaining var") + removevars (fs, 0) + luaK:ret (fs, 0, 0) + f.sizecode = fs.pc + f.sizelineinfo = fs.pc + f.sizek = fs.nk + f.sizep = fs.np + f.sizelocvars = fs.nlocvars + f.sizeupvalues = f.nups + assert (fs.bl == nil) + if next(fs.forward_gotos) then + local x = pp.tostring(fs.forward_gotos) + error ("Unresolved goto: "..x) + end +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function pushclosure(fs, func, v) + local f = fs.f + f.p [fs.np] = func.f + fs.np = fs.np + 1 + init_exp (v, "VRELOCABLE", luaK:codeABx (fs, "OP_CLOSURE", 0, fs.np - 1)) + for i = 0, func.f.nups - 1 do + local o = (func.upvalues[i].k == "VLOCAL") and "OP_MOVE" or "OP_GETUPVAL" + luaK:codeABC (fs, o, 0, func.upvalues[i].info, 0) + end +end + +------------------------------------------------------------------------ +-- FIXME: is there a need for f=fs.f? if yes, why not always using it? +------------------------------------------------------------------------ +local function indexupvalue(fs, name, v) + local f = fs.f + for i = 0, f.nups - 1 do + if fs.upvalues[i].k == v.k and fs.upvalues[i].info == v.info then + assert(fs.f.upvalues[i] == name) + return i + end + end + -- new one + f.upvalues[f.nups] = name + assert (v.k == "VLOCAL" or v.k == "VUPVAL") + fs.upvalues[f.nups] = { k = v.k; info = v.info } + local nups = f.nups + f.nups = f.nups + 1 + return nups +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function markupval(fs, level) + local bl = fs.bl + while bl and bl.nactvar > level do bl = bl.previous end + if bl then bl.upval = true end +end + + +--for debug only +--[[ +local function bldepth(fs) + local i, x= 1, fs.bl + while x do i=i+1; x=x.previous end + return i +end +--]] + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function enterblock (fs, bl, isbreakable) + bl.breaklist = luaK.NO_JUMP + bl.isbreakable = isbreakable + bl.nactvar = fs.nactvar + bl.upval = false + bl.previous = fs.bl + fs.bl = bl + assert (fs.freereg == fs.nactvar) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function leaveblock (fs) + local bl = fs.bl + fs.bl = bl.previous + --printf("[LEAVEBLOCK] Removing vars...") + removevars (fs, bl.nactvar) + --printf("[LEAVEBLOCK] ...Vars removed") + if bl.upval then + luaK:codeABC (fs, "OP_CLOSE", bl.nactvar, 0, 0) + end + -- a block either controls scope or breaks (never both) + assert (not bl.isbreakable or not bl.upval) + assert (bl.nactvar == fs.nactvar) + fs.freereg = fs.nactvar -- free registers + luaK:patchtohere (fs, bl.breaklist) +end + + +------------------------------------------------------------------------ +-- read a list of expressions from a list of ast [astlist] +-- starts at the [offset]th element of the list (defaults to 1) +------------------------------------------------------------------------ +local function explist(fs, astlist, v, offset) + offset = offset or 1 + if #astlist < offset then error "I don't handle empty expr lists yet" end + --printf("[EXPLIST] about to precompile 1st element %s", disp.ast(astlist[offset])) + expr.expr (fs, astlist[offset], v) + --printf("[EXPLIST] precompiled first element v=%s", tostringv(v)) + for i = offset+1, #astlist do + luaK:exp2nextreg (fs, v) + --printf("[EXPLIST] flushed v=%s", tostringv(v)) + expr.expr (fs, astlist[i], v) + --printf("[EXPLIST] precompiled element v=%s", tostringv(v)) + end + return #astlist - offset + 1 +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function funcargs (fs, ast, v, idx_from) + local args = { } -- expdesc + local nparams + if #ast < idx_from then args.k = "VVOID" else + explist(fs, ast, args, idx_from) + luaK:setmultret(fs, args) + end + assert(v.k == "VNONRELOC") + local base = v.info -- base register for call + if hasmultret(args.k) then nparams = luaK.LUA_MULTRET else -- open call + if args.k ~= "VVOID" then + luaK:exp2nextreg(fs, args) end -- close last argument + nparams = fs.freereg - (base + 1) + end + init_exp(v, "VCALL", luaK:codeABC(fs, "OP_CALL", base, nparams + 1, 2)) + if ast.lineinfo then + luaK:fixline(fs, ast.lineinfo.first.line) + else + luaK:fixline(fs, ast.line) + end + fs.freereg = base + 1 -- call remove function and arguments and leaves + -- (unless changed) one result +end + +------------------------------------------------------------------------ +-- calculates log value for encoding the hash portion's size +------------------------------------------------------------------------ +local function log2(x) + -- math result is always one more than lua0_log2() + local mn, ex = math.frexp(x) + return ex - 1 +end + +------------------------------------------------------------------------ +-- converts an integer to a "floating point byte", represented as +-- (mmmmmxxx), where the real value is (xxx) * 2^(mmmmm) +------------------------------------------------------------------------ + +-- local function int2fb(x) +-- local m = 0 -- mantissa +-- while x >= 8 do x = math.floor((x + 1) / 2); m = m + 1 end +-- return m * 8 + x +-- end + +local function int2fb(x) + local e = 0 + while x >= 16 do + x = math.floor ( (x+1) / 2) + e = e+1 + end + if x<8 then return x + else return (e+1) * 8 + x - 8 end +end + + +------------------------------------------------------------------------ +-- FIXME: to be unified with singlevar +------------------------------------------------------------------------ +local function singlevaraux(fs, n, var, base) +--[[ +print("\n\nsinglevaraux: fs, n, var, base") +printv(fs) +printv(n) +printv(var) +printv(base) +print("\n") +--]] + if fs == nil then -- no more levels? + init_exp(var, "VGLOBAL", luaP.NO_REG) -- default is global variable + return "VGLOBAL" + else + local v = searchvar(fs, n) -- look up at current level + if v >= 0 then + init_exp(var, "VLOCAL", v) + if not base then + markupval(fs, v) -- local will be used as an upval + end + else -- not found at current level; try upper one + if singlevaraux(fs.prev, n, var, false) == "VGLOBAL" then + return "VGLOBAL" end + var.info = indexupvalue (fs, n, var) + var.k = "VUPVAL" + return "VUPVAL" + end + end +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function singlevar(fs, varname, var) + if singlevaraux(fs, varname, var, true) == "VGLOBAL" then + var.info = luaK:stringK (fs, varname) end +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function new_localvar (fs, name, n) + assert (type (name) == "string") + if fs.nactvar + n > M.MAXVARS then error ("too many local vars") end + fs.actvar[fs.nactvar + n] = registerlocalvar (fs, name) + --printf("[NEW_LOCVAR] %i = %s", fs.nactvar+n, name) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function parlist (fs, ast_params) + local dots = (#ast_params > 0 and ast_params[#ast_params].tag == "Dots") + local nparams = dots and #ast_params - 1 or #ast_params + for i = 1, nparams do + assert (ast_params[i].tag == "Id", "Function parameters must be Ids") + new_localvar (fs, ast_params[i][1], i-1) + end + -- from [code_param]: + --checklimit (fs, fs.nactvar, self.M.MAXPARAMS, "parameters") + fs.f.numparams = fs.nactvar + fs.f.is_vararg = dots and M.VARARG_ISVARARG or 0 + adjustlocalvars (fs, nparams) + fs.f.numparams = fs.nactvar --FIXME vararg must be taken in account + luaK:reserveregs (fs, fs.nactvar) -- reserve register for parameters +end + +------------------------------------------------------------------------ +-- if there's more variables than expressions in an assignment, +-- some assignations to nil are made for extraneous vars. +-- Also handles multiret functions +------------------------------------------------------------------------ +local function adjust_assign (fs, nvars, nexps, e) + local extra = nvars - nexps + if hasmultret (e.k) then + extra = extra+1 -- includes call itself + if extra <= 0 then extra = 0 end + luaK:setreturns(fs, e, extra) -- call provides the difference + if extra > 1 then luaK:reserveregs(fs, extra-1) end + else + if e.k ~= "VVOID" then + luaK:exp2nextreg(fs, e) end -- close last expression + if extra > 0 then + local reg = fs.freereg + luaK:reserveregs(fs, extra) + luaK:_nil(fs, reg, extra) + end + end +end + + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function enterlevel (fs) + fs.nestlevel = fs.nestlevel + 1 + assert (fs.nestlevel <= M.LUA_MAXPARSERLEVEL, "too many syntax levels") +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function leavelevel (fs) + fs.nestlevel = fs.nestlevel - 1 +end + +------------------------------------------------------------------------ +-- Parse conditions in if/then/else, while, repeat +------------------------------------------------------------------------ +local function cond (fs, ast) + local v = { } + expr.expr(fs, ast, v) -- read condition + if v.k == "VNIL" then v.k = "VFALSE" end -- 'falses' are all equal here + luaK:goiftrue (fs, v) + return v.f +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function chunk (fs, ast) + enterlevel (fs) + assert (not ast.tag) + for i=1, #ast do + stat.stat (fs, ast[i]); + fs.freereg = fs.nactvar + end + leavelevel (fs) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function block (fs, ast) + local bl = {} + enterblock (fs, bl, false) + for i=1, #ast do + stat.stat (fs, ast[i]) + fs.freereg = fs.nactvar + end + assert (bl.breaklist == luaK.NO_JUMP) + leaveblock (fs) +end + +------------------------------------------------------------------------ +-- Forin / Fornum body parser +-- [fs] +-- [body] +-- [base] +-- [nvars] +-- [isnum] +------------------------------------------------------------------------ +local function forbody (fs, ast_body, base, nvars, isnum) + local bl = {} -- BlockCnt + adjustlocalvars (fs, 3) -- control variables + local prep = + isnum and luaK:codeAsBx (fs, "OP_FORPREP", base, luaK.NO_JUMP) + or luaK:jump (fs) + enterblock (fs, bl, false) -- loop block + adjustlocalvars (fs, nvars) -- scope for declared variables + luaK:reserveregs (fs, nvars) + block (fs, ast_body) + leaveblock (fs) + --luaK:patchtohere (fs, prep-1) + luaK:patchtohere (fs, prep) + local endfor = + isnum and luaK:codeAsBx (fs, "OP_FORLOOP", base, luaK.NO_JUMP) + or luaK:codeABC (fs, "OP_TFORLOOP", base, 0, nvars) + luaK:fixline (fs, ast_body.line) -- pretend that 'OP_FOR' starts the loop + luaK:patchlist (fs, isnum and endfor or luaK:jump(fs), prep + 1) +end + + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function recfield (fs, ast, cc) + local reg = fs.freereg + local key, val = {}, {} -- expdesc + --FIXME: expr + exp2val = index --> + -- check reduncancy between exp2val and exp2rk + cc.nh = cc.nh + 1 + expr.expr(fs, ast[1], key); + luaK:exp2val (fs, key) + local keyreg = luaK:exp2RK (fs, key) + expr.expr(fs, ast[2], val) + local valreg = luaK:exp2RK (fs, val) + luaK:codeABC(fs, "OP_SETTABLE", cc.t.info, keyreg, valreg) + fs.freereg = reg -- free registers +end + + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function listfield(fs, ast, cc) + expr.expr(fs, ast, cc.v) + assert (cc.na <= luaP.MAXARG_Bx) -- FIXME check <= or < + cc.na = cc.na + 1 + cc.tostore = cc.tostore + 1 +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +local function closelistfield(fs, cc) + if cc.v.k == "VVOID" then return end -- there is no list item + luaK:exp2nextreg(fs, cc.v) + cc.v.k = "VVOID" + if cc.tostore == luaP.LFIELDS_PER_FLUSH then + luaK:setlist (fs, cc.t.info, cc.na, cc.tostore) + cc.tostore = 0 + end +end + +------------------------------------------------------------------------ +-- The last field might be a call to a multireturn function. In that +-- case, we must unfold all of its results into the list. +------------------------------------------------------------------------ +local function lastlistfield(fs, cc) + if cc.tostore == 0 then return end + if hasmultret (cc.v.k) then + luaK:setmultret(fs, cc.v) + luaK:setlist (fs, cc.t.info, cc.na, luaK.LUA_MULTRET) + cc.na = cc.na - 1 + else + if cc.v.k ~= "VVOID" then luaK:exp2nextreg(fs, cc.v) end + luaK:setlist (fs, cc.t.info, cc.na, cc.tostore) + end +end +------------------------------------------------------------------------ +------------------------------------------------------------------------ +-- +-- Statement parsers table +-- +------------------------------------------------------------------------ +------------------------------------------------------------------------ + +function stat.stat (fs, ast) + if ast.lineinfo then fs.lastline = ast.lineinfo.last.line end + --debugf (" - Statement %s", table.tostring (ast) ) + + if not ast.tag then chunk (fs, ast) else + + local parser = stat [ast.tag] + if not parser then + error ("A statement cannot have tag `"..ast.tag) end + parser (fs, ast) + end + --debugf (" - /Statement `%s", ast.tag) +end + +------------------------------------------------------------------------ + +stat.Do = block + +------------------------------------------------------------------------ + +function stat.Break (fs, ast) + -- if ast.lineinfo then fs.lastline = ast.lineinfo.last.line + local bl, upval = fs.bl, false + while bl and not bl.isbreakable do + if bl.upval then upval = true end + bl = bl.previous + end + assert (bl, "no loop to break") + if upval then luaK:codeABC(fs, "OP_CLOSE", bl.nactvar, 0, 0) end + bl.breaklist = luaK:concat(fs, bl.breaklist, luaK:jump(fs)) +end + +------------------------------------------------------------------------ + +function stat.Return (fs, ast) + local e = {} -- expdesc + local first -- registers with returned values + local nret = #ast + + if nret == 0 then first = 0 + else + --printf("[RETURN] compiling explist") + explist (fs, ast, e) + --printf("[RETURN] explist e=%s", tostringv(e)) + if hasmultret (e.k) then + luaK:setmultret(fs, e) + if e.k == "VCALL" and nret == 1 then + luaP:SET_OPCODE(luaK:getcode(fs, e), "OP_TAILCALL") + assert(luaP:GETARG_A(luaK:getcode(fs, e)) == fs.nactvar) + end + first = fs.nactvar + nret = luaK.LUA_MULTRET -- return all values + elseif nret == 1 then + first = luaK:exp2anyreg(fs, e) + else + --printf("* Return multiple vals in nextreg %i", fs.freereg) + luaK:exp2nextreg(fs, e) -- values must go to the 'stack' + first = fs.nactvar -- return all 'active' values + assert(nret == fs.freereg - first) + end + end + luaK:ret(fs, first, nret) +end +------------------------------------------------------------------------ + +function stat.Local (fs, ast) + local names, values = ast[1], ast[2] or { } + for i = 1, #names do new_localvar (fs, names[i][1], i-1) end + local e = { } + if #values == 0 then e.k = "VVOID" else explist (fs, values, e) end + adjust_assign (fs, #names, #values, e) + adjustlocalvars (fs, #names) +end + +------------------------------------------------------------------------ + +function stat.Localrec (fs, ast) + assert(#ast[1]==1 and #ast[2]==1, "Multiple letrecs not implemented yet") + local ast_var, ast_val, e_var, e_val = ast[1][1], ast[2][1], { }, { } + new_localvar (fs, ast_var[1], 0) + init_exp (e_var, "VLOCAL", fs.freereg) + luaK:reserveregs (fs, 1) + adjustlocalvars (fs, 1) + expr.expr (fs, ast_val, e_val) + luaK:storevar (fs, e_var, e_val) + getlocvar (fs, fs.nactvar-1).startpc = fs.pc +end + +------------------------------------------------------------------------ + +function stat.If (fs, ast) + local astlen = #ast + -- Degenerate case #1: no statement + if astlen==0 then return block(fs, { }) end + -- Degenerate case #2: only an else statement + if astlen==1 then return block(fs, ast[1]) end + + local function test_then_block (fs, test, body) + local condexit = cond (fs, test); + block (fs, body) + return condexit + end + + local escapelist = luaK.NO_JUMP + + local flist = test_then_block (fs, ast[1], ast[2]) -- 'then' statement + for i = 3, #ast - 1, 2 do -- 'elseif' statement + escapelist = luaK:concat( fs, escapelist, luaK:jump(fs)) + luaK:patchtohere (fs, flist) + flist = test_then_block (fs, ast[i], ast[i+1]) + end + if #ast % 2 == 1 then -- 'else' statement + escapelist = luaK:concat(fs, escapelist, luaK:jump(fs)) + luaK:patchtohere(fs, flist) + block (fs, ast[#ast]) + else + escapelist = luaK:concat(fs, escapelist, flist) + end + luaK:patchtohere(fs, escapelist) +end + +------------------------------------------------------------------------ + +function stat.Forin (fs, ast) + local vars, vals, body = ast[1], ast[2], ast[3] + -- imitating forstat: + local bl = { } + enterblock (fs, bl, true) + -- imitating forlist: + local e, base = { }, fs.freereg + new_localvar (fs, "(for generator)", 0) + new_localvar (fs, "(for state)", 1) + new_localvar (fs, "(for control)", 2) + for i = 1, #vars do new_localvar (fs, vars[i][1], i+2) end + explist (fs, vals, e) + adjust_assign (fs, 3, #vals, e) + luaK:checkstack (fs, 3) + forbody (fs, body, base, #vars, false) + -- back to forstat: + leaveblock (fs) +end + +------------------------------------------------------------------------ + +function stat.Fornum (fs, ast) + + local function exp1 (ast_e) + local e = { } + expr.expr (fs, ast_e, e) + luaK:exp2nextreg (fs, e) + end + -- imitating forstat: + local bl = { } + enterblock (fs, bl, true) + -- imitating fornum: + local base = fs.freereg + new_localvar (fs, "(for index)", 0) + new_localvar (fs, "(for limit)", 1) + new_localvar (fs, "(for step)", 2) + new_localvar (fs, ast[1][1], 3) + exp1 (ast[2]) -- initial value + exp1 (ast[3]) -- limit + if #ast == 5 then exp1 (ast[4]) else -- default step = 1 + luaK:codeABx(fs, "OP_LOADK", fs.freereg, luaK:numberK(fs, 1)) + luaK:reserveregs(fs, 1) + end + forbody (fs, ast[#ast], base, 1, true) + -- back to forstat: + leaveblock (fs) +end + +------------------------------------------------------------------------ +function stat.Repeat (fs, ast) + local repeat_init = luaK:getlabel (fs) + local bl1, bl2 = { }, { } + enterblock (fs, bl1, true) + enterblock (fs, bl2, false) + chunk (fs, ast[1]) + local condexit = cond (fs, ast[2]) + if not bl2.upval then + leaveblock (fs) + luaK:patchlist (fs, condexit, repeat_init) + else + stat.Break (fs) + luaK:patchtohere (fs, condexit) + leaveblock (fs) + luaK:patchlist (fs, luaK:jump (fs), repeat_init) + end + leaveblock (fs) +end + +------------------------------------------------------------------------ + +function stat.While (fs, ast) + local whileinit = luaK:getlabel (fs) + local condexit = cond (fs, ast[1]) + local bl = { } + enterblock (fs, bl, true) + block (fs, ast[2]) + luaK:patchlist (fs, luaK:jump (fs), whileinit) + leaveblock (fs) + luaK:patchtohere (fs, condexit); +end + +------------------------------------------------------------------------ + +-- FIXME: it's cumbersome to write this in this semi-recursive way. +function stat.Set (fs, ast) + local ast_lhs, ast_vals, e = ast[1], ast[2], { } + + --print "\n\nSet ast_lhs ast_vals:" + --print(disp.ast(ast_lhs)) + --print(disp.ast(ast_vals)) + + local function let_aux (lhs, nvars) + local legal = { VLOCAL=1, VUPVAL=1, VGLOBAL=1, VINDEXED=1 } + --printv(lhs) + if not legal [lhs.v.k] then + error ("Bad lhs expr: "..pp.tostring(ast_lhs)) + end + if nvars < #ast_lhs then -- this is not the last lhs + local nv = { v = { }, prev = lhs } + expr.expr (fs, ast_lhs [nvars+1], nv.v) + if nv.v.k == "VLOCAL" then check_conflict (fs, lhs, nv.v) end + let_aux (nv, nvars+1) + else -- this IS the last lhs + explist (fs, ast_vals, e) + if #ast_vals < nvars then + adjust_assign (fs, nvars, #ast_vals, e) + elseif #ast_vals > nvars then + adjust_assign (fs, nvars, #ast_vals, e) + fs.freereg = fs.freereg - #ast_vals + nvars + else -- #ast_vals == nvars (and we're at last lhs) + luaK:setoneret (fs, e) -- close last expression + luaK:storevar (fs, lhs.v, e) + return -- avoid default + end + end + init_exp (e, "VNONRELOC", fs.freereg - 1) -- default assignment + luaK:storevar (fs, lhs.v, e) + end + + local lhs = { v = { }, prev = nil } + expr.expr (fs, ast_lhs[1], lhs.v) + let_aux( lhs, 1) +end + +------------------------------------------------------------------------ + +function stat.Call (fs, ast) + local v = { } + expr.Call (fs, ast, v) + luaP:SETARG_C (luaK:getcode(fs, v), 1) +end + +------------------------------------------------------------------------ + +function stat.Invoke (fs, ast) + local v = { } + expr.Invoke (fs, ast, v) + --FIXME: didn't check that, just copied from stat.Call + luaP:SETARG_C (luaK:getcode(fs, v), 1) +end + + +local function patch_goto (fs, src, dst) + +end + + +------------------------------------------------------------------------ +-- Goto/Label data: +-- fs.labels :: string => { nactvar :: int; pc :: int } +-- fs.forward_gotos :: string => list(int) +-- +-- fs.labels goes from label ids to the number of active variables at +-- the label's PC, and that PC +-- +-- fs.forward_gotos goes from label ids to the list of the PC where +-- some goto wants to jump to this label. Since gotos are actually made +-- up of two instructions OP_CLOSE and OP_JMP, it's the first instruction's +-- PC that's stored in fs.forward_gotos +-- +-- Note that backward gotos aren't stored: since their destination is knowns +-- when they're compiled, their target is directly set. +------------------------------------------------------------------------ + +------------------------------------------------------------------------ +-- Set a Label to jump to with Goto +------------------------------------------------------------------------ +function stat.Label (fs, ast) + local label_id = ast[1] + if type(label_id)=='table' then label_id=label_id[1] end + -- printf("Label %s at PC %i", label_id, fs.pc) + ------------------------------------------------------------------- + -- Register the label, so that future gotos can use it. + ------------------------------------------------------------------- + if fs.labels [label_id] then error "Duplicate label in function" + else fs.labels [label_id] = { pc = fs.pc; nactvar = fs.nactvar } end + local gotos = fs.forward_gotos [label_id] + if gotos then + ---------------------------------------------------------------- + -- Patch forward gotos which were targetting this label. + ---------------------------------------------------------------- + for _, goto_pc in ipairs(gotos) do + local close_instr = fs.f.code[goto_pc] + local jmp_instr = fs.f.code[goto_pc+1] + local goto_nactvar = luaP:GETARG_A (close_instr) + if fs.nactvar < goto_nactvar then + luaP:SETARG_A (close_instr, fs.nactvar) end + luaP:SETARG_sBx (jmp_instr, fs.pc - goto_pc - 2) + end + ---------------------------------------------------------------- + -- Gotos are patched, they can be forgotten about (when the + -- function will be finished, it will be checked that all gotos + -- have been patched, by checking that forward_goto is empty). + ---------------------------------------------------------------- + fs.forward_gotos[label_id] = nil + end +end + +------------------------------------------------------------------------ +-- jumps to a label set with stat.Label. +-- Argument must be a String or an Id +-- FIXME/optim: get rid of useless OP_CLOSE when nactvar doesn't change. +-- Thsi must be done both here for backward gotos, and in +-- stat.Label for forward gotos. +------------------------------------------------------------------------ +function stat.Goto (fs, ast) + local label_id = ast[1] + if type(label_id)=='table' then label_id=label_id[1] end + -- printf("Goto %s at PC %i", label_id, fs.pc) + local label = fs.labels[label_id] + if label then + ---------------------------------------------------------------- + -- Backward goto: the label already exists, so I can get its + -- nactvar and address directly. nactvar is used to close + -- upvalues if we get out of scoping blocks by jumping. + ---------------------------------------------------------------- + if fs.nactvar > label.nactvar then + luaK:codeABC (fs, "OP_CLOSE", label.nactvar, 0, 0) end + local offset = label.pc - fs.pc - 1 + luaK:codeAsBx (fs, "OP_JMP", 0, offset) + else + ---------------------------------------------------------------- + -- Forward goto: will be patched when the matching label is + -- found, forward_gotos[label_id] keeps the PC of the CLOSE + -- instruction just before the JMP. [stat.Label] will use it to + -- patch the OP_CLOSE and the OP_JMP. + ---------------------------------------------------------------- + if not fs.forward_gotos[label_id] then + fs.forward_gotos[label_id] = { } end + table.insert (fs.forward_gotos[label_id], fs.pc) + luaK:codeABC (fs, "OP_CLOSE", fs.nactvar, 0, 0) + luaK:codeAsBx (fs, "OP_JMP", 0, luaK.NO_JUMP) + end +end + +------------------------------------------------------------------------ +------------------------------------------------------------------------ +-- +-- Expression parsers table +-- +------------------------------------------------------------------------ +------------------------------------------------------------------------ + +function expr.expr (fs, ast, v) + if type(ast) ~= "table" then + error ("Expr AST expected, got "..pp.tostring(ast)) end + + if ast.lineinfo then fs.lastline = ast.lineinfo.last.line end + + --debugf (" - Expression %s", table.tostring (ast)) + local parser = expr[ast.tag] + if parser then parser (fs, ast, v) + elseif not ast.tag then + error ("No tag in expression ".. + pp.tostring(ast, {line_max=80, hide_hash=1, metalua_tag=1})) + else + error ("No parser for node `"..ast.tag) end + --debugf (" - /Expression `%s", ast.tag) +end + +------------------------------------------------------------------------ + +function expr.Nil (fs, ast, v) init_exp (v, "VNIL", 0) end +function expr.True (fs, ast, v) init_exp (v, "VTRUE", 0) end +function expr.False (fs, ast, v) init_exp (v, "VFALSE", 0) end +function expr.String (fs, ast, v) codestring (fs, v, ast[1]) end +function expr.Number (fs, ast, v) + init_exp (v, "VKNUM", 0) + v.nval = ast[1] +end + +function expr.Paren (fs, ast, v) + expr.expr (fs, ast[1], v) + luaK:setoneret (fs, v) +end + +function expr.Dots (fs, ast, v) + assert (fs.f.is_vararg ~= 0, "No vararg in this function") + -- NEEDSARG flag is set if and only if the function is a vararg, + -- but no vararg has been used yet in its code. + if fs.f.is_vararg < M.VARARG_NEEDSARG then + fs.f.is_varag = fs.f.is_vararg - M.VARARG_NEEDSARG end + init_exp (v, "VVARARG", luaK:codeABC (fs, "OP_VARARG", 0, 1, 0)) +end + +------------------------------------------------------------------------ + +function expr.Table (fs, ast, v) + local pc = luaK:codeABC(fs, "OP_NEWTABLE", 0, 0, 0) + local cc = { v = { } , na = 0, nh = 0, tostore = 0, t = v } -- ConsControl + init_exp (v, "VRELOCABLE", pc) + init_exp (cc.v, "VVOID", 0) -- no value (yet) + luaK:exp2nextreg (fs, v) -- fix it at stack top (for gc) + for i = 1, #ast do + assert(cc.v.k == "VVOID" or cc.tostore > 0) + closelistfield(fs, cc); + (ast[i].tag == "Pair" and recfield or listfield) (fs, ast[i], cc) + end + lastlistfield(fs, cc) + + -- Configure [OP_NEWTABLE] dimensions + luaP:SETARG_B(fs.f.code[pc], int2fb(cc.na)) -- set initial array size + luaP:SETARG_C(fs.f.code[pc], int2fb(cc.nh)) -- set initial table size + --printv(fs.f.code[pc]) +end + + +------------------------------------------------------------------------ + +function expr.Function (fs, ast, v) + if ast.lineinfo then fs.lastline = ast.lineinfo.last.line end + + local new_fs = open_func(fs) + if ast.lineinfo then + new_fs.f.lineDefined, new_fs.f.lastLineDefined = + ast.lineinfo.first.line, ast.lineinfo.last.line + end + parlist (new_fs, ast[1]) + chunk (new_fs, ast[2]) + close_func (new_fs) + pushclosure(fs, new_fs, v) +end + +------------------------------------------------------------------------ + +function expr.Op (fs, ast, v) + if ast.lineinfo then fs.lastline = ast.lineinfo.last.line end + local op = ast[1] + + if #ast == 2 then + expr.expr (fs, ast[2], v) + luaK:prefix (fs, op, v) + elseif #ast == 3 then + local v2 = { } + expr.expr (fs, ast[2], v) + luaK:infix (fs, op, v) + expr.expr (fs, ast[3], v2) + luaK:posfix (fs, op, v, v2) + else + error "Wrong arg number" + end +end + +------------------------------------------------------------------------ + +function expr.Call (fs, ast, v) + expr.expr (fs, ast[1], v) + luaK:exp2nextreg (fs, v) + funcargs(fs, ast, v, 2) + --debugf("after expr.Call: %s, %s", v.k, luaP.opnames[luaK:getcode(fs, v).OP]) +end + +------------------------------------------------------------------------ +-- `Invoke{ table key args } +function expr.Invoke (fs, ast, v) + expr.expr (fs, ast[1], v) + luaK:dischargevars (fs, v) + local key = { } + codestring (fs, key, ast[2][1]) + luaK:_self (fs, v, key) + funcargs (fs, ast, v, 3) +end + +------------------------------------------------------------------------ + +function expr.Index (fs, ast, v) + if #ast ~= 2 then + print"\n\nBAD INDEX AST:" + pp.print(ast) + error "generalized indexes not implemented" end + + if ast.lineinfo then fs.lastline = ast.lineinfo.last.line end + + --assert(fs.lastline ~= 0, ast.tag) + + expr.expr (fs, ast[1], v) + luaK:exp2anyreg (fs, v) + + local k = { } + expr.expr (fs, ast[2], k) + luaK:exp2val (fs, k) + luaK:indexed (fs, v, k) +end + +------------------------------------------------------------------------ + +function expr.Id (fs, ast, v) + assert (ast.tag == "Id") + singlevar (fs, ast[1], v) +end + +------------------------------------------------------------------------ + +function expr.Stat (fs, ast, v) + --printf(" * Stat: %i actvars, first freereg is %i", fs.nactvar, fs.freereg) + --printf(" actvars: %s", table.tostring(fs.actvar)) + + -- Protect temporary stack values by pretending they are local + -- variables. Local vars are in registers 0 ... fs.nactvar-1, + -- and temporary unnamed variables in fs.nactvar ... fs.freereg-1 + local save_nactvar = fs.nactvar + + -- Eventually, the result should go on top of stack *after all + -- `Stat{ } related computation and string usage is over. The index + -- of this destination register is kept here: + local dest_reg = fs.freereg + + -- There might be variables in actvar whose register is > nactvar, + -- and therefore will not be protected by the "nactvar := freereg" + -- trick. Indeed, `Local only increases nactvar after the variable + -- content has been computed. Therefore, in + -- "local foo = -{`Stat{...}}", variable foo will be messed up by + -- the compilation of `Stat. + -- FIX: save the active variables at indices >= nactvar in + -- save_actvar, and restore them after `Stat has been computed. + -- + -- I use a while rather than for loops and length operators because + -- fs.actvar is a 0-based array... + local save_actvar = { } do + local i = fs.nactvar + while true do + local v = fs.actvar[i] + if not v then break end + --printf("save hald-baked actvar %s at index %i", table.tostring(v), i) + save_actvar[i] = v + i=i+1 + end + end + + fs.nactvar = fs.freereg -- Now temp unnamed registers are protected + enterblock (fs, { }, false) + chunk (fs, ast[1]) + expr.expr (fs, ast[2], v) + luaK:exp2nextreg (fs, v) + leaveblock (fs) + luaK:exp2reg (fs, v, dest_reg) + + -- Reserve the newly allocated stack level + -- Puzzled note: here was written "fs.freereg = fs.freereg+1". + -- I'm pretty sure it should rather be dest_reg+1, but maybe + -- both are equivalent? + fs.freereg = dest_reg+1 + + -- Restore nactvar, so that intermediate stacked value stop + -- being protected. + --printf(" nactvar back from %i to %i", fs.nactvar, save_nactvar) + fs.nactvar = save_nactvar + + -- restore messed-up unregistered local vars + for i, j in pairs(save_actvar) do + --printf(" Restoring actvar %i", i) + fs.actvar[i] = j + end + --printf(" * End of Stat") +end + +------------------------------------------------------------------------ +-- Main function: ast --> proto +------------------------------------------------------------------------ +function M.ast_to_proto (ast, source) + local fs = open_func (nil) + fs.f.is_vararg = M.VARARG_ISVARARG + chunk (fs, ast) + close_func (fs) + assert (fs.prev == nil) + assert (fs.f.nups == 0) + assert (fs.nestlevel == 0) + if source then fs.f.source = source end + return fs.f, source +end + +return M \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/lcode.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/lcode.lua new file mode 100644 index 000000000..ede1a1c45 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/lcode.lua @@ -0,0 +1,1038 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2005-2013 Kein-Hong Man, Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Kein-Hong Man - Initial implementation for Lua 5.0, part of Yueliang +-- Fabien Fleutot - Port to Lua 5.1, integration with Metalua +-- +------------------------------------------------------------------------------- + +--[[-------------------------------------------------------------------- + + $Id$ + + lcode.lua + Lua 5 code generator in Lua + This file is part of Yueliang. + + Copyright (c) 2005 Kein-Hong Man + The COPYRIGHT file describes the conditions + under which this software may be distributed. + + See the ChangeLog for more information. + +------------------------------------------------------------------------ + + [FF] Slightly modified, mainly to produce Lua 5.1 bytecode. + +----------------------------------------------------------------------]] + +--[[-------------------------------------------------------------------- +-- Notes: +-- * one function manipulate a pointer argument with a simple data type +-- (can't be emulated by a table, ambiguous), now returns that value: +-- luaK:concat(fs, l1, l2) +-- * some function parameters changed to boolean, additional code +-- translates boolean back to 1/0 for instruction fields +-- * Added: +-- luaK:ttisnumber(o) (from lobject.h) +-- luaK:nvalue(o) (from lobject.h) +-- luaK:setnilvalue(o) (from lobject.h) +-- luaK:setsvalue(o) (from lobject.h) +-- luaK:setnvalue(o) (from lobject.h) +-- luaK:sethvalue(o) (from lobject.h) +----------------------------------------------------------------------]] + +local luaP = require 'metalua.compiler.bytecode.lopcodes' + +local function debugf() end + +local luaK = { } + +luaK.MAXSTACK = 250 -- (llimits.h, used in lcode.lua) +luaK.LUA_MULTRET = -1 -- (lua.h) + +------------------------------------------------------------------------ +-- Marks the end of a patch list. It is an invalid value both as an absolute +-- address, and as a list link (would link an element to itself). +------------------------------------------------------------------------ +luaK.NO_JUMP = -1 + +--FF 5.1 +function luaK:isnumeral(e) + return e.k=="VKNUM" and e.t==self.NO_JUMP and e.t==self.NO_JUMP +end + +------------------------------------------------------------------------ +-- emulation of TObject macros (these are from lobject.h) +-- * TObject is a table since lcode passes references around +-- * tt member field removed, using Lua's type() instead +------------------------------------------------------------------------ +function luaK:ttisnumber(o) + if o then return type(o.value) == "number" else return false end +end +function luaK:nvalue(o) return o.value end +function luaK:setnilvalue(o) o.value = nil end +function luaK:setsvalue(o, s) o.value = s end +luaK.setnvalue = luaK.setsvalue +luaK.sethvalue = luaK.setsvalue + +------------------------------------------------------------------------ +-- returns the instruction object for given e (expdesc) +------------------------------------------------------------------------ +function luaK:getcode(fs, e) + return fs.f.code[e.info] +end + +------------------------------------------------------------------------ +-- codes an instruction with a signed Bx (sBx) field +------------------------------------------------------------------------ +function luaK:codeAsBx(fs, o, A, sBx) + return self:codeABx(fs, o, A, sBx + luaP.MAXARG_sBx) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:hasjumps(e) + return e.t ~= e.f +end + +------------------------------------------------------------------------ +-- FF updated 5.1 +------------------------------------------------------------------------ +function luaK:_nil(fs, from, n) + if fs.pc > fs.lasttarget then -- no jumps to current position? + if fs.pc == 0 then return end --function start, positions are already clean + local previous = fs.f.code[fs.pc - 1] + if luaP:GET_OPCODE(previous) == "OP_LOADNIL" then + local pfrom = luaP:GETARG_A(previous) + local pto = luaP:GETARG_B(previous) + if pfrom <= from and from <= pto + 1 then -- can connect both? + if from + n - 1 > pto then + luaP:SETARG_B(previous, from + n - 1) + end + return + end + end + end + self:codeABC(fs, "OP_LOADNIL", from, from + n - 1, 0) -- else no optimization +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:jump(fs) + local jpc = fs.jpc -- save list of jumps to here + fs.jpc = self.NO_JUMP + local j = self:codeAsBx(fs, "OP_JMP", 0, self.NO_JUMP) + return self:concat(fs, j, jpc) -- keep them on hold +end + +--FF 5.1 +function luaK:ret (fs, first, nret) + luaK:codeABC (fs, "OP_RETURN", first, nret+1, 0) +end + + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:condjump(fs, op, A, B, C) + self:codeABC(fs, op, A, B, C) + return self:jump(fs) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:fixjump(fs, pc, dest) + local jmp = fs.f.code[pc] + local offset = dest - (pc + 1) + assert(dest ~= self.NO_JUMP) + if math.abs(offset) > luaP.MAXARG_sBx then + error("control structure too long") + end + luaP:SETARG_sBx(jmp, offset) +end + +------------------------------------------------------------------------ +-- returns current 'pc' and marks it as a jump target (to avoid wrong +-- optimizations with consecutive instructions not in the same basic block). +------------------------------------------------------------------------ +function luaK:getlabel(fs) + fs.lasttarget = fs.pc + return fs.pc +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:getjump(fs, pc) + local offset = luaP:GETARG_sBx(fs.f.code[pc]) + if offset == self.NO_JUMP then -- point to itself represents end of list + return self.NO_JUMP -- end of list + else + return (pc + 1) + offset -- turn offset into absolute position + end +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:getjumpcontrol(fs, pc) + local pi = fs.f.code[pc] + local ppi = fs.f.code[pc - 1] + if pc >= 1 and luaP:testOpMode(luaP:GET_OPCODE(ppi), "OpModeT") then + return ppi + else + return pi + end +end + +------------------------------------------------------------------------ +-- check whether list has any jump that do not produce a value +-- (or produce an inverted value) +------------------------------------------------------------------------ +--FF updated 5.1 +function luaK:need_value(fs, list, cond) + while list ~= self.NO_JUMP do + local i = self:getjumpcontrol(fs, list) + if luaP:GET_OPCODE(i) ~= "OP_TESTSET" or + luaP:GETARG_A(i) ~= luaP.NO_REG or + luaP:GETARG_C(i) ~= cond then + return true + end + list = self:getjump(fs, list) + end + return false -- not found +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +--FF updated 5.1 +function luaK:patchtestreg(fs, node, reg) + assert(reg) -- pour assurer, vu que j'ai ajoute un parametre p/r a 5.0 + local i = self:getjumpcontrol(fs, node) + if luaP:GET_OPCODE(i) ~= "OP_TESTSET" then + return false end -- cannot patch other instructions + if reg ~= luaP.NO_REG and reg ~= luaP:GETARG_B(i) then + luaP:SETARG_A(i, reg) + else + -- no register to put value or register already has the value + luaP:SET_OPCODE(i, "OP_TEST") + luaP:SETARG_A(i, luaP:GETARG_B(i)) + luaP:SETARG_B(i, 0) + luaP:SETARG_C(i, luaP:GETARG_C(i)) + end + return true +end + +--FF added 5.1 +function luaK:removevalues (fs, list) + while list ~= self.NO_JUMP do + self:patchtestreg (fs, list, luaP.NO_REG) + list = self:getjump (fs, list) + end +end + +------------------------------------------------------------------------ +-- FF updated 5.1 +------------------------------------------------------------------------ +function luaK:patchlistaux(fs, list, vtarget, reg, dtarget) + while list ~= self.NO_JUMP do + local _next = self:getjump(fs, list) + if self:patchtestreg (fs, list, reg) then + self:fixjump(fs, list, vtarget) + else + self:fixjump (fs, list, dtarget) + end + list = _next + end +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:dischargejpc(fs) + self:patchlistaux(fs, fs.jpc, fs.pc, luaP.NO_REG, fs.pc) + fs.jpc = self.NO_JUMP +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:patchlist(fs, list, target) + if target == fs.pc then + self:patchtohere(fs, list) + else + assert(target < fs.pc) + self:patchlistaux(fs, list, target, luaP.NO_REG, target) + end +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:patchtohere(fs, list) + self:getlabel(fs) + fs.jpc = self:concat(fs, fs.jpc, list) +end + +------------------------------------------------------------------------ +-- * l1 was a pointer, now l1 is returned and callee assigns the value +------------------------------------------------------------------------ +function luaK:concat(fs, l1, l2) + if l2 == self.NO_JUMP then return l1 -- unchanged + elseif l1 == self.NO_JUMP then + return l2 -- changed + else + local list = l1 + local _next = self:getjump(fs, list) + while _next ~= self.NO_JUMP do -- find last element + list = _next + _next = self:getjump(fs, list) + end + self:fixjump(fs, list, l2) + end + return l1 -- unchanged +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:checkstack(fs, n) + local newstack = fs.freereg + n + if newstack > fs.f.maxstacksize then + if newstack >= luaK.MAXSTACK then + error("function or expression too complex") + end + fs.f.maxstacksize = newstack + end +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:reserveregs(fs, n) + self:checkstack(fs, n) + fs.freereg = fs.freereg + n +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:freereg(fs, reg) + if not luaP:ISK (reg) and reg >= fs.nactvar then + fs.freereg = fs.freereg - 1 + assert(reg == fs.freereg, + string.format("reg=%i, fs.freereg=%i", reg, fs.freereg)) + end +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:freeexp(fs, e) + if e.k == "VNONRELOC" then + self:freereg(fs, e.info) + end +end + +------------------------------------------------------------------------ +-- k is a constant, v is... what? +-- fs.h is a hash value --> index in f.k +------------------------------------------------------------------------ +-- * luaH_get, luaH_set deleted; direct table access used instead +-- * luaO_rawequalObj deleted in first assert +-- * setobj2n deleted in assignment of v to f.k table +------------------------------------------------------------------------ +--FF radically updated, not completely understood +function luaK:addk(fs, k, v) + local idx = fs.h[k.value] + local f = fs.f +-- local oldsize = f.sizek + if self:ttisnumber (idx) then + --TODO this assert currently FAILS + --assert(fs.f.k[self:nvalue(idx)] == v) + return self:nvalue(idx) + else -- constant not found; create a new entry + do + local t = type (v.value) + assert(t=="nil" or t=="string" or t=="number" or t=="boolean") + end + --debugf("[const: k[%i] = %s ]", fs.nk, tostringv(v.value)) + fs.f.k[fs.nk] = v + fs.h[k.value] = { } + self:setnvalue(fs.h[k.value], fs.nk) + local nk = fs.nk + fs.nk = fs.nk+1 + return nk + end +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:stringK(fs, s) + assert (type(s)=="string") + local o = {} -- TObject + self:setsvalue(o, s) + return self:addk(fs, o, o) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:numberK(fs, r) + assert (type(r)=="number") + local o = {} -- TObject + self:setnvalue(o, r) + return self:addk(fs, o, o) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:boolK(fs, r) + assert (type(r)=="boolean") + local o = {} -- TObject + self:setnvalue(o, r) + return self:addk(fs, o, o) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:nilK(fs) + local k, v = {}, {} -- TObject + self:setnilvalue(v) + self:sethvalue(k, fs.h) -- cannot use nil as key; instead use table itself + return self:addk(fs, k, v) +end + + +--FF 5.1 +function luaK:setreturns (fs, e, nresults) + if e.k == "VCALL" then -- expression is an open function call? + luaP:SETARG_C(self:getcode(fs, e), nresults + 1) + elseif e.k == "VVARARG" then + luaP:SETARG_B (self:getcode (fs, e), nresults + 1) + luaP:SETARG_A (self:getcode (fs, e), fs.freereg) + self:reserveregs (fs, 1) + end +end + +--FF 5.1 +function luaK:setmultret (fs, e) + self:setreturns (fs, e, self.LUA_MULTRET) +end + +--FF 5.1 +function luaK:setoneret (fs, e) + if e.k == "VCALL" then -- expression is an open function call? + e.k = "VNONRELOC" + e.info = luaP:GETARG_A(self:getcode(fs, e)) + elseif e.k == "VVARARG" then + luaP:SETARG_B (self:getcode (fs, e), 2) + e.k = "VRELOCABLE" + end +end + + +------------------------------------------------------------------------ +--FF deprecated in 5.1 +------------------------------------------------------------------------ +function luaK:setcallreturns(fs, e, nresults) + assert (false, "setcallreturns deprecated") + --print "SCR:" + --printv(e) + --printv(self:getcode(fs, e)) + if e.k == "VCALL" then -- expression is an open function call? + luaP:SETARG_C(self:getcode(fs, e), nresults + 1) + if nresults == 1 then -- 'regular' expression? + e.k = "VNONRELOC" + e.info = luaP:GETARG_A(self:getcode(fs, e)) + end + elseif e.k == "VVARARG" then + --printf("Handle vararg return on expr %s, whose code is %s", + -- tostringv(e), tostringv(self:getcode(fs, e))) + if nresults == 1 then + luaP:SETARG_B (self:getcode (fs, e), 2) + e.k = "VRELOCABLE" +--FIXME: why no SETARG_A??? + else + luaP:SETARG_B (self:getcode (fs, e), nresults + 1) + luaP:SETARG_A (self:getcode (fs, e), fs.freereg) + self:reserveregs (fs, 1) + --printf("Now code is %s", tostringv(self:getcode(fs, e))) + end + end +end + +------------------------------------------------------------------------ +-- Ajoute le code pour effectuer l'extraction de la locvar/upval/globvar +-- /idx, sachant +------------------------------------------------------------------------ +function luaK:dischargevars(fs, e) +--printf("\ndischargevars\n") + local k = e.k + if k == "VLOCAL" then + e.k = "VNONRELOC" + elseif k == "VUPVAL" then + e.info = self:codeABC(fs, "OP_GETUPVAL", 0, e.info, 0) + e.k = "VRELOCABLE" + elseif k == "VGLOBAL" then + e.info = self:codeABx(fs, "OP_GETGLOBAL", 0, e.info) + e.k = "VRELOCABLE" + elseif k == "VINDEXED" then + self:freereg(fs, e.aux) + self:freereg(fs, e.info) + e.info = self:codeABC(fs, "OP_GETTABLE", 0, e.info, e.aux) + e.k = "VRELOCABLE" + elseif k == "VCALL" or k == "VVARARG" then + self:setoneret(fs, e) + else + -- there is one value available (somewhere) + end +--printf("\n/dischargevars\n") +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:code_label(fs, A, b, jump) + self:getlabel(fs) -- those instructions may be jump targets + return self:codeABC(fs, "OP_LOADBOOL", A, b, jump) +end + +------------------------------------------------------------------------ +-- FF updated 5.1 +------------------------------------------------------------------------ +function luaK:discharge2reg(fs, e, reg) + self:dischargevars(fs, e) + local k = e.k + if k == "VNIL" then + self:_nil(fs, reg, 1) + elseif k == "VFALSE" or k == "VTRUE" then + self:codeABC(fs, "OP_LOADBOOL", reg, (e.k == "VTRUE") and 1 or 0, 0) + elseif k == "VKNUM" then + self:codeABx (fs, "OP_LOADK", reg, self:numberK(fs, e.nval)) + elseif k == "VK" then + self:codeABx(fs, "OP_LOADK", reg, e.info) + elseif k == "VRELOCABLE" then + local pc = self:getcode(fs, e) + luaP:SETARG_A(pc, reg) + elseif k == "VNONRELOC" then + if reg ~= e.info then + self:codeABC(fs, "OP_MOVE", reg, e.info, 0) + end + else + assert(e.k == "VVOID" or e.k == "VJMP") + return -- nothing to do... + end + e.info = reg + e.k = "VNONRELOC" +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:discharge2anyreg(fs, e) + if e.k ~= "VNONRELOC" then + self:reserveregs(fs, 1) + self:discharge2reg(fs, e, fs.freereg - 1) + end +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:exp2reg(fs, e, reg) + self:discharge2reg(fs, e, reg) + if e.k == "VJMP" then + e.t = self:concat(fs, e.t, e.info) -- put this jump in 't' list + end + if self:hasjumps(e) then + local final -- position after whole expression + local p_f = self.NO_JUMP -- position of an eventual LOAD false + local p_t = self.NO_JUMP -- position of an eventual LOAD true + if self:need_value(fs, e.t, 1) or self:need_value(fs, e.f, 0) then + local fj = self.NO_JUMP -- first jump (over LOAD ops.) + if e.k ~= "VJMP" then fj = self:jump(fs) end + p_f = self:code_label(fs, reg, 0, 1) + p_t = self:code_label(fs, reg, 1, 0) + self:patchtohere(fs, fj) + end + final = self:getlabel(fs) + self:patchlistaux(fs, e.f, final, reg, p_f) + self:patchlistaux(fs, e.t, final, reg, p_t) + end + e.f, e.t = self.NO_JUMP, self.NO_JUMP + e.info = reg + e.k = "VNONRELOC" +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:exp2nextreg(fs, e) + self:dischargevars(fs, e) + --[FF] Allready in place (added for expr.Stat) + if e.k == "VNONRELOC" and e.info == fs.freereg then + --printf("Expression already in next reg %i: %s", fs.freereg, tostringv(e)) + return end + self:freeexp(fs, e) + self:reserveregs(fs, 1) + self:exp2reg(fs, e, fs.freereg - 1) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:exp2anyreg(fs, e) + --printf("exp2anyregs(e=%s)", tostringv(e)) + self:dischargevars(fs, e) + if e.k == "VNONRELOC" then + if not self:hasjumps(e) then -- exp is already in a register + return e.info + end + if e.info >= fs.nactvar then -- reg. is not a local? + self:exp2reg(fs, e, e.info) -- put value on it + return e.info + end + end + self:exp2nextreg(fs, e) -- default + return e.info +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:exp2val(fs, e) + if self:hasjumps(e) then + self:exp2anyreg(fs, e) + else + self:dischargevars(fs, e) + end +end + +------------------------------------------------------------------------ +-- FF updated 5.1 +------------------------------------------------------------------------ +function luaK:exp2RK(fs, e) + self:exp2val(fs, e) + local k = e.k + if k=="VNIL" or k=="VTRUE" or k=="VFALSE" or k=="VKNUM" then + if fs.nk <= luaP.MAXINDEXRK then + if k=="VNIL" then e.info = self:nilK(fs) + elseif k=="VKNUM" then e.info = self:numberK (fs, e.nval) + else e.info = self:boolK(fs, e.k=="VTRUE") end + e.k = "VK" + return luaP:RKASK(e.info) + end + elseif k == "VK" then + if e.info <= luaP.MAXINDEXRK then -- constant fit in argC? + return luaP:RKASK (e.info) + end + end + -- not a constant in the right range: put it in a register + return self:exp2anyreg(fs, e) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:storevar(fs, var, exp) + --print("STOREVAR") + --printf("var=%s", tostringv(var)) + --printf("exp=%s", tostringv(exp)) + + local k = var.k + if k == "VLOCAL" then + self:freeexp(fs, exp) + self:exp2reg(fs, exp, var.info) + return + elseif k == "VUPVAL" then + local e = self:exp2anyreg(fs, exp) + self:codeABC(fs, "OP_SETUPVAL", e, var.info, 0) + elseif k == "VGLOBAL" then + --printf("store global, exp=%s", tostringv(exp)) + local e = self:exp2anyreg(fs, exp) + self:codeABx(fs, "OP_SETGLOBAL", e, var.info) + elseif k == "VINDEXED" then + local e = self:exp2RK(fs, exp) + self:codeABC(fs, "OP_SETTABLE", var.info, var.aux, e) + else + assert(0) -- invalid var kind to store + end + self:freeexp(fs, exp) + --print("/STOREVAR") +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:_self(fs, e, key) + self:exp2anyreg(fs, e) + self:freeexp(fs, e) + local func = fs.freereg + self:reserveregs(fs, 2) + self:codeABC(fs, "OP_SELF", func, e.info, self:exp2RK(fs, key)) + self:freeexp(fs, key) + e.info = func + e.k = "VNONRELOC" +end + +------------------------------------------------------------------------ +-- FF updated 5.1 +------------------------------------------------------------------------ +function luaK:invertjump(fs, e) + --printf("invertjump on jump instruction #%i", e.info) + --printv(self:getcode(fs, e)) + local pc = self:getjumpcontrol(fs, e.info) + assert(luaP:testOpMode(luaP:GET_OPCODE(pc), "OpModeT") and + luaP:GET_OPCODE(pc) ~= "OP_TESTSET" and + luaP:GET_OPCODE(pc) ~= "OP_TEST") + --printf("Before invert:") + --printv(pc) + luaP:SETARG_A(pc, (luaP:GETARG_A(pc) == 0) and 1 or 0) + --printf("After invert:") + --printv(pc) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:jumponcond(fs, e, cond) + if e.k == "VRELOCABLE" then + local ie = self:getcode(fs, e) + if luaP:GET_OPCODE(ie) == "OP_NOT" then + fs.pc = fs.pc - 1 -- remove previous OP_NOT + return self:condjump(fs, "OP_TEST", luaP:GETARG_B(ie), 0, + cond and 0 or 1) + end + -- else go through + end + self:discharge2anyreg(fs, e) + self:freeexp(fs, e) + return self:condjump(fs, "OP_TESTSET", luaP.NO_REG, e.info, cond and 1 or 0) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:goiftrue(fs, e) + local pc -- pc of last jump + self:dischargevars(fs, e) + local k = e.k + if k == "VK" or k == "VTRUE" or k == "VKNUM" then + pc = self.NO_JUMP -- always true; do nothing + elseif k == "VFALSE" then + pc = self:jump(fs) -- always jump + elseif k == "VJMP" then + self:invertjump(fs, e) + pc = e.info + else + pc = self:jumponcond(fs, e, false) + end + e.f = self:concat(fs, e.f, pc) -- insert last jump in 'f' list + self:patchtohere(fs, e.t) + e.t = self.NO_JUMP +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:goiffalse(fs, e) + local pc -- pc of last jump + self:dischargevars(fs, e) + local k = e.k + if k == "VNIL" or k == "VFALSE"then + pc = self.NO_JUMP -- always false; do nothing + elseif k == "VTRUE" then + pc = self:jump(fs) -- always jump + elseif k == "VJMP" then + pc = e.info + else + pc = self:jumponcond(fs, e, true) + end + e.t = self:concat(fs, e.t, pc) -- insert last jump in 't' list + self:patchtohere(fs, e.f) + e.f = self.NO_JUMP +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:codenot(fs, e) + self:dischargevars(fs, e) + local k = e.k + if k == "VNIL" or k == "VFALSE" then + e.k = "VTRUE" + elseif k == "VK" or k == "VKNUM" or k == "VTRUE" then + e.k = "VFALSE" + elseif k == "VJMP" then + self:invertjump(fs, e) + elseif k == "VRELOCABLE" or k == "VNONRELOC" then + self:discharge2anyreg(fs, e) + self:freeexp(fs, e) + e.info = self:codeABC(fs, "OP_NOT", 0, e.info, 0) + e.k = "VRELOCABLE" + else + assert(0) -- cannot happen + end + -- interchange true and false lists + e.f, e.t = e.t, e.f + self:removevalues(fs, e.f) + self:removevalues(fs, e.t) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:indexed(fs, t, k) + t.aux = self:exp2RK(fs, k) + t.k = "VINDEXED" +end + +--FF 5.1 +function luaK:constfolding (op, e1, e2) + if not self:isnumeral(e1) or not self:isnumeral(e2) then return false end + local v1, v2, e, r = e1.nval, e2 and e2.nval, nil + if op == "OP_ADD" then r = v1+v2 + elseif op == "OP_SUB" then r = v1-v2 + elseif op == "OP_MUL" then r = v1*v2 + elseif op == "OP_DIV" then if v2==0 then return false end r = v1/v2 + elseif op == "OP_MOD" then if v2==0 then return false end r = v1%v2 + elseif op == "OP_POW" then r = v1^v2 + elseif op == "OP_UNM" then r = -v1 + elseif op == "OP_LEN" then return false + else assert (false, "Unknown numeric value") end + e1.nval = r + return true +end + +--FF 5.1 +function luaK:codearith (fs, op, e1, e2) + if self:constfolding (op, e1, e2) then return else + local o1 = self:exp2RK (fs, e1) + local o2 = 0 + if op ~= "OP_UNM" and op ~= "OP_LEN" then + o2 = self:exp2RK (fs, e2) end + self:freeexp(fs, e2) + self:freeexp(fs, e1) + e1.info = self:codeABC (fs, op, 0, o1, o2) + e1.k = "VRELOCABLE" + end +end + +--FF 5.1 +function luaK:codecomp (fs, op, cond, e1, e2) + assert (type (cond) == "boolean") + local o1 = self:exp2RK (fs, e1) + local o2 = self:exp2RK (fs, e2) + self:freeexp (fs, e2) + self:freeexp (fs, e1) + if not cond and op ~= "OP_EQ" then + local temp = o1; o1=o2; o2=temp cond = true end + e1.info = self:condjump (fs, op, cond and 1 or 0, o1, o2) + e1.k = "VJMP" +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:prefix (fs, op, e) + local e2 = { t = self.NO_JUMP; f = self.NO_JUMP; + k = "VKNUM"; nval = 0 } + if op == "unm" then + if e.k == "VK" then + self:exp2anyreg (fs, e) end + self:codearith (fs, "OP_UNM", e, e2) + elseif op == "not" then + self:codenot (fs, e) + elseif op == "len" then + self:exp2anyreg (fs, e) + self:codearith (fs, "OP_LEN", e, e2) + else + assert (false, "Unknown unary operator") + end +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:infix (fs, op, v) + if op == "and" then + self:goiftrue(fs, v) + elseif op == "or" then + self:goiffalse(fs, v) + elseif op == "concat" then + self:exp2nextreg(fs, v) -- operand must be on the 'stack' + else + if not self:isnumeral (v) then self:exp2RK(fs, v) end + end +end + +------------------------------------------------------------------------ +-- +-- grep "ORDER OPR" if you change these enums +------------------------------------------------------------------------ +luaK.arith_opc = { -- done as a table lookup instead of a calc + add = "OP_ADD", + sub = "OP_SUB", + mul = "OP_MUL", + mod = "OP_MOD", + div = "OP_DIV", + pow = "OP_POW", + len = "OP_LEN", + ["not"] = "OP_NOT" +} +luaK.test_opc = { -- was ops[] in the codebinop function + eq = {opc="OP_EQ", cond=true}, + lt = {opc="OP_LT", cond=true}, + le = {opc="OP_LE", cond=true}, + + -- Pseudo-ops, with no metatable equivalent: + ne = {opc="OP_EQ", cond=false}, + gt = {opc="OP_LT", cond=false}, + ge = {opc="OP_LE", cond=false} +} + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:posfix(fs, op, e1, e2) + if op == "and" then + assert(e1.t == self.NO_JUMP) -- list must be closed + self:dischargevars(fs, e2) + e2.f = self:concat(fs, e2.f, e1.f) + for k,v in pairs(e2) do e1[k]=v end -- *e1 = *e2 + elseif op == "or" then + assert(e1.f == self.NO_JUMP) -- list must be closed + self:dischargevars(fs, e2) + e2.t = self:concat(fs, e2.t, e1.t) + for k,v in pairs(e2) do e1[k]=v end -- *e1 = *e2 + elseif op == "concat" then + self:exp2val(fs, e2) + if e2.k == "VRELOCABLE" + and luaP:GET_OPCODE(self:getcode(fs, e2)) == "OP_CONCAT" then + assert(e1.info == luaP:GETARG_B(self:getcode(fs, e2)) - 1) + self:freeexp(fs, e1) + luaP:SETARG_B(self:getcode(fs, e2), e1.info) + e1.k = "VRELOCABLE"; e1.info = e2.info + else + self:exp2nextreg(fs, e2) + self:codearith (fs, "OP_CONCAT", e1, e2) + end + else + local opc = self.arith_opc[op] + if opc then self:codearith (fs, opc, e1, e2) else + opc = self.test_opc[op] or error ("Unknown operator "..op) + self:codecomp (fs, opc.opc, opc.cond, e1, e2) + end + end +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:fixline(fs, line) + --assert (line) + if not line then + --print(debug.traceback "fixline (line == nil)") + end + fs.f.lineinfo[fs.pc - 1] = line or 0 +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:code(fs, i, line) + if not line then + line = 0 + --print(debug.traceback "line == nil") + end + local f = fs.f + + do -- print it + local params = { } + for _,x in ipairs{"A","B","Bx", "sBx", "C"} do + if i[x] then table.insert (params, string.format ("%s=%i", x, i[x])) end + end + debugf ("[code:\t%s\t%s]", luaP.opnames[i.OP], table.concat (params, ", ")) + end + + self:dischargejpc(fs) -- 'pc' will change + + f.code[fs.pc] = i + f.lineinfo[fs.pc] = line + + if line == 0 then + f.lineinfo[fs.pc] = fs.lastline + if fs.lastline == 0 then + --print(debug.traceback()) + end + end + + if f.lineinfo[fs.pc] == 0 then + f.lineinfo[fs.pc] = 42 + end + + local pc = fs.pc + fs.pc = fs.pc + 1 + return pc +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:codeABC(fs, o, a, b, c) + assert(luaP:getOpMode(o) == "iABC", o.." is not an ABC operation") + --assert getbmode(o) ~= opargn or b == 0 + --assert getcmode(o) ~= opargn or c == 0 + --FF + --return self:code(fs, luaP:CREATE_ABC(o, a, b, c), fs.ls.lastline) + return self:code(fs, luaP:CREATE_ABC(o, a, b, c), fs.lastline) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:codeABx(fs, o, a, bc) + assert(luaP:getOpMode(o) == "iABx" or luaP:getOpMode(o) == "iAsBx") + --assert getcmode(o) == opargn + --FF + --return self:code(fs, luaP:CREATE_ABx(o, a, bc), fs.ls.lastline) + return self:code(fs, luaP:CREATE_ABx(o, a, bc), fs.lastline) +end + +------------------------------------------------------------------------ +-- +------------------------------------------------------------------------ +function luaK:setlist (fs, base, nelems, tostore) + local c = math.floor ((nelems-1) / luaP.LFIELDS_PER_FLUSH + 1) + local b = tostore == self.LUA_MULTRET and 0 or tostore + assert (tostore ~= 0) + if c <= luaP.MAXARG_C then self:codeABC (fs, "OP_SETLIST", base, b, c) + else + self:codeABC (fs, "OP_SETLIST", base, b, 0) + self:code (fs, c, fs.lastline)--FIXME + end + fs.freereg = base + 1 +end + +return luaK \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/ldump.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/ldump.lua new file mode 100644 index 000000000..6ac76179f --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/ldump.lua @@ -0,0 +1,448 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2005-2013 Kein-Hong Man, Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Kein-Hong Man - Initial implementation for Lua 5.0, part of Yueliang +-- Fabien Fleutot - Port to Lua 5.1, integration with Metalua +-- +------------------------------------------------------------------------------- + +--[[-------------------------------------------------------------------- + + ldump.lua + Save bytecodes in Lua + This file is part of Yueliang. + + Copyright (c) 2005 Kein-Hong Man + The COPYRIGHT file describes the conditions + under which this software may be distributed. + +------------------------------------------------------------------------ + + [FF] Slightly modified, mainly to produce Lua 5.1 bytecode. + +----------------------------------------------------------------------]] + +--[[-------------------------------------------------------------------- +-- Notes: +-- * LUA_NUMBER (double), byte order (little endian) and some other +-- header values hard-coded; see other notes below... +-- * One significant difference is that instructions are still in table +-- form (with OP/A/B/C/Bx fields) and luaP:Instruction() is needed to +-- convert them into 4-char strings +-- * Deleted: +-- luaU:DumpVector: folded into DumpLines, DumpCode +-- * Added: +-- luaU:endianness() (from lundump.c) +-- luaU:make_setS: create a chunk writer that writes to a string +-- luaU:make_setF: create a chunk writer that writes to a file +-- (lua.h contains a typedef for a Chunkwriter pointer, and +-- a Lua-based implementation exists, writer() in lstrlib.c) +-- luaU:from_double(x): encode double value for writing +-- luaU:from_int(x): encode integer value for writing +-- (error checking is limited for these conversion functions) +-- (double conversion does not support denormals or NaNs) +-- luaU:ttype(o) (from lobject.h) +----------------------------------------------------------------------]] + +local luaP = require 'metalua.compiler.bytecode.lopcodes' + +local M = { } + +local format = { } +format.header = string.dump(function()end):sub(1, 12) +format.little_endian, format.int_size, +format.size_t_size, format.instr_size, +format.number_size, format.integral = format.header:byte(7, 12) +format.little_endian = format.little_endian~=0 +format.integral = format.integral ~=0 + +assert(format.integral or format.number_size==8, "Number format not supported by dumper") +assert(format.little_endian, "Big endian architectures not supported by dumper") + +--requires luaP +local luaU = { } +M.luaU = luaU + +luaU.format = format + +-- constants used by dumper +luaU.LUA_TNIL = 0 +luaU.LUA_TBOOLEAN = 1 +luaU.LUA_TNUMBER = 3 -- (all in lua.h) +luaU.LUA_TSTRING = 4 +luaU.LUA_TNONE = -1 + +-- definitions for headers of binary files +--luaU.LUA_SIGNATURE = "\27Lua" -- binary files start with "Lua" +--luaU.VERSION = 81 -- 0x50; last format change was in 5.0 +--luaU.FORMAT_VERSION = 0 -- 0 is official version. yeah I know I'm a liar. + +-- a multiple of PI for testing native format +-- multiplying by 1E7 gives non-trivial integer values +--luaU.TEST_NUMBER = 3.14159265358979323846E7 + +--[[-------------------------------------------------------------------- +-- Additional functions to handle chunk writing +-- * to use make_setS and make_setF, see test_ldump.lua elsewhere +----------------------------------------------------------------------]] + +------------------------------------------------------------------------ +-- works like the lobject.h version except that TObject used in these +-- scripts only has a 'value' field, no 'tt' field (native types used) +------------------------------------------------------------------------ +function luaU:ttype(o) + local tt = type(o.value) + if tt == "number" then return self.LUA_TNUMBER + elseif tt == "string" then return self.LUA_TSTRING + elseif tt == "nil" then return self.LUA_TNIL + elseif tt == "boolean" then return self.LUA_TBOOLEAN + else + return self.LUA_TNONE -- the rest should not appear + end +end + +------------------------------------------------------------------------ +-- create a chunk writer that writes to a string +-- * returns the writer function and a table containing the string +-- * to get the final result, look in buff.data +------------------------------------------------------------------------ +function luaU:make_setS() + local buff = {} + buff.data = "" + local writer = + function(s, buff) -- chunk writer + if not s then return end + buff.data = buff.data..s + end + return writer, buff +end + +------------------------------------------------------------------------ +-- create a chunk writer that writes to a file +-- * returns the writer function and a table containing the file handle +-- * if a nil is passed, then writer should close the open file +------------------------------------------------------------------------ +function luaU:make_setF(filename) + local buff = {} + buff.h = io.open(filename, "wb") + if not buff.h then return nil end + local writer = + function(s, buff) -- chunk writer + if not buff.h then return end + if not s then buff.h:close(); return end + buff.h:write(s) + end + return writer, buff +end + +----------------------------------------------------------------------- +-- converts a IEEE754 double number to an 8-byte little-endian string +-- * luaU:from_double() and luaU:from_int() are from ChunkBake project +-- * supports +/- Infinity, but not denormals or NaNs +----------------------------------------------------------------------- +function luaU:from_double(x) + local function grab_byte(v) + return math.floor(v / 256), + string.char(math.mod(math.floor(v), 256)) + end + local sign = 0 + if x < 0 then sign = 1; x = -x end + local mantissa, exponent = math.frexp(x) + if x == 0 then -- zero + mantissa, exponent = 0, 0 + elseif x == 1/0 then + mantissa, exponent = 0, 2047 + else + mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53) + exponent = exponent + 1022 + end + local v, byte = "" -- convert to bytes + x = mantissa + for i = 1,6 do + x, byte = grab_byte(x); v = v..byte -- 47:0 + end + x, byte = grab_byte(exponent * 16 + x); v = v..byte -- 55:48 + x, byte = grab_byte(sign * 128 + x); v = v..byte -- 63:56 + return v +end + +----------------------------------------------------------------------- +-- converts a number to a little-endian 32-bit integer string +-- * input value assumed to not overflow, can be signed/unsigned +----------------------------------------------------------------------- +function luaU:from_int(x, size) + local v = "" + x = math.floor(x) + if x >= 0 then + for i = 1, size do + v = v..string.char(math.mod(x, 256)); x = math.floor(x / 256) + end + else -- x < 0 + x = -x + local carry = 1 + for i = 1, size do + local c = 255 - math.mod(x, 256) + carry + if c == 256 then c = 0; carry = 1 else carry = 0 end + v = v..string.char(c); x = math.floor(x / 256) + end + end + return v +end + +--[[-------------------------------------------------------------------- +-- Functions to make a binary chunk +-- * many functions have the size parameter removed, since output is +-- in the form of a string and some sizes are implicit or hard-coded +-- * luaU:DumpVector has been deleted (used in DumpCode & DumpLines) +----------------------------------------------------------------------]] + +------------------------------------------------------------------------ +-- dump a block of literal bytes +------------------------------------------------------------------------ +function luaU:DumpLiteral(s, D) self:DumpBlock(s, D) end + +--[[-------------------------------------------------------------------- +-- struct DumpState: +-- L -- lua_State (not used in this script) +-- write -- lua_Chunkwriter (chunk writer function) +-- data -- void* (chunk writer context or data already written) +----------------------------------------------------------------------]] + +------------------------------------------------------------------------ +-- dumps a block of bytes +-- * lua_unlock(D.L), lua_lock(D.L) deleted +------------------------------------------------------------------------ +function luaU:DumpBlock(b, D) D.write(b, D.data) end + +------------------------------------------------------------------------ +-- dumps a single byte +------------------------------------------------------------------------ +function luaU:DumpByte(y, D) + self:DumpBlock(string.char(y), D) +end + +------------------------------------------------------------------------ +-- dumps a signed integer of size `format.int_size` (for int) +------------------------------------------------------------------------ +function luaU:DumpInt(x, D) + self:DumpBlock(self:from_int(x, format.int_size), D) +end + +------------------------------------------------------------------------ +-- dumps an unsigned integer of size `format.size_t_size` (for size_t) +------------------------------------------------------------------------ +function luaU:DumpSize(x, D) + self:DumpBlock(self:from_int(x, format.size_t_size), D) +end + +------------------------------------------------------------------------ +-- dumps a LUA_NUMBER; can be an int or double depending on the VM. +------------------------------------------------------------------------ +function luaU:DumpNumber(x, D) + if format.integral then + self:DumpBlock(self:from_int(x, format.number_size), D) + else + self:DumpBlock(self:from_double(x), D) + end +end + +------------------------------------------------------------------------ +-- dumps a Lua string +------------------------------------------------------------------------ +function luaU:DumpString(s, D) + if s == nil then + self:DumpSize(0, D) + else + s = s.."\0" -- include trailing '\0' + self:DumpSize(string.len(s), D) + self:DumpBlock(s, D) + end +end + +------------------------------------------------------------------------ +-- dumps instruction block from function prototype +------------------------------------------------------------------------ +function luaU:DumpCode(f, D) + local n = f.sizecode + self:DumpInt(n, D) + --was DumpVector + for i = 0, n - 1 do + self:DumpBlock(luaP:Instruction(f.code[i]), D) + end +end + +------------------------------------------------------------------------ +-- dumps local variable names from function prototype +------------------------------------------------------------------------ +function luaU:DumpLocals(f, D) + local n = f.sizelocvars + self:DumpInt(n, D) + for i = 0, n - 1 do + -- Dirty temporary fix: + -- `Stat{ } keeps properly count of the number of local vars, + -- but fails to keep score of their debug info (names). + -- It therefore might happen that #f.localvars < f.sizelocvars, or + -- that a variable's startpc and endpc fields are left unset. + -- FIXME: This might not be needed anymore, check the bug report + -- by J. Belmonte. + local var = f.locvars[i] + if not var then break end + -- printf("[DUMPLOCALS] dumping local var #%i = %s", i, table.tostring(var)) + self:DumpString(var.varname, D) + self:DumpInt(var.startpc or 0, D) + self:DumpInt(var.endpc or 0, D) + end +end + +------------------------------------------------------------------------ +-- dumps line information from function prototype +------------------------------------------------------------------------ +function luaU:DumpLines(f, D) + local n = f.sizelineinfo + self:DumpInt(n, D) + --was DumpVector + for i = 0, n - 1 do + self:DumpInt(f.lineinfo[i], D) -- was DumpBlock + --print(i, f.lineinfo[i]) + end +end + +------------------------------------------------------------------------ +-- dump upvalue names from function prototype +------------------------------------------------------------------------ +function luaU:DumpUpvalues(f, D) + local n = f.sizeupvalues + self:DumpInt(n, D) + for i = 0, n - 1 do + self:DumpString(f.upvalues[i], D) + end +end + +------------------------------------------------------------------------ +-- dump constant pool from function prototype +-- * nvalue(o) and tsvalue(o) macros removed +------------------------------------------------------------------------ +function luaU:DumpConstants(f, D) + local n = f.sizek + self:DumpInt(n, D) + for i = 0, n - 1 do + local o = f.k[i] -- TObject + local tt = self:ttype(o) + assert (tt >= 0) + self:DumpByte(tt, D) + if tt == self.LUA_TNUMBER then + self:DumpNumber(o.value, D) + elseif tt == self.LUA_TSTRING then + self:DumpString(o.value, D) + elseif tt == self.LUA_TBOOLEAN then + self:DumpByte (o.value and 1 or 0, D) + elseif tt == self.LUA_TNIL then + else + assert(false) -- cannot happen + end + end +end + + +function luaU:DumpProtos (f, D) + local n = f.sizep + assert (n) + self:DumpInt(n, D) + for i = 0, n - 1 do + self:DumpFunction(f.p[i], f.source, D) + end +end + +function luaU:DumpDebug(f, D) + self:DumpLines(f, D) + self:DumpLocals(f, D) + self:DumpUpvalues(f, D) +end + + +------------------------------------------------------------------------ +-- dump child function prototypes from function prototype +--FF completely reworked for 5.1 format +------------------------------------------------------------------------ +function luaU:DumpFunction(f, p, D) + -- print "Dumping function:" + -- table.print(f, 60) + + local source = f.source + if source == p then source = nil end + self:DumpString(source, D) + self:DumpInt(f.lineDefined, D) + self:DumpInt(f.lastLineDefined or 42, D) + self:DumpByte(f.nups, D) + self:DumpByte(f.numparams, D) + self:DumpByte(f.is_vararg, D) + self:DumpByte(f.maxstacksize, D) + self:DumpCode(f, D) + self:DumpConstants(f, D) + self:DumpProtos( f, D) + self:DumpDebug(f, D) +end + +------------------------------------------------------------------------ +-- dump Lua header section (some sizes hard-coded) +--FF: updated for version 5.1 +------------------------------------------------------------------------ +function luaU:DumpHeader(D) + self:DumpLiteral(format.header, D) +end + +------------------------------------------------------------------------ +-- dump function as precompiled chunk +-- * w, data are created from make_setS, make_setF +--FF: suppressed extraneous [L] param +------------------------------------------------------------------------ +function luaU:dump (Main, w, data) + local D = {} -- DumpState + D.write = w + D.data = data + self:DumpHeader(D) + self:DumpFunction(Main, nil, D) + -- added: for a chunk writer writing to a file, this final call with + -- nil data is to indicate to the writer to close the file + D.write(nil, D.data) +end + +------------------------------------------------------------------------ +-- find byte order (from lundump.c) +-- * hard-coded to little-endian +------------------------------------------------------------------------ +function luaU:endianness() + return 1 +end + +-- FIXME: ugly concat-base generation in [make_setS], bufferize properly! +function M.dump_string (proto) + local writer, buff = luaU:make_setS() + luaU:dump (proto, writer, buff) + return buff.data +end + +-- FIXME: [make_setS] sucks, perform synchronous file writing +-- Now unused +function M.dump_file (proto, filename) + local writer, buff = luaU:make_setS() + luaU:dump (proto, writer, buff) + local file = io.open (filename, "wb") + file:write (buff.data) + io.close(file) + --if UNIX_SHARPBANG then os.execute ("chmod a+x "..filename) end +end + +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/lopcodes.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/lopcodes.lua new file mode 100644 index 000000000..e49285e6f --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/lopcodes.lua @@ -0,0 +1,442 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2005-2013 Kein-Hong Man, Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Kein-Hong Man - Initial implementation for Lua 5.0, part of Yueliang +-- Fabien Fleutot - Port to Lua 5.1, integration with Metalua +-- +------------------------------------------------------------------------------- + +--[[-------------------------------------------------------------------- + + $Id$ + + lopcodes.lua + Lua 5 virtual machine opcodes in Lua + This file is part of Yueliang. + + Copyright (c) 2005 Kein-Hong Man + The COPYRIGHT file describes the conditions + under which this software may be distributed. + + See the ChangeLog for more information. + +------------------------------------------------------------------------ + + [FF] Slightly modified, mainly to produce Lua 5.1 bytecode. + +----------------------------------------------------------------------]] + +--[[-------------------------------------------------------------------- +-- Notes: +-- * an Instruction is a table with OP, A, B, C, Bx elements; this +-- should allow instruction handling to work with doubles and ints +-- * Added: +-- luaP:Instruction(i): convert field elements to a 4-char string +-- luaP:DecodeInst(x): convert 4-char string into field elements +-- * WARNING luaP:Instruction outputs instructions encoded in little- +-- endian form and field size and positions are hard-coded +----------------------------------------------------------------------]] + +local function debugf() end + +local luaP = { } + +--[[ +=========================================================================== + We assume that instructions are unsigned numbers. + All instructions have an opcode in the first 6 bits. + Instructions can have the following fields: + 'A' : 8 bits + 'B' : 9 bits + 'C' : 9 bits + 'Bx' : 18 bits ('B' and 'C' together) + 'sBx' : signed Bx + + A signed argument is represented in excess K; that is, the number + value is the unsigned value minus K. K is exactly the maximum value + for that argument (so that -max is represented by 0, and +max is + represented by 2*max), which is half the maximum for the corresponding + unsigned argument. +=========================================================================== +--]] + +luaP.OpMode = {"iABC", "iABx", "iAsBx"} -- basic instruction format + +------------------------------------------------------------------------ +-- size and position of opcode arguments. +-- * WARNING size and position is hard-coded elsewhere in this script +------------------------------------------------------------------------ +luaP.SIZE_C = 9 +luaP.SIZE_B = 9 +luaP.SIZE_Bx = luaP.SIZE_C + luaP.SIZE_B +luaP.SIZE_A = 8 + +luaP.SIZE_OP = 6 + +luaP.POS_C = luaP.SIZE_OP +luaP.POS_B = luaP.POS_C + luaP.SIZE_C +luaP.POS_Bx = luaP.POS_C +luaP.POS_A = luaP.POS_B + luaP.SIZE_B + +--FF from 5.1 +luaP.BITRK = 2^(luaP.SIZE_B - 1) +function luaP:ISK(x) return x >= self.BITRK end +luaP.MAXINDEXRK = luaP.BITRK - 1 +function luaP:RKASK(x) + if x < self.BITRK then return x+self.BITRK else return x end +end + + + +------------------------------------------------------------------------ +-- limits for opcode arguments. +-- we use (signed) int to manipulate most arguments, +-- so they must fit in BITS_INT-1 bits (-1 for sign) +------------------------------------------------------------------------ +-- removed "#if SIZE_Bx < BITS_INT-1" test, assume this script is +-- running on a Lua VM with double or int as LUA_NUMBER + +luaP.MAXARG_Bx = math.ldexp(1, luaP.SIZE_Bx) - 1 +luaP.MAXARG_sBx = math.floor(luaP.MAXARG_Bx / 2) -- 'sBx' is signed + +luaP.MAXARG_A = math.ldexp(1, luaP.SIZE_A) - 1 +luaP.MAXARG_B = math.ldexp(1, luaP.SIZE_B) - 1 +luaP.MAXARG_C = math.ldexp(1, luaP.SIZE_C) - 1 + +-- creates a mask with 'n' 1 bits at position 'p' +-- MASK1(n,p) deleted +-- creates a mask with 'n' 0 bits at position 'p' +-- MASK0(n,p) deleted + +--[[-------------------------------------------------------------------- + Visual representation for reference: + + 31 | | | 0 bit position + +-----+-----+-----+----------+ + | B | C | A | Opcode | iABC format + +-----+-----+-----+----------+ + - 9 - 9 - 8 - 6 - field sizes + +-----+-----+-----+----------+ + | [s]Bx | A | Opcode | iABx | iAsBx format + +-----+-----+-----+----------+ +----------------------------------------------------------------------]] + +------------------------------------------------------------------------ +-- the following macros help to manipulate instructions +-- * changed to a table object representation, very clean compared to +-- the [nightmare] alternatives of using a number or a string +------------------------------------------------------------------------ + +-- these accept or return opcodes in the form of string names +function luaP:GET_OPCODE(i) return self.ROpCode[i.OP] end +function luaP:SET_OPCODE(i, o) i.OP = self.OpCode[o] end + +function luaP:GETARG_A(i) return i.A end +function luaP:SETARG_A(i, u) i.A = u end + +function luaP:GETARG_B(i) return i.B end +function luaP:SETARG_B(i, b) i.B = b end + +function luaP:GETARG_C(i) return i.C end +function luaP:SETARG_C(i, b) i.C = b end + +function luaP:GETARG_Bx(i) return i.Bx end +function luaP:SETARG_Bx(i, b) i.Bx = b end + +function luaP:GETARG_sBx(i) return i.Bx - self.MAXARG_sBx end +function luaP:SETARG_sBx(i, b) i.Bx = b + self.MAXARG_sBx end + +function luaP:CREATE_ABC(o,a,b,c) + return {OP = self.OpCode[o], A = a, B = b, C = c} +end + +function luaP:CREATE_ABx(o,a,bc) + return {OP = self.OpCode[o], A = a, Bx = bc} +end + +------------------------------------------------------------------------ +-- Bit shuffling stuffs +------------------------------------------------------------------------ + +if false and pcall (require, 'bit') then + ------------------------------------------------------------------------ + -- Return a 4-char string little-endian encoded form of an instruction + ------------------------------------------------------------------------ + function luaP:Instruction(i) + --FIXME + end +else + ------------------------------------------------------------------------ + -- Version without bit manipulation library. + ------------------------------------------------------------------------ + local p2 = {1,2,4,8,16,32,64,128,256, 512, 1024, 2048, 4096} + -- keeps [n] bits from [x] + local function keep (x, n) return x % p2[n+1] end + -- shifts bits of [x] [n] places to the right + local function srb (x,n) return math.floor (x / p2[n+1]) end + -- shifts bits of [x] [n] places to the left + local function slb (x,n) return x * p2[n+1] end + + ------------------------------------------------------------------------ + -- Return a 4-char string little-endian encoded form of an instruction + ------------------------------------------------------------------------ + function luaP:Instruction(i) + -- printf("Instr->string: %s %s", self.opnames[i.OP], table.tostring(i)) + local c0, c1, c2, c3 + -- change to OP/A/B/C format if needed + if i.Bx then i.C = keep (i.Bx, 9); i.B = srb (i.Bx, 9) end + -- c0 = 6B from opcode + 2LSB from A (flushed to MSB) + c0 = i.OP + slb (keep (i.A, 2), 6) + -- c1 = 6MSB from A + 2LSB from C (flushed to MSB) + c1 = srb (i.A, 2) + slb (keep (i.C, 2), 6) + -- c2 = 7MSB from C + 1LSB from B (flushed to MSB) + c2 = srb (i.C, 2) + slb (keep (i.B, 1), 7) + -- c3 = 8MSB from B + c3 = srb (i.B, 1) + --printf ("Instruction: %s %s", self.opnames[i.OP], tostringv (i)) + --printf ("Bin encoding: %x %x %x %x", c0, c1, c2, c3) + return string.char(c0, c1, c2, c3) + end +end +------------------------------------------------------------------------ +-- decodes a 4-char little-endian string into an instruction struct +------------------------------------------------------------------------ +function luaP:DecodeInst(x) + error "Not implemented" +end + +------------------------------------------------------------------------ +-- invalid register that fits in 8 bits +------------------------------------------------------------------------ +luaP.NO_REG = luaP.MAXARG_A + +------------------------------------------------------------------------ +-- R(x) - register +-- Kst(x) - constant (in constant table) +-- RK(x) == if x < MAXSTACK then R(x) else Kst(x-MAXSTACK) +------------------------------------------------------------------------ + +------------------------------------------------------------------------ +-- grep "ORDER OP" if you change these enums +------------------------------------------------------------------------ + +--[[-------------------------------------------------------------------- +Lua virtual machine opcodes (enum OpCode): +------------------------------------------------------------------------ +name args description +------------------------------------------------------------------------ +OP_MOVE A B R(A) := R(B) +OP_LOADK A Bx R(A) := Kst(Bx) +OP_LOADBOOL A B C R(A) := (Bool)B; if (C) PC++ +OP_LOADNIL A B R(A) := ... := R(B) := nil +OP_GETUPVAL A B R(A) := UpValue[B] +OP_GETGLOBAL A Bx R(A) := Gbl[Kst(Bx)] +OP_GETTABLE A B C R(A) := R(B)[RK(C)] +OP_SETGLOBAL A Bx Gbl[Kst(Bx)] := R(A) +OP_SETUPVAL A B UpValue[B] := R(A) +OP_SETTABLE A B C R(A)[RK(B)] := RK(C) +OP_NEWTABLE A B C R(A) := {} (size = B,C) +OP_SELF A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] +OP_ADD A B C R(A) := RK(B) + RK(C) +OP_SUB A B C R(A) := RK(B) - RK(C) +OP_MUL A B C R(A) := RK(B) * RK(C) +OP_DIV A B C R(A) := RK(B) / RK(C) +OP_POW A B C R(A) := RK(B) ^ RK(C) +OP_UNM A B R(A) := -R(B) +OP_NOT A B R(A) := not R(B) +OP_CONCAT A B C R(A) := R(B).. ... ..R(C) +OP_JMP sBx PC += sBx +OP_EQ A B C if ((RK(B) == RK(C)) ~= A) then pc++ +OP_LT A B C if ((RK(B) < RK(C)) ~= A) then pc++ +OP_LE A B C if ((RK(B) <= RK(C)) ~= A) then pc++ +OP_TEST A B C if (R(B) <=> C) then R(A) := R(B) else pc++ +OP_CALL A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) +OP_TAILCALL A B C return R(A)(R(A+1), ... ,R(A+B-1)) +OP_RETURN A B return R(A), ... ,R(A+B-2) (see note) +OP_FORLOOP A sBx R(A)+=R(A+2); if R(A) =) R(A) +OP_CLOSURE A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) +----------------------------------------------------------------------]] + +luaP.opnames = {} -- opcode names +luaP.OpCode = {} -- lookup name -> number +luaP.ROpCode = {} -- lookup number -> name + +local i = 0 +for v in string.gfind([[ +MOVE -- 0 +LOADK +LOADBOOL +LOADNIL +GETUPVAL +GETGLOBAL -- 5 +GETTABLE +SETGLOBAL +SETUPVAL +SETTABLE +NEWTABLE -- 10 +SELF +ADD +SUB +MUL +DIV -- 15 +MOD +POW +UNM +NOT +LEN -- 20 +CONCAT +JMP +EQ +LT +LE -- 25 +TEST +TESTSET +CALL +TAILCALL +RETURN -- 30 +FORLOOP +FORPREP +TFORLOOP +SETLIST +CLOSE -- 35 +CLOSURE +VARARG +]], "[%a]+") do + local n = "OP_"..v + luaP.opnames[i] = v + luaP.OpCode[n] = i + luaP.ROpCode[i] = n + i = i + 1 +end +luaP.NUM_OPCODES = i + +--[[ +=========================================================================== + Notes: + (1) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1, + and can be 0: OP_CALL then sets 'top' to last_result+1, so + next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use 'top'. + + (2) In OP_RETURN, if (B == 0) then return up to 'top' + + (3) For comparisons, B specifies what conditions the test should accept. + + (4) All 'skips' (pc++) assume that next instruction is a jump + + (5) OP_SETLISTO is used when the last item in a table constructor is a + function, so the number of elements set is up to top of stack +=========================================================================== +--]] + +------------------------------------------------------------------------ +-- masks for instruction properties +------------------------------------------------------------------------ +-- was enum OpModeMask: +luaP.OpModeBreg = 2 -- B is a register +luaP.OpModeBrk = 3 -- B is a register/constant +luaP.OpModeCrk = 4 -- C is a register/constant +luaP.OpModesetA = 5 -- instruction set register A +luaP.OpModeK = 6 -- Bx is a constant +luaP.OpModeT = 1 -- operator is a test + +------------------------------------------------------------------------ +-- get opcode mode, e.g. "iABC" +------------------------------------------------------------------------ +function luaP:getOpMode(m) + --printv(m) + --printv(self.OpCode[m]) + --printv(self.opmodes [self.OpCode[m]+1]) + return self.OpMode[tonumber(string.sub(self.opmodes[self.OpCode[m] + 1], 7, 7))] +end + +------------------------------------------------------------------------ +-- test an instruction property flag +-- * b is a string, e.g. "OpModeBreg" +------------------------------------------------------------------------ +function luaP:testOpMode(m, b) + return (string.sub(self.opmodes[self.OpCode[m] + 1], self[b], self[b]) == "1") +end + +-- number of list items to accumulate before a SETLIST instruction +-- (must be a power of 2) +-- * used in lparser, lvm, ldebug, ltests +luaP.LFIELDS_PER_FLUSH = 50 --FF updated to match 5.1 + +-- luaP_opnames[] is set above, as the luaP.opnames table +-- opmode(t,b,bk,ck,sa,k,m) deleted + +--[[-------------------------------------------------------------------- + Legend for luaP:opmodes: + 1 T -> T (is a test?) + 2 B -> B is a register + 3 b -> B is an RK register/constant combination + 4 C -> C is an RK register/constant combination + 5 A -> register A is set by the opcode + 6 K -> Bx is a constant + 7 m -> 1 if iABC layout, + 2 if iABx layout, + 3 if iAsBx layout +----------------------------------------------------------------------]] + +luaP.opmodes = { +-- TBbCAKm opcode + "0100101", -- OP_MOVE 0 + "0000112", -- OP_LOADK + "0000101", -- OP_LOADBOOL + "0100101", -- OP_LOADNIL + "0000101", -- OP_GETUPVAL + "0000112", -- OP_GETGLOBAL 5 + "0101101", -- OP_GETTABLE + "0000012", -- OP_SETGLOBAL + "0000001", -- OP_SETUPVAL + "0011001", -- OP_SETTABLE + "0000101", -- OP_NEWTABLE 10 + "0101101", -- OP_SELF + "0011101", -- OP_ADD + "0011101", -- OP_SUB + "0011101", -- OP_MUL + "0011101", -- OP_DIV 15 + "0011101", -- OP_MOD + "0011101", -- OP_POW + "0100101", -- OP_UNM + "0100101", -- OP_NOT + "0100101", -- OP_LEN 20 + "0101101", -- OP_CONCAT + "0000003", -- OP_JMP + "1011001", -- OP_EQ + "1011001", -- OP_LT + "1011001", -- OP_LE 25 + "1000101", -- OP_TEST + "1100101", -- OP_TESTSET + "0000001", -- OP_CALL + "0000001", -- OP_TAILCALL + "0000001", -- OP_RETURN 30 + "0000003", -- OP_FORLOOP + "0000103", -- OP_FORPREP + "1000101", -- OP_TFORLOOP + "0000001", -- OP_SETLIST + "0000001", -- OP_CLOSE 35 + "0000102", -- OP_CLOSURE + "0000101" -- OP_VARARG +} + +return luaP \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/globals.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/globals.lua new file mode 100644 index 000000000..d5f7459e9 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/globals.lua @@ -0,0 +1,86 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +-------------------------------------------------------------------------------- + +--*-lua-*----------------------------------------------------------------------- +-- Override Lua's default compilation functions, so that they support Metalua +-- rather than only plain Lua +-------------------------------------------------------------------------------- + +local mlc = require 'metalua.compiler' + +local M = { } + +-- Original versions +local original_lua_versions = { + load = load, + loadfile = loadfile, + loadstring = loadstring, + dofile = dofile } + +local lua_loadstring = loadstring +local lua_loadfile = loadfile + +function M.loadstring(str, name) + if type(str) ~= 'string' then error 'string expected' end + if str:match '^\027LuaQ' then return lua_loadstring(str) end + local n = str:match '^#![^\n]*\n()' + if n then str=str:sub(n, -1) end + -- FIXME: handle erroneous returns (return nil + error msg) + return mlc.new():src_to_function(str, name) +end + +function M.loadfile(filename) + local f, err_msg = io.open(filename, 'rb') + if not f then return nil, err_msg end + local success, src = pcall( f.read, f, '*a') + pcall(f.close, f) + if success then return M.loadstring (src, '@'..filename) + else return nil, src end +end + +function M.load(f, name) + local acc = { } + while true do + local x = f() + if not x then break end + assert(type(x)=='string', "function passed to load() must return strings") + table.insert(acc, x) + end + return M.loadstring(table.concat(acc)) +end + +function M.dostring(src) + local f, msg = M.loadstring(src) + if not f then error(msg) end + return f() +end + +function M.dofile(name) + local f, msg = M.loadfile(name) + if not f then error(msg) end + return f() +end + +-- Export replacement functions as globals +for name, f in pairs(M) do _G[name] = f end + +-- To be done *after* exportation +M.lua = original_lua_versions + +return M \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser.lua new file mode 100644 index 000000000..74997aef2 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser.lua @@ -0,0 +1,42 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +-------------------------------------------------------------------------------- + +-- Export all public APIs from sub-modules, squashed into a flat spacename + +local MT = { __type='metalua.compiler.parser' } + +local MODULE_REL_NAMES = { "annot.grammar", "expr", "meta", "misc", + "stat", "table", "ext" } + +local function new() + local M = { + lexer = require "metalua.compiler.parser.lexer" (); + extensions = { } } + for _, rel_name in ipairs(MODULE_REL_NAMES) do + local abs_name = "metalua.compiler.parser."..rel_name + local extender = require (abs_name) + if not M.extensions[abs_name] then + if type (extender) == 'function' then extender(M) end + M.extensions[abs_name] = extender + end + end + return setmetatable(M, MT) +end + +return { new = new } diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/annot/generator.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/annot/generator.lua new file mode 100644 index 000000000..3a805ad42 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/annot/generator.lua @@ -0,0 +1,48 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +-------------------------------------------------------------------------------- + +local checks = require 'checks' +local gg = require 'metalua.grammar.generator' +local M = { } + +function M.opt(mlc, primary, a_type) + checks('table', 'table|function', 'string') + return gg.sequence{ + primary, + gg.onkeyword{ "#", function() return assert(mlc.annot[a_type]) end }, + builder = function(x) + local t, annot = unpack(x) + return annot and { tag='Annot', t, annot } or t + end } +end + +-- split a list of "foo" and "`Annot{foo, annot}" into a list of "foo" +-- and a list of "annot". +-- No annot list is returned if none of the elements were annotated. +function M.split(lst) + local x, a, some = { }, { }, false + for i, p in ipairs(lst) do + if p.tag=='Annot' then + some, x[i], a[i] = true, unpack(p) + else x[i] = p end + end + if some then return x, a else return lst end +end + +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/annot/grammar.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/annot/grammar.lua new file mode 100644 index 000000000..7ce3ec41b --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/annot/grammar.lua @@ -0,0 +1,112 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +-------------------------------------------------------------------------------- + +local gg = require 'metalua.grammar.generator' + +return function(M) + local _M = gg.future(M) + M.lexer :add '->' + local A = { } + local _A = gg.future(A) + M.annot = A + + -- Type identifier: Lua keywords such as `"nil"` allowed. + function M.annot.tid(lx) + local w = lx :next() + local t = w.tag + if t=='Keyword' and w[1] :match '^[%a_][%w_]*$' or w.tag=='Id' + then return {tag='TId'; lineinfo=w.lineinfo; w[1]} + else return gg.parse_error (lx, 'tid expected') end + end + + local field_types = { var='TVar'; const='TConst'; + currently='TCurrently'; field='TField' } + + -- TODO check lineinfo + function M.annot.tf(lx) + local tk = lx:next() + local w = tk[1] + local tag = field_types[w] + if not tag then error ('Invalid field type '..w) + elseif tag=='TField' then return {tag='TField'} else + local te = M.te(lx) + return {tag=tag; te} + end + end + + M.annot.tebar_content = gg.list{ + name = 'tebar content', + primary = _A.te, + separators = { ",", ";" }, + terminators = ")" } + + M.annot.tebar = gg.multisequence{ + name = 'annot.tebar', + --{ '*', builder = 'TDynbar' }, -- maybe not user-available + { '(', _A.tebar_content, ')', + builder = function(x) return x[1] end }, + { _A.te } + } + + M.annot.te = gg.multisequence{ + name = 'annot.te', + { _A.tid, builder=function(x) return x[1] end }, + { '*', builder = 'TDyn' }, + { "[", + gg.list{ + primary = gg.sequence{ + _M.expr, "=", _A.tf, + builder = 'TPair' + }, + separators = { ",", ";" }, + terminators = { "]", "|" } }, + gg.onkeyword{ "|", _A.tf }, + "]", + builder = function(x) + local fields, other = unpack(x) + return { tag='TTable', other or {tag='TField'}, fields } + end }, -- "[ ... ]" + { '(', _A.tebar_content, ')', '->', '(', _A.tebar_content, ')', + builder = function(x) + local p, r = unpack(x) + return {tag='TFunction', p, r } + end } } + + M.annot.ts = gg.multisequence{ + name = 'annot.ts', + { 'return', _A.tebar_content, builder='TReturn' }, + { _A.tid, builder = function(x) + if x[1][1]=='pass' then return {tag='TPass'} + else error "Bad statement type" end + end } } + +-- TODO: add parsers for statements: +-- #return tebar +-- #alias = te +-- #ell = tf +--[[ + M.annot.stat_annot = gg.sequence{ + gg.list{ primary=_A.tid, separators='.' }, + '=', + XXX??, + builder = 'Annot' } +--]] + + return M.annot +end \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/expr.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/expr.lua new file mode 100644 index 000000000..8ce4677a5 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/expr.lua @@ -0,0 +1,206 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- +-- Exported API: +-- * [mlp.expr()] +-- * [mlp.expr_list()] +-- * [mlp.func_val()] +-- +------------------------------------------------------------------------------- + +local pp = require 'metalua.pprint' +local gg = require 'metalua.grammar.generator' +local annot = require 'metalua.compiler.parser.annot.generator' + +return function(M) + local _M = gg.future(M) + local _table = gg.future(M, 'table') + local _meta = gg.future(M, 'meta') -- TODO move to ext? + local _annot = gg.future(M, 'annot') -- TODO move to annot + + -------------------------------------------------------------------------------- + -- Non-empty expression list. Actually, this isn't used here, but that's + -- handy to give to users. + -------------------------------------------------------------------------------- + M.expr_list = gg.list{ primary=_M.expr, separators="," } + + -------------------------------------------------------------------------------- + -- Helpers for function applications / method applications + -------------------------------------------------------------------------------- + M.func_args_content = gg.list{ + name = "function arguments", + primary = _M.expr, + separators = ",", + terminators = ")" } + + -- Used to parse methods + M.method_args = gg.multisequence{ + name = "function argument(s)", + { "{", _table.content, "}" }, + { "(", _M.func_args_content, ")", builder = unpack }, + { "+{", _meta.quote_content, "}" }, + -- TODO lineinfo? + function(lx) local r = M.opt_string(lx); return r and {r} or { } end } + + -------------------------------------------------------------------------------- + -- [func_val] parses a function, from opening parameters parenthese to + -- "end" keyword included. Used for anonymous functions as well as + -- function declaration statements (both local and global). + -- + -- It's wrapped in a [_func_val] eta expansion, so that when expr + -- parser uses the latter, they will notice updates of [func_val] + -- definitions. + -------------------------------------------------------------------------------- + M.func_params_content = gg.list{ + name="function parameters", + gg.multisequence{ { "...", builder = "Dots" }, annot.opt(M, _M.id, 'te') }, + separators = ",", terminators = {")", "|"} } + + -- TODO move to annot + M.func_val = gg.sequence{ + name = "function body", + "(", _M.func_params_content, ")", _M.block, "end", + builder = function(x) + local params, body = unpack(x) + local annots, some = { }, false + for i, p in ipairs(params) do + if p.tag=='Annot' then + params[i], annots[i], some = p[1], p[2], true + else annots[i] = false end + end + if some then return { tag='Function', params, body, annots } + else return { tag='Function', params, body } end + end } + + local func_val = function(lx) return M.func_val(lx) end + + -------------------------------------------------------------------------------- + -- Default parser for primary expressions + -------------------------------------------------------------------------------- + function M.id_or_literal (lx) + local a = lx:next() + if a.tag~="Id" and a.tag~="String" and a.tag~="Number" then + local msg + if a.tag=='Eof' then + msg = "End of file reached when an expression was expected" + elseif a.tag=='Keyword' then + msg = "An expression was expected, and `"..a[1].. + "' can't start an expression" + else + msg = "Unexpected expr token " .. pp.tostring (a) + end + gg.parse_error (lx, msg) + end + return a + end + + + -------------------------------------------------------------------------------- + -- Builder generator for operators. Wouldn't be worth it if "|x|" notation + -- were allowed, but then lua 5.1 wouldn't compile it + -------------------------------------------------------------------------------- + + -- opf1 = |op| |_,a| `Op{ op, a } + local function opf1 (op) return + function (_,a) return { tag="Op", op, a } end end + + -- opf2 = |op| |a,_,b| `Op{ op, a, b } + local function opf2 (op) return + function (a,_,b) return { tag="Op", op, a, b } end end + + -- opf2r = |op| |a,_,b| `Op{ op, b, a } -- (args reversed) + local function opf2r (op) return + function (a,_,b) return { tag="Op", op, b, a } end end + + local function op_ne(a, _, b) + -- This version allows to remove the "ne" operator from the AST definition. + -- However, it doesn't always produce the exact same bytecode as Lua 5.1. + return { tag="Op", "not", + { tag="Op", "eq", a, b, lineinfo= { + first = a.lineinfo.first, last = b.lineinfo.last } } } + end + + + -------------------------------------------------------------------------------- + -- + -- complete expression + -- + -------------------------------------------------------------------------------- + + -- FIXME: set line number. In [expr] transformers probably + M.expr = gg.expr { + name = "expression", + primary = gg.multisequence{ + name = "expr primary", + { "(", _M.expr, ")", builder = "Paren" }, + { "function", _M.func_val, builder = unpack }, + { "-{", _meta.splice_content, "}", builder = unpack }, + { "+{", _meta.quote_content, "}", builder = unpack }, + { "nil", builder = "Nil" }, + { "true", builder = "True" }, + { "false", builder = "False" }, + { "...", builder = "Dots" }, + { "{", _table.content, "}", builder = unpack }, + _M.id_or_literal }, + + infix = { + name = "expr infix op", + { "+", prec = 60, builder = opf2 "add" }, + { "-", prec = 60, builder = opf2 "sub" }, + { "*", prec = 70, builder = opf2 "mul" }, + { "/", prec = 70, builder = opf2 "div" }, + { "%", prec = 70, builder = opf2 "mod" }, + { "^", prec = 90, builder = opf2 "pow", assoc = "right" }, + { "..", prec = 40, builder = opf2 "concat", assoc = "right" }, + { "==", prec = 30, builder = opf2 "eq" }, + { "~=", prec = 30, builder = op_ne }, + { "<", prec = 30, builder = opf2 "lt" }, + { "<=", prec = 30, builder = opf2 "le" }, + { ">", prec = 30, builder = opf2r "lt" }, + { ">=", prec = 30, builder = opf2r "le" }, + { "and",prec = 20, builder = opf2 "and" }, + { "or", prec = 10, builder = opf2 "or" } }, + + prefix = { + name = "expr prefix op", + { "not", prec = 80, builder = opf1 "not" }, + { "#", prec = 80, builder = opf1 "len" }, + { "-", prec = 80, builder = opf1 "unm" } }, + + suffix = { + name = "expr suffix op", + { "[", _M.expr, "]", builder = function (tab, idx) + return {tag="Index", tab, idx[1]} end}, + { ".", _M.id, builder = function (tab, field) + return {tag="Index", tab, _M.id2string(field[1])} end }, + { "(", _M.func_args_content, ")", builder = function(f, args) + return {tag="Call", f, unpack(args[1])} end }, + { "{", _table.content, "}", builder = function (f, arg) + return {tag="Call", f, arg[1]} end}, + { ":", _M.id, _M.method_args, builder = function (obj, post) + local m_name, args = unpack(post) + return {tag="Invoke", obj, _M.id2string(m_name), unpack(args)} end}, + { "+{", _meta.quote_content, "}", builder = function (f, arg) + return {tag="Call", f, arg[1] } end }, + default = { name="opt_string_arg", parse = _M.opt_string, builder = function(f, arg) + return {tag="Call", f, arg } end } } } + return M +end \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/ext.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/ext.lua new file mode 100644 index 000000000..4e9d3950f --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/ext.lua @@ -0,0 +1,96 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- +-- Non-Lua syntax extensions +-- +-------------------------------------------------------------------------------- + +local gg = require 'metalua.grammar.generator' + +return function(M) + + local _M = gg.future(M) + + --------------------------------------------------------------------------- + -- Algebraic Datatypes + ---------------------------------------------------------------------------- + local function adt (lx) + local node = _M.id (lx) + local tagval = node[1] + -- tagkey = `Pair{ `String "key", `String{ -{tagval} } } + local tagkey = { tag="Pair", {tag="String", "tag"}, {tag="String", tagval} } + if lx:peek().tag == "String" or lx:peek().tag == "Number" then + -- TODO support boolean litterals + return { tag="Table", tagkey, lx:next() } + elseif lx:is_keyword (lx:peek(), "{") then + local x = M.table.table (lx) + table.insert (x, 1, tagkey) + return x + else return { tag="Table", tagkey } end + end + + M.adt = gg.sequence{ "`", adt, builder = unpack } + + M.expr.primary :add(M.adt) + + ---------------------------------------------------------------------------- + -- Anonymous lambda + ---------------------------------------------------------------------------- + M.lambda_expr = gg.sequence{ + "|", _M.func_params_content, "|", _M.expr, + builder = function (x) + local li = x[2].lineinfo + return { tag="Function", x[1], + { {tag="Return", x[2], lineinfo=li }, lineinfo=li } } + end } + + M.expr.primary :add (M.lambda_expr) + + -------------------------------------------------------------------------------- + -- Allows to write "a `f` b" instead of "f(a, b)". Taken from Haskell. + -------------------------------------------------------------------------------- + function M.expr_in_backquotes (lx) return M.expr(lx, 35) end -- 35=limited precedence + M.expr.infix :add{ name = "infix function", + "`", _M.expr_in_backquotes, "`", prec = 35, assoc="left", + builder = function(a, op, b) return {tag="Call", op[1], a, b} end } + + -------------------------------------------------------------------------------- + -- C-style op+assignments + -- TODO: no protection against side-effects in LHS vars. + -------------------------------------------------------------------------------- + local function op_assign(kw, op) + local function rhs(a, b) return { tag="Op", op, a, b } end + local function f(a,b) + if #a ~= #b then gg.parse_error "assymetric operator+assignment" end + local right = { } + local r = { tag="Set", a, right } + for i=1, #a do right[i] = { tag="Op", op, a[i], b[i] } end + return r + end + M.lexer :add (kw) + M.assignments[kw] = f + end + + local ops = { add='+='; sub='-='; mul='*='; div='/=' } + for ast_op_name, keyword in pairs(ops) do op_assign(keyword, ast_op_name) end + + return M +end \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/lexer.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/lexer.lua new file mode 100644 index 000000000..2b5ff7e9d --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/lexer.lua @@ -0,0 +1,43 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2006-2014 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +-------------------------------------------------------------------------------- + +---------------------------------------------------------------------- +-- Generate a new lua-specific lexer, derived from the generic lexer. +---------------------------------------------------------------------- + +local generic_lexer = require 'metalua.grammar.lexer' + +return function() + local lexer = generic_lexer.lexer :clone() + + local keywords = { + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", + "goto", -- Lua5.2 + "if", + "in", "local", "nil", "not", "or", "repeat", + "return", "then", "true", "until", "while", + "...", "..", "==", ">=", "<=", "~=", + "::", -- Lua5,2 + "+{", "-{" } -- Metalua + + for _, w in ipairs(keywords) do lexer :add (w) end + + return lexer +end \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/meta.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/meta.lua new file mode 100644 index 000000000..71eb3c358 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/meta.lua @@ -0,0 +1,138 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2006-2014 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +------------------------------------------------------------------------------- + +-- Compile-time metaprogramming features: splicing ASTs generated during compilation, +-- AST quasi-quoting helpers. + +local gg = require 'metalua.grammar.generator' + +return function(M) + local _M = gg.future(M) + M.meta={ } + local _MM = gg.future(M.meta) + + -------------------------------------------------------------------------------- + -- External splicing: compile an AST into a chunk, load and evaluate + -- that chunk, and replace the chunk by its result (which must also be + -- an AST). + -------------------------------------------------------------------------------- + + -- TODO: that's not part of the parser + function M.meta.eval (ast) + -- TODO: should there be one mlc per splice, or per parser instance? + local mlc = require 'metalua.compiler'.new() + local f = mlc :ast_to_function (ast, '=splice') + local result=f(M) -- splices act on the current parser + return result + end + + ---------------------------------------------------------------------------- + -- Going from an AST to an AST representing that AST + -- the only hash-part key being lifted is `"tag"`. + -- Doesn't lift subtrees protected inside a `Splice{ ... }. + -- e.g. change `Foo{ 123 } into + -- `Table{ `Pair{ `String "tag", `String "foo" }, `Number 123 } + ---------------------------------------------------------------------------- + local function lift (t) + --print("QUOTING:", table.tostring(t, 60,'nohash')) + local cases = { } + function cases.table (t) + local mt = { tag = "Table" } + --table.insert (mt, { tag = "Pair", quote "quote", { tag = "True" } }) + if t.tag == "Splice" then + assert (#t==1, "Invalid splice") + local sp = t[1] + return sp + elseif t.tag then + table.insert (mt, { tag="Pair", lift "tag", lift(t.tag) }) + end + for _, v in ipairs (t) do + table.insert (mt, lift(v)) + end + return mt + end + function cases.number (t) return { tag = "Number", t, quote = true } end + function cases.string (t) return { tag = "String", t, quote = true } end + function cases.boolean (t) return { tag = t and "True" or "False", t, quote = true } end + local f = cases [type(t)] + if f then return f(t) else error ("Cannot quote an AST containing "..tostring(t)) end + end + M.meta.lift = lift + + -------------------------------------------------------------------------------- + -- when this variable is false, code inside [-{...}] is compiled and + -- avaluated immediately. When it's true (supposedly when we're + -- parsing data inside a quasiquote), [-{foo}] is replaced by + -- [`Splice{foo}], which will be unpacked by [quote()]. + -------------------------------------------------------------------------------- + local in_a_quote = false + + -------------------------------------------------------------------------------- + -- Parse the inside of a "-{ ... }" + -------------------------------------------------------------------------------- + function M.meta.splice_content (lx) + local parser_name = "expr" + if lx:is_keyword (lx:peek(2), ":") then + local a = lx:next() + lx:next() -- skip ":" + assert (a.tag=="Id", "Invalid splice parser name") + parser_name = a[1] + end + -- TODO FIXME running a new parser with the old lexer?! + local parser = require 'metalua.compiler.parser'.new() + local ast = parser [parser_name](lx) + if in_a_quote then -- only prevent quotation in this subtree + --printf("SPLICE_IN_QUOTE:\n%s", _G.table.tostring(ast, "nohash", 60)) + return { tag="Splice", ast } + else -- convert in a block, eval, replace with result + if parser_name == "expr" then ast = { { tag="Return", ast } } + elseif parser_name == "stat" then ast = { ast } + elseif parser_name ~= "block" then + error ("splice content must be an expr, stat or block") end + --printf("EXEC THIS SPLICE:\n%s", _G.table.tostring(ast, "nohash", 60)) + return M.meta.eval (ast) + end + end + + M.meta.splice = gg.sequence{ "-{", _MM.splice_content, "}", builder=unpack } + + -------------------------------------------------------------------------------- + -- Parse the inside of a "+{ ... }" + -------------------------------------------------------------------------------- + function M.meta.quote_content (lx) + local parser + if lx:is_keyword (lx:peek(2), ":") then -- +{parser: content } + local parser_name = M.id(lx)[1] + parser = M[parser_name] + lx:next() -- skip ":" + else -- +{ content } + parser = M.expr + end + + local prev_iq = in_a_quote + in_a_quote = true + --print("IN_A_QUOTE") + local content = parser (lx) + local q_content = M.meta.lift (content) + in_a_quote = prev_iq + return q_content + end + + return M +end \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/misc.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/misc.lua new file mode 100644 index 000000000..f7dde0923 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/misc.lua @@ -0,0 +1,176 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- +-- Summary: metalua parser, miscellaneous utility functions. +-- +------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- +-- Exported API: +-- * [mlp.fget()] +-- * [mlp.id()] +-- * [mlp.opt_id()] +-- * [mlp.id_list()] +-- * [mlp.string()] +-- * [mlp.opt_string()] +-- * [mlp.id2string()] +-- +-------------------------------------------------------------------------------- + +local pp = require 'metalua.pprint' +local gg = require 'metalua.grammar.generator' + +-- TODO: replace splice-aware versions with naive ones, move etensions in ./meta + +return function(M) + local _M = gg.future(M) + +--[[ metaprog-free versions: + function M.id(lx) + if lx:peek().tag~='Id' then gg.parse_error(lx, "Identifier expected") + else return lx:next() end + end + + function M.opt_id(lx) + if lx:peek().tag~='Id' then return lx:next() else return false end + end + + function M.string(lx) + if lx:peek().tag~='String' then gg.parse_error(lx, "String expected") + else return lx:next() end + end + + function M.opt_string(lx) + if lx:peek().tag~='String' then return lx:next() else return false end + end + + -------------------------------------------------------------------------------- + -- Converts an identifier into a string. Hopefully one day it'll handle + -- splices gracefully, but that proves quite tricky. + -------------------------------------------------------------------------------- + function M.id2string (id) + if id.tag == "Id" then id.tag = "String"; return id + else error ("Identifier expected: "..table.tostring(id, 'nohash')) end + end +--]] + + -------------------------------------------------------------------------------- + -- Try to read an identifier (possibly as a splice), or return [false] if no + -- id is found. + -------------------------------------------------------------------------------- + function M.opt_id (lx) + local a = lx:peek(); + if lx:is_keyword (a, "-{") then + local v = M.meta.splice(lx) + if v.tag ~= "Id" and v.tag ~= "Splice" then + gg.parse_error(lx, "Bad id splice") + end + return v + elseif a.tag == "Id" then return lx:next() + else return false end + end + + -------------------------------------------------------------------------------- + -- Mandatory reading of an id: causes an error if it can't read one. + -------------------------------------------------------------------------------- + function M.id (lx) + return M.opt_id (lx) or gg.parse_error(lx,"Identifier expected") + end + + -------------------------------------------------------------------------------- + -- Common helper function + -------------------------------------------------------------------------------- + M.id_list = gg.list { primary = _M.id, separators = "," } + + -------------------------------------------------------------------------------- + -- Converts an identifier into a string. Hopefully one day it'll handle + -- splices gracefully, but that proves quite tricky. + -------------------------------------------------------------------------------- + function M.id2string (id) + --print("id2string:", disp.ast(id)) + if id.tag == "Id" then id.tag = "String"; return id + elseif id.tag == "Splice" then + error ("id2string on splice not implemented") + -- Evaluating id[1] will produce `Id{ xxx }, + -- and we want it to produce `String{ xxx }. + -- The following is the plain notation of: + -- +{ `String{ `Index{ `Splice{ -{id[1]} }, `Number 1 } } } + return { tag="String", { tag="Index", { tag="Splice", id[1] }, + { tag="Number", 1 } } } + else error ("Identifier expected: "..pp.tostring (id, {metalua_tag=1, hide_hash=1})) end + end + + -------------------------------------------------------------------------------- + -- Read a string, possibly spliced, or return an error if it can't + -------------------------------------------------------------------------------- + function M.string (lx) + local a = lx:peek() + if lx:is_keyword (a, "-{") then + local v = M.meta.splice(lx) + if v.tag ~= "String" and v.tag ~= "Splice" then + gg.parse_error(lx,"Bad string splice") + end + return v + elseif a.tag == "String" then return lx:next() + else error "String expected" end + end + + -------------------------------------------------------------------------------- + -- Try to read a string, or return false if it can't. No splice allowed. + -------------------------------------------------------------------------------- + function M.opt_string (lx) + return lx:peek().tag == "String" and lx:next() + end + + -------------------------------------------------------------------------------- + -- Chunk reader: block + Eof + -------------------------------------------------------------------------------- + function M.skip_initial_sharp_comment (lx) + -- Dirty hack: I'm happily fondling lexer's private parts + -- FIXME: redundant with lexer:newstream() + lx :sync() + local i = lx.src:match ("^#.-\n()", lx.i) + if i then + lx.i = i + lx.column_offset = i + lx.line = lx.line and lx.line + 1 or 1 + end + end + + local function chunk (lx) + if lx:peek().tag == 'Eof' then + return { } -- handle empty files + else + M.skip_initial_sharp_comment (lx) + local chunk = M.block (lx) + if lx:peek().tag ~= "Eof" then + gg.parse_error(lx, "End-of-file expected") + end + return chunk + end + end + + -- chunk is wrapped in a sequence so that it has a "transformer" field. + M.chunk = gg.sequence { chunk, builder = unpack } + + return M +end \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/stat.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/stat.lua new file mode 100644 index 000000000..5d5e3a91d --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/stat.lua @@ -0,0 +1,279 @@ +------------------------------------------------------------------------------ +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- +-- Summary: metalua parser, statement/block parser. This is part of the +-- definition of module [mlp]. +-- +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- +-- Exports API: +-- * [mlp.stat()] +-- * [mlp.block()] +-- * [mlp.for_header()] +-- +------------------------------------------------------------------------------- + +local lexer = require 'metalua.grammar.lexer' +local gg = require 'metalua.grammar.generator' + +local annot = require 'metalua.compiler.parser.annot.generator' + +-------------------------------------------------------------------------------- +-- List of all keywords that indicate the end of a statement block. Users are +-- likely to extend this list when designing extensions. +-------------------------------------------------------------------------------- + + +return function(M) + local _M = gg.future(M) + + M.block_terminators = { "else", "elseif", "end", "until", ")", "}", "]" } + + -- FIXME: this must be handled from within GG!!! + -- FIXME: there's no :add method in the list anyway. Added by gg.list?! + function M.block_terminators :add(x) + if type (x) == "table" then for _, y in ipairs(x) do self :add (y) end + else table.insert (self, x) end + end + + ---------------------------------------------------------------------------- + -- list of statements, possibly followed by semicolons + ---------------------------------------------------------------------------- + M.block = gg.list { + name = "statements block", + terminators = M.block_terminators, + primary = function (lx) + -- FIXME use gg.optkeyword() + local x = M.stat (lx) + if lx:is_keyword (lx:peek(), ";") then lx:next() end + return x + end } + + ---------------------------------------------------------------------------- + -- Helper function for "return " parsing. + -- Called when parsing return statements. + -- The specific test for initial ";" is because it's not a block terminator, + -- so without it gg.list would choke on "return ;" statements. + -- We don't make a modified copy of block_terminators because this list + -- is sometimes modified at runtime, and the return parser would get out of + -- sync if it was relying on a copy. + ---------------------------------------------------------------------------- + local return_expr_list_parser = gg.multisequence{ + { ";" , builder = function() return { } end }, + default = gg.list { + _M.expr, separators = ",", terminators = M.block_terminators } } + + + local for_vars_list = gg.list{ + name = "for variables list", + primary = _M.id, + separators = ",", + terminators = "in" } + + ---------------------------------------------------------------------------- + -- for header, between [for] and [do] (exclusive). + -- Return the `Forxxx{...} AST, without the body element (the last one). + ---------------------------------------------------------------------------- + function M.for_header (lx) + local vars = M.id_list(lx) + if lx :is_keyword (lx:peek(), "=") then + if #vars ~= 1 then + gg.parse_error (lx, "numeric for only accepts one variable") + end + lx:next() -- skip "=" + local exprs = M.expr_list (lx) + if #exprs < 2 or #exprs > 3 then + gg.parse_error (lx, "numeric for requires 2 or 3 boundaries") + end + return { tag="Fornum", vars[1], unpack (exprs) } + else + if not lx :is_keyword (lx :next(), "in") then + gg.parse_error (lx, '"=" or "in" expected in for loop') + end + local exprs = M.expr_list (lx) + return { tag="Forin", vars, exprs } + end + end + + ---------------------------------------------------------------------------- + -- Function def parser helper: id ( . id ) * + ---------------------------------------------------------------------------- + local function fn_builder (list) + local acc = list[1] + local first = acc.lineinfo.first + for i = 2, #list do + local index = M.id2string(list[i]) + local li = lexer.new_lineinfo(first, index.lineinfo.last) + acc = { tag="Index", acc, index, lineinfo=li } + end + return acc + end + local func_name = gg.list{ _M.id, separators = ".", builder = fn_builder } + + ---------------------------------------------------------------------------- + -- Function def parser helper: ( : id )? + ---------------------------------------------------------------------------- + local method_name = gg.onkeyword{ name = "method invocation", ":", _M.id, + transformers = { function(x) return x and x.tag=='Id' and M.id2string(x) end } } + + ---------------------------------------------------------------------------- + -- Function def builder + ---------------------------------------------------------------------------- + local function funcdef_builder(x) + local name, method, func = unpack(x) + if method then + name = { tag="Index", name, method, + lineinfo = { + first = name.lineinfo.first, + last = method.lineinfo.last } } + table.insert (func[1], 1, {tag="Id", "self"}) + end + local r = { tag="Set", {name}, {func} } + r[1].lineinfo = name.lineinfo + r[2].lineinfo = func.lineinfo + return r + end + + + ---------------------------------------------------------------------------- + -- if statement builder + ---------------------------------------------------------------------------- + local function if_builder (x) + local cond_block_pairs, else_block, r = x[1], x[2], {tag="If"} + local n_pairs = #cond_block_pairs + for i = 1, n_pairs do + local cond, block = unpack(cond_block_pairs[i]) + r[2*i-1], r[2*i] = cond, block + end + if else_block then table.insert(r, #r+1, else_block) end + return r + end + + -------------------------------------------------------------------------------- + -- produce a list of (expr,block) pairs + -------------------------------------------------------------------------------- + local elseifs_parser = gg.list { + gg.sequence { _M.expr, "then", _M.block , name='elseif parser' }, + separators = "elseif", + terminators = { "else", "end" } + } + + local annot_expr = gg.sequence { + _M.expr, + gg.onkeyword{ "#", gg.future(M, 'annot').tf }, + builder = function(x) + local e, a = unpack(x) + if a then return { tag='Annot', e, a } + else return e end + end } + + local annot_expr_list = gg.list { + primary = annot.opt(M, _M.expr, 'tf'), separators = ',' } + + ------------------------------------------------------------------------ + -- assignments and calls: statements that don't start with a keyword + ------------------------------------------------------------------------ + local function assign_or_call_stat_parser (lx) + local e = annot_expr_list (lx) + local a = lx:is_keyword(lx:peek()) + local op = a and M.assignments[a] + -- TODO: refactor annotations + if op then + --FIXME: check that [e] is a LHS + lx :next() + local annots + e, annots = annot.split(e) + local v = M.expr_list (lx) + if type(op)=="string" then return { tag=op, e, v, annots } + else return op (e, v) end + else + assert (#e > 0) + if #e > 1 then + gg.parse_error (lx, + "comma is not a valid statement separator; statement can be ".. + "separated by semicolons, or not separated at all") + elseif e[1].tag ~= "Call" and e[1].tag ~= "Invoke" then + local typename + if e[1].tag == 'Id' then + typename = '("'..e[1][1]..'") is an identifier' + elseif e[1].tag == 'Op' then + typename = "is an arithmetic operation" + else typename = "is of type '"..(e[1].tag or "").."'" end + gg.parse_error (lx, + "This expression %s; ".. + "a statement was expected, and only function and method call ".. + "expressions can be used as statements", typename); + end + return e[1] + end + end + + M.local_stat_parser = gg.multisequence{ + -- local function + { "function", _M.id, _M.func_val, builder = + function(x) + local vars = { x[1], lineinfo = x[1].lineinfo } + local vals = { x[2], lineinfo = x[2].lineinfo } + return { tag="Localrec", vars, vals } + end }, + -- local ( = )? + default = gg.sequence{ + gg.list{ + primary = annot.opt(M, _M.id, 'tf'), + separators = ',' }, + gg.onkeyword{ "=", _M.expr_list }, + builder = function(x) + local annotated_left, right = unpack(x) + local left, annotations = annot.split(annotated_left) + return {tag="Local", left, right or { }, annotations } + end } } + + ------------------------------------------------------------------------ + -- statement + ------------------------------------------------------------------------ + M.stat = gg.multisequence { + name = "statement", + { "do", _M.block, "end", builder = + function (x) return { tag="Do", unpack (x[1]) } end }, + { "for", _M.for_header, "do", _M.block, "end", builder = + function (x) x[1][#x[1]+1] = x[2]; return x[1] end }, + { "function", func_name, method_name, _M.func_val, builder=funcdef_builder }, + { "while", _M.expr, "do", _M.block, "end", builder = "While" }, + { "repeat", _M.block, "until", _M.expr, builder = "Repeat" }, + { "local", _M.local_stat_parser, builder = unpack }, + { "return", return_expr_list_parser, builder = + function(x) x[1].tag='Return'; return x[1] end }, + { "break", builder = function() return { tag="Break" } end }, + { "-{", gg.future(M, 'meta').splice_content, "}", builder = unpack }, + { "if", gg.nonempty(elseifs_parser), gg.onkeyword{ "else", M.block }, "end", + builder = if_builder }, + default = assign_or_call_stat_parser } + + M.assignments = { + ["="] = "Set" + } + + function M.assignments:add(k, v) self[k] = v end + + return M +end \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/table.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/table.lua new file mode 100644 index 000000000..11102d9ec --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/table.lua @@ -0,0 +1,77 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- +-- Exported API: +-- * [M.table_bracket_field()] +-- * [M.table_field()] +-- * [M.table_content()] +-- * [M.table()] +-- +-- KNOWN BUG: doesn't handle final ";" or "," before final "}" +-- +-------------------------------------------------------------------------------- + +local gg = require 'metalua.grammar.generator' + +return function(M) + + M.table = { } + local _table = gg.future(M.table) + local _expr = gg.future(M).expr + + -------------------------------------------------------------------------------- + -- `[key] = value` table field definition + -------------------------------------------------------------------------------- + M.table.bracket_pair = gg.sequence{ "[", _expr, "]", "=", _expr, builder = "Pair" } + + -------------------------------------------------------------------------------- + -- table element parser: list value, `id = value` pair or `[value] = value` pair. + -------------------------------------------------------------------------------- + function M.table.element (lx) + if lx :is_keyword (lx :peek(), "[") then return M.table.bracket_pair(lx) end + local e = M.expr (lx) + if not lx :is_keyword (lx :peek(), "=") then return e end + lx :next(); -- skip the "=" + local key = M.id2string(e) -- will fail on non-identifiers + local val = M.expr(lx) + local r = { tag="Pair", key, val } + r.lineinfo = { first = key.lineinfo.first, last = val.lineinfo.last } + return r + end + + ----------------------------------------------------------------------------- + -- table constructor, without enclosing braces; returns a full table object + ----------------------------------------------------------------------------- + M.table.content = gg.list { + -- eta expansion to allow patching the element definition + primary = _table.element, + separators = { ",", ";" }, + terminators = "}", + builder = "Table" } + + -------------------------------------------------------------------------------- + -- complete table constructor including [{...}] + -------------------------------------------------------------------------------- + -- TODO beware, stat and expr use only table.content, this can't be patched. + M.table.table = gg.sequence{ "{", _table.content, "}", builder = unpack } + + return M +end \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/extension/comprehension.mlua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/extension/comprehension.mlua new file mode 100644 index 000000000..8917b9a92 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/extension/comprehension.mlua @@ -0,0 +1,282 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +------------------------------------------------------------------------------- +-- +-- This extension implements list comprehensions, similar to Haskell and +-- Python syntax, to easily describe lists. +-- +-- * x[a ... b] is the list { x[a], x[a+1], ..., x[b] } +-- * { f()..., b } contains all the elements returned by f(), then b +-- (allows to expand list fields other than the last one) +-- * list comprehensions a la python, with "for" and "if" suffixes: +-- {i+10*j for i=1,3 for j=1,3 if i~=j} is { 21, 31, 12, 32, 13, 23 } +-- +------------------------------------------------------------------------------- + +-{ extension ("match", ...) } + +local SUPPORT_IMPROVED_LOOPS = true +local SUPPORT_IMPROVED_INDEXES = false -- depends on deprecated table.isub +local SUPPORT_CONTINUE = true +local SUPPORT_COMP_LISTS = true + +assert (SUPPORT_IMPROVED_LOOPS or not SUPPORT_CONTINUE, + "Can't support 'continue' without improved loop headers") + +local gg = require 'metalua.grammar.generator' +local Q = require 'metalua.treequery' + +local function dots_list_suffix_builder (x) return `DotsSuffix{ x } end + +local function for_list_suffix_builder (list_element, suffix) + local new_header = suffix[1] + match list_element with + | `Comp{ _, acc } -> table.insert (acc, new_header); return list_element + | _ -> return `Comp{ list_element, { new_header } } + end +end + +local function if_list_suffix_builder (list_element, suffix) + local new_header = `If{ suffix[1] } + match list_element with + | `Comp{ _, acc } -> table.insert (acc, new_header); return list_element + | _ -> return `Comp{ list_element, { new_header } } + end +end + +-- Builds a statement from a table element, which adds this element to +-- a table `t`, potentially thanks to an alias `tinsert` to +-- `table.insert`. +-- @param core the part around which the loops are built. +-- either `DotsSuffix{expr}, `Pair{ expr } or a plain expression +-- @param list comprehension suffixes, in the order in which they appear +-- either `Forin{ ... } or `Fornum{ ...} or `If{ ... }. In each case, +-- it misses a last child node as its body. +-- @param t a variable containing the table to fill +-- @param tinsert a variable containing `table.insert`. +-- +-- @return fill a statement which fills empty table `t` with the denoted element +local function comp_list_builder(core, list, t, tinsert) + local filler + -- 1 - Build the loop's core: if it has suffix "...", every elements of the + -- multi-return must be inserted, hence the extra [for] loop. + match core with + | `DotsSuffix{ element } -> + local x = gg.gensym() + filler = +{stat: for _, -{x} in pairs{ -{element} } do (-{tinsert})(-{t}, -{x}) end } + | `Pair{ key, value } -> + --filler = +{ -{t}[-{key}] = -{value} } + filler = `Set{ { `Index{ t, key } }, { value } } + | _ -> filler = +{ (-{tinsert})(-{t}, -{core}) } + end + + -- 2 - Stack the `if` and `for` control structures, from outside to inside. + -- This is done in a destructive way for the elements of [list]. + for i = #list, 1, -1 do + table.insert (list[i], {filler}) + filler = list[i] + end + + return filler +end + +local function table_content_builder (list) + local special = false -- Does the table need a special builder? + for _, element in ipairs(list) do + local etag = element.tag + if etag=='Comp' or etag=='DotsSuffix' then special=true; break end + end + if not special then list.tag='Table'; return list end + + local t, tinsert = gg.gensym 'table', gg.gensym 'table_insert' + local filler_block = { +{stat: local -{t}, -{tinsert} = { }, table.insert } } + for _, element in ipairs(list) do + local filler + match element with + | `Comp{ core, comp } -> filler = comp_list_builder(core, comp, t, tinsert) + | _ -> filler = comp_list_builder(element, { }, t, tinsert) + end + table.insert(filler_block, filler) + end + return `Stat{ filler_block, t } +end + + +-------------------------------------------------------------------------------- +-- Back-end for improved index operator. +local function index_builder(a, suffix) + match suffix[1] with + -- Single index, no range: keep the native semantics + | { { e, false } } -> return `Index{ a, e } + -- Either a range, or multiple indexes, or both + | ranges -> + local r = `Call{ +{table.isub}, a } + local function acc (x,y) table.insert (r,x); table.insert (r,y) end + for _, seq in ipairs (ranges) do + match seq with + | { e, false } -> acc(e,e) + | { e, f } -> acc(e,f) + end + end + return r + end +end + +------------------------------------------------------------------- +-- Find continue statements in a loop body, change them into goto +-- end-of-body. +local function transform_continue_statements(body) + local continue_statements = Q(body) + :if_unknown() -- tolerate unknown 'Continue' statements + :not_under ('Forin', 'Fornum', 'While', 'Repeat') + :filter ('Continue') + :list() + if next(continue_statements) then + local continue_label = gg.gensym 'continue' [1] + table.insert(body, `Label{ continue_label }) + for _, statement in ipairs(continue_statements) do + statement.tag = 'Goto' + statement[1] = continue_label + end + return true + else return false end +end + +------------------------------------------------------------------------------- +-- Back-end for loops with a multi-element header +local function loop_builder(x) + local first, elements, body = unpack(x) + + -- Change continue statements into gotos. + if SUPPORT_CONTINUE then transform_continue_statements(body) end + + ------------------------------------------------------------------- + -- If it's a regular loop, don't bloat the code + if not next(elements) then + table.insert(first, body) + return first + end + + ------------------------------------------------------------------- + -- There's no reason to treat the first element in a special way + table.insert(elements, 1, first) + + ------------------------------------------------------------------- + -- Change breaks into gotos that escape all loops at once. + local exit_label = nil + local function break_to_goto(break_node) + if not exit_label then exit_label = gg.gensym 'break' [1] end + break_node = break_node or { } + break_node.tag = 'Goto' + break_node[1] = exit_label + return break_node + end + Q(body) + :not_under('Function', 'Forin', 'Fornum', 'While', 'Repeat') + :filter('Break') + :foreach (break_to_goto) + + ------------------------------------------------------------------- + -- Compile all headers elements, from last to first. + -- invariant: `body` is a block (not a statement) + local result = body + for i = #elements, 1, -1 do + local e = elements[i] + match e with + | `If{ cond } -> + result = { `If{ cond, result } } + | `Until{ cond } -> + result = +{block: if -{cond} then -{break_to_goto()} else -{result} end } + | `While{ cond } -> + if i==1 then result = { `While{ cond, result } } -- top-level while + else result = +{block: if -{cond} then -{result} else -{break_to_goto()} end } end + | `Forin{ ... } | `Fornum{ ... } -> + table.insert (e, result); result={e} + | _-> require'metalua.pprint'.printf("Bad loop header element %s", e) + end + end + + + ------------------------------------------------------------------- + -- If some breaks had to be changed into gotos, insert the label + if exit_label then result = { result, `Label{ exit_label } } end + + return result +end + + +-------------------------------------------------------------------------------- +-- Improved "[...]" index operator: +-- * support for multi-indexes ("foo[bar, gnat]") +-- * support for ranges ("foo[bar ... gnat]") +-------------------------------------------------------------------------------- +local function extend(M) + + local _M = gg.future(M) + + if SUPPORT_COMP_LISTS then + -- support for "for" / "if" comprehension suffixes in literal tables + local original_table_element = M.table.element + M.table.element = gg.expr{ name="table cell", + primary = original_table_element, + suffix = { name="table cell suffix", + { "...", builder = dots_list_suffix_builder }, + { "for", _M.for_header, builder = for_list_suffix_builder }, + { "if", _M.expr, builder = if_list_suffix_builder } } } + M.table.content.builder = table_content_builder + end + + if SUPPORT_IMPROVED_INDEXES then + -- Support for ranges and multiple indices in bracket suffixes + M.expr.suffix:del '[' + M.expr.suffix:add{ name="table index/range", + "[", gg.list{ + gg.sequence { _M.expr, gg.onkeyword{ "...", _M.expr } } , + separators = { ",", ";" } }, + "]", builder = index_builder } + end + + if SUPPORT_IMPROVED_LOOPS then + local original_for_header = M.for_header + M.stat :del 'for' + M.stat :del 'while' + + M.loop_suffix = gg.multisequence{ + { 'while', _M.expr, builder = |x| `Until{ `Op{ 'not', x[1] } } }, + { 'until', _M.expr, builder = |x| `Until{ x[1] } }, + { 'if', _M.expr, builder = |x| `If{ x[1] } }, + { 'for', original_for_header, builder = |x| x[1] } } + + M.loop_suffix_list = gg.list{ _M.loop_suffix, terminators='do' } + + M.stat :add{ + 'for', original_for_header, _M.loop_suffix_list, 'do', _M.block, 'end', + builder = loop_builder } + + M.stat :add{ + 'while', _M.expr, _M.loop_suffix_list, 'do', _M.block, 'end', + builder = |x| loop_builder{ `While{x[1]}, x[2], x[3] } } + end + + if SUPPORT_CONTINUE then + M.lexer :add 'continue' + M.stat :add{ 'continue', builder='Continue' } + end +end + +return extend diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/extension/match.mlua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/extension/match.mlua new file mode 100644 index 000000000..f55ff4140 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/extension/match.mlua @@ -0,0 +1,400 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- +-- Glossary: +-- +-- * term_seq: the tested stuff, a sequence of terms +-- * pattern_element: might match one term of a term seq. Represented +-- as expression ASTs. +-- * pattern_seq: might match a term_seq +-- * pattern_group: several pattern seqs, one of them might match +-- the term seq. +-- * case: pattern_group * guard option * block +-- * match_statement: tested term_seq * case list +-- +-- Hence a complete match statement is a: +-- +-- { list(expr), list{ list(list(expr)), expr or false, block } } +-- +-- Implementation hints +-- ==================== +-- +-- The implementation is made as modular as possible, so that parts +-- can be reused in other extensions. The priviledged way to share +-- contextual information across functions is through the 'cfg' table +-- argument. Its fields include: +-- +-- * code: code generated from pattern. A pattern_(element|seq|group) +-- is compiled as a sequence of instructions which will jump to +-- label [cfg.on_failure] if the tested term doesn't match. +-- +-- * on_failure: name of the label where the code will jump if the +-- pattern doesn't match +-- +-- * locals: names of local variables used by the pattern. This +-- includes bound variables, and temporary variables used to +-- destructure tables. Names are stored as keys of the table, +-- values are meaningless. +-- +-- * after_success: label where the code must jump after a pattern +-- succeeded to capture a term, and the guard suceeded if there is +-- any, and the conditional block has run. +-- +-- * ntmp: number of temporary variables used to destructurate table +-- in the current case. +-- +-- Code generation is performed by acc_xxx() functions, which accumulate +-- code in cfg.code: +-- +-- * acc_test(test, cfg) will generate a jump to cfg.on_failure +-- *when the test returns TRUE* +-- +-- * acc_stat accumulates a statement +-- +-- * acc_assign accumulate an assignment statement, and makes sure that +-- the LHS variable the registered as local in cfg.locals. +-- +------------------------------------------------------------------------------- + +-- TODO: hygiene wrt type() +-- TODO: cfg.ntmp isn't reset as often as it could. I'm not even sure +-- the corresponding locals are declared. + +local checks = require 'checks' +local gg = require 'metalua.grammar.generator' +local pp = require 'metalua.pprint' + +---------------------------------------------------------------------- +-- This would have been best done through library 'metalua.walk', +-- but walk depends on match, so we have to break the dependency. +-- It replaces all instances of `...' in `ast' with `term', unless +-- it appears in a function. +---------------------------------------------------------------------- +local function replace_dots (ast, term) + local function rec (node) + for i, child in ipairs(node) do + if type(child)~="table" then -- pass + elseif child.tag=='Dots' then + if term=='ambiguous' then + error ("You can't use `...' on the right of a match case when it appears ".. + "more than once on the left") + else node[i] = term end + elseif child.tag=='Function' then return nil + else rec(child) end + end + end + return rec(ast) +end + +local tmpvar_base = gg.gensym 'submatch.' [1] + +local function next_tmpvar(cfg) + assert (cfg.ntmp, "No cfg.ntmp imbrication level in the match compiler") + cfg.ntmp = cfg.ntmp+1 + return `Id{ tmpvar_base .. cfg.ntmp } +end + +-- Code accumulators +local acc_stat = |x,cfg| table.insert (cfg.code, x) +local acc_test = |x,cfg| acc_stat(+{stat: if -{x} then -{`Goto{cfg.on_failure}} end}, cfg) +-- lhs :: `Id{ string } +-- rhs :: expr +local function acc_assign (lhs, rhs, cfg) + assert(lhs.tag=='Id') + cfg.locals[lhs[1]] = true + acc_stat (`Set{ {lhs}, {rhs} }, cfg) +end + +local literal_tags = { String=1, Number=1, True=1, False=1, Nil=1 } + +-- pattern :: `Id{ string } +-- term :: expr +local function id_pattern_element_builder (pattern, term, cfg) + assert (pattern.tag == "Id") + if pattern[1] == "_" then + -- "_" is used as a dummy var ==> no assignment, no == checking + cfg.locals._ = true + elseif cfg.locals[pattern[1]] then + -- This var is already bound ==> test for equality + acc_test (+{ -{term} ~= -{pattern} }, cfg) + else + -- Free var ==> bind it, and remember it for latter linearity checking + acc_assign (pattern, term, cfg) + cfg.locals[pattern[1]] = true + end +end + +-- mutually recursive with table_pattern_element_builder +local pattern_element_builder + +-- pattern :: pattern and `Table{ } +-- term :: expr +local function table_pattern_element_builder (pattern, term, cfg) + local seen_dots, len = false, 0 + acc_test (+{ type( -{term} ) ~= "table" }, cfg) + for i = 1, #pattern do + local key, sub_pattern + if pattern[i].tag=="Pair" then -- Explicit key/value pair + key, sub_pattern = unpack (pattern[i]) + assert (literal_tags[key.tag], "Invalid key") + else -- Implicit key + len, key, sub_pattern = len+1, `Number{ len+1 }, pattern[i] + end + + -- '...' can only appear in final position + -- Could be fixed actually... + assert (not seen_dots, "Wrongly placed `...' ") + + if sub_pattern.tag == "Id" then + -- Optimization: save a useless [ v(n+1)=v(n).key ] + id_pattern_element_builder (sub_pattern, `Index{ term, key }, cfg) + if sub_pattern[1] ~= "_" then + acc_test (+{ -{sub_pattern} == nil }, cfg) + end + elseif sub_pattern.tag == "Dots" then + -- Remember where the capture is, and thatt arity checking shouldn't occur + seen_dots = true + else + -- Business as usual: + local v2 = next_tmpvar(cfg) + acc_assign (v2, `Index{ term, key }, cfg) + pattern_element_builder (sub_pattern, v2, cfg) + -- TODO: restore ntmp? + end + end + if seen_dots then -- remember how to retrieve `...' + -- FIXME: check, but there might be cases where the variable -{term} + -- will be overridden in contrieved tables. + -- ==> save it now, and clean the setting statement if unused + if cfg.dots_replacement then cfg.dots_replacement = 'ambiguous' + else cfg.dots_replacement = +{ select (-{`Number{len}}, unpack(-{term})) } end + else -- Check arity + acc_test (+{ #-{term} ~= -{`Number{len}} }, cfg) + end +end + +-- mutually recursive with pattern_element_builder +local eq_pattern_element_builder, regexp_pattern_element_builder + +-- Concatenate code in [cfg.code], that will jump to label +-- [cfg.on_failure] if [pattern] doesn't match [term]. [pattern] +-- should be an identifier, or at least cheap to compute and +-- side-effects free. +-- +-- pattern :: pattern_element +-- term :: expr +function pattern_element_builder (pattern, term, cfg) + if literal_tags[pattern.tag] then + acc_test (+{ -{term} ~= -{pattern} }, cfg) + elseif "Id" == pattern.tag then + id_pattern_element_builder (pattern, term, cfg) + elseif "Op" == pattern.tag and "div" == pattern[1] then + regexp_pattern_element_builder (pattern, term, cfg) + elseif "Op" == pattern.tag and "eq" == pattern[1] then + eq_pattern_element_builder (pattern, term, cfg) + elseif "Table" == pattern.tag then + table_pattern_element_builder (pattern, term, cfg) + else + error ("Invalid pattern at ".. + tostring(pattern.lineinfo).. + ": "..pp.tostring(pattern, {hide_hash=true})) + end +end + +function eq_pattern_element_builder (pattern, term, cfg) + local _, pat1, pat2 = unpack (pattern) + local ntmp_save = cfg.ntmp + pattern_element_builder (pat1, term, cfg) + cfg.ntmp = ntmp_save + pattern_element_builder (pat2, term, cfg) +end + +-- pattern :: `Op{ 'div', string, list{`Id string} or `Id{ string }} +-- term :: expr +local function regexp_pattern_element_builder (pattern, term, cfg) + local op, regexp, sub_pattern = unpack(pattern) + + -- Sanity checks -- + assert (op=='div', "Don't know what to do with that op in a pattern") + assert (regexp.tag=="String", + "Left hand side operand for '/' in a pattern must be ".. + "a literal string representing a regular expression") + if sub_pattern.tag=="Table" then + for _, x in ipairs(sub_pattern) do + assert (x.tag=="Id" or x.tag=='Dots', + "Right hand side operand for '/' in a pattern must be ".. + "a list of identifiers") + end + else + assert (sub_pattern.tag=="Id", + "Right hand side operand for '/' in a pattern must be ".. + "an identifier or a list of identifiers") + end + + -- Regexp patterns can only match strings + acc_test (+{ type(-{term}) ~= 'string' }, cfg) + -- put all captures in a list + local capt_list = +{ { string.strmatch(-{term}, -{regexp}) } } + -- save them in a var_n for recursive decomposition + local v2 = next_tmpvar(cfg) + acc_stat (+{stat: local -{v2} = -{capt_list} }, cfg) + -- was capture successful? + acc_test (+{ not next (-{v2}) }, cfg) + pattern_element_builder (sub_pattern, v2, cfg) +end + + +-- Jumps to [cfg.on_faliure] if pattern_seq doesn't match +-- term_seq. +local function pattern_seq_builder (pattern_seq, term_seq, cfg) + if #pattern_seq ~= #term_seq then error ("Bad seq arity") end + cfg.locals = { } -- reset bound variables between alternatives + for i=1, #pattern_seq do + cfg.ntmp = 1 -- reset the tmp var generator + pattern_element_builder(pattern_seq[i], term_seq[i], cfg) + end +end + +-------------------------------------------------- +-- for each case i: +-- pattern_seq_builder_i: +-- * on failure, go to on_failure_i +-- * on success, go to on_success +-- label on_success: +-- block +-- goto after_success +-- label on_failure_i +-------------------------------------------------- +local function case_builder (case, term_seq, cfg) + local patterns_group, guard, block = unpack(case) + local on_success = gg.gensym 'on_success' [1] + for i = 1, #patterns_group do + local pattern_seq = patterns_group[i] + cfg.on_failure = gg.gensym 'match_fail' [1] + cfg.dots_replacement = false + pattern_seq_builder (pattern_seq, term_seq, cfg) + if i<#patterns_group then + acc_stat (`Goto{on_success}, cfg) + acc_stat (`Label{cfg.on_failure}, cfg) + end + end + acc_stat (`Label{on_success}, cfg) + if guard then acc_test (+{not -{guard}}, cfg) end + if cfg.dots_replacement then + replace_dots (block, cfg.dots_replacement) + end + block.tag = 'Do' + acc_stat (block, cfg) + acc_stat (`Goto{cfg.after_success}, cfg) + acc_stat (`Label{cfg.on_failure}, cfg) +end + +local function match_builder (x) + local term_seq, cases = unpack(x) + local cfg = { + code = `Do{ }, + after_success = gg.gensym "_after_success" } + + + -- Some sharing issues occur when modifying term_seq, + -- so it's replaced by a copy new_term_seq. + -- TODO: clean that up, and re-suppress the useless copies + -- (cf. remarks about capture bug below). + local new_term_seq = { } + + local match_locals + + -- Make sure that all tested terms are variables or literals + for i=1, #term_seq do + local t = term_seq[i] + -- Capture problem: the following would compile wrongly: + -- `match x with x -> end' + -- Temporary workaround: suppress the condition, so that + -- all external variables are copied into unique names. + --if t.tag ~= 'Id' and not literal_tags[t.tag] then + local v = gg.gensym 'v' + if not match_locals then match_locals = `Local{ {v}, {t} } else + table.insert(match_locals[1], v) + table.insert(match_locals[2], t) + end + new_term_seq[i] = v + --end + end + term_seq = new_term_seq + + if match_locals then acc_stat(match_locals, cfg) end + + for i=1, #cases do + local case_cfg = { + after_success = cfg.after_success, + code = `Do{ } + -- locals = { } -- unnecessary, done by pattern_seq_builder + } + case_builder (cases[i], term_seq, case_cfg) + if next (case_cfg.locals) then + local case_locals = { } + table.insert (case_cfg.code, 1, `Local{ case_locals, { } }) + for v, _ in pairs (case_cfg.locals) do + table.insert (case_locals, `Id{ v }) + end + end + acc_stat(case_cfg.code, cfg) + end + local li = `String{tostring(cases.lineinfo)} + acc_stat(+{error('mismatch at '..-{li})}, cfg) + acc_stat(`Label{cfg.after_success}, cfg) + return cfg.code +end + +---------------------------------------------------------------------- +-- Syntactical front-end +---------------------------------------------------------------------- + +local function extend(M) + + local _M = gg.future(M) + + checks('metalua.compiler.parser') + M.lexer:add{ "match", "with", "->" } + M.block.terminators:add "|" + + local match_cases_list_parser = gg.list{ name = "match cases list", + gg.sequence{ name = "match case", + gg.list{ name = "match case patterns list", + primary = _M.expr_list, + separators = "|", + terminators = { "->", "if" } }, + gg.onkeyword{ "if", _M.expr, consume = true }, + "->", + _M.block }, + separators = "|", + terminators = "end" } + + M.stat:add{ name = "match statement", + "match", + _M.expr_list, + "with", gg.optkeyword "|", + match_cases_list_parser, + "end", + builder = |x| match_builder{ x[1], x[3] } } +end + +return extend \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/grammar/generator.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/grammar/generator.lua new file mode 100644 index 000000000..2680d8a53 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/grammar/generator.lua @@ -0,0 +1,834 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- +-- Summary: parser generator. Collection of higher order functors, +-- which allow to build and combine parsers. Relies on a lexer +-- that supports the same API as the one exposed in mll.lua. +-- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- +-- Exported API: +-- +-- Parser generators: +-- * [gg.sequence()] +-- * [gg.multisequence()] +-- * [gg.expr()] +-- * [gg.list()] +-- * [gg.onkeyword()] +-- * [gg.optkeyword()] +-- +-- Other functions: +-- * [gg.parse_error()] +-- * [gg.make_parser()] +-- * [gg.is_parser()] +-- +-------------------------------------------------------------------------------- + +local M = { } + +local checks = require 'checks' +local lexer = require 'metalua.grammar.lexer' +local pp = require 'metalua.pprint' + +-------------------------------------------------------------------------------- +-- Symbol generator: [gensym()] returns a guaranteed-to-be-unique identifier. +-- The main purpose is to avoid variable capture in macros. +-- +-- If a string is passed as an argument, theis string will be part of the +-- id name (helpful for macro debugging) +-------------------------------------------------------------------------------- +local gensymidx = 0 + +function M.gensym (arg) + gensymidx = gensymidx + 1 + return { tag="Id", string.format(".%i.%s", gensymidx, arg or "")} +end + + +------------------------------------------------------------------------------- +-- parser metatable, which maps __call to method parse, and adds some +-- error tracing boilerplate. +------------------------------------------------------------------------------- +local parser_metatable = { } + +function parser_metatable :__call (lx, ...) + return self :parse (lx, ...) +end + +------------------------------------------------------------------------------- +-- Turn a table into a parser, mainly by setting the metatable. +------------------------------------------------------------------------------- +function M.make_parser(kind, p) + p.kind = kind + if not p.transformers then p.transformers = { } end + function p.transformers:add (x) + table.insert (self, x) + end + setmetatable (p, parser_metatable) + return p +end + +------------------------------------------------------------------------------- +-- Return true iff [x] is a parser. +-- If it's a gg-generated parser, return the name of its kind. +------------------------------------------------------------------------------- +function M.is_parser (x) + return type(x)=="function" or getmetatable(x)==parser_metatable and x.kind +end + +------------------------------------------------------------------------------- +-- Parse a sequence, without applying builder nor transformers. +------------------------------------------------------------------------------- +local function raw_parse_sequence (lx, p) + local r = { } + for i=1, #p do + local e=p[i] + if type(e) == "string" then + local kw = lx :next() + if not lx :is_keyword (kw, e) then + M.parse_error( + lx, "A keyword was expected, probably `%s'.", e) + end + elseif M.is_parser (e) then + table.insert (r, e(lx)) + else -- Invalid parser definition, this is *not* a parsing error + error(string.format( + "Sequence `%s': element #%i is neither a string nor a parser: %s", + p.name, i, pp.tostring(e))) + end + end + return r +end + +------------------------------------------------------------------------------- +-- Parse a multisequence, without applying multisequence transformers. +-- The sequences are completely parsed. +------------------------------------------------------------------------------- +local function raw_parse_multisequence (lx, sequence_table, default) + local seq_parser = sequence_table[lx:is_keyword(lx:peek())] + if seq_parser then return seq_parser (lx) + elseif default then return default (lx) + else return false end +end + +------------------------------------------------------------------------------- +-- Applies all transformers listed in parser on ast. +------------------------------------------------------------------------------- +local function transform (ast, parser, fli, lli) + if parser.transformers then + for _, t in ipairs (parser.transformers) do ast = t(ast) or ast end + end + if type(ast) == 'table' then + local ali = ast.lineinfo + if not ali or ali.first~=fli or ali.last~=lli then + ast.lineinfo = lexer.new_lineinfo(fli, lli) + end + end + return ast +end + +------------------------------------------------------------------------------- +-- Generate a tracable parsing error (not implemented yet) +------------------------------------------------------------------------------- +function M.parse_error(lx, fmt, ...) + local li = lx:lineinfo_left() + local file, line, column, offset, positions + if li then + file, line, column, offset = li.source, li.line, li.column, li.offset + positions = { first = li, last = li } + else + line, column, offset = -1, -1, -1 + end + + local msg = string.format("line %i, char %i: "..fmt, line, column, ...) + if file and file~='?' then msg = "file "..file..", "..msg end + + local src = lx.src + if offset>0 and src then + local i, j = offset, offset + while src:sub(i,i) ~= '\n' and i>=0 do i=i-1 end + while src:sub(j,j) ~= '\n' and j<=#src do j=j+1 end + local srcline = src:sub (i+1, j-1) + local idx = string.rep (" ", column).."^" + msg = string.format("%s\n>>> %s\n>>> %s", msg, srcline, idx) + end + --lx :kill() + error(msg) +end + +------------------------------------------------------------------------------- +-- +-- Sequence parser generator +-- +------------------------------------------------------------------------------- +-- Input fields: +-- +-- * [builder]: how to build an AST out of sequence parts. let [x] be the list +-- of subparser results (keywords are simply omitted). [builder] can be: +-- - [nil], in which case the result of parsing is simply [x] +-- - a string, which is then put as a tag on [x] +-- - a function, which takes [x] as a parameter and returns an AST. +-- +-- * [name]: the name of the parser. Used for debug messages +-- +-- * [transformers]: a list of AST->AST functions, applied in order on ASTs +-- returned by the parser. +-- +-- * Table-part entries corresponds to keywords (strings) and subparsers +-- (function and callable objects). +-- +-- After creation, the following fields are added: +-- * [parse] the parsing function lexer->AST +-- * [kind] == "sequence" +-- * [name] is set, if it wasn't in the input. +-- +------------------------------------------------------------------------------- +function M.sequence (p) + M.make_parser ("sequence", p) + + ------------------------------------------------------------------- + -- Parsing method + ------------------------------------------------------------------- + function p:parse (lx) + + -- Raw parsing: + local fli = lx:lineinfo_right() + local seq = raw_parse_sequence (lx, self) + local lli = lx:lineinfo_left() + + -- Builder application: + local builder, tb = self.builder, type (self.builder) + if tb == "string" then seq.tag = builder + elseif tb == "function" or builder and builder.__call then seq = builder(seq) + elseif builder == nil then -- nothing + else error ("Invalid builder of type "..tb.." in sequence") end + seq = transform (seq, self, fli, lli) + assert (not seq or seq.lineinfo) + return seq + end + + ------------------------------------------------------------------- + -- Construction + ------------------------------------------------------------------- + -- Try to build a proper name + if p.name then + -- don't touch existing name + elseif type(p[1])=="string" then -- find name based on 1st keyword + if #p==1 then p.name=p[1] + elseif type(p[#p])=="string" then + p.name = p[1] .. " ... " .. p[#p] + else p.name = p[1] .. " ..." end + else -- can't find a decent name + p.name = "unnamed_sequence" + end + + return p +end -- + + +------------------------------------------------------------------------------- +-- +-- Multiple, keyword-driven, sequence parser generator +-- +------------------------------------------------------------------------------- +-- in [p], useful fields are: +-- +-- * [transformers]: as usual +-- +-- * [name]: as usual +-- +-- * Table-part entries must be sequence parsers, or tables which can +-- be turned into a sequence parser by [gg.sequence]. These +-- sequences must start with a keyword, and this initial keyword +-- must be different for each sequence. The table-part entries will +-- be removed after [gg.multisequence] returns. +-- +-- * [default]: the parser to run if the next keyword in the lexer is +-- none of the registered initial keywords. If there's no default +-- parser and no suitable initial keyword, the multisequence parser +-- simply returns [false]. +-- +-- After creation, the following fields are added: +-- +-- * [parse] the parsing function lexer->AST +-- +-- * [sequences] the table of sequences, indexed by initial keywords. +-- +-- * [add] method takes a sequence parser or a config table for +-- [gg.sequence], and adds/replaces the corresponding sequence +-- parser. If the keyword was already used, the former sequence is +-- removed and a warning is issued. +-- +-- * [get] method returns a sequence by its initial keyword +-- +-- * [kind] == "multisequence" +-- +------------------------------------------------------------------------------- +function M.multisequence (p) + M.make_parser ("multisequence", p) + + ------------------------------------------------------------------- + -- Add a sequence (might be just a config table for [gg.sequence]) + ------------------------------------------------------------------- + function p :add (s) + -- compile if necessary: + local keyword = type(s)=='table' and s[1] + if type(s)=='table' and not M.is_parser(s) then M.sequence(s) end + if M.is_parser(s)~='sequence' or type(keyword)~='string' then + if self.default then -- two defaults + error ("In a multisequence parser, all but one sequences ".. + "must start with a keyword") + else self.default = s end -- first default + else + if self.sequences[keyword] then -- duplicate keyword + -- TODO: warn that initial keyword `keyword` is overloaded in multiseq + end + self.sequences[keyword] = s + end + end -- + + ------------------------------------------------------------------- + -- Get the sequence starting with this keyword. [kw :: string] + ------------------------------------------------------------------- + function p :get (kw) return self.sequences [kw] end + + ------------------------------------------------------------------- + -- Remove the sequence starting with keyword [kw :: string] + ------------------------------------------------------------------- + function p :del (kw) + if not self.sequences[kw] then + -- TODO: warn that we try to delete a non-existent entry + end + local removed = self.sequences[kw] + self.sequences[kw] = nil + return removed + end + + ------------------------------------------------------------------- + -- Parsing method + ------------------------------------------------------------------- + function p :parse (lx) + local fli = lx:lineinfo_right() + local x = raw_parse_multisequence (lx, self.sequences, self.default) + local lli = lx:lineinfo_left() + return transform (x, self, fli, lli) + end + + ------------------------------------------------------------------- + -- Construction + ------------------------------------------------------------------- + -- Register the sequences passed to the constructor. They're going + -- from the array part of the parser to the hash part of field + -- [sequences] + p.sequences = { } + for i=1, #p do p :add (p[i]); p[i] = nil end + + -- FIXME: why is this commented out? + --if p.default and not is_parser(p.default) then sequence(p.default) end + return p +end -- + + +------------------------------------------------------------------------------- +-- +-- Expression parser generator +-- +------------------------------------------------------------------------------- +-- +-- Expression configuration relies on three tables: [prefix], [infix] +-- and [suffix]. Moreover, the primary parser can be replaced by a +-- table: in this case the [primary] table will be passed to +-- [gg.multisequence] to create a parser. +-- +-- Each of these tables is a modified multisequence parser: the +-- differences with respect to regular multisequence config tables are: +-- +-- * the builder takes specific parameters: +-- - for [prefix], it takes the result of the prefix sequence parser, +-- and the prefixed expression +-- - for [infix], it takes the left-hand-side expression, the results +-- of the infix sequence parser, and the right-hand-side expression. +-- - for [suffix], it takes the suffixed expression, and the result +-- of the suffix sequence parser. +-- +-- * the default field is a list, with parameters: +-- - [parser] the raw parsing function +-- - [transformers], as usual +-- - [prec], the operator's precedence +-- - [assoc] for [infix] table, the operator's associativity, which +-- can be "left", "right" or "flat" (default to left) +-- +-- In [p], useful fields are: +-- * [transformers]: as usual +-- * [name]: as usual +-- * [primary]: the atomic expression parser, or a multisequence config +-- table (mandatory) +-- * [prefix]: prefix operators config table, see above. +-- * [infix]: infix operators config table, see above. +-- * [suffix]: suffix operators config table, see above. +-- +-- After creation, these fields are added: +-- * [kind] == "expr" +-- * [parse] as usual +-- * each table is turned into a multisequence, and therefore has an +-- [add] method +-- +------------------------------------------------------------------------------- +function M.expr (p) + M.make_parser ("expr", p) + + ------------------------------------------------------------------- + -- parser method. + -- In addition to the lexer, it takes an optional precedence: + -- it won't read expressions whose precedence is lower or equal + -- to [prec]. + ------------------------------------------------------------------- + function p :parse (lx, prec) + prec = prec or 0 + + ------------------------------------------------------ + -- Extract the right parser and the corresponding + -- options table, for (pre|in|suff)fix operators. + -- Options include prec, assoc, transformers. + ------------------------------------------------------ + local function get_parser_info (tab) + local p2 = tab :get (lx :is_keyword (lx :peek())) + if p2 then -- keyword-based sequence found + local function parser(lx) return raw_parse_sequence(lx, p2) end + return parser, p2 + else -- Got to use the default parser + local d = tab.default + if d then return d.parse or d.parser, d + else return false, false end + end + end + + ------------------------------------------------------ + -- Look for a prefix sequence. Multiple prefixes are + -- handled through the recursive [p.parse] call. + -- Notice the double-transform: one for the primary + -- expr, and one for the one with the prefix op. + ------------------------------------------------------ + local function handle_prefix () + local fli = lx :lineinfo_right() + local p2_func, p2 = get_parser_info (self.prefix) + local op = p2_func and p2_func (lx) + if op then -- Keyword-based sequence found + local ili = lx :lineinfo_right() -- Intermediate LineInfo + local e = p2.builder (op, self :parse (lx, p2.prec)) + local lli = lx :lineinfo_left() + return transform (transform (e, p2, ili, lli), self, fli, lli) + else -- No prefix found, get a primary expression + local e = self.primary(lx) + local lli = lx :lineinfo_left() + return transform (e, self, fli, lli) + end + end -- + + ------------------------------------------------------ + -- Look for an infix sequence+right-hand-side operand. + -- Return the whole binary expression result, + -- or false if no operator was found. + ------------------------------------------------------ + local function handle_infix (e) + local p2_func, p2 = get_parser_info (self.infix) + if not p2 then return false end + + ----------------------------------------- + -- Handle flattening operators: gather all operands + -- of the series in [list]; when a different operator + -- is found, stop, build from [list], [transform] and + -- return. + ----------------------------------------- + if (not p2.prec or p2.prec>prec) and p2.assoc=="flat" then + local fli = lx:lineinfo_right() + local pflat, list = p2, { e } + repeat + local op = p2_func(lx) + if not op then break end + table.insert (list, self:parse (lx, p2.prec)) + local _ -- We only care about checking that p2==pflat + _, p2 = get_parser_info (self.infix) + until p2 ~= pflat + local e2 = pflat.builder (list) + local lli = lx:lineinfo_left() + return transform (transform (e2, pflat, fli, lli), self, fli, lli) + + ----------------------------------------- + -- Handle regular infix operators: [e] the LHS is known, + -- just gather the operator and [e2] the RHS. + -- Result goes in [e3]. + ----------------------------------------- + elseif p2.prec and p2.prec>prec or + p2.prec==prec and p2.assoc=="right" then + local fli = e.lineinfo.first -- lx:lineinfo_right() + local op = p2_func(lx) + if not op then return false end + local e2 = self:parse (lx, p2.prec) + local e3 = p2.builder (e, op, e2) + local lli = lx:lineinfo_left() + return transform (transform (e3, p2, fli, lli), self, fli, lli) + + ----------------------------------------- + -- Check for non-associative operators, and complain if applicable. + ----------------------------------------- + elseif p2.assoc=="none" and p2.prec==prec then + M.parse_error (lx, "non-associative operator!") + + ----------------------------------------- + -- No infix operator suitable at that precedence + ----------------------------------------- + else return false end + + end -- + + ------------------------------------------------------ + -- Look for a suffix sequence. + -- Return the result of suffix operator on [e], + -- or false if no operator was found. + ------------------------------------------------------ + local function handle_suffix (e) + -- FIXME bad fli, must take e.lineinfo.first + local p2_func, p2 = get_parser_info (self.suffix) + if not p2 then return false end + if not p2.prec or p2.prec>=prec then + --local fli = lx:lineinfo_right() + local fli = e.lineinfo.first + local op = p2_func(lx) + if not op then return false end + local lli = lx:lineinfo_left() + e = p2.builder (e, op) + e = transform (transform (e, p2, fli, lli), self, fli, lli) + return e + end + return false + end -- + + ------------------------------------------------------ + -- Parser body: read suffix and (infix+operand) + -- extensions as long as we're able to fetch more at + -- this precedence level. + ------------------------------------------------------ + local e = handle_prefix() + repeat + local x = handle_suffix (e); e = x or e + local y = handle_infix (e); e = y or e + until not (x or y) + + -- No transform: it already happened in operators handling + return e + end -- + + ------------------------------------------------------------------- + -- Construction + ------------------------------------------------------------------- + if not p.primary then p.primary=p[1]; p[1]=nil end + for _, t in ipairs{ "primary", "prefix", "infix", "suffix" } do + if not p[t] then p[t] = { } end + if not M.is_parser(p[t]) then M.multisequence(p[t]) end + end + function p:add(...) return self.primary:add(...) end + return p +end -- + + +------------------------------------------------------------------------------- +-- +-- List parser generator +-- +------------------------------------------------------------------------------- +-- In [p], the following fields can be provided in input: +-- +-- * [builder]: takes list of subparser results, returns AST +-- * [transformers]: as usual +-- * [name]: as usual +-- +-- * [terminators]: list of strings representing the keywords which +-- might mark the end of the list. When non-empty, the list is +-- allowed to be empty. A string is treated as a single-element +-- table, whose element is that string, e.g. ["do"] is the same as +-- [{"do"}]. +-- +-- * [separators]: list of strings representing the keywords which can +-- separate elements of the list. When non-empty, one of these +-- keyword has to be found between each element. Lack of a separator +-- indicates the end of the list. A string is treated as a +-- single-element table, whose element is that string, e.g. ["do"] +-- is the same as [{"do"}]. If [terminators] is empty/nil, then +-- [separators] has to be non-empty. +-- +-- After creation, the following fields are added: +-- * [parse] the parsing function lexer->AST +-- * [kind] == "list" +-- +------------------------------------------------------------------------------- +function M.list (p) + M.make_parser ("list", p) + + ------------------------------------------------------------------- + -- Parsing method + ------------------------------------------------------------------- + function p :parse (lx) + + ------------------------------------------------------ + -- Used to quickly check whether there's a terminator + -- or a separator immediately ahead + ------------------------------------------------------ + local function peek_is_in (keywords) + return keywords and lx:is_keyword(lx:peek(), unpack(keywords)) end + + local x = { } + local fli = lx :lineinfo_right() + + -- if there's a terminator to start with, don't bother trying + local is_empty_list = self.terminators and (peek_is_in (self.terminators) or lx:peek().tag=="Eof") + if not is_empty_list then + repeat + local item = self.primary(lx) + table.insert (x, item) -- read one element + until + -- There's a separator list specified, and next token isn't in it. + -- Otherwise, consume it with [lx:next()] + self.separators and not(peek_is_in (self.separators) and lx:next()) or + -- Terminator token ahead + peek_is_in (self.terminators) or + -- Last reason: end of file reached + lx:peek().tag=="Eof" + end + + local lli = lx:lineinfo_left() + + -- Apply the builder. It can be a string, or a callable value, + -- or simply nothing. + local b = self.builder + if b then + if type(b)=="string" then x.tag = b -- b is a string, use it as a tag + elseif type(b)=="function" then x=b(x) + else + local bmt = getmetatable(b) + if bmt and bmt.__call then x=b(x) end + end + end + return transform (x, self, fli, lli) + end -- + + ------------------------------------------------------------------- + -- Construction + ------------------------------------------------------------------- + if not p.primary then p.primary = p[1]; p[1] = nil end + if type(p.terminators) == "string" then p.terminators = { p.terminators } + elseif p.terminators and #p.terminators == 0 then p.terminators = nil end + if type(p.separators) == "string" then p.separators = { p.separators } + elseif p.separators and #p.separators == 0 then p.separators = nil end + + return p +end -- + + +------------------------------------------------------------------------------- +-- +-- Keyword-conditioned parser generator +-- +------------------------------------------------------------------------------- +-- +-- Only apply a parser if a given keyword is found. The result of +-- [gg.onkeyword] parser is the result of the subparser (modulo +-- [transformers] applications). +-- +-- lineinfo: the keyword is *not* included in the boundaries of the +-- resulting lineinfo. A review of all usages of gg.onkeyword() in the +-- implementation of metalua has shown that it was the appropriate choice +-- in every case. +-- +-- Input fields: +-- +-- * [name]: as usual +-- +-- * [transformers]: as usual +-- +-- * [peek]: if non-nil, the conditioning keyword is left in the lexeme +-- stream instead of being consumed. +-- +-- * [primary]: the subparser. +-- +-- * [keywords]: list of strings representing triggering keywords. +-- +-- * Table-part entries can contain strings, and/or exactly one parser. +-- Strings are put in [keywords], and the parser is put in [primary]. +-- +-- After the call, the following fields will be set: +-- +-- * [parse] the parsing method +-- * [kind] == "onkeyword" +-- * [primary] +-- * [keywords] +-- +------------------------------------------------------------------------------- +function M.onkeyword (p) + M.make_parser ("onkeyword", p) + + ------------------------------------------------------------------- + -- Parsing method + ------------------------------------------------------------------- + function p :parse (lx) + if lx :is_keyword (lx:peek(), unpack(self.keywords)) then + local fli = lx:lineinfo_right() + if not self.peek then lx:next() end + local content = self.primary (lx) + local lli = lx:lineinfo_left() + local li = content.lineinfo or { } + fli, lli = li.first or fli, li.last or lli + return transform (content, p, fli, lli) + else return false end + end + + ------------------------------------------------------------------- + -- Construction + ------------------------------------------------------------------- + if not p.keywords then p.keywords = { } end + for _, x in ipairs(p) do + if type(x)=="string" then table.insert (p.keywords, x) + else assert (not p.primary and M.is_parser (x)); p.primary = x end + end + assert (next (p.keywords), "Missing trigger keyword in gg.onkeyword") + assert (p.primary, 'no primary parser in gg.onkeyword') + return p +end -- + + +------------------------------------------------------------------------------- +-- +-- Optional keyword consummer pseudo-parser generator +-- +------------------------------------------------------------------------------- +-- +-- This doesn't return a real parser, just a function. That function parses +-- one of the keywords passed as parameters, and returns it. It returns +-- [false] if no matching keyword is found. +-- +-- Notice that tokens returned by lexer already carry lineinfo, therefore +-- there's no need to add them, as done usually through transform() calls. +------------------------------------------------------------------------------- +function M.optkeyword (...) + local args = {...} + if type (args[1]) == "table" then + assert (#args == 1) + args = args[1] + end + for _, v in ipairs(args) do assert (type(v)=="string") end + return function (lx) + local x = lx:is_keyword (lx:peek(), unpack (args)) + if x then lx:next(); return x + else return false end + end +end + + +------------------------------------------------------------------------------- +-- +-- Run a parser with a special lexer +-- +------------------------------------------------------------------------------- +-- +-- This doesn't return a real parser, just a function. +-- First argument is the lexer class to be used with the parser, +-- 2nd is the parser itself. +-- The resulting parser returns whatever the argument parser does. +-- +------------------------------------------------------------------------------- +function M.with_lexer(new_lexer, parser) + + ------------------------------------------------------------------- + -- Most gg functions take their parameters in a table, so it's + -- better to silently accept when with_lexer{ } is called with + -- its arguments in a list: + ------------------------------------------------------------------- + if not parser and #new_lexer==2 and type(new_lexer[1])=='table' then + return M.with_lexer(unpack(new_lexer)) + end + + ------------------------------------------------------------------- + -- Save the current lexer, switch it for the new one, run the parser, + -- restore the previous lexer, even if the parser caused an error. + ------------------------------------------------------------------- + return function (lx) + local old_lexer = getmetatable(lx) + lx:sync() + setmetatable(lx, new_lexer) + local status, result = pcall(parser, lx) + lx:sync() + setmetatable(lx, old_lexer) + if status then return result else error(result) end + end +end + +-------------------------------------------------------------------------------- +-- +-- Make sure a parser is used and returns successfully. +-- +-------------------------------------------------------------------------------- +function M.nonempty(primary) + local p = M.make_parser('non-empty list', { primary = primary, name=primary.name }) + function p :parse (lx) + local fli = lx:lineinfo_right() + local content = self.primary (lx) + local lli = lx:lineinfo_left() + local li = content.lineinfo or { } + fli, lli = li.first or fli, li.last or lli + if #content == 0 then + M.parse_error (lx, "`%s' must not be empty.", self.name or "list") + else + return transform (content, self, fli, lli) + end + end + return p +end + +local FUTURE_MT = { } +function FUTURE_MT:__tostring() return "" end +function FUTURE_MT:__newindex(key, value) error "don't write in futures" end +function FUTURE_MT :__index (parser_name) + return function(...) + local p, m = rawget(self, '__path'), self.__module + if p then for _, name in ipairs(p) do + m=rawget(m, name) + if not m then error ("Submodule '"..name.."' undefined") end + end end + local f = rawget(m, parser_name) + if not f then error ("Parser '"..parser_name.."' undefined") end + return f(...) + end +end + +function M.future(module, ...) + checks('table') + local path = ... and {...} + if path then for _, x in ipairs(path) do + assert(type(x)=='string', "Bad future arg") + end end + local self = { __module = module, + __path = path } + return setmetatable(self, FUTURE_MT) +end + +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/grammar/lexer.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/grammar/lexer.lua new file mode 100644 index 000000000..0f96f8edf --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/grammar/lexer.lua @@ -0,0 +1,672 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +------------------------------------------------------------------------------- + +local checks = require 'checks' + +local M = { } + +local lexer = { alpha={ }, sym={ } } +lexer.__index=lexer +lexer.__type='lexer.stream' + +M.lexer = lexer + + +local debugf = function() end +-- local debugf=printf + +---------------------------------------------------------------------- +-- Some locale settings produce bad results, e.g. French locale +-- expect float numbers to use commas instead of periods. +-- TODO: change number parser into something loclae-independent, +-- locales are nasty. +---------------------------------------------------------------------- +os.setlocale('C') + +local MT = { } + +M.metatables=MT + +---------------------------------------------------------------------- +-- Create a new metatable, for a new class of objects. +---------------------------------------------------------------------- +local function new_metatable(name) + local mt = { __type = 'lexer.'..name }; + mt.__index = mt + MT[name] = mt +end + + +---------------------------------------------------------------------- +-- Position: represent a point in a source file. +---------------------------------------------------------------------- +new_metatable 'position' + +local position_idx=1 + +function M.new_position(line, column, offset, source) + checks('number', 'number', 'number', 'string') + local id = position_idx; position_idx = position_idx+1 + return setmetatable({line=line, column=column, offset=offset, + source=source, id=id}, MT.position) +end + +function MT.position :__tostring() + return string.format("<%s%s|L%d|C%d|K%d>", + self.comments and "C|" or "", + self.source, self.line, self.column, self.offset) +end + + + +---------------------------------------------------------------------- +-- Position factory: convert offsets into line/column/offset positions. +---------------------------------------------------------------------- +new_metatable 'position_factory' + +function M.new_position_factory(src, src_name) + -- assert(type(src)=='string') + -- assert(type(src_name)=='string') + local lines = { 1 } + for offset in src :gmatch '\n()' do table.insert(lines, offset) end + local max = #src+1 + table.insert(lines, max+1) -- +1 includes Eof + return setmetatable({ src_name=src_name, line2offset=lines, max=max }, + MT.position_factory) +end + +function MT.position_factory :get_position (offset) + -- assert(type(offset)=='number') + assert(offset<=self.max) + local line2offset = self.line2offset + local left = self.last_left or 1 + if offset", + fli.comments and "C|" or "", + fli.source, line, column, offset, + lli.comments and "|C" or "") +end + +---------------------------------------------------------------------- +-- Token: atomic Lua language element, with a category, a content, +-- and some lineinfo relating it to its original source. +---------------------------------------------------------------------- +new_metatable 'token' + +function M.new_token(tag, content, lineinfo) + --printf("TOKEN `%s{ %q, lineinfo = %s} boundaries %d, %d", + -- tag, content, tostring(lineinfo), lineinfo.first.id, lineinfo.last.id) + return setmetatable({tag=tag, lineinfo=lineinfo, content}, MT.token) +end + +function MT.token :__tostring() + --return string.format("`%s{ %q, %s }", self.tag, self[1], tostring(self.lineinfo)) + return string.format("`%s %q", self.tag, self[1]) +end + + +---------------------------------------------------------------------- +-- Comment: series of comment blocks with associated lineinfo. +-- To be attached to the tokens just before and just after them. +---------------------------------------------------------------------- +new_metatable 'comment' + +function M.new_comment(lines) + local first = lines[1].lineinfo.first + local last = lines[#lines].lineinfo.last + local lineinfo = M.new_lineinfo(first, last) + return setmetatable({lineinfo=lineinfo, unpack(lines)}, MT.comment) +end + +function MT.comment :text() + local last_line = self[1].lineinfo.last.line + local acc = { } + for i, line in ipairs(self) do + local nreturns = line.lineinfo.first.line - last_line + table.insert(acc, ("\n"):rep(nreturns)) + table.insert(acc, line[1]) + end + return table.concat(acc) +end + +function M.new_comment_line(text, lineinfo, nequals) + checks('string', 'lexer.lineinfo', '?number') + return { lineinfo = lineinfo, text, nequals } +end + + + +---------------------------------------------------------------------- +-- Patterns used by [lexer :extract] to decompose the raw string into +-- correctly tagged tokens. +---------------------------------------------------------------------- +lexer.patterns = { + spaces = "^[ \r\n\t]*()", + short_comment = "^%-%-([^\n]*)\n?()", + --final_short_comment = "^%-%-([^\n]*)()$", + long_comment = "^%-%-%[(=*)%[\n?(.-)%]%1%]()", + long_string = "^%[(=*)%[\n?(.-)%]%1%]()", + number_mantissa = { "^%d+%.?%d*()", "^%d*%.%d+()" }, + number_mantissa_hex = { "^%x+%.?%x*()", "^%x*%.%x+()" }, --Lua5.1 and Lua5.2 + number_exponant = "^[eE][%+%-]?%d+()", + number_exponant_hex = "^[pP][%+%-]?%d+()", --Lua5.2 + number_hex = "^0[xX]()", + word = "^([%a_][%w_]*)()" +} + +---------------------------------------------------------------------- +-- unescape a whole string, applying [unesc_digits] and +-- [unesc_letter] as many times as required. +---------------------------------------------------------------------- +local function unescape_string (s) + + -- Turn the digits of an escape sequence into the corresponding + -- character, e.g. [unesc_digits("123") == string.char(123)]. + local function unesc_digits (backslashes, digits) + if #backslashes%2==0 then + -- Even number of backslashes, they escape each other, not the digits. + -- Return them so that unesc_letter() can treat them + return backslashes..digits + else + -- Remove the odd backslash, which escapes the number sequence. + -- The rest will be returned and parsed by unesc_letter() + backslashes = backslashes :sub (1,-2) + end + local k, j, i = digits :reverse() :byte(1, 3) + local z = string.byte "0" + local code = (k or z) + 10*(j or z) + 100*(i or z) - 111*z + if code > 255 then + error ("Illegal escape sequence '\\"..digits.. + "' in string: ASCII codes must be in [0..255]") + end + local c = string.char (code) + if c == '\\' then c = '\\\\' end -- parsed by unesc_letter (test: "\092b" --> "\\b") + return backslashes..c + end + + -- Turn hex digits of escape sequence into char. + local function unesc_hex(backslashes, digits) + if #backslashes%2==0 then + return backslashes..'x'..digits + else + backslashes = backslashes :sub (1,-2) + end + local c = string.char(tonumber(digits,16)) + if c == '\\' then c = '\\\\' end -- parsed by unesc_letter (test: "\x5cb" --> "\\b") + return backslashes..c + end + + -- Handle Lua 5.2 \z sequences + local function unesc_z(backslashes, more) + if #backslashes%2==0 then + return backslashes..more + else + return backslashes :sub (1,-2) + end + end + + -- Take a letter [x], and returns the character represented by the + -- sequence ['\\'..x], e.g. [unesc_letter "n" == "\n"]. + local function unesc_letter(x) + local t = { + a = "\a", b = "\b", f = "\f", + n = "\n", r = "\r", t = "\t", v = "\v", + ["\\"] = "\\", ["'"] = "'", ['"'] = '"', ["\n"] = "\n" } + return t[x] or x + end + + s = s: gsub ("(\\+)(z%s*)", unesc_z) -- Lua 5.2 + s = s: gsub ("(\\+)([0-9][0-9]?[0-9]?)", unesc_digits) + s = s: gsub ("(\\+)x([0-9a-fA-F][0-9a-fA-F])", unesc_hex) -- Lua 5.2 + s = s: gsub ("\\(%D)",unesc_letter) + return s +end + +lexer.extractors = { + "extract_long_comment", "extract_short_comment", + "extract_short_string", "extract_word", "extract_number", + "extract_long_string", "extract_symbol" } + + + +---------------------------------------------------------------------- +-- Really extract next token from the raw string +-- (and update the index). +-- loc: offset of the position just after spaces and comments +-- previous_i: offset in src before extraction began +---------------------------------------------------------------------- +function lexer :extract () + local attached_comments = { } + local function gen_token(...) + local token = M.new_token(...) + if #attached_comments>0 then -- attach previous comments to token + local comments = M.new_comment(attached_comments) + token.lineinfo.first.comments = comments + if self.lineinfo_last_extracted then + self.lineinfo_last_extracted.comments = comments + end + attached_comments = { } + end + token.lineinfo.first.facing = self.lineinfo_last_extracted + self.lineinfo_last_extracted.facing = assert(token.lineinfo.first) + self.lineinfo_last_extracted = assert(token.lineinfo.last) + return token + end + while true do -- loop until a non-comment token is found + + -- skip whitespaces + self.i = self.src:match (self.patterns.spaces, self.i) + if self.i>#self.src then + local fli = self.posfact :get_position (#self.src+1) + local lli = self.posfact :get_position (#self.src+1) -- ok? + local tok = gen_token("Eof", "eof", M.new_lineinfo(fli, lli)) + tok.lineinfo.last.facing = lli + return tok + end + local i_first = self.i -- loc = position after whitespaces + + -- try every extractor until a token is found + for _, extractor in ipairs(self.extractors) do + local tag, content, xtra = self [extractor] (self) + if tag then + local fli = self.posfact :get_position (i_first) + local lli = self.posfact :get_position (self.i-1) + local lineinfo = M.new_lineinfo(fli, lli) + if tag=='Comment' then + local prev_comment = attached_comments[#attached_comments] + if not xtra -- new comment is short + and prev_comment and not prev_comment[2] -- prev comment is short + and prev_comment.lineinfo.last.line+1==fli.line then -- adjascent lines + -- concat with previous comment + prev_comment[1] = prev_comment[1].."\n"..content -- TODO quadratic, BAD! + prev_comment.lineinfo.last = lli + else -- accumulate comment + local comment = M.new_comment_line(content, lineinfo, xtra) + table.insert(attached_comments, comment) + end + break -- back to skipping spaces + else -- not a comment: real token, then + return gen_token(tag, content, lineinfo) + end -- if token is a comment + end -- if token found + end -- for each extractor + end -- while token is a comment +end -- :extract() + + + + +---------------------------------------------------------------------- +-- Extract a short comment. +---------------------------------------------------------------------- +function lexer :extract_short_comment() + -- TODO: handle final_short_comment + local content, j = self.src :match (self.patterns.short_comment, self.i) + if content then self.i=j; return 'Comment', content, nil end +end + +---------------------------------------------------------------------- +-- Extract a long comment. +---------------------------------------------------------------------- +function lexer :extract_long_comment() + local equals, content, j = self.src:match (self.patterns.long_comment, self.i) + if j then self.i = j; return "Comment", content, #equals end +end + +---------------------------------------------------------------------- +-- Extract a '...' or "..." short string. +---------------------------------------------------------------------- +function lexer :extract_short_string() + local k = self.src :sub (self.i,self.i) -- first char + if k~=[[']] and k~=[["]] then return end -- no match' + local i = self.i + 1 + local j = i + while true do + local x,y; x, j, y = self.src :match ("([\\\r\n"..k.."])()(.?)", j) -- next interesting char + if x == '\\' then + if y == 'z' then -- Lua 5.2 \z + j = self.src :match ("^%s*()", j+1) + else + j=j+1 -- escaped char + end + elseif x == k then break -- end of string + else + assert (not x or x=='\r' or x=='\n') + return nil, 'Unterminated string' + end + end + self.i = j + + return 'String', unescape_string (self.src :sub (i,j-2)) +end + +---------------------------------------------------------------------- +-- Extract Id or Keyword. +---------------------------------------------------------------------- +function lexer :extract_word() + local word, j = self.src:match (self.patterns.word, self.i) + if word then + self.i = j + return (self.alpha [word] and 'Keyword' or 'Id'), word + end +end + +---------------------------------------------------------------------- +-- Extract Number. +---------------------------------------------------------------------- +function lexer :extract_number() + local j = self.src:match(self.patterns.number_hex, self.i) + if j then + j = self.src:match (self.patterns.number_mantissa_hex[1], j) or + self.src:match (self.patterns.number_mantissa_hex[2], j) + if j then + j = self.src:match (self.patterns.number_exponant_hex, j) or j + end + else + j = self.src:match (self.patterns.number_mantissa[1], self.i) or + self.src:match (self.patterns.number_mantissa[2], self.i) + if j then + j = self.src:match (self.patterns.number_exponant, j) or j + end + end + if not j then return end + -- Number found, interpret with tonumber() and return it + local str = self.src:sub (self.i, j-1) + -- :TODO: tonumber on Lua5.2 floating hex may or may not work on Lua5.1 + local n = tonumber (str) + if not n then error(str.." is not a valid number according to tonumber()") end + self.i = j + return 'Number', n +end + +---------------------------------------------------------------------- +-- Extract long string. +---------------------------------------------------------------------- +function lexer :extract_long_string() + local _, content, j = self.src :match (self.patterns.long_string, self.i) + if j then self.i = j; return 'String', content end +end + +---------------------------------------------------------------------- +-- Extract symbol. +---------------------------------------------------------------------- +function lexer :extract_symbol() + local k = self.src:sub (self.i,self.i) + local symk = self.sym [k] -- symbols starting with `k` + if not symk then + self.i = self.i + 1 + return 'Keyword', k + end + for _, sym in pairs (symk) do + if sym == self.src:sub (self.i, self.i + #sym - 1) then + self.i = self.i + #sym + return 'Keyword', sym + end + end + self.i = self.i+1 + return 'Keyword', k +end + +---------------------------------------------------------------------- +-- Add a keyword to the list of keywords recognized by the lexer. +---------------------------------------------------------------------- +function lexer :add (w, ...) + assert(not ..., "lexer :add() takes only one arg, although possibly a table") + if type (w) == "table" then + for _, x in ipairs (w) do self :add (x) end + else + if w:match (self.patterns.word .. "$") then self.alpha [w] = true + elseif w:match "^%p%p+$" then + local k = w:sub(1,1) + local list = self.sym [k] + if not list then list = { }; self.sym [k] = list end + table.insert (list, w) + elseif w:match "^%p$" then return + else error "Invalid keyword" end + end +end + +---------------------------------------------------------------------- +-- Return the [n]th next token, without consuming it. +-- [n] defaults to 1. If it goes pass the end of the stream, an EOF +-- token is returned. +---------------------------------------------------------------------- +function lexer :peek (n) + if not n then n=1 end + if n > #self.peeked then + for i = #self.peeked+1, n do + self.peeked [i] = self :extract() + end + end + return self.peeked [n] +end + +---------------------------------------------------------------------- +-- Return the [n]th next token, removing it as well as the 0..n-1 +-- previous tokens. [n] defaults to 1. If it goes pass the end of the +-- stream, an EOF token is returned. +---------------------------------------------------------------------- +function lexer :next (n) + n = n or 1 + self :peek (n) + local a + for i=1,n do + a = table.remove (self.peeked, 1) + -- TODO: is this used anywhere? I think not. a.lineinfo.last may be nil. + --self.lastline = a.lineinfo.last.line + end + self.lineinfo_last_consumed = a.lineinfo.last + return a +end + +---------------------------------------------------------------------- +-- Returns an object which saves the stream's current state. +---------------------------------------------------------------------- +-- FIXME there are more fields than that to save +function lexer :save () return { self.i; {unpack(self.peeked) } } end + +---------------------------------------------------------------------- +-- Restore the stream's state, as saved by method [save]. +---------------------------------------------------------------------- +-- FIXME there are more fields than that to restore +function lexer :restore (s) self.i=s[1]; self.peeked=s[2] end + +---------------------------------------------------------------------- +-- Resynchronize: cancel any token in self.peeked, by emptying the +-- list and resetting the indexes +---------------------------------------------------------------------- +function lexer :sync() + local p1 = self.peeked[1] + if p1 then + local li_first = p1.lineinfo.first + if li_first.comments then li_first=li_first.comments.lineinfo.first end + self.i = li_first.offset + self.column_offset = self.i - li_first.column + self.peeked = { } + self.attached_comments = p1.lineinfo.first.comments or { } + end +end + +---------------------------------------------------------------------- +-- Take the source and offset of an old lexer. +---------------------------------------------------------------------- +function lexer :takeover(old) + self :sync(); old :sync() + for _, field in ipairs{ 'i', 'src', 'attached_comments', 'posfact' } do + self[field] = old[field] + end + return self +end + +---------------------------------------------------------------------- +-- Return the current position in the sources. This position is between +-- two tokens, and can be within a space / comment area, and therefore +-- have a non-null width. :lineinfo_left() returns the beginning of the +-- separation area, :lineinfo_right() returns the end of that area. +-- +-- ____ last consummed token ____ first unconsummed token +-- / / +-- XXXXX YYYYY +-- \____ \____ +-- :lineinfo_left() :lineinfo_right() +---------------------------------------------------------------------- +function lexer :lineinfo_right() + return self :peek(1).lineinfo.first +end + +function lexer :lineinfo_left() + return self.lineinfo_last_consumed +end + +---------------------------------------------------------------------- +-- Create a new lexstream. +---------------------------------------------------------------------- +function lexer :newstream (src_or_stream, name) + name = name or "?" + if type(src_or_stream)=='table' then -- it's a stream + return setmetatable ({ }, self) :takeover (src_or_stream) + elseif type(src_or_stream)=='string' then -- it's a source string + local src = src_or_stream + local pos1 = M.new_position(1, 1, 1, name) + local stream = { + src_name = name; -- Name of the file + src = src; -- The source, as a single string + peeked = { }; -- Already peeked, but not discarded yet, tokens + i = 1; -- Character offset in src + attached_comments = { },-- comments accumulator + lineinfo_last_extracted = pos1, + lineinfo_last_consumed = pos1, + posfact = M.new_position_factory (src_or_stream, name) + } + setmetatable (stream, self) + + -- Skip initial sharp-bang for Unix scripts + -- FIXME: redundant with mlp.chunk() + if src and src :match "^#!" then + local endofline = src :find "\n" + stream.i = endofline and (endofline + 1) or #src + end + return stream + else + assert(false, ":newstream() takes a source string or a stream, not a ".. + type(src_or_stream)) + end +end + +---------------------------------------------------------------------- +-- If there's no ... args, return the token a (whose truth value is +-- true) if it's a `Keyword{ }, or nil. If there are ... args, they +-- have to be strings. if the token a is a keyword, and it's content +-- is one of the ... args, then returns it (it's truth value is +-- true). If no a keyword or not in ..., return nil. +---------------------------------------------------------------------- +function lexer :is_keyword (a, ...) + if not a or a.tag ~= "Keyword" then return false end + local words = {...} + if #words == 0 then return a[1] end + for _, w in ipairs (words) do + if w == a[1] then return w end + end + return false +end + +---------------------------------------------------------------------- +-- Cause an error if the next token isn't a keyword whose content +-- is listed among ... args (which have to be strings). +---------------------------------------------------------------------- +function lexer :check (...) + local words = {...} + local a = self :next() + local function err () + error ("Got " .. tostring (a) .. + ", expected one of these keywords : '" .. + table.concat (words,"', '") .. "'") end + if not a or a.tag ~= "Keyword" then err () end + if #words == 0 then return a[1] end + for _, w in ipairs (words) do + if w == a[1] then return w end + end + err () +end + +---------------------------------------------------------------------- +-- +---------------------------------------------------------------------- +function lexer :clone() + local alpha_clone, sym_clone = { }, { } + for word in pairs(self.alpha) do alpha_clone[word]=true end + for letter, list in pairs(self.sym) do sym_clone[letter] = { unpack(list) } end + local clone = { alpha=alpha_clone, sym=sym_clone } + setmetatable(clone, self) + clone.__index = clone + return clone +end + +---------------------------------------------------------------------- +-- Cancel everything left in a lexer, all subsequent attempts at +-- `:peek()` or `:next()` will return `Eof`. +---------------------------------------------------------------------- +function lexer :kill() + self.i = #self.src+1 + self.peeked = { } + self.attached_comments = { } + self.lineinfo_last = self.posfact :get_position (#self.src+1) +end + +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/loader.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/loader.lua new file mode 100644 index 000000000..e535fef32 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/loader.lua @@ -0,0 +1,133 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +-------------------------------------------------------------------------------- + +local M = require "package" -- extend Lua's basic "package" module +local checks = require 'checks' + +M.metalua_extension_prefix = 'metalua.extension.' + +-- Initialize package.mpath from package.path +M.mpath = M.mpath or os.getenv 'LUA_MPATH' or + (M.path..";") :gsub("%.(lua[:;])", ".m%1") :sub(1, -2) + +M.mcache = M.mcache or os.getenv 'LUA_MCACHE' + +---------------------------------------------------------------------- +-- resc(k) returns "%"..k if it's a special regular expression char, +-- or just k if it's normal. +---------------------------------------------------------------------- +local regexp_magic = { } +for k in ("^$()%.[]*+-?") :gmatch "." do regexp_magic[k]="%"..k end + +local function resc(k) return regexp_magic[k] or k end + +---------------------------------------------------------------------- +-- Take a Lua module name, return the open file and its name, +-- or and an error message. +---------------------------------------------------------------------- +function M.findfile(name, path_string) + local config_regexp = ("([^\n])\n"):rep(5):sub(1, -2) + local dir_sep, path_sep, path_mark, execdir, igmark = + M.config :match (config_regexp) + name = name:gsub ('%.', dir_sep) + local errors = { } + local path_pattern = string.format('[^%s]+', resc(path_sep)) + for path in path_string:gmatch (path_pattern) do + --printf('path = %s, rpath_mark=%s, name=%s', path, resc(path_mark), name) + local filename = path:gsub (resc (path_mark), name) + --printf('filename = %s', filename) + local file = io.open (filename, 'rb') + if file then return file, filename end + table.insert(errors, string.format("\tno file %q", filename)) + end + return false, '\n'..table.concat(errors, "\n")..'\n' +end + +---------------------------------------------------------------------- +-- Before compiling a metalua source module, try to find and load +-- a more recent bytecode dump. Requires lfs +---------------------------------------------------------------------- +local function metalua_cache_loader(name, src_filename, src) + if not M.mcache:find('%?') then + -- This is highly suspicious... + print("WARNING: no '?' character in $LUA_MCACHE/package.mcache") + end + local mlc = require 'metalua.compiler'.new() + local lfs = require 'lfs' + local dir_sep = M.config:sub(1,1) + local dst_filename = M.mcache :gsub ('%?', (name:gsub('%.', dir_sep))) + local src_a = lfs.attributes(src_filename) + local src_date = src_a and src_a.modification or 0 + local dst_a = lfs.attributes(dst_filename) + local dst_date = dst_a and dst_a.modification or 0 + local delta = dst_date - src_date + local bytecode, file, msg + if delta <= 0 then + --print ("(need to recompile "..src_filename.." into "..dst_filename..")") + bytecode = mlc :src_to_bytecode (src, '@'..src_filename) + for x in dst_filename :gmatch('()'..dir_sep) do + lfs.mkdir(dst_filename:sub(1,x)) + end + file, msg = io.open(dst_filename, 'wb') + if not file then error(msg) end + file :write (bytecode) + file :close() + else + file, msg = io.open(dst_filename, 'rb') + if not file then error(msg) end + bytecode = file :read '*a' + file :close() + end + return mlc :bytecode_to_function (bytecode, '@'..src_filename) +end + +---------------------------------------------------------------------- +-- Load a metalua source file. +---------------------------------------------------------------------- +function M.metalua_loader (name) + local file, filename_or_msg = M.findfile (name, M.mpath) + if not file then return filename_or_msg end + local luastring = file:read '*a' + file:close() + if M.mcache and pcall(require, 'lfs') then + return metalua_cache_loader(name, filename_or_msg, luastring) + else return require 'metalua.compiler'.new() :src_to_function (luastring, '@'..filename_or_msg) end +end + + +---------------------------------------------------------------------- +-- Placed after lua/luac loader, so precompiled files have +-- higher precedence. +---------------------------------------------------------------------- +table.insert(M.loaders, M.metalua_loader) + +---------------------------------------------------------------------- +-- Load an extension. +---------------------------------------------------------------------- +function extension (name, mlp) + local complete_name = M.metalua_extension_prefix..name + local extend_func = require (complete_name) + if not mlp.extensions[complete_name] then + local ast =extend_func(mlp) + mlp.extensions[complete_name] =extend_func + return ast + end +end + +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/pprint.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/pprint.lua new file mode 100644 index 000000000..73a842b63 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/pprint.lua @@ -0,0 +1,295 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +---------------------------------------------------------------------- + +---------------------------------------------------------------------- +---------------------------------------------------------------------- +-- +-- Lua objects pretty-printer +-- +---------------------------------------------------------------------- +---------------------------------------------------------------------- + +local M = { } + +M.DEFAULT_CFG = { + hide_hash = false; -- Print the non-array part of tables? + metalua_tag = true; -- Use Metalua's backtick syntax sugar? + fix_indent = nil; -- If a number, number of indentation spaces; + -- If false, indent to the previous brace. + line_max = nil; -- If a number, tries to avoid making lines with + -- more than this number of chars. + initial_indent = 0; -- If a number, starts at this level of indentation + keywords = { }; -- Set of keywords which must not use Lua's field + -- shortcuts {["foo"]=...} -> {foo=...} +} + +local function valid_id(cfg, x) + if type(x) ~= "string" then return false end + if not x:match "^[a-zA-Z_][a-zA-Z0-9_]*$" then return false end + if cfg.keywords and cfg.keywords[x] then return false end + return true +end + +local __tostring_cache = setmetatable({ }, {__mode='k'}) + +-- Retrieve the string produced by `__tostring` metamethod if present, +-- return `false` otherwise. Cached in `__tostring_cache`. +local function __tostring(x) + local the_string = __tostring_cache[x] + if the_string~=nil then return the_string end + local mt = getmetatable(x) + if mt then + local __tostring = mt.__tostring + if __tostring then + the_string = __tostring(x) + __tostring_cache[x] = the_string + return the_string + end + end + if x~=nil then __tostring_cache[x] = false end -- nil is an illegal key + return false +end + +local xlen -- mutually recursive with `xlen_type` + +local xlen_cache = setmetatable({ }, {__mode='k'}) + +-- Helpers for the `xlen` function +local xlen_type = { + ["nil"] = function ( ) return 3 end; + number = function (x) return #tostring(x) end; + boolean = function (x) return x and 4 or 5 end; + string = function (x) return #string.format("%q",x) end; +} + +function xlen_type.table (adt, cfg, nested) + local custom_string = __tostring(adt) + if custom_string then return #custom_string end + + -- Circular referenced objects are printed with the plain + -- `tostring` function in nested positions. + if nested [adt] then return #tostring(adt) end + nested [adt] = true + + local has_tag = cfg.metalua_tag and valid_id(cfg, adt.tag) + local alen = #adt + local has_arr = alen>0 + local has_hash = false + local x = 0 + + if not cfg.hide_hash then + -- first pass: count hash-part + for k, v in pairs(adt) do + if k=="tag" and has_tag then + -- this is the tag -> do nothing! + elseif type(k)=="number" and k<=alen and math.fmod(k,1)==0 and k>0 then + -- array-part pair -> do nothing! + else + has_hash = true + if valid_id(cfg, k) then x=x+#k + else x = x + xlen (k, cfg, nested) + 2 end -- count surrounding brackets + x = x + xlen (v, cfg, nested) + 5 -- count " = " and ", " + end + end + end + + for i = 1, alen do x = x + xlen (adt[i], nested) + 2 end -- count ", " + + nested[adt] = false -- No more nested calls + + if not (has_tag or has_arr or has_hash) then return 3 end + if has_tag then x=x+#adt.tag+1 end + if not (has_arr or has_hash) then return x end + if not has_hash and alen==1 and type(adt[1])~="table" then + return x-2 -- substract extraneous ", " + end + return x+2 -- count "{ " and " }", substract extraneous ", " +end + + +-- Compute the number of chars it would require to display the table +-- on a single line. Helps to decide whether some carriage returns are +-- required. Since the size of each sub-table is required many times, +-- it's cached in [xlen_cache]. +xlen = function (x, cfg, nested) + -- no need to compute length for 1-line prints + if not cfg.line_max then return 0 end + nested = nested or { } + if x==nil then return #"nil" end + local len = xlen_cache[x] + if len then return len end + local f = xlen_type[type(x)] + if not f then return #tostring(x) end + len = f (x, cfg, nested) + xlen_cache[x] = len + return len +end + +local function consider_newline(p, len) + if not p.cfg.line_max then return end + if p.current_offset + len <= p.cfg.line_max then return end + if p.indent < p.current_offset then + p:acc "\n"; p:acc ((" "):rep(p.indent)) + p.current_offset = p.indent + end +end + +local acc_value + +local acc_type = { + ["nil"] = function(p) p:acc("nil") end; + number = function(p, adt) p:acc (tostring (adt)) end; + string = function(p, adt) p:acc ((string.format ("%q", adt):gsub("\\\n", "\\n"))) end; + boolean = function(p, adt) p:acc (adt and "true" or "false") end } + +-- Indentation: +-- * if `cfg.fix_indent` is set to a number: +-- * add this number of space for each level of depth +-- * return to the line as soon as it flushes things further left +-- * if not, tabulate to one space after the opening brace. +-- * as a result, it never saves right-space to return before first element + +function acc_type.table(p, adt) + if p.nested[adt] then p:acc(tostring(adt)); return end + p.nested[adt] = true + + local has_tag = p.cfg.metalua_tag and valid_id(p.cfg, adt.tag) + local alen = #adt + local has_arr = alen>0 + local has_hash = false + + local previous_indent = p.indent + + if has_tag then p:acc("`"); p:acc(adt.tag) end + + local function indent(p) + if not p.cfg.fix_indent then p.indent = p.current_offset + else p.indent = p.indent + p.cfg.fix_indent end + end + + -- First pass: handle hash-part + if not p.cfg.hide_hash then + for k, v in pairs(adt) do + + if has_tag and k=='tag' then -- pass the 'tag' field + elseif type(k)=="number" and k<=alen and k>0 and math.fmod(k,1)==0 then + -- pass array-part keys (consecutive ints less than `#adt`) + else -- hash-part keys + if has_hash then p:acc ", " else -- 1st hash-part pair ever found + p:acc "{ "; indent(p) + end + + -- Determine whether a newline is required + local is_id, expected_len=valid_id(p.cfg, k) + if is_id then expected_len=#k+xlen(v, p.cfg, p.nested)+#" = , " + else expected_len = xlen(k, p.cfg, p.nested)+xlen(v, p.cfg, p.nested)+#"[] = , " end + consider_newline(p, expected_len) + + -- Print the key + if is_id then p:acc(k); p:acc " = " else + p:acc "["; acc_value (p, k); p:acc "] = " + end + + acc_value (p, v) -- Print the value + has_hash = true + end + end + end + + -- Now we know whether there's a hash-part, an array-part, and a tag. + -- Tag and hash-part are already printed if they're present. + if not has_tag and not has_hash and not has_arr then p:acc "{ }"; + elseif has_tag and not has_hash and not has_arr then -- nothing, tag already in acc + else + assert (has_hash or has_arr) -- special case { } already handled + local no_brace = false + if has_hash and has_arr then p:acc ", " + elseif has_tag and not has_hash and alen==1 and type(adt[1])~="table" then + -- No brace required; don't print "{", remember not to print "}" + p:acc (" "); acc_value (p, adt[1]) -- indent= indent+(cfg.fix_indent or 0)) + no_brace = true + elseif not has_hash then + -- Braces required, but not opened by hash-part handler yet + p:acc "{ "; indent(p) + end + + -- 2nd pass: array-part + if not no_brace and has_arr then + local expected_len = xlen(adt[1], p.cfg, p.nested) + consider_newline(p, expected_len) + acc_value(p, adt[1]) -- indent+(cfg.fix_indent or 0) + for i=2, alen do + p:acc ", "; + consider_newline(p, xlen(adt[i], p.cfg, p.nested)) + acc_value (p, adt[i]) --indent+(cfg.fix_indent or 0) + end + end + if not no_brace then p:acc " }" end + end + p.nested[adt] = false -- No more nested calls + p.indent = previous_indent +end + + +function acc_value(p, v) + local custom_string = __tostring(v) + if custom_string then p:acc(custom_string) else + local f = acc_type[type(v)] + if f then f(p, v) else p:acc(tostring(v)) end + end +end + + +-- FIXME: new_indent seems to be always nil?!s detection +-- FIXME: accumulator function should be configurable, +-- so that print() doesn't need to bufferize the whole string +-- before starting to print. +function M.tostring(t, cfg) + + cfg = cfg or M.DEFAULT_CFG or { } + + local p = { + cfg = cfg; + indent = 0; + current_offset = cfg.initial_indent or 0; + buffer = { }; + nested = { }; + acc = function(self, str) + table.insert(self.buffer, str) + self.current_offset = self.current_offset + #str + end; + } + acc_value(p, t) + return table.concat(p.buffer) +end + +function M.print(...) return print(M.tostring(...)) end +function M.sprintf(fmt, ...) + local args={...} + for i, v in pairs(args) do + local t=type(v) + if t=='table' then args[i]=M.tostring(v) + elseif t=='nil' then args[i]='nil' end + end + return string.format(fmt, unpack(args)) +end + +function M.printf(...) print(M.sprintf(...)) end + +return M \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/repl.mlua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/repl.mlua new file mode 100644 index 000000000..0c89bc0b4 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/repl.mlua @@ -0,0 +1,108 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +------------------------------------------------------------------------------- + +-- Keep these global: +PRINT_AST = true +LINE_WIDTH = 60 +PROMPT = "M> " +PROMPT2 = ">> " + +local pp=require 'metalua.pprint' +local M = { } + +mlc = require 'metalua.compiler'.new() + +local readline + +do -- set readline() to a line reader, either editline otr a default + local status, editline = pcall(require, 'editline') + if status then + local rl_handle = editline.init 'metalua' + readline = |p| rl_handle:read(p) + else + local status, rl = pcall(require, 'readline') + if status then + rl.set_options{histfile='~/.metalua_history', keeplines=100, completion=false } + readline = rl.readline + else -- neither editline nor readline available + function readline (p) + io.write (p) + io.flush () + return io.read '*l' + end + end + end +end + +local function reached_eof(lx, msg) + return lx:peek().tag=='Eof' or msg:find "token `Eof" +end + + +function M.run() + pp.printf ("Metalua, interactive REPLoop.\n".. + "(c) 2006-2013 ") + local lines = { } + while true do + local src, lx, ast, f, results, success + repeat + local line = readline(next(lines) and PROMPT2 or PROMPT) + if not line then print(); os.exit(0) end -- line==nil iff eof on stdin + if not next(lines) then + line = line:gsub('^%s*=', 'return ') + end + table.insert(lines, line) + src = table.concat (lines, "\n") + until #line>0 + lx = mlc :src_to_lexstream(src) + success, ast = pcall(mlc.lexstream_to_ast, mlc, lx) + if success then + success, f = pcall(mlc.ast_to_function, mlc, ast, '=stdin') + if success then + results = { xpcall(f, debug.traceback) } + success = table.remove (results, 1) + if success then + -- Success! + for _, x in ipairs(results) do + pp.print(x, {line_max=LINE_WIDTH, metalua_tag=true}) + end + lines = { } + else + print "Evaluation error:" + print (results[1]) + lines = { } + end + else + print "Can't compile into bytecode:" + print (f) + lines = { } + end + else + -- If lx has been read entirely, try to read + -- another line before failing. + if not reached_eof(lx, ast) then + print "Can't compile source into AST:" + print (ast) + lines = { } + end + end + end +end + +return M \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/treequery.mlua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/treequery.mlua new file mode 100644 index 000000000..e369b9954 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/treequery.mlua @@ -0,0 +1,488 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +------------------------------------------------------------------------------- + +local walk = require 'metalua.treequery.walk' + +local M = { } +-- support for old-style modules +treequery = M + +-- ----------------------------------------------------------------------------- +-- ----------------------------------------------------------------------------- +-- +-- multimap helper mmap: associate a key to a set of values +-- +-- ----------------------------------------------------------------------------- +-- ----------------------------------------------------------------------------- + +local function mmap_add (mmap, node, x) + if node==nil then return false end + local set = mmap[node] + if set then set[x] = true + else mmap[node] = {[x]=true} end +end + +-- currently unused, I throw the whole set away +local function mmap_remove (mmap, node, x) + local set = mmap[node] + if not set then return false + elseif not set[x] then return false + elseif next(set) then set[x]=nil + else mmap[node] = nil end + return true +end + +-- ----------------------------------------------------------------------------- +-- ----------------------------------------------------------------------------- +-- +-- TreeQuery object. +-- +-- ----------------------------------------------------------------------------- +-- ----------------------------------------------------------------------------- + +local ACTIVE_SCOPE = setmetatable({ }, {__mode="k"}) + +-- treequery metatable +local Q = { }; Q.__index = Q + +--- treequery constructor +-- the resultingg object will allow to filter ans operate on the AST +-- @param root the AST to visit +-- @return a treequery visitor instance +function M.treequery(root) + return setmetatable({ + root = root, + unsatisfied = 0, + predicates = { }, + until_up = { }, + from_up = { }, + up_f = false, + down_f = false, + filters = { }, + }, Q) +end + +-- helper to share the implementations of positional filters +local function add_pos_filter(self, position, inverted, inclusive, f, ...) + if type(f)=='string' then f = M.has_tag(f, ...) end + if not inverted then self.unsatisfied += 1 end + local x = { + pred = f, + position = position, + satisfied = false, + inverted = inverted or false, + inclusive = inclusive or false } + table.insert(self.predicates, x) + return self +end + +function Q :if_unknown(f) + self.unknown_handler = f or (||nil) + return self +end + +-- TODO: offer an API for inclusive pos_filters + +--- select nodes which are after one which satisfies predicate f +Q.after = |self, f, ...| add_pos_filter(self, 'after', false, false, f, ...) +--- select nodes which are not after one which satisfies predicate f +Q.not_after = |self, f, ...| add_pos_filter(self, 'after', true, false, f, ...) +--- select nodes which are under one which satisfies predicate f +Q.under = |self, f, ...| add_pos_filter(self, 'under', false, false, f, ...) +--- select nodes which are not under one which satisfies predicate f +Q.not_under = |self, f, ...| add_pos_filter(self, 'under', true, false, f, ...) + +--- select nodes which satisfy predicate f +function Q :filter(f, ...) + if type(f)=='string' then f = M.has_tag(f, ...) end + table.insert(self.filters, f); + return self +end + +--- select nodes which satisfy predicate f +function Q :filter_not(f, ...) + if type(f)=='string' then f = M.has_tag(f, ...) end + table.insert(self.filters, |...| not f(...)) + return self +end + +-- private helper: apply filters and execute up/down callbacks when applicable +function Q :execute() + local cfg = { } + -- TODO: optimize away not_under & not_after by pruning the tree + function cfg.down(...) + --printf ("[down]\t%s\t%s", self.unsatisfied, table.tostring((...))) + ACTIVE_SCOPE[...] = cfg.scope + local satisfied = self.unsatisfied==0 + for _, x in ipairs(self.predicates) do + if not x.satisfied and x.pred(...) then + x.satisfied = true + local node, parent = ... + local inc = x.inverted and 1 or -1 + if x.position=='under' then + -- satisfied from after we get down this node... + self.unsatisfied += inc + -- ...until before we get up this node + mmap_add(self.until_up, node, x) + elseif x.position=='after' then + -- satisfied from after we get up this node... + mmap_add(self.from_up, node, x) + -- ...until before we get up this node's parent + mmap_add(self.until_up, parent, x) + elseif x.position=='under_or_after' then + -- satisfied from after we get down this node... + self.satisfied += inc + -- ...until before we get up this node's parent... + mmap_add(self.until_up, parent, x) + else + error "position not understood" + end -- position + if x.inclusive then satisfied = self.unsatisfied==0 end + end -- predicate passed + end -- for predicates + + if satisfied then + for _, f in ipairs(self.filters) do + if not f(...) then satisfied=false; break end + end + if satisfied and self.down_f then self.down_f(...) end + end + end + + function cfg.up(...) + --printf ("[up]\t%s", table.tostring((...))) + + -- Remove predicates which are due before we go up this node + local preds = self.until_up[...] + if preds then + for x, _ in pairs(preds) do + local inc = x.inverted and -1 or 1 + self.unsatisfied += inc + x.satisfied = false + end + self.until_up[...] = nil + end + + -- Execute the up callback + -- TODO: cache the filter passing result from the down callback + -- TODO: skip if there's no callback + local satisfied = self.unsatisfied==0 + if satisfied then + for _, f in ipairs(self.filters) do + if not f(self, ...) then satisfied=false; break end + end + if satisfied and self.up_f then self.up_f(...) end + end + + -- Set predicate which are due after we go up this node + local preds = self.from_up[...] + if preds then + for p, _ in pairs(preds) do + local inc = p.inverted and 1 or -1 + self.unsatisfied += inc + end + self.from_up[...] = nil + end + ACTIVE_SCOPE[...] = nil + end + + function cfg.binder(id_node, ...) + --printf(" >>> Binder called on %s, %s", table.tostring(id_node), + -- table.tostring{...}:sub(2,-2)) + cfg.down(id_node, ...) + cfg.up(id_node, ...) + --printf("down/up on binder done") + end + + cfg.unknown = self.unknown_handler + + --function cfg.occurrence (binder, occ) + -- if binder then OCC2BIND[occ] = binder[1] end + --printf(" >>> %s is an occurrence of %s", occ[1], table.tostring(binder and binder[2])) + --end + + --function cfg.binder(...) cfg.down(...); cfg.up(...) end + return walk.guess(cfg, self.root) +end + +--- Execute a function on each selected node +-- @down: function executed when we go down a node, i.e. before its children +-- have been examined. +-- @up: function executed when we go up a node, i.e. after its children +-- have been examined. +function Q :foreach(down, up) + if not up and not down then + error "iterator missing" + end + self.up_f = up + self.down_f = down + return self :execute() +end + +--- Return the list of nodes selected by a given treequery. +function Q :list() + local acc = { } + self :foreach(|x| table.insert(acc, x)) + return acc +end + +--- Return the first matching element +-- TODO: dirty hack, to implement properly with a 'break' return. +-- Also, it won't behave correctly if a predicate causes an error, +-- or if coroutines are involved. +function Q :first() + local result = { } + local function f(...) result = {...}; error() end + pcall(|| self :foreach(f)) + return unpack(result) +end + +--- Pretty printer for queries +function Q :__tostring() return "" end + +-- ----------------------------------------------------------------------------- +-- ----------------------------------------------------------------------------- +-- +-- Predicates. +-- +-- ----------------------------------------------------------------------------- +-- ----------------------------------------------------------------------------- + +--- Return a predicate which is true if the tested node's tag is among the +-- one listed as arguments +-- @param ... a sequence of tag names +function M.has_tag(...) + local args = {...} + if #args==1 then + local tag = ... + return (|node| node.tag==tag) + --return function(self, node) printf("node %s has_tag %s?", table.tostring(node), tag); return node.tag==tag end + else + local tags = { } + for _, tag in ipairs(args) do tags[tag]=true end + return function(node) + local node_tag = node.tag + return node_tag and tags[node_tag] + end + end +end + +--- Predicate to test whether a node represents an expression. +M.is_expr = M.has_tag('Nil', 'Dots', 'True', 'False', 'Number','String', + 'Function', 'Table', 'Op', 'Paren', 'Call', 'Invoke', + 'Id', 'Index') + +-- helper for is_stat +local STAT_TAGS = { Do=1, Set=1, While=1, Repeat=1, If=1, Fornum=1, + Forin=1, Local=1, Localrec=1, Return=1, Break=1 } + +--- Predicate to test whether a node represents a statement. +-- It is context-aware, i.e. it recognizes `Call and `Invoke nodes +-- used in a statement context as such. +function M.is_stat(node, parent) + local tag = node.tag + if not tag then return false + elseif STAT_TAGS[tag] then return true + elseif tag=='Call' or tag=='Invoke' then return parent and parent.tag==nil + else return false end +end + +--- Predicate to test whether a node represents a statements block. +function M.is_block(node) return node.tag==nil end + +-- ----------------------------------------------------------------------------- +-- ----------------------------------------------------------------------------- +-- +-- Variables and scopes. +-- +-- ----------------------------------------------------------------------------- +-- ----------------------------------------------------------------------------- + +local BINDER_PARENT_TAG = { + Local=true, Localrec=true, Forin=true, Function=true } + +--- Test whether a node is a binder. This is local predicate, although it +-- might need to inspect the parent node. +function M.is_binder(node, parent) + --printf('is_binder(%s, %s)', table.tostring(node), table.tostring(parent)) + if node.tag ~= 'Id' or not parent then return false end + if parent.tag=='Fornum' then return parent[1]==node end + if not BINDER_PARENT_TAG[parent.tag] then return false end + for _, binder in ipairs(parent[1]) do + if binder==node then return true end + end + return false +end + +--- Retrieve the binder associated to an occurrence within root. +-- @param occurrence an Id node representing an occurrence in `root`. +-- @param root the tree in which `node` and its binder occur. +-- @return the binder node, and its ancestors up to root if found. +-- @return nil if node is global (or not an occurrence) in `root`. +function M.binder(occurrence, root) + local cfg, id_name, result = { }, occurrence[1], { } + function cfg.occurrence(id) + if id == occurrence then result = cfg.scope :get(id_name) end + -- TODO: break the walker + end + walk.guess(cfg, root) + return unpack(result) +end + +--- Predicate to filter occurrences of a given binder. +-- Warning: it relies on internal scope book-keeping, +-- and for this reason, it only works as query method argument. +-- It won't work outside of a query. +-- @param binder the binder whose occurrences must be kept by predicate +-- @return a predicate + +-- function M.is_occurrence_of(binder) +-- return function(node, ...) +-- if node.tag ~= 'Id' then return nil end +-- if M.is_binder(node, ...) then return nil end +-- local scope = ACTIVE_SCOPE[node] +-- if not scope then return nil end +-- local result = scope :get (node[1]) or { } +-- if result[1] ~= binder then return nil end +-- return unpack(result) +-- end +-- end + +function M.is_occurrence_of(binder) + return function(node, ...) + local b = M.get_binder(node) + return b and b==binder + end +end + +function M.get_binder(occurrence, ...) + if occurrence.tag ~= 'Id' then return nil end + if M.is_binder(occurrence, ...) then return nil end + local scope = ACTIVE_SCOPE[occurrence] + local binder_hierarchy = scope :get(occurrence[1]) + return unpack (binder_hierarchy or { }) +end + +--- Transform a predicate on a node into a predicate on this node's +-- parent. For instance if p tests whether a node has property P, +-- then parent(p) tests whether this node's parent has property P. +-- The ancestor level is precised with n, with 1 being the node itself, +-- 2 its parent, 3 its grand-parent etc. +-- @param[optional] n the parent to examine, default=2 +-- @param pred the predicate to transform +-- @return a predicate +function M.parent(n, pred, ...) + if type(n)~='number' then n, pred = 2, n end + if type(pred)=='string' then pred = M.has_tag(pred, ...) end + return function(self, ...) + return select(n, ...) and pred(self, select(n, ...)) + end +end + +--- Transform a predicate on a node into a predicate on this node's +-- n-th child. +-- @param n the child's index number +-- @param pred the predicate to transform +-- @return a predicate +function M.child(n, pred) + return function(node, ...) + local child = node[n] + return child and pred(child, node, ...) + end +end + +--- Predicate to test the position of a node in its parent. +-- The predicate succeeds if the node is the n-th child of its parent, +-- and a <= n <= b. +-- nth(a) is equivalent to nth(a, a). +-- Negative indices are admitted, and count from the last child, +-- as done for instance by string.sub(). +-- +-- TODO: This is wrong, this tests the table relationship rather than the +-- AST node relationship. +-- Must build a getindex helper, based on pattern matching, then build +-- the predicate around it. +-- +-- @param a lower bound +-- @param a upper bound +-- @return a predicate +function M.is_nth(a, b) + b = b or a + return function(self, node, parent) + if not parent then return false end + local nchildren = #parent + local a = a<=0 and nchildren+a+1 or a + if a>nchildren then return false end + local b = b<=0 and nchildren+b+1 or b>nchildren and nchildren or b + for i=a,b do if parent[i]==node then return true end end + return false + end +end + +--- Returns a list of the direct children of AST node `ast`. +-- Children are only expressions, statements and blocks, +-- not intermediates such as `Pair` nodes or internal lists +-- in `Local` or `Set` nodes. +-- Children are returned in parsing order, which isn't necessarily +-- the same as source code order. For instance, the right-hand-side +-- of a `Local` node is listed before the left-hand-side, because +-- semantically the right is evaluated before the variables on the +-- left enter scope. +-- +-- @param ast the node whose children are needed +-- @return a list of the direct children of `ast` +function M.children(ast) + local acc = { } + local cfg = { } + function cfg.down(x) + if x~=ast then table.insert(acc, x); return 'break' end + end + walk.guess(cfg, ast) + return acc +end + +-- ----------------------------------------------------------------------------- +-- ----------------------------------------------------------------------------- +-- +-- Comments parsing. +-- +-- ----------------------------------------------------------------------------- +-- ----------------------------------------------------------------------------- + +local comment_extractor = |which_side| function (node) + local x = node.lineinfo + x = x and x[which_side] + x = x and x.comments + if not x then return nil end + local lines = { } + for _, record in ipairs(x) do + table.insert(lines, record[1]) + end + return table.concat(lines, '\n') +end + +M.comment_prefix = comment_extractor 'first' +M.comment_suffix = comment_extractor 'last' + + +--- Shortcut for the query constructor +function M :__call(...) return self.treequery(...) end +setmetatable(M, M) + +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/treequery/walk.mlua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/treequery/walk.mlua new file mode 100644 index 000000000..94fc5d65d --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/treequery/walk.mlua @@ -0,0 +1,266 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2006-2013 Fabien Fleutot and others. +-- +-- All rights reserved. +-- +-- This program and the accompanying materials are made available +-- under the terms of the Eclipse Public License v1.0 which +-- accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- This program and the accompanying materials are also made available +-- under the terms of the MIT public license which accompanies this +-- distribution, and is available at http://www.lua.org/license.html +-- +-- Contributors: +-- Fabien Fleutot - API and implementation +-- +------------------------------------------------------------------------------- + +-- Low level AST traversal library. +-- +-- This library is a helper for the higher-level `treequery` library. +-- It walks through every node of an AST, depth-first, and executes +-- some callbacks contained in its `cfg` config table: +-- +-- * `cfg.down(...)` is called when it walks down a node, and receive as +-- parameters the node just entered, followed by its parent, grand-parent +-- etc. until the root node. +-- +-- * `cfg.up(...)` is called when it walks back up a node, and receive as +-- parameters the node just entered, followed by its parent, grand-parent +-- etc. until the root node. +-- +-- * `cfg.occurrence(binder, id_node, ...)` is called when it visits +-- an `` `Id{ }`` node which isn't a local variable creator. binder +-- is a reference to its binder with its context. The binder is the +-- `` `Id{ }`` node which created this local variable. By "binder +-- and its context", we mean a list starting with the `` `Id{ }``, +-- and followed by every ancestor of the binder node, up until the +-- common root node. `binder` is nil if the variable is global. +-- `id_node` is followed by its ancestor, up until the root node. +-- +-- `cfg.scope` is maintained during the traversal, associating a +-- variable name to the binder which creates it in the context of the +-- node currently visited. +-- +-- `walk.traverse.xxx` functions are in charge of the recursive +-- descent into children nodes. They're private helpers. They are also +-- in charge of calling appropriate `cfg.xxx` callbacks. + +-{ extension ("match", ...) } + +local pp = require 'metalua.pprint' + +local M = { traverse = { }; tags = { }; debug = false } + +local function table_transpose(t) + local tt = { }; for a, b in pairs(t) do tt[b]=a end; return tt +end + +-------------------------------------------------------------------------------- +-- Standard tags: can be used to guess the type of an AST, or to check +-- that the type of an AST is respected. +-------------------------------------------------------------------------------- +M.tags.stat = table_transpose{ + 'Do', 'Set', 'While', 'Repeat', 'Local', 'Localrec', 'Return', + 'Fornum', 'Forin', 'If', 'Break', 'Goto', 'Label', + 'Call', 'Invoke' } +M.tags.expr = table_transpose{ + 'Paren', 'Call', 'Invoke', 'Index', 'Op', 'Function', 'Stat', + 'Table', 'Nil', 'Dots', 'True', 'False', 'Number', 'String', 'Id' } + +-------------------------------------------------------------------------------- +-- These [M.traverse.xxx()] functions are in charge of actually going through +-- ASTs. At each node, they make sure to call the appropriate walker. +-------------------------------------------------------------------------------- + +function M.traverse.stat (cfg, x, ...) + if M.debug then pp.printf("traverse stat %s", x) end + local ancestors = {...} + local B = |y| M.block (cfg, y, x, unpack(ancestors)) -- Block + local S = |y| M.stat (cfg, y, x, unpack(ancestors)) -- Statement + local E = |y| M.expr (cfg, y, x, unpack(ancestors)) -- Expression + local EL = |y| M.expr_list (cfg, y, x, unpack(ancestors)) -- Expression List + local IL = |y| M.binder_list (cfg, y, x, unpack(ancestors)) -- Id binders List + local OS = || cfg.scope :save() -- Open scope + local CS = || cfg.scope :restore() -- Close scope + + match x with + | {...} if x.tag == nil -> for _, y in ipairs(x) do M.stat(cfg, y, ...) end + -- no tag --> node not inserted in the history ancestors + | `Do{...} -> OS(x); for _, y in ipairs(x) do S(y) end; CS(x) + | `Set{ lhs, rhs } -> EL(lhs); EL(rhs) + | `While{ cond, body } -> E(cond); OS(); B(body); CS() + | `Repeat{ body, cond } -> OS(body); B(body); E(cond); CS(body) + | `Local{ lhs } -> IL(lhs) + | `Local{ lhs, rhs } -> EL(rhs); IL(lhs) + | `Localrec{ lhs, rhs } -> IL(lhs); EL(rhs) + | `Fornum{ i, a, b, body } -> E(a); E(b); OS(); IL{i}; B(body); CS() + | `Fornum{ i, a, b, c, body } -> E(a); E(b); E(c); OS(); IL{i}; B(body); CS() + | `Forin{ i, rhs, body } -> EL(rhs); OS(); IL(i); B(body); CS() + | `If{...} -> + for i=1, #x-1, 2 do + E(x[i]); OS(); B(x[i+1]); CS() + end + if #x%2 == 1 then + OS(); B(x[#x]); CS() + end + | `Call{...}|`Invoke{...}|`Return{...} -> EL(x) + | `Break | `Goto{ _ } | `Label{ _ } -> -- nothing + | { tag=tag, ...} if M.tags.stat[tag]-> + M.malformed (cfg, x, unpack (ancestors)) + | _ -> + M.unknown (cfg, x, unpack (ancestors)) + end +end + +function M.traverse.expr (cfg, x, ...) + if M.debug then pp.printf("traverse expr %s", x) end + local ancestors = {...} + local B = |y| M.block (cfg, y, x, unpack(ancestors)) -- Block + local S = |y| M.stat (cfg, y, x, unpack(ancestors)) -- Statement + local E = |y| M.expr (cfg, y, x, unpack(ancestors)) -- Expression + local EL = |y| M.expr_list (cfg, y, x, unpack(ancestors)) -- Expression List + local IL = |y| M.binder_list (cfg, y, x, unpack(ancestors)) -- Id binders list + local OS = || cfg.scope :save() -- Open scope + local CS = || cfg.scope :restore() -- Close scope + + match x with + | `Paren{ e } -> E(e) + | `Call{...} | `Invoke{...} -> EL(x) + | `Index{ a, b } -> E(a); E(b) + | `Op{ opid, ... } -> E(x[2]); if #x==3 then E(x[3]) end + | `Function{ params, body } -> OS(body); IL(params); B(body); CS(body) + | `Stat{ b, e } -> OS(b); B(b); E(e); CS(b) + | `Id{ name } -> M.occurrence(cfg, x, unpack(ancestors)) + | `Table{ ... } -> + for i = 1, #x do match x[i] with + | `Pair{ k, v } -> E(k); E(v) + | v -> E(v) + end end + | `Nil|`Dots|`True|`False|`Number{_}|`String{_} -> -- terminal node + | { tag=tag, ...} if M.tags.expr[tag]-> M.malformed (cfg, x, unpack (ancestors)) + | _ -> M.unknown (cfg, x, unpack (ancestors)) + end +end + +function M.traverse.block (cfg, x, ...) + assert(type(x)=='table', "traverse.block() expects a table") + if x.tag then M.malformed(cfg, x, ...) + else for _, y in ipairs(x) do M.stat(cfg, y, x, ...) end + end +end + +function M.traverse.expr_list (cfg, x, ...) + assert(type(x)=='table', "traverse.expr_list() expects a table") + -- x doesn't appear in the ancestors + for _, y in ipairs(x) do M.expr(cfg, y, ...) end +end + +function M.malformed(cfg, x, ...) + local f = cfg.malformed or cfg.error + if f then f(x, ...) else + error ("Malformed node of tag "..(x.tag or '(nil)')) + end +end + +function M.unknown(cfg, x, ...) + local f = cfg.unknown or cfg.error + if f then f(x, ...) else + error ("Unknown node tag "..(x.tag or '(nil)')) + end +end + +function M.occurrence(cfg, x, ...) + if cfg.occurrence then cfg.occurrence(cfg.scope :get(x[1]), x, ...) end +end + +-- TODO: Is it useful to call each error handling function? +function M.binder_list (cfg, id_list, ...) + local f = cfg.binder + local ferror = cfg.error or cfg.malformed or cfg.unknown + for i, id_node in ipairs(id_list) do + local down, up = cfg.down, cfg.up + if id_node.tag == 'Id' then + cfg.scope :set (id_node[1], { id_node, ... }) + if down then down(id_node, ...) end + if f then f(id_node, ...) end + if up then up(id_node, ...) end + elseif i==#id_list and id_node.tag=='Dots' then + if down then down(id_node, ...) end + if up then up(id_node, ...) end + -- Do nothing, those are valid `Dots + elseif ferror then + -- Traverse error handling function + ferror(id_node, ...) + else + error("Invalid binders list") + end + end +end + +---------------------------------------------------------------------- +-- Generic walker generator. +-- * if `cfg' has an entry matching the tree name, use this entry +-- * if not, try to use the entry whose name matched the ast kind +-- * if an entry is a table, look for 'up' and 'down' entries +-- * if it is a function, consider it as a `down' traverser. +---------------------------------------------------------------------- +local walker_builder = function(traverse) + assert(traverse) + return function (cfg, ...) + if not cfg.scope then cfg.scope = M.newscope() end + local down, up = cfg.down, cfg.up + local broken = down and down(...) + if broken ~= 'break' then M.traverse[traverse] (cfg, ...) end + if up then up(...) end + end +end + +---------------------------------------------------------------------- +-- Declare [M.stat], [M.expr], [M.block]. +-- `M.binder_list` is not here, because `cfg.up` and `cfg.down` must +-- be called on individual binders, not on the list itself. +-- It's therefore handled in `traverse.binder_list()` +---------------------------------------------------------------------- +for _, w in ipairs{ "stat", "expr", "block" } do --, "malformed", "unknown" } do + M[w] = walker_builder (w, M.traverse[w]) +end + +-- Don't call up/down callbacks on expr lists +M.expr_list = M.traverse.expr_list + + +---------------------------------------------------------------------- +-- Try to guess the type of the AST then choose the right walkker. +---------------------------------------------------------------------- +function M.guess (cfg, x, ...) + assert(type(x)=='table', "arg #2 in a walker must be an AST") + if M.tags.expr[x.tag] then return M.expr(cfg, x, ...) end + if M.tags.stat[x.tag] then return M.stat(cfg, x, ...) end + if not x.tag then return M.block(cfg, x, ...) end + error ("Can't guess the AST type from tag "..(x.tag or '')) +end + +local S = { }; S.__index = S + +function M.newscope() + local instance = { current = { } } + instance.stack = { instance.current } + setmetatable (instance, S) + return instance +end + +function S :save(...) + local current_copy = { } + for a, b in pairs(self.current) do current_copy[a]=b end + table.insert (self.stack, current_copy) + if ... then return self :add(...) end +end + +function S :restore() self.current = table.remove (self.stack) end +function S :get (var_name) return self.current[var_name] end +function S :set (key, val) self.current[key] = val end + +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/apimodel.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/apimodel.lua new file mode 100644 index 000000000..e0ed1c968 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/apimodel.lua @@ -0,0 +1,241 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2011-2012 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Simon BERNARD +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +local M = {} + +-------------------------------------------------------------------------------- +-- API MODEL +-------------------------------------------------------------------------------- + +function M._file() + local file = { + -- FIELDS + tag = "file", + name = nil, -- string + shortdescription = "", -- string + description = "", -- string + types = {}, -- map from typename to type + globalvars = {}, -- map from varname to item + returns = {}, -- list of return + + -- FUNCTIONS + addtype = function (self,type) + self.types[type.name] = type + type.parent = self + end, + + mergetype = function (self,newtype,erase,erasesourcerangefield) + local currenttype = self.types[newtype.name] + if currenttype then + -- merge recordtypedef + if currenttype.tag =="recordtypedef" and newtype.tag == "recordtypedef" then + -- merge fields + for fieldname ,field in pairs( newtype.fields) do + local currentfield = currenttype.fields[fieldname] + if erase or not currentfield then + currenttype:addfield(field) + elseif erasesourcerangefield then + if field.sourcerange.min and field.sourcerange.max then + currentfield.sourcerange.min = field.sourcerange.min + currentfield.sourcerange.max = field.sourcerange.max + end + end + end + + -- merge descriptions and source ranges + if erase then + if newtype.description or newtype.description == "" then currenttype.description = newtype.description end + if newtype.shortdescription or newtype.shortdescription == "" then currenttype.shortdescription = newtype.shortdescription end + if newtype.sourcerange.min and newtype.sourcerange.max then + currenttype.sourcerange.min = newtype.sourcerange.min + currenttype.sourcerange.max = newtype.sourcerange.max + end + end + -- merge functiontypedef + elseif currenttype.tag == "functiontypedef" and newtype.tag == "functiontypedef" then + -- merge params + for i, param1 in ipairs(newtype.params) do + local missing = true + for j, param2 in ipairs(currenttype.params) do + if param1.name == param2.name then + missing = false + break + end + end + if missing then + table.insert(currenttype.params,param1) + end + end + + -- merge descriptions and source ranges + if erase then + if newtype.description or newtype.description == "" then currenttype.description = newtype.description end + if newtype.shortdescription or newtype.shortdescription == "" then currenttype.shortdescription = newtype.shortdescription end + if newtype.sourcerange.min and newtype.sourcerange.max then + currenttype.sourcerange.min = newtype.sourcerange.min + currenttype.sourcerange.max = newtype.sourcerange.max + end + end + end + else + self:addtype(newtype) + end + end, + + addglobalvar = function (self,item) + self.globalvars[item.name] = item + item.parent = self + end, + + moduletyperef = function (self) + if self and self.returns[1] and self.returns[1].types[1] then + local typeref = self.returns[1].types[1] + return typeref + end + end + } + return file +end + +function M._recordtypedef(name) + local recordtype = { + -- FIELDS + tag = "recordtypedef", + name = name, -- string (mandatory) + shortdescription = "", -- string + description = "", -- string + fields = {}, -- map from fieldname to field + sourcerange = {min=0,max=0}, + + -- FUNCTIONS + addfield = function (self,field) + self.fields[field.name] = field + field.parent = self + end + } + return recordtype +end + +function M._functiontypedef(name) + return { + tag = "functiontypedef", + name = name, -- string (mandatory) + shortdescription = "", -- string + description = "", -- string + params = {}, -- list of parameter + returns = {} -- list of return + } +end + +function M._parameter(name) + return { + tag = "parameter", + name = name, -- string (mandatory) + description = "", -- string + type = nil -- typeref (external or internal or primitive typeref) + } +end + +function M._item(name) + return { + -- FIELDS + tag = "item", + name = name, -- string (mandatory) + shortdescription = "", -- string + description = "", -- string + type = nil, -- typeref (external or internal or primitive typeref) + occurrences = {}, -- list of identifier (see internalmodel) + sourcerange = {min=0, max=0}, + + -- This is A TRICK + -- This value is ALWAYS nil, except for internal purposes (short references). + external = nil, + + -- FUNCTIONS + addoccurence = function (self,occ) + table.insert(self.occurrences,occ) + occ.definition = self + end, + + resolvetype = function (self,file) + if self and self.type then + if self.type.tag =="internaltyperef" then + -- if file is not given try to retrieve it. + if not file then + if self.parent and self.parent.tag == 'recordtypedef' then + file = self.parent.parent + elseif self.parent.tag == 'file' then + file = self.parent + end + end + if file then return file.types[self.type.typename] end + elseif self.type.tag =="inlinetyperef" then + return self.type.def + end + end + end + } +end + +function M._externaltypref(modulename, typename) + return { + tag = "externaltyperef", + modulename = modulename, -- string + typename = typename -- string + } +end + +function M._internaltyperef(typename) + return { + tag = "internaltyperef", + typename = typename -- string + } +end + +function M._primitivetyperef(typename) + return { + tag = "primitivetyperef", + typename = typename -- string + } +end + +function M._moduletyperef(modulename,returnposition) + return { + tag = "moduletyperef", + modulename = modulename, -- string + returnposition = returnposition -- number + } +end + +function M._exprtyperef(expression,returnposition) + return { + tag = "exprtyperef", + expression = expression, -- expression (see internal model) + returnposition = returnposition -- number + } +end + +function M._inlinetyperef(definition) + return { + tag = "inlinetyperef", + def = definition, -- expression (see internal model) + + } +end + +function M._return(description) + return { + tag = "return", + description = description or "", -- string + types = {} -- list of typref (external or internal or primitive typeref) + } +end +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/apimodelbuilder.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/apimodelbuilder.lua new file mode 100644 index 000000000..6fcd3c8f5 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/apimodelbuilder.lua @@ -0,0 +1,459 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2011-2012 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Simon BERNARD +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +local apimodel = require "models.apimodel" +local ldp = require "models.ldparser" +local Q = require "metalua.treequery" + +local M = {} + +local handledcomments={} -- cache to know the comment already handled + +---- +-- UTILITY METHODS +local primitivetypes = { + ['boolean'] = true, + ['function'] = true, + ['nil'] = true, + ['number'] = true, + ['string'] = true, + ['table'] = true, + ['thread'] = true, + ['userdata'] = true +} + +-- get or create the typedef with the name "name" +local function gettypedef(_file,name,kind,sourcerangemin,sourcerangemax) + local kind = kind or "recordtypedef" + local _typedef = _file.types[name] + if _typedef then + if _typedef.tag == kind then return _typedef end + else + if kind == "recordtypedef" and name ~= "global" then + local _recordtypedef = apimodel._recordtypedef(name) + + -- define sourcerange + _recordtypedef.sourcerange.min = sourcerangemin + _recordtypedef.sourcerange.max = sourcerangemax + + -- add to file if a name is defined + if _recordtypedef.name then _file:addtype(_recordtypedef) end + return _recordtypedef + elseif kind == "functiontypedef" then + -- TODO support function + return nil + else + return nil + end + end + return nil +end + + +-- create a typeref from the typref doc_tag +local function createtyperef(dt_typeref,_file,sourcerangemin,sourcerangemax) + local _typeref + if dt_typeref.tag == "typeref" then + if dt_typeref.module then + -- manage external type + _typeref = apimodel._externaltypref() + _typeref.modulename = dt_typeref.module + _typeref.typename = dt_typeref.type + else + if primitivetypes[dt_typeref.type] then + -- manage primitive type + _typeref = apimodel._primitivetyperef() + _typeref.typename = dt_typeref.type + else + -- manage internal type + _typeref = apimodel._internaltyperef() + _typeref.typename = dt_typeref.type + if _file then + gettypedef(_file, _typeref.typename, "recordtypedef", sourcerangemin,sourcerangemax) + end + end + end + end + return _typeref +end + +-- create a return from the return doc_tag +local function createreturn(dt_return,_file,sourcerangemin,sourcerangemax) + local _return = apimodel._return() + + _return.description = dt_return.description + + -- manage typeref + if dt_return.types then + for _, dt_typeref in ipairs(dt_return.types) do + local _typeref = createtyperef(dt_typeref,_file,sourcerangemin,sourcerangemax) + if _typeref then + table.insert(_return.types,_typeref) + end + end + end + return _return +end + +-- create a item from the field doc_tag +local function createfield(dt_field,_file,sourcerangemin,sourcerangemax) + local _item = apimodel._item(dt_field.name) + + if dt_field.shortdescription then + _item.shortdescription = dt_field.shortdescription + _item.description = dt_field.description + else + _item.shortdescription = dt_field.description + end + + -- manage typeref + local dt_typeref = dt_field.type + if dt_typeref then + _item.type = createtyperef(dt_typeref,_file,sourcerangemin,sourcerangemax) + end + return _item +end + +-- create a param from the param doc_tag +local function createparam(dt_param,_file,sourcerangemin,sourcerangemax) + if not dt_param.name then return nil end + + local _parameter = apimodel._parameter(dt_param.name) + _parameter.description = dt_param.description + + -- manage typeref + local dt_typeref = dt_param.type + if dt_typeref then + _parameter.type = createtyperef(dt_typeref,_file,sourcerangemin,sourcerangemax) + end + return _parameter +end + +-- get or create the typedef with the name "name" +function M.additemtoparent(_file,_item,scope,sourcerangemin,sourcerangemax) + if scope and not scope.module then + if _item.name then + if scope.type == "global" then + _file:addglobalvar(_item) + else + local _recordtypedef = gettypedef (_file, scope.type ,"recordtypedef",sourcerangemin,sourcerangemax) + _recordtypedef:addfield(_item) + end + else + -- if no item name precise we store the scope in the item to be able to add it to the right parent later + _item.scope = scope + end + end +end + +-- Function type counter +local i = 0 + +-- Reset function type counter +local function resetfunctiontypeidgenerator() + i = 0 +end + +-- Provides an unique index for a function type +local function generatefunctiontypeid() + i = i + 1 + return i +end + +-- generate a function type name +local function generatefunctiontypename(_functiontypedef) + local name = {"__"} + if _functiontypedef.returns and _functiontypedef.returns[1] then + local ret = _functiontypedef.returns[1] + for _, type in ipairs(ret.types) do + if type.typename then + if type.modulename then + table.insert(name,type.modulename) + end + table.insert(name,"#") + table.insert(name,type.typename) + end + end + + end + table.insert(name,"=") + if _functiontypedef.params then + for _, param in ipairs(_functiontypedef.params) do + local type = param.type + if type then + if type.typename then + if type.modulename then + table.insert(name,type.modulename) + end + table.insert(name,"#") + table.insert(name,type.typename) + else + table.insert(name,"#unknown") + end + end + table.insert(name,"[") + table.insert(name,param.name) + table.insert(name,"]") + end + end + table.insert(name,"__") + table.insert(name, generatefunctiontypeid()) + return table.concat(name) +end + + + +------------------------------------------------------ +-- create the module api +function M.createmoduleapi(ast,modulename) + + -- Initialise function type naming + resetfunctiontypeidgenerator() + + local _file = apimodel._file() + + local _comment2apiobj = {} + + local function handlecomment(comment) + + -- Extract information from tagged comments + local parsedcomment = ldp.parse(comment[1]) + if not parsedcomment then return nil end + + -- Get tags from the languages + local regulartags = parsedcomment.tags + + -- Will contain last API object generated from comments + local _lastapiobject + + -- if comment is an ld comment + if regulartags then + -- manage "module" comment + if regulartags["module"] then + -- get name + _file.name = regulartags["module"][1].name or modulename + _lastapiobject = _file + + -- manage descriptions + _file.shortdescription = parsedcomment.shortdescription + _file.description = parsedcomment.description + + local sourcerangemin = comment.lineinfo.first.offset + local sourcerangemax = comment.lineinfo.last.offset + + -- manage returns + if regulartags ["return"] then + for _, dt_return in ipairs(regulartags ["return"]) do + local _return = createreturn(dt_return,_file,sourcerangemin,sourcerangemax) + table.insert(_file.returns,_return) + end + end + -- if no returns on module create a defaultreturn of type #modulename + if #_file.returns == 0 and _file.name then + -- create internal type ref + local _typeref = apimodel._internaltyperef() + _typeref.typename = _file.name + + -- create return + local _return = apimodel._return() + table.insert(_return.types,_typeref) + + -- add return + table.insert(_file.returns,_return) + + --create recordtypedef is not define + gettypedef(_file,_typeref.typename,"recordtypedef",sourcerangemin,sourcerangemax) + end + -- manage "type" comment + elseif regulartags["type"] and regulartags["type"][1].name ~= "global" then + local dt_type = regulartags["type"][1]; + -- create record type if it doesn't exist + local sourcerangemin = comment.lineinfo.first.offset + local sourcerangemax = comment.lineinfo.last.offset + local _recordtypedef = gettypedef (_file, dt_type.name ,"recordtypedef",sourcerangemin,sourcerangemax) + _lastapiobject = _recordtypedef + + -- re-set sourcerange in case the type was created before the type tag + _recordtypedef.sourcerange.min = sourcerangemin + _recordtypedef.sourcerange.max = sourcerangemax + + -- manage description + _recordtypedef.shortdescription = parsedcomment.shortdescription + _recordtypedef.description = parsedcomment.description + + -- manage fields + if regulartags["field"] then + for _, dt_field in ipairs(regulartags["field"]) do + local _item = createfield(dt_field,_file,sourcerangemin,sourcerangemax) + -- define sourcerange only if we create it + _item.sourcerange.min = sourcerangemin + _item.sourcerange.max = sourcerangemax + if _item then _recordtypedef:addfield(_item) end + end + end + elseif regulartags["field"] then + local dt_field = regulartags["field"][1] + + -- create item + local sourcerangemin = comment.lineinfo.first.offset + local sourcerangemax = comment.lineinfo.last.offset + local _item = createfield(dt_field,_file,sourcerangemin,sourcerangemax) + _item.shortdescription = parsedcomment.shortdescription + _item.description = parsedcomment.description + _lastapiobject = _item + + -- define sourcerange + _item.sourcerange.min = sourcerangemin + _item.sourcerange.max = sourcerangemax + + -- add item to its parent + local scope = regulartags["field"][1].parent + M.additemtoparent(_file,_item,scope,sourcerangemin,sourcerangemax) + elseif regulartags["function"] or regulartags["param"] or regulartags["return"] then + -- create item + local _item = apimodel._item() + _item.shortdescription = parsedcomment.shortdescription + _item.description = parsedcomment.description + _lastapiobject = _item + + -- set name + if regulartags["function"] then _item.name = regulartags["function"][1].name end + + -- define sourcerange + local sourcerangemin = comment.lineinfo.first.offset + local sourcerangemax = comment.lineinfo.last.offset + _item.sourcerange.min = sourcerangemin + _item.sourcerange.max = sourcerangemax + + + -- create function type + local _functiontypedef = apimodel._functiontypedef() + _functiontypedef.shortdescription = parsedcomment.shortdescription + _functiontypedef.description = parsedcomment.description + + + -- manage params + if regulartags["param"] then + for _, dt_param in ipairs(regulartags["param"]) do + local _param = createparam(dt_param,_file,sourcerangemin,sourcerangemax) + table.insert(_functiontypedef.params,_param) + end + end + + -- manage returns + if regulartags["return"] then + for _, dt_return in ipairs(regulartags["return"]) do + local _return = createreturn(dt_return,_file,sourcerangemin,sourcerangemax) + table.insert(_functiontypedef.returns,_return) + end + end + + -- add type name + _functiontypedef.name = generatefunctiontypename(_functiontypedef) + _file:addtype(_functiontypedef) + + -- create ref to this type + local _internaltyperef = apimodel._internaltyperef() + _internaltyperef.typename = _functiontypedef.name + _item.type=_internaltyperef + + -- add item to its parent + local sourcerangemin = comment.lineinfo.first.offset + local sourcerangemax = comment.lineinfo.last.offset + local scope = (regulartags["function"] and regulartags["function"][1].parent) or nil + M.additemtoparent(_file,_item,scope,sourcerangemin,sourcerangemax) + end + end + + -- when we could not know which type of api object it is, we suppose this is an item + if not _lastapiobject then + _lastapiobject = apimodel._item() + _lastapiobject.shortdescription = parsedcomment.shortdescription + _lastapiobject.description = parsedcomment.description + _lastapiobject.sourcerange.min = comment.lineinfo.first.offset + _lastapiobject.sourcerange.max = comment.lineinfo.last.offset + end + + -- + -- Store user defined tags + -- + local thirdtags = parsedcomment and parsedcomment.unknowntags + if thirdtags then + -- Define a storage index for user defined tags on current API element + if not _lastapiobject.metadata then _lastapiobject.metadata = {} end + + -- Loop over user defined tags + for usertag, taglist in pairs(thirdtags) do + if not _lastapiobject.metadata[ usertag ] then + _lastapiobject.metadata[ usertag ] = { + tag = usertag + } + end + for _, tag in ipairs( taglist ) do + table.insert(_lastapiobject.metadata[usertag], tag) + end + end + end + + -- if we create an api object linked it to + _comment2apiobj[comment] =_lastapiobject + end + + local function parsecomment(node, parent, ...) + -- check for comments before this node + if node.lineinfo and node.lineinfo.first.comments then + local comments = node.lineinfo.first.comments + -- check all comments + for _,comment in ipairs(comments) do + -- if not already handled + if not handledcomments[comment] then + handlecomment(comment) + handledcomments[comment]=true + end + end + end + -- check for comments after this node + if node.lineinfo and node.lineinfo.last.comments then + local comments = node.lineinfo.last.comments + -- check all comments + for _,comment in ipairs(comments) do + -- if not already handled + if not handledcomments[comment] then + handlecomment(comment) + handledcomments[comment]=true + end + end + end + end + Q(ast):filter(function(x) return x.tag~=nil end):foreach(parsecomment) + return _file, _comment2apiobj +end + + +function M.extractlocaltype ( commentblock,_file) + if not commentblock then return nil end + + local stringcomment = commentblock[1] + + local parsedtag = ldp.parseinlinecomment(stringcomment) + if parsedtag then + local sourcerangemin = commentblock.lineinfo.first.offset + local sourcerangemax = commentblock.lineinfo.last.offset + + return createtyperef(parsedtag,_file,sourcerangemin,sourcerangemax), parsedtag.description + end + + return nil, stringcomment +end + +M.generatefunctiontypename = generatefunctiontypename + +return M \ No newline at end of file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/internalmodel.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/internalmodel.lua new file mode 100644 index 000000000..552521052 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/internalmodel.lua @@ -0,0 +1,65 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2012 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Kevin KIN-FOO +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +local M = {} + +function M._internalcontent() + return { + content = nil, -- block + unknownglobalvars = {}, -- list of item + tag = "MInternalContent" + } +end + +function M._block() + return { + content = {}, -- list of expr (identifier, index, call, invoke, block) + localvars = {}, -- list of {var=item, scope ={min,max}} + sourcerange = {min=0,max=0}, + tag = "MBlock" + } +end + +function M._identifier() + return { + definition = nil, -- item + sourcerange = {min=0,max=0}, + tag = "MIdentifier" + } +end + +function M._index(key, value) + return { + left= key, -- expr (identifier, index, call, invoke, block) + right= value, -- string + sourcerange = {min=0,max=0}, + tag = "MIndex" + } +end + +function M._call(funct) + return { + func = funct, -- expr (identifier, index, call, invoke, block) + sourcerange = {min=0,max=0}, + tag = "MCall" + } +end + +function M._invoke(name, expr) + return { + functionname = name, -- string + record = expr, -- expr (identifier, index, call, invoke, block) + sourcerange = {min=0,max=0}, + tag = "MInvoke" + } +end + +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/internalmodelbuilder.mlua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/internalmodelbuilder.mlua new file mode 100644 index 000000000..4aeafa6cb --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/internalmodelbuilder.mlua @@ -0,0 +1,861 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2011-2012 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Simon BERNARD +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +-{ extension ('match', ...) } + +local Q = require 'metalua.treequery' + +local internalmodel = require 'models.internalmodel' +local apimodel = require 'models.apimodel' +local apimodelbuilder = require 'models.apimodelbuilder' + +local M = {} + +-- Analyzes an AST and returns two tables +-- * `locals`, which associates `Id{ } nodes which create a local variable +-- to a list of the `Id{ } occurrence nodes of that variable; +-- * `globals` which associates variable names to occurrences of +-- global variables having that name. +function bindings(ast) + local locals, globals = { }, { } + local function f(id, ...) + local name = id[1] + if Q.is_binder(id, ...) then + local binder = ... -- parent is the binder + locals[binder] = locals[binder] or { } + locals[binder][name]={ } + else + local _, binder = Q.get_binder(id, ...) + if binder then -- this is a local + table.insert(locals[binder][name], id) + else + local g = globals[name] + if g then table.insert(g, id) else globals[name]={id} end + end + end + end + Q(ast) :filter('Id') :foreach(f) + return locals, globals +end + +-- -------------------------------------- + +-- ---------------------------------------------------------- +-- return the comment linked before to this node +-- ---------------------------------------------------------- +local function getlinkedcommentbefore(node) + local function _getlinkedcomment(node,line) + if node and node.lineinfo and node.lineinfo.first.line == line then + -- get the last comment before (the nearest of code) + local comments = node.lineinfo.first.comments + local comment = comments and comments[#comments] + if comment and comment.lineinfo.last.line == line-1 then + -- ignore the comment if there are code before on the same line + if node.lineinfo.first.facing and (node.lineinfo.first.facing.line ~= comment.lineinfo.first.line) then + return comment + end + else + return _getlinkedcomment(node.parent,line) + end + end + return nil + end + + if node.lineinfo and node.lineinfo.first.line then + return _getlinkedcomment(node,node.lineinfo.first.line) + else + return nil + end +end + +-- ---------------------------------------------------------- +-- return the comment linked after to this node +-- ---------------------------------------------------------- +local function getlinkedcommentafter(node) + local function _getlinkedcomment(node,line) + if node and node.lineinfo and node.lineinfo.last.line == line then + -- get the first comment after (the nearest of code) + local comments = node.lineinfo.last.comments + local comment = comments and comments[1] + if comment and comment.lineinfo.first.line == line then + return comment + else + return _getlinkedcomment(node.parent,line) + end + end + return nil + end + + if node.lineinfo and node.lineinfo.last.line then + return _getlinkedcomment(node,node.lineinfo.last.line) + else + return nil + end +end + +-- ---------------------------------------------------------- +-- return true if this node is a block for the internal representation +-- ---------------------------------------------------------- +local supported_b = { + Function = true, + Do = true, + While = true, + Fornum = true, + Forin = true, + Repeat = true, +} +local function supportedblock(node, parent) + return supported_b[ node.tag ] or + (parent and parent.tag == "If" and node.tag == nil) +end + +-- ---------------------------------------------------------- +-- create a block from the metalua node +-- ---------------------------------------------------------- +local function createblock(block, parent) + local _block = internalmodel._block() + match block with + | `Function{param, body} + | `Do{...} + | `Fornum {identifier, min, max, body} + | `Forin {identifiers, exprs, body} + | `Repeat {body, expr} -> + _block.sourcerange.min = block.lineinfo.first.offset + _block.sourcerange.max = block.lineinfo.last.offset + | `While {expr, body} -> + _block.sourcerange.min = body.lineinfo.first.facing.offset + _block.sourcerange.max = body.lineinfo.last.facing.offset + | _ -> + if parent and parent.tag == "If" and block.tag == nil then + _block.sourcerange.min = block.lineinfo.first.facing.offset + _block.sourcerange.max = block.lineinfo.last.facing.offset + end + end + return _block +end + +-- ---------------------------------------------------------- +-- return true if this node is a expression in the internal representation +-- ---------------------------------------------------------- +local supported_e = { + Index = true, + Id = true, + Call = true, + Invoke = true +} +local function supportedexpr(node) + return supported_e[ node.tag ] +end + +local idto_block = {} -- cache from metalua id to internal model block +local idto_identifier = {} -- cache from metalua id to internal model indentifier +local expreto_expression = {} -- cache from metalua expression to internal model expression + +-- ---------------------------------------------------------- +-- create an expression from a metalua node +-- ---------------------------------------------------------- +local function createexpr(expr,_block) + local _expr = nil + + match expr with + | `Id { name } -> + -- we store the block which hold this node + -- to be able to define + idto_block[expr]= _block + + -- if expr has not line info, it means expr has no representation in the code + -- so we don't need it. + if not expr.lineinfo then return nil end + + -- create identifier + local _identifier = internalmodel._identifier() + idto_identifier[expr]= _identifier + _expr = _identifier + | `Index { innerexpr, `String{fieldname} } -> + if not expr.lineinfo then return nil end + -- create index + local _expression = createexpr(innerexpr,_block) + if _expression then _expr = internalmodel._index(_expression,fieldname) end + | `Call{innerexpr, ...} -> + if not expr.lineinfo then return nil end + -- create call + local _expression = createexpr(innerexpr,_block) + if _expression then _expr = internalmodel._call(_expression) end + | `Invoke{innerexpr,`String{functionname},...} -> + if not expr.lineinfo then return nil end + -- create invoke + local _expression = createexpr(innerexpr,_block) + if _expression then _expr = internalmodel._invoke(functionname,_expression) end + | _ -> + end + + if _expr then + _expr.sourcerange.min = expr.lineinfo.first.offset + _expr.sourcerange.max = expr.lineinfo.last.offset + + expreto_expression[expr] = _expr + end + + return _expr +end + +-- ---------------------------------------------------------- +-- create block and expression node +-- ---------------------------------------------------------- +local function createtreestructure(ast) + -- create internal content + local _internalcontent = internalmodel._internalcontent() + + -- create root block + local _block = internalmodel._block() + local _blocks = { _block } + _block.sourcerange.min = ast.lineinfo.first.facing.offset + -- TODO remove the math.max when we support partial AST + _block.sourcerange.max = math.max(ast.lineinfo.last.facing.offset, 10000) + + _internalcontent.content = _block + + -- visitor function (down) + local function down (node,parent) + if supportedblock(node,parent) then + -- create the block + local _block = createblock(node,parent) + -- add it to parent block + table.insert(_blocks[#_blocks].content, _block) + -- enqueue the last block to know the "current" block + table.insert(_blocks,_block) + elseif supportedexpr(node) then + -- we handle expression only if it was not already do + if not expreto_expression[node] then + -- create expr + local _expression = createexpr(node,_blocks[#_blocks]) + -- add it to parent block + if _expression then + table.insert(_blocks[#_blocks].content, _expression) + end + end + end + end + + -- visitor function (up) + local function up (node, parent) + if supportedblock(node,parent) then + -- dequeue the last block to know the "current" block + table.remove(_blocks,#_blocks) + end + end + + -- visit ast and build internal model + Q(ast):foreach(down,up) + + return _internalcontent +end + +local getitem + +-- ---------------------------------------------------------- +-- create the type from the node and position +-- ---------------------------------------------------------- +local function createtype(node,position,comment2apiobj,file) + -- create module type ref + match node with + | `Call{ `Id "require", `String {modulename}} -> + return apimodel._moduletyperef(modulename,position) + | `Function {params, body} -> + -- create the functiontypedef from code + local _functiontypedef = apimodel._functiontypedef() + for _, p in ipairs(params) do + -- create parameters + local paramname + if p.tag=="Dots" then + paramname = "..." + else + paramname = p[1] + end + local _param = apimodel._parameter(paramname) + table.insert(_functiontypedef.params,_param) + end + _functiontypedef.name = "___" -- no name for inline type + + return apimodel._inlinetyperef(_functiontypedef) + | `String {value} -> + local typeref = apimodel._primitivetyperef("string") + return typeref + | `Number {value} -> + local typeref = apimodel._primitivetyperef("number") + return typeref + | `True | `False -> + local typeref = apimodel._primitivetyperef("boolean") + return typeref + | `Table {...} -> + -- create recordtypedef from code + local _recordtypedef = apimodel._recordtypedef("___") -- no name for inline type + -- for each element of the table + for i=1,select("#", ...) do + local pair = select(i, ...) + -- if this is a pair we create a new item in the type + if pair.tag == "Pair" then + -- create an item + local _item = getitem(pair,nil, comment2apiobj,file) + if _item then + _recordtypedef:addfield(_item) + end + end + end + return apimodel._inlinetyperef(_recordtypedef) + | _ -> + end + -- if node is an expression supported + local supportedexpr = expreto_expression[node] + if supportedexpr then + -- create expression type ref + return apimodel._exprtyperef(supportedexpr,position) + end + +end + +local function completeapidoctype(apidoctype,itemname,init,file,comment2apiobj) + if not apidoctype.name then + apidoctype.name = itemname + file:mergetype(apidoctype) + end + + -- create type from code + local typeref = createtype(init,1,comment2apiobj,file) + if typeref and typeref.tag == "inlinetyperef" + and typeref.def.tag == "recordtypedef" then + + -- set the name + typeref.def.name = apidoctype.name + + -- merge the type with priority to documentation except for source range + file:mergetype(typeref.def,false,true) + end +end + +local function completeapidocitem (apidocitem, itemname, init, file, binder, comment2apiobj) + -- manage the case item has no name + if not apidocitem.name then + apidocitem.name = itemname + + -- if item has no name this means it could not be attach to a parent + if apidocitem.scope then + apimodelbuilder.additemtoparent(file,apidocitem,apidocitem.scope,apidocitem.sourcerange.min,apidocitem.sourcerange.max) + apidocitem.scope = nil + end + end + + -- for function try to merge definition + local apitype = apidocitem:resolvetype(file) + if apitype and apitype.tag == "functiontypedef" then + local codetype = createtype(init,1,comment2apiobj,file) + if codetype and codetype.tag =="inlinetyperef" then + codetype.def.name = apitype.name + file:mergetype(codetype.def) + end + end + + -- manage the case item has no type + if not apidocitem.type then + -- extract typing from comment + local type, desc = apimodelbuilder.extractlocaltype(getlinkedcommentafter(binder),file) + + if type then + apidocitem.type = type + else + -- if not found extracttype from code + apidocitem.type = createtype(init,1,comment2apiobj,file) + end + end +end + +-- ---------------------------------------------------------- +-- create or get the item finding in the binder with the given itemname +-- return also the ast node corresponding to this item +-- ---------------------------------------------------------- +getitem = function (binder, itemname, comment2apiobj, file) + + -- local function to create item + local function createitem(itemname, astnode, itemtype, description) + local _item = apimodel._item(itemname) + if description then _item.description = description end + _item.type = itemtype + if astnode and astnode.lineinfo then + _item.sourcerange.min = astnode.lineinfo.first.offset + _item.sourcerange.max = astnode.lineinfo.last.offset + end + return _item, astnode + end + + -- try to match binder with known patter of item declaration + match binder with + | `Pair {string, init} + | `Set { {`Index { right , string}}, {init,...}} if string and string.tag =="String" -> + -- Pair and set is for searching field from type .. + -- if the itemname is given this mean we search for a local or a global not a field type. + if not itemname then + local itemname = string[1] + + -- check for luadoc typing + local commentbefore = getlinkedcommentbefore(binder) + local apiobj = comment2apiobj[commentbefore] -- find apiobj linked to this comment + if apiobj then + if apiobj.tag=="item" then + if not apiobj.name or apiobj.name == itemname then + -- use code to complete api information if it's necessary + completeapidocitem(apiobj, itemname, init,file,binder,comment2apiobj) + -- for item use code source range rather than doc source range + if string and string.lineinfo then + apiobj.sourcerange.min = string.lineinfo.first.offset + apiobj.sourcerange.max = string.lineinfo.last.offset + end + return apiobj, string + end + elseif apiobj.tag=="recordtypedef" then + -- use code to complete api information if it's necessary + completeapidoctype(apiobj, itemname, init,file,comment2apiobj) + return createitem(itemname, string, apimodel._internaltyperef(apiobj.name), nil) + end + + -- if the apiobj could not be associated to the current obj, + -- we do not use the documentation neither + commentbefore = nil + end + + -- else we use code to extract the type and description + -- check for "local" typing + local type, desc = apimodelbuilder.extractlocaltype(getlinkedcommentafter(binder),file) + local desc = desc or (commentbefore and commentbefore[1]) + if type then + return createitem(itemname, string, type, desc ) + else + -- if no "local typing" extract type from code + return createitem(itemname, string, createtype(init,1,comment2apiobj,file), desc) + end + end + | `Set {ids, inits} + | `Local {ids, inits} -> + -- if this is a single local var declaration + -- we check if there are a comment block linked and try to extract the type + if #ids == 1 then + local currentid, currentinit = ids[1],inits[1] + -- ignore non Ids node + if currentid.tag ~= 'Id' or currentid[1] ~= itemname then return nil end + + -- check for luadoc typing + local commentbefore = getlinkedcommentbefore(binder) + local apiobj = comment2apiobj[commentbefore] -- find apiobj linked to this comment + if apiobj then + if apiobj.tag=="item" then + -- use code to complete api information if it's necessary + if not apiobj.name or apiobj.name == itemname then + completeapidocitem(apiobj, itemname, currentinit,file,binder,comment2apiobj) + -- if this is a global var or if is has no parent + -- we do not create a new item + if not apiobj.parent or apiobj.parent == file then + -- for item use code source range rather than doc source range + if currentid and currentid.lineinfo then + apiobj.sourcerange.min = currentid.lineinfo.first.offset + apiobj.sourcerange.max = currentid.lineinfo.last.offset + end + return apiobj, currentid + else + return createitem(itemname, currentid, apiobj.type, nil) + end + end + elseif apiobj.tag=="recordtypedef" then + -- use code to complete api information if it's necessary + completeapidoctype(apiobj, itemname, currentinit,file,comment2apiobj) + return createitem(itemname, currentid, apimodel._internaltyperef(apiobj.name), nil) + end + + -- if the apiobj could not be associated to the current obj, + -- we do not use the documentation neither + commentbefore = nil + end + + -- else we use code to extract the type and description + -- check for "local" typing + local type,desc = apimodelbuilder.extractlocaltype(getlinkedcommentafter(binder),file) + desc = desc or (commentbefore and commentbefore[1]) + if type then + return createitem(itemname, currentid, type, desc) + else + -- if no "local typing" extract type from code + return createitem(itemname, currentid, createtype(currentinit,1,comment2apiobj,file), desc) + end + end + -- else we use code to extract the type + local init,returnposition = nil,1 + for i,id in ipairs(ids) do + -- calculate the current return position + if init and (init.tag == "Call" or init.tag == "Invoke") then + -- if previous init was a call or an invoke + -- we increment the returnposition + returnposition= returnposition+1 + else + -- if init is not a function call + -- we change the init used to determine the type + init = inits[i] + end + + -- get the name of the current id + local idname = id[1] + + -- if this is the good id + if itemname == idname then + -- create type from init node and return position + return createitem (itemname, id, createtype(init,returnposition,comment2apiobj,file),nil) + end + end + | `Function {params, body} -> + for i,id in ipairs(params) do + -- get the name of the current id + local idname = id[1] + -- if this is the good id + if itemname == idname then + -- extract param's type from luadocumentation + local obj = comment2apiobj[getlinkedcommentbefore(binder)] + if obj and obj.tag=="item" then + local typedef = obj:resolvetype(file) + if typedef and typedef.tag =="functiontypedef" then + for j, param in ipairs(typedef.params) do + if i==j then + if i ==1 and itemname == "self" and param.type == nil + and obj.parent and obj.parent.tag == "recordtypedef" and obj.parent.name then + param.type = apimodel._internaltyperef(obj.parent.name) + end + -- TODO perhaps we must clone the typeref + return createitem(itemname,id, param.type,param.description) + end + end + end + end + return createitem(itemname,id) + end + end + | `Forin {ids, expr, body} -> + for i,id in ipairs(ids) do + -- get the name of the current id + local idname = id[1] + -- if this is the good id + if itemname == idname then + -- return data : we can not guess the type for now + return createitem(itemname,id) + end + end + | `Fornum {id, ...} -> + -- get the name of the current id + local idname = id[1] + -- if this is the good id + if itemname == idname then + -- return data : we can not guess the type for now + return createitem(itemname,id) + end + | `Localrec {{id}, {func}} -> + -- get the name of the current id + local idname = id[1] + -- if this is the good id + if itemname == idname then + -- check for luadoc typing + local commentbefore = getlinkedcommentbefore(binder) + local apiobj = comment2apiobj[commentbefore] -- find apiobj linked to this comment + if apiobj then + if apiobj.tag=="item" then + if not apiobj.name or apiobj.name == itemname then + -- use code to complete api information if it's necessary + completeapidocitem(apiobj, itemname, func,file,binder,comment2apiobj) + return createitem(itemname,id,apiobj.type,nil) + end + end + + -- if the apiobj could not be associated to the current obj, + -- we do not use the documentation neither + commentbefore = nil + end + + -- else we use code to extract the type and description + -- check for "local" typing + local type,desc = apimodelbuilder.extractlocaltype(getlinkedcommentafter(binder),file) + desc = desc or (commentbefore and commentbefore[1]) + if type then + return createitem(itemname, id, type, desc) + else + -- if no "local typing" extract type from code + return createitem(itemname, id, createtype(func,1,comment2apiobj,file), desc) + end + end + | _ -> + end +end + +-- ---------------------------------------------------------- +-- Search from Id node to Set node to find field of type. +-- +-- Lua code : table.field1.field2 = 12 +-- looks like that in metalua : +-- `Set{ +-- `Index { `Index { `Id "table", `String "field1" }, +-- `String "field2"}, +-- `Number "12"} +-- ---------------------------------------------------------- +local function searchtypefield(node,_currentitem,comment2apiobj,file) + + -- we are just interested : + -- by item which is field of recordtypedef + -- by ast node which are Index + if _currentitem then + local type = _currentitem:resolvetype(file) + if type and type.tag == "recordtypedef" then + if node and node.tag == "Index" then + local rightpart = node[2] + local _newcurrentitem = type.fields[rightpart[1]] + + if _newcurrentitem then + -- if this index represent a known field of the type we continue to search + searchtypefield (node.parent,_newcurrentitem,comment2apiobj,file) + else + -- if not, this is perhaps a new field, but + -- to be a new field this index must be include in a Set + if node.parent and node.parent.tag =="Set" then + -- in this case we create the new item ans add it to the type + local set = node.parent + local item, string = getitem(set,nil, comment2apiobj,file) + -- add this item to the type, only if it has no parent and if this type does not contain already this field + if item and not item.parent and string and not type.fields[string[1]] then + type:addfield(item) + end + end + end + end + end + end +end + +-- ---------------------------------------------------------- +-- create local vars, global vars and linked it with theirs occurences +-- ---------------------------------------------------------- +local function createvardefinitions(_internalcontent,ast,file,comment2apiobj) + -- use bindings to get locals and globals definition + local locals, globals = bindings( ast ) + + -- create locals var + for binder, namesAndOccurrences in pairs(locals) do + for name, occurrences in pairs(namesAndOccurrences) do + -- get item, id + local _item, id = getitem(binder, name,comment2apiobj,file) + if id then + -- add definition as occurence + -- we consider the identifier in the binder as an occurence + local _identifierdef = idto_identifier[id] + if _identifierdef then + table.insert(_item.occurrences, _identifierdef) + _identifierdef.definition = _item + end + + -- add occurences + for _,occurrence in ipairs(occurrences) do + searchtypefield(occurrence.parent, _item,comment2apiobj,file) + local _identifier = idto_identifier[occurrence] + if _identifier then + table.insert(_item.occurrences, _identifier) + _identifier.definition = _item + end + end + + -- add item to block + local _block = idto_block[id] + table.insert(_block.localvars,{item=_item,scope = {min=0,max=0}}) + end + end + end + + -- create globals var + for name, occurrences in pairs( globals ) do + + -- get or create definition + local _item = file.globalvars[name] + local binder = occurrences[1].parent + if not _item then + -- global declaration is only if the first occurence in left part of a 'Set' + if binder and binder.tag == "Set" then + _item = getitem(binder, name,comment2apiobj,file) + end + + -- if we find and item this is a global var declaration + if _item then + file:addglobalvar(_item) + else + -- else it is an unknown global var + _item = apimodel._item(name) + local _firstoccurrence = idto_identifier[occurrences[1]] + if _firstoccurrence then + _item.sourcerange.min = _firstoccurrence.sourcerange.min + _item.sourcerange.max = _firstoccurrence.sourcerange.max + end + table.insert(_internalcontent.unknownglobalvars,_item) + end + else + -- if the global var definition already exists, we just try to it + if binder then + match binder with + | `Set {ids, inits} -> + -- manage case only if there are 1 element in the Set + if #ids == 1 then + local currentid, currentinit = ids[1],inits[1] + -- ignore non Ids node and bad name + if currentid.tag == 'Id' and currentid[1] == name then + completeapidocitem(_item, name, currentinit,file,binder,comment2apiobj) + + if currentid and currentid.lineinfo then + _item.sourcerange.min = currentid.lineinfo.first.offset + _item.sourcerange.max = currentid.lineinfo.last.offset + end + end + end + | _ -> + end + end + end + + -- add occurences + for _,occurence in ipairs(occurrences) do + local _identifier = idto_identifier[occurence] + searchtypefield(occurence.parent, _item,comment2apiobj,file) + if _identifier then + table.insert(_item.occurrences, _identifier) + _identifier.definition = _item + end + end + end +end + +-- ---------------------------------------------------------- +-- add parent to all ast node +-- ---------------------------------------------------------- +local function addparents(ast) + -- visitor function (down) + local function down (node,parent) + node.parent = parent + end + + -- visit ast and build internal model + Q(ast):foreach(down,up) +end + +-- ---------------------------------------------------------- +-- try to detect a module declaration from code +-- ---------------------------------------------------------- +local function searchmodule(ast,file,comment2apiobj,modulename) + -- if the last statement is a return + if ast then + local laststatement = ast[#ast] + if laststatement and laststatement.tag == "Return" then + -- and if the first expression returned is an identifier. + local firstexpr = laststatement[1] + if firstexpr and firstexpr.tag == "Id" then + -- get identifier in internal model + local _identifier = idto_identifier [firstexpr] + -- the definition should be an inline type + if _identifier + and _identifier.definition + and _identifier.definition.type + and _identifier.definition.type.tag == "inlinetyperef" + and _identifier.definition.type.def.tag == "recordtypedef" then + + --set modulename if needed + if not file.name then file.name = modulename end + + -- create or merge type + local _type = _identifier.definition.type.def + _type.name = modulename + + -- if file (module) has no documentation add item documentation to it + -- else add it to the type. + if not file.description or file.description == "" then + file.description = _identifier.definition.description + else + _type.description = _identifier.definition.description + end + _identifier.definition.description = "" + if not file.shortdescription or file.shortdescription == "" then + file.shortdescription = _identifier.definition.shortdescription + else + _type.shortdescription = _identifier.definition.shortdescription + end + _identifier.definition.shortdescription = "" + + -- WORKAROUND FOR BUG 421622: [outline]module selection in outline does not select it in texteditor + --_type.sourcerange.min = _identifier.definition.sourcerange.min + --_type.sourcerange.max = _identifier.definition.sourcerange.max + + -- merge the type with priority to documentation except for source range + file:mergetype(_type,false,true) + + -- create return if needed + if not file.returns[1] then + file.returns[1] = apimodel._return() + file.returns[1].types = { apimodel._internaltyperef(modulename) } + end + + -- change the type of the identifier + _identifier.definition.type = apimodel._internaltyperef(modulename) + end + end + end + end +end + +-- ---------------------------------------------------------- +-- create the internalcontent from an ast metalua +-- ---------------------------------------------------------- +function M.createinternalcontent (ast,file,comment2apiobj,modulename) + -- init cache + idto_block = {} + idto_identifier = {} + expreto_expression = {} + comment2apiobj = comment2apiobj or {} + file = file or apimodel._file() + + -- execute code safely to be sure to clean cache correctly + local internalcontent + local ok, errmsg = pcall(function () + -- add parent to all node + addparents(ast) + + -- create block and expression node + internalcontent = createtreestructure(ast) + + -- create Local vars, global vars and linked occurences (Items) + createvardefinitions(internalcontent,ast,file,comment2apiobj) + + -- try to dectect module information from code + local moduletyperef = file:moduletyperef() + if moduletyperef and moduletyperef.tag == "internaltyperef" then + modulename = moduletyperef.typename or modulename + end + if modulename then + searchmodule(ast,file,comment2apiobj,modulename) + end + end) + + -- clean cache + idto_block = {} + idto_identifier = {} + expreto_expression = {} + + -- if not ok raise an error + if not ok then error (errmsg) end + + return internalcontent +end + +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/ldparser.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/ldparser.lua new file mode 100644 index 000000000..d74071bf4 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/ldparser.lua @@ -0,0 +1,656 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2011-2013 Sierra Wireless and others. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Sierra Wireless - initial API and implementation +------------------------------------------------------------------------------- +local mlc = require ('metalua.compiler').new() +local gg = require 'metalua.grammar.generator' +local lexer = require 'metalua.grammar.lexer' +local mlp = mlc.parser + +local M = {} -- module +local lx -- lexer used to parse tag +local registeredparsers -- table {tagname => {list de parsers}} + +-- ---------------------------------------------------- +-- raise an error if result contains a node error +-- ---------------------------------------------------- +local function raiserror(result) + for i, node in ipairs(result) do + assert(not node or node.tag ~= "Error") + end +end + + +-- ---------------------------------------------------- +-- copy key and value from one table to an other +-- ---------------------------------------------------- +local function copykey(tablefrom, tableto) + for key, value in pairs(tablefrom) do + if key ~= "lineinfos" then + tableto[key] = value + end + end +end + +-- ---------------------------------------------------- +-- Handle keyword and identifiers as word +-- ---------------------------------------------------- +local function parseword(lx) + local word = lx :peek() + local tag = word.tag + + if tag=='Keyword' or tag=='Id' then + lx:next() + return {tag='Word', lineinfo=word.lineinfo, word[1]} + else + return gg.parse_error(lx,'Id or Keyword expected') + end +end + +-- ---------------------------------------------------- +-- parse an id +-- return a table {name, lineinfo) +-- ---------------------------------------------------- +local idparser = gg.sequence({ + builder = function (result) + raiserror(result) + return { name = result[1][1] } + end, + parseword +}) + +-- ---------------------------------------------------- +-- parse a modulename (id.)?id +-- return a table {name, lineinfo) +-- ---------------------------------------------------- +local modulenameparser = gg.list({ + builder = function (result) + raiserror(result) + local ids = {} + for i, id in ipairs(result) do + table.insert(ids,id.name) + end + return {name = table.concat(ids,".")} + end, + primary = idparser, + separators = '.' +}) +-- ---------------------------------------------------- +-- parse a typename (id.)?id +-- return a table {name, lineinfo) +-- ---------------------------------------------------- +local typenameparser= modulenameparser + +-- ---------------------------------------------------- +-- parse an internaltype ref +-- return a table {name, lineinfo) +-- ---------------------------------------------------- +local internaltyperefparser = gg.sequence({ + builder = function(result) + raiserror(result) + return {tag = "typeref",type=result[1].name} + end, + "#", typenameparser +}) + +-- ---------------------------------------------------- +-- parse en external type ref +-- return a table {name, lineinfo) +-- ---------------------------------------------------- +local externaltyperefparser = gg.sequence({ + builder = function(result) + raiserror(result) + return {tag = "typeref",module=result[1].name,type=result[2].name} + end, + modulenameparser,"#", typenameparser +}) + + +-- ---------------------------------------------------- +-- parse a typeref +-- return a table {name, lineinfo) +-- ---------------------------------------------------- +local typerefparser = gg.multisequence{ + internaltyperefparser, + externaltyperefparser} + +-- ---------------------------------------------------- +-- parse a list of typeref +-- return a list of table {name, lineinfo) +-- ---------------------------------------------------- +local typereflistparser = gg.list({ + primary = typerefparser, + separators = ',' +}) + +-- ---------------------------------------------------- +-- TODO use a more generic way to parse (modifier if not always a typeref) +-- TODO support more than one modifier +-- ---------------------------------------------------- +local modifiersparser = gg.sequence({ + builder = function(result) + raiserror(result) + return {[result[1].name]=result[2]} + end, + "[", idparser , "=" , internaltyperefparser , "]" +}) + +-- ---------------------------------------------------- +-- parse a return tag +-- ---------------------------------------------------- +local returnparsers = { + -- full parser + gg.sequence({ + builder = function (result) + raiserror(result) + return { types= result[1]} + end, + '@','return', typereflistparser + }), + -- parser without typerefs + gg.sequence({ + builder = function (result) + raiserror(result) + return { types = {}} + end, + '@','return' + }) +} + +-- ---------------------------------------------------- +-- parse a param tag +-- ---------------------------------------------------- +local paramparsers = { + -- full parser + gg.sequence({ + builder = function (result) + raiserror(result) + return { name = result[2].name, type = result[1]} + end, + '@','param', typerefparser, idparser + }), + + -- full parser without type + gg.sequence({ + builder = function (result) + raiserror(result) + return { name = result[1].name} + end, + '@','param', idparser + }), + + -- Parser for `Dots + gg.sequence({ + builder = function (result) + raiserror(result) + return { name = '...' } + end, + '@','param', '...' + }), +} +-- ---------------------------------------------------- +-- parse a field tag +-- ---------------------------------------------------- +local fieldparsers = { + -- full parser + gg.sequence({ + builder = function (result) + raiserror(result) + local tag = {} + copykey(result[1],tag) + tag.type = result[2] + tag.name = result[3].name + return tag + end, + '@','field', modifiersparser, typerefparser, idparser + }), + + -- parser without name + gg.sequence({ + builder = function (result) + raiserror(result) + local tag = {} + copykey(result[1],tag) + tag.type = result[2] + return tag + end, + '@','field', modifiersparser, typerefparser + }), + + -- parser without type + gg.sequence({ + builder = function (result) + raiserror(result) + local tag = {} + copykey(result[1],tag) + tag.name = result[2].name + return tag + end, + '@','field', modifiersparser, idparser + }), + + -- parser without type and name + gg.sequence({ + builder = function (result) + raiserror(result) + local tag = {} + copykey(result[1],tag) + return tag + end, + '@','field', modifiersparser + }), + + -- parser without modifiers + gg.sequence({ + builder = function (result) + raiserror(result) + return { name = result[2].name, type = result[1]} + end, + '@','field', typerefparser, idparser + }), + + -- parser without modifiers and name + gg.sequence({ + builder = function (result) + raiserror(result) + return {type = result[1]} + end, + '@','field', typerefparser + }), + + -- parser without type and modifiers + gg.sequence({ + builder = function (result) + raiserror(result) + return { name = result[1].name} + end, + '@','field', idparser + }), + + -- parser with nothing + gg.sequence({ + builder = function (result) + raiserror(result) + return {} + end, + '@','field' + }) +} + +-- ---------------------------------------------------- +-- parse a function tag +-- TODO use a more generic way to parse modifier ! +-- ---------------------------------------------------- +local functionparsers = { + -- full parser + gg.sequence({ + builder = function (result) + raiserror(result) + local tag = {} + copykey(result[1],tag) + tag.name = result[2].name + return tag + end, + '@','function', modifiersparser, idparser + }), + + -- parser without name + gg.sequence({ + builder = function (result) + raiserror(result) + local tag = {} + copykey(result[1],tag) + return tag + end, + '@','function', modifiersparser + }), + + -- parser without modifier + gg.sequence({ + builder = function (result) + raiserror(result) + local tag = {} + tag.name = result[1].name + return tag + end, + '@','function', idparser + }), + + -- empty parser + gg.sequence({ + builder = function (result) + raiserror(result) + return {} + end, + '@','function' + }) +} + +-- ---------------------------------------------------- +-- parse a type tag +-- ---------------------------------------------------- +local typeparsers = { + -- full parser + gg.sequence({ + builder = function (result) + raiserror(result) + return { name = result[1].name} + end, + '@','type',typenameparser + }), + -- parser without name + gg.sequence({ + builder = function (result) + raiserror(result) + return {} + end, + '@','type' + }) +} + +-- ---------------------------------------------------- +-- parse a module tag +-- ---------------------------------------------------- +local moduleparsers = { + -- full parser + gg.sequence({ + builder = function (result) + raiserror(result) + return { name = result[1].name } + end, + '@','module', modulenameparser + }), + -- parser without name + gg.sequence({ + builder = function (result) + raiserror(result) + return {} + end, + '@','module' + }) +} + +-- ---------------------------------------------------- +-- parse a third tag +-- ---------------------------------------------------- +local thirdtagsparser = gg.sequence({ + builder = function (result) + raiserror(result) + return { name = result[1][1] } + end, + '@', mlp.id +}) +-- ---------------------------------------------------- +-- init parser +-- ---------------------------------------------------- +local function initparser() + -- register parsers + -- each tag name has several parsers + registeredparsers = { + ["module"] = moduleparsers, + ["return"] = returnparsers, + ["type"] = typeparsers, + ["field"] = fieldparsers, + ["function"] = functionparsers, + ["param"] = paramparsers + } + + -- create lexer used for parsing + lx = lexer.lexer:clone() + lx.extractors = { + -- "extract_long_comment", + -- "extract_short_comment", + -- "extract_long_string", + "extract_short_string", + "extract_word", + "extract_number", + "extract_symbol" + } + + -- Add dots as keyword + local tagnames = { '...' } + + -- Add tag names as key word + for tagname, _ in pairs(registeredparsers) do + table.insert(tagnames,tagname) + end + lx:add(tagnames) + + return lx, parsers +end + +initparser() + +-- ---------------------------------------------------- +-- get the string pattern to remove for each line of description +-- the goal is to fix the indentation problems +-- ---------------------------------------------------- +local function getstringtoremove (stringcomment,commentstart) + local _,_,capture = string.find(stringcomment,"\n?([ \t]*)@[^{]+",commentstart) + if not capture then + _,_,capture = string.find(stringcomment,"^([ \t]*)",commentstart) + end + capture = string.gsub(capture,"(.)","%1?") + return capture +end + +-- ---------------------------------------------------- +-- parse comment tag partition and return table structure +-- ---------------------------------------------------- +local function parsetag(part) + if part.comment:find("^@") then + -- check if the part start by a supported tag + for tagname,parsers in pairs(registeredparsers) do + if (part.comment:find("^@"..tagname)) then + -- try the registered parsers for this tag + local result + for i, parser in ipairs(parsers) do + local valid, tag = pcall(parser, lx:newstream(part.comment, tagname .. 'tag lexer')) + if valid then + -- add tagname + tag.tagname = tagname + + -- add description + local endoffset = tag.lineinfo.last.offset + tag.description = part.comment:sub(endoffset+2,-1) + return tag + end + end + end + end + end + return nil +end + +-- ---------------------------------------------------- +-- Parse third party tags. +-- +-- Enable to parse a tag not defined in language. +-- So for, accepted format is: @sometagname adescription +-- ---------------------------------------------------- +local function parsethirdtag( part ) + + -- Check it there is someting to process + if not part.comment:find("^@") then + return nil, 'No tag to parse' + end + + -- Apply parser + local status, parsedtag = pcall(thirdtagsparser, lx:newstream(part.comment, 'Third party tag lexer')) + if not status then + return nil, "Unable to parse given string." + end + + -- Retrieve description + local endoffset = parsedtag.lineinfo.last.offset + local tag = { + description = part.comment:sub(endoffset+2,-1) + } + return parsedtag.name, tag +end + +-- --------------------------------------------------------- +-- split string comment in several part +-- return list of {comment = string, offset = number} +-- the first part is the part before the first tag +-- the others are the part from a tag to the next one +-- ---------------------------------------------------- +local function split(stringcomment,commentstart) + local partstart = commentstart + local result = {} + + -- manage case where the comment start by @ + -- (we must ignore the inline see tag @{..}) + local at_startoffset, at_endoffset = stringcomment:find("^[ \t]*@[^{]",partstart) + if at_endoffset then + partstart = at_endoffset-1 -- we start before the @ and the non '{' character + end + + -- split comment + -- (we must ignore the inline see tag @{..}) + repeat + at_startoffset, at_endoffset = stringcomment:find("\n[ \t]*@[^{]",partstart) + local partend + if at_startoffset then + partend= at_startoffset-1 -- the end is before the separator pattern (just before the \n) + else + partend = #stringcomment -- we don't find any pattern so the end is the end of the string + end + table.insert(result, { comment = stringcomment:sub (partstart,partend) , + offset = partstart}) + if at_endoffset then + partstart = at_endoffset-1 -- the new start is befire the @ and the non { char + end + until not at_endoffset + return result +end + + +-- ---------------------------------------------------- +-- parse a comment block and return a table +-- ---------------------------------------------------- +function M.parse(stringcomment) + + local _comment = {description="", shortdescription=""} + + -- clean windows carriage return + stringcomment = string.gsub(stringcomment,"\r\n","\n") + + -- check if it's a ld comment + -- get the begin of the comment + -- ============================ + if not stringcomment:find("^-") then + -- if this comment don't start by -, we will not handle it. + return nil + end + + -- retrieve the real start + local commentstart = 2 --after the first hyphen + -- if the first line is an empty comment line with at least 3 hyphens we ignore it + local _ , endoffset = stringcomment:find("^-+[ \t]*\n") + if endoffset then + commentstart = endoffset+1 + end + + -- clean comments + -- =================== + -- remove line of "-" + stringcomment = string.sub(stringcomment,commentstart) + -- clean indentation + local pattern = getstringtoremove (stringcomment,1) + stringcomment = string.gsub(stringcomment,"^"..pattern,"") + stringcomment = string.gsub(stringcomment,"\n"..pattern,"\n") + + -- split comment part + -- ==================== + local commentparts = split(stringcomment, 1) + + -- Extract descriptions + -- ==================== + local firstpart = commentparts[1].comment + if firstpart:find("^[^@]") or firstpart:find("^@{") then + -- if the comment part don't start by @ + -- it's the part which contains descriptions + -- (there are an exception for the in-line see tag @{..}) + local shortdescription, description = string.match(firstpart,'^(.-[.?])(%s.+)') + -- store description + if shortdescription then + _comment.shortdescription = shortdescription + -- clean description + -- remove always the first space character + -- (this manage the case short and long description is on the same line) + description = string.gsub(description, "^[ \t]","") + -- if first line is only an empty string remove it + description = string.gsub(description, "^[ \t]*\n","") + _comment.description = description + else + _comment.shortdescription = firstpart + _comment.description = "" + end + end + + -- Extract tags + -- =================== + -- Parse regular tags + local tag + for i, part in ipairs(commentparts) do + tag = parsetag(part) + --if it's a supported tag (so tag is not nil, it's a table) + if tag then + if not _comment.tags then _comment.tags = {} end + if not _comment.tags[tag.tagname] then + _comment.tags[tag.tagname] = {} + end + table.insert(_comment.tags[tag.tagname], tag) + else + + -- Try user defined tags, so far they will look like + -- @identifier description + local tagname, thirdtag = parsethirdtag( part ) + if tagname then + -- + -- Append found tag + -- + local reservedname = 'unknowntags' + if not _comment.unknowntags then + _comment.unknowntags = {} + end + + -- Create specific section for parsed tag + if not _comment.unknowntags[tagname] then + _comment.unknowntags[tagname] = {} + end + -- Append to specific section + table.insert(_comment.unknowntags[tagname], thirdtag) + end + end + end + return _comment +end + + +function M.parseinlinecomment(stringcomment) + --TODO this code is use to activate typage only on --- comments. (deactivate for now) + -- if not stringcomment or not stringcomment:find("^-") then + -- -- if this comment don't start by -, we will not handle it. + -- return nil + -- end + -- -- remove the first '-' + -- stringcomment = string.sub(stringcomment,2) + -- print (stringcomment) + -- io.flush() + local valid, parsedtag = pcall(typerefparser, lx:newstream(stringcomment, 'typeref parser')) + if valid then + local endoffset = parsedtag.lineinfo.last.offset + parsedtag.description = stringcomment:sub(endoffset+2,-1) + return parsedtag + end +end + +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Date.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Date.lua new file mode 100644 index 000000000..c11a9ef18 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Date.lua @@ -0,0 +1,546 @@ +--- Date and Date Format classes. +-- See the Guide. +-- @class module +-- @name pl.Date +-- @pragma nostrip + +--[[ +module("pl.Date") +]] + +local class = require 'pl.class' +local os_time, os_date = os.time, os.date +local stringx = require 'pl.stringx' + +local Date = class() +Date.Format = class() + +--- Date constructor. +-- @param t this can be either
      +--
    • nil - use current date and time
    • +--
    • number - seconds since epoch (as returned by @{os.time})
    • +--
    • Date - copy constructor
    • +--
    • table - table containing year, month, etc as for os.time() +-- You may leave out year, month or day, in which case current values will be used. +--
    • +--
    • two to six numbers: year, month, day, hour, min, sec +--
    +-- @function Date +function Date:_init(t,...) + local time + if select('#',...) > 0 then + local extra = {...} + local year = t + t = { + year = year, + month = extra[1], + day = extra[2], + hour = extra[3], + min = extra[4], + sec = extra[5] + } + end + if t == nil then + time = os_time() + elseif type(t) == 'number' then + time = t + elseif type(t) == 'table' then + if getmetatable(t) == Date then -- copy ctor + time = t.time + else + if not (t.year and t.month and t.year) then + local lt = os.date('*t') + if not t.year and not t.month and not t.day then + t.year = lt.year + t.month = lt.month + t.day = lt.day + else + t.year = t.year or lt.year + t.month = t.month or (t.day and lt.month or 1) + t.day = t.day or 1 + end + end + time = os_time(t) + end + end + self:set(time) +end + +local thour,tmin + +--- get the time zone offset from UTC. +-- @return hours ahead of UTC +-- @return minutes ahead of UTC +function Date.tzone () + if not thour then + local t = os.time() + local ut = os.date('!*t',t) + local lt = os.date('*t',t) + thour = lt.hour - ut.hour + tmin = lt.min - ut.min + end + return thour, tmin +end + +--- convert this date to UTC. +function Date:toUTC () + local th, tm = Date.tzone() + self:add { hour = -th } + + if tm > 0 then self:add {min = -tm} end +end + +--- convert this UTC date to local. +function Date:toLocal () + local th, tm = Date.tzone() + self:add { hour = th } + if tm > 0 then self:add {min = tm} end +end + +--- set the current time of this Date object. +-- @param t seconds since epoch +function Date:set(t) + self.time = t + self.tab = os_date('*t',self.time) +end + +--- set the year. +-- @param y Four-digit year +-- @class function +-- @name Date:year + +--- set the month. +-- @param m month +-- @class function +-- @name Date:month + +--- set the day. +-- @param d day +-- @class function +-- @name Date:day + +--- set the hour. +-- @param h hour +-- @class function +-- @name Date:hour + +--- set the minutes. +-- @param min minutes +-- @class function +-- @name Date:min + +--- set the seconds. +-- @param sec seconds +-- @class function +-- @name Date:sec + +--- set the day of year. +-- @class function +-- @param yday day of year +-- @name Date:yday + +--- get the year. +-- @param y Four-digit year +-- @class function +-- @name Date:year + +--- get the month. +-- @class function +-- @name Date:month + +--- get the day. +-- @class function +-- @name Date:day + +--- get the hour. +-- @class function +-- @name Date:hour + +--- get the minutes. +-- @class function +-- @name Date:min + +--- get the seconds. +-- @class function +-- @name Date:sec + +--- get the day of year. +-- @class function +-- @name Date:yday + + +for _,c in ipairs{'year','month','day','hour','min','sec','yday'} do + Date[c] = function(self,val) + if val then + self.tab[c] = val + self:set(os_time(self.tab)) + return self + else + return self.tab[c] + end + end +end + +--- name of day of week. +-- @param full abbreviated if true, full otherwise. +-- @return string name +function Date:weekday_name(full) + return os_date(full and '%A' or '%a',self.time) +end + +--- name of month. +-- @param full abbreviated if true, full otherwise. +-- @return string name +function Date:month_name(full) + return os_date(full and '%B' or '%b',self.time) +end + +--- is this day on a weekend?. +function Date:is_weekend() + return self.tab.wday == 0 or self.tab.wday == 6 +end + +--- add to a date object. +-- @param t a table containing one of the following keys and a value:
    +-- year,month,day,hour,min,sec +-- @return this date +function Date:add(t) + local key,val = next(t) + self.tab[key] = self.tab[key] + val + self:set(os_time(self.tab)) + return self +end + +--- last day of the month. +-- @return int day +function Date:last_day() + local d = 28 + local m = self.tab.month + while self.tab.month == m do + d = d + 1 + self:add{day=1} + end + self:add{day=-1} + return self +end + +--- difference between two Date objects. +-- Note: currently the result is a regular @{Date} object, +-- but also has `interval` field set, which means a more +-- appropriate string rep is used. +-- @param other Date object +-- @return a Date object +function Date:diff(other) + local dt = self.time - other.time + if dt < 0 then error("date difference is negative!",2) end + local date = Date(dt) + date.interval = true + return date +end + +--- long numerical ISO data format version of this date. +function Date:__tostring() + if not self.interval then + return os_date('%Y-%m-%d %H:%M:%S',self.time) + else + local t, res = self.tab, '' + local y,m,d = t.year - 1970, t.month - 1, t.day - 1 + if y > 0 then res = res .. y .. ' years ' end + if m > 0 then res = res .. m .. ' months ' end + if d > 0 then res = res .. d .. ' days ' end + if y == 0 and m == 0 then + local h = t.hour - Date.tzone() -- not accounting for UTC mins! + if h > 0 then res = res .. h .. ' hours ' end + if t.min > 0 then res = res .. t.min .. ' min ' end + if t.sec > 0 then res = res .. t.sec .. ' sec ' end + end + return res + end +end + +--- equality between Date objects. +function Date:__eq(other) + return self.time == other.time +end + +--- equality between Date objects. +function Date:__lt(other) + return self.time < other.time +end + + +------------ Date.Format class: parsing and renderinig dates ------------ + +-- short field names, explicit os.date names, and a mask for allowed field repeats +local formats = { + d = {'day',{true,true}}, + y = {'year',{false,true,false,true}}, + m = {'month',{true,true}}, + H = {'hour',{true,true}}, + M = {'min',{true,true}}, + S = {'sec',{true,true}}, +} + +-- + +--- Date.Format constructor. +-- @param fmt. A string where the following fields are significant:
      +--
    • d day (either d or dd)
    • +--
    • y year (either yy or yyy)
    • +--
    • m month (either m or mm)
    • +--
    • H hour (either H or HH)
    • +--
    • M minute (either M or MM)
    • +--
    • S second (either S or SS)
    • +--
    +-- Alternatively, if fmt is nil then this returns a flexible date parser +-- that tries various date/time schemes in turn: +--
      +--
    1. ISO 8601, +-- like 2010-05-10 12:35:23Z or 2008-10-03T14:30+02
    2. +--
    3. times like 15:30 or 8.05pm (assumed to be today's date)
    4. +--
    5. dates like 28/10/02 (European order!) or 5 Feb 2012
    6. +--
    7. month name like march or Mar (case-insensitive, first 3 letters); +-- here the day will be 1 and the year this current year
    8. +--
    +-- A date in format 3 can be optionally followed by a time in format 2. +-- Please see test-date.lua in the tests folder for more examples. +-- @usage df = Date.Format("yyyy-mm-dd HH:MM:SS") +-- @class function +-- @name Date.Format +function Date.Format:_init(fmt) + if not fmt then return end + local append = table.insert + local D,PLUS,OPENP,CLOSEP = '\001','\002','\003','\004' + local vars,used = {},{} + local patt,outf = {},{} + local i = 1 + while i < #fmt do + local ch = fmt:sub(i,i) + local df = formats[ch] + if df then + if used[ch] then error("field appeared twice: "..ch,2) end + used[ch] = true + -- this field may be repeated + local _,inext = fmt:find(ch..'+',i+1) + local cnt = not _ and 1 or inext-i+1 + if not df[2][cnt] then error("wrong number of fields: "..ch,2) end + -- single chars mean 'accept more than one digit' + local p = cnt==1 and (D..PLUS) or (D):rep(cnt) + append(patt,OPENP..p..CLOSEP) + append(vars,ch) + if ch == 'y' then + append(outf,cnt==2 and '%y' or '%Y') + else + append(outf,'%'..ch) + end + i = i + cnt + else + append(patt,ch) + append(outf,ch) + i = i + 1 + end + end + -- escape any magic characters + fmt = table.concat(patt):gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1') + -- replace markers with their magic equivalents + fmt = fmt:gsub(D,'%%d'):gsub(PLUS,'+'):gsub(OPENP,'('):gsub(CLOSEP,')') + self.fmt = fmt + self.outf = table.concat(outf) + self.vars = vars + +end + +local parse_date + +--- parse a string into a Date object. +-- @param str a date string +-- @return date object +function Date.Format:parse(str) + if not self.fmt then + return parse_date(str,self.us) + end + local res = {str:match(self.fmt)} + if #res==0 then return nil, 'cannot parse '..str end + local tab = {} + for i,v in ipairs(self.vars) do + local name = formats[v][1] -- e.g. 'y' becomes 'year' + tab[name] = tonumber(res[i]) + end + -- os.date() requires these fields; if not present, we assume + -- that the time set is for the current day. + if not (tab.year and tab.month and tab.year) then + local today = Date() + tab.year = tab.year or today:year() + tab.month = tab.month or today:month() + tab.day = tab.day or today:month() + end + local Y = tab.year + if Y < 100 then -- classic Y2K pivot + tab.year = Y + (Y < 35 and 2000 or 1999) + elseif not Y then + tab.year = 1970 + end + --dump(tab) + return Date(tab) +end + +--- convert a Date object into a string. +-- @param d a date object, or a time value as returned by @{os.time} +-- @return string +function Date.Format:tostring(d) + local tm = type(d) == 'number' and d or d.time + if self.outf then + return os.date(self.outf,tm) + else + return tostring(Date(d)) + end +end + +function Date.Format:US_order(yesno) + self.us = yesno +end + +local months = {jan=1,feb=2,mar=3,apr=4,may=5,jun=6,jul=7,aug=8,sep=9,oct=10,nov=11,dec=12} + +--[[ +Allowed patterns: +- [day] [monthname] [year] [time] +- [day]/[month][/year] [time] + +]] + + +local is_word = stringx.isalpha +local is_number = stringx.isdigit +local function tonum(s,l1,l2,kind) + kind = kind or '' + local n = tonumber(s) + if not n then error(("%snot a number: '%s'"):format(kind,s)) end + if n < l1 or n > l2 then + error(("%s out of range: %s is not between %d and %d"):format(kind,s,l1,l2)) + end + return n +end + +local function parse_iso_end(p,ns,sec) + -- may be fractional part of seconds + local _,nfrac,secfrac = p:find('^%.%d+',ns+1) + if secfrac then + sec = sec .. secfrac + p = p:sub(nfrac+1) + else + p = p:sub(ns+1) + end + -- ISO 8601 dates may end in Z (for UTC) or [+-][isotime] + if p:match 'z$' then return sec, {h=0,m=0} end -- we're UTC! + p = p:gsub(':','') -- turn 00:30 to 0030 + local _,_,sign,offs = p:find('^([%+%-])(%d+)') + if not sign then return sec, nil end -- not UTC + + if #offs == 2 then offs = offs .. '00' end -- 01 to 0100 + local tz = { h = tonumber(offs:sub(1,2)), m = tonumber(offs:sub(3,4)) } + if sign == '-' then tz.h = -tz.h; tz.m = -tz.m end + return sec, tz +end + +local function parse_date_unsafe (s,US) + s = s:gsub('T',' ') -- ISO 8601 + local parts = stringx.split(s:lower()) + local i,p = 1,parts[1] + local function nextp() i = i + 1; p = parts[i] end + local year,min,hour,sec,apm + local tz + local _,nxt,day, month = p:find '^(%d+)/(%d+)' + if day then + -- swop for US case + if US then + day, month = month, day + end + _,_,year = p:find('^/(%d+)',nxt+1) + nextp() + else -- ISO + year,month,day = p:match('^(%d+)%-(%d+)%-(%d+)') + if year then + nextp() + end + end + if p and not year and is_number(p) then -- has to be date + day = p + nextp() + end + if p and is_word(p) then + p = p:sub(1,3) + local mon = months[p] + if mon then + month = mon + else error("not a month: " .. p) end + nextp() + end + if p and not year and is_number(p) then + year = p + nextp() + end + + if p then -- time is hh:mm[:ss], hhmm[ss] or H.M[am|pm] + _,nxt,hour,min = p:find '^(%d+):(%d+)' + local ns + if nxt then -- are there seconds? + _,ns,sec = p:find ('^:(%d+)',nxt+1) + --if ns then + sec,tz = parse_iso_end(p,ns or nxt,sec) + --end + else -- might be h.m + _,ns,hour,min = p:find '^(%d+)%.(%d+)' + if ns then + apm = p:match '[ap]m$' + else -- or hhmm[ss] + local hourmin + _,nxt,hourmin = p:find ('^(%d+)') + if nxt then + hour = hourmin:sub(1,2) + min = hourmin:sub(3,4) + sec = hourmin:sub(5,6) + if #sec == 0 then sec = nil end + sec,tz = parse_iso_end(p,nxt,sec) + end + end + end + end + local today + if not (year and month and day) then + today = Date() + end + day = day and tonum(day,1,31,'day') or (month and 1 or today:day()) + month = month and tonum(month,1,12,'month') or today:month() + year = year and tonumber(year) or today:year() + if year < 100 then -- two-digit year pivot + year = year + (year < 35 and 2000 or 1900) + end + hour = hour and tonum(hour,1,apm and 12 or 24,'hour') or 12 + if apm == 'pm' then + hour = hour + 12 + end + min = min and tonum(min,1,60) or 0 + sec = sec and tonum(sec,1,60) or 0 + local res = Date {year = year, month = month, day = day, hour = hour, min = min, sec = sec} + if tz then -- ISO 8601 UTC time + res:toUTC() + res:add {hour = tz.h} + if tz.m ~= 0 then res:add {min = tz.m} end + end + return res +end + +function parse_date (s) + local ok, d = pcall(parse_date_unsafe,s) + if not ok then -- error + d = d:gsub('.-:%d+: ','') + return nil, d + else + return d + end +end + + +return Date + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/List.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/List.lua new file mode 100644 index 000000000..776f4d938 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/List.lua @@ -0,0 +1,553 @@ +--- Python-style list class.

    +-- Based on original code by Nick Trout. +--

    +-- Please Note: methods that change the list will return the list. +-- This is to allow for method chaining, but please note that ls = ls:sort() +-- does not mean that a new copy of the list is made. In-place (mutable) methods +-- are marked as returning 'the list' in this documentation. +--

    +-- See the Guide for further discussion +--

    +-- See http://www.python.org/doc/current/tut/tut.html, section 5.1 +--

    +-- Note: The comments before some of the functions are from the Python docs +-- and contain Python code. +--

    +-- Written for Lua version 4.0
    +-- Redone for Lua 5.1, Steve Donovan. +-- @class module +-- @name pl.List +-- @pragma nostrip + +local tinsert,tremove,concat,tsort = table.insert,table.remove,table.concat,table.sort +local setmetatable, getmetatable,type,tostring,assert,string,next = setmetatable,getmetatable,type,tostring,assert,string,next +local write = io.write +local tablex = require 'pl.tablex' +local filter,imap,imap2,reduce,transform,tremovevalues = tablex.filter,tablex.imap,tablex.imap2,tablex.reduce,tablex.transform,tablex.removevalues +local tablex = tablex +local tsub = tablex.sub +local utils = require 'pl.utils' +local function_arg = utils.function_arg +local is_type = utils.is_type +local split = utils.split +local assert_arg = utils.assert_arg +local normalize_slice = tablex._normalize_slice + +--[[ +module ('pl.List',utils._module) +]] + +local Multimap = utils.stdmt.MultiMap +-- metatable for our list objects +local List = utils.stdmt.List +List.__index = List +List._class = List + +local iter + +-- we give the metatable its own metatable so that we can call it like a function! +setmetatable(List,{ + __call = function (tbl,arg) + return List.new(arg) + end, +}) + +local function makelist (t,obj) + local klass = List + if obj then + klass = getmetatable(obj) + end + return setmetatable(t,klass) +end + +local function is_list(t) + return getmetatable(t) == List +end + +local function simple_table(t) + return type(t) == 'table' and not is_list(t) and #t > 0 +end + +function List:_init (src) + if src then + for v in iter(src) do + tinsert(self,v) + end + end +end + +--- Create a new list. Can optionally pass a table; +-- passing another instance of List will cause a copy to be created +-- we pass anything which isn't a simple table to iterate() to work out +-- an appropriate iterator @see List.iterate +-- @param t An optional list-like table +-- @return a new List +-- @usage ls = List(); ls = List {1,2,3,4} +function List.new(t) + local ls + if not simple_table(t) then + ls = {} + List._init(ls,t) + else + ls = t + end + makelist(ls) + return ls +end + +function List:clone() + local ls = makelist({},self) + List._init(ls,self) + return ls +end + +function List.default_map_with(T) + return function(self,name) + local f = T[name] + if f then + return function(self,...) + return self:map(f,...) + end + else + error("method not found: "..name,2) + end + end +end + + +---Add an item to the end of the list. +-- @param i An item +-- @return the list +function List:append(i) + tinsert(self,i) + return self +end + +List.push = tinsert + +--- Extend the list by appending all the items in the given list. +-- equivalent to 'a[len(a):] = L'. +-- @param L Another List +-- @return the list +function List:extend(L) + assert_arg(1,L,'table') + for i = 1,#L do tinsert(self,L[i]) end + return self +end + +--- Insert an item at a given position. i is the index of the +-- element before which to insert. +-- @param i index of element before whichh to insert +-- @param x A data item +-- @return the list +function List:insert(i, x) + assert_arg(1,i,'number') + tinsert(self,i,x) + return self +end + +--- Insert an item at the begining of the list. +-- @param x a data item +-- @return the list +function List:put (x) + return self:insert(1,x) +end + +--- Remove an element given its index. +-- (equivalent of Python's del s[i]) +-- @param i the index +-- @return the list +function List:remove (i) + assert_arg(1,i,'number') + tremove(self,i) + return self +end + +--- Remove the first item from the list whose value is given. +-- (This is called 'remove' in Python; renamed to avoid confusion +-- with table.remove) +-- Return nil if there is no such item. +-- @param x A data value +-- @return the list +function List:remove_value(x) + for i=1,#self do + if self[i]==x then tremove(self,i) return self end + end + return self + end + +--- Remove the item at the given position in the list, and return it. +-- If no index is specified, a:pop() returns the last item in the list. +-- The item is also removed from the list. +-- @param i An index +-- @return the item +function List:pop(i) + if not i then i = #self end + assert_arg(1,i,'number') + return tremove(self,i) +end + +List.get = List.pop + +--- Return the index in the list of the first item whose value is given. +-- Return nil if there is no such item. +-- @class function +-- @name List:index +-- @param x A data value +-- @param idx where to start search (default 1) +-- @return the index, or nil if not found. + +local tfind = tablex.find +List.index = tfind + +--- does this list contain the value?. +-- @param x A data value +-- @return true or false +function List:contains(x) + return tfind(self,x) and true or false +end + +--- Return the number of times value appears in the list. +-- @param x A data value +-- @return number of times x appears +function List:count(x) + local cnt=0 + for i=1,#self do + if self[i]==x then cnt=cnt+1 end + end + return cnt +end + +--- Sort the items of the list, in place. +-- @param cmp an optional comparison function; '<' is used if not given. +-- @return the list +function List:sort(cmp) + tsort(self,cmp) + return self +end + +--- Reverse the elements of the list, in place. +-- @return the list +function List:reverse() + local t = self + local n = #t + local n2 = n/2 + for i = 1,n2 do + local k = n-i+1 + t[i],t[k] = t[k],t[i] + end + return self +end + +--- Emulate list slicing. like 'list[first:last]' in Python. +-- If first or last are negative then they are relative to the end of the list +-- eg. slice(-2) gives last 2 entries in a list, and +-- slice(-4,-2) gives from -4th to -2nd +-- @param first An index +-- @param last An index +-- @return a new List +function List:slice(first,last) + return tsub(self,first,last) +end + +--- empty the list. +-- @return the list +function List:clear() + for i=1,#self do tremove(self,i) end + return self +end + +local eps = 1.0e-10 + +--- Emulate Python's range(x) function. +-- Include it in List table for tidiness +-- @param start A number +-- @param finish A number greater than start; if zero, then 0..start-1 +-- @param incr an optional increment (may be less than 1) +-- @usage List.range(0,3) == List {0,1,2,3} +function List.range(start,finish,incr) + if not finish then + start = 0 + finish = finish - 1 + end + if incr then + if not utils.is_integer(incr) then finish = finish + eps end + else + incr = 1 + end + assert_arg(1,start,'number') + assert_arg(2,finish,'number') + local t = List.new() + for i=start,finish,incr do tinsert(t,i) end + return t +end + +--- list:len() is the same as #list. +function List:len() + return #self +end + +-- Extended operations -- + +--- Remove a subrange of elements. +-- equivalent to 'del s[i1:i2]' in Python. +-- @param i1 start of range +-- @param i2 end of range +-- @return the list +function List:chop(i1,i2) + return tremovevalues(self,i1,i2) +end + +--- Insert a sublist into a list +-- equivalent to 's[idx:idx] = list' in Python +-- @param idx index +-- @param list list to insert +-- @return the list +-- @usage l = List{10,20}; l:splice(2,{21,22}); assert(l == List{10,21,22,20}) +function List:splice(idx,list) + assert_arg(1,idx,'number') + idx = idx - 1 + local i = 1 + for v in iter(list) do + tinsert(self,i+idx,v) + i = i + 1 + end + return self +end + +--- general slice assignment s[i1:i2] = seq. +-- @param i1 start index +-- @param i2 end index +-- @param seq a list +-- @return the list +function List:slice_assign(i1,i2,seq) + assert_arg(1,i1,'number') + assert_arg(1,i2,'number') + i1,i2 = normalize_slice(self,i1,i2) + if i2 >= i1 then self:chop(i1,i2) end + self:splice(i1,seq) + return self +end + +--- concatenation operator. +-- @param L another List +-- @return a new list consisting of the list with the elements of the new list appended +function List:__concat(L) + assert_arg(1,L,'table') + local ls = self:clone() + ls:extend(L) + return ls +end + +--- equality operator ==. True iff all elements of two lists are equal. +-- @param L another List +-- @return true or false +function List:__eq(L) + if #self ~= #L then return false end + for i = 1,#self do + if self[i] ~= L[i] then return false end + end + return true +end + +--- join the elements of a list using a delimiter.
    +-- This method uses tostring on all elements. +-- @param delim a delimiter string, can be empty. +-- @return a string +function List:join (delim) + delim = delim or '' + assert_arg(1,delim,'string') + return concat(imap(tostring,self),delim) +end + +--- join a list of strings.
    +-- Uses table.concat directly. +-- @class function +-- @name List:concat +-- @param delim a delimiter +-- @return a string +List.concat = concat + +local function tostring_q(val) + local s = tostring(val) + if type(val) == 'string' then + s = '"'..s..'"' + end + return s +end + +--- how our list should be rendered as a string. Uses join(). +-- @see List:join +function List:__tostring() + return '{'..self:join(',',tostring_q)..'}' +end + +--[[ +-- NOTE: this works, but is unreliable. If you leave the loop before finishing, +-- then the iterator is not reset. +--- can iterate over a list directly. +-- @usage for v in ls do print(v) end +function List:__call() + if not self.key then self.key = 1 end + local value = self[self.key] + self.key = self.key + 1 + if not value then self.key = nil end + return value +end +--]] + +--[[ +function List.__call(t,v,i) + i = (i or 0) + 1 + v = t[i] + if v then return i, v end +end +--]] + +--- call the function for each element of the list. +-- @param fun a function or callable object +-- @param ... optional values to pass to function +function List:foreach (fun,...) + local t = self + fun = function_arg(1,fun) + for i = 1,#t do + fun(t[i],...) + end +end + +--- create a list of all elements which match a function. +-- @param fun a boolean function +-- @param arg optional argument to be passed as second argument of the predicate +-- @return a new filtered list. +function List:filter (fun,arg) + return makelist(filter(self,fun,arg),self) +end + +--- split a string using a delimiter. +-- @param s the string +-- @param delim the delimiter (default spaces) +-- @return a List of strings +-- @see pl.utils.split +function List.split (s,delim) + assert_arg(1,s,'string') + return makelist(split(s,delim)) +end + +--- apply a function to all elements. +-- Any extra arguments will be passed to the function +-- @param fun a function of at least one argument +-- @param ... arbitrary extra arguments. +-- @return a new list: {f(x) for x in self} +-- @see pl.tablex.imap +function List:map (fun,...) + return makelist(imap(fun,self,...),self) +end + +--- apply a function to all elements, in-place. +-- Any extra arguments are passed to the function. +-- @param fun A function that takes at least one argument +-- @param ... arbitrary extra arguments. +function List:transform (fun,...) + transform(fun,self,...) +end + +--- apply a function to elements of two lists. +-- Any extra arguments will be passed to the function +-- @param fun a function of at least two arguments +-- @param ls another list +-- @param ... arbitrary extra arguments. +-- @return a new list: {f(x,y) for x in self, for x in arg1} +-- @see pl.tablex.imap2 +function List:map2 (fun,ls,...) + return makelist(imap2(fun,self,ls,...),self) +end + +--- apply a named method to all elements. +-- Any extra arguments will be passed to the method. +-- @param name name of method +-- @param ... extra arguments +-- @return a new list of the results +-- @see pl.seq.mapmethod +function List:mapm (name,...) + local res = {} + local t = self + for i = 1,#t do + local val = t[i] + local fn = val[name] + if not fn then error(type(val).." does not have method "..name,2) end + res[i] = fn(val,...) + end + return makelist(res,self) +end + +--- 'reduce' a list using a binary function. +-- @param fun a function of two arguments +-- @return result of the function +-- @see pl.tablex.reduce +function List:reduce (fun) + return reduce(fun,self) +end + +--- partition a list using a classifier function. +-- The function may return nil, but this will be converted to the string key ''. +-- @param fun a function of at least one argument +-- @param ... will also be passed to the function +-- @return a table where the keys are the returned values, and the values are Lists +-- of values where the function returned that key. It is given the type of Multimap. +-- @see pl.MultiMap +function List:partition (fun,...) + fun = function_arg(1,fun) + local res = {} + for i = 1,#self do + local val = self[i] + local klass = fun(val,...) + if klass == nil then klass = '' end + if not res[klass] then res[klass] = List() end + res[klass]:append(val) + end + return setmetatable(res,Multimap) +end + +--- return an iterator over all values. +function List:iter () + return iter(self) +end + +--- Create an iterator over a seqence. +-- This captures the Python concept of 'sequence'. +-- For tables, iterates over all values with integer indices. +-- @param seq a sequence; a string (over characters), a table, a file object (over lines) or an iterator function +-- @usage for x in iterate {1,10,22,55} do io.write(x,',') end ==> 1,10,22,55 +-- @usage for ch in iterate 'help' do do io.write(ch,' ') end ==> h e l p +function List.iterate(seq) + if type(seq) == 'string' then + local idx = 0 + local n = #seq + local sub = string.sub + return function () + idx = idx + 1 + if idx > n then return nil + else + return sub(seq,idx,idx) + end + end + elseif type(seq) == 'table' then + local idx = 0 + local n = #seq + return function() + idx = idx + 1 + if idx > n then return nil + else + return seq[idx] + end + end + elseif type(seq) == 'function' then + return seq + elseif type(seq) == 'userdata' and io.type(seq) == 'file' then + return seq:lines() + end +end +iter = List.iterate + +return List + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Map.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Map.lua new file mode 100644 index 000000000..8b7284037 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Map.lua @@ -0,0 +1,108 @@ +--- A Map class. +-- @class module +-- @name pl.Map + +--[[ +module ('pl.Map') +]] +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local stdmt = utils.stdmt +local is_callable = utils.is_callable +local tmakeset,deepcompare,merge,keys,difference,tupdate = tablex.makeset,tablex.deepcompare,tablex.merge,tablex.keys,tablex.difference,tablex.update + +local pretty_write = require 'pl.pretty' . write +local Map = stdmt.Map +local Set = stdmt.Set +local List = stdmt.List + +local class = require 'pl.class' + +-- the Map class --------------------- +class(nil,nil,Map) + +local function makemap (m) + return setmetatable(m,Map) +end + +function Map:_init (t) + local mt = getmetatable(t) + if mt == Set or mt == Map then + self:update(t) + else + return t -- otherwise assumed to be a map-like table + end +end + + +local function makelist(t) + return setmetatable(t,List) +end + +--- list of keys. +Map.keys = tablex.keys + +--- list of values. +Map.values = tablex.values + +--- return an iterator over all key-value pairs. +function Map:iter () + return pairs(self) +end + +--- return a List of all key-value pairs, sorted by the keys. +function Map:items() + local ls = makelist(tablex.pairmap (function (k,v) return makelist {k,v} end, self)) + ls:sort(function(t1,t2) return t1[1] < t2[1] end) + return ls +end + +-- Will return the existing value, or if it doesn't exist it will set +-- a default value and return it. +function Map:setdefault(key, defaultval) + return self[key] or self:set(key,defaultval) or defaultval +end + +--- size of map. +-- note: this is a relatively expensive operation! +-- @class function +-- @name Map:len +Map.len = tablex.size + +--- put a value into the map. +-- @param key the key +-- @param val the value +function Map:set (key,val) + self[key] = val +end + +--- get a value from the map. +-- @param key the key +-- @return the value, or nil if not found. +function Map:get (key) + return rawget(self,key) +end + +local index_by = tablex.index_by + +-- get a list of values indexed by a list of keys. +-- @param keys a list-like table of keys +-- @return a new list +function Map:getvalues (keys) + return makelist(index_by(self,keys)) +end + +Map.iter = pairs + +Map.update = tablex.update + +function Map:__eq (m) + -- note we explicitly ask deepcompare _not_ to use __eq! + return deepcompare(self,m,true) +end + +function Map:__tostring () + return pretty_write(self,'') +end + +return Map diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/MultiMap.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/MultiMap.lua new file mode 100644 index 000000000..f1c430db5 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/MultiMap.lua @@ -0,0 +1,65 @@ +--- MultiMap, a Map which has multiple values per key.
    +-- @class module +-- @name pl.MultiMap + +--[[ +module ('pl.MultiMap') +]] + +local classes = require 'pl.class' +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local List = require 'pl.List' + +local index_by,tsort,concat = tablex.index_by,table.sort,table.concat +local append,extend,slice = List.append,List.extend,List.slice +local append = table.insert +local is_type = utils.is_type + +local class = require 'pl.class' +local Map = require 'pl.Map' + +-- MultiMap is a standard MT +local MultiMap = utils.stdmt.MultiMap + +class(Map,nil,MultiMap) +MultiMap._name = 'MultiMap' + +function MultiMap:_init (t) + if not t then return end + self:update(t) +end + +--- update a MultiMap using a table. +-- @param t either a Multimap or a map-like table. +-- @return the map +function MultiMap:update (t) + utils.assert_arg(1,t,'table') + if Map:class_of(t) then + for k,v in pairs(t) do + self[k] = List() + self[k]:append(v) + end + else + for k,v in pairs(t) do + self[k] = List(v) + end + end +end + +--- add a new value to a key. Setting a nil value removes the key. +-- @param key the key +-- @param val the value +-- @return the map +function MultiMap:set (key,val) + if val == nil then + self[key] = nil + else + if not self[key] then + self[key] = List() + end + self[key]:append(val) + end +end + +return MultiMap diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/OrderedMap.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/OrderedMap.lua new file mode 100644 index 000000000..7cc79635a --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/OrderedMap.lua @@ -0,0 +1,150 @@ +--- OrderedMap, a pl.Map which preserves ordering. +-- @class module +-- @name pl.OrderedMap + +--[[ +module ('pl.OrderedMap') +]] + +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local List = require 'pl.List' +local index_by,tsort,concat = tablex.index_by,table.sort,table.concat + +local class = require 'pl.class' +local Map = require 'pl.Map' + +local OrderedMap = class(Map) +OrderedMap._name = 'OrderedMap' + +--- construct an OrderedMap. +-- Will throw an error if the argument is bad. +-- @param t optional initialization table, same as for @{OrderedMap:update} +function OrderedMap:_init (t) + self._keys = List() + if t then + local map,err = self:update(t) + if not map then error(err,2) end + end +end + +local assert_arg,raise = utils.assert_arg,utils.raise + +--- update an OrderedMap using a table.
    +-- If the table is itself an OrderedMap, then its entries will be appended.
    +-- if it s a table of the form {{key1=val1},{key2=val2},...} these will be appended.
    +-- Otherwise, it is assumed to be a map-like table, and order of extra entries is arbitrary. +-- @param t a table. +-- @return the map, or nil in case of error +-- @return the error message +function OrderedMap:update (t) + assert_arg(1,t,'table') + if OrderedMap:class_of(t) then + for k,v in t:iter() do + self:set(k,v) + end + elseif #t > 0 then -- an array must contain {key=val} tables + if type(t[1]) == 'table' then + for _,pair in ipairs(t) do + local key,value = next(pair) + if not key then return raise 'empty pair initialization table' end + self:set(key,value) + end + else + return raise 'cannot use an array to initialize an OrderedMap' + end + else + for k,v in pairs(t) do + self:set(k,v) + end + end + return self +end + +--- set the key's value. This key will be appended at the end of the map.
    +-- If the value is nil, then the key is removed. +-- @param key the key +-- @param val the value +-- @return the map +function OrderedMap:set (key,val) + if not self[key] and val ~= nil then -- ensure that keys are unique + self._keys:append(key) + elseif val == nil then -- removing a key-value pair + self._keys:remove_value(key) + end + self[key] = val + return self +end + +--- insert a key/value pair before a given position. +-- Note: if the map already contains the key, then this effectively +-- moves the item to the new position by first removing at the old position. +-- Has no effect if the key does not exist and val is nil +-- @param pos a position starting at 1 +-- @param key the key +-- @param val the value; if nil use the old value +function OrderedMap:insert (pos,key,val) + local oldval = self[key] + val = val or oldval + if oldval then + self._keys:remove_value(key) + end + if val then + self._keys:insert(pos,key) + self[key] = val + end + return self +end + +--- return the keys in order. +-- (Not a copy!) +-- @return List +function OrderedMap:keys () + return self._keys +end + +--- return the values in order. +-- this is relatively expensive. +-- @return List +function OrderedMap:values () + return List(index_by(self,self._keys)) +end + +--- sort the keys. +-- @param cmp a comparison function as for @{table.sort} +-- @return the map +function OrderedMap:sort (cmp) + tsort(self._keys,cmp) + return self +end + +--- iterate over key-value pairs in order. +function OrderedMap:iter () + local i = 0 + local keys = self._keys + local n,idx = #keys + return function() + i = i + 1 + if i > #keys then return nil end + idx = keys[i] + return idx,self[idx] + end +end + +function OrderedMap:__tostring () + local res = {} + for i,v in ipairs(self._keys) do + local val = self[v] + local vs = tostring(val) + if type(val) ~= 'number' then + vs = '"'..vs..'"' + end + res[i] = tostring(v)..'='..vs + end + return '{'..concat(res,',')..'}' +end + +return OrderedMap + + + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Set.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Set.lua new file mode 100644 index 000000000..fd6de90ba --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Set.lua @@ -0,0 +1,127 @@ +---- A Set class. +-- @class module +-- @name pl.Set + +--[[ +module ('pl.Set') +]] +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local stdmt = utils.stdmt +local tmakeset,deepcompare,merge,keys,difference,tupdate = tablex.makeset,tablex.deepcompare,tablex.merge,tablex.keys,tablex.difference,tablex.update +local Map = stdmt.Map +local Set = stdmt.Set +local List = stdmt.List +local class = require 'pl.class' + +-- the Set class -------------------- +class(Map,nil,Set) + +local function makeset (t) + return setmetatable(t,Set) +end + +--- create a set.
    +-- @param t may be a Set, Map or list-like table. +-- @class function +-- @name Set +function Set:_init (t) + local mt = getmetatable(t) + if mt == Set or mt == Map then + for k in pairs(t) do self[k] = true end + else + for _,v in ipairs(t) do self[v] = true end + end +end + +function Set:__tostring () + return '['..self:keys():join ','..']' +end + +--- add a value to a set. +-- @param key a value +function Set:set (key) + self[key] = true +end + +--- remove a value from a set. +-- @param key a value +function Set:unset (key) + self[key] = nil +end + +--- get a list of the values in a set. +-- @class function +-- @name Set:values +Set.values = Map.keys + +--- map a function over the values of a set. +-- @param fn a function +-- @param ... extra arguments to pass to the function. +-- @return a new set +function Set:map (fn,...) + fn = utils.function_arg(1,fn) + local res = {} + for k in pairs(self) do + res[fn(k,...)] = true + end + return makeset(res) +end + +--- union of two sets (also +). +-- @param set another set +-- @return a new set +function Set:union (set) + return merge(self,set,true) +end +Set.__add = Set.union + +--- intersection of two sets (also *). +-- @param set another set +-- @return a new set +function Set:intersection (set) + return merge(self,set,false) +end +Set.__mul = Set.intersection + +--- new set with elements in the set that are not in the other (also -). +-- @param set another set +-- @return a new set +function Set:difference (set) + return difference(self,set,false) +end +Set.__sub = Set.difference + +-- a new set with elements in _either_ the set _or_ other but not both (also ^). +-- @param set another set +-- @return a new set +function Set:symmetric_difference (set) + return difference(self,set,true) +end +Set.__pow = Set.symmetric_difference + +--- is the first set a subset of the second?. +-- @return true or false +function Set:issubset (set) + for k in pairs(self) do + if not set[k] then return false end + end + return true +end +Set.__lt = Set.subset + +--- is the set empty?. +-- @return true or false +function Set:issempty () + return next(self) == nil +end + +--- are the sets disjoint? (no elements in common). +-- Uses naive definition, i.e. that intersection is empty +-- @param set another set +-- @return true or false +function Set:isdisjoint (set) + return self:intersection(set):isempty() +end + +return Set diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/app.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/app.lua new file mode 100644 index 000000000..845255094 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/app.lua @@ -0,0 +1,143 @@ +--- Application support functions. +--

    See the Guide +-- @class module +-- @name pl.app + +local io,package,require = _G.io, _G.package, _G.require +local utils = require 'pl.utils' +local path = require 'pl.path' +local lfs = require 'lfs' + + +local app = {} + +local function check_script_name () + if _G.arg == nil then error('no command line args available\nWas this run from a main script?') end + return _G.arg[0] +end + +--- add the current script's path to the Lua module path. +-- Applies to both the source and the binary module paths. It makes it easy for +-- the main file of a multi-file program to access its modules in the same directory. +-- `base` allows these modules to be put in a specified subdirectory, to allow for +-- cleaner deployment and resolve potential conflicts between a script name and its +-- library directory. +-- @param base optional base directory. +-- @return the current script's path with a trailing slash +function app.require_here (base) + local p = path.dirname(check_script_name()) + if not path.isabs(p) then + p = path.join(lfs.currentdir(),p) + end + if p:sub(-1,-1) ~= path.sep then + p = p..path.sep + end + if base then + p = p..base..path.sep + end + local so_ext = path.is_windows and 'dll' or 'so' + local lsep = package.path:find '^;' and '' or ';' + local csep = package.cpath:find '^;' and '' or ';' + package.path = ('%s?.lua;%s?%sinit.lua%s%s'):format(p,p,path.sep,lsep,package.path) + package.cpath = ('%s?.%s%s%s'):format(p,so_ext,csep,package.cpath) + return p +end + +--- return a suitable path for files private to this application. +-- These will look like '~/.SNAME/file', with '~' as with expanduser and +-- SNAME is the name of the script without .lua extension. +-- @param file a filename (w/out path) +-- @return a full pathname, or nil +-- @return 'cannot create' error +function app.appfile (file) + local sname = path.basename(check_script_name()) + local name,ext = path.splitext(sname) + local dir = path.join(path.expanduser('~'),'.'..name) + if not path.isdir(dir) then + local ret = lfs.mkdir(dir) + if not ret then return utils.raise ('cannot create '..dir) end + end + return path.join(dir,file) +end + +--- return string indicating operating system. +-- @return 'Windows','OSX' or whatever uname returns (e.g. 'Linux') +function app.platform() + if path.is_windows then + return 'Windows' + else + local f = io.popen('uname') + local res = f:read() + if res == 'Darwin' then res = 'OSX' end + f:close() + return res + end +end + +--- parse command-line arguments into flags and parameters. +-- Understands GNU-style command-line flags; short (-f) and long (--flag). +-- These may be given a value with either '=' or ':' (-k:2,--alpha=3.2,-n2); +-- note that a number value can be given without a space. +-- Multiple short args can be combined like so: (-abcd). +-- @param args an array of strings (default is the global 'arg') +-- @param flags_with_values any flags that take values, e.g. {out=true} +-- @return a table of flags (flag=value pairs) +-- @return an array of parameters +-- @raise if args is nil, then the global `args` must be available! +function app.parse_args (args,flags_with_values) + if not args then + args = _G.arg + if not args then error "Not in a main program: 'arg' not found" end + end + flags_with_values = flags_with_values or {} + local _args = {} + local flags = {} + local i = 1 + while i <= #args do + local a = args[i] + local v = a:match('^-(.+)') + local is_long + if v then -- we have a flag + if v:find '^-' then + is_long = true + v = v:sub(2) + end + if flags_with_values[v] then + if i == #_args or args[i+1]:find '^-' then + return utils.raise ("no value for '"..v.."'") + end + flags[v] = args[i+1] + i = i + 1 + else + -- a value can be indicated with = or : + local var,val = utils.splitv (v,'[=:]') + var = var or v + val = val or true + if not is_long then + if #var > 1 then + if var:find '.%d+' then -- short flag, number value + val = var:sub(2) + var = var:sub(1,1) + else -- multiple short flags + for i = 1,#var do + flags[var:sub(i,i)] = true + end + val = nil -- prevents use of var as a flag below + end + else -- single short flag (can have value, defaults to true) + val = val or true + end + end + if val then + flags[var] = val + end + end + else + _args[#_args+1] = a + end + i = i + 1 + end + return flags,_args +end + +return app diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/array2d.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/array2d.lua new file mode 100644 index 000000000..e178df975 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/array2d.lua @@ -0,0 +1,391 @@ +--- Operations on two-dimensional arrays. +-- @class module +-- @name pl.array2d + +local require, type,tonumber,assert,tostring,io,ipairs,string,table = + _G.require, _G.type,_G.tonumber,_G.assert,_G.tostring,_G.io,_G.ipairs,_G.string,_G.table +local ops = require 'pl.operator' +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' + +local imap,tmap,reduce,keys,tmap2,tset,index_by = tablex.imap,tablex.map,tablex.reduce,tablex.keys,tablex.map2,tablex.set,tablex.index_by +local remove = table.remove +local perm = require 'pl.permute' +local splitv,fprintf,assert_arg = utils.splitv,utils.fprintf,utils.assert_arg +local byte = string.byte +local stdout = io.stdout + +--[[ +module ('pl.array2d',utils._module) +]] + +local array2d = {} + +--- extract a column from the 2D array. +-- @param a 2d array +-- @param key an index or key +-- @return 1d array +function array2d.column (a,key) + assert_arg(1,a,'table') + return imap(ops.index,a,key) +end +local column = array2d.column + +--- map a function over a 2D array +-- @param f a function of at least one argument +-- @param a 2d array +-- @param arg an optional extra argument to be passed to the function. +-- @return 2d array +function array2d.map (f,a,arg) + assert_arg(1,a,'table') + f = utils.function_arg(1,f) + return imap(function(row) return imap(f,row,arg) end, a) +end + +--- reduce the rows using a function. +-- @param f a binary function +-- @param a 2d array +-- @return 1d array +-- @see pl.tablex.reduce +function array2d.reduce_rows (f,a) + assert_arg(1,a,'table') + return tmap(function(row) return reduce(f,row) end, a) +end + + + +--- reduce the columns using a function. +-- @param f a binary function +-- @param a 2d array +-- @return 1d array +-- @see pl.tablex.reduce +function array2d.reduce_cols (f,a) + assert_arg(1,a,'table') + return tmap(function(c) return reduce(f,column(a,c)) end, keys(a[1])) +end + +--- reduce a 2D array into a scalar, using two operations. +-- @param opc operation to reduce the final result +-- @param opr operation to reduce the rows +-- @param a 2D array +function array2d.reduce2 (opc,opr,a) + assert_arg(3,a,'table') + local tmp = array2d.reduce_rows(opr,a) + return reduce(opc,tmp) +end + +local function dimension (t) + return type(t[1])=='table' and 2 or 1 +end + +--- map a function over two arrays. +-- They can be both or either 2D arrays +-- @param f function of at least two arguments +-- @param ad order of first array +-- @param bd order of second array +-- @param a 1d or 2d array +-- @param b 1d or 2d array +-- @param arg optional extra argument to pass to function +-- @return 2D array, unless both arrays are 1D +function array2d.map2 (f,ad,bd,a,b,arg) + assert_arg(1,a,'table') + assert_arg(2,b,'table') + f = utils.function_arg(1,f) + --local ad,bd = dimension(a),dimension(b) + if ad == 1 and bd == 2 then + return imap(function(row) + return tmap2(f,a,row,arg) + end, b) + elseif ad == 2 and bd == 1 then + return imap(function(row) + return tmap2(f,row,b,arg) + end, a) + elseif ad == 1 and bd == 1 then + return tmap2(f,a,b) + elseif ad == 2 and bd == 2 then + return tmap2(function(rowa,rowb) + return tmap2(f,rowa,rowb,arg) + end, a,b) + end +end + +--- cartesian product of two 1d arrays. +-- @param f a function of 2 arguments +-- @param t1 a 1d table +-- @param t2 a 1d table +-- @return 2d table +-- @usage product('..',{1,2},{'a','b'}) == {{'1a','2a'},{'1b','2b'}} +function array2d.product (f,t1,t2) + f = utils.function_arg(1,f) + assert_arg(2,t1,'table') + assert_arg(3,t2,'table') + local res, map = {}, tablex.map + for i,v in ipairs(t2) do + res[i] = map(f,t1,v) + end + return res +end + +--- flatten a 2D array. +-- (this goes over columns first.) +-- @param t 2d table +-- @return a 1d table +-- @usage flatten {{1,2},{3,4},{5,6}} == {1,2,3,4,5,6} +function array2d.flatten (t) + local res = {} + local k = 1 + for _,a in ipairs(t) do -- for all rows + for i = 1,#a do + res[k] = a[i] + k = k + 1 + end + end + return res +end + +--- swap two rows of an array. +-- @param t a 2d array +-- @param i1 a row index +-- @param i2 a row index +function array2d.swap_rows (t,i1,i2) + assert_arg(1,t,'table') + t[i1],t[i2] = t[i2],t[i1] +end + +--- swap two columns of an array. +-- @param t a 2d array +-- @param j1 a column index +-- @param j2 a column index +function array2d.swap_cols (t,j1,j2) + assert_arg(1,t,'table') + for i = 1,#t do + local row = t[i] + row[j1],row[j2] = row[j2],row[j1] + end +end + +--- extract the specified rows. +-- @param t 2d array +-- @param ridx a table of row indices +function array2d.extract_rows (t,ridx) + return index_by(t,ridx) +end + +--- extract the specified columns. +-- @param t 2d array +-- @param cidx a table of column indices +function array2d.extract_cols (t,cidx) + assert_arg(1,t,'table') + for i = 1,#t do + t[i] = index_by(t[i],cidx) + end +end + +--- remove a row from an array. +-- @class function +-- @name array2d.remove_row +-- @param t a 2d array +-- @param i a row index +array2d.remove_row = remove + +--- remove a column from an array. +-- @param t a 2d array +-- @param j a column index +function array2d.remove_col (t,j) + assert_arg(1,t,'table') + for i = 1,#t do + remove(t[i],j) + end +end + +local Ai = byte 'A' + +local function _parse (s) + local c,r + if s:sub(1,1) == 'R' then + r,c = s:match 'R(%d+)C(%d+)' + r,c = tonumber(r),tonumber(c) + else + c,r = s:match '(.)(.)' + c = byte(c) - byte 'A' + 1 + r = tonumber(r) + end + assert(c ~= nil and r ~= nil,'bad cell specifier: '..s) + return r,c +end + +--- parse a spreadsheet range. +-- The range can be specified either as 'A1:B2' or 'R1C1:R2C2'; +-- a special case is a single element (e.g 'A1' or 'R1C1') +-- @param s a range. +-- @return start col +-- @return start row +-- @return end col +-- @return end row +function array2d.parse_range (s) + if s:find ':' then + local start,finish = splitv(s,':') + local i1,j1 = _parse(start) + local i2,j2 = _parse(finish) + return i1,j1,i2,j2 + else -- single value + local i,j = _parse(s) + return i,j + end +end + +--- get a slice of a 2D array using spreadsheet range notation. @see parse_range +-- @param t a 2D array +-- @param rstr range expression +-- @return a slice +-- @see array2d.parse_range +-- @see array2d.slice +function array2d.range (t,rstr) + assert_arg(1,t,'table') + local i1,j1,i2,j2 = array2d.parse_range(rstr) + if i2 then + return array2d.slice(t,i1,j1,i2,j2) + else -- single value + return t[i1][j1] + end +end + +local function default_range (t,i1,j1,i2,j2) + assert(t and type(t)=='table','not a table') + i1,j1 = i1 or 1, j1 or 1 + i2,j2 = i2 or #t, j2 or #t[1] + return i1,j1,i2,j2 +end + +--- get a slice of a 2D array. Note that if the specified range has +-- a 1D result, the rank of the result will be 1. +-- @param t a 2D array +-- @param i1 start row (default 1) +-- @param j1 start col (default 1) +-- @param i2 end row (default N) +-- @param j2 end col (default M) +-- @return an array, 2D in general but 1D in special cases. +function array2d.slice (t,i1,j1,i2,j2) + assert_arg(1,t,'table') + i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2) + local res = {} + for i = i1,i2 do + local val + local row = t[i] + if j1 == j2 then + val = row[j1] + else + val = {} + for j = j1,j2 do + val[#val+1] = row[j] + end + end + res[#res+1] = val + end + if i1 == i2 then res = res[1] end + return res +end + +--- set a specified range of an array to a value. +-- @param t a 2D array +-- @param value the value +-- @param i1 start row (default 1) +-- @param j1 start col (default 1) +-- @param i2 end row (default N) +-- @param j2 end col (default M) +function array2d.set (t,value,i1,j1,i2,j2) + i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2) + for i = i1,i2 do + tset(t[i],value) + end +end + +--- write a 2D array to a file. +-- @param t a 2D array +-- @param f a file object (default stdout) +-- @param fmt a format string (default is just to use tostring) +-- @param i1 start row (default 1) +-- @param j1 start col (default 1) +-- @param i2 end row (default N) +-- @param j2 end col (default M) +function array2d.write (t,f,fmt,i1,j1,i2,j2) + assert_arg(1,t,'table') + f = f or stdout + local rowop + if fmt then + rowop = function(row,j) fprintf(f,fmt,row[j]) end + else + rowop = function(row,j) f:write(tostring(row[j]),' ') end + end + local function newline() + f:write '\n' + end + array2d.forall(t,rowop,newline,i1,j1,i2,j2) +end + +--- perform an operation for all values in a 2D array. +-- @param t 2D array +-- @param row_op function to call on each value +-- @param end_row_op function to call at end of each row +-- @param i1 start row (default 1) +-- @param j1 start col (default 1) +-- @param i2 end row (default N) +-- @param j2 end col (default M) +function array2d.forall (t,row_op,end_row_op,i1,j1,i2,j2) + assert_arg(1,t,'table') + i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2) + for i = i1,i2 do + local row = t[i] + for j = j1,j2 do + row_op(row,j) + end + if end_row_op then end_row_op(i) end + end +end + +--- iterate over all elements in a 2D array, with optional indices. +-- @param a 2D array +-- @param indices with indices (default false) +-- @param i1 start row (default 1) +-- @param j1 start col (default 1) +-- @param i2 end row (default N) +-- @param j2 end col (default M) +-- @return either value or i,j,value depending on indices +function array2d.iter (a,indices,i1,j1,i2,j2) + assert_arg(1,a,'table') + local norowset = not (i2 and j2) + i1,j1,i2,j2 = default_range(a,i1,j1,i2,j2) + local n,i,j = i2-i1+1,i1-1,j1-1 + local row,nr = nil,0 + local onr = j2 - j1 + 1 + return function() + j = j + 1 + if j > nr then + j = j1 + i = i + 1 + if i > i2 then return nil end + row = a[i] + nr = norowset and #row or onr + end + if indices then + return i,j,row[j] + else + return row[j] + end + end +end + +function array2d.columns (a) + assert_arg(1,a,'table') + local n = a[1][1] + local i = 0 + return function() + i = i + 1 + if i > n then return nil end + return column(a,i) + end +end + +return array2d + + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/class.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/class.lua new file mode 100644 index 000000000..5cfbf991d --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/class.lua @@ -0,0 +1,155 @@ +--- Provides a reuseable and convenient framework for creating classes in Lua. +-- Two possible notations:
    B = class(A) or class.B(A) .
    +--

    The latter form creates a named class.

    +-- See the Guide for further discussion +-- @module pl.class + +local error, getmetatable, io, pairs, rawget, rawset, setmetatable, tostring, type = + _G.error, _G.getmetatable, _G.io, _G.pairs, _G.rawget, _G.rawset, _G.setmetatable, _G.tostring, _G.type +-- this trickery is necessary to prevent the inheritance of 'super' and +-- the resulting recursive call problems. +local function call_ctor (c,obj,...) + -- nice alias for the base class ctor + local base = rawget(c,'_base') + if base then obj.super = rawget(base,'_init') end + local res = c._init(obj,...) + obj.super = nil + return res +end + +local function is_a(self,klass) + local m = getmetatable(self) + if not m then return false end --*can't be an object! + while m do + if m == klass then return true end + m = rawget(m,'_base') + end + return false +end + +local function class_of(klass,obj) + if type(klass) ~= 'table' or not rawget(klass,'is_a') then return false end + return klass.is_a(obj,klass) +end + +local function _class_tostring (obj) + local mt = obj._class + local name = rawget(mt,'_name') + setmetatable(obj,nil) + local str = tostring(obj) + setmetatable(obj,mt) + if name then str = name ..str:gsub('table','') end + return str +end + +local function tupdate(td,ts) + for k,v in pairs(ts) do + td[k] = v + end +end + +local function _class(base,c_arg,c) + c = c or {} -- a new class instance, which is the metatable for all objects of this type + -- the class will be the metatable for all its objects, + -- and they will look up their methods in it. + local mt = {} -- a metatable for the class instance + + if type(base) == 'table' then + -- our new class is a shallow copy of the base class! + tupdate(c,base) + c._base = base + -- inherit the 'not found' handler, if present + if rawget(c,'_handler') then mt.__index = c._handler end + elseif base ~= nil then + error("must derive from a table type",3) + end + + c.__index = c + setmetatable(c,mt) + c._init = nil + + if base and rawget(base,'_class_init') then + base._class_init(c,c_arg) + end + + -- expose a ctor which can be called by () + mt.__call = function(class_tbl,...) + local obj = {} + setmetatable(obj,c) + + if rawget(c,'_init') then -- explicit constructor + local res = call_ctor(c,obj,...) + if res then -- _if_ a ctor returns a value, it becomes the object... + obj = res + setmetatable(obj,c) + end + elseif base and rawget(base,'_init') then -- default constructor + -- make sure that any stuff from the base class is initialized! + call_ctor(base,obj,...) + end + + if base and rawget(base,'_post_init') then + base._post_init(obj) + end + + if not rawget(c,'__tostring') then + c.__tostring = _class_tostring + end + return obj + end + -- Call Class.catch to set a handler for methods/properties not found in the class! + c.catch = function(handler) + c._handler = handler + mt.__index = handler + end + c.is_a = is_a + c.class_of = class_of + c._class = c + -- any object can have a specified delegate which is called with unrecognized methods + -- if _handler exists and obj[key] is nil, then pass onto handler! + c.delegate = function(self,obj) + mt.__index = function(tbl,key) + local method = obj[key] + if method then + return function(self,...) + return method(obj,...) + end + elseif self._handler then + return self._handler(tbl,key) + end + end + end + return c +end + +--- create a new class, derived from a given base class.
    +-- Supporting two class creation syntaxes: +-- either Name = class(base) or class.Name(base) +-- @class function +-- @name class +-- @param base optional base class +-- @param c_arg optional parameter to class ctor +-- @param c optional table to be used as class +local class +class = setmetatable({},{ + __call = function(fun,...) + return _class(...) + end, + __index = function(tbl,key) + if key == 'class' then + io.stderr:write('require("pl.class").class is deprecated. Use require("pl.class")\n') + return class + end + local env = _G + return function(...) + local c = _class(...) + c._name = key + rawset(env,key,c) + return c + end + end +}) + + +return class + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/comprehension.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/comprehension.lua new file mode 100644 index 000000000..9765bbf87 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/comprehension.lua @@ -0,0 +1,288 @@ +--- List comprehensions implemented in Lua.

    +-- +-- See the wiki page +--

    +--   local C= require 'pl.comprehension' . new()
    +--
    +--    C ('x for x=1,10') ()
    +--    ==> {1,2,3,4,5,6,7,8,9,10}
    +--    C 'x^2 for x=1,4' ()
    +--    ==> {1,4,9,16}
    +--    C '{x,x^2} for x=1,4' ()
    +--    ==> {{1,1},{2,4},{3,9},{4,16}}
    +--    C '2*x for x' {1,2,3}
    +--    ==> {2,4,6}
    +--    dbl = C '2*x for x'
    +--    dbl {10,20,30}
    +--    ==> {20,40,60}
    +--    C 'x for x if x % 2 == 0' {1,2,3,4,5}
    +--    ==> {2,4}
    +--    C '{x,y} for x = 1,2 for y = 1,2' ()
    +--    ==> {{1,1},{1,2},{2,1},{2,2}}
    +--    C '{x,y} for x for y' ({1,2},{10,20})
    +--    ==> {{1,10},{1,20},{2,10},{2,20}}
    +--   assert(C 'sum(x^2 for x)' {2,3,4} == 2^2+3^2+4^2)
    +-- 
    +-- +--

    (c) 2008 David Manura. Licensed under the same terms as Lua (MIT license). +--

    -- See the Guide +-- @class module +-- @name pl.comprehension + +local utils = require 'pl.utils' + +--~ local _VERSION, assert, getfenv, ipairs, load, math, pcall, require, setmetatable, table, tonumber = +--~ _G._VERSION, _G.assert, _G.getfenv, _G.ipairs, _G.load, _G.math, _G.pcall, _G.require, _G.setmetatable, _G.table, _G.tonumber + +local status,lb = pcall(require, "pl.luabalanced") +if not status then + lb = require 'luabalanced' +end + +local math_max = math.max +local table_concat = table.concat + +-- fold operations +-- http://en.wikipedia.org/wiki/Fold_(higher-order_function) +local ops = { + list = {init=' {} ', accum=' __result[#__result+1] = (%s) '}, + table = {init=' {} ', accum=' local __k, __v = %s __result[__k] = __v '}, + sum = {init=' 0 ', accum=' __result = __result + (%s) '}, + min = {init=' nil ', accum=' local __tmp = %s ' .. + ' if __result then if __tmp < __result then ' .. + '__result = __tmp end else __result = __tmp end '}, + max = {init=' nil ', accum=' local __tmp = %s ' .. + ' if __result then if __tmp > __result then ' .. + '__result = __tmp end else __result = __tmp end '}, +} + + +-- Parses comprehension string expr. +-- Returns output expression list string, array of for types +-- ('=', 'in' or nil) , array of input variable name +-- strings , array of input variable value strings +-- , array of predicate expression strings , +-- operation name string , and number of placeholder +-- parameters . +-- +-- The is equivalent to the mathematical set-builder notation: +-- +-- { | in , } +-- +-- @usage "x^2 for x" -- array values +-- @usage "x^2 for x=1,10,2" -- numeric for +-- @usage "k^v for k,v in pairs(_1)" -- iterator for +-- @usage "(x+y)^2 for x for y if x > y" -- nested +-- +local function parse_comprehension(expr) + local t = {} + local pos = 1 + + -- extract opname (if exists) + local opname + local tok, post = expr:match('^%s*([%a_][%w_]*)%s*%(()', pos) + local pose = #expr + 1 + if tok then + local tok2, posb = lb.match_bracketed(expr, post-1) + assert(tok2, 'syntax error') + if expr:match('^%s*$', posb) then + opname = tok + pose = posb - 1 + pos = post + end + end + opname = opname or "list" + + -- extract out expression list + local out; out, pos = lb.match_explist(expr, pos) + assert(out, "syntax error: missing expression list") + out = table_concat(out, ', ') + + -- extract "for" clauses + local fortypes = {} + local invarlists = {} + local invallists = {} + while 1 do + local post = expr:match('^%s*for%s+()', pos) + if not post then break end + pos = post + + -- extract input vars + local iv; iv, pos = lb.match_namelist(expr, pos) + assert(#iv > 0, 'syntax error: zero variables') + for _,ident in ipairs(iv) do + assert(not ident:match'^__', + "identifier " .. ident .. " may not contain __ prefix") + end + invarlists[#invarlists+1] = iv + + -- extract '=' or 'in' (optional) + local fortype, post = expr:match('^(=)%s*()', pos) + if not fortype then fortype, post = expr:match('^(in)%s+()', pos) end + if fortype then + pos = post + -- extract input value range + local il; il, pos = lb.match_explist(expr, pos) + assert(#il > 0, 'syntax error: zero expressions') + assert(fortype ~= '=' or #il == 2 or #il == 3, + 'syntax error: numeric for requires 2 or three expressions') + fortypes[#invarlists] = fortype + invallists[#invarlists] = il + else + fortypes[#invarlists] = false + invallists[#invarlists] = false + end + end + assert(#invarlists > 0, 'syntax error: missing "for" clause') + + -- extract "if" clauses + local preds = {} + while 1 do + local post = expr:match('^%s*if%s+()', pos) + if not post then break end + pos = post + local pred; pred, pos = lb.match_expression(expr, pos) + assert(pred, 'syntax error: predicated expression not found') + preds[#preds+1] = pred + end + + -- extract number of parameter variables (name matching "_%d+") + local stmp = ''; lb.gsub(expr, function(u, sin) -- strip comments/strings + if u == 'e' then stmp = stmp .. ' ' .. sin .. ' ' end + end) + local max_param = 0; stmp:gsub('[%a_][%w_]*', function(s) + local s = s:match('^_(%d+)$') + if s then max_param = math_max(max_param, tonumber(s)) end + end) + + if pos ~= pose then + assert(false, "syntax error: unrecognized " .. expr:sub(pos)) + end + + --DEBUG: + --print('----\n', string.format("%q", expr), string.format("%q", out), opname) + --for k,v in ipairs(invarlists) do print(k,v, invallists[k]) end + --for k,v in ipairs(preds) do print(k,v) end + + return out, fortypes, invarlists, invallists, preds, opname, max_param +end + + +-- Create Lua code string representing comprehension. +-- Arguments are in the form returned by parse_comprehension. +local function code_comprehension( + out, fortypes, invarlists, invallists, preds, opname, max_param +) + local op = assert(ops[opname]) + local code = op.accum:gsub('%%s', out) + + for i=#preds,1,-1 do local pred = preds[i] + code = ' if ' .. pred .. ' then ' .. code .. ' end ' + end + for i=#invarlists,1,-1 do + if not fortypes[i] then + local arrayname = '__in' .. i + local idx = '__idx' .. i + code = + ' for ' .. idx .. ' = 1, #' .. arrayname .. ' do ' .. + ' local ' .. invarlists[i][1] .. ' = ' .. arrayname .. '['..idx..'] ' .. + code .. ' end ' + else + code = + ' for ' .. + table_concat(invarlists[i], ', ') .. + ' ' .. fortypes[i] .. ' ' .. + table_concat(invallists[i], ', ') .. + ' do ' .. code .. ' end ' + end + end + code = ' local __result = ( ' .. op.init .. ' ) ' .. code + return code +end + + +-- Convert code string represented by code_comprehension +-- into Lua function. Also must pass ninputs = #invarlists, +-- max_param, and invallists (from parse_comprehension). +-- Uses environment env. +local function wrap_comprehension(code, ninputs, max_param, invallists, env) + assert(ninputs > 0) + local ts = {} + for i=1,max_param do + ts[#ts+1] = '_' .. i + end + for i=1,ninputs do + if not invallists[i] then + local name = '__in' .. i + ts[#ts+1] = name + end + end + if #ts > 0 then + code = ' local ' .. table_concat(ts, ', ') .. ' = ... ' .. code + end + code = code .. ' return __result ' + --print('DEBUG:', code) + local f, err = utils.load(code,'tmp','t',env) + if not f then assert(false, err .. ' with generated code ' .. code) end + return f +end + + +-- Build Lua function from comprehension string. +-- Uses environment env. +local function build_comprehension(expr, env) + local out, fortypes, invarlists, invallists, preds, opname, max_param + = parse_comprehension(expr) + local code = code_comprehension( + out, fortypes, invarlists, invallists, preds, opname, max_param) + local f = wrap_comprehension(code, #invarlists, max_param, invallists, env) + return f +end + + +-- Creates new comprehension cache. +-- Any list comprehension function created are set to the environment +-- env (defaults to caller of new). +local function new(env) + -- Note: using a single global comprehension cache would have had + -- security implications (e.g. retrieving cached functions created + -- in other environments). + -- The cache lookup function could have instead been written to retrieve + -- the caller's environment, lookup up the cache private to that + -- environment, and then looked up the function in that cache. + -- That would avoid the need for this call to + -- explicitly manage caches; however, that might also have an undue + -- performance penalty. + + if not env then + env = getfenv(2) + end + + local mt = {} + local cache = setmetatable({}, mt) + + -- Index operator builds, caches, and returns Lua function + -- corresponding to comprehension expression string. + -- + -- Example: f = comprehension['x^2 for x'] + -- + function mt:__index(expr) + local f = build_comprehension(expr, env) + self[expr] = f -- cache + return f + end + + -- Convenience syntax. + -- Allows comprehension 'x^2 for x' instead of comprehension['x^2 for x']. + mt.__call = mt.__index + + cache.new = new + + return cache +end + + +local comprehension = {} +comprehension.new = new + +return comprehension diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/config.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/config.lua new file mode 100644 index 000000000..bd6b89781 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/config.lua @@ -0,0 +1,169 @@ +--- Reads configuration files into a Lua table.

    +-- Understands INI files, classic Unix config files, and simple +-- delimited columns of values.

    +--

    +--    # test.config
    +--    # Read timeout in seconds
    +--    read.timeout=10
    +--    # Write timeout in seconds
    +--    write.timeout=5
    +--    #acceptable ports
    +--    ports = 1002,1003,1004
    +--
    +--        -- readconfig.lua
    +--    require 'pl'
    +--    local t = config.read 'test.config'
    +--    print(pretty.write(t))
    +--
    +--    ### output #####
    +--   {
    +--      ports = {
    +--        1002,
    +--        1003,
    +--        1004
    +--      },
    +--      write_timeout = 5,
    +--      read_timeout = 10
    +--    }
    +-- 
    +-- See the Guide for further discussion +-- @class module +-- @name pl.config + +local type,tonumber,ipairs,io, table = _G.type,_G.tonumber,_G.ipairs,_G.io,_G.table + +local function split(s,re) + local res = {} + local t_insert = table.insert + re = '[^'..re..']+' + for k in s:gmatch(re) do t_insert(res,k) end + return res +end + +local function strip(s) + return s:gsub('^%s+',''):gsub('%s+$','') +end + +local function strip_quotes (s) + return s:gsub("['\"](.*)['\"]",'%1') +end + +local config = {} + +--- like io.lines(), but allows for lines to be continued with '\'. +-- @param file a file-like object (anything where read() returns the next line) or a filename. +-- Defaults to stardard input. +-- @return an iterator over the lines, or nil +-- @return error 'not a file-like object' or 'file is nil' +function config.lines(file) + local f,openf,err + local line = '' + if type(file) == 'string' then + f,err = io.open(file,'r') + if not f then return nil,err end + openf = true + else + f = file or io.stdin + if not file.read then return nil, 'not a file-like object' end + end + if not f then return nil, 'file is nil' end + return function() + local l = f:read() + while l do + -- does the line end with '\'? + local i = l:find '\\%s*$' + if i then -- if so, + line = line..l:sub(1,i-1) + elseif line == '' then + return l + else + l = line..l + line = '' + return l + end + l = f:read() + end + if openf then f:close() end + end +end + +--- read a configuration file into a table +-- @param file either a file-like object or a string, which must be a filename +-- @param cnfg a configuration table that may contain these fields: +--
      +--
    • variablilize make names into valid Lua identifiers (default true)
    • +--
    • convert_numbers try to convert values into numbers (default true)
    • +--
    • trim_space ensure that there is no starting or trailing whitespace with values (default true)
    • +--
    • trim_quotes remove quotes from strings (default false)
    • +--
    • list_delim delimiter to use when separating columns (default ',')
    • +--
    +-- @return a table containing items, or nil +-- @return error message (same as @{config.lines} +function config.read(file,cnfg) + local f,openf,err + cnfg = cnfg or {} + local function check_cnfg (var,def) + local val = cnfg[var] + if val == nil then return def else return val end + end + local t = {} + local top_t = t + local variablilize = check_cnfg ('variabilize',true) + local list_delim = check_cnfg('list_delim',',') + local convert_numbers = check_cnfg('convert_numbers',true) + local trim_space = check_cnfg('trim_space',true) + local trim_quotes = check_cnfg('trim_quotes',false) + local ignore_assign = check_cnfg('ignore_assign',false) + + local function process_name(key) + if variablilize then + key = key:gsub('[^%w]','_') + end + return key + end + + local function process_value(value) + if list_delim and value:find(list_delim) then + value = split(value,list_delim) + for i,v in ipairs(value) do + value[i] = process_value(v) + end + elseif convert_numbers and value:find('^[%d%+%-]') then + local val = tonumber(value) + if val then value = val end + end + if type(value) == 'string' then + if trim_space then value = strip(value) end + if trim_quotes then value = strip_quotes(value) end + end + return value + end + + local iter,err = config.lines(file) + if not iter then return nil,err end + for line in iter do + -- strips comments + local ci = line:find('%s*[#;]') + if ci then line = line:sub(1,ci-1) end + -- and ignore blank lines + if line:find('^%s*$') then + elseif line:find('^%[') then -- section! + local section = process_name(line:match('%[([^%]]+)%]')) + t = top_t + t[section] = {} + t = t[section] + else + local i1,i2 = line:find('%s*=%s*') + if i1 and not ignore_assign then -- key,value assignment + local key = process_name(line:sub(1,i1-1)) + local value = process_value(line:sub(i2+1)) + t[key] = value + else -- a plain list of values... + t[#t+1] = process_value(line) + end + end + end + return top_t +end + +return config diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/data.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/data.lua new file mode 100644 index 000000000..931c92692 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/data.lua @@ -0,0 +1,588 @@ +--- Reading and querying simple tabular data. +--
    +-- data.read 'test.txt'
    +-- ==> {{10,20},{2,5},{40,50},fieldnames={'x','y'},delim=','}
    +-- 
    +-- Provides a way of creating basic SQL-like queries. +--
    +--    require 'pl'
    +--    local d = data.read('xyz.txt')
    +--    local q = d:select('x,y,z where x > 3 and z < 2 sort by y')
    +--    for x,y,z in q do
    +--        print(x,y,z)
    +--    end
    +-- 
    +--

    See the Guide +-- @class module +-- @name pl.data + +local utils = require 'pl.utils' +local _DEBUG = rawget(_G,'_DEBUG') + +local patterns,function_arg,usplit = utils.patterns,utils.function_arg,utils.split +local append,concat = table.insert,table.concat +local gsub = string.gsub +local io = io +local _G,print,loadstring,type,tonumber,ipairs,setmetatable,pcall,error,setfenv = _G,print,loadstring,type,tonumber,ipairs,setmetatable,pcall,error,setfenv + +--[[ +module ('pl.data',utils._module) +]] + +local data = {} + +local parse_select + +local function count(s,chr) + chr = utils.escape(chr) + local _,cnt = s:gsub(chr,' ') + return cnt +end + +local function rstrip(s) + return s:gsub('%s+$','') +end + +local function make_list(l) + return setmetatable(l,utils.stdmt.List) +end + +local function split(s,delim) + return make_list(usplit(s,delim)) +end + +local function map(fun,t) + local res = {} + for i = 1,#t do + append(res,fun(t[i])) + end + return res +end + +local function find(t,v) + for i = 1,#t do + if v == t[i] then return i end + end +end + +local DataMT = { + column_by_name = function(self,name) + if type(name) == 'number' then + name = '$'..name + end + local arr = {} + for res in data.query(self,name) do + append(arr,res) + end + return make_list(arr) + end, + + copy_select = function(self,condn) + condn = parse_select(condn,self) + local iter = data.query(self,condn) + local res = {} + local row = make_list{iter()} + while #row > 0 do + append(res,row) + row = make_list{iter()} + end + res.delim = self.delim + return data.new(res,split(condn.fields,',')) + end, + + column_names = function(self) + return self.fieldnames + end, +} +DataMT.__index = DataMT + +--- return a particular column as a list of values (Method).
    +-- @param name either name of column, or numerical index. +-- @class function +-- @name Data.column_by_name + +--- return a query iterator on this data object (Method).
    +-- @param condn the query expression +-- @class function +-- @name Data.select +-- @see data.query + +--- return a new data object based on this query (Method).
    +-- @param condn the query expression +-- @class function +-- @name Data.copy_select + +--- return the field names of this data object (Method).
    +-- @class function +-- @name Data.column_names + +--- write out a row (Method).
    +-- @param f file-like object +-- @class function +-- @name Data.write_row + +--- write data out to file(Method).
    +-- @param f file-like object +-- @class function +-- @name Data.write + + +-- [guessing delimiter] We check for comma, tab and spaces in that order. +-- [issue] any other delimiters to be checked? +local delims = {',','\t',' ',';'} + +local function guess_delim (line) + for _,delim in ipairs(delims) do + if count(line,delim) > 0 then + return delim == ' ' and '%s+' or delim + end + end + return ' ' +end + +-- [file parameter] If it's a string, we try open as a filename. If nil, then +-- either stdin or stdout depending on the mode. Otherwise, check if this is +-- a file-like object (implements read or write depending) +local function open_file (f,mode) + local opened, err + local reading = mode == 'r' + if type(f) == 'string' then + if f == 'stdin' then + f = io.stdin + elseif f == 'stdout' then + f = io.stdout + else + f,err = io.open(f,mode) + if not f then return nil,err end + opened = true + end + end + if f and ((reading and not f.read) or (not reading and not f.write)) then + return nil, "not a file-like object" + end + return f,nil,opened +end + +local function all_n () + +end + +--- read a delimited file in a Lua table. +-- By default, attempts to treat first line as separated list of fieldnames. +-- @param file a filename or a file-like object (default stdin) +-- @param cnfg options table: can override delim (a string pattern), fieldnames (a list), +-- specify no_convert (default is to convert), numfields (indices of columns known +-- to be numbers) and thousands_dot (thousands separator in Excel CSV is '.') +function data.read(file,cnfg) + local convert,err,opened + local D = {} + if not cnfg then cnfg = {} end + local f,err,opened = open_file(file,'r') + if not f then return nil, err end + local thousands_dot = cnfg.thousands_dot + + local function try_tonumber(x) + if thousands_dot then x = x:gsub('%.(...)','%1') end + return tonumber(x) + end + + local line = f:read() + if not line then return nil, "empty file" end + -- first question: what is the delimiter? + D.delim = cnfg.delim and cnfg.delim or guess_delim(line) + local delim = D.delim + local collect_end = cnfg.last_field_collect + local numfields = cnfg.numfields + -- some space-delimited data starts with a space. This should not be a column, + -- although it certainly would be for comma-separated, etc. + local strip + if delim == '%s+' and line:find(delim) == 1 then + strip = function(s) return s:gsub('^%s+','') end + line = strip(line) + end + -- first line will usually be field names. Unless fieldnames are specified, + -- we check if it contains purely numerical values for the case of reading + -- plain data files. + if not cnfg.fieldnames then + local fields = split(line,delim) + local nums = map(tonumber,fields) + if #nums == #fields then + convert = tonumber + append(D,nums) + numfields = {} + for i = 1,#nums do numfields[i] = i end + else + cnfg.fieldnames = fields + end + line = f:read() + if strip then line = strip(line) end + elseif type(cnfg.fieldnames) == 'string' then + cnfg.fieldnames = split(cnfg.fieldnames,delim) + end + -- at this point, the column headers have been read in. If the first + -- row consisted of numbers, it has already been added to the dataset. + if cnfg.fieldnames then + D.fieldnames = cnfg.fieldnames + -- [conversion] unless @no_convert, we need the numerical field indices + -- of the first data row. Can also be specified by @numfields. + if not cnfg.no_convert then + if not numfields then + numfields = {} + local fields = split(line,D.delim) + for i = 1,#fields do + if tonumber(fields[i]) then + append(numfields,i) + end + end + end + if #numfields > 0 then -- there are numerical fields + -- note that using dot as the thousands separator (@thousands_dot) + -- requires a special conversion function! + convert = thousands_dot and try_tonumber or tonumber + end + end + end + -- keep going until finished + while line do + if not line:find ('^%s*$') then + if strip then line = strip(line) end + local fields = split(line,delim) + if convert then + for k = 1,#numfields do + local i = numfields[k] + local val = convert(fields[i]) + if val == nil then + return nil, "not a number: "..fields[i] + else + fields[i] = val + end + end + end + -- [collecting end field] If @last_field_collect then we will collect + -- all extra space-delimited fields into a single last field. + if collect_end and #fields > #D.fieldnames then + local ends,N = {},#D.fieldnames + for i = N+1,#fields do + append(ends,fields[i]) + end + ends = concat(ends,' ') + local cfields = {} + for i = 1,N do cfields[i] = fields[i] end + cfields[N] = cfields[N]..' '..ends + fields = cfields + end + append(D,fields) + end + line = f:read() + end + if opened then f:close() end + if delim == '%s+' then D.delim = ' ' end + if not D.fieldnames then D.fieldnames = {} end + return data.new(D) +end + +local function write_row (data,f,row) + f:write(concat(row,data.delim),'\n') +end + +DataMT.write_row = write_row + +local function write (data,file) + local f,err,opened = open_file(file,'w') + if not f then return nil, err end + if #data.fieldnames > 0 then + f:write(concat(data.fieldnames,data.delim),'\n') + end + for i = 1,#data do + write_row(data,f,data[i]) + end + if opened then f:close() end +end + +DataMT.write = write + +local function massage_fieldnames (fields) + -- [fieldnames must be valid Lua identifiers] fix 0.8 was %A + for i = 1,#fields do + fields[i] = fields[i]:gsub('%W','_') + end +end + + +--- create a new dataset from a table of rows.
    +-- Can specify the fieldnames, else the table must have a field called +-- 'fieldnames', which is either a string of delimiter-separated names, +-- or a table of names.
    +-- If the table does not have a field called 'delim', then an attempt will be +-- made to guess it from the fieldnames string, defaults otherwise to tab. +-- @param d the table. +-- @param fieldnames optional fieldnames +-- @return the table. +function data.new (d,fieldnames) + d.fieldnames = d.fieldnames or fieldnames + if not d.delim and type(d.fieldnames) == 'string' then + d.delim = guess_delim(d.fieldnames) + d.fieldnames = split(d.fieldnames,d.delim) + end + d.fieldnames = make_list(d.fieldnames) + massage_fieldnames(d.fieldnames) + setmetatable(d,DataMT) + -- a query with just the fieldname will return a sequence + -- of values, which seq.copy turns into a table. + return d +end + +local sorted_query = [[ +return function (t) + local i = 0 + local v + local ls = {} + for i,v in ipairs(t) do + if CONDITION then + ls[#ls+1] = v + end + end + table.sort(ls,function(v1,v2) + return SORT_EXPR + end) + local n = #ls + return function() + i = i + 1 + v = ls[i] + if i > n then return end + return FIELDLIST + end +end +]] + +-- question: is this optimized case actually worth the extra code? +local simple_query = [[ +return function (t) + local n = #t + local i = 0 + local v + return function() + repeat + i = i + 1 + v = t[i] + until i > n or CONDITION + if i > n then return end + return FIELDLIST + end +end +]] + +local function is_string (s) + return type(s) == 'string' +end + +local field_error + +local function fieldnames_as_string (data) + return concat(data.fieldnames,',') +end + +local function massage_fields(data,f) + local idx + if f:find '^%d+$' then + idx = tonumber(f) + else + idx = find(data.fieldnames,f) + end + if idx then + return 'v['..idx..']' + else + field_error = f..' not found in '..fieldnames_as_string(data) + return f + end +end + +local List = require 'pl.List' + +local function process_select (data,parms) + --- preparing fields ---- + local res,ret + field_error = nil + local fields = parms.fields + local numfields = fields:find '%$' or #data.fieldnames == 0 + if fields:find '^%s*%*%s*' then + if not numfields then + fields = fieldnames_as_string(data) + else + local ncol = #data[1] + fields = {} + for i = 1,ncol do append(fields,'$'..i) end + fields = concat(fields,',') + end + end + local idpat = patterns.IDEN + if numfields then + idpat = '%$(%d+)' + else + -- massage field names to replace non-identifier chars + fields = rstrip(fields):gsub('[^,%w]','_') + end + local massage_fields = utils.bind1(massage_fields,data) + ret = gsub(fields,idpat,massage_fields) + if field_error then return nil,field_error end + parms.fields = fields + parms.proc_fields = ret + parms.where = parms.where or 'true' + if is_string(parms.where) then + parms.where = gsub(parms.where,idpat,massage_fields) + field_error = nil + end + return true +end + + +parse_select = function(s,data) + local endp + local parms = {} + local w1,w2 = s:find('where ') + local s1,s2 = s:find('sort by ') + if w1 then -- where clause! + endp = (s1 or 0)-1 + parms.where = s:sub(w2+1,endp) + end + if s1 then -- sort by clause (must be last!) + parms.sort_by = s:sub(s2+1) + end + endp = (w1 or s1 or 0)-1 + parms.fields = s:sub(1,endp) + local status,err = process_select(data,parms) + if not status then return nil,err + else return parms end +end + +--- create a query iterator from a select string. +-- Select string has this format:
    +-- FIELDLIST [ where LUA-CONDN [ sort by FIELD] ]
    +-- FIELDLIST is a comma-separated list of valid fields, or '*'.

    +-- The condition can also be a table, with fields 'fields' (comma-sep string or +-- table), 'sort_by' (string) and 'where' (Lua expression string or function) +-- @param data table produced by read +-- @param condn select string or table +-- @param context a list of tables to be searched when resolving functions +-- @param return_row if true, wrap the results in a row table +-- @return an iterator over the specified fields, or nil +-- @return an error message +function data.query(data,condn,context,return_row) + local err + if is_string(condn) then + condn,err = parse_select(condn,data) + if not condn then return nil,err end + elseif type(condn) == 'table' then + if type(condn.fields) == 'table' then + condn.fields = concat(condn.fields,',') + end + if not condn.proc_fields then + local status,err = process_select(data,condn) + if not status then return nil,err end + end + else + return nil, "condition must be a string or a table" + end + local query, k + if condn.sort_by then -- use sorted_query + query = sorted_query + else + query = simple_query + end + local fields = condn.proc_fields or condn.fields + if return_row then + fields = '{'..fields..'}' + end + query,k = query:gsub('FIELDLIST',fields) + if is_string(condn.where) then + query = query:gsub('CONDITION',condn.where) + condn.where = nil + else + query = query:gsub('CONDITION','_condn(v)') + condn.where = function_arg(0,condn.where,'condition.where must be callable') + end + if condn.sort_by then + local expr,sort_var,sort_dir + local sort_by = condn.sort_by + local i1,i2 = sort_by:find('%s+') + if i1 then + sort_var,sort_dir = sort_by:sub(1,i1-1),sort_by:sub(i2+1) + else + sort_var = sort_by + sort_dir = 'asc' + end + if sort_var:match '^%$' then sort_var = sort_var:sub(2) end + sort_var = massage_fields(data,sort_var) + if field_error then return nil,field_error end + if sort_dir == 'asc' then + sort_dir = '<' + else + sort_dir = '>' + end + expr = ('%s %s %s'):format(sort_var:gsub('v','v1'),sort_dir,sort_var:gsub('v','v2')) + query = query:gsub('SORT_EXPR',expr) + end + if condn.where then + query = 'return function(_condn) '..query..' end' + end + if _DEBUG then print(query) end + + local fn,err = loadstring(query,'tmp') + if not fn then return nil,err end + fn = fn() -- get the function + if condn.where then + fn = fn(condn.where) + end + local qfun = fn(data) + if context then + -- [specifying context for condition] @context is a list of tables which are + -- 'injected'into the condition's custom context + append(context,_G) + local lookup = {} + setfenv(qfun,lookup) + setmetatable(lookup,{ + __index = function(tbl,key) + -- _G.print(tbl,key) + for k,t in ipairs(context) do + if t[key] then return t[key] end + end + end + }) + end + return qfun +end + + +DataMT.select = data.query +DataMT.select_row = function(d,condn,context) + return data.query(d,condn,context,true) +end + +--- Filter input using a query. +-- @param Q a query string +-- @param infile filename or file-like object +-- @param outfile filename or file-like object +-- @param dont_fail true if you want to return an error, not just fail +function data.filter (Q,infile,outfile,dont_fail) + local err + local d = data.read(infile or 'stdin') + local out = open_file(outfile or 'stdout') + local iter,err = d:select(Q) + local delim = d.delim + if not iter then + err = 'error: '..err + if dont_fail then + return nil,err + else + utils.quit(1,err) + end + end + while true do + local res = {iter()} + if #res == 0 then break end + out:write(concat(res,delim),'\n') + end +end + +return data + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/dir.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/dir.lua new file mode 100644 index 000000000..5cea49e27 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/dir.lua @@ -0,0 +1,478 @@ +--- Useful functions for getting directory contents and matching them against wildcards. +-- @class module +-- @name pl.dir + +local utils = require 'pl.utils' +local path = require 'pl.path' +local is_windows = path.is_windows +local tablex = require 'pl.tablex' +local ldir = path.dir +local chdir = path.chdir +local mkdir = path.mkdir +local rmdir = path.rmdir +local sub = string.sub +local os,pcall,ipairs,pairs,require,setmetatable,_G = os,pcall,ipairs,pairs,require,setmetatable,_G +local remove = os.remove +local append = table.insert +local wrap = coroutine.wrap +local yield = coroutine.yield +local assert_arg,assert_string,raise = utils.assert_arg,utils.assert_string,utils.raise +local List = utils.stdmt.List + +--[[ +module ('pl.dir',utils._module) +]] + +local dir = {} + +local function assert_dir (n,val) + assert_arg(n,val,'string',path.isdir,'not a directory') +end + +local function assert_file (n,val) + assert_arg(n,val,'string',path.isfile,'not a file') +end + +local function filemask(mask) + mask = utils.escape(mask) + return mask:gsub('%%%*','.+'):gsub('%%%?','.')..'$' +end + +--- does the filename match the shell pattern?. +-- (cf. fnmatch.fnmatch in Python, 11.8) +-- @param file A file name +-- @param pattern A shell pattern +-- @return true or false +-- @raise file and pattern must be strings +function dir.fnmatch(file,pattern) + assert_string(1,file) + assert_string(2,pattern) + return path.normcase(file):find(filemask(pattern)) ~= nil +end + +--- return a list of all files which match the pattern. +-- (cf. fnmatch.filter in Python, 11.8) +-- @param files A table containing file names +-- @param pattern A shell pattern. +-- @return list of files +-- @raise file and pattern must be strings +function dir.filter(files,pattern) + assert_arg(1,files,'table') + assert_string(2,pattern) + local res = {} + local mask = filemask(pattern) + for i,f in ipairs(files) do + if f:find(mask) then append(res,f) end + end + return setmetatable(res,List) +end + +local function _listfiles(dir,filemode,match) + local res = {} + local check = utils.choose(filemode,path.isfile,path.isdir) + if not dir then dir = '.' end + for f in ldir(dir) do + if f ~= '.' and f ~= '..' then + local p = path.join(dir,f) + if check(p) and (not match or match(p)) then + append(res,p) + end + end + end + return setmetatable(res,List) +end + +--- return a list of all files in a directory which match the a shell pattern. +-- @param dir A directory. If not given, all files in current directory are returned. +-- @param mask A shell pattern. If not given, all files are returned. +-- @return lsit of files +-- @raise dir and mask must be strings +function dir.getfiles(dir,mask) + assert_dir(1,dir) + assert_string(2,mask) + local match + if mask then + mask = filemask(mask) + match = function(f) + return f:find(mask) + end + end + return _listfiles(dir,true,match) +end + +--- return a list of all subdirectories of the directory. +-- @param dir A directory +-- @return a list of directories +-- @raise dir must be a string +function dir.getdirectories(dir) + assert_dir(1,dir) + return _listfiles(dir,false) +end + +local function quote_argument (f) + f = path.normcase(f) + if f:find '%s' then + return '"'..f..'"' + else + return f + end +end + + +local alien,ffi,ffi_checked,CopyFile,MoveFile,GetLastError,win32_errors,cmd_tmpfile + +local function execute_command(cmd,parms) + if not cmd_tmpfile then cmd_tmpfile = path.tmpname () end + local err = path.is_windows and ' > ' or ' 2> ' + cmd = cmd..' '..parms..err..cmd_tmpfile + local ret = utils.execute(cmd) + if not ret then + return false,(utils.readfile(cmd_tmpfile):gsub('\n(.*)','')) + else + return true + end +end + +local function find_ffi_copyfile () + if not ffi_checked then + ffi_checked = true + local res + res,alien = pcall(require,'alien') + if not res then + alien = nil + res, ffi = pcall(require,'ffi') + end + if not res then + ffi = nil + return + end + else + return + end + if alien then + -- register the Win32 CopyFile and MoveFile functions + local kernel = alien.load('kernel32.dll') + CopyFile = kernel.CopyFileA + CopyFile:types{'string','string','int',ret='int',abi='stdcall'} + MoveFile = kernel.MoveFileA + MoveFile:types{'string','string',ret='int',abi='stdcall'} + GetLastError = kernel.GetLastError + GetLastError:types{ret ='int', abi='stdcall'} + elseif ffi then + ffi.cdef [[ + int CopyFileA(const char *src, const char *dest, int iovr); + int MoveFileA(const char *src, const char *dest); + int GetLastError(); + ]] + CopyFile = ffi.C.CopyFileA + MoveFile = ffi.C.MoveFileA + GetLastError = ffi.C.GetLastError + end + win32_errors = { + ERROR_FILE_NOT_FOUND = 2, + ERROR_PATH_NOT_FOUND = 3, + ERROR_ACCESS_DENIED = 5, + ERROR_WRITE_PROTECT = 19, + ERROR_BAD_UNIT = 20, + ERROR_NOT_READY = 21, + ERROR_WRITE_FAULT = 29, + ERROR_READ_FAULT = 30, + ERROR_SHARING_VIOLATION = 32, + ERROR_LOCK_VIOLATION = 33, + ERROR_HANDLE_DISK_FULL = 39, + ERROR_BAD_NETPATH = 53, + ERROR_NETWORK_BUSY = 54, + ERROR_DEV_NOT_EXIST = 55, + ERROR_FILE_EXISTS = 80, + ERROR_OPEN_FAILED = 110, + ERROR_INVALID_NAME = 123, + ERROR_BAD_PATHNAME = 161, + ERROR_ALREADY_EXISTS = 183, + } +end + +local function two_arguments (f1,f2) + return quote_argument(f1)..' '..quote_argument(f2) +end + +local function file_op (is_copy,src,dest,flag) + if flag == 1 and path.exists(dest) then + return false,"cannot overwrite destination" + end + if is_windows then + -- if we haven't tried to load Alien/LuaJIT FFI before, then do so + find_ffi_copyfile() + -- fallback if there's no Alien, just use DOS commands *shudder* + -- 'rename' involves a copy and then deleting the source. + if not CopyFile then + src = path.normcase(src) + dest = path.normcase(dest) + local cmd = is_copy and 'copy' or 'rename' + local res, err = execute_command('copy',two_arguments(src,dest)) + if not res then return nil,err end + if not is_copy then + return execute_command('del',quote_argument(src)) + end + else + if path.isdir(dest) then + dest = path.join(dest,path.basename(src)) + end + local ret + if is_copy then ret = CopyFile(src,dest,flag) + else ret = MoveFile(src,dest) end + if ret == 0 then + local err = GetLastError() + for name,value in pairs(win32_errors) do + if value == err then return false,name end + end + return false,"Error #"..err + else return true + end + end + else -- for Unix, just use cp for now + return execute_command(is_copy and 'cp' or 'mv', + two_arguments(src,dest)) + end +end + +--- copy a file. +-- @param src source file +-- @param dest destination file or directory +-- @param flag true if you want to force the copy (default) +-- @return true if operation succeeded +-- @raise src and dest must be strings +function dir.copyfile (src,dest,flag) + assert_string(1,src) + assert_string(2,dest) + flag = flag==nil or flag + return file_op(true,src,dest,flag and 0 or 1) +end + +--- move a file. +-- @param src source file +-- @param dest destination file or directory +-- @return true if operation succeeded +-- @raise src and dest must be strings +function dir.movefile (src,dest) + assert_string(1,src) + assert_string(2,dest) + return file_op(false,src,dest,0) +end + +local function _dirfiles(dir,attrib) + local dirs = {} + local files = {} + for f in ldir(dir) do + if f ~= '.' and f ~= '..' then + local p = path.join(dir,f) + local mode = attrib(p,'mode') + if mode=='directory' then + append(dirs,f) + else + append(files,f) + end + end + end + return setmetatable(dirs,List),setmetatable(files,List) +end + + +local function _walker(root,bottom_up,attrib) + local dirs,files = _dirfiles(root,attrib) + if not bottom_up then yield(root,dirs,files) end + for i,d in ipairs(dirs) do + _walker(root..path.sep..d,bottom_up,attrib) + end + if bottom_up then yield(root,dirs,files) end +end + +--- return an iterator which walks through a directory tree starting at root. +-- The iterator returns (root,dirs,files) +-- Note that dirs and files are lists of names (i.e. you must say path.join(root,d) +-- to get the actual full path) +-- If bottom_up is false (or not present), then the entries at the current level are returned +-- before we go deeper. This means that you can modify the returned list of directories before +-- continuing. +-- This is a clone of os.walk from the Python libraries. +-- @param root A starting directory +-- @param bottom_up False if we start listing entries immediately. +-- @param follow_links follow symbolic links +-- @return an iterator returning root,dirs,files +-- @raise root must be a string +function dir.walk(root,bottom_up,follow_links) + assert_string(1,root) + if not path.isdir(root) then return raise 'not a directory' end + local attrib + if path.is_windows or not follow_links then + attrib = path.attrib + else + attrib = path.link_attrib + end + return wrap(function () _walker(root,bottom_up,attrib) end) +end + +--- remove a whole directory tree. +-- @param fullpath A directory path +-- @return true or nil +-- @return error if failed +-- @raise fullpath must be a string +function dir.rmtree(fullpath) + assert_string(1,fullpath) + if not path.isdir(fullpath) then return raise 'not a directory' end + if path.islink(fullpath) then return false,'will not follow symlink' end + for root,dirs,files in dir.walk(fullpath,true) do + for i,f in ipairs(files) do + remove(path.join(root,f)) + end + rmdir(root) + end + return true +end + +local dirpat +if path.is_windows then + dirpat = '(.+)\\[^\\]+$' +else + dirpat = '(.+)/[^/]+$' +end + +local _makepath +function _makepath(p) + -- windows root drive case + if p:find '^%a:[\\]*$' then + return true + end + if not path.isdir(p) then + local subp = p:match(dirpat) + if not _makepath(subp) then return raise ('cannot create '..subp) end + return mkdir(p) + else + return true + end +end + +--- create a directory path. +-- This will create subdirectories as necessary! +-- @param p A directory path +-- @return a valid created path +-- @raise p must be a string +function dir.makepath (p) + assert_string(1,p) + return _makepath(path.normcase(path.abspath(p))) +end + + +--- clone a directory tree. Will always try to create a new directory structure +-- if necessary. +-- @param path1 the base path of the source tree +-- @param path2 the new base path for the destination +-- @param file_fun an optional function to apply on all files +-- @param verbose an optional boolean to control the verbosity of the output. +-- It can also be a logging function that behaves like print() +-- @return true, or nil +-- @return error message, or list of failed directory creations +-- @return list of failed file operations +-- @raise path1 and path2 must be strings +-- @usage clonetree('.','../backup',copyfile) +function dir.clonetree (path1,path2,file_fun,verbose) + assert_string(1,path1) + assert_string(2,path2) + if verbose == true then verbose = print end + local abspath,normcase,isdir,join = path.abspath,path.normcase,path.isdir,path.join + local faildirs,failfiles = {},{} + if not isdir(path1) then return raise 'source is not a valid directory' end + path1 = abspath(normcase(path1)) + path2 = abspath(normcase(path2)) + if verbose then verbose('normalized:',path1,path2) end + -- particularly NB that the new path isn't fully contained in the old path + if path1 == path2 then return raise "paths are the same" end + local i1,i2 = path2:find(path1,1,true) + if i2 == #path1 and path2:sub(i2+1,i2+1) == path.sep then + return raise 'destination is a subdirectory of the source' + end + local cp = path.common_prefix (path1,path2) + local idx = #cp + if idx == 0 then -- no common path, but watch out for Windows paths! + if path1:sub(2,2) == ':' then idx = 3 end + end + for root,dirs,files in dir.walk(path1) do + local opath = path2..root:sub(idx) + if verbose then verbose('paths:',opath,root) end + if not isdir(opath) then + local ret = dir.makepath(opath) + if not ret then append(faildirs,opath) end + if verbose then verbose('creating:',opath,ret) end + end + if file_fun then + for i,f in ipairs(files) do + local p1 = join(root,f) + local p2 = join(opath,f) + local ret = file_fun(p1,p2) + if not ret then append(failfiles,p2) end + if verbose then + verbose('files:',p1,p2,ret) + end + end + end + end + return true,faildirs,failfiles +end + +--- return an iterator over all entries in a directory tree +-- @param d a directory +-- @return an iterator giving pathname and mode (true for dir, false otherwise) +-- @raise d must be a non-empty string +function dir.dirtree( d ) + assert( d and d ~= "", "directory parameter is missing or empty" ) + local exists, isdir = path.exists, path.isdir + local sep = path.sep + + local last = sub ( d, -1 ) + if last == sep or last == '/' then + d = sub( d, 1, -2 ) + end + + local function yieldtree( dir ) + for entry in ldir( dir ) do + if entry ~= "." and entry ~= ".." then + entry = dir .. sep .. entry + if exists(entry) then -- Just in case a symlink is broken. + local is_dir = isdir(entry) + yield( entry, is_dir ) + if is_dir then + yieldtree( entry ) + end + end + end + end + end + + return wrap( function() yieldtree( d ) end ) +end + + +--- Recursively returns all the file starting at path. It can optionally take a shell pattern and +-- only returns files that match pattern. If a pattern is given it will do a case insensitive search. +-- @param start_path {string} A directory. If not given, all files in current directory are returned. +-- @param pattern {string} A shell pattern. If not given, all files are returned. +-- @return Table containing all the files found recursively starting at path and filtered by pattern. +-- @raise start_path must be a string +function dir.getallfiles( start_path, pattern ) + assert( type( start_path ) == "string", "bad argument #1 to 'GetAllFiles' (Expected string but recieved " .. type( start_path ) .. ")" ) + pattern = pattern or "" + + local files = {} + local normcase = path.normcase + for filename, mode in dir.dirtree( start_path ) do + if not mode then + local mask = filemask( pattern ) + if normcase(filename):find( mask ) then + files[#files + 1] = filename + end + end + end + + return files +end + +return dir diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/file.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/file.lua new file mode 100644 index 000000000..6c82e2859 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/file.lua @@ -0,0 +1,69 @@ +--- File manipulation functions: reading, writing, moving and copying. +-- @class module +-- @name pl.file +local os = os +local utils = require 'pl.utils' +local dir = require 'pl.dir' +local path = require 'pl.path' + +--[[ +module ('pl.file',utils._module) +]] +local file = {} + +--- return the contents of a file as a string +-- @class function +-- @name file.read +-- @param filename The file path +-- @return file contents +file.read = utils.readfile + +--- write a string to a file +-- @class function +-- @name file.write +-- @param filename The file path +-- @param str The string +file.write = utils.writefile + +--- copy a file. +-- @class function +-- @name file.copy +-- @param src source file +-- @param dest destination file +-- @param flag true if you want to force the copy (default) +-- @return true if operation succeeded +file.copy = dir.copyfile + +--- move a file. +-- @class function +-- @name file.move +-- @param src source file +-- @param dest destination file +-- @return true if operation succeeded, else false and the reason for the error. +file.move = dir.movefile + +--- Return the time of last access as the number of seconds since the epoch. +-- @class function +-- @name file.access_time +-- @param path A file path +file.access_time = path.getatime + +---Return when the file was created. +-- @class function +-- @name file.creation_time +-- @param path A file path +file.creation_time = path.getctime + +--- Return the time of last modification +-- @class function +-- @name file.modified_time +-- @param path A file path +file.modified_time = path.getmtime + +--- Delete a file +-- @class function +-- @name file.delete +-- @param path A file path +file.delete = os.remove + +return file diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/func.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/func.lua new file mode 100644 index 000000000..3711c9a71 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/func.lua @@ -0,0 +1,379 @@ +--- Functional helpers like composition, binding and placeholder expressions. +-- Placeholder expressions are useful for short anonymous functions, and were +-- inspired by the Boost Lambda library. +--

    +-- utils.import 'pl.func'
    +-- ls = List{10,20,30}
    +-- = ls:map(_1+1)
    +--    {11,21,31}
    +-- 
    +-- They can also be used to bind particular arguments of a function. +--
    +-- p = bind(print,'start>',_0)
    +-- p(10,20,30)
    +-- start>   10   20  30
    +-- 
    +-- See the Guide +-- @class module +-- @name pl.func +local type,select,setmetatable,getmetatable,rawset = type,select,setmetatable,getmetatable,rawset +local concat,append = table.concat,table.insert +local max = math.max +local print,tostring = print,tostring +local pairs,ipairs,loadstring,rawget,unpack = pairs,ipairs,loadstring,rawget,unpack +local _G = _G +local utils = require 'pl.utils' +local tablex = require 'pl.tablex' +local map = tablex.map +local _DEBUG = rawget(_G,'_DEBUG') +local assert_arg = utils.assert_arg + +--[[ +module ('pl.func',utils._module) +]] + +local func = {} + +-- metatable for Placeholder Expressions (PE) +local _PEMT = {} + +local function P (t) + setmetatable(t,_PEMT) + return t +end + +func.PE = P + +local function isPE (obj) + return getmetatable(obj) == _PEMT +end + +func.isPE = isPE + +-- construct a placeholder variable (e.g _1 and _2) +local function PH (idx) + return P {op='X',repr='_'..idx, index=idx} +end + +-- construct a constant placeholder variable (e.g _C1 and _C2) +local function CPH (idx) + return P {op='X',repr='_C'..idx, index=idx} +end + +func._1,func._2,func._3,func._4,func._5 = PH(1),PH(2),PH(3),PH(4),PH(5) +func._0 = P{op='X',repr='...',index=0} + +function func.Var (name) + local ls = utils.split(name,'[%s,]+') + local res = {} + for _,n in ipairs(ls) do + append(res,P{op='X',repr=n,index=0}) + end + return unpack(res) +end + +function func._ (value) + return P{op='X',repr=value,index='wrap'} +end + +local repr + +func.Nil = func.Var 'nil' + +function _PEMT.__index(obj,key) + return P{op='[]',obj,key} +end + +function _PEMT.__call(fun,...) + return P{op='()',fun,...} +end + +function _PEMT.__tostring (e) + return repr(e) +end + +function _PEMT.__unm(arg) + return P{op='-',arg} +end + +function func.Not (arg) + return P{op='not',arg} +end + +function func.Len (arg) + return P{op='#',arg} +end + + +local function binreg(context,t) + for name,op in pairs(t) do + rawset(context,name,function(x,y) + return P{op=op,x,y} + end) + end +end + +local function import_name (name,fun,context) + rawset(context,name,function(...) + return P{op='()',fun,...} + end) +end + +local imported_functions = {} + +local function is_global_table (n) + return type(_G[n]) == 'table' +end + +--- wrap a table of functions. This makes them available for use in +-- placeholder expressions. +-- @param tname a table name +-- @param context context to put results, defaults to environment of caller +function func.import(tname,context) + assert_arg(1,tname,'string',is_global_table,'arg# 1: not a name of a global table') + local t = _G[tname] + context = context or _G + for name,fun in pairs(t) do + import_name(name,fun,context) + imported_functions[fun] = name + end +end + +--- register a function for use in placeholder expressions. +-- @param fun a function +-- @param name an optional name +-- @return a placeholder functiond +function func.register (fun,name) + assert_arg(1,fun,'function') + if name then + assert_arg(2,name,'string') + imported_functions[fun] = name + end + return function(...) + return P{op='()',fun,...} + end +end + +function func.lookup_imported_name (fun) + return imported_functions[fun] +end + +local function _arg(...) return ... end + +function func.Args (...) + return P{op='()',_arg,...} +end + +-- binary and unary operators, with their precedences (see 2.5.6) +local operators = { + ['or'] = 0, + ['and'] = 1, + ['=='] = 2, ['~='] = 2, ['<'] = 2, ['>'] = 2, ['<='] = 2, ['>='] = 2, + ['..'] = 3, + ['+'] = 4, ['-'] = 4, + ['*'] = 5, ['/'] = 5, ['%'] = 5, + ['not'] = 6, ['#'] = 6, ['-'] = 6, + ['^'] = 7 +} + +-- comparisons (as prefix functions) +binreg (func,{And='and',Or='or',Eq='==',Lt='<',Gt='>',Le='<=',Ge='>='}) + +-- standard binary operators (as metamethods) +binreg (_PEMT,{__add='+',__sub='-',__mul='*',__div='/',__mod='%',__pow='^',__concat='..'}) + +binreg (_PEMT,{__eq='=='}) + +--- all elements of a table except the first. +-- @param ls a list-like table. +function func.tail (ls) + assert_arg(1,ls,'table') + local res = {} + for i = 2,#ls do + append(res,ls[i]) + end + return res +end + +--- create a string representation of a placeholder expression. +-- @param e a placeholder expression +-- @param lastpred not used +function repr (e,lastpred) + local tail = func.tail + if isPE(e) then + local pred = operators[e.op] + local ls = map(repr,e,pred) + if pred then --unary or binary operator + if #ls ~= 1 then + local s = concat(ls,' '..e.op..' ') + if lastpred and lastpred > pred then + s = '('..s..')' + end + return s + else + return e.op..' '..ls[1] + end + else -- either postfix, or a placeholder + if e.op == '[]' then + return ls[1]..'['..ls[2]..']' + elseif e.op == '()' then + local fn + if ls[1] ~= nil then -- was _args, undeclared! + fn = ls[1] + else + fn = '' + end + return fn..'('..concat(tail(ls),',')..')' + else + return e.repr + end + end + elseif type(e) == 'string' then + return '"'..e..'"' + elseif type(e) == 'function' then + local name = func.lookup_imported_name(e) + if name then return name else return tostring(e) end + else + return tostring(e) --should not really get here! + end +end +func.repr = repr + +-- collect all the non-PE values in this PE into vlist, and replace each occurence +-- with a constant PH (_C1, etc). Return the maximum placeholder index found. +local collect_values +function collect_values (e,vlist) + if isPE(e) then + if e.op ~= 'X' then + local m = 0 + for i,subx in ipairs(e) do + local pe = isPE(subx) + if pe then + if subx.op == 'X' and subx.index == 'wrap' then + subx = subx.repr + pe = false + else + m = max(m,collect_values(subx,vlist)) + end + end + if not pe then + append(vlist,subx) + e[i] = CPH(#vlist) + end + end + return m + else -- was a placeholder, it has an index... + return e.index + end + else -- plain value has no placeholder dependence + return 0 + end +end +func.collect_values = collect_values + +--- instantiate a PE into an actual function. First we find the largest placeholder used, +-- e.g. _2; from this a list of the formal parameters can be build. Then we collect and replace +-- any non-PE values from the PE, and build up a constant binding list. +-- Finally, the expression can be compiled, and e.__PE_function is set. +-- @param e a placeholder expression +-- @return a function +function func.instantiate (e) + local consts,values,parms = {},{},{} + local rep, err, fun + local n = func.collect_values(e,values) + for i = 1,#values do + append(consts,'_C'..i) + if _DEBUG then print(i,values[i]) end + end + for i =1,n do + append(parms,'_'..i) + end + consts = concat(consts,',') + parms = concat(parms,',') + rep = repr(e) + local fstr = ('return function(%s) return function(%s) return %s end end'):format(consts,parms,rep) + if _DEBUG then print(fstr) end + fun,err = loadstring(fstr,'fun') + if not fun then return nil,err end + fun = fun() -- get wrapper + fun = fun(unpack(values)) -- call wrapper (values could be empty) + e.__PE_function = fun + return fun +end + +--- instantiate a PE unless it has already been done. +-- @param e a placeholder expression +-- @return the function +function func.I(e) + if rawget(e,'__PE_function') then + return e.__PE_function + else return func.instantiate(e) + end +end + +utils.add_function_factory(_PEMT,func.I) + +--- bind the first parameter of the function to a value. +-- @class function +-- @name func.curry +-- @param fn a function of one or more arguments +-- @param p a value +-- @return a function of one less argument +-- @usage (curry(math.max,10))(20) == math.max(10,20) +func.curry = utils.bind1 + +--- create a function which chains two functions. +-- @param f a function of at least one argument +-- @param g a function of at least one argument +-- @return a function +-- @usage printf = compose(io.write,string.format) +function func.compose (f,g) + return function(...) return f(g(...)) end +end + +--- bind the arguments of a function to given values. +-- bind(fn,v,_2) is equivalent to curry(fn,v). +-- @param fn a function of at least one argument +-- @param ... values or placeholder variables +-- @return a function +-- @usage (bind(f,_1,a))(b) == f(a,b) +-- @usage (bind(f,_2,_1))(a,b) == f(b,a) +function func.bind(fn,...) + local args = table.pack(...) + local holders,parms,bvalues,values = {},{},{'fn'},{} + local nv,maxplace,varargs = 1,0,false + for i = 1,args.n do + local a = args[i] + if isPE(a) and a.op == 'X' then + append(holders,a.repr) + maxplace = max(maxplace,a.index) + if a.index == 0 then varargs = true end + else + local v = '_v'..nv + append(bvalues,v) + append(holders,v) + append(values,a) + nv = nv + 1 + end + end + for np = 1,maxplace do + append(parms,'_'..np) + end + if varargs then append(parms,'...') end + bvalues = concat(bvalues,',') + parms = concat(parms,',') + holders = concat(holders,',') + local fstr = ([[ +return function (%s) + return function(%s) return fn(%s) end +end +]]):format(bvalues,parms,holders) + if _DEBUG then print(fstr) end + local res,err = loadstring(fstr) + res = res() + return res(fn,unpack(values)) +end + +return func + + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/init.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/init.lua new file mode 100644 index 000000000..ee336e57c --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/init.lua @@ -0,0 +1,47 @@ +-------------- +-- entry point for loading all PL libraries only on demand. +-- Requiring 'pl' means that whenever a module is accesssed (e.g. utils.split) +-- then that module is dynamically loaded. The submodules are all brought into +-- the global space. +-- @class module +-- @name pl + +local modules = { + utils = true,path=true,dir=true,tablex=true,stringio=true,sip=true, + input=true,seq=true,lexer=true,stringx=true, + config=true,pretty=true,data=true,func=true,text=true, + operator=true,lapp=true,array2d=true, + comprehension=true,xml=true, + test = true, app = true, file = true, class = true, List = true, + Map = true, Set = true, OrderedMap = true, MultiMap = true, + Date = true, + -- classes -- +} +_G.utils = require 'pl.utils' + +for name,klass in pairs(_G.utils.stdmt) do + klass.__index = function(t,key) + return require ('pl.'..name)[key] + end; +end + +local _hook +setmetatable(_G,{ + hook = function(handler) + _hook = handler + end, + __index = function(t,name) + local found = modules[name] + -- either true, or the name of the module containing this class. + -- either way, we load the required module and make it globally available. + if found then + -- e..g pretty.dump causes pl.pretty to become available as 'pretty' + rawset(_G,name,require('pl.'..name)) + return _G[name] + elseif _hook then + return _hook(t,name) + end + end +}) + +if _G.PENLIGHT_STRICT then require 'pl.strict' end diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/input.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/input.lua new file mode 100644 index 000000000..ea566f470 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/input.lua @@ -0,0 +1,172 @@ +--- Iterators for extracting words or numbers from an input source. +--
    +--    require 'pl'
    +--    local total,n = seq.sum(input.numbers())
    +--    print('average',total/n)
    +-- 
    +--

    See here +-- @class module +-- @name pl.input +local strfind = string.find +local strsub = string.sub +local strmatch = string.match +local pairs,type,unpack,tonumber = pairs,type,unpack,tonumber +local utils = require 'pl.utils' +local patterns = utils.patterns +local io = io +local assert_arg = utils.assert_arg + +--[[ +module ('pl.input',utils._module) +]] + +local input = {} + +--- create an iterator over all tokens. +-- based on allwords from PiL, 7.1 +-- @param getter any function that returns a line of text +-- @param pattern +-- @param fn Optionally can pass a function to process each token as it/s found. +-- @return an iterator +function input.alltokens (getter,pattern,fn) + local line = getter() -- current line + local pos = 1 -- current position in the line + return function () -- iterator function + while line do -- repeat while there are lines + local s, e = strfind(line, pattern, pos) + if s then -- found a word? + pos = e + 1 -- next position is after this token + local res = strsub(line, s, e) -- return the token + if fn then res = fn(res) end + return res + else + line = getter() -- token not found; try next line + pos = 1 -- restart from first position + end + end + return nil -- no more lines: end of traversal + end +end +local alltokens = input.alltokens + +-- question: shd this _split_ a string containing line feeds? + +--- create a function which grabs the next value from a source. If the source is a string, then the getter +-- will return the string and thereafter return nil. If not specified then the source is assumed to be stdin. +-- @param f a string or a file-like object (i.e. has a read() method which returns the next line) +-- @return a getter function +function input.create_getter(f) + if f then + if type(f) == 'string' then + local ls = utils.split(f,'\n') + local i,n = 0,#ls + return function() + i = i + 1 + if i > n then return nil end + return ls[i] + end + else + -- anything that supports the read() method! + if not f.read then error('not a file-like object') end + return function() return f:read() end + end + else + return io.read -- i.e. just read from stdin + end +end + +--- generate a sequence of numbers from a source. +-- @param f A source +-- @return An iterator +function input.numbers(f) + return alltokens(input.create_getter(f), + '('..patterns.FLOAT..')',tonumber) +end + +--- generate a sequence of words from a source. +-- @param f A source +-- @return An iterator +function input.words(f) + return alltokens(input.create_getter(f),"%w+") +end + +local function apply_tonumber (no_fail,...) + local args = {...} + for i = 1,#args do + local n = tonumber(args[i]) + if n == nil then + if not no_fail then return nil,args[i] end + else + args[i] = n + end + end + return args +end + +--- parse an input source into fields. +-- By default, will fail if it cannot convert a field to a number. +-- @param ids a list of field indices, or a maximum field index +-- @param delim delimiter to parse fields (default space) +-- @param f a source @see create_getter +-- @param opts option table, {no_fail=true} +-- @return an iterator with the field values +-- @usage for x,y in fields {2,3} do print(x,y) end -- 2nd and 3rd fields from stdin +function input.fields (ids,delim,f,opts) + local sep + local s + local getter = input.create_getter(f) + local no_fail = opts and opts.no_fail + local no_convert = opts and opts.no_convert + if not delim or delim == ' ' then + delim = '%s' + sep = '%s+' + s = '%s*' + else + sep = delim + s = '' + end + local max_id = 0 + if type(ids) == 'table' then + for i,id in pairs(ids) do + if id > max_id then max_id = id end + end + else + max_id = ids + ids = {} + for i = 1,max_id do ids[#ids+1] = i end + end + local pat = '[^'..delim..']*' + local k = 1 + for i = 1,max_id do + if ids[k] == i then + k = k + 1 + s = s..'('..pat..')' + else + s = s..pat + end + if i < max_id then + s = s..sep + end + end + local linecount = 1 + return function() + local line,results,err + repeat + line = getter() + linecount = linecount + 1 + if not line then return nil end + if no_convert then + results = {strmatch(line,s)} + else + results,err = apply_tonumber(no_fail,strmatch(line,s)) + if not results then + utils.quit("line "..(linecount-1)..": cannot convert '"..err.."' to number") + end + end + until #results > 0 + return unpack(results) + end +end + +return input + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/lapp.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/lapp.lua new file mode 100644 index 000000000..80a81294a --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/lapp.lua @@ -0,0 +1,350 @@ +--- Simple command-line parsing using human-readable specification. +-- Supports GNU-style parameters. +--

    +--      lapp = require 'pl.lapp'
    +--      local args = lapp [[
    +--      Does some calculations
    +--        -o,--offset (default 0.0)  Offset to add to scaled number
    +--        -s,--scale  (number)  Scaling factor
    +--         <number> (number )  Number to be scaled
    +--      ]]
    +--
    +--      print(args.offset + args.scale * args.number)
    +-- 
    +-- Lines begining with '-' are flags; there may be a short and a long name; +-- lines begining wih '<var>' are arguments. Anything in parens after +-- the flag/argument is either a default, a type name or a range constraint. +--

    See the Guide +-- @class module +-- @name pl.lapp + +local status,sip = pcall(require,'pl.sip') +if not status then + sip = require 'sip' +end +local match = sip.match_at_start +local append,tinsert = table.insert,table.insert + +--[[ +module('pl.lapp') +]] + +local function lines(s) return s:gmatch('([^\n]*)\n') end +local function lstrip(str) return str:gsub('^%s+','') end +local function strip(str) return lstrip(str):gsub('%s+$','') end +local function at(s,k) return s:sub(k,k) end +local function isdigit(s) return s:find('^%d+$') == 1 end + +local lapp = {} + +local open_files,parms,aliases,parmlist,usage,windows,script + +lapp.callback = false -- keep Strict happy + +local filetypes = { + stdin = {io.stdin,'file-in'}, stdout = {io.stdout,'file-out'}, + stderr = {io.stderr,'file-out'} +} + +--- controls whether to dump usage on error. +-- Defaults to true +lapp.show_usage_error = true + +--- quit this script immediately. +-- @param msg optional message +-- @param no_usage suppress 'usage' display +function lapp.quit(msg,no_usage) + if msg then + io.stderr:write(msg..'\n\n') + end + if not no_usage then + io.stderr:write(usage) + end + os.exit(1); +end + +--- print an error to stderr and quit. +-- @param msg a message +-- @param no_usage suppress 'usage' display +function lapp.error(msg,no_usage) + if not lapp.show_usage_error then + no_usage = true + end + lapp.quit(script..':'..msg,no_usage) +end + +--- open a file. +-- This will quit on error, and keep a list of file objects for later cleanup. +-- @param file filename +-- @param opt same as second parameter of io.open +function lapp.open (file,opt) + local val,err = io.open(file,opt) + if not val then lapp.error(err,true) end + append(open_files,val) + return val +end + +--- quit if the condition is false. +-- @param condn a condition +-- @param msg an optional message +function lapp.assert(condn,msg) + if not condn then + lapp.error(msg) + end +end + +local function range_check(x,min,max,parm) + lapp.assert(min <= x and max >= x,parm..' out of range') +end + +local function xtonumber(s) + local val = tonumber(s) + if not val then lapp.error("unable to convert to number: "..s) end + return val +end + +local function is_filetype(type) + return type == 'file-in' or type == 'file-out' +end + +local types + +local function convert_parameter(ps,val) + if ps.converter then + val = ps.converter(val) + end + if ps.type == 'number' then + val = xtonumber(val) + elseif is_filetype(ps.type) then + val = lapp.open(val,(ps.type == 'file-in' and 'r') or 'w' ) + elseif ps.type == 'boolean' then + val = true + end + if ps.constraint then + ps.constraint(val) + end + return val +end + +--- add a new type to Lapp. These appear in parens after the value like +-- a range constraint, e.g. ' (integer) Process PID' +-- @param name name of type +-- @param converter either a function to convert values, or a Lua type name. +-- @param constraint optional function to verify values, should use lapp.error +-- if failed. +function lapp.add_type (name,converter,constraint) + types[name] = {converter=converter,constraint=constraint} +end + +local function force_short(short) + lapp.assert(#short==1,short..": short parameters should be one character") +end + +local function process_default (sval) + local val = tonumber(sval) + if val then -- we have a number! + return val,'number' + elseif filetypes[sval] then + local ft = filetypes[sval] + return ft[1],ft[2] + else + if sval:match '^["\']' then sval = sval:sub(2,-2) end + return sval,'string' + end +end + +--- process a Lapp options string. +-- Usually called as lapp(). +-- @param str the options text +-- @return a table with parameter-value pairs +function lapp.process_options_string(str) + local results = {} + local opts = {at_start=true} + local varargs + open_files = {} + parms = {} + aliases = {} + parmlist = {} + types = {} + + local function check_varargs(s) + local res,cnt = s:gsub('^%.%.%.%s*','') + return res, (cnt > 0) + end + + local function set_result(ps,parm,val) + if not ps.varargs then + results[parm] = val + else + if not results[parm] then + results[parm] = { val } + else + append(results[parm],val) + end + end + end + + usage = str + + for line in lines(str) do + local res = {} + local optspec,optparm,i1,i2,defval,vtype,constraint,rest + line = lstrip(line) + local function check(str) + return match(str,line,res) + end + + -- flags: either '-', '-,--' or '--' + if check '-$v{short}, --$v{long} $' or check '-$v{short} $' or check '--$v{long} $' then + if res.long then + optparm = res.long + if res.short then aliases[res.short] = optparm end + else + optparm = res.short + end + if res.short then force_short(res.short) end + res.rest, varargs = check_varargs(res.rest) + elseif check '$<{name} $' then -- is it ? + -- so becomes input_file ... + optparm,rest = res.name:match '([^%.]+)(.*)' + optparm = optparm:gsub('%A','_') + varargs = rest == '...' + append(parmlist,optparm) + end + if res.rest then -- this is not a pure doc line + line = res.rest + res = {} + -- do we have (default ) or ()? + if match('$({def} $',line,res) or match('$({def}',line,res) then + local typespec = strip(res.def) + if match('default $',typespec,res) then + defval,vtype = process_default(res[1]) + elseif match('$f{min}..$f{max}',typespec,res) then + local min,max = res.min,res.max + vtype = 'number' + constraint = function(x) + range_check(x,min,max,optparm) + end + else -- () just contains type of required parameter + vtype = typespec + end + else -- must be a plain flag, no extra parameter required + defval = false + vtype = 'boolean' + end + local ps = { + type = vtype, + defval = defval, + required = defval == nil, + comment = res.rest or optparm, + constraint = constraint, + varargs = varargs + } + varargs = nil + if types[vtype] then + local converter = types[vtype].converter + if type(converter) == 'string' then + ps.type = converter + else + ps.converter = converter + end + ps.constraint = types[vtype].constraint + end + parms[optparm] = ps + end + end + -- cool, we have our parms, let's parse the command line args + local iparm = 1 + local iextra = 1 + local i = 1 + local parm,ps,val + + while i <= #arg do + local theArg = arg[i] + local res = {} + -- look for a flag, - or -- + if match('--$v{long}',theArg,res) or match('-$v{short}',theArg,res) then + if res.long then -- long option + parm = res.long + elseif #res.short == 1 then + parm = res.short + else + local parmstr = res.short + parm = at(parmstr,1) + if isdigit(at(parmstr,2)) then + -- a short option followed by a digit is an exception (for AW;)) + -- push ahead into the arg array + tinsert(arg,i+1,parmstr:sub(2)) + else + -- push multiple flags into the arg array! + for k = 2,#parmstr do + tinsert(arg,i+k-1,'-'..at(parmstr,k)) + end + end + end + if parm == 'h' or parm == 'help' then + lapp.quit() + end + if aliases[parm] then parm = aliases[parm] end + else -- a parameter + parm = parmlist[iparm] + if not parm then + -- extra unnamed parameters are indexed starting at 1 + parm = iextra + ps = { type = 'string' } + parms[parm] = ps + iextra = iextra + 1 + else + ps = parms[parm] + end + if not ps.varargs then + iparm = iparm + 1 + end + val = theArg + end + ps = parms[parm] + if not ps then lapp.error("unrecognized parameter: "..parm) end + if ps.type ~= 'boolean' then -- we need a value! This should follow + if not val then + i = i + 1 + val = arg[i] + end + lapp.assert(val,parm.." was expecting a value") + end + ps.used = true + val = convert_parameter(ps,val) + set_result(ps,parm,val) + if is_filetype(ps.type) then + set_result(ps,parm..'_name',theArg) + end + if lapp.callback then + lapp.callback(parm,theArg,res) + end + i = i + 1 + val = nil + end + -- check unused parms, set defaults and check if any required parameters were missed + for parm,ps in pairs(parms) do + if not ps.used then + if ps.required then lapp.error("missing required parameter: "..parm) end + set_result(ps,parm,ps.defval) + end + end + return results +end + +if arg then + script = arg[0]:gsub('.+[\\/]',''):gsub('%.%a+$','') +else + script = "inter" +end + + +setmetatable(lapp, { + __call = function(tbl,str) return lapp.process_options_string(str) end, +}) + + +return lapp + + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/lexer.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/lexer.lua new file mode 100644 index 000000000..5b9fa0c80 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/lexer.lua @@ -0,0 +1,461 @@ +--- Lexical scanner for creating a sequence of tokens from text.
    +--

    lexer.scan(s) returns an iterator over all tokens found in the +-- string s. This iterator returns two values, a token type string +-- (such as 'string' for quoted string, 'iden' for identifier) and the value of the +-- token. +--

    +-- Versions specialized for Lua and C are available; these also handle block comments +-- and classify keywords as 'keyword' tokens. For example: +--

    +-- > s = 'for i=1,n do'
    +-- > for t,v in lexer.lua(s)  do print(t,v) end
    +-- keyword for
    +-- iden    i
    +-- =       =
    +-- number  1
    +-- ,       ,
    +-- iden    n
    +-- keyword do
    +-- 
    +-- See the Guide for further discussion
    +-- @class module +-- @name pl.lexer + +local yield,wrap = coroutine.yield,coroutine.wrap +local strfind = string.find +local strsub = string.sub +local append = table.insert +--[[ +module ('pl.lexer',utils._module) +]] + +local function assert_arg(idx,val,tp) + if type(val) ~= tp then + error("argument "..idx.." must be "..tp, 2) + end +end + +local lexer = {} + +local NUMBER1 = '^[%+%-]?%d+%.?%d*[eE][%+%-]?%d+' +local NUMBER2 = '^[%+%-]?%d+%.?%d*' +local NUMBER3 = '^0x[%da-fA-F]+' +local NUMBER4 = '^%d+%.?%d*[eE][%+%-]?%d+' +local NUMBER5 = '^%d+%.?%d*' +local IDEN = '^[%a_][%w_]*' +local WSPACE = '^%s+' +local STRING1 = [[^'.-[^\\]']] +local STRING2 = [[^".-[^\\]"]] +local STRING3 = "^((['\"])%2)" -- empty string +local PREPRO = '^#.-[^\\]\n' + +local plain_matches,lua_matches,cpp_matches,lua_keyword,cpp_keyword + +local function tdump(tok) + return yield(tok,tok) +end + +local function ndump(tok,options) + if options and options.number then + tok = tonumber(tok) + end + return yield("number",tok) +end + +-- regular strings, single or double quotes; usually we want them +-- without the quotes +local function sdump(tok,options) + if options and options.string then + tok = tok:sub(2,-2) + end + return yield("string",tok) +end + +-- long Lua strings need extra work to get rid of the quotes +local function sdump_l(tok,options) + if options and options.string then + tok = tok:sub(3,-3) + end + return yield("string",tok) +end + +local function chdump(tok,options) + if options and options.string then + tok = tok:sub(2,-2) + end + return yield("char",tok) +end + +local function cdump(tok) + return yield('comment',tok) +end + +local function wsdump (tok) + return yield("space",tok) +end + +local function pdump (tok) + return yield('prepro',tok) +end + +local function plain_vdump(tok) + return yield("iden",tok) +end + +local function lua_vdump(tok) + if lua_keyword[tok] then + return yield("keyword",tok) + else + return yield("iden",tok) + end +end + +local function cpp_vdump(tok) + if cpp_keyword[tok] then + return yield("keyword",tok) + else + return yield("iden",tok) + end +end + +--- create a plain token iterator from a string or file-like object. +-- @param s the string +-- @param matches an optional match table (set of pattern-action pairs) +-- @param filter a table of token types to exclude, by default {space=true} +-- @param options a table of options; by default, {number=true,string=true}, +-- which means convert numbers and strip string quotes. +function lexer.scan (s,matches,filter,options) + --assert_arg(1,s,'string') + local file = type(s) ~= 'string' and s + filter = filter or {space=true} + options = options or {number=true,string=true} + if filter then + if filter.space then filter[wsdump] = true end + if filter.comments then + filter[cdump] = true + end + end + if not matches then + if not plain_matches then + plain_matches = { + {WSPACE,wsdump}, + {NUMBER3,ndump}, + {IDEN,plain_vdump}, + {NUMBER1,ndump}, + {NUMBER2,ndump}, + {STRING3,sdump}, + {STRING1,sdump}, + {STRING2,sdump}, + {'^.',tdump} + } + end + matches = plain_matches + end + local function lex () + local i1,i2,idx,res1,res2,tok,pat,fun,capt + local line = 1 + if file then s = file:read()..'\n' end + local sz = #s + local idx = 1 + --print('sz',sz) + while true do + for _,m in ipairs(matches) do + pat = m[1] + fun = m[2] + i1,i2 = strfind(s,pat,idx) + if i1 then + tok = strsub(s,i1,i2) + idx = i2 + 1 + if not (filter and filter[fun]) then + lexer.finished = idx > sz + res1,res2 = fun(tok,options) + end + if res1 then + local tp = type(res1) + -- insert a token list + if tp=='table' then + yield('','') + for _,t in ipairs(res1) do + yield(t[1],t[2]) + end + elseif tp == 'string' then -- or search up to some special pattern + i1,i2 = strfind(s,res1,idx) + if i1 then + tok = strsub(s,i1,i2) + idx = i2 + 1 + yield('',tok) + else + yield('','') + idx = sz + 1 + end + --if idx > sz then return end + else + yield(line,idx) + end + end + if idx > sz then + if file then + --repeat -- next non-empty line + line = line + 1 + s = file:read() + if not s then return end + --until not s:match '^%s*$' + s = s .. '\n' + idx ,sz = 1,#s + break + else + return + end + else break end + end + end + end + end + return wrap(lex) +end + +local function isstring (s) + return type(s) == 'string' +end + +--- insert tokens into a stream. +-- @param tok a token stream +-- @param a1 a string is the type, a table is a token list and +-- a function is assumed to be a token-like iterator (returns type & value) +-- @param a2 a string is the value +function lexer.insert (tok,a1,a2) + if not a1 then return end + local ts + if isstring(a1) and isstring(a2) then + ts = {{a1,a2}} + elseif type(a1) == 'function' then + ts = {} + for t,v in a1() do + append(ts,{t,v}) + end + else + ts = a1 + end + tok(ts) +end + +--- get everything in a stream upto a newline. +-- @param tok a token stream +-- @return a string +function lexer.getline (tok) + local t,v = tok('.-\n') + return v +end + +--- get current line number.
    +-- Only available if the input source is a file-like object. +-- @param tok a token stream +-- @return the line number and current column +function lexer.lineno (tok) + return tok(0) +end + +--- get the rest of the stream. +-- @param tok a token stream +-- @return a string +function lexer.getrest (tok) + local t,v = tok('.+') + return v +end + +--- get the Lua keywords as a set-like table. +-- So res["and"] etc would be true. +-- @return a table +function lexer.get_keywords () + if not lua_keyword then + lua_keyword = { + ["and"] = true, ["break"] = true, ["do"] = true, + ["else"] = true, ["elseif"] = true, ["end"] = true, + ["false"] = true, ["for"] = true, ["function"] = true, + ["if"] = true, ["in"] = true, ["local"] = true, ["nil"] = true, + ["not"] = true, ["or"] = true, ["repeat"] = true, + ["return"] = true, ["then"] = true, ["true"] = true, + ["until"] = true, ["while"] = true + } + end + return lua_keyword +end + + +--- create a Lua token iterator from a string or file-like object. +-- Will return the token type and value. +-- @param s the string +-- @param filter a table of token types to exclude, by default {space=true,comments=true} +-- @param options a table of options; by default, {number=true,string=true}, +-- which means convert numbers and strip string quotes. +function lexer.lua(s,filter,options) + filter = filter or {space=true,comments=true} + lexer.get_keywords() + if not lua_matches then + lua_matches = { + {WSPACE,wsdump}, + {NUMBER3,ndump}, + {IDEN,lua_vdump}, + {NUMBER4,ndump}, + {NUMBER5,ndump}, + {STRING3,sdump}, + {STRING1,sdump}, + {STRING2,sdump}, + {'^%-%-%[%[.-%]%]',cdump}, + {'^%-%-.-\n',cdump}, + {'^%[%[.-%]%]',sdump_l}, + {'^==',tdump}, + {'^~=',tdump}, + {'^<=',tdump}, + {'^>=',tdump}, + {'^%.%.%.',tdump}, + {'^%.%.',tdump}, + {'^.',tdump} + } + end + return lexer.scan(s,lua_matches,filter,options) +end + +--- create a C/C++ token iterator from a string or file-like object. +-- Will return the token type type and value. +-- @param s the string +-- @param filter a table of token types to exclude, by default {space=true,comments=true} +-- @param options a table of options; by default, {number=true,string=true}, +-- which means convert numbers and strip string quotes. +function lexer.cpp(s,filter,options) + filter = filter or {comments=true} + if not cpp_keyword then + cpp_keyword = { + ["class"] = true, ["break"] = true, ["do"] = true, ["sizeof"] = true, + ["else"] = true, ["continue"] = true, ["struct"] = true, + ["false"] = true, ["for"] = true, ["public"] = true, ["void"] = true, + ["private"] = true, ["protected"] = true, ["goto"] = true, + ["if"] = true, ["static"] = true, ["const"] = true, ["typedef"] = true, + ["enum"] = true, ["char"] = true, ["int"] = true, ["bool"] = true, + ["long"] = true, ["float"] = true, ["true"] = true, ["delete"] = true, + ["double"] = true, ["while"] = true, ["new"] = true, + ["namespace"] = true, ["try"] = true, ["catch"] = true, + ["switch"] = true, ["case"] = true, ["extern"] = true, + ["return"] = true,["default"] = true,['unsigned'] = true,['signed'] = true, + ["union"] = true, ["volatile"] = true, ["register"] = true,["short"] = true, + } + end + if not cpp_matches then + cpp_matches = { + {WSPACE,wsdump}, + {PREPRO,pdump}, + {NUMBER3,ndump}, + {IDEN,cpp_vdump}, + {NUMBER4,ndump}, + {NUMBER5,ndump}, + {STRING3,sdump}, + {STRING1,chdump}, + {STRING2,sdump}, + {'^//.-\n',cdump}, + {'^/%*.-%*/',cdump}, + {'^==',tdump}, + {'^!=',tdump}, + {'^<=',tdump}, + {'^>=',tdump}, + {'^->',tdump}, + {'^&&',tdump}, + {'^||',tdump}, + {'^%+%+',tdump}, + {'^%-%-',tdump}, + {'^%+=',tdump}, + {'^%-=',tdump}, + {'^%*=',tdump}, + {'^/=',tdump}, + {'^|=',tdump}, + {'^%^=',tdump}, + {'^::',tdump}, + {'^.',tdump} + } + end + return lexer.scan(s,cpp_matches,filter,options) +end + +--- get a list of parameters separated by a delimiter from a stream. +-- @param tok the token stream +-- @param endtoken end of list (default ')'). Can be '\n' +-- @param delim separator (default ',') +-- @return a list of token lists. +function lexer.get_separated_list(tok,endtoken,delim) + endtoken = endtoken or ')' + delim = delim or ',' + local parm_values = {} + local level = 1 -- used to count ( and ) + local tl = {} + local function tappend (tl,t,val) + val = val or t + append(tl,{t,val}) + end + local is_end + if endtoken == '\n' then + is_end = function(t,val) + return t == 'space' and val:find '\n' + end + else + is_end = function (t) + return t == endtoken + end + end + local token,value + while true do + token,value=tok() + if not token then return nil,'EOS' end -- end of stream is an error! + if is_end(token,value) and level == 1 then + append(parm_values,tl) + break + elseif token == '(' then + level = level + 1 + tappend(tl,'(') + elseif token == ')' then + level = level - 1 + if level == 0 then -- finished with parm list + append(parm_values,tl) + break + else + tappend(tl,')') + end + elseif token == delim and level == 1 then + append(parm_values,tl) -- a new parm + tl = {} + else + tappend(tl,token,value) + end + end + return parm_values,{token,value} +end + +--- get the next non-space token from the stream. +-- @param tok the token stream. +function lexer.skipws (tok) + local t,v = tok() + while t == 'space' do + t,v = tok() + end + return t,v +end + +local skipws = lexer.skipws + +--- get the next token, which must be of the expected type. +-- Throws an error if this type does not match! +-- @param tok the token stream +-- @param expected_type the token type +-- @param no_skip_ws whether we should skip whitespace +function lexer.expecting (tok,expected_type,no_skip_ws) + assert_arg(1,tok,'function') + assert_arg(2,expected_type,'string') + local t,v + if no_skip_ws then + t,v = tok() + else + t,v = skipws(tok) + end + if t ~= expected_type then error ("expecting "..expected_type,2) end + return v +end + +return lexer diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/luabalanced.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/luabalanced.lua new file mode 100644 index 000000000..bb2377836 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/luabalanced.lua @@ -0,0 +1,264 @@ +--- Extract delimited Lua sequences from strings. +-- Inspired by Damian Conway's Text::Balanced in Perl.
    +--
      +--
    • [1] Lua Wiki Page
    • +--
    • [2] http://search.cpan.org/dist/Text-Balanced/lib/Text/Balanced.pm
    • +--

    +--
    +-- local lb = require "pl.luabalanced"
    +-- --Extract Lua expression starting at position 4.
    +--  print(lb.match_expression("if x^2 + x > 5 then print(x) end", 4))
    +--  --> x^2 + x > 5     16
    +-- --Extract Lua string starting at (default) position 1.
    +-- print(lb.match_string([["test\"123" .. "more"]]))
    +-- --> "test\"123"     12
    +-- 
    +-- (c) 2008, David Manura, Licensed under the same terms as Lua (MIT license). +-- @class module +-- @name pl.luabalanced + +local M = {} + +local assert = assert +local table_concat = table.concat + +-- map opening brace <-> closing brace. +local ends = { ['('] = ')', ['{'] = '}', ['['] = ']' } +local begins = {}; for k,v in pairs(ends) do begins[v] = k end + + +-- Match Lua string in string starting at position . +-- Returns , , where is the matched +-- string (or nil on no match) and is the character +-- following the match (or on no match). +-- Supports all Lua string syntax: "...", '...', [[...]], [=[...]=], etc. +local function match_string(s, pos) + pos = pos or 1 + local posa = pos + local c = s:sub(pos,pos) + if c == '"' or c == "'" then + pos = pos + 1 + while 1 do + pos = assert(s:find("[" .. c .. "\\]", pos), 'syntax error') + if s:sub(pos,pos) == c then + local part = s:sub(posa, pos) + return part, pos + 1 + else + pos = pos + 2 + end + end + else + local sc = s:match("^%[(=*)%[", pos) + if sc then + local _; _, pos = s:find("%]" .. sc .. "%]", pos) + assert(pos) + local part = s:sub(posa, pos) + return part, pos + 1 + else + return nil, pos + end + end +end +M.match_string = match_string + + +-- Match bracketed Lua expression, e.g. "(...)", "{...}", "[...]", "[[...]]", +-- [=[...]=], etc. +-- Function interface is similar to match_string. +local function match_bracketed(s, pos) + pos = pos or 1 + local posa = pos + local ca = s:sub(pos,pos) + if not ends[ca] then + return nil, pos + end + local stack = {} + while 1 do + pos = s:find('[%(%{%[%)%}%]\"\']', pos) + assert(pos, 'syntax error: unbalanced') + local c = s:sub(pos,pos) + if c == '"' or c == "'" then + local part; part, pos = match_string(s, pos) + assert(part) + elseif ends[c] then -- open + local mid, posb + if c == '[' then mid, posb = s:match('^%[(=*)%[()', pos) end + if mid then + pos = s:match('%]' .. mid .. '%]()', posb) + assert(pos, 'syntax error: long string not terminated') + if #stack == 0 then + local part = s:sub(posa, pos-1) + return part, pos + end + else + stack[#stack+1] = c + pos = pos + 1 + end + else -- close + assert(stack[#stack] == assert(begins[c]), 'syntax error: unbalanced') + stack[#stack] = nil + if #stack == 0 then + local part = s:sub(posa, pos) + return part, pos+1 + end + pos = pos + 1 + end + end +end +M.match_bracketed = match_bracketed + + +-- Match Lua comment, e.g. "--...\n", "--[[...]]", "--[=[...]=]", etc. +-- Function interface is similar to match_string. +local function match_comment(s, pos) + pos = pos or 1 + if s:sub(pos, pos+1) ~= '--' then + return nil, pos + end + pos = pos + 2 + local partt, post = match_string(s, pos) + if partt then + return '--' .. partt, post + end + local part; part, pos = s:match('^([^\n]*\n?)()', pos) + return '--' .. part, pos +end + + +-- Match Lua expression, e.g. "a + b * c[e]". +-- Function interface is similar to match_string. +local wordop = {['and']=true, ['or']=true, ['not']=true} +local is_compare = {['>']=true, ['<']=true, ['~']=true} +local function match_expression(s, pos) + pos = pos or 1 + local posa = pos + local lastident + local poscs, posce + while pos do + local c = s:sub(pos,pos) + if c == '"' or c == "'" or c == '[' and s:find('^[=%[]', pos+1) then + local part; part, pos = match_string(s, pos) + assert(part, 'syntax error') + elseif c == '-' and s:sub(pos+1,pos+1) == '-' then + -- note: handle adjacent comments in loop to properly support + -- backtracing (poscs/posce). + poscs = pos + while s:sub(pos,pos+1) == '--' do + local part; part, pos = match_comment(s, pos) + assert(part) + pos = s:match('^%s*()', pos) + posce = pos + end + elseif c == '(' or c == '{' or c == '[' then + local part; part, pos = match_bracketed(s, pos) + elseif c == '=' and s:sub(pos+1,pos+1) == '=' then + pos = pos + 2 -- skip over two-char op containing '=' + elseif c == '=' and is_compare[s:sub(pos-1,pos-1)] then + pos = pos + 1 -- skip over two-char op containing '=' + elseif c:match'^[%)%}%];,=]' then + local part = s:sub(posa, pos-1) + return part, pos + elseif c:match'^[%w_]' then + local newident,newpos = s:match('^([%w_]+)()', pos) + if pos ~= posa and not wordop[newident] then -- non-first ident + local pose = ((posce == pos) and poscs or pos) - 1 + while s:match('^%s', pose) do pose = pose - 1 end + local ce = s:sub(pose,pose) + if ce:match'[%)%}\'\"%]]' or + ce:match'[%w_]' and not wordop[lastident] + then + local part = s:sub(posa, pos-1) + return part, pos + end + end + lastident, pos = newident, newpos + else + pos = pos + 1 + end + pos = s:find('[%(%{%[%)%}%]\"\';,=%w_%-]', pos) + end + local part = s:sub(posa, #s) + return part, #s+1 +end +M.match_expression = match_expression + + +-- Match name list (zero or more names). E.g. "a,b,c" +-- Function interface is similar to match_string, +-- but returns array as match. +local function match_namelist(s, pos) + pos = pos or 1 + local list = {} + while 1 do + local c = #list == 0 and '^' or '^%s*,%s*' + local item, post = s:match(c .. '([%a_][%w_]*)%s*()', pos) + if item then pos = post else break end + list[#list+1] = item + end + return list, pos +end +M.match_namelist = match_namelist + + +-- Match expression list (zero or more expressions). E.g. "a+b,b*c". +-- Function interface is similar to match_string, +-- but returns array as match. +local function match_explist(s, pos) + pos = pos or 1 + local list = {} + while 1 do + if #list ~= 0 then + local post = s:match('^%s*,%s*()', pos) + if post then pos = post else break end + end + local item; item, pos = match_expression(s, pos) + assert(item, 'syntax error') + list[#list+1] = item + end + return list, pos +end +M.match_explist = match_explist + + +-- Replace snippets of code in Lua code string +-- using replacement function f(u,sin) --> sout. +-- is the type of snippet ('c' = comment, 's' = string, +-- 'e' = any other code). +-- Snippet is replaced with (unless is nil or false, in +-- which case the original snippet is kept) +-- This is somewhat analogous to string.gsub . +local function gsub(s, f) + local pos = 1 + local posa = 1 + local sret = '' + while 1 do + pos = s:find('[%-\'\"%[]', pos) + if not pos then break end + if s:match('^%-%-', pos) then + local exp = s:sub(posa, pos-1) + if #exp > 0 then sret = sret .. (f('e', exp) or exp) end + local comment; comment, pos = match_comment(s, pos) + sret = sret .. (f('c', assert(comment)) or comment) + posa = pos + else + local posb = s:find('^[\'\"%[]', pos) + local str + if posb then str, pos = match_string(s, posb) end + if str then + local exp = s:sub(posa, posb-1) + if #exp > 0 then sret = sret .. (f('e', exp) or exp) end + sret = sret .. (f('s', str) or str) + posa = pos + else + pos = pos + 1 + end + end + end + local exp = s:sub(posa) + if #exp > 0 then sret = sret .. (f('e', exp) or exp) end + return sret +end +M.gsub = gsub + + +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/operator.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/operator.lua new file mode 100644 index 000000000..48c8a10b4 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/operator.lua @@ -0,0 +1,197 @@ +--- Lua operators available as functions. +-- (similar to the Python module of the same name)
    +-- There is a module field optable which maps the operator strings +-- onto these functions, e.g.
    operator.optable['()']==operator.call
    +--

    Operator strings like '>' and '{}' can be passed to most Penlight functions +-- expecting a function argument.

    +-- @class module +-- @name pl.operator + +local strfind = string.find +local utils = require 'pl.utils' + +local operator = {} + +--- apply function to some arguments () +-- @param fn a function or callable object +-- @param ... arguments +function operator.call(fn,...) + return fn(...) +end + +--- get the indexed value from a table [] +-- @param t a table or any indexable object +-- @param k the key +function operator.index(t,k) + return t[k] +end + +--- returns true if arguments are equal == +-- @param a value +-- @param b value +function operator.eq(a,b) + return a==b +end + +--- returns true if arguments are not equal ~= + -- @param a value +-- @param b value +function operator.neq(a,b) + return a~=b +end + +--- returns true if a is less than b < +-- @param a value +-- @param b value +function operator.lt(a,b) + return a < b +end + +--- returns true if a is less or equal to b <= +-- @param a value +-- @param b value +function operator.le(a,b) + return a <= b +end + +--- returns true if a is greater than b > +-- @param a value +-- @param b value +function operator.gt(a,b) + return a > b +end + +--- returns true if a is greater or equal to b >= +-- @param a value +-- @param b value +function operator.ge(a,b) + return a >= b +end + +--- returns length of string or table # +-- @param a a string or a table +function operator.len(a) + return #a +end + +--- add two values + +-- @param a value +-- @param b value +function operator.add(a,b) + return a+b +end + +--- subtract b from a - +-- @param a value +-- @param b value +function operator.sub(a,b) + return a-b +end + +--- multiply two values * +-- @param a value +-- @param b value +function operator.mul(a,b) + return a*b +end + +--- divide first value by second / +-- @param a value +-- @param b value +function operator.div(a,b) + return a/b +end + +--- raise first to the power of second ^ +-- @param a value +-- @param b value +function operator.pow(a,b) + return a^b +end + +--- modulo; remainder of a divided by b % +-- @param a value +-- @param b value +function operator.mod(a,b) + return a%b +end + +--- concatenate two values (either strings or __concat defined) .. +-- @param a value +-- @param b value +function operator.concat(a,b) + return a..b +end + +--- return the negative of a value - +-- @param a value +-- @param b value +function operator.unm(a) + return -a +end + +--- false if value evaluates as true not +-- @param a value +function operator.lnot(a) + return not a +end + +--- true if both values evaluate as true and +-- @param a value +-- @param b value +function operator.land(a,b) + return a and b +end + +--- true if either value evaluate as true or +-- @param a value +-- @param b value +function operator.lor(a,b) + return a or b +end + +--- make a table from the arguments {} +-- @param ... non-nil arguments +-- @return a table +function operator.table (...) + return {...} +end + +--- match two strings ~ +-- uses @{string.find} +function operator.match (a,b) + return strfind(a,b)~=nil +end + +--- the null operation. +-- @param ... arguments +-- @return the arguments +function operator.nop (...) + return ... +end + + operator.optable = { + ['+']=operator.add, + ['-']=operator.sub, + ['*']=operator.mul, + ['/']=operator.div, + ['%']=operator.mod, + ['^']=operator.pow, + ['..']=operator.concat, + ['()']=operator.call, + ['[]']=operator.index, + ['<']=operator.lt, + ['<=']=operator.le, + ['>']=operator.gt, + ['>=']=operator.ge, + ['==']=operator.eq, + ['~=']=operator.neq, + ['#']=operator.len, + ['and']=operator.land, + ['or']=operator.lor, + ['{}']=operator.table, + ['~']=operator.match, + ['']=operator.nop, +} + +return operator diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/path.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/path.lua new file mode 100644 index 000000000..9055f3f4b --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/path.lua @@ -0,0 +1,335 @@ +--- Path manipulation and file queries.
    +-- This is modelled after Python's os.path library (11.1) +-- @class module +-- @name pl.path + +-- imports and locals +local _G = _G +local sub = string.sub +local getenv = os.getenv +local tmpnam = os.tmpname +local attributes, currentdir, link_attrib +local package = package +local io = io +local append = table.insert +local ipairs = ipairs +local utils = require 'pl.utils' +local assert_arg,assert_string,raise = utils.assert_arg,utils.assert_string,utils.raise + +--[[ +module ('pl.path',utils._module) +]] + +local path, attrib + +if _G.luajava then + path = require 'pl.platf.luajava' +else + path = {} + + local res,lfs = _G.pcall(_G.require,'lfs') + if res then + attributes = lfs.attributes + currentdir = lfs.currentdir + link_attrib = lfs.symlinkattributes + else + error("pl.path requires LuaFileSystem") + end + + attrib = attributes + path.attrib = attrib + path.link_attrib = link_attrib + path.dir = lfs.dir + path.mkdir = lfs.mkdir + path.rmdir = lfs.rmdir + path.chdir = lfs.chdir + + --- is this a directory? + -- @param P A file path + function path.isdir(P) + if P:match("\\$") then + P = P:sub(1,-2) + end + return attrib(P,'mode') == 'directory' + end + + --- is this a file?. + -- @param P A file path + function path.isfile(P) + return attrib(P,'mode') == 'file' + end + + -- is this a symbolic link? + -- @param P A file path + function path.islink(P) + if link_attrib then + return link_attrib(P,'mode')=='link' + else + return false + end + end + + --- return size of a file. + -- @param P A file path + function path.getsize(P) + return attrib(P,'size') + end + + --- does a path exist?. + -- @param P A file path + -- @return the file path if it exists, nil otherwise + function path.exists(P) + return attrib(P,'mode') ~= nil and P + end + + --- Return the time of last access as the number of seconds since the epoch. + -- @param P A file path + function path.getatime(P) + return attrib(P,'access') + end + + --- Return the time of last modification + -- @param P A file path + function path.getmtime(P) + return attrib(P,'modification') + end + + ---Return the system's ctime. + -- @param P A file path + function path.getctime(P) + return path.attrib(P,'change') + end +end + + +local function at(s,i) + return sub(s,i,i) +end + +path.is_windows = utils.dir_separator == '\\' + +local other_sep +-- !constant sep is the directory separator for this platform. +if path.is_windows then + path.sep = '\\'; other_sep = '/' + path.dirsep = ';' +else + path.sep = '/' + path.dirsep = ':' +end +local sep,dirsep = path.sep,path.dirsep + +--- are we running Windows? +-- @class field +-- @name path.is_windows + +--- path separator for this platform. +-- @class field +-- @name path.sep + +--- separator for PATH for this platform +-- @class field +-- @name path.dirsep + +--- given a path, return the directory part and a file part. +-- if there's no directory part, the first value will be empty +-- @param P A file path +function path.splitpath(P) + assert_string(1,P) + local i = #P + local ch = at(P,i) + while i > 0 and ch ~= sep and ch ~= other_sep do + i = i - 1 + ch = at(P,i) + end + if i == 0 then + return '',P + else + return sub(P,1,i-1), sub(P,i+1) + end +end + +--- return an absolute path. +-- @param P A file path +function path.abspath(P) + assert_string(1,P) + if not currentdir then return P end + P = P:gsub('[\\/]$','') + local pwd = currentdir() + if not path.isabs(P) then + return path.join(pwd,P) + elseif path.is_windows and at(P,2) ~= ':' and at(P,2) ~= '\\' then + return pwd:sub(1,2)..P + else + return P + end +end + +--- given a path, return the root part and the extension part. +-- if there's no extension part, the second value will be empty +-- @param P A file path +function path.splitext(P) + assert_string(1,P) + local i = #P + local ch = at(P,i) + while i > 0 and ch ~= '.' do + if ch == sep or ch == other_sep then + return P,'' + end + i = i - 1 + ch = at(P,i) + end + if i == 0 then + return P,'' + else + return sub(P,1,i-1),sub(P,i) + end +end + +--- return the directory part of a path +-- @param P A file path +function path.dirname(P) + assert_string(1,P) + local p1,p2 = path.splitpath(P) + return p1 +end + +--- return the file part of a path +-- @param P A file path +function path.basename(P) + assert_string(1,P) + local p1,p2 = path.splitpath(P) + return p2 +end + +--- get the extension part of a path. +-- @param P A file path +function path.extension(P) + assert_string(1,P) + local p1,p2 = path.splitext(P) + return p2 +end + +--- is this an absolute path?. +-- @param P A file path +function path.isabs(P) + assert_string(1,P) + if path.is_windows then + return at(P,1) == '/' or at(P,1)=='\\' or at(P,2)==':' + else + return at(P,1) == '/' + end +end + +--- return the P resulting from combining the two paths. +-- if the second is already an absolute path, then it returns it. +-- @param p1 A file path +-- @param p2 A file path +function path.join(p1,p2) + assert_string(1,p1) + assert_string(2,p2) + if path.isabs(p2) then return p2 end + local endc = at(p1,#p1) + if endc ~= path.sep and endc ~= other_sep then + p1 = p1..path.sep + end + return p1..p2 +end + +--- normalize the case of a pathname. On Unix, this returns the path unchanged; +-- for Windows, it converts the path to lowercase, and it also converts forward slashes +-- to backward slashes. +-- @param P A file path +function path.normcase(P) + assert_string(1,P) + if path.is_windows then + return (P:lower():gsub('/','\\')) + else + return P + end +end + +--- normalize a path name. +-- A//B, A/./B and A/foo/../B all become A/B. +-- @param P a file path +function path.normpath (P) + assert_string(1,P) + if path.is_windows then + P = P:gsub('/','\\') + return (P:gsub('[^\\]+\\%.%.\\',''):gsub('\\%.?\\','\\')) + else + return (P:gsub('[^/]+/%.%./',''):gsub('/%.?/','/')) + end +end + + +--- Replace a starting '~' with the user's home directory. +-- In windows, if HOME isn't set, then USERPROFILE is used in preference to +-- HOMEDRIVE HOMEPATH. This is guaranteed to be writeable on all versions of Windows. +-- @param P A file path +function path.expanduser(P) + assert_string(1,P) + if at(P,1) == '~' then + local home = getenv('HOME') + if not home then -- has to be Windows + home = getenv 'USERPROFILE' or (getenv 'HOMEDRIVE' .. getenv 'HOMEPATH') + end + return home..sub(P,2) + else + return P + end +end + + +---Return a suitable full path to a new temporary file name. +-- unlike os.tmpnam(), it always gives you a writeable path (uses %TMP% on Windows) +function path.tmpname () + local res = tmpnam() + if path.is_windows then res = getenv('TMP')..res end + return res +end + +--- return the largest common prefix path of two paths. +-- @param path1 a file path +-- @param path2 a file path +function path.common_prefix (path1,path2) + assert_string(1,path1) + assert_string(2,path2) + -- get them in order! + if #path1 > #path2 then path2,path1 = path1,path2 end + for i = 1,#path1 do + local c1 = at(path1,i) + if c1 ~= at(path2,i) then + local cp = path1:sub(1,i-1) + if at(path1,i-1) ~= sep then + cp = path.dirname(cp) + end + return cp + end + end + if at(path2,#path1+1) ~= sep then path1 = path.dirname(path1) end + return path1 + --return '' +end + + +--- return the full path where a particular Lua module would be found. +-- Both package.path and package.cpath is searched, so the result may +-- either be a Lua file or a shared libarary. +-- @param mod name of the module +-- @return on success: path of module, lua or binary +-- @return on error: nil,error string +function path.package_path(mod) + assert_string(1,mod) + local res + mod = mod:gsub('%.',sep) + res = package.searchpath(mod,package.path) + if res then return res,true end + res = package.searchpath(mod,package.cpath) + if res then return res,false end + return raise 'cannot find module on path' +end + + +---- finis ----- +return path diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/permute.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/permute.lua new file mode 100644 index 000000000..8c2e66fa9 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/permute.lua @@ -0,0 +1,65 @@ +--- Permutation operations. +-- @class module +-- @name pl.permute +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local copy = tablex.deepcopy +local append = table.insert +local coroutine = coroutine +local resume = coroutine.resume +local assert_arg = utils.assert_arg + +--[[ +module ('pl.permute',utils._module) +]] + +local permute = {} + +-- PiL, 9.3 + +local permgen +permgen = function (a, n, fn) + if n == 0 then + fn(a) + else + for i=1,n do + -- put i-th element as the last one + a[n], a[i] = a[i], a[n] + + -- generate all permutations of the other elements + permgen(a, n - 1, fn) + + -- restore i-th element + a[n], a[i] = a[i], a[n] + + end + end +end + +--- an iterator over all permutations of the elements of a list. +-- Please note that the same list is returned each time, so do not keep references! +-- @param a list-like table +-- @return an iterator which provides the next permutation as a list +function permute.iter (a) + assert_arg(1,a,'table') + local n = #a + local co = coroutine.create(function () permgen(a, n, coroutine.yield) end) + return function () -- iterator + local code, res = resume(co) + return res + end +end + +--- construct a table containing all the permutations of a list. +-- @param a list-like table +-- @return a table of tables +-- @usage permute.table {1,2,3} --> {{2,3,1},{3,2,1},{3,1,2},{1,3,2},{2,1,3},{1,2,3}} +function permute.table (a) + assert_arg(1,a,'table') + local res = {} + local n = #a + permgen(a,n,function(t) append(res,copy(t)) end) + return res +end + +return permute diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/platf/luajava.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/platf/luajava.lua new file mode 100644 index 000000000..4fb82e619 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/platf/luajava.lua @@ -0,0 +1,101 @@ +-- experimental support for LuaJava +-- +local path = {} + + +path.link_attrib = nil + +local File = luajava.bindClass("java.io.File") +local Array = luajava.bindClass('java.lang.reflect.Array') + +local function file(s) + return luajava.new(File,s) +end + +function path.dir(P) + local ls = file(P):list() + print(ls) + local idx,n = -1,Array:getLength(ls) + return function () + idx = idx + 1 + if idx == n then return nil + else + return Array:get(ls,idx) + end + end +end + +function path.mkdir(P) + return file(P):mkdir() +end + +function path.rmdir(P) + return file(P):delete() +end + +--- is this a directory? +-- @param P A file path +function path.isdir(P) + if P:match("\\$") then + P = P:sub(1,-2) + end + return file(P):isDirectory() +end + +--- is this a file?. +-- @param P A file path +function path.isfile(P) + return file(P):isFile() +end + +-- is this a symbolic link? +-- Direct support for symbolic links is not provided. +-- see http://stackoverflow.com/questions/813710/java-1-6-determine-symbolic-links +-- and the caveats therein. +-- @param P A file path +function path.islink(P) + local f = file(P) + local canon + local parent = f:getParent() + if not parent then + canon = f + else + parent = f.getParentFile():getCanonicalFile() + canon = luajava.new(File,parent,f:getName()) + end + return canon:getCanonicalFile() ~= canon:getAbsoluteFile() +end + +--- return size of a file. +-- @param P A file path +function path.getsize(P) + return file(P):length() +end + +--- does a path exist?. +-- @param P A file path +-- @return the file path if it exists, nil otherwise +function path.exists(P) + return file(P):exists() and P +end + +--- Return the time of last access as the number of seconds since the epoch. +-- @param P A file path +function path.getatime(P) + return path.getmtime(P) +end + +--- Return the time of last modification +-- @param P A file path +function path.getmtime(P) + -- Java time is no. of millisec since the epoch + return file(P):lastModified()/1000 +end + +---Return the system's ctime. +-- @param P A file path +function path.getctime(P) + return path.getmtime(P) +end + +return path diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/pretty.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/pretty.lua new file mode 100644 index 000000000..c414c9d5c --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/pretty.lua @@ -0,0 +1,224 @@ +--- Pretty-printing Lua tables. +-- Also provides a sandboxed Lua table reader and +-- a function to present large numbers in human-friendly format. +-- @class module +-- @name pl.pretty + +local append = table.insert +local concat = table.concat +local utils = require 'pl.utils' +local lexer = require 'pl.lexer' +local assert_arg = utils.assert_arg + +local pretty = {} + +--- read a string representation of a Lua table. +-- Uses load(), but tries to be cautious about loading arbitrary code! +-- It is expecting a string of the form '{...}', with perhaps some whitespace +-- before or after the curly braces. An empty environment is used, and +-- any occurance of the keyword 'function' will be considered a problem. +-- @param s {string} string of the form '{...}', with perhaps some whitespace +-- before or after the curly braces. +function pretty.read(s) + assert_arg(1,s,'string') + if not s:find '^%s*%b{}%s*$' then return nil,"not a Lua table" end + if s:find '[^\'"%w_]function[^\'"%w_]' then + local tok = lexer.lua(s) + for t,v in tok do + if t == 'keyword' then + return nil,"cannot have Lua keywords in table definition" + end + end + end + local chunk,err = utils.load('return '..s,'tbl','t',{}) + if not chunk then return nil,err end + return chunk() +end + +local function quote_if_necessary (v) + if not v then return '' + else + if v:find ' ' then v = '"'..v..'"' end + end + return v +end + +local keywords + + +--- Create a string representation of a Lua table. +-- This function never fails, but may complain by returning an +-- extra value. Normally puts out one item per line, using +-- the provided indent; set the second parameter to '' if +-- you want output on one line. +-- @param tbl {table} Table to serialize to a string. +-- @param space {string} (optional) The indent to use. +-- Defaults to two spaces. +-- @param not_clever {bool} (optional) Use for plain output, e.g {['key']=1}. +-- Defaults to false. +-- @return a string +-- @return a possible error message +function pretty.write (tbl,space,not_clever) + if type(tbl) ~= 'table' then + local res = tostring(tbl) + if type(tbl) == 'string' then res = '"'..res..'"' end + return res, 'not a table' + end + if not keywords then + keywords = lexer.get_keywords() + end + local set = ' = ' + if space == '' then set = '=' end + space = space or ' ' + local lines = {} + local line = '' + local tables = {} + + local function is_identifier (s) + return (s:find('^[%a_][%w_]*$')) and not keywords[s] + end + + local function put(s) + if #s > 0 then + line = line..s + end + end + + local function putln (s) + if #line > 0 then + line = line..s + append(lines,line) + line = '' + else + append(lines,s) + end + end + + local function eat_last_comma () + local n,lastch = #lines + local lastch = lines[n]:sub(-1,-1) + if lastch == ',' then + lines[n] = lines[n]:sub(1,-2) + end + end + + local function quote (s) + return ('%q'):format(tostring(s)) + end + + local function index (numkey,key) + if not numkey then key = quote(key) end + return '['..key..']' + end + + local writeit + writeit = function (t,oldindent,indent) + local tp = type(t) + if tp ~= 'string' and tp ~= 'table' then + putln(quote_if_necessary(tostring(t))..',') + elseif tp == 'string' then + if t:find('\n') then + putln('[[\n'..t..']],') + else + putln(quote(t)..',') + end + elseif tp == 'table' then + if tables[t] then + putln(',') + return + end + tables[t] = true + local newindent = indent..space + putln('{') + local used = {} + if not not_clever then + for i,val in ipairs(t) do + put(indent) + writeit(val,indent,newindent) + used[i] = true + end + end + for key,val in pairs(t) do + local numkey = type(key) == 'number' + if not_clever then + key = tostring(key) + put(indent..index(numkey,key)..set) + writeit(val,indent,newindent) + else + if not numkey or not used[key] then -- non-array indices + if numkey or not is_identifier(key) then + key = index(numkey,key) + end + put(indent..key..set) + writeit(val,indent,newindent) + end + end + end + eat_last_comma() + putln(oldindent..'},') + else + putln(tostring(t)..',') + end + end + writeit(tbl,'',space) + eat_last_comma() + return concat(lines,#space > 0 and '\n' or '') +end + +--- Dump a Lua table out to a file or stdout. +-- @param t {table} The table to write to a file or stdout. +-- @param ... {string} (optional) File name to write too. Defaults to writing +-- to stdout. +function pretty.dump (t,...) + if select('#',...)==0 then + print(pretty.write(t)) + return true + else + return utils.writefile(...,pretty.write(t)) + end +end + +local memp,nump = {'B','KiB','MiB','GiB'},{'','K','M','B'} + +local comma +function comma (val) + local thou = math.floor(val/1000) + if thou > 0 then return comma(thou)..','..(val % 1000) + else return tostring(val) end +end + +--- format large numbers nicely for human consumption. +-- @param num a number +-- @param kind one of 'M' (memory in KiB etc), 'N' (postfixes are 'K','M' and 'B') +-- and 'T' (use commas as thousands separator) +-- @param prec number of digits to use for 'M' and 'N' (default 1) +function pretty.number (num,kind,prec) + local fmt = '%.'..(prec or 1)..'f%s' + if kind == 'T' then + return comma(num) + else + local postfixes, fact + if kind == 'M' then + fact = 1024 + postfixes = memp + else + fact = 1000 + postfixes = nump + end + local div = fact + local k = 1 + while num >= div and k <= #postfixes do + div = div * fact + k = k + 1 + end + div = div / fact + if k > #postfixes then k = k - 1; div = div/fact end + if k > 1 then + return fmt:format(num/div,postfixes[k] or 'duh') + else + return num..postfixes[1] + end + end +end + +return pretty diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/seq.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/seq.lua new file mode 100644 index 000000000..479a12a6e --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/seq.lua @@ -0,0 +1,527 @@ +--- Manipulating sequences as iterators. +-- @class module +-- @name pl.seq + +local next,assert,type,pairs,tonumber,type,setmetatable,getmetatable,_G = next,assert,type,pairs,tonumber,type,setmetatable,getmetatable,_G +local strfind = string.find +local strmatch = string.match +local format = string.format +local mrandom = math.random +local remove,tsort,tappend = table.remove,table.sort,table.insert +local io = io +local utils = require 'pl.utils' +local function_arg = utils.function_arg +local _List = utils.stdmt.List +local _Map = utils.stdmt.Map +local assert_arg = utils.assert_arg +require 'debug' + +--[[ +module("pl.seq",utils._module) +]] + +local seq = {} + +-- given a number, return a function(y) which returns true if y > x +-- @param x a number +function seq.greater_than(x) + return function(v) + return tonumber(v) > x + end +end + +-- given a number, returns a function(y) which returns true if y < x +-- @param x a number +function seq.less_than(x) + return function(v) + return tonumber(v) < x + end +end + +-- given any value, return a function(y) which returns true if y == x +-- @param x a value +function seq.equal_to(x) + if type(x) == "number" then + return function(v) + return tonumber(v) == x + end + else + return function(v) + return v == x + end + end +end + +--- given a string, return a function(y) which matches y against the string. +-- @param s a string +function seq.matching(s) + return function(v) + return strfind(v,s) + end +end + +--- sequence adaptor for a table. Note that if any generic function is +-- passed a table, it will automatically use seq.list() +-- @param t a list-like table +-- @usage sum(list(t)) is the sum of all elements of t +-- @usage for x in list(t) do...end +function seq.list(t) + assert_arg(1,t,'table') + local key,value + return function() + key,value = next(t,key) + return value + end +end + +--- return the keys of the table. +-- @param t a list-like table +-- @return iterator over keys +function seq.keys(t) + assert_arg(1,t,'table') + local key,value + return function() + key,value = next(t,key) + return key + end +end + +local list = seq.list +local function default_iter(iter) + if type(iter) == 'table' then return list(iter) + else return iter end +end + +seq.iter = default_iter + +--- create an iterator over a numerical range. Like the standard Python function xrange. +-- @param start a number +-- @param finish a number greater than start +function seq.range(start,finish) + local i = start - 1 + return function() + i = i + 1 + if i > finish then return nil + else return i end + end +end + +-- count the number of elements in the sequence which satisfy the predicate +-- @param iter a sequence +-- @param condn a predicate function (must return either true or false) +-- @param optional argument to be passed to predicate as second argument. +function seq.count(iter,condn,arg) + local i = 0 + seq.foreach(iter,function(val) + if condn(val,arg) then i = i + 1 end + end) + return i +end + +--- return the minimum and the maximum value of the sequence. +-- @param iter a sequence +function seq.minmax(iter) + local vmin,vmax = 1e70,-1e70 + for v in default_iter(iter) do + v = tonumber(v) + if v < vmin then vmin = v end + if v > vmax then vmax = v end + end + return vmin,vmax +end + +--- return the sum and element count of the sequence. +-- @param iter a sequence +-- @param fn an optional function to apply to the values +function seq.sum(iter,fn) + local s = 0 + local i = 0 + for v in default_iter(iter) do + if fn then v = fn(v) end + s = s + v + i = i + 1 + end + return s,i +end + +--- create a table from the sequence. (This will make the result a List.) +-- @param iter a sequence +-- @return a List +-- @usage copy(list(ls)) is equal to ls +-- @usage copy(list {1,2,3}) == List{1,2,3} +function seq.copy(iter) + local res = {} + for v in default_iter(iter) do + tappend(res,v) + end + setmetatable(res,_List) + return res +end + +--- create a table of pairs from the double-valued sequence. +-- @param iter a double-valued sequence +-- @param i1 used to capture extra iterator values +-- @param i2 as with pairs & ipairs +-- @usage copy2(ipairs{10,20,30}) == {{1,10},{2,20},{3,30}} +-- @return a list-like table +function seq.copy2 (iter,i1,i2) + local res = {} + for v1,v2 in iter,i1,i2 do + tappend(res,{v1,v2}) + end + return res +end + +--- create a table of 'tuples' from a multi-valued sequence. +-- A generalization of copy2 above +-- @param iter a multiple-valued sequence +-- @return a list-like table +function seq.copy_tuples (iter) + iter = default_iter(iter) + local res = {} + local row = {iter()} + while #row > 0 do + tappend(res,row) + row = {iter()} + end + return res +end + +--- return an iterator of random numbers. +-- @param n the length of the sequence +-- @param l same as the first optional argument to math.random +-- @param u same as the second optional argument to math.random +-- @return a sequnce +function seq.random(n,l,u) + local rand + assert(type(n) == 'number') + if u then + rand = function() return mrandom(l,u) end + elseif l then + rand = function() return mrandom(l) end + else + rand = mrandom + end + + return function() + if n == 0 then return nil + else + n = n - 1 + return rand() + end + end +end + +--- return an iterator to the sorted elements of a sequence. +-- @param iter a sequence +-- @param comp an optional comparison function (comp(x,y) is true if x < y) +function seq.sort(iter,comp) + local t = seq.copy(iter) + tsort(t,comp) + return list(t) +end + +--- return an iterator which returns elements of two sequences. +-- @param iter1 a sequence +-- @param iter2 a sequence +-- @usage for x,y in seq.zip(ls1,ls2) do....end +function seq.zip(iter1,iter2) + iter1 = default_iter(iter1) + iter2 = default_iter(iter2) + return function() + return iter1(),iter2() + end +end + +--- A table where the key/values are the values and value counts of the sequence. +-- This version works with 'hashable' values like strings and numbers.
    +-- pl.tablex.count_map is more general. +-- @param iter a sequence +-- @return a map-like table +-- @return a table +-- @see pl.tablex.count_map +function seq.count_map(iter) + local t = {} + local v + for s in default_iter(iter) do + v = t[s] + if v then t[s] = v + 1 + else t[s] = 1 end + end + return setmetatable(t,_Map) +end + +-- given a sequence, return all the unique values in that sequence. +-- @param iter a sequence +-- @param returns_table true if we return a table, not a sequence +-- @return a sequence or a table; defaults to a sequence. +function seq.unique(iter,returns_table) + local t = seq.count_map(iter) + local res = {} + for k in pairs(t) do tappend(res,k) end + table.sort(res) + if returns_table then + return res + else + return list(res) + end +end + +-- print out a sequence @iter, with a separator @sep (default space) +-- and maximum number of values per line @nfields (default 7) +-- @fmt is an optional format function to create a representation of each value. +function seq.printall(iter,sep,nfields,fmt) + local write = io.write + if not sep then sep = ' ' end + if not nfields then + if sep == '\n' then nfields = 1e30 + else nfields = 7 end + end + if fmt then + local fstr = fmt + fmt = function(v) return format(fstr,v) end + end + local k = 1 + for v in default_iter(iter) do + if fmt then v = fmt(v) end + if k < nfields then + write(v,sep) + k = k + 1 + else + write(v,'\n') + k = 1 + end + end + write '\n' +end + +-- return an iterator running over every element of two sequences (concatenation). +-- @param iter1 a sequence +-- @param iter2 a sequence +function seq.splice(iter1,iter2) + iter1 = default_iter(iter1) + iter2 = default_iter(iter2) + local iter = iter1 + return function() + local ret = iter() + if ret == nil then + if iter == iter1 then + iter = iter2 + return iter() + else return nil end + else + return ret + end + end +end + +--- return a sequence where every element of a sequence has been transformed +-- by a function. If you don't supply an argument, then the function will +-- receive both values of a double-valued sequence, otherwise behaves rather like +-- tablex.map. +-- @param fn a function to apply to elements; may take two arguments +-- @param iter a sequence of one or two values +-- @param arg optional argument to pass to function. +function seq.map(fn,iter,arg) + fn = function_arg(1,fn) + iter = default_iter(iter) + return function() + local v1,v2 = iter() + if v1 == nil then return nil end + if arg then return fn(v1,arg) or false + else return fn(v1,v2) or false + end + end +end + +--- filter a sequence using a predicate function +-- @param iter a sequence of one or two values +-- @param pred a boolean function; may take two arguments +-- @param arg optional argument to pass to function. +function seq.filter (iter,pred,arg) + pred = function_arg(2,pred) + return function () + local v1,v2 + while true do + v1,v2 = iter() + if v1 == nil then return nil end + if arg then + if pred(v1,arg) then return v1,v2 end + else + if pred(v1,v2) then return v1,v2 end + end + end + end +end + +--- 'reduce' a sequence using a binary function. +-- @param fun a function of two arguments +-- @param iter a sequence +-- @param oldval optional initial value +-- @usage seq.reduce(operator.add,seq.list{1,2,3,4}) == 10 +-- @usage seq.reduce('-',{1,2,3,4,5}) == -13 +function seq.reduce (fun,iter,oldval) + fun = function_arg(1,fun) + iter = default_iter(iter) + if not oldval then + oldval = iter() + end + local val = oldval + for v in iter do + val = fun(val,v) + end + return val +end + +--- take the first n values from the sequence. +-- @param iter a sequence of one or two values +-- @param n number of items to take +-- @return a sequence of at most n items +function seq.take (iter,n) + local i = 1 + iter = default_iter(iter) + return function() + if i > n then return end + local val1,val2 = iter() + if not val1 then return end + i = i + 1 + return val1,val2 + end +end + +--- skip the first n values of a sequence +-- @param iter a sequence of one or more values +-- @param n number of items to skip +function seq.skip (iter,n) + n = n or 1 + for i = 1,n do iter() end + return iter +end + +--- a sequence with a sequence count and the original value.
    +-- enum(copy(ls)) is a roundabout way of saying ipairs(ls). +-- @param iter a single or double valued sequence +-- @return sequence of (i,v), i = 1..n and v is from iter. +function seq.enum (iter) + local i = 0 + iter = default_iter(iter) + return function () + local val1,val2 = iter() + if not val1 then return end + i = i + 1 + return i,val1,val2 + end +end + +--- map using a named method over a sequence. +-- @param iter a sequence +-- @param name the method name +-- @param arg1 optional first extra argument +-- @param arg2 optional second extra argument +function seq.mapmethod (iter,name,arg1,arg2) + iter = default_iter(iter) + return function() + local val = iter() + if not val then return end + local fn = val[name] + if not fn then error(type(val).." does not have method "..name) end + return fn(val,arg1,arg2) + end +end + +--- a sequence of (last,current) values from another sequence. +-- This will return S(i-1),S(i) if given S(i) +-- @param iter a sequence +function seq.last (iter) + iter = default_iter(iter) + local l = iter() + if l == nil then return nil end + return function () + local val,ll + val = iter() + if val == nil then return nil end + ll = l + l = val + return val,ll + end +end + +--- call the function on each element of the sequence. +-- @param iter a sequence with up to 3 values +-- @param fn a function +function seq.foreach(iter,fn) + fn = function_arg(2,fn) + for i1,i2,i3 in default_iter(iter) do fn(i1,i2,i3) end +end + +---------------------- Sequence Adapters --------------------- + +local SMT +local callable = utils.is_callable + +local function SW (iter,...) + if callable(iter) then + return setmetatable({iter=iter},SMT) + else + return iter,... + end +end + + +-- can't directly look these up in seq because of the wrong argument order... +local map,reduce,mapmethod = seq.map, seq.reduce, seq.mapmethod +local overrides = { + map = function(self,fun,arg) + return map(fun,self,arg) + end, + reduce = function(self,fun) + return reduce(fun,self) + end +} + +SMT = { + __index = function (tbl,key) + local s = overrides[key] or seq[key] + if s then + return function(sw,...) return SW(s(sw.iter,...)) end + else + return function(sw,...) return SW(mapmethod(sw.iter,key,...)) end + end + end, + __call = function (sw) + return sw.iter() + end, +} + +setmetatable(seq,{ + __call = function(tbl,iter) + if not callable(iter) then + if type(iter) == 'table' then iter = seq.list(iter) + else return iter + end + end + return setmetatable({iter=iter},SMT) + end +}) + +--- create a wrapped iterator over all lines in the file. +-- @param f either a filename or nil (for standard input) +-- @return a sequence wrapper +function seq.lines (f) + local iter = f and io.lines(f) or io.lines() + return SW(iter) +end + +function seq.import () + _G.debug.setmetatable(function() end,{ + __index = function(tbl,key) + local s = overrides[key] or seq[key] + if s then return s + else + return function(s,...) return seq.mapmethod(s,key,...) end + end + end + }) +end + +return seq diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/sip.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/sip.lua new file mode 100644 index 000000000..c739a54ca --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/sip.lua @@ -0,0 +1,335 @@ +--- Simple Input Patterns (SIP).

    +-- SIP patterns start with '$', then a +-- one-letter type, and then an optional variable in curly braces.

    +-- Example: +--

    +--  sip.match('$v=$q','name="dolly"',res)
    +--  ==> res=={'name','dolly'}
    +--  sip.match('($q{first},$q{second})','("john","smith")',res)
    +--  ==> res=={second='smith',first='john'}
    +-- 
    +--
    +-- Type names
    +-- v    identifier
    +-- i     integer
    +-- f     floating-point
    +-- q    quoted string
    +-- ([{<  match up to closing bracket
    +-- 
    +--

    +-- See the Guide +-- @class module +-- @name pl.sip + +local append,concat = table.insert,table.concat +local concat = table.concat +local ipairs,loadstring,type,unpack = ipairs,loadstring,type,unpack +local io,_G = io,_G +local print,rawget = print,rawget + +local patterns = { + FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*', + INTEGER = '[+%-%d]%d*', + IDEN = '[%a_][%w_]*', + FILE = '[%a%.\\][:%][%w%._%-\\]*' +} + +local function assert_arg(idx,val,tp) + if type(val) ~= tp then + error("argument "..idx.." must be "..tp, 2) + end +end + + +--[[ +module ('pl.sip',utils._module) +]] + +local sip = {} + +local brackets = {['<'] = '>', ['('] = ')', ['{'] = '}', ['['] = ']' } +local stdclasses = {a=1,c=0,d=1,l=1,p=0,u=1,w=1,x=1,s=0} + +local _patterns = {} + + +local function group(s) + return '('..s..')' +end + +-- escape all magic characters except $, which has special meaning +-- Also, un-escape any characters after $, so $( passes through as is. +local function escape (spec) + --_G.print('spec',spec) + local res = spec:gsub('[%-%.%+%[%]%(%)%^%%%?%*]','%%%1'):gsub('%$%%(%S)','$%1') + --_G.print('res',res) + return res +end + +local function imcompressible (s) + return s:gsub('%s+','\001') +end + +-- [handling of spaces in patterns] +-- spaces may be 'compressed' (i.e will match zero or more spaces) +-- unless this occurs within a number or an identifier. So we mark +-- the four possible imcompressible patterns first and then replace. +-- The possible alnum patterns are v,f,a,d,x,l and u. +local function compress_spaces (s) + s = s:gsub('%$[vifadxlu]%s+%$[vfadxlu]',imcompressible) + s = s:gsub('[%w_]%s+[%w_]',imcompressible) + s = s:gsub('[%w_]%s+%$[vfadxlu]',imcompressible) + s = s:gsub('%$[vfadxlu]%s+[%w_]',imcompressible) + s = s:gsub('%s+','%%s*') + s = s:gsub('\001',' ') + return s +end + +--- convert a SIP pattern into the equivalent Lua string pattern. +-- @param spec a SIP pattern +-- @param options a table; only the at_start field is +-- currently meaningful and esures that the pattern is anchored +-- at the start of the string. +-- @return a Lua string pattern. +function sip.create_pattern (spec,options) + assert_arg(1,spec,'string') + local fieldnames,fieldtypes = {},{} + + if type(spec) == 'string' then + spec = escape(spec) + else + local res = {} + for i,s in ipairs(spec) do + res[i] = escape(s) + end + spec = concat(res,'.-') + end + + local kount = 1 + + local function addfield (name,type) + if not name then name = kount end + if fieldnames then append(fieldnames,name) end + if fieldtypes then fieldtypes[name] = type end + kount = kount + 1 + end + + local named_vars, pattern + named_vars = spec:find('{%a+}') + pattern = '%$%S' + + if options and options.at_start then + spec = '^'..spec + end + if spec:sub(-1,-1) == '$' then + spec = spec:sub(1,-2)..'$r' + if named_vars then spec = spec..'{rest}' end + end + + + local names + + if named_vars then + names = {} + spec = spec:gsub('{(%a+)}',function(name) + append(names,name) + return '' + end) + end + spec = compress_spaces(spec) + + local k = 1 + local err + local r = (spec:gsub(pattern,function(s) + local type,name + type = s:sub(2,2) + if names then name = names[k]; k=k+1 end + -- this kludge is necessary because %q generates two matches, and + -- we want to ignore the first. Not a problem for named captures. + if not names and type == 'q' then + addfield(nil,'Q') + else + addfield(name,type) + end + local res + if type == 'v' then + res = group(patterns.IDEN) + elseif type == 'i' then + res = group(patterns.INTEGER) + elseif type == 'f' then + res = group(patterns.FLOAT) + elseif type == 'r' then + res = '(%S.*)' + elseif type == 'q' then + -- some Lua pattern matching voodoo; we want to match '...' as + -- well as "...", and can use the fact that %n will match a + -- previous capture. Adding the extra field above comes from needing + -- to accomodate the extra spurious match (which is either ' or ") + addfield(name,type) + res = '(["\'])(.-)%'..(kount-2) + elseif type == 'p' then + res = '([%a]?[:]?[\\/%.%w_]+)' + else + local endbracket = brackets[type] + if endbracket then + res = '(%b'..type..endbracket..')' + elseif stdclasses[type] or stdclasses[type:lower()] then + res = '(%'..type..'+)' + else + err = "unknown format type or character class" + end + end + return res + end)) + --print(r,err) + if err then + return nil,err + else + return r,fieldnames,fieldtypes + end +end + + +local function tnumber (s) + return s == 'd' or s == 'i' or s == 'f' +end + +function sip.create_spec_fun(spec,options) + local fieldtypes,fieldnames + local ls = {} + spec,fieldnames,fieldtypes = sip.create_pattern(spec,options) + if not spec then return spec,fieldnames end + local named_vars = type(fieldnames[1]) == 'string' + for i = 1,#fieldnames do + append(ls,'mm'..i) + end + local fun = ('return (function(s,res)\n\tlocal %s = s:match(%q)\n'):format(concat(ls,','),spec) + fun = fun..'\tif not mm1 then return false end\n' + local k=1 + for i,f in ipairs(fieldnames) do + if f ~= '_' then + local var = 'mm'..i + if tnumber(fieldtypes[f]) then + var = 'tonumber('..var..')' + elseif brackets[fieldtypes[f]] then + var = var..':sub(2,-2)' + end + if named_vars then + fun = ('%s\tres.%s = %s\n'):format(fun,f,var) + else + if fieldtypes[f] ~= 'Q' then -- we skip the string-delim capture + fun = ('%s\tres[%d] = %s\n'):format(fun,k,var) + k = k + 1 + end + end + end + end + return fun..'\treturn true\nend)\n', named_vars +end + +--- convert a SIP pattern into a matching function. +-- The returned function takes two arguments, the line and an empty table. +-- If the line matched the pattern, then this function return true +-- and the table is filled with field-value pairs. +-- @param spec a SIP pattern +-- @param options optional table; {anywhere=true} will stop pattern anchoring at start +-- @return a function if successful, or nil, +function sip.compile(spec,options) + assert_arg(1,spec,'string') + local fun,names = sip.create_spec_fun(spec,options) + if not fun then return nil,names end + if rawget(_G,'_DEBUG') then print(fun) end + local chunk,err = loadstring(fun,'tmp') + if err then return nil,err end + return chunk(),names +end + +local cache = {} + +--- match a SIP pattern against a string. +-- @param spec a SIP pattern +-- @param line a string +-- @param res a table to receive values +-- @param options (optional) option table +-- @return true or false +function sip.match (spec,line,res,options) + assert_arg(1,spec,'string') + assert_arg(2,line,'string') + assert_arg(3,res,'table') + if not cache[spec] then + cache[spec] = sip.compile(spec,options) + end + return cache[spec](line,res) +end + +--- match a SIP pattern against the start of a string. +-- @param spec a SIP pattern +-- @param line a string +-- @param res a table to receive values +-- @return true or false +function sip.match_at_start (spec,line,res) + return sip.match(spec,line,res,{at_start=true}) +end + +--- given a pattern and a file object, return an iterator over the results +-- @param spec a SIP pattern +-- @param f a file - use standard input if not specified. +function sip.fields (spec,f) + assert_arg(1,spec,'string') + f = f or io.stdin + local fun,err = sip.compile(spec) + if not fun then return nil,err end + local res = {} + return function() + while true do + local line = f:read() + if not line then return end + if fun(line,res) then + local values = res + res = {} + return unpack(values) + end + end + end +end + +--- register a match which will be used in the read function. +-- @param spec a SIP pattern +-- @param fun a function to be called with the results of the match +-- @see read +function sip.pattern (spec,fun) + assert_arg(1,spec,'string') + local pat,named = sip.compile(spec) + append(_patterns,{pat=pat,named=named,callback=fun or false}) +end + +--- enter a loop which applies all registered matches to the input file. +-- @param f a file object; if nil, then io.stdin is assumed. +function sip.read (f) + local owned,err + f = f or io.stdin + if type(f) == 'string' then + f,err = io.open(f) + if not f then return nil,err end + owned = true + end + local res = {} + for line in f:lines() do + for _,item in ipairs(_patterns) do + if item.pat(line,res) then + if item.callback then + if item.named then + item.callback(res) + else + item.callback(unpack(res)) + end + end + res = {} + break + end + end + end + if owned then f:close() end +end + +return sip diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/strict.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/strict.lua new file mode 100644 index 000000000..f7d8c94c8 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/strict.lua @@ -0,0 +1,71 @@ +--- Checks uses of undeclared global variables. +-- All global variables must be 'declared' through a regular assignment +-- (even assigning nil will do) in a main chunk before being used +-- anywhere or assigned to inside a function. +-- @class module +-- @name pl.strict + +require 'debug' +local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget +local handler,hooked + +local mt = getmetatable(_G) +if mt == nil then + mt = {} + setmetatable(_G, mt) +elseif mt.hook then + hooked = true +end + +-- predeclaring _PROMPT keeps the Lua Interpreter happy +mt.__declared = {_PROMPT=true} + +local function what () + local d = getinfo(3, "S") + return d and d.what or "C" +end + +mt.__newindex = function (t, n, v) + if not mt.__declared[n] then + local w = what() + if w ~= "main" and w ~= "C" then + error("assign to undeclared variable '"..n.."'", 2) + end + mt.__declared[n] = true + end + rawset(t, n, v) +end + +handler = function(t,n) + if not mt.__declared[n] and what() ~= "C" then + error("variable '"..n.."' is not declared", 2) + end + return rawget(t, n) +end + +function package.strict (mod) + local mt = getmetatable(mod) + if mt == nil then + mt = {} + setmetatable(mod, mt) + end + mt.__declared = {} + mt.__newindex = function(t, n, v) + mt.__declared[n] = true + rawset(t, n, v) + end + mt.__index = function(t,n) + if not mt.__declared[n] then + error("variable '"..n.."' is not declared", 2) + end + return rawget(t, n) + end +end + +if not hooked then + mt.__index = handler +else + mt.hook(handler) +end + + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/stringio.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/stringio.lua new file mode 100644 index 000000000..0129ddd56 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/stringio.lua @@ -0,0 +1,144 @@ +--- Reading and writing strings using file-like objects.
    +--

    +--  f = stringio.open(text)
    +--  l1 = f:read()  -- read first line
    +--  n,m = f:read ('*n','*n') -- read two numbers
    +--  for line in f:lines() do print(line) end -- iterate over all lines
    +--  f = stringio.create()
    +--  f:write('hello')
    +--  f:write('dolly')
    +--  assert(f:value(),'hellodolly')
    +-- 
    +-- See the Guide. +-- @class module +-- @name pl.stringio + +local getmetatable,tostring,unpack,tonumber = getmetatable,tostring,unpack,tonumber +local concat,append = table.concat,table.insert + +local stringio = {} + +--- Writer class +local SW = {} +SW.__index = SW + +local function xwrite(self,...) + local args = {...} --arguments may not be nil! + for i = 1, #args do + append(self.tbl,args[i]) + end +end + +function SW:write(arg1,arg2,...) + if arg2 then + xwrite(self,arg1,arg2,...) + else + append(self.tbl,arg1) + end +end + +function SW:writef(fmt,...) + self:write(fmt:format(...)) +end + +function SW:value() + return concat(self.tbl) +end + +function SW:close() -- for compatibility only +end + +function SW:seek() +end + +--- Reader class +local SR = {} +SR.__index = SR + +function SR:_read(fmt) + local i,str = self.i,self.str + local sz = #str + if i >= sz then return nil end + local res + if fmt == nil or fmt == '*l' then + local idx = str:find('\n',i) or (sz+1) + res = str:sub(i,idx-1) + self.i = idx+1 + elseif fmt == '*a' then + res = str:sub(i) + self.i = sz + elseif fmt == '*n' then + local _,i2,i2,idx + _,idx = str:find ('%s*%d+',i) + _,i2 = str:find ('%.%d+',idx+1) + if i2 then idx = i2 end + _,i2 = str:find ('[eE][%+%-]*%d+',idx+1) + if i2 then idx = i2 end + local val = str:sub(i,idx) + res = tonumber(val) + self.i = idx+1 + elseif type(fmt) == 'number' then + res = str:sub(i,i+fmt-1) + self.i = i + fmt + else + error("bad read format",2) + end + return res +end + +function SR:read(...) + local fmts = {...} + if #fmts <= 1 then + return self:_read(fmts[1]) + else + local res = {} + for i = 1, #fmts do + res[i] = self:_read(fmts[i]) + end + return unpack(res) + end +end + +function SR:seek(whence,offset) + local base + whence = whence or 'cur' + offset = offset or 0 + if whence == 'set' then + base = 1 + elseif whence == 'cur' then + base = self.i + elseif whence == 'end' then + base = #self.str + end + self.i = base + offset + return self.i +end + +function SR:lines() + return function() + return self:read() + end +end + +function SR:close() -- for compatibility only +end + +--- create a file-like object which can be used to construct a string. +-- The resulting object has an extra value() method for +-- retrieving the string value. +-- @usage f = create(); f:write('hello, dolly\n'); print(f:value()) +function stringio.create() + return setmetatable({tbl={}},SW) +end + +--- create a file-like object for reading from a given string. +-- @param s The input string. +function stringio.open(s) + return setmetatable({str=s,i=1},SR) +end + +function stringio.lines(s) + return stringio.open(s):lines() +end + +return stringio diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/stringx.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/stringx.lua new file mode 100644 index 000000000..5699b2039 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/stringx.lua @@ -0,0 +1,441 @@ +--- Python-style string library.

    +-- see 3.6.1 of the Python reference.

    +-- If you want to make these available as string methods, then say +-- stringx.import() to bring them into the standard string +-- table. +-- @class module +-- @name pl.stringx +local string = string +local find = string.find +local type,setmetatable,getmetatable,ipairs,unpack = type,setmetatable,getmetatable,ipairs,unpack +local error,tostring = error,tostring +local gsub = string.gsub +local rep = string.rep +local sub = string.sub +local concat = table.concat +local utils = require 'pl.utils' +local escape = utils.escape +local ceil = math.ceil +local _G = _G +local assert_arg,usplit,list_MT = utils.assert_arg,utils.split,utils.stdmt.List +local lstrip + +local function assert_string (n,s) + assert_arg(n,s,'string') +end + +local function non_empty(s) + return #s > 0 +end + +local function assert_nonempty_string(n,s) + assert_arg(n,s,'string',non_empty,'must be a non-empty string') +end + +--[[ +module ('pl.stringx',utils._module) +]] + +local stringx = {} + +--- does s only contain alphabetic characters?. +-- @param s a string +function stringx.isalpha(s) + assert_string(1,s) + return find(s,'^%a+$') == 1 +end + +--- does s only contain digits?. +-- @param s a string +function stringx.isdigit(s) + assert_string(1,s) + return find(s,'^%d+$') == 1 +end + +--- does s only contain alphanumeric characters?. +-- @param s a string +function stringx.isalnum(s) + assert_string(1,s) + return find(s,'^%w+$') == 1 +end + +--- does s only contain spaces?. +-- @param s a string +function stringx.isspace(s) + assert_string(1,s) + return find(s,'^%s+$') == 1 +end + +--- does s only contain lower case characters?. +-- @param s a string +function stringx.islower(s) + assert_string(1,s) + return find(s,'^[%l%s]+$') == 1 +end + +--- does s only contain upper case characters?. +-- @param s a string +function stringx.isupper(s) + assert_string(1,s) + return find(s,'^[%u%s]+$') == 1 +end + +--- concatenate the strings using this string as a delimiter. +-- @param self the string +-- @param seq a table of strings or numbers +-- @usage (' '):join {1,2,3} == '1 2 3' +function stringx.join (self,seq) + assert_string(1,self) + return concat(seq,self) +end + +--- does string start with the substring?. +-- @param self the string +-- @param s2 a string +function stringx.startswith(self,s2) + assert_string(1,self) + assert_string(2,s2) + return find(self,s2,1,true) == 1 +end + +local function _find_all(s,sub,first,last) + if sub == '' then return #s+1,#s end + local i1,i2 = find(s,sub,first,true) + local res + local k = 0 + while i1 do + res = i1 + k = k + 1 + i1,i2 = find(s,sub,i2+1,true) + if last and i1 > last then break end + end + return res,k +end + +--- does string end with the given substring?. +-- @param s a string +-- @param send a substring or a table of suffixes +function stringx.endswith(s,send) + assert_string(1,s) + if type(send) == 'string' then + return #s >= #send and s:find(send, #s-#send+1, true) and true or false + elseif type(send) == 'table' then + local endswith = stringx.endswith + for _,suffix in ipairs(send) do + if endswith(s,suffix) then return true end + end + return false + else + error('argument #2: either a substring or a table of suffixes expected') + end +end + +-- break string into a list of lines +-- @param self the string +-- @param keepends (currently not used) +function stringx.splitlines (self,keepends) + assert_string(1,self) + local res = usplit(self,'[\r\n]') + -- we are currently hacking around a problem with utils.split (see stringx.split) + if #res == 0 then res = {''} end + return setmetatable(res,list_MT) +end + +local function tab_expand (self,n) + return (gsub(self,'([^\t]*)\t', function(s) + return s..(' '):rep(n - #s % n) + end)) +end + +--- replace all tabs in s with n spaces. If not specified, n defaults to 8. +-- with 0.9.5 this now correctly expands to the next tab stop (if you really +-- want to just replace tabs, use :gsub('\t',' ') etc) +-- @param self the string +-- @param n number of spaces to expand each tab, (default 8) +function stringx.expandtabs(self,n) + assert_string(1,self) + n = n or 8 + if not self:find '\n' then return tab_expand(self,n) end + local res,i = {},1 + for line in stringx.lines(self) do + res[i] = tab_expand(line,n) + i = i + 1 + end + return table.concat(res,'\n') +end + +--- find index of first instance of sub in s from the left. +-- @param self the string +-- @param sub substring +-- @param i1 start index +function stringx.lfind(self,sub,i1) + assert_string(1,self) + assert_string(2,sub) + local idx = find(self,sub,i1,true) + if idx then return idx else return nil end +end + +--- find index of first instance of sub in s from the right. +-- @param self the string +-- @param sub substring +-- @param first first index +-- @param last last index +function stringx.rfind(self,sub,first,last) + assert_string(1,self) + assert_string(2,sub) + local idx = _find_all(self,sub,first,last) + if idx then return idx else return nil end +end + +--- replace up to n instances of old by new in the string s. +-- if n is not present, replace all instances. +-- @param s the string +-- @param old the target substring +-- @param new the substitution +-- @param n optional maximum number of substitutions +-- @return result string +-- @return the number of substitutions +function stringx.replace(s,old,new,n) + assert_string(1,s) + assert_string(1,old) + return (gsub(s,escape(old),new:gsub('%%','%%%%'),n)) +end + +--- split a string into a list of strings using a delimiter. +-- @class function +-- @name split +-- @param self the string +-- @param re a delimiter (defaults to whitespace) +-- @param n maximum number of results +-- @usage #(('one two'):split()) == 2 +-- @usage ('one,two,three'):split(',') == List{'one','two','three'} +-- @usage ('one,two,three'):split(',',2) == List{'one','two,three'} +function stringx.split(self,re,n) + local s = self + local plain = true + if not re then -- default spaces + s = lstrip(s) + plain = false + end + local res = usplit(s,re,plain,n) + if re and re ~= '' and find(s,re,-#re,true) then + res[#res+1] = "" + end + return setmetatable(res,list_MT) +end + +--- split a string using a pattern. Note that at least one value will be returned! +-- @param self the string +-- @param re a Lua string pattern (defaults to whitespace) +-- @return the parts of the string +-- @usage a,b = line:splitv('=') +function stringx.splitv (self,re) + assert_string(1,self) + return utils.splitv(self,re) +end + +local function copy(self) + return self..'' +end + +--- count all instances of substring in string. +-- @param self the string +-- @param sub substring +function stringx.count(self,sub) + assert_string(1,self) + local i,k = _find_all(self,sub,1) + return k +end + +local function _just(s,w,ch,left,right) + local n = #s + if w > n then + if not ch then ch = ' ' end + local f1,f2 + if left and right then + local ln = ceil((w-n)/2) + local rn = w - n - ln + f1 = rep(ch,ln) + f2 = rep(ch,rn) + elseif right then + f1 = rep(ch,w-n) + f2 = '' + else + f2 = rep(ch,w-n) + f1 = '' + end + return f1..s..f2 + else + return copy(s) + end +end + +--- left-justify s with width w. +-- @param self the string +-- @param w width of justification +-- @param ch padding character, default ' ' +function stringx.ljust(self,w,ch) + assert_string(1,self) + assert_arg(2,w,'number') + return _just(self,w,ch,true,false) +end + +--- right-justify s with width w. +-- @param s the string +-- @param w width of justification +-- @param ch padding character, default ' ' +function stringx.rjust(s,w,ch) + assert_string(1,s) + assert_arg(2,w,'number') + return _just(s,w,ch,false,true) +end + +--- center-justify s with width w. +-- @param s the string +-- @param w width of justification +-- @param ch padding character, default ' ' +function stringx.center(s,w,ch) + assert_string(1,s) + assert_arg(2,w,'number') + return _just(s,w,ch,true,true) +end + +local function _strip(s,left,right,chrs) + if not chrs then + chrs = '%s' + else + chrs = '['..escape(chrs)..']' + end + if left then + local i1,i2 = find(s,'^'..chrs..'*') + if i2 >= i1 then + s = sub(s,i2+1) + end + end + if right then + local i1,i2 = find(s,chrs..'*$') + if i2 >= i1 then + s = sub(s,1,i1-1) + end + end + return s +end + +--- trim any whitespace on the left of s. +-- @param self the string +-- @param chrs default space, can be a string of characters to be trimmed +function stringx.lstrip(self,chrs) + assert_string(1,self) + return _strip(self,true,false,chrs) +end +lstrip = stringx.lstrip + +--- trim any whitespace on the right of s. +-- @param s the string +-- @param chrs default space, can be a string of characters to be trimmed +function stringx.rstrip(s,chrs) + assert_string(1,s) + return _strip(s,false,true,chrs) +end + +--- trim any whitespace on both left and right of s. +-- @param self the string +-- @param chrs default space, can be a string of characters to be trimmed +function stringx.strip(self,chrs) + assert_string(1,self) + return _strip(self,true,true,chrs) +end + +-- The partition functions split a string using a delimiter into three parts: +-- the part before, the delimiter itself, and the part afterwards +local function _partition(p,delim,fn) + local i1,i2 = fn(p,delim) + if not i1 or i1 == -1 then + return p,'','' + else + if not i2 then i2 = i1 end + return sub(p,1,i1-1),sub(p,i1,i2),sub(p,i2+1) + end +end + +--- partition the string using first occurance of a delimiter +-- @param self the string +-- @param ch delimiter +-- @return part before ch +-- @return ch +-- @return part after ch +function stringx.partition(self,ch) + assert_string(1,self) + assert_nonempty_string(2,ch) + return _partition(self,ch,stringx.lfind) +end + +--- partition the string p using last occurance of a delimiter +-- @param self the string +-- @param ch delimiter +-- @return part before ch +-- @return ch +-- @return part after ch +function stringx.rpartition(self,ch) + assert_string(1,self) + assert_nonempty_string(2,ch) + return _partition(self,ch,stringx.rfind) +end + +--- return the 'character' at the index. +-- @param self the string +-- @param idx an index (can be negative) +-- @return a substring of length 1 if successful, empty string otherwise. +function stringx.at(self,idx) + assert_string(1,self) + assert_arg(2,idx,'number') + return sub(self,idx,idx) +end + +--- return an interator over all lines in a string +-- @param self the string +-- @return an iterator +function stringx.lines (self) + assert_string(1,self) + local s = self + if not s:find '\n$' then s = s..'\n' end + return s:gmatch('([^\n]*)\n') +end + +--- iniital word letters uppercase ('title case'). +-- Here 'words' mean chunks of non-space characters. +-- @param self the string +-- @return a string with each word's first letter uppercase +function stringx.title(self) + return (self:gsub('(%S)(%S*)',function(f,r) + return f:upper()..r:lower() + end)) +end + +stringx.capitalize = stringx.title + +local elipsis = '...' +local n_elipsis = #elipsis + +--- return a shorted version of a string. +-- @param self the string +-- @param sz the maxinum size allowed +-- @param tail true if we want to show the end of the string (head otherwise) +function stringx.shorten(self,sz,tail) + if #self > sz then + if sz < n_elipsis then return elipsis:sub(1,sz) end + if tail then + local i = #self - sz + 1 + n_elipsis + return elipsis .. self:sub(i) + else + return self:sub(1,sz-n_elipsis) .. elipsis + end + end + return self +end + +function stringx.import(dont_overload) + utils.import(stringx,string) +end + +return stringx diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/tablex.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/tablex.lua new file mode 100644 index 000000000..bfddb4b70 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/tablex.lua @@ -0,0 +1,766 @@ +--- Extended operations on Lua tables. +-- @class module +-- @name pl.tablex +local getmetatable,setmetatable,require = getmetatable,setmetatable,require +local append,remove = table.insert,table.remove +local min,max = math.min,math.max +local pairs,type,unpack,next,ipairs,select,tostring = pairs,type,unpack,next,ipairs,select,tostring +local utils = require ('pl.utils') +local function_arg = utils.function_arg +local Set = utils.stdmt.Set +local List = utils.stdmt.List +local Map = utils.stdmt.Map +local assert_arg = utils.assert_arg + +--[[ +module ('pl.tablex',utils._module) +]] + +local tablex = {} + +-- generally, functions that make copies of tables try to preserve the metatable. +-- However, when the source has no obvious type, then we attach appropriate metatables +-- like List, Map, etc to the result. +local function setmeta (res,tbl,def) + return setmetatable(res,getmetatable(tbl) or def) +end + +local function makelist (res) + return setmetatable(res,List) +end + +--- copy a table into another, in-place. +-- @param t1 destination table +-- @param t2 source table +-- @return first table +function tablex.update (t1,t2) + assert_arg(1,t1,'table') + assert_arg(2,t2,'table') + for k,v in pairs(t2) do + t1[k] = v + end + return t1 +end + +--- total number of elements in this table.
    +-- Note that this is distinct from #t, which is the number +-- of values in the array part; this value will always +-- be greater or equal. The difference gives the size of +-- the hash part, for practical purposes. +-- @param t a table +-- @return the size +function tablex.size (t) + assert_arg(1,t,'table') + local i = 0 + for k in pairs(t) do i = i + 1 end + return i +end + +--- make a shallow copy of a table +-- @param t source table +-- @return new table +function tablex.copy (t) + assert_arg(1,t,'table') + local res = {} + for k,v in pairs(t) do + res[k] = v + end + return res +end + +--- make a deep copy of a table, recursively copying all the keys and fields. +-- This will also set the copied table's metatable to that of the original. +-- @param t A table +-- @return new table +function tablex.deepcopy(t) + assert_arg(1,t,'table') + if type(t) ~= 'table' then return t end + local mt = getmetatable(t) + local res = {} + for k,v in pairs(t) do + if type(v) == 'table' then + v = tablex.deepcopy(v) + end + res[k] = v + end + setmetatable(res,mt) + return res +end + +local abs = math.abs + +--- compare two values. +-- if they are tables, then compare their keys and fields recursively. +-- @param t1 A value +-- @param t2 A value +-- @param ignore_mt if true, ignore __eq metamethod (default false) +-- @param eps if defined, then used for any number comparisons +-- @return true or false +function tablex.deepcompare(t1,t2,ignore_mt,eps) + local ty1 = type(t1) + local ty2 = type(t2) + if ty1 ~= ty2 then return false end + -- non-table types can be directly compared + if ty1 ~= 'table' then + if ty1 == 'number' and eps then return abs(t1-t2) < eps end + return t1 == t2 + end + -- as well as tables which have the metamethod __eq + local mt = getmetatable(t1) + if not ignore_mt and mt and mt.__eq then return t1 == t2 end + for k1,v1 in pairs(t1) do + local v2 = t2[k1] + if v2 == nil or not tablex.deepcompare(v1,v2,ignore_mt,eps) then return false end + end + for k2,v2 in pairs(t2) do + local v1 = t1[k2] + if v1 == nil or not tablex.deepcompare(v1,v2,ignore_mt,eps) then return false end + end + return true +end + +--- compare two list-like tables using a predicate. +-- @param t1 a table +-- @param t2 a table +-- @param cmp A comparison function +function tablex.compare (t1,t2,cmp) + assert_arg(1,t1,'table') + assert_arg(2,t2,'table') + if #t1 ~= #t2 then return false end + cmp = function_arg(3,cmp) + for k in ipairs(t1) do + if not cmp(t1[k],t2[k]) then return false end + end + return true +end + +--- compare two list-like tables using an optional predicate, without regard for element order. +-- @param t1 a list-like table +-- @param t2 a list-like table +-- @param cmp A comparison function (may be nil) +function tablex.compare_no_order (t1,t2,cmp) + assert_arg(1,t1,'table') + assert_arg(2,t2,'table') + if cmp then cmp = function_arg(3,cmp) end + if #t1 ~= #t2 then return false end + local visited = {} + for i = 1,#t1 do + local val = t1[i] + local gotcha + for j = 1,#t2 do if not visited[j] then + local match + if cmp then match = cmp(val,t2[j]) else match = val == t2[j] end + if match then + gotcha = j + break + end + end end + if not gotcha then return false end + visited[gotcha] = true + end + return true +end + + +--- return the index of a value in a list. +-- Like string.find, there is an optional index to start searching, +-- which can be negative. +-- @param t A list-like table (i.e. with numerical indices) +-- @param val A value +-- @param idx index to start; -1 means last element,etc (default 1) +-- @return index of value or nil if not found +-- @usage find({10,20,30},20) == 2 +-- @usage find({'a','b','a','c'},'a',2) == 3 + +function tablex.find(t,val,idx) + assert_arg(1,t,'table') + idx = idx or 1 + if idx < 0 then idx = #t + idx + 1 end + for i = idx,#t do + if t[i] == val then return i end + end + return nil +end + +--- return the index of a value in a list, searching from the end. +-- Like string.find, there is an optional index to start searching, +-- which can be negative. +-- @param t A list-like table (i.e. with numerical indices) +-- @param val A value +-- @param idx index to start; -1 means last element,etc (default 1) +-- @return index of value or nil if not found +-- @usage rfind({10,10,10},10) == 3 +function tablex.rfind(t,val,idx) + assert_arg(1,t,'table') + idx = idx or #t + if idx < 0 then idx = #t + idx + 1 end + for i = idx,1,-1 do + if t[i] == val then return i end + end + return nil +end + + +--- return the index (or key) of a value in a table using a comparison function. +-- @param t A table +-- @param cmp A comparison function +-- @param arg an optional second argument to the function +-- @return index of value, or nil if not found +-- @return value returned by comparison function +function tablex.find_if(t,cmp,arg) + assert_arg(1,t,'table') + cmp = function_arg(2,cmp) + for k,v in pairs(t) do + local c = cmp(v,arg) + if c then return k,c end + end + return nil +end + +--- return a list of all values in a table indexed by another list. +-- @param tbl a table +-- @param idx an index table (a list of keys) +-- @return a list-like table +-- @usage index_by({10,20,30,40},{2,4}) == {20,40} +-- @usage index_by({one=1,two=2,three=3},{'one','three'}) == {1,3} +function tablex.index_by(tbl,idx) + assert_arg(1,tbl,'table') + assert_arg(2,idx,'table') + local res = {} + for _,i in ipairs(idx) do + append(res,tbl[i]) + end + return setmeta(res,tbl,List) +end + +--- apply a function to all values of a table. +-- This returns a table of the results. +-- Any extra arguments are passed to the function. +-- @param fun A function that takes at least one argument +-- @param t A table +-- @param ... optional arguments +-- @usage map(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900,fred=4} +function tablex.map(fun,t,...) + assert_arg(1,t,'table') + fun = function_arg(1,fun) + local res = {} + for k,v in pairs(t) do + res[k] = fun(v,...) + end + return setmeta(res,t) +end + +--- apply a function to all values of a list. +-- This returns a table of the results. +-- Any extra arguments are passed to the function. +-- @param fun A function that takes at least one argument +-- @param t a table (applies to array part) +-- @param ... optional arguments +-- @return a list-like table +-- @usage imap(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900} +function tablex.imap(fun,t,...) + assert_arg(1,t,'table') + fun = function_arg(1,fun) + local res = {} + for i = 1,#t do + res[i] = fun(t[i],...) or false + end + return setmeta(res,t,List) +end + +--- apply a named method to values from a table. +-- @param name the method name +-- @param t a list-like table +-- @param ... any extra arguments to the method +function tablex.map_named_method (name,t,...) + assert_arg(1,name,'string') + assert_arg(2,t,'table') + local res = {} + for i = 1,#t do + local val = t[i] + local fun = val[name] + res[i] = fun(val,...) + end + return setmeta(res,t,List) +end + + +--- apply a function to all values of a table, in-place. +-- Any extra arguments are passed to the function. +-- @param fun A function that takes at least one argument +-- @param t a table +-- @param ... extra arguments +function tablex.transform (fun,t,...) + assert_arg(1,t,'table') + fun = function_arg(1,fun) + for k,v in pairs(t) do + t[v] = fun(v,...) + end +end + +--- generate a table of all numbers in a range +-- @param start number +-- @param finish number +-- @param step optional increment (default 1 for increasing, -1 for decreasing) +function tablex.range (start,finish,step) + local res = {} + local k = 1 + if not step then + if finish > start then step = finish > start and 1 or -1 end + end + for i=start,finish,step do res[k]=i; k=k+1 end + return res +end + +--- apply a function to values from two tables. +-- @param fun a function of at least two arguments +-- @param t1 a table +-- @param t2 a table +-- @param ... extra arguments +-- @return a table +-- @usage map2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23,m=44} +function tablex.map2 (fun,t1,t2,...) + assert_arg(1,t1,'table') + assert_arg(2,t2,'table') + fun = function_arg(1,fun) + local res = {} + for k,v in pairs(t1) do + res[k] = fun(v,t2[k],...) + end + return setmeta(res,t1,List) +end + +--- apply a function to values from two arrays. +-- @param fun a function of at least two arguments +-- @param t1 a list-like table +-- @param t2 a list-like table +-- @param ... extra arguments +-- @usage imap2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23} +function tablex.imap2 (fun,t1,t2,...) + assert_arg(2,t1,'table') + assert_arg(3,t2,'table') + fun = function_arg(1,fun) + local res = {} + for i = 1,#t1 do + res[i] = fun(t1[i],t2[i],...) + end + return res +end + +--- 'reduce' a list using a binary function. +-- @param fun a function of two arguments +-- @param t a list-like table +-- @return the result of the function +-- @usage reduce('+',{1,2,3,4}) == 10 +function tablex.reduce (fun,t) + assert_arg(2,t,'table') + fun = function_arg(1,fun) + local n = #t + local res = t[1] + for i = 2,n do + res = fun(res,t[i]) + end + return res +end + +--- apply a function to all elements of a table. +-- The arguments to the function will be the value, +-- the key and finally any extra arguments passed to this function. +-- Note that the Lua 5.0 function table.foreach passed the key first. +-- @param t a table +-- @param fun a function with at least one argument +-- @param ... extra arguments +function tablex.foreach(t,fun,...) + assert_arg(1,t,'table') + fun = function_arg(2,fun) + for k,v in pairs(t) do + fun(v,k,...) + end +end + +--- apply a function to all elements of a list-like table in order. +-- The arguments to the function will be the value, +-- the index and finally any extra arguments passed to this function +-- @param t a table +-- @param fun a function with at least one argument +-- @param ... optional arguments +function tablex.foreachi(t,fun,...) + assert_arg(1,t,'table') + fun = function_arg(2,fun) + for k,v in ipairs(t) do + fun(v,k,...) + end +end + + +--- Apply a function to a number of tables. +-- A more general version of map +-- The result is a table containing the result of applying that function to the +-- ith value of each table. Length of output list is the minimum length of all the lists +-- @param fun a function of n arguments +-- @param ... n tables +-- @usage mapn(function(x,y,z) return x+y+z end, {1,2,3},{10,20,30},{100,200,300}) is {111,222,333} +-- @usage mapn(math.max, {1,20,300},{10,2,3},{100,200,100}) is {100,200,300} +-- @param fun A function that takes as many arguments as there are tables +function tablex.mapn(fun,...) + fun = function_arg(1,fun) + local res = {} + local lists = {...} + local minn = 1e40 + for i = 1,#lists do + minn = min(minn,#(lists[i])) + end + for i = 1,minn do + local args = {} + for j = 1,#lists do + args[#args+1] = lists[j][i] + end + res[#res+1] = fun(unpack(args)) + end + return res +end + +--- call the function with the key and value pairs from a table. +-- The function can return a value and a key (note the order!). If both +-- are not nil, then this pair is inserted into the result. If only value is not nil, then +-- it is appended to the result. +-- @param fun A function which will be passed each key and value as arguments, plus any extra arguments to pairmap. +-- @param t A table +-- @param ... optional arguments +-- @usage pairmap({fred=10,bonzo=20},function(k,v) return v end) is {10,20} +-- @usage pairmap({one=1,two=2},function(k,v) return {k,v},k end) is {one={'one',1},two={'two',2}} +function tablex.pairmap(fun,t,...) + assert_arg(1,t,'table') + fun = function_arg(1,fun) + local res = {} + for k,v in pairs(t) do + local rv,rk = fun(k,v,...) + if rk then + res[rk] = rv + else + res[#res+1] = rv + end + end + return res +end + +local function keys_op(i,v) return i end + +--- return all the keys of a table in arbitrary order. +-- @param t A table +function tablex.keys(t) + assert_arg(1,t,'table') + return makelist(tablex.pairmap(keys_op,t)) +end + +local function values_op(i,v) return v end + +--- return all the values of the table in arbitrary order +-- @param t A table +function tablex.values(t) + assert_arg(1,t,'table') + return makelist(tablex.pairmap(values_op,t)) +end + +local function index_map_op (i,v) return i,v end + +--- create an index map from a list-like table. The original values become keys, +-- and the associated values are the indices into the original list. +-- @param t a list-like table +-- @return a map-like table +function tablex.index_map (t) + assert_arg(1,t,'table') + return setmetatable(tablex.pairmap(index_map_op,t),Map) +end + +local function set_op(i,v) return true,v end + +--- create a set from a list-like table. A set is a table where the original values +-- become keys, and the associated values are all true. +-- @param t a list-like table +-- @return a set (a map-like table) +function tablex.makeset (t) + assert_arg(1,t,'table') + return setmetatable(tablex.pairmap(set_op,t),Set) +end + + +--- combine two tables, either as union or intersection. Corresponds to +-- set operations for sets () but more general. Not particularly +-- useful for list-like tables. +-- @param t1 a table +-- @param t2 a table +-- @param dup true for a union, false for an intersection. +-- @usage merge({alice=23,fred=34},{bob=25,fred=34}) is {fred=34} +-- @usage merge({alice=23,fred=34},{bob=25,fred=34},true) is {bob=25,fred=34,alice=23} +-- @see tablex.index_map +function tablex.merge (t1,t2,dup) + assert_arg(1,t1,'table') + assert_arg(2,t2,'table') + local res = {} + for k,v in pairs(t1) do + if dup or t2[k] then res[k] = v end + end + for k,v in pairs(t2) do + if dup or t1[k] then res[k] = v end + end + return setmeta(res,t1,Map) +end + +--- a new table which is the difference of two tables. +-- With sets (where the values are all true) this is set difference and +-- symmetric difference depending on the third parameter. +-- @param s1 a map-like table or set +-- @param s2 a map-like table or set +-- @param symm symmetric difference (default false) +-- @return a map-like table or set +function tablex.difference (s1,s2,symm) + assert_arg(1,s1,'table') + assert_arg(2,s2,'table') + local res = {} + for k,v in pairs(s1) do + if not s2[k] then res[k] = v end + end + if symm then + for k,v in pairs(s2) do + if not s1[k] then res[k] = v end + end + end + return setmeta(res,s1,Map) +end + +--- A table where the key/values are the values and value counts of the table. +-- @param t a list-like table +-- @param cmp a function that defines equality (otherwise uses ==) +-- @return a map-like table +-- @see seq.count_map +function tablex.count_map (t,cmp) + assert_arg(1,t,'table') + local res,mask = {},{} + cmp = function_arg(2,cmp) + local n = #t + for i,v in ipairs(t) do + if not mask[v] then + mask[v] = true + -- check this value against all other values + res[v] = 1 -- there's at least one instance + for j = i+1,n do + local w = t[j] + if cmp and cmp(v,w) or v == w then + res[v] = res[v] + 1 + mask[w] = true + end + end + end + end + return setmetatable(res,Map) +end + +--- filter a table's values using a predicate function +-- @param t a list-like table +-- @param pred a boolean function +-- @param arg optional argument to be passed as second argument of the predicate +function tablex.filter (t,pred,arg) + assert_arg(1,t,'table') + pred = function_arg(2,pred) + local res = {} + for k,v in ipairs(t) do + if pred(v,arg) then append(res,v) end + end + return setmeta(res,t,List) +end + +--- return a table where each element is a table of the ith values of an arbitrary +-- number of tables. It is equivalent to a matrix transpose. +-- @usage zip({10,20,30},{100,200,300}) is {{10,100},{20,200},{30,300}} +function tablex.zip(...) + return tablex.mapn(function(...) return {...} end,...) +end + +local _copy +function _copy (dest,src,idest,isrc,nsrc,clean_tail) + idest = idest or 1 + isrc = isrc or 1 + local iend + if not nsrc then + nsrc = #src + iend = #src + else + iend = isrc + min(nsrc-1,#src-isrc) + end + if dest == src then -- special case + if idest > isrc and iend >= idest then -- overlapping ranges + src = tablex.sub(src,isrc,nsrc) + isrc = 1; iend = #src + end + end + for i = isrc,iend do + dest[idest] = src[i] + idest = idest + 1 + end + if clean_tail then + tablex.clear(dest,idest) + end + return dest +end + +--- copy an array into another one, resizing the destination if necessary.
    +-- @param dest a list-like table +-- @param src a list-like table +-- @param idest where to start copying values from source (default 1) +-- @param isrc where to start copying values into destination (default 1) +-- @param nsrc number of elements to copy from source (default source size) +function tablex.icopy (dest,src,idest,isrc,nsrc) + assert_arg(1,dest,'table') + assert_arg(2,src,'table') + return _copy(dest,src,idest,isrc,nsrc,true) +end + +--- copy an array into another one.
    +-- @param dest a list-like table +-- @param src a list-like table +-- @param idest where to start copying values from source (default 1) +-- @param isrc where to start copying values into destination (default 1) +-- @param nsrc number of elements to copy from source (default source size) +function tablex.move (dest,src,idest,isrc,nsrc) + assert_arg(1,dest,'table') + assert_arg(2,src,'table') + return _copy(dest,src,idest,isrc,nsrc,false) +end + +function tablex._normalize_slice(self,first,last) + local sz = #self + if not first then first=1 end + if first<0 then first=sz+first+1 end + -- make the range _inclusive_! + if not last then last=sz end + if last < 0 then last=sz+1+last end + return first,last +end + +--- Extract a range from a table, like 'string.sub'. +-- If first or last are negative then they are relative to the end of the list +-- eg. sub(t,-2) gives last 2 entries in a list, and +-- sub(t,-4,-2) gives from -4th to -2nd +-- @param t a list-like table +-- @param first An index +-- @param last An index +-- @return a new List +function tablex.sub(t,first,last) + assert_arg(1,t,'table') + first,last = tablex._normalize_slice(t,first,last) + local res={} + for i=first,last do append(res,t[i]) end + return setmeta(res,t,List) +end + +--- set an array range to a value. If it's a function we use the result +-- of applying it to the indices. +-- @param t a list-like table +-- @param val a value +-- @param i1 start range (default 1) +-- @param i2 end range (default table size) +function tablex.set (t,val,i1,i2) + i1,i2 = i1 or 1,i2 or #t + if utils.is_callable(val) then + for i = i1,i2 do + t[i] = val(i) + end + else + for i = i1,i2 do + t[i] = val + end + end +end + +--- create a new array of specified size with initial value. +-- @param n size +-- @param val initial value (can be nil, but don't expect # to work!) +-- @return the table +function tablex.new (n,val) + local res = {} + tablex.set(res,val,1,n) + return res +end + +--- clear out the contents of a table. +-- @param t a table +-- @param istart optional start position +function tablex.clear(t,istart) + istart = istart or 1 + for i = istart,#t do remove(t) end +end + +--- insert values into a table.
    +-- insertvalues(t, [pos,] values)
    +-- similar to table.insert but inserts values from given table "values", +-- not the object itself, into table "t" at position "pos". +function tablex.insertvalues(t, ...) + local pos, values + if select('#', ...) == 1 then + pos,values = #t+1, ... + else + pos,values = ... + end + if #values > 0 then + for i=#t,pos,-1 do + t[i+#values] = t[i] + end + local offset = 1 - pos + for i=pos,pos+#values-1 do + t[i] = values[i + offset] + end + end + return t +end + +--- remove a range of values from a table. +-- @param t a list-like table +-- @param i1 start index +-- @param i2 end index +-- @return the table +function tablex.removevalues (t,i1,i2) + i1,i2 = tablex._normalize_slice(t,i1,i2) + for i = i1,i2 do + remove(t,i1) + end + return t +end + +local _find +_find = function (t,value,tables) + for k,v in pairs(t) do + if v == value then return k end + end + for k,v in pairs(t) do + if not tables[v] and type(v) == 'table' then + tables[v] = true + local res = _find(v,value,tables) + if res then + res = tostring(res) + if type(k) ~= 'string' then + return '['..k..']'..res + else + return k..'.'..res + end + end + end + end +end + +--- find a value in a table by recursive search. +-- @param t the table +-- @param value the value +-- @param exclude any tables to avoid searching +-- @usage search(_G,math.sin,{package.path}) == 'math.sin' +-- @return a fieldspec, e.g. 'a.b' or 'math.sin' +function tablex.search (t,value,exclude) + assert_arg(1,t,'table') + local tables = {[t]=true} + if exclude then + for _,v in pairs(exclude) do tables[v] = true end + end + return _find(t,value,tables) +end + +return tablex diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/template.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/template.lua new file mode 100644 index 000000000..dbff1f2fc --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/template.lua @@ -0,0 +1,99 @@ +--- A template preprocessor. +-- Originally by Ricki Lake +--

    There are two rules:

      +--
    • lines starting with # are Lua
    • +--
    • otherwise, `$(expr)` is the result of evaluating `expr`
    • +--
    +--
    +-- #  for i = 1,3 do
    +--    $(i) Hello, Word!
    +-- #  end
    +-- 
    +-- Other escape characters can be used, when the defaults conflict +-- with the output language. +--
    +-- > for _,n in pairs{'one','two','three'} do
    +--  static int l_${n} (luaState *state);
    +-- > end
    +-- 
    +-- See the Guide. +-- @class module +-- @name pl.template + +--[[ + module('pl.template') +]] + +local utils = require 'pl.utils' +local append,format = table.insert,string.format + +local function parseHashLines(chunk,brackets,esc) + local exec_pat = "()$(%b"..brackets..")()" + + local function parseDollarParen(pieces, chunk, s, e) + local s = 1 + for term, executed, e in chunk:gmatch (exec_pat) do + executed = '('..executed:sub(2,-2)..')' + append(pieces, + format("%q..(%s or '')..",chunk:sub(s, term - 1), executed)) + s = e + end + append(pieces, format("%q", chunk:sub(s))) + end + + local esc_pat = esc.."+([^\n]*\n?)" + local esc_pat1, esc_pat2 = "^"..esc_pat, "\n"..esc_pat + local pieces, s = {"return function(_put) ", n = 1}, 1 + while true do + local ss, e, lua = chunk:find (esc_pat1, s) + if not e then + ss, e, lua = chunk:find(esc_pat2, s) + append(pieces, "_put(") + parseDollarParen(pieces, chunk:sub(s, ss)) + append(pieces, ")") + if not e then break end + end + append(pieces, lua) + s = e + 1 + end + append(pieces, " end") + return table.concat(pieces) +end + +local template = {} + +--- expand the template using the specified environment. +-- @param str the template string +-- @param env the environment (by default empty).
    +-- There are three special fields in the environment table
      +--
    • _parent continue looking up in this table
    • +--
    • _brackets; default is '()', can be any suitable bracket pair
    • +--
    • _escape; default is '#'
    • +--
    +function template.substitute(str,env) + env = env or {} + if rawget(env,"_parent") then + setmetatable(env,{__index = env._parent}) + end + local brackets = rawget(env,"_brackets") or '()' + local escape = rawget(env,"_escape") or '#' + local code = parseHashLines(str,brackets,escape) + local fn,err = utils.load(code,'TMP','t',env) + if not fn then return nil,err end + fn = fn() + local out = {} + local res,err = xpcall(function() fn(function(s) + out[#out+1] = s + end) end,debug.traceback) + if not res then + if env._debug then print(code) end + return nil,err + end + return table.concat(out) +end + +return template + + + + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/test.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/test.lua new file mode 100644 index 000000000..162337968 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/test.lua @@ -0,0 +1,116 @@ +--- Useful test utilities. +-- @module pl.test + +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local pretty = require 'pl.pretty' +local path = require 'pl.path' +local print,type = print,type +local clock = os.clock +local debug = require 'debug' +local io,debug = io,debug + +local function dump(x) + if type(x) == 'table' and not (getmetatable(x) and getmetatable(x).__tostring) then + return pretty.write(x,' ',true) + else + return tostring(x) + end +end + +local test = {} + +local function complain (x,y,msg) + local i = debug.getinfo(3) + local err = io.stderr + err:write(path.basename(i.short_src)..':'..i.currentline..': assertion failed\n') + err:write("got:\t",dump(x),'\n') + err:write("needed:\t",dump(y),'\n') + utils.quit(1,msg or "these values were not equal") +end + +--- like assert, except takes two arguments that must be equal and can be tables. +-- If they are plain tables, it will use tablex.deepcompare. +-- @param x any value +-- @param y a value equal to x +-- @param eps an optional tolerance for numerical comparisons +function test.asserteq (x,y,eps) + local res = x == y + if not res then + res = tablex.deepcompare(x,y,true,eps) + end + if not res then + complain(x,y) + end +end + +--- assert that the first string matches the second. +-- @param s1 a string +-- @param s2 a string +function test.assertmatch (s1,s2) + if not s1:match(s2) then + complain (s1,s2,"these strings did not match") + end +end + +function test.assertraise(fn,e) + local ok, err = pcall(unpack(fn)) + if not err or err:match(e)==nil then + complain (err,e,"these errors did not match") + end +end + +--- a version of asserteq that takes two pairs of values. +-- x1==y1 and x2==y2 must be true. Useful for functions that naturally +-- return two values. +-- @param x1 any value +-- @param x2 any value +-- @param y1 any value +-- @param y2 any value +function test.asserteq2 (x1,x2,y1,y2) + if x1 ~= y1 then complain(x1,y1) end + if x2 ~= y2 then complain(x2,y2) end +end + +-- tuple type -- + +local tuple_mt = {} + +function tuple_mt.__tostring(self) + local ts = {} + for i=1, self.n do + local s = self[i] + ts[i] = type(s) == 'string' and string.format('%q', s) or tostring(s) + end + return 'tuple(' .. table.concat(ts, ', ') .. ')' +end + +function tuple_mt.__eq(a, b) + if a.n ~= b.n then return false end + for i=1, a.n do + if a[i] ~= b[i] then return false end + end + return true +end + +--- encode an arbitrary argument list as a tuple. +-- This can be used to compare to other argument lists, which is +-- very useful for testing functions which return a number of values. +-- @usage asserteq(tuple( ('ab'):find 'a'), tuple(1,1)) +function test.tuple(...) + return setmetatable({n=select('#', ...), ...}, tuple_mt) +end + +--- Time a function. Call the function a given number of times, and report the number of seconds taken, +-- together with a message. Any extra arguments will be passed to the function. +-- @param msg a descriptive message +-- @param n number of times to call the function +-- @param fun the function +-- @param ... optional arguments to fun +function test.timer(msg,n,fun,...) + local start = clock() + for i = 1,n do fun(...) end + utils.printf("%s: took %7.2f sec\n",msg,clock()-start) +end + +return test diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/text.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/text.lua new file mode 100644 index 000000000..4c09c7003 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/text.lua @@ -0,0 +1,241 @@ +--- Text processing utilities.

    +-- This provides a Template class (modeled after the same from the Python +-- libraries, see string.Template). It also provides similar functions to those +-- found in the textwrap module. +-- See the Guide. +--

    +-- Calling text.format_operator() overloads the % operator for strings to give Python/Ruby style formated output. +-- This is extended to also do template-like substitution for map-like data. +--

    +-- > require 'pl.text'.format_operator()
    +-- > = '%s = %5.3f' % {'PI',math.pi}
    +-- PI = 3.142
    +-- > = '$name = $value' % {name='dog',value='Pluto'}
    +-- dog = Pluto
    +-- 
    +-- @class module +-- @name pl.text + +local gsub = string.gsub +local concat,append = table.concat,table.insert +local utils = require 'pl.utils' +local bind1,usplit,assert_arg,is_callable = utils.bind1,utils.split,utils.assert_arg,utils.is_callable + +local function lstrip(str) return (str:gsub('^%s+','')) end +local function strip(str) return (lstrip(str):gsub('%s+$','')) end +local function make_list(l) return setmetatable(l,utils.stdmt.List) end +local function split(s,delim) return make_list(usplit(s,delim)) end + +local function imap(f,t,...) + local res = {} + for i = 1,#t do res[i] = f(t[i],...) end + return res +end + +--[[ +module ('pl.text',utils._module) +]] + +local text = {} + +local function _indent (s,sp) + local sl = split(s,'\n') + return concat(imap(bind1('..',sp),sl),'\n')..'\n' +end + +--- indent a multiline string. +-- @param s the string +-- @param n the size of the indent +-- @param ch the character to use when indenting (default ' ') +-- @return indented string +function text.indent (s,n,ch) + assert_arg(1,s,'string') + assert_arg(2,s,'number') + return _indent(s,string.rep(ch or ' ',n)) +end + +--- dedent a multiline string by removing any initial indent. +-- useful when working with [[..]] strings. +-- @param s the string +-- @return a string with initial indent zero. +function text.dedent (s) + assert_arg(1,s,'string') + local sl = split(s,'\n') + local i1,i2 = sl[1]:find('^%s*') + sl = imap(string.sub,sl,i2+1) + return concat(sl,'\n')..'\n' +end + +--- format a paragraph into lines so that they fit into a line width. +-- It will not break long words, so lines can be over the length +-- to that extent. +-- @param s the string +-- @param width the margin width, default 70 +-- @return a list of lines +function text.wrap (s,width) + assert_arg(1,s,'string') + width = width or 70 + s = s:gsub('\n',' ') + local i,nxt = 1 + local lines,line = {} + while i < #s do + nxt = i+width + if s:find("[%w']",nxt) then -- inside a word + nxt = s:find('%W',nxt+1) -- so find word boundary + end + line = s:sub(i,nxt) + i = i + #line + append(lines,strip(line)) + end + return make_list(lines) +end + +--- format a paragraph so that it fits into a line width. +-- @param s the string +-- @param width the margin width, default 70 +-- @return a string +-- @see wrap +function text.fill (s,width) + return concat(text.wrap(s,width),'\n') .. '\n' +end + +local Template = {} +text.Template = Template +Template.__index = Template +setmetatable(Template, { + __call = function(obj,tmpl) + return Template.new(tmpl) + end}) + +function Template.new(tmpl) + assert_arg(1,tmpl,'string') + local res = {} + res.tmpl = tmpl + setmetatable(res,Template) + return res +end + +local function _substitute(s,tbl,safe) + local subst + if is_callable(tbl) then + subst = tbl + else + function subst(f) + local s = tbl[f] + if not s then + if safe then + return f + else + error("not present in table "..f) + end + else + return s + end + end + end + local res = gsub(s,'%${([%w_]+)}',subst) + return (gsub(res,'%$([%w_]+)',subst)) +end + +--- substitute values into a template, throwing an error. +-- This will throw an error if no name is found. +-- @param tbl a table of name-value pairs. +function Template:substitute(tbl) + assert_arg(1,tbl,'table') + return _substitute(self.tmpl,tbl,false) +end + +--- substitute values into a template. +-- This version just passes unknown names through. +-- @param tbl a table of name-value pairs. +function Template:safe_substitute(tbl) + assert_arg(1,tbl,'table') + return _substitute(self.tmpl,tbl,true) +end + +--- substitute values into a template, preserving indentation.
    +-- If the value is a multiline string _or_ a template, it will insert +-- the lines at the correct indentation.
    +-- Furthermore, if a template, then that template will be subsituted +-- using the same table. +-- @param tbl a table of name-value pairs. +function Template:indent_substitute(tbl) + assert_arg(1,tbl,'table') + if not self.strings then + self.strings = split(self.tmpl,'\n') + end + -- the idea is to substitute line by line, grabbing any spaces as + -- well as the $var. If the value to be substituted contains newlines, + -- then we split that into lines and adjust the indent before inserting. + local function subst(line) + return line:gsub('(%s*)%$([%w_]+)',function(sp,f) + local subtmpl + local s = tbl[f] + if not s then error("not present in table "..f) end + if getmetatable(s) == Template then + subtmpl = s + s = s.tmpl + else + s = tostring(s) + end + if s:find '\n' then + s = _indent(s,sp) + end + if subtmpl then return _substitute(s,tbl) + else return s + end + end) + end + local lines = imap(subst,self.strings) + return concat(lines,'\n')..'\n' +end + +------- Python-style formatting operator ------ +-- (see the lua-users wiki) -- + +function text.format_operator() + + local format = string.format + + -- a more forgiving version of string.format, which applies + -- tostring() to any value with a %s format. + local function formatx (fmt,...) + local args = {...} + local i = 1 + for p in fmt:gmatch('%%.') do + if p == '%s' and type(args[i]) ~= 'string' then + args[i] = tostring(args[i]) + end + i = i + 1 + end + return format(fmt,unpack(args)) + end + + local function basic_subst(s,t) + return (s:gsub('%$([%w_]+)',t)) + end + + -- Note this goes further than the original, and will allow these cases: + -- 1. a single value + -- 2. a list of values + -- 3. a map of var=value pairs + -- 4. a function, as in gsub + -- For the second two cases, it uses $-variable substituion. + getmetatable("").__mod = function(a, b) + if b == nil then + return a + elseif type(b) == "table" and getmetatable(b) == nil then + if #b == 0 then -- assume a map-like table + return _substitute(a,b,true) + else + return formatx(a,unpack(b)) + end + elseif type(b) == 'function' then + return basic_subst(a,b) + else + return formatx(a,b) + end + end +end + +return text diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/utils.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/utils.lua new file mode 100644 index 000000000..af1553b2f --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/utils.lua @@ -0,0 +1,529 @@ +--- Generally useful routines. +-- See the Guide. +-- @class module +-- @name pl.utils +local format,gsub,byte = string.format,string.gsub,string.byte +local clock = os.clock +local stdout = io.stdout +local append = table.insert + +local collisions = {} + +local utils = {} + +utils._VERSION = "0.9.4" + +utils.dir_separator = _G.package.config:sub(1,1) + +--- end this program gracefully. +-- @param code The exit code or a message to be printed +-- @param ... extra arguments for message's format' +-- @see utils.fprintf +function utils.quit(code,...) + if type(code) == 'string' then + utils.fprintf(io.stderr,code,...) + code = -1 + else + utils.fprintf(io.stderr,...) + end + io.stderr:write('\n') + os.exit(code) +end + +--- print an arbitrary number of arguments using a format. +-- @param fmt The format (see string.format) +-- @param ... Extra arguments for format +function utils.printf(fmt,...) + utils.fprintf(stdout,fmt,...) +end + +--- write an arbitrary number of arguments to a file using a format. +-- @param f File handle to write to. +-- @param fmt The format (see string.format). +-- @param ... Extra arguments for format +function utils.fprintf(f,fmt,...) + utils.assert_string(2,fmt) + f:write(format(fmt,...)) +end + +local function import_symbol(T,k,v,libname) + local key = rawget(T,k) + -- warn about collisions! + if key and k ~= '_M' and k ~= '_NAME' and k ~= '_PACKAGE' and k ~= '_VERSION' then + utils.printf("warning: '%s.%s' overrides existing symbol\n",libname,k) + end + rawset(T,k,v) +end + +local function lookup_lib(T,t) + for k,v in pairs(T) do + if v == t then return k end + end + return '?' +end + +local already_imported = {} + +--- take a table and 'inject' it into the local namespace. +-- @param t The Table +-- @param T An optional destination table (defaults to callers environment) +function utils.import(t,T) + T = T or _G + t = t or utils + if type(t) == 'string' then + t = require (t) + end + local libname = lookup_lib(T,t) + if already_imported[t] then return end + already_imported[t] = libname + for k,v in pairs(t) do + import_symbol(T,k,v,libname) + end +end + +utils.patterns = { + FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*', + INTEGER = '[+%-%d]%d*', + IDEN = '[%a_][%w_]*', + FILE = '[%a%.\\][:%][%w%._%-\\]*' +} + +--- escape any 'magic' characters in a string +-- @param s The input string +function utils.escape(s) + utils.assert_string(1,s) + return (s:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1')) +end + +--- return either of two values, depending on a condition. +-- @param cond A condition +-- @param value1 Value returned if cond is true +-- @param value2 Value returned if cond is false (can be optional) +function utils.choose(cond,value1,value2) + if cond then return value1 + else return value2 + end +end + +local raise + +--- return the contents of a file as a string +-- @param filename The file path +-- @param is_bin open in binary mode +-- @return file contents +function utils.readfile(filename,is_bin) + local mode = is_bin and 'b' or '' + utils.assert_string(1,filename) + local f,err = io.open(filename,'r'..mode) + if not f then return utils.raise (err) end + local res,err = f:read('*a') + f:close() + if not res then return raise (err) end + return res +end + +--- write a string to a file +-- @param filename The file path +-- @param str The string +-- @return true or nil +-- @return error message +-- @raise error if filename or str aren't strings +function utils.writefile(filename,str) + utils.assert_string(1,filename) + utils.assert_string(2,str) + local f,err = io.open(filename,'w') + if not f then return raise(err) end + f:write(str) + f:close() + return true +end + +--- return the contents of a file as a list of lines +-- @param filename The file path +-- @return file contents as a table +-- @raise errror if filename is not a string +function utils.readlines(filename) + utils.assert_string(1,filename) + local f,err = io.open(filename,'r') + if not f then return raise(err) end + local res = {} + for line in f:lines() do + append(res,line) + end + f:close() + return res +end + +--- split a string into a list of strings separated by a delimiter. +-- @param s The input string +-- @param re A Lua string pattern; defaults to '%s+' +-- @param plain don't use Lua patterns +-- @param n optional maximum number of splits +-- @return a list-like table +-- @raise error if s is not a string +function utils.split(s,re,plain,n) + utils.assert_string(1,s) + local find,sub,append = string.find, string.sub, table.insert + local i1,ls = 1,{} + if not re then re = '%s+' end + if re == '' then return {s} end + while true do + local i2,i3 = find(s,re,i1,plain) + if not i2 then + local last = sub(s,i1) + if last ~= '' then append(ls,last) end + if #ls == 1 and ls[1] == '' then + return {} + else + return ls + end + end + append(ls,sub(s,i1,i2-1)) + if n and #ls == n then + ls[#ls] = sub(s,i1) + return ls + end + i1 = i3+1 + end +end + +--- split a string into a number of values. +-- @param s the string +-- @param re the delimiter, default space +-- @return n values +-- @usage first,next = splitv('jane:doe',':') +-- @see split +function utils.splitv (s,re) + return unpack(utils.split(s,re)) +end + +local lua52 = table.pack ~= nil +local lua51_load = load + +if not lua52 then -- define Lua 5.2 style load() + function utils.load(str,src,mode,env) + local chunk,err + if type(str) == 'string' then + chunk,err = loadstring(str,src) + else + chunk,err = lua51_load(str,src) + end + if chunk and env then setfenv(chunk,env) end + return chunk,err + end +else + utils.load = load + -- setfenv/getfenv replacements for Lua 5.2 + -- by Sergey Rozhenko + -- http://lua-users.org/lists/lua-l/2010-06/msg00313.html + -- Roberto Ierusalimschy notes that it is possible for getfenv to return nil + -- in the case of a function with no globals: + -- http://lua-users.org/lists/lua-l/2010-06/msg00315.html + function setfenv(f, t) + f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func) + local name + local up = 0 + repeat + up = up + 1 + name = debug.getupvalue(f, up) + until name == '_ENV' or name == nil + if name then + debug.upvaluejoin(f, up, function() return name end, 1) -- use unique upvalue + debug.setupvalue(f, up, t) + end + end + + function getfenv(f) + f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func) + local name, val + local up = 0 + repeat + up = up + 1 + name, val = debug.getupvalue(f, up) + until name == '_ENV' or name == nil + return val + end +end + + +--- execute a shell command. +-- This is a compatibility function that returns the same for Lua 5.1 and Lua 5.2 +-- @param cmd a shell command +-- @return true if successful +-- @return actual return code +function utils.execute (cmd) + local res1,res2,res2 = os.execute(cmd) + if not lua52 then + return res1==0,res1 + else + return res1,res2 + end +end + +if not lua52 then + function table.pack (...) + local n = select('#',...) + return {n=n; ...},n + end + local sep = package.config:sub(1,1) + function package.searchpath (mod,path) + mod = mod:gsub('%.',sep) + for m in path:gmatch('[^;]+') do + local nm = m:gsub('?',mod) + local f = io.open(nm,'r') + if f then f:close(); return nm end + end + end +end + +if not table.pack then table.pack = _G.pack end +if not rawget(_G,"pack") then _G.pack = table.pack end + +--- take an arbitrary set of arguments and make into a table. +-- This returns the table and the size; works fine for nil arguments +-- @param ... arguments +-- @return table +-- @return table size +-- @usage local t,n = utils.args(...) + +--- 'memoize' a function (cache returned value for next call). +-- This is useful if you have a function which is relatively expensive, +-- but you don't know in advance what values will be required, so +-- building a table upfront is wasteful/impossible. +-- @param func a function of at least one argument +-- @return a function with at least one argument, which is used as the key. +function utils.memoize(func) + return setmetatable({}, { + __index = function(self, k, ...) + local v = func(k,...) + self[k] = v + return v + end, + __call = function(self, k) return self[k] end + }) +end + +--- is the object either a function or a callable object?. +-- @param obj Object to check. +function utils.is_callable (obj) + return type(obj) == 'function' or getmetatable(obj) and getmetatable(obj).__call +end + +--- is the object of the specified type?. +-- If the type is a string, then use type, otherwise compare with metatable +-- @param obj An object to check +-- @param tp String of what type it should be +function utils.is_type (obj,tp) + if type(tp) == 'string' then return type(obj) == tp end + local mt = getmetatable(obj) + return tp == mt +end + +local fileMT = getmetatable(io.stdout) + +--- a string representation of a type. +-- For tables with metatables, we assume that the metatable has a `_name` +-- field. Knows about Lua file objects. +-- @param obj an object +-- @return a string like 'number', 'table' or 'List' +function utils.type (obj) + local t = type(obj) + if t == 'table' or t == 'userdata' then + local mt = getmetatable(obj) + if mt == fileMT then + return 'file' + else + return mt._name or "unknown "..t + end + else + return t + end +end + +--- is this number an integer? +-- @param x a number +-- @raise error if x is not a number +function utils.is_integer (x) + return math.ceil(x)==x +end + +utils.stdmt = { + List = {_name='List'}, Map = {_name='Map'}, + Set = {_name='Set'}, MultiMap = {_name='MultiMap'} +} + +local _function_factories = {} + +--- associate a function factory with a type. +-- A function factory takes an object of the given type and +-- returns a function for evaluating it +-- @param mt metatable +-- @param fun a callable that returns a function +function utils.add_function_factory (mt,fun) + _function_factories[mt] = fun +end + +local function _string_lambda(f) + local raise = utils.raise + if f:find '^|' or f:find '_' then + local args,body = f:match '|([^|]*)|(.+)' + if f:find '_' then + args = '_' + body = f + else + if not args then return raise 'bad string lambda' end + end + local fstr = 'return function('..args..') return '..body..' end' + local fn,err = loadstring(fstr) + if not fn then return raise(err) end + fn = fn() + return fn + else return raise 'not a string lambda' + end +end + +--- an anonymous function as a string. This string is either of the form +-- '|args| expression' or is a function of one argument, '_' +-- @param lf function as a string +-- @return a function +-- @usage string_lambda '|x|x+1' (2) == 3 +-- @usage string_lambda '_+1 (2) == 3 +utils.string_lambda = utils.memoize(_string_lambda) + +local ops + +--- process a function argument. +-- This is used throughout Penlight and defines what is meant by a function: +-- Something that is callable, or an operator string as defined by pl.operator, +-- such as '>' or '#'. If a function factory has been registered for the type, it will +-- be called to get the function. +-- @param idx argument index +-- @param f a function, operator string, or callable object +-- @param msg optional error message +-- @return a callable +-- @raise if idx is not a number or if f is not callable +-- @see utils.is_callable +function utils.function_arg (idx,f,msg) + utils.assert_arg(1,idx,'number') + local tp = type(f) + if tp == 'function' then return f end -- no worries! + -- ok, a string can correspond to an operator (like '==') + if tp == 'string' then + if not ops then ops = require 'pl.operator'.optable end + local fn = ops[f] + if fn then return fn end + local fn, err = utils.string_lambda(f) + if not fn then error(err..': '..f) end + return fn + elseif tp == 'table' or tp == 'userdata' then + local mt = getmetatable(f) + if not mt then error('not a callable object',2) end + local ff = _function_factories[mt] + if not ff then + if not mt.__call then error('not a callable object',2) end + return f + else + return ff(f) -- we have a function factory for this type! + end + end + if not msg then msg = " must be callable" end + if idx > 0 then + error("argument "..idx..": "..msg,2) + else + error(msg,2) + end +end + +--- bind the first argument of the function to a value. +-- @param fn a function of at least two values (may be an operator string) +-- @param p a value +-- @return a function such that f(x) is fn(p,x) +-- @raise same as @{function_arg} +-- @see pl.func.curry +function utils.bind1 (fn,p) + fn = utils.function_arg(1,fn) + return function(...) return fn(p,...) end +end + +--- assert that the given argument is in fact of the correct type. +-- @param n argument index +-- @param val the value +-- @param tp the type +-- @param verify an optional verfication function +-- @param msg an optional custom message +-- @param lev optional stack position for trace, default 2 +-- @raise if the argument n is not the correct type +-- @usage assert_arg(1,t,'table') +-- @usage assert_arg(n,val,'string',path.isdir,'not a directory') +function utils.assert_arg (n,val,tp,verify,msg,lev) + if type(val) ~= tp then + error(("argument %d expected a '%s', got a '%s'"):format(n,tp,type(val)),2) + end + if verify and not verify(val) then + error(("argument %d: '%s' %s"):format(n,val,msg),lev or 2) + end +end + +--- assert the common case that the argument is a string. +-- @param n argument index +-- @param val a value that must be a string +-- @raise val must be a string +function utils.assert_string (n,val) + utils.assert_arg(n,val,'string',nil,nil,nil,3) +end + +local err_mode = 'default' + +--- control the error strategy used by Penlight. +-- Controls how utils.raise works; the default is for it +-- to return nil and the error string, but if the mode is 'error' then +-- it will throw an error. If mode is 'quit' it will immediately terminate +-- the program. +-- @param mode - either 'default', 'quit' or 'error' +-- @see utils.raise +function utils.on_error (mode) + err_mode = mode +end + +--- used by Penlight functions to return errors. Its global behaviour is controlled +-- by utils.on_error +-- @param err the error string. +-- @see utils.on_error +function utils.raise (err) + if err_mode == 'default' then return nil,err + elseif err_mode == 'quit' then utils.quit(err) + else error(err,2) + end +end + +raise = utils.raise + +--- load a code string or bytecode chunk. +-- @param code Lua code as a string or bytecode +-- @param name for source errors +-- @param mode kind of chunk, 't' for text, 'b' for bytecode, 'bt' for all (default) +-- @param env the environment for the new chunk (default nil) +-- @return compiled chunk +-- @return error message (chunk is nil) +-- @function utils.load + + +--- Lua 5.2 Compatible Functions +-- @section lua52 + +--- pack an argument list into a table. +-- @param ... any arguments +-- @return a table with field n set to the length +-- @return the length +-- @function table.pack + +------ +-- return the full path where a Lua module name would be matched. +-- @param mod module name, possibly dotted +-- @param path a path in the same form as package.path or package.cpath +-- @see path.package_path +-- @function package.searchpath + +return utils + + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/xml.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/xml.lua new file mode 100644 index 000000000..a452b26a0 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/xml.lua @@ -0,0 +1,676 @@ +--- XML LOM Utilities. +-- This implements some useful things on LOM documents, such as returned by lxp.lom.parse. +-- In particular, it can convert LOM back into XML text, with optional pretty-printing control. +-- It's based on stanza.lua from Prosody http://hg.prosody.im/trunk/file/4621c92d2368/util/stanza.lua) +-- +-- Can be used as a lightweight one-stop-shop for simple XML processing; a simple XML parser is included +-- but the default is to use lxp.lom if it can be found. +--
    +-- Prosody IM
    +-- Copyright (C) 2008-2010 Matthew Wild
    +-- Copyright (C) 2008-2010 Waqas Hussain
    +--
    +-- classic Lua XML parser by Roberto Ierusalimschy.
    +-- modified to output LOM format.
    +-- http://lua-users.org/wiki/LuaXml
    +-- 
    +-- @module pl.xml + +local t_insert = table.insert; +local t_concat = table.concat; +local t_remove = table.remove; +local s_format = string.format; +local s_match = string.match; +local tostring = tostring; +local setmetatable = setmetatable; +local getmetatable = getmetatable; +local pairs = pairs; +local ipairs = ipairs; +local type = type; +local next = next; +local print = print; +local unpack = unpack or table.unpack; +local s_gsub = string.gsub; +local s_char = string.char; +local s_find = string.find; +local os = os; +local pcall,require,io = pcall,require,io +local split = require 'pl.utils'.split + +local _M = {} +local Doc = { __type = "doc" }; +Doc.__index = Doc; + +--- create a new document node. +-- @param tag the tag name +-- @param attr optional attributes (table of name-value pairs) +function _M.new(tag, attr) + local doc = { tag = tag, attr = attr or {}, last_add = {}}; + return setmetatable(doc, Doc); +end + +--- parse an XML document. By default, this uses lxp.lom.parse, but +-- falls back to basic_parse, or if use_basic is true +-- @param text_or_file file or string representation +-- @param is_file whether text_or_file is a file name or not +-- @param use_basic do a basic parse +-- @return a parsed LOM document with the document metatatables set +-- @return nil, error the error can either be a file error or a parse error +function _M.parse(text_or_file, is_file, use_basic) + local parser,status,lom + if use_basic then parser = _M.basic_parse + else + status,lom = pcall(require,'lxp.lom') + if not status then parser = _M.basic_parse else parser = lom.parse end + end + if is_file then + local f,err = io.open(text_or_file) + if not f then return nil,err end + text_or_file = f:read '*a' + f:close() + end + local doc,err = parser(text_or_file) + if not doc then return nil,err end + if lom then + _M.walk(doc,false,function(_,d) + setmetatable(d,Doc) + end) + end + return doc +end + +---- convenient function to add a document node, This updates the last inserted position. +-- @param tag a tag name +-- @param attrs optional set of attributes (name-string pairs) +function Doc:addtag(tag, attrs) + local s = _M.new(tag, attrs); + (self.last_add[#self.last_add] or self):add_direct_child(s); + t_insert(self.last_add, s); + return self; +end + +--- convenient function to add a text node. This updates the last inserted position. +-- @param text a string +function Doc:text(text) + (self.last_add[#self.last_add] or self):add_direct_child(text); + return self; +end + +---- go up one level in a document +function Doc:up() + t_remove(self.last_add); + return self; +end + +function Doc:reset() + local last_add = self.last_add; + for i = 1,#last_add do + last_add[i] = nil; + end + return self; +end + +--- append a child to a document directly. +-- @param child a child node (either text or a document) +function Doc:add_direct_child(child) + t_insert(self, child); +end + +--- append a child to a document at the last element added +-- @param child a child node (either text or a document) +function Doc:add_child(child) + (self.last_add[#self.last_add] or self):add_direct_child(child); + return self; +end + +--accessing attributes: useful not to have to expose implementation (attr) +--but also can allow attr to be nil in any future optimizations + +--- set attributes of a document node. +-- @param t a table containing attribute/value pairs +function Doc:set_attribs (t) + for k,v in pairs(t) do + self.attr[k] = v + end +end + +--- set a single attribute of a document node. +-- @param a attribute +-- @param v its value +function Doc:set_attrib(a,v) + self.attr[a] = v +end + +--- access the attributes of a document node. +function Doc:get_attribs() + return self.attr +end + +--- function to create an element with a given tag name and a set of children. +-- @param tag a tag name +-- @param items either text or a table where the hash part is the attributes and the list part is the children. +function _M.elem(tag,items) + local s = _M.new(tag) + if type(items) == 'string' then items = {items} end + if _M.is_tag(items) then + t_insert(s,items) + elseif type(items) == 'table' then + for k,v in pairs(items) do + if type(k) == 'string' then + s.attr[k] = v + t_insert(s.attr,k) + else + s[k] = v + end + end + end + return s +end + +--- given a list of names, return a number of element constructors. +-- @param list a list of names, or a comma-separated string. +-- @usage local parent,children = doc.tags 'parent,children'
    +-- doc = parent {child 'one', child 'two'} +function _M.tags(list) + local ctors = {} + local elem = _M.elem + if type(list) == 'string' then list = split(list,'%s*,%s*') end + for _,tag in ipairs(list) do + local ctor = function(items) return _M.elem(tag,items) end + t_insert(ctors,ctor) + end + return unpack(ctors) +end + +local templ_cache = {} + +local function is_data(data) + return #data == 0 or type(data[1]) ~= 'table' +end + +local function prepare_data(data) + -- a hack for ensuring that $1 maps to first element of data, etc. + -- Either this or could change the gsub call just below. + for i,v in ipairs(data) do + data[tostring(i)] = v + end +end + +--- create a substituted copy of a document, +-- @param templ may be a document or a string representation which will be parsed and cached +-- @param data a table of name-value pairs or a list of such tables +-- @return an XML document +function Doc.subst(templ, data) + if type(data) ~= 'table' or not next(data) then return nil, "data must be a non-empty table" end + if is_data(data) then + prepare_data(data) + end + if type(templ) == 'string' then + if templ_cache[templ] then + templ = templ_cache[templ] + else + local str,err = templ + templ,err = _M.parse(str) + if not templ then return nil,err end + templ_cache[str] = templ + end + end + local function _subst(item) + return _M.clone(templ,function(s) + return s:gsub('%$(%w+)',item) + end) + end + if is_data(data) then return _subst(data) end + local list = {} + for _,item in ipairs(data) do + prepare_data(item) + t_insert(list,_subst(item)) + end + if data.tag then + list = _M.elem(data.tag,list) + end + return list +end + + +--- get the first child with a given tag name. +-- @param tag the tag name +function Doc:child_with_name(tag) + for _, child in ipairs(self) do + if child.tag == tag then return child; end + end +end + +local _children_with_name +function _children_with_name(self,tag,list,recurse) + for _, child in ipairs(self) do if type(child) == 'table' then + if child.tag == tag then t_insert(list,child) end + if recurse then _children_with_name(child,tag,list,recurse) end + end end +end + +--- get all elements in a document that have a given tag. +-- @param tag a tag name +-- @param dont_recurse optionally only return the immediate children with this tag name +-- @return a list of elements +function Doc:get_elements_with_name(tag,dont_recurse) + local res = {} + _children_with_name(self,tag,res,not dont_recurse) + return res +end + +-- iterate over all children of a document node, including text nodes. +function Doc:children() + local i = 0; + return function (a) + i = i + 1 + return a[i]; + end, self, i; +end + +-- return the first child element of a node, if it exists. +function Doc:first_childtag() + if #self == 0 then return end + for _,t in ipairs(self) do + if type(t) == 'table' then return t end + end +end + +function Doc:matching_tags(tag, xmlns) + xmlns = xmlns or self.attr.xmlns; + local tags = self; + local start_i, max_i = 1, #tags; + return function () + for i=start_i,max_i do + v = tags[i]; + if (not tag or v.tag == tag) + and (not xmlns or xmlns == v.attr.xmlns) then + start_i = i+1; + return v; + end + end + end, tags, i; +end + +--- iterate over all child elements of a document node. +function Doc:childtags() + local i = 0; + return function (a) + local v + repeat + i = i + 1 + v = self[i] + if v and type(v) == 'table' then return v; end + until not v + end, self[1], i; +end + +--- visit child element of a node and call a function, possibility modifying the document. +-- @param callback a function passed the node (text or element). If it returns nil, that node will be removed. +-- If it returns a value, that will replace the current node. +function Doc:maptags(callback) + local is_tag = _M.is_tag + local i = 1; + while i <= #self do + if is_tag(self[i]) then + local ret = callback(self[i]); + if ret == nil then + t_remove(self, i); + else + self[i] = ret; + i = i + 1; + end + end + end + return self; +end + +local xml_escape +do + local escape_table = { ["'"] = "'", ["\""] = """, ["<"] = "<", [">"] = ">", ["&"] = "&" }; + function xml_escape(str) return (s_gsub(str, "['&<>\"]", escape_table)); end + _M.xml_escape = xml_escape; +end + +-- pretty printing +-- if indent, then put each new tag on its own line +-- if attr_indent, put each new attribute on its own line +local function _dostring(t, buf, self, xml_escape, parentns, idn, indent, attr_indent) + local nsid = 0; + local tag = t.tag + local lf,alf = ""," " + if indent then lf = '\n'..idn end + if attr_indent then alf = '\n'..idn..attr_indent end + t_insert(buf, lf.."<"..tag); + for k, v in pairs(t.attr) do + if type(k) ~= 'number' then -- LOM attr table has list-like part + if s_find(k, "\1", 1, true) then + local ns, attrk = s_match(k, "^([^\1]*)\1?(.*)$"); + nsid = nsid + 1; + t_insert(buf, " xmlns:ns"..nsid.."='"..xml_escape(ns).."' ".."ns"..nsid..":"..attrk.."='"..xml_escape(v).."'"); + elseif not(k == "xmlns" and v == parentns) then + t_insert(buf, alf..k.."='"..xml_escape(v).."'"); + end + end + end + local len,has_children = #t; + if len == 0 then + local out = "/>" + if attr_indent then out = '\n'..idn..out end + t_insert(buf, out); + else + t_insert(buf, ">"); + for n=1,len do + local child = t[n]; + if child.tag then + self(child, buf, self, xml_escape, t.attr.xmlns,idn and idn..indent, indent, attr_indent ); + has_children = true + else -- text element + t_insert(buf, xml_escape(child)); + end + end + t_insert(buf, (has_children and lf or '')..""); + end +end + +---- pretty-print an XML document +--- @param t an XML document +--- @param idn an initial indent (indents are all strings) +--- @param indent an indent for each level +--- @param attr_indent if given, indent each attribute pair and put on a separate line +--- @return a string representation +function _M.tostring(t,idn,indent, attr_indent) + local buf = {}; + _dostring(t, buf, _dostring, xml_escape, nil,idn,indent, attr_indent); + return t_concat(buf); +end + +Doc.__tostring = _M.tostring + +--- get the full text value of an element +function Doc:get_text() + local res = {} + for i,el in ipairs(self) do + if type(el) == 'string' then t_insert(res,el) end + end + return t_concat(res); +end + +--- make a copy of a document +-- @param doc the original document +-- @param strsubst an optional function for handling string copying which could do substitution, etc. +function _M.clone(doc, strsubst) + local lookup_table = {}; + local function _copy(object) + if type(object) ~= "table" then + if strsubst and type(object) == 'string' then return strsubst(object) + else return object; + end + 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); -- is cloning keys much use, hm? + end + return setmetatable(new_table, getmetatable(object)); + end + + return _copy(doc) +end + +--- compare two documents. +-- @param t1 any value +-- @param t2 any value +function _M.compare(t1,t2) + local ty1 = type(t1) + local ty2 = type(t2) + if ty1 ~= ty2 then return false, 'type mismatch' end + if ty1 == 'string' then + return t1 == t2 and true or 'text '..t1..' ~= text '..t2 + end + if ty1 ~= 'table' or ty2 ~= 'table' then return false, 'not a document' end + if t1.tag ~= t2.tag then return false, 'tag '..t1.tag..' ~= tag '..t2.tag end + if #t1 ~= #t2 then return false, 'size '..#t1..' ~= size '..#t2..' for tag '..t1.tag end + -- compare attributes + for k,v in pairs(t1.attr) do + if t2.attr[k] ~= v then return false, 'mismatch attrib' end + end + for k,v in pairs(t2.attr) do + if t1.attr[k] ~= v then return false, 'mismatch attrib' end + end + -- compare children + for i = 1,#t1 do + local yes,err = _M.compare(t1[i],t2[i]) + if not yes then return err end + end + return true +end + +--- is this value a document element? +-- @param d any value +function _M.is_tag(d) + return type(d) == 'table' and type(d.tag) == 'string' +end + +--- call the desired function recursively over the document. +-- @param doc the document +-- @param depth_first visit child notes first, then the current node +-- @param operation a function which will receive the current tag name and current node. +function _M.walk (doc, depth_first, operation) + if not depth_first then operation(doc.tag,doc) end + for _,d in ipairs(doc) do + if _M.is_tag(d) then + _M.walk(d,depth_first,operation) + end + end + if depth_first then operation(doc.tag,doc) end +end + +local escapes = { quot = "\"", apos = "'", lt = "<", gt = ">", amp = "&" } +local function unescape(str) return (str:gsub( "&(%a+);", escapes)); end + +local function parseargs(s) + local arg = {} + s:gsub("([%w:]+)%s*=%s*([\"'])(.-)%2", function (w, _, a) + arg[w] = unescape(a) + end) + return arg +end + +--- Parse a simple XML document using a pure Lua parser based on Robero Ierusalimschy's original version. +-- @param s the XML document to be parsed. +-- @param all_text if true, preserves all whitespace. Otherwise only text containing non-whitespace is included. +function _M.basic_parse(s,all_text) + local t_insert,t_remove = table.insert,table.remove + local s_find,s_sub = string.find,string.sub + local stack = {} + local top = {} + t_insert(stack, top) + local ni,c,label,xarg, empty + local i, j = 1, 1 + -- we're not interested in + local _,istart = s_find(s,'^%s*<%?[^%?]+%?>%s*') + if istart then i = istart+1 end + while true do + ni,j,c,label,xarg, empty = s_find(s, "<(%/?)([%w:%-_]+)(.-)(%/?)>", i) + if not ni then break end + local text = s_sub(s, i, ni-1) + if all_text or not s_find(text, "^%s*$") then + t_insert(top, unescape(text)) + end + if empty == "/" then -- empty element tag + t_insert(top, setmetatable({tag=label, attr=parseargs(xarg), empty=1},Doc)) + elseif c == "" then -- start tag + top = setmetatable({tag=label, attr=parseargs(xarg)},Doc) + t_insert(stack, top) -- new level + else -- end tag + local toclose = t_remove(stack) -- remove top + top = stack[#stack] + if #stack < 1 then + error("nothing to close with "..label) + end + if toclose.tag ~= label then + error("trying to close "..toclose.tag.." with "..label) + end + t_insert(top, toclose) + end + i = j+1 + end + local text = s_sub(s, i) + if all_text or not s_find(text, "^%s*$") then + t_insert(stack[#stack], unescape(text)) + end + if #stack > 1 then + error("unclosed "..stack[#stack].tag) + end + local res = stack[1] + return type(res[1])=='string' and res[2] or res[1] +end + +local function empty(attr) return not attr or not next(attr) end +local function is_text(s) return type(s) == 'string' end +local function is_element(d) return type(d) == 'table' and d.tag ~= nil end + +-- returns the key,value pair from a table if it has exactly one entry +local function has_one_element(t) + local key,value = next(t) + if next(t,key) ~= nil then return false end + return key,value +end + +local function append_capture(res,tbl) + if not empty(tbl) then -- no point in capturing empty tables... + local key + if tbl._ then -- if $_ was set then it is meant as the top-level key for the captured table + key = tbl._ + tbl._ = nil + if empty(tbl) then return end + end + -- a table with only one pair {[0]=value} shall be reduced to that value + local numkey,val = has_one_element(tbl) + if numkey == 0 then tbl = val end + if key then + res[key] = tbl + else -- otherwise, we append the captured table + t_insert(res,tbl) + end + end +end + +local function make_number(pat) + if pat:find '^%d+$' then -- $1 etc means use this as an array location + pat = tonumber(pat) + end + return pat +end + +local function capture_attrib(res,pat,value) + pat = make_number(pat:sub(2)) + res[pat] = value + return true +end + +local match +function match(d,pat,res,keep_going) + local ret = true + if d == nil then return false end + -- attribute string matching is straight equality, except if the pattern is a $ capture, + -- which always succeeds. + if type(d) == 'string' then + if type(pat) ~= 'string' then return false end + if _M.debug then print(d,pat) end + if pat:find '^%$' then + return capture_attrib(res,pat,d) + else + return d == pat + end + else + if _M.debug then print(d.tag,pat.tag) end + -- this is an element node. For a match to succeed, the attributes must + -- match as well. + -- a tagname in the pattern ending with '-' is a wildcard and matches like an attribute + local tagpat = pat.tag:match '^(.-)%-$' + if tagpat then + tagpat = make_number(tagpat) + res[tagpat] = d.tag + end + if d.tag == pat.tag or tagpat then + + if not empty(pat.attr) then + if empty(d.attr) then ret = false + else + for prop,pval in pairs(pat.attr) do + local dval = d.attr[prop] + if not match(dval,pval,res) then ret = false; break end + end + end + end + -- the pattern may have child nodes. We match partially, so that {P1,P2} shall match {X,P1,X,X,P2,..} + if ret and #pat > 0 then + local i,j = 1,1 + local function next_elem() + j = j + 1 -- next child element of data + if is_text(d[j]) then j = j + 1 end + return j <= #d + end + repeat + local p = pat[i] + -- repeated {{<...>}} patterns shall match one or more elements + -- so e.g. {P+} will match {X,X,P,P,X,P,X,X,X} + if is_element(p) and p.repeated then + local found + repeat + local tbl = {} + ret = match(d[j],p,tbl,false) + if ret then + found = false --true + append_capture(res,tbl) + end + until not next_elem() or (found and not ret) + i = i + 1 + else + ret = match(d[j],p,res,false) + if ret then i = i + 1 end + end + until not next_elem() or i > #pat -- run out of elements or patterns to match + -- if every element in our pattern matched ok, then it's been a successful match + if i > #pat then return true end + end + if ret then return true end + else + ret = false + end + -- keep going anyway - look at the children! + if keep_going then + for child in d:childtags() do + ret = match(child,pat,res,keep_going) + if ret then break end + end + end + end + return ret +end + +function Doc:match(pat) + if is_text(pat) then + pat = _M.parse(pat,false,true) + end + _M.walk(pat,false,function(_,d) + if is_text(d[1]) and is_element(d[2]) and is_text(d[3]) and + d[1]:find '%s*{{' and d[3]:find '}}%s*' then + t_remove(d,1) + t_remove(d,2) + d[1].repeated = true + end + end) + + local res = {} + local ret = match(self,pat,res,true) + return res,ret +end + + +return _M + diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/file.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/file.lua new file mode 100644 index 000000000..e562c9d55 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/file.lua @@ -0,0 +1,106 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2012-2014 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Kevin KIN-FOO +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +return[[# +
    +# -- +# -- Module name +# -- +# if _file.name then + Module $(_file.name) +# end +# -- +# -- Descriptions +# -- +# if _file.shortdescription then + $( format(_file.shortdescription) ) +# end +# if _file.description and #_file.description > 0 then + $( format(_file.description) ) +# end +# -- +# -- Handle "@usage" special tag +# -- +#if _file.metadata and _file.metadata.usage then + $( applytemplate(_file.metadata.usage, i+1) ) +#end +# -- +# -- Show quick description of current type +# -- +# +# -- show quick description for globals +# if not isempty(_file.globalvars) then + Global(s) + +# for _, item in sortedpairs(_file.globalvars) do + + + + +# end +
    $( fulllinkto(item) )$( format(item.shortdescription) )
    +# end +# +# -- get type corresponding to this file (module) +# local currenttype +# local typeref = _file:moduletyperef() +# if typeref and typeref.tag == "internaltyperef" then +# local typedef = _file.types[typeref.typename] +# if typedef and typedef.tag == "recordtypedef" then +# currenttype = typedef +# end +# end +# +# -- show quick description type exposed by module +# if currenttype and not isempty(currenttype.fields) then + Type $(currenttype.name) + $( applytemplate(currenttype, i+2, 'index') ) +# end +# -- +# -- Show quick description of other types +# -- +# if _file.types then +# for name, type in sortedpairs( _file.types ) do +# if type ~= currenttype and type.tag == 'recordtypedef' and not isempty(type.fields) then + Type $(name) + $( applytemplate(type, i+2, 'index') ) +# end +# end +# end +# -- +# -- Long description of globals +# -- +# if not isempty(_file.globalvars) then + Global(s) +# for name, item in sortedpairs(_file.globalvars) do + $( applytemplate(item, i+2) ) +# end +# end +# -- +# -- Long description of current type +# -- +# if currenttype then + Type $(currenttype.name) + $( applytemplate(currenttype, i+2) ) +# end +# -- +# -- Long description of other types +# -- +# if not isempty( _file.types ) then +# for name, type in sortedpairs( _file.types ) do +# if type ~= currenttype and type.tag == 'recordtypedef' then + Type $(name) + $( applytemplate(type, i+2) ) +# end +# end +# end +
    +]] diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/index.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/index.lua new file mode 100644 index 000000000..555c2d373 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/index.lua @@ -0,0 +1,28 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2012-2014 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Kevin KIN-FOO +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +return +[[# +#if _index.modules then +
    +

    Module$( #_index.modules > 1 and 's' )

    + +# for _, module in sortedpairs( _index.modules ) do +# if module.tag ~= 'index' then + + + + +# end +# end +
    $( fulllinkto(module) )$( module.description and format(module.shortdescription) )
    +
    +#end ]] diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/index/recordtypedef.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/index/recordtypedef.lua new file mode 100644 index 000000000..8dfdbc4ce --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/index/recordtypedef.lua @@ -0,0 +1,23 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2012-2014 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Kevin KIN-FOO +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +return [[# +# if not isempty(_recordtypedef.fields) then + +# for _, item in sortedpairs( _recordtypedef.fields ) do + + + + +# end +
    $( fulllinkto(item) )$( format(item.shortdescription) )
    +# end +# ]] diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/item.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/item.lua new file mode 100644 index 000000000..122006185 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/item.lua @@ -0,0 +1,167 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2012-2014 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Kevin KIN-FOO +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +return +[[
    +
    +# -- +# -- Resolve item type definition +# -- +# local typedef = _item:resolvetype() + +# -- +# -- Show item type for internal type +# -- +#if _item.type and (not typedef or typedef.tag ~= 'functiontypedef') then +# --Show link only when available +# local link = fulllinkto(_item.type) +# if link then + $( link ) +# else + $(prettyname(_item.type)) +# end +#end + +$( prettyname(_item) ) + +
    +
    +# if _item.shortdescription then + $( format(_item.shortdescription) ) +# end +# if _item.description and #_item.description > 0 then + $( format(_item.description) ) +# end +# +# -- +# -- For function definitions, describe parameters and return values +# -- +#if typedef and typedef.tag == 'functiontypedef' then +# -- +# -- Describe parameters +# -- +# local fdef = typedef +# +# -- Adjust parameter count if first one is 'self' +# local paramcount +# if #fdef.params > 0 and isinvokable(_item) then +# paramcount = #fdef.params - 1 +# else +# paramcount = #fdef.params +# end +# +# -- List parameters +# if paramcount > 0 then + Parameter$( paramcount > 1 and 's' ) +
      +# for position, param in ipairs( fdef.params ) do +# if not (position == 1 and isinvokable(_item)) then +
    • +# local paramline = "" +# if param.type then +# local link = linkto( param.type ) +# local name = prettyname( param.type ) +# if link then +# paramline = paramline .. '' .. name .. "" +# else +# paramline = paramline .. name +# end +# end +# +# paramline = paramline .. " " .. param.name .. " " +# +# if param.optional then +# paramline = paramline .. "optional" .. " " +# end +# if param.hidden then +# paramline = paramline .. "hidden" +# end +# +# paramline = paramline .. ": " +# +# if param.description and #param.description > 0 then +# paramline = paramline .. "\n" .. param.description +# end +# + $( format (paramline)) +
    • +# end +# end +
    +# end +# +# -- +# -- Describe returns types +# -- +# if fdef and #fdef.returns > 0 then + Return value$(#fdef.returns > 1 and 's') +# -- +# -- Format nice type list +# -- +# local function niceparmlist( parlist ) +# local typelist = {} +# for position, type in ipairs(parlist) do +# local link = linkto( type ) +# local name = prettyname( type ) +# if link then +# typelist[#typelist + 1] = ''..name..'' +# else +# typelist[#typelist + 1] = name +# end +# -- Append end separator or separating comma +# typelist[#typelist + 1] = position == #parlist and ':' or ', ' +# end +# return table.concat( typelist ) +# end +# -- +# -- Generate a list if they are several return clauses +# -- +# if #fdef.returns > 1 then +
      +# for position, ret in ipairs(fdef.returns) do +
    1. +# local returnline = ""; +# +# local paramlist = niceparmlist(ret.types) +# if #ret.types > 0 and #paramlist > 0 then +# returnline = "" .. paramlist .. "" +# end +# returnline = returnline .. "\n" .. ret.description + $( format (returnline)) +
    2. +# end +
    +# else +# local paramlist = niceparmlist(fdef.returns[1].types) +# local isreturn = fdef.returns and #fdef.returns > 0 and #paramlist > 0 +# local isdescription = fdef.returns and fdef.returns[1].description and #format(fdef.returns[1].description) > 0 +# +# local returnline = ""; +# -- Show return type if provided +# if isreturn then +# returnline = ""..paramlist.."" +# end +# if isdescription then +# returnline = returnline .. "\n" .. fdef.returns[1].description +# end + $( format(returnline)) +# end +# end +#end +# +#-- +#-- Show usage samples +#-- +#if _item.metadata and _item.metadata.usage then + $( applytemplate(_item.metadata.usage, i) ) +#end +
    +
    ]] diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/page.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/page.lua new file mode 100644 index 000000000..32e1957c4 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/page.lua @@ -0,0 +1,68 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2012-2014 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Kevin KIN-FOO +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +return +[[ + +#if _page.headers and #_page.headers > 0 then + +# for _, header in ipairs(_page.headers) do + $(header) +# end + +#end + +
    +
    + +
    +
    +
    +
    +# -- +# -- Generating lateral menu +# -- + + $( applytemplate(_page.currentmodule) ) +
    + + +]] diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/recordtypedef.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/recordtypedef.lua new file mode 100644 index 000000000..758f1e0b0 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/recordtypedef.lua @@ -0,0 +1,36 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2012-2014 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Kevin KIN-FOO +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +return [[# +# -- +# -- Descriptions +# -- +#if _recordtypedef.shortdescription and #_recordtypedef.shortdescription > 0 then + $( format( _recordtypedef.shortdescription ) ) +#end +#if _recordtypedef.description and #_recordtypedef.description > 0 then + $( format( _recordtypedef.description ) ) +#end +#-- +#-- Describe usage +#-- +#if _recordtypedef.metadata and _recordtypedef.metadata.usage then + $( applytemplate(_recordtypedef.metadata.usage, i) ) +#end +# -- +# -- Describe type fields +# -- +#if not isempty( _recordtypedef.fields ) then + Field(s) +# for name, item in sortedpairs( _recordtypedef.fields ) do + $( applytemplate(item, i) ) +# end +#end ]] diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/usage.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/usage.lua new file mode 100644 index 000000000..93a8c7148 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/usage.lua @@ -0,0 +1,33 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2012-2014 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Marc AUBRY +-- - initial API and implementation +-------------------------------------------------------------------------------- +return[[# +#-- +#-- Show usage samples +#-- +#if _usage then +# if #_usage > 1 then +# -- Show all usages + Usages: +
      +# -- Loop over several usage description +# for _, usage in ipairs(_usage) do +
    • $( securechevrons(usage.description) )
    • +# end +
    +# elseif #_usage == 1 then +# -- Show unique usage sample + Usage: +# local usage = _usage[1] +
    $( securechevrons(usage.description) )
    +# end +#end +#]] diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/utils.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/utils.lua new file mode 100644 index 000000000..f2fd01f40 --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/utils.lua @@ -0,0 +1,470 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2012-2014 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Kevin KIN-FOO +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +local apimodel = require 'models.apimodel' + +--- +-- @module docutils +-- Handles link generation, node quick description. +-- +-- Provides: +-- * link generation +-- * anchor generation +-- * node quick description +local M = {} + +function M.isempty(map) + local f = pairs(map) + return f(map) == nil +end + +--- +-- Provide a handling function for all supported anchor types +-- recordtypedef => #(typename) +-- item (field of recordtypedef) => #(typename).itemname +-- item (global) => itemname +M.anchortypes = { + recordtypedef = function (o) return string.format('#(%s)', o.name) end, + item = function(o) + if not o.parent or o.parent.tag == 'file' then + -- Handle items referencing globals + return o.name + elseif o.parent and o.parent.tag == 'recordtypedef' then + -- Handle items included in recordtypedef + local recordtypedef = o.parent + local recordtypedefanchor = M.anchor(recordtypedef) + if not recordtypedefanchor then + return nil, 'Unable to generate anchor for `recordtypedef parent.' + end + return string.format('%s.%s', recordtypedefanchor, o.name) + end + return nil, 'Unable to generate anchor for `item' + end +} + +--- +-- Provides anchor string for an object of API mode +-- +-- @function [parent = #docutils] anchor +-- @param modelobject Object form API model +-- @result #string Anchor for an API model object, this function __may rise an error__ +-- @usage # -- In a template +-- # local anchorname = anchor(someobject) +-- +function M.anchor( modelobject ) + local tag = modelobject.tag + if M.anchortypes[ tag ] then + return M.anchortypes[ tag ](modelobject) + end + return nil, string.format('No anchor available for `%s', tag) +end + +local function getexternalmodule( item ) + -- Get file which contains this item + local file + if item.parent then + if item.parent.tag =='recordtypedef' then + local recordtypedefparent = item.parent.parent + if recordtypedefparent and recordtypedefparent.tag =='file'then + file = recordtypedefparent + end + elseif item.parent.tag =='file' then + file = item.parent + else + return nil, 'Unable to fetch item parent' + end + end + return file +end + +--- +-- Provide a handling function for all supported link types +-- +-- internaltyperef => ##(typename) +-- => #anchor(recordtyperef) +-- externaltyperef => modulename.html##(typename) +-- => linkto(file)#anchor(recordtyperef) +-- file(module) => modulename.html +-- index => index.html +-- recordtypedef => ##(typename) +-- => #anchor(recordtyperef) +-- item (internal field of recordtypedef) => ##(typename).itemname +-- => #anchor(item) +-- item (internal global) => #itemname +-- => #anchor(item) +-- item (external field of recordtypedef) => modulename.html##(typename).itemname +-- => linkto(file)#anchor(item) +-- item (externalglobal) => modulename.html#itemname +-- => linkto(file)#anchor(item) +M.linktypes = { + internaltyperef = function(o) return string.format('##(%s)', o.typename) end, + externaltyperef = function(o) return string.format('%s.html##(%s)', o.modulename, o.typename) end, + file = function(o) return string.format('%s.html', o.name) end, + index = function() return 'index.html' end, + recordtypedef = function(o) + local anchor = M.anchor(o) + if not anchor then + return nil, 'Unable to generate anchor for `recordtypedef.' + end + return string.format('#%s', anchor) + end, + item = function(o) + + -- For every item get anchor + local anchor = M.anchor(o) + if not anchor then + return nil, 'Unable to generate anchor for `item.' + end + + -- Built local link to item + local linktoitem = string.format('#%s', anchor) + + -- + -- For external item, prefix with the link to the module. + -- + -- The "external item" concept is used only here for short/embedded + -- notation purposed. This concept and the `.external` field SHALL NOT + -- be used elsewhere. + -- + if o.external then + + -- Get link to file which contains this item + local file = getexternalmodule( o ) + local linktofile = file and M.linkto( file ) + if not linktofile then + return nil, 'Unable to generate link for external `item.' + end + + -- Built external link to item + linktoitem = string.format("%s%s", linktofile, linktoitem) + end + + return linktoitem + end +} + +--- +-- Generates text for HTML links from API model element +-- +-- @function [parent = #docutils] +-- @param modelobject Object form API model +-- @result #string Links text for an API model element, this function __may rise an error__. +-- @usage # -- In a template +-- Some text +function M.linkto( apiobject ) + local tag = apiobject.tag + if M.linktypes[ tag ] then + return M.linktypes[tag](apiobject) + end + if not tag then + return nil, 'Link generation is impossible as no tag has been provided.' + end + return nil, string.format('No link generation available for `%s.', tag) +end + +--- +-- Provide a handling function for all supported pretty name types +-- primitivetyperef => #typename +-- internaltyperef => #typename +-- externaltyperef => modulename#typename +-- file(module) => modulename +-- index => index +-- recordtypedef => typename +-- item (internal function of recordtypedef) => typename.itemname(param1, param2,...) +-- item (internal func with self of recordtypedef) => typename:itemname(param2) +-- item (internal non func field of recordtypedef) => typename.itemname +-- item (internal func global) => functionname(param1, param2,...) +-- item (internal non func global) => itemname +-- item (external function of recordtypedef) => modulename#typename.itemname(param1, param2,...) +-- item (external func with self of recordtypedef) => modulename#typename:itemname(param2) +-- item (external non func field of recordtypedef) => modulename#typename.itemname +-- item (external func global) => functionname(param1, param2,...) +-- item (external non func global) => itemname +M.prettynametypes = { + primitivetyperef = function(o) return string.format('#%s', o.typename) end, + externaltyperef = function(o) return string.format('%s#%s', o.modulename, o.typename) end, + index = function(o) return "index" end, + file = function(o) return o.name end, + recordtypedef = function(o) return o.name end, + item = function( o ) + + -- Determine item name + -- ---------------------- + local itemname = o.name + + -- Determine scope + -- ---------------------- + local parent = o.parent + local isglobal = parent and parent.tag == 'file' + local isfield = parent and parent.tag == 'recordtypedef' + + -- Determine type name + -- ---------------------- + + local typename = isfield and parent.name + + -- Fetch item definition + -- ---------------------- + -- Get file object + local file + if isglobal then + file = parent + elseif isfield then + file = parent.parent + end + -- Get definition + local definition = o:resolvetype (file) + + + + -- Build prettyname + -- ---------------------- + local prettyname + if not definition or definition.tag ~= 'functiontypedef' then + -- Fields + if isglobal or not typename then + prettyname = itemname + else + prettyname = string.format('%s.%s', typename, itemname) + end + else + -- Functions + -- Build parameter list + local paramlist = {} + local isinvokable = M.isinvokable(o) + for position, param in ipairs(definition.params) do + -- For non global function, when first parameter is 'self', + -- it will not be part of listed parameters + if not (position == 1 and isinvokable and isfield) then + table.insert(paramlist, param.name) + if position ~= #definition.params then + table.insert(paramlist, ', ') + end + end + end + + if isglobal or not typename then + prettyname = string.format('%s(%s)',itemname, table.concat(paramlist)) + else + -- Determine function prefix operator, + -- ':' if 'self' is first parameter, '.' else way + local operator = isinvokable and ':' or '.' + + -- Append function parameters + prettyname = string.format('%s%s%s(%s)',typename, operator, itemname, table.concat(paramlist)) + end + end + + -- Manage external Item prettyname + -- ---------------------- + local externalmodule = o.external and getexternalmodule( o ) + local externalmodulename = externalmodule and externalmodule.name + + if externalmodulename then + return string.format('%s#%s',externalmodulename,prettyname) + else + return prettyname + end + end +} +M.prettynametypes.internaltyperef = M.prettynametypes.primitivetyperef + +--- +-- Check if the given item is a function that can be invoked +function M.isinvokable(item) + --test if the item is global + if item.parent and item.parent.tag == 'file' then + return false + end + -- check first param + local definition = item:resolvetype() + if definition and definition.tag == 'functiontypedef' then + if (#definition.params > 0) then + return definition.params[1].name == 'self' + end + end +end + +--- +-- Provide human readable overview from an API model element +-- +-- Resolve all element needed to summurize nicely an element form API model. +-- @usage $ print( prettyname(item) ) +-- module:somefunction(secondparameter) +-- @function [parent = #docutils] +-- @param apiobject Object form API model +-- @result #string Human readable description of given element. +-- @result #nil, #string In case of error. +function M.prettyname( apiobject ) + local tag = apiobject.tag + if M.prettynametypes[tag] then + return M.prettynametypes[tag](apiobject) + elseif not tag then + return nil, 'No pretty name available as no tag has been provided.' + end + return nil, string.format('No pretty name for `%s.', tag) +end + +--- +-- Just make a string print table in HTML. +-- @function [parent = #docutils] securechevrons +-- @param #string String to convert. +-- @usage securechevrons('') => '<markup>' +-- @return #string Converted string. +function M.securechevrons( str ) + if not str then return nil, 'String expected.' end + return string.gsub(str:gsub('<', '<'), '>', '>') +end + +------------------------------------------------------------------------------- +-- Handling format of @{some#type} tag. +-- Following functions enable to recognize several type of references between +-- "{}". +------------------------------------------------------------------------------- + +--- +-- Provide API Model elements from string describing global elements +-- such as: +-- * `global#foo` +-- * `foo#global.bar` +local globals = function(str) + -- Handling globals from modules + for modulename, fieldname in str:gmatch('([%a%.%d_]+)#global%.([%a%.%d_]+)') do + local item = apimodel._item(fieldname) + local file = apimodel._file() + file.name = modulename + file:addglobalvar( item ) + return item + end + -- Handling other globals + for name in str:gmatch('global#([%a%.%d_]+)') do + -- print("globale", name) + return apimodel._externaltypref('global', name) + end + return nil +end + +--- +-- Transform a string like `module#(type).field` in an API Model item +local field = function( str ) + + -- Match `module#type.field` + local mod, typename, fieldname = str:gmatch('([%a%.%d_]*)#([%a%.%d_]+)%.([%a%.%d_]+)')() + + -- Try matching `module#(type).field` + if not mod then + mod, typename, fieldname = str:gmatch('([%a%.%d_]*)#%(([%a%.%d_]+)%)%.([%a%.%d_]+)')() + if not mod then + -- No match + return nil + end + end + + -- Build according `item + local modulefielditem = apimodel._item( fieldname ) + local moduletype = apimodel._recordtypedef(typename) + moduletype:addfield( modulefielditem ) + local typeref + if #mod > 0 then + local modulefile = apimodel._file() + modulefile.name = mod + modulefile:addtype( moduletype ) + typeref = apimodel._externaltypref(mod, typename) + modulefielditem.external = true + else + typeref = apimodel._internaltyperef(typename) + end + modulefielditem.type = typeref + return modulefielditem +end + +--- +-- Build an API internal reference from a string like: `#typeref` +local internal = function ( typestring ) + for name in typestring:gmatch('#([%a%.%d_]+)') do + -- Do not handle this name is it starts with reserved name "global" + if name:find("global.") == 1 then return nil end + return apimodel._internaltyperef(name) + end + return nil +end + +--- +-- Build an API external reference from a string like: `mod.ule#type` +local extern = function (type) + + -- Match `mod.ule#ty.pe` + local modulename, typename = type:gmatch('([%a%.%d_]+)#([%a%.%d_]+)')() + + -- Trying `mod.ule#(ty.pe)` + if not modulename then + modulename, typename = type:gmatch('([%a%.%d_]+)#%(([%a%.%d_]+)%)')() + + -- No match at all + if not modulename then + return nil + end + end + return apimodel._externaltypref(modulename, typename) +end + +--- +-- Build an API external reference from a string like: `mod.ule` +local file = function (type) + for modulename in type:gmatch('([%a%.%d_]+)') do + local file = apimodel._file() + file.name = modulename + return file + end + return nil +end + + +--- +-- Provide API Model element from a string +-- @usage local externaltyperef = getelement("somemodule#somefield") +function M.getelement( str ) + + -- Order matters, more restrictive are at begin of table + local extractors = { + globals, + field, + extern, + internal, + file + } + -- Loop over extractors. + -- First valid result is used + for _, extractor in ipairs( extractors ) do + local result = extractor( str ) + if result then return result end + end + return nil +end + +-------------------------------------------------------------------------------- +-- Iterator that iterates on the table in key ascending order. +-- +-- @function [parent=#utils.table] sortedPairs +-- @param t table to iterate. +-- @return iterator function. +function M.sortedpairs(t) + local a = {} + local insert = table.insert + for n in pairs(t) do insert(a, n) end + table.sort(a) + local i = 0 + return function() + i = i + 1 + return a[i], t[a[i]] + end +end +return M diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/templateengine.lua b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/templateengine.lua new file mode 100644 index 000000000..efa976b8a --- /dev/null +++ b/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/templateengine.lua @@ -0,0 +1,116 @@ +-------------------------------------------------------------------------------- +-- Copyright (c) 2011-2012 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Kevin KIN-FOO +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- +--- +-- This library provide html description of elements from the externalapi +local M = {} + +-- Load template engine +local pltemplate = require 'pl.template' + +-- Markdown handling +local markdown = require 'markdown' + +-- apply template to the given element +function M.applytemplate(elem, ident, templatetype) + -- define environment + local env = M.getenv(elem, ident) + + -- load template + local template = M.gettemplate(elem,templatetype) + if not template then + templatetype = templatetype and string.format(' "%s"', templatetype) or '' + local elementname = string.format(' for %s', elem.tag or 'untagged element') + error(string.format('Unable to load %s template %s', templatetype, elementname)) + end + + -- apply template + local str, err = pltemplate.substitute(template, env) + + --manage errors + if not str then + local templateerror = templatetype and string.format(' parsing "%s" template ', templatetype) or '' + error(string.format('An error occured%s for "%s"\n%s',templateerror, elem.tag, err)) + end + return str +end + +-- get the a new environment for this element +function M.getenv(elem, ident) + local currentenv ={} + for k,v in pairs(M.env) do currentenv[k] = v end + if elem and elem.tag then + currentenv['_'..elem.tag]= elem + end + currentenv['i']= ident or 1 + return currentenv +end + +-- get the template for this element +function M.gettemplate(elem,templatetype) + local tag = elem and elem.tag + if tag then + if templatetype then + return require ("template." .. templatetype.. "." .. tag) + else + return require ("template." .. tag) + end + end +end + + +--- +-- Allow user to format text in descriptions. +-- Default implementation replaces @{---} tags with links and apply markdown. +-- @return #string +local function format(string) + -- Allow to replace encountered tags with valid links + local replace = function(found) + local apiobj = M.env.getelement(found) + if apiobj then + return M.env.fulllinkto(apiobj) + end + return found + end + string = string:gsub('@{%s*(.-)%s*}', replace) + return M.env.markdown( string ) +end +--- +-- Provide a full link to an element using `prettyname` and `linkto`. +-- Default implementation is for HTML. +local function fulllinkto(o) + local ref = M.env.linkto(o) + local name = M.env.prettyname(o) + if not ref then + return name + end + return string.format('%s', ref, name) +end +-- +-- Define default template environnement +-- +local defaultenv = { + table = table, + ipairs = ipairs, + pairs = pairs, + markdown = markdown, + applytemplate = M.applytemplate, + format = format, + linkto = function(str) return str end, + fulllinkto = fulllinkto, + prettyname = function(s) return s end, + getelement = function(s) return nil end +} + +-- this is the global env accessible in the templates +-- env should be redefine by docgenerator user to add functions or redefine it. +M.env = defaultenv +return M diff --git a/Moose Development Evironment Setup/MooseDevelopmentEnvironmentSetup.au3 b/Moose Development Evironment Setup/MooseDevelopmentEnvironmentSetup.au3 new file mode 100644 index 000000000..d12fc405a --- /dev/null +++ b/Moose Development Evironment Setup/MooseDevelopmentEnvironmentSetup.au3 @@ -0,0 +1,246 @@ +; MooseDevelopmentEnvironmentSetup.exe +; ------------------------------------ +; This program sets up the Moose Development Evironment for Testers and Developers. +; The goal is to make it easy to use the Dynamic Loading Moose.lua, which is more suitable for rapid development and regular changes +; than its static counterpart. +; +; Author : Hugues "GreyEcho" Bousquet + +#include +#include +#include +#include +#include +#include +#include +#include +#Include +#Include +#include + +Global $7zipPath +Global $DCSWorldPath +Global $RepoPath +Global $DCSWorldScriptsMoosePath +Global $MooseDevFolderPath +Global $Log +Global $ProgramFilesDir = @HomeDrive & '\Program Files\' + + +Func CleanExit() + _FileWriteLog($Log, 'INFO:'&@TAB&'Program exited cleanly'&@CRLF) + FileClose($Log) + Exit +EndFunc + +Func Welcome() + #Region ### START Koda GUI section ### Form= + $Form2 = GUICreate("Welcome", 532, 150, 620, 457) + $Label1 = GUICtrlCreateLabel("Welcome to Moose ! ", 120, 16, 217, 33) + GUICtrlSetFont(-1, 18, 800, 0, "Calibri") + $Label2 = GUICtrlCreateLabel("This tool is designed to help you setup your Moose development environment.", 104, 56, 370, 17) + $Button1 = GUICtrlCreateButton("&OK", 268, 115, 75, 25) + $Button2 = GUICtrlCreateButton("&Cancel", 187, 116, 75, 25) + $Label3 = GUICtrlCreateLabel("Before you proceed, please make sure that you correctly installed GitHub, as well as 7-zip.", 104, 80, 423, 17) + $Pic1 = GUICtrlCreatePic("C:\Users\Hugues\Desktop\Moose\MOOSE_Logo_Primary_Color.jpg", 8, 8, 89, 89) + GUISetState(@SW_SHOW) + #EndRegion ### END Koda GUI section ### + + _FileWriteLog($Log, 'INFO:'&@TAB&'In window "Welcome"'&@CRLF) + + While 1 + $nMsg = GUIGetMsg() + Switch $nMsg + case $GUI_EVENT_CLOSE + GUIDelete() + CleanExit() + case $Button2 + GUIDelete() + CleanExit() + case $Button1 + GUIDelete() + ExitLoop + EndSwitch + WEnd + +EndFunc + +Func FoldersLocation() + #Region ### START Koda GUI section ### Form= + $Form1 = GUICreate("Location of your Folders", 603, 237, 585, 425) + $GroupBox1 = GUICtrlCreateGroup("Folder Locations ", 8, 9, 585, 185) + $Input1 = GUICtrlCreateInput("C:\Program Files\7-Zip\", 24, 48, 505, 21) + $Label1 = GUICtrlCreateLabel("7-Zip Location", 24, 32, 72, 17) + $Input2 = GUICtrlCreateInput("C:\Program Files\Eagle Dynamics\DCS World\", 24, 104, 505, 21) + $Label2 = GUICtrlCreateLabel("DCS World Install Location", 24, 88, 131, 17) + $Input3 = GUICtrlCreateInput("C:\Users\Hugues\Documents\GitHub\MOOSE\", 24, 160, 505, 21) + $Label3 = GUICtrlCreateLabel("MOOSE Local Repository Location", 24, 144, 169, 17) + $Button3 = GUICtrlCreateButton("Browse", 528, 48, 57, 21) + $Button4 = GUICtrlCreateButton("Browse", 528, 104, 57, 21) + $Button5 = GUICtrlCreateButton("Browse", 528, 160, 57, 21) + GUICtrlCreateGroup("", -99, -99, 1, 1) + $Button1 = GUICtrlCreateButton("&OK", 308, 203, 75, 25) + $Button2 = GUICtrlCreateButton("&Cancel", 219, 204, 75, 25) + GUISetState(@SW_SHOW) + #EndRegion ### END Koda GUI section ### + + _FileWriteLog($Log, 'INFO:'&@TAB&'In window "Folders Location"'&@CRLF) + + While 1 + $nMsg = GUIGetMsg() + Switch $nMsg + case $GUI_EVENT_CLOSE + GUIDelete() + CleanExit() + case $Button2 + GUIDelete() + CleanExit() + ; Browse buttons + case $Button3 + $7zipPath = FileSelectFolder("Select the 7-Zip Installation Folder", $ProgramFilesDir) + If $7zipPath Then + GUICtrlSetData($Input1, $7zipPath) + EndIf + case $Button4 + $DCSWorldPath = FileSelectFolder("Select the DCS World Installation Folder", $ProgramFilesDir) + If $DCSWorldPath Then + GUICtrlSetData($Input2, $DCSWorldPath) + EndIf + case $Button5 + $RepoPath = FileSelectFolder("Select the local MOOSE GitHub Repository Folder", @MyDocumentsDir) + If $RepoPath Then + GUICtrlSetData($Input3, $RepoPath) + EndIf + ; ok ! + case $Button1 + If FileExists(GUICtrlRead($Input1)) and FileExists(GUICtrlRead($Input2)) and FileExists(GUICtrlRead($Input3)) Then + $7zipPath = GUICtrlRead($Input1) + $DCSWorldPath = GUICtrlRead($Input2) + $RepoPath = GUICtrlRead($Input3) + + ; add trailing '\' when necessary + If StringRight($7zipPath, 1) <> "\" Then + $7zipPath &= "\" + EndIf + If StringRight($DCSWorldPath, 1) <> "\" Then + $DCSWorldPath &= "\" + EndIf + If StringRight($RepoPath, 1) <> "\" Then + $RepoPath &= "\" + EndIf + + DirCreate($DCSWorldPath&'Scripts\Moose\') + $DCSWorldScriptsMoosePath = $DCSWorldPath & 'Scripts\Moose\' + $MooseDevFolderPath = $RepoPath & 'Moose Development\Moose\' + + _FileWriteLog($Log, 'INFO:'&@TAB&'7Zip Path : '&$7zipPath&@CRLF) + _FileWriteLog($Log, 'INFO:'&@TAB&'DCS World Path : '&$DCSWorldPath&@CRLF) + _FileWriteLog($Log, 'INFO:'&@TAB&'Moose Repo Path : '&$RepoPath&@CRLF) + _FileWriteLog($Log, 'INFO:'&@TAB&'DCS World Scripts Path : '&$DCSWorldScriptsMoosePath&@CRLF) + _FileWriteLog($Log, 'INFO:'&@TAB&'Moose Development Folder Path : '&$MooseDevFolderPath&@CRLF) + GUIDelete() + ExitLoop + Else + MsgBox(16, "Error", "One of the file paths is invalid, please check again.") ; TODO : Which one is wrong ? + _FileWriteLog($Log, 'ERROR:'&@TAB&'One of the paths is invalid'&@CRLF) + _FileWriteLog($Log, 'INFO:'&@TAB&'7Zip Path : '&$7zipPath&@CRLF) + _FileWriteLog($Log, 'INFO:'&@TAB&'DCS World Path : '&$DCSWorldPath&@CRLF) + _FileWriteLog($Log, 'INFO:'&@TAB&'Moose Repo Path : '&$RepoPath&@CRLF) + EndIf + EndSwitch + Wend +EndFunc + +Func SetupInProgress() + #Region ### START Koda GUI section ### Form= + $Form3 = GUICreate("Setup In Progress", 522, 237, 638, 427) + $Button1 = GUICtrlCreateButton("&OK", 223, 203, 75, 25) + $Edit1 = GUICtrlCreateEdit("", 8, 8, 505, 185) + GUISetState(@SW_SHOW) + #EndRegion ### END Koda GUI section ### + + GUICtrlSetState($Button1, $GUI_DISABLE) + + local $InstallSuccessfull = 1 + + _FileWriteLog($Log, 'INFO:'&@TAB&'In window "SetupInProgress"'&@CRLF) + + local $TrimmedMooseDevFolderPath = StringTrimRight($MooseDevFolderPath, 1) + + ; Create the Dynamic Link + If FileCreateNTFSLink($TrimmedMooseDevFolderPath, $DCSWorldScriptsMoosePath, $FC_OVERWRITE) Then + _FileWriteLog($Log, 'INFO:'&@TAB&"Hard Link created for "&$TrimmedMooseDevFolderPath&" in "&$DCSWorldScriptsMoosePath&@CRLF) + _GUICtrlEdit_AppendText($Edit1, "Hard Link Ccreated... Ok!"&@CRLF) + Else + _FileWriteLog($Log, 'ERROR:'&@TAB&"Couldn't create a hard link for "&$TrimmedMooseDevFolderPath&" in "&$DCSWorldScriptsMoosePath&@CRLF) + _GUICtrlEdit_AppendText($Edit1, "ERROR : Couldn't create a hard link for "&$TrimmedMooseDevFolderPath&" in "&$DCSWorldScriptsMoosePath&@CRLF) + $InstallSuccessfull = 0 + EndIf + + ; Get the current PATH and append 7Zip's path to it + local $NewPathContent = EnvGet("PATH") + If StringRight($NewPathContent, 1) <> ";" Then + $NewPathContent &= ";" + EndIf + $NewPathContent &= $7zipPath + + ; Add the 7zip folder path to %PATH% + If Not StringInStr(EnvGet("PATH"), "7-Zip") Then + If RegWrite("HKEY_CURRENT_USER\Environment", "Path", "REG_SZ", $NewPathContent) Then + _FileWriteLog($Log, 'INFO:'&@TAB&$7zipPath&" added to %PATH%. PATH = "&EnvGet("PATH")&@CRLF) + _GUICtrlEdit_AppendText($Edit1, "%PATH% Evrionment Variable updated... Ok!"&@CRLF) + Else + _FileWriteLog($Log, 'ERROR:'&@TAB&$7zipPath&" could not to %PATH%. Command :"&'"' & @ComSpec & '" /k ' & 'setx /M PATH "%PATH%;' & $7zipPath&@CRLF) + _GUICtrlEdit_AppendText($Edit1, "ERROR : Couldn't add "&$7zipPath&" to %PATH%"&@CRLF) + $InstallSuccessfull = 0 + EndIf + Else + _FileWriteLog($Log, 'INFO:'&@TAB&$7zipPath&" is already set in %PATH%. PATH = "&EnvGet("PATH")&@CRLF) + _GUICtrlEdit_AppendText($Edit1, "INFO : %PATH% already stores the 7-Zip folder path, no need to modify"&@CRLF) + EndIf + + ; Copy lua folder to ProgramFiles + local $TrimmedLuaPath = @ScriptDir&"\LuaFiles" + local $TrimmedProgramFilesDir = StringTrimRight($ProgramFilesDir, 1) + If DirCopy($TrimmedLuaPath, $TrimmedProgramFilesDir, $FC_OVERWRITE) Then + _FileWriteLog($Log, 'INFO:'&@TAB&$TrimmedLuaPath&" successfully copied to "&$TrimmedProgramFilesDir&@CRLF) + _GUICtrlEdit_AppendText($Edit1, "Lua 5.1 Installation... Ok!"&@CRLF) + Else + _FileWriteLog($Log, 'ERROR:'&@TAB&"Could not copy "&$TrimmedLuaPath&" to "&$TrimmedProgramFilesDir&@CRLF) + _GUICtrlEdit_AppendText($Edit1, "ERROR : Could not install lua 5.1 in "&$ProgramFilesDir&" Please retry, running this program is admin"&@CRLF) + $InstallSuccessfull = 0 + EndIf + + ; Succesfull Message + If $InstallSuccessfull Then + _GUICtrlEdit_AppendText($Edit1, "Setup Complete !"&@CRLF) + _FileWriteLog($Log, 'INFO:'&@TAB&'Setup Successful. Please reboot the computer.'&@CRLF) + Else + _GUICtrlEdit_AppendText($Edit1, "Setup finished, but some problem occured. Please fix them manually or retry the installation process."&@CRLF) + _FileWriteLog($Log, 'INFO:'&@TAB&'Setup finished, but some error occured'&@CRLF) + EndIf + + GUICtrlSetState($Button1, $GUI_ENABLE) + + While 1 + $nMsg = GUIGetMsg() + Switch $nMsg + case $GUI_EVENT_CLOSE + GUIDelete() + CleanExit() + case $Button1 + MsgBox(64, "Reboot", "You need to reboot your system to be able to use the automated .miz manipualtion tools") ; TODO : Automtically reboot ? + GUIDelete() + CleanExit() + EndSwitch + WEnd + +EndFunc + +While 1 + $Log = FileOpen(@ScriptDir & "\mdes.log", 1) + FileWrite($Log, @CRLF&'New Session !'&@CRLF&'============='&@CRLF) + Welcome() + FoldersLocation() + SetupInProgress() +WEnd diff --git a/Moose Development Evironment Setup/MooseDevelopmentEnvironmentSetup.exe b/Moose Development Evironment Setup/MooseDevelopmentEnvironmentSetup.exe new file mode 100644 index 000000000..507bf8bf5 Binary files /dev/null and b/Moose Development Evironment Setup/MooseDevelopmentEnvironmentSetup.exe differ diff --git a/Moose Development/LDT External Tools/Update ALL Moose Test Missions.launch b/Moose Development/LDT External Tools/Update ALL Moose Test Missions.launch index 1545357c6..465818763 100644 --- a/Moose Development/LDT External Tools/Update ALL Moose Test Missions.launch +++ b/Moose Development/LDT External Tools/Update ALL Moose Test Missions.launch @@ -1,6 +1,9 @@ + + + - + diff --git a/Moose Development/LDT External Tools/Update Moose Test Missions.launch b/Moose Development/LDT External Tools/Update Moose Test Missions.launch new file mode 100644 index 000000000..3fec0cfe5 --- /dev/null +++ b/Moose Development/LDT External Tools/Update Moose Test Missions.launch @@ -0,0 +1,6 @@ + + + + + + diff --git a/Moose Development/LDT External Tools/Update Single Moose Test Mission.launch b/Moose Development/LDT External Tools/Update Single Moose Test Mission.launch new file mode 100644 index 000000000..5a375b0f0 --- /dev/null +++ b/Moose Development/LDT External Tools/Update Single Moose Test Mission.launch @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Moose Development/Maths/Probability factor models for Detection.xlsx b/Moose Development/Maths/Probability factor models for Detection.xlsx new file mode 100644 index 000000000..300f24e1e Binary files /dev/null and b/Moose Development/Maths/Probability factor models for Detection.xlsx differ diff --git a/Moose Development/Maths/Task Templates.xlsx b/Moose Development/Maths/Task Templates.xlsx new file mode 100644 index 000000000..0c2e0acb2 Binary files /dev/null and b/Moose Development/Maths/Task Templates.xlsx differ diff --git a/Moose Development/Moose/AI/AI_CAP.lua b/Moose Development/Moose/AI/AI_CAP.lua index 155b5284e..1b7dcbe22 100644 --- a/Moose Development/Moose/AI/AI_CAP.lua +++ b/Moose Development/Moose/AI/AI_CAP.lua @@ -1,10 +1,54 @@ ---- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Air** -- **Execute Combat Air Patrol (CAP).** +--- **AI** - **Execute Combat Air Patrol (CAP).** -- -- ![Banner Image](..\Presentations\AI_CAP\Dia1.JPG) -- -- === +-- +-- AI CAP classes makes AI Controllables execute a Combat Air Patrol. +-- +-- There are the following types of CAP classes defined: +-- +-- * @{#AI_CAP_ZONE}: Perform a CAP in a zone. +-- +-- ==== -- --- # 1) @{#AI_CAP_ZONE} class, extends @{AI_CAP#AI_PATROL_ZONE} +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-01-15: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. +-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. +-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. +-- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing. +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module AI_Cap + + +--- @type AI_CAP_ZONE +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. +-- @extends AI.AI_Patrol#AI_PATROL_ZONE + + +--- # 1) @{#AI_CAP_ZONE} class, extends @{AI_CAP#AI_PATROL_ZONE} -- -- The @{#AI_CAP_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group} -- and automatically engage any airborne enemies that are within a certain range or within a certain zone. @@ -82,44 +126,11 @@ -- An optional @{Zone} can be set, -- that will define when the AI will engage with the detected airborne enemy targets. -- Use the method @{AI_Cap#AI_CAP_ZONE.SetEngageZone}() to define that Zone. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-15: Initial class and API. --- +-- -- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. --- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. --- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. --- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module AI_Cap - - ---- AI_CAP_ZONE class --- @type AI_CAP_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. --- @extends AI.AI_Patrol#AI_PATROL_ZONE +-- +-- @field #AI_CAP_ZONE AI_CAP_ZONE +-- AI_CAP_ZONE = { ClassName = "AI_CAP_ZONE", } diff --git a/Moose Development/Moose/AI/AI_CAS.lua b/Moose Development/Moose/AI/AI_CAS.lua index c5c37cf4c..49ffdbc51 100644 --- a/Moose Development/Moose/AI/AI_CAS.lua +++ b/Moose Development/Moose/AI/AI_CAS.lua @@ -1,11 +1,52 @@ ---- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Air** -- --- **Provide Close Air Support to friendly ground troops.** +--- **AI** -- **Provide Close Air Support to friendly ground troops.** -- -- ![Banner Image](..\Presentations\AI_CAS\Dia1.JPG) -- -- === +-- +-- AI CAS classes makes AI Controllables execute a Close Air Support. +-- +-- There are the following types of CAS classes defined: +-- +-- * @{#AI_CAS_ZONE}: Perform a CAS in a zone. +-- +-- === -- --- # 1) @{#AI_CAS_ZONE} class, extends @{AI_Patrol#AI_PATROL_ZONE} +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-01-15: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. +-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module AI_Cas + + +--- AI_CAS_ZONE class +-- @type AI_CAS_ZONE +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. +-- @extends AI.AI_Patrol#AI_PATROL_ZONE + +--- # 1) @{#AI_CAS_ZONE} class, extends @{AI_Patrol#AI_PATROL_ZONE} -- -- @{#AI_CAS_ZONE} derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. -- @@ -90,42 +131,11 @@ -- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Unit}. -- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Unit}s assigned in the CAS task. -- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-15: Initial class and API. --- +-- -- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. --- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module AI_Cas - - ---- AI_CAS_ZONE class --- @type AI_CAS_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. --- @extends AI.AI_Patrol#AI_PATROL_ZONE +-- +-- @field #AI_CAS_ZONE AI_CAS_ZONE +-- AI_CAS_ZONE = { ClassName = "AI_CAS_ZONE", } @@ -161,11 +171,6 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. - -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. - -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. - -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. - -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @return #boolean Return false to cancel Transition. @@ -176,18 +181,15 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. - -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. - -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. - -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. - -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- Synchronous Event Trigger for Event Engage. -- @function [parent=#AI_CAS_ZONE] Engage -- @param #AI_CAS_ZONE self -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. + -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. + -- If parameter is not defined the unit / controllable will choose expend on its own discretion. + -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. @@ -196,8 +198,10 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #AI_CAS_ZONE self -- @param #number Delay The delay in seconds. -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. + -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. + -- If parameter is not defined the unit / controllable will choose expend on its own discretion. + -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. @@ -408,7 +412,7 @@ function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To ) if Detected == true then self:E( {"Target: ", DetectedUnit } ) self.DetectedUnits[DetectedUnit] = false - local AttackTask = Controllable:EnRouteTaskEngageUnit( DetectedUnit, 1, true, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil ) + local AttackTask = Controllable:TaskAttackUnit( DetectedUnit, false, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil ) self.Controllable:PushTask( AttackTask, 1 ) end end @@ -439,8 +443,8 @@ end -- @param #string Event The Event string. -- @param #string To The To State string. -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. +-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, @@ -458,7 +462,6 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, self.EngageDirection = EngageDirection if Controllable:IsAlive() then - local EngageRoute = {} @@ -479,29 +482,28 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, EngageRoute[#EngageRoute+1] = CurrentRoutePoint - - if self.Controllable:IsNotInZone( self.EngageZone ) then - - -- Find a random 2D point in EngageZone. - local ToEngageZoneVec2 = self.EngageZone:GetRandomVec2() - self:T2( ToEngageZoneVec2 ) - - -- Obtain a 3D @{Point} from the 2D point + altitude. - local ToEngageZonePointVec3 = POINT_VEC3:New( ToEngageZoneVec2.x, self.EngageAltitude, ToEngageZoneVec2.y ) - - -- Create a route point of type air. - local ToEngageZoneRoutePoint = ToEngageZonePointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - self.EngageSpeed, - true - ) - - EngageRoute[#EngageRoute+1] = ToEngageZoneRoutePoint + local AttackTasks = {} + for DetectedUnitID, DetectedUnit in pairs( self.DetectedUnits ) do + local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT + self:T( DetectedUnit ) + if DetectedUnit:IsAlive() then + if DetectedUnit:IsInZone( self.EngageZone ) then + self:E( {"Engaging ", DetectedUnit } ) + AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit, + true, + EngageWeaponExpend, + EngageAttackQty, + EngageDirection + ) + end + else + self.DetectedUnits[DetectedUnit] = nil + end end - + + EngageRoute[1].task = Controllable:TaskCombo( AttackTasks ) + --- Define a random point in the @{Zone}. The AI will fly to that point within the zone. --- Find a random 2D point in EngageZone. @@ -520,45 +522,25 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, true ) - --ToTargetPointVec3:SmokeBlue() - EngageRoute[#EngageRoute+1] = ToTargetRoutePoint + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + Controllable:WayPointInitialize( EngageRoute ) + --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... + Controllable:SetState( Controllable, "EngageZone", self ) + + Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" ) + + --- NOW ROUTE THE GROUP! + Controllable:WayPointExecute( 1 ) Controllable:OptionROEOpenFire() Controllable:OptionROTVertical() - --- local AttackTasks = {} --- --- for DetectedUnitID, DetectedUnit in pairs( self.DetectedUnits ) do --- local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT --- self:T( DetectedUnit ) --- if DetectedUnit:IsAlive() then --- if DetectedUnit:IsInZone( self.EngageZone ) then --- self:E( {"Engaging ", DetectedUnit } ) --- AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) --- end --- else --- self.DetectedUnits[DetectedUnit] = nil --- end --- end --- --- EngageRoute[1].task = Controllable:TaskCombo( AttackTasks ) - - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - self.Controllable:WayPointInitialize( EngageRoute ) - --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... - self.Controllable:SetState( self.Controllable, "EngageZone", self ) - - self.Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" ) - - --- NOW ROUTE THE GROUP! - self.Controllable:WayPointExecute( 1 ) - - self:SetDetectionInterval( 10 ) + self:SetDetectionInterval( 2 ) self:SetDetectionActivated() - self:__Target( -10 ) -- Start Targetting + self:__Target( -2 ) -- Start Targetting end end diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index b925325a5..404b0d904 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -1,11 +1,70 @@ ---- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Air** -- --- **Air Patrolling or Staging.** +--- **AI** -- **Air Patrolling or Staging.** -- -- ![Banner Image](..\Presentations\AI_PATROL\Dia1.JPG) -- -- === -- --- # 1) @{#AI_PATROL_ZONE} class, extends @{Fsm#FSM_CONTROLLABLE} +-- AI PATROL classes makes AI Controllables execute an Patrol. +-- +-- There are the following types of PATROL classes defined: +-- +-- * @{#AI_PATROL_ZONE}: Perform a PATROL in a zone. +-- +-- ==== +-- +-- # **OPEN ISSUES** +-- +-- 2017-01-17: When Spawned AI is located at an airbase, it will be routed first back to the airbase after take-off. +-- +-- 2016-01-17: +-- -- Fixed problem with AI returning to base too early and unexpected. +-- -- ReSpawning of AI will reset the AI_PATROL and derived classes. +-- -- Checked the correct workings of SCHEDULER, and it DOES work correctly. +-- +-- ==== +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-01-17: Rename of class: **AI\_PATROL\_ZONE** is the new name for the old _AI\_PATROLZONE_. +-- +-- 2017-01-15: Complete revision. AI_PATROL_ZONE is the base class for other AI_PATROL like classes. +-- +-- 2016-09-01: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review. +-- +-- ### Authors: +-- +-- * **FlightControl**: Design & Programming. +-- +-- @module AI_Patrol + +--- AI_PATROL_ZONE class +-- @type AI_PATROL_ZONE +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. +-- @field Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @field Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. +-- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @field Functional.Spawn#SPAWN CoordTest +-- @extends Core.Fsm#FSM_CONTROLLABLE + +--- # 1) @{#AI_PATROL_ZONE} class, extends @{Fsm#FSM_CONTROLLABLE} -- -- The @{#AI_PATROL_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}. -- @@ -106,59 +165,10 @@ -- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB). -- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this proces in place. -- --- ==== --- --- # **OPEN ISSUES** --- --- 2017-01-17: When Spawned AI is located at an airbase, it will be routed first back to the airbase after take-off. --- --- 2016-01-17: --- -- Fixed problem with AI returning to base too early and unexpected. --- -- ReSpawning of AI will reset the AI_PATROL and derived classes. --- -- Checked the correct workings of SCHEDULER, and it DOES work correctly. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-17: Rename of class: **AI\_PATROL\_ZONE** is the new name for the old _AI\_PATROLZONE_. --- --- 2017-01-15: Complete revision. AI_PATROL_ZONE is the base class for other AI_PATROL like classes. --- --- 2016-09-01: Initial class and API. --- -- === -- --- # **AUTHORS and CONTRIBUTIONS** +-- @field #AI_PATROL_ZONE AI_PATROL_ZONE -- --- ### Contributions: --- --- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review. --- --- ### Authors: --- --- * **FlightControl**: Design & Programming. --- --- @module AI_Patrol - ---- AI_PATROL_ZONE class --- @type AI_PATROL_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @field Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @field Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. --- @field Functional.Spawn#SPAWN CoordTest --- @extends Core.Fsm#FSM_CONTROLLABLE AI_PATROL_ZONE = { ClassName = "AI_PATROL_ZONE", } diff --git a/Moose Development/Moose/Actions/Act_Account.lua b/Moose Development/Moose/Actions/Act_Account.lua index 862079368..14e83b07f 100644 --- a/Moose Development/Moose/Actions/Act_Account.lua +++ b/Moose Development/Moose/Actions/Act_Account.lua @@ -1,71 +1,6 @@ ---- (SP) (MP) (FSM) Account for (Detect, count and report) DCS events occuring on DCS objects (units). +--- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occuring on @{Unit}s. -- --- === --- --- # @{#ACT_ACCOUNT} FSM class, extends @{Fsm#FSM_PROCESS} --- --- ## ACT_ACCOUNT state machine: --- --- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. --- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. --- Each derived class follows exactly the same process, using the same events and following the same state transitions, --- but will have **different implementation behaviour** upon each event or state transition. --- --- ### ACT_ACCOUNT **Events**: --- --- These are the events defined in this class: --- --- * **Start**: The process is started. The process will go into the Report state. --- * **Event**: A relevant event has occured that needs to be accounted for. The process will go into the Account state. --- * **Report**: The process is reporting to the player the accounting status of the DCS events. --- * **More**: There are more DCS events that need to be accounted for. The process will go back into the Report state. --- * **NoMore**: There are no more DCS events that need to be accounted for. The process will go into the Success state. --- --- ### ACT_ACCOUNT **Event methods**: --- --- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process. --- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine: --- --- * **Immediate**: The event method has exactly the name of the event. --- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. --- --- ### ACT_ACCOUNT **States**: --- --- * **Assigned**: The player is assigned to the task. This is the initialization state for the process. --- * **Waiting**: the process is waiting for a DCS event to occur within the simulator. This state is set automatically. --- * **Report**: The process is Reporting to the players in the group of the unit. This state is set automatically every 30 seconds. --- * **Account**: The relevant DCS event has occurred, and is accounted for. --- * **Success (*)**: All DCS events were accounted for. --- * **Failed (*)**: The process has failed. --- --- (*) End states of the process. --- --- ### ACT_ACCOUNT state transition methods: --- --- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. --- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Before** the state transition. --- The state transition method needs to start with the name **OnBefore + the name of the state**. --- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, --- but then you'll need to specify your own logic using the AIControllable! --- --- * **After** the state transition. --- The state transition method needs to start with the name **OnAfter + the name of the state**. --- These state transition methods need to provide a return value, which is specified at the function description. --- --- # 1) @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Fsm.Account#ACT_ACCOUNT} --- --- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units. --- The process is given a @{Set} of units that will be tracked upon successful destruction. --- The process will end after each target has been successfully destroyed. --- Each successful dead will trigger an Account state transition that can be scored, modified or administered. --- --- --- ## ACT_ACCOUNT_DEADS constructor: --- --- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object. +-- ![Banner Image](..\Presentations\ACT_ACCOUNT\Dia1.JPG) -- -- === -- @@ -74,7 +9,51 @@ do -- ACT_ACCOUNT - --- ACT_ACCOUNT class + --- # @{#ACT_ACCOUNT} FSM class, extends @{Fsm#FSM_PROCESS} + -- + -- ## ACT_ACCOUNT state machine: + -- + -- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. + -- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. + -- Each derived class follows exactly the same process, using the same events and following the same state transitions, + -- but will have **different implementation behaviour** upon each event or state transition. + -- + -- ### ACT_ACCOUNT States + -- + -- * **Asigned**: The player is assigned. + -- * **Waiting**: Waiting for an event. + -- * **Report**: Reporting. + -- * **Account**: Account for an event. + -- * **Accounted**: All events have been accounted for, end of the process. + -- * **Failed**: Failed the process. + -- + -- ### ACT_ACCOUNT Events + -- + -- * **Start**: Start the process. + -- * **Wait**: Wait for an event. + -- * **Report**: Report the status of the accounting. + -- * **Event**: An event happened, process the event. + -- * **More**: More targets. + -- * **NoMore (*)**: No more targets. + -- * **Fail (*)**: The action process has failed. + -- + -- (*) End states of the process. + -- + -- ### ACT_ACCOUNT state transition methods: + -- + -- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. + -- There are 2 moments when state transition methods will be called by the state machine: + -- + -- * **Before** the state transition. + -- The state transition method needs to start with the name **OnBefore + the name of the state**. + -- If the state transition method returns false, then the processing of the state transition will not be done! + -- If you want to change the behaviour of the AIControllable at this event, return false, + -- but then you'll need to specify your own logic using the AIControllable! + -- + -- * **After** the state transition. + -- The state transition method needs to start with the name **OnAfter + the name of the state**. + -- These state transition methods need to provide a return value, which is specified at the function description. + -- -- @type ACT_ACCOUNT -- @field Set#SET_UNIT TargetSetUnit -- @extends Core.Fsm#FSM_PROCESS @@ -156,7 +135,18 @@ end -- ACT_ACCOUNT do -- ACT_ACCOUNT_DEADS - --- ACT_ACCOUNT_DEADS class + --- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Fsm.Account#ACT_ACCOUNT} + -- + -- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units. + -- The process is given a @{Set} of units that will be tracked upon successful destruction. + -- The process will end after each target has been successfully destroyed. + -- Each successful dead will trigger an Account state transition that can be scored, modified or administered. + -- + -- + -- ## ACT_ACCOUNT_DEADS constructor: + -- + -- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object. + -- -- @type ACT_ACCOUNT_DEADS -- @field Set#SET_UNIT TargetSetUnit -- @extends #ACT_ACCOUNT @@ -192,15 +182,6 @@ do -- ACT_ACCOUNT_DEADS self.TaskName = FsmAccount.TaskName end - - - function ACT_ACCOUNT_DEADS:_Destructor() - self:E("_Destructor") - - self:EventRemoveAll() - - end - --- Process Events --- StateMachine callback function @@ -209,7 +190,7 @@ do -- ACT_ACCOUNT_DEADS -- @param #string Event -- @param #string From -- @param #string To - function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, From, Event, To ) + function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, Task, From, Event, To ) self:E( { ProcessUnit, From, Event, To } ) self:Message( "Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed." ) @@ -222,18 +203,21 @@ do -- ACT_ACCOUNT_DEADS -- @param #string Event -- @param #string From -- @param #string To - function ACT_ACCOUNT_DEADS:onenterAccount( ProcessUnit, From, Event, To, EventData ) + function ACT_ACCOUNT_DEADS:onenterAccount( ProcessUnit, Task, From, Event, To, EventData ) self:T( { ProcessUnit, EventData, From, Event, To } ) self:T({self.Controllable}) self.TargetSetUnit:Flush() + self:T( { "Before sending Message", EventData.IniUnitName, self.TargetSetUnit:FindUnit( EventData.IniUnitName ) } ) if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then + self:T( "Sending Message" ) local TaskGroup = ProcessUnit:GetGroup() - self.TargetSetUnit:RemoveUnitsByName( EventData.IniUnitName ) + self.TargetSetUnit:Remove( EventData.IniUnitName ) self:Message( "You hit a target. Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." ) end + self:T( { "After sending Message" } ) end --- StateMachine callback function @@ -242,7 +226,7 @@ do -- ACT_ACCOUNT_DEADS -- @param #string Event -- @param #string From -- @param #string To - function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, From, Event, To, EventData ) + function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To ) if self.TargetSetUnit:Count() > 0 then self:__More( 1 ) @@ -259,7 +243,7 @@ do -- ACT_ACCOUNT_DEADS self:T( { "EventDead", EventData } ) if EventData.IniDCSUnit then - self:__Event( 1, EventData ) + self:Event( EventData ) end end diff --git a/Moose Development/Moose/Actions/Act_Assign.lua b/Moose Development/Moose/Actions/Act_Assign.lua index e78ca3b62..7d45ad21a 100644 --- a/Moose Development/Moose/Actions/Act_Assign.lua +++ b/Moose Development/Moose/Actions/Act_Assign.lua @@ -175,7 +175,7 @@ do -- ACT_ASSIGN_ACCEPT self:Message( "You are assigned to the task " .. self.Task:GetName() ) - self.Task:Assign() + self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() ) end end -- ACT_ASSIGN_ACCEPT diff --git a/Moose Development/Moose/Actions/Act_Assist.lua b/Moose Development/Moose/Actions/Act_Assist.lua index 23c5fc056..439abc49d 100644 --- a/Moose Development/Moose/Actions/Act_Assist.lua +++ b/Moose Development/Moose/Actions/Act_Assist.lua @@ -108,7 +108,7 @@ do -- ACT_ASSIST function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To ) local ProcessGroup = ProcessUnit:GetGroup() - local MissionMenu = self:GetMission():GetMissionMenu( ProcessGroup ) + local MissionMenu = self:GetMission():GetMenu( ProcessGroup ) local function MenuSmoke( MenuParam ) self:E( MenuParam ) @@ -125,6 +125,17 @@ do -- ACT_ASSIST self.MenuSmokeRed = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop Red smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Red } ) self.MenuSmokeWhite = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop White smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.White } ) end + + --- StateMachine callback function + -- @param #ACT_ASSIST self + -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit + -- @param #string Event + -- @param #string From + -- @param #string To + function ACT_ASSIST:onafterStop( ProcessUnit, From, Event, To ) + + self.Menu:Remove() -- When stopped, remove the menus + end end diff --git a/Moose Development/Moose/Actions/Act_JTAC.lua b/Moose Development/Moose/Actions/Act_JTAC.lua index 668abfcb5..2dd363f58 100644 --- a/Moose Development/Moose/Actions/Act_JTAC.lua +++ b/Moose Development/Moose/Actions/Act_JTAC.lua @@ -56,8 +56,7 @@ function PROCESS_JTAC:New( Task, ProcessUnit, TargetSetUnit, FACUnit ) endstates = { 'Failed' } } ) - - _EVENTDISPATCHER:OnDead( self.EventDead, self ) + self:HandleEvent( EVENTS.Dead, self.EventDead ) return self end diff --git a/Moose Development/Moose/Actions/Act_Route.lua b/Moose Development/Moose/Actions/Act_Route.lua index 53fa5d9b9..ec5e9fff3 100644 --- a/Moose Development/Moose/Actions/Act_Route.lua +++ b/Moose Development/Moose/Actions/Act_Route.lua @@ -81,7 +81,7 @@ do -- ACT_ROUTE -- @type ACT_ROUTE -- @field Tasking.Task#TASK TASK -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone + -- @field Core.Zone#ZONE_BASE Zone -- @extends Core.Fsm#FSM_PROCESS ACT_ROUTE = { ClassName = "ACT_ROUTE", @@ -177,6 +177,115 @@ do -- ACT_ROUTE end -- ACT_ROUTE +do -- ACT_ROUTE_POINT + + --- ACT_ROUTE_POINT class + -- @type ACT_ROUTE_POINT + -- @field Tasking.Task#TASK TASK + -- @extends #ACT_ROUTE + ACT_ROUTE_POINT = { + ClassName = "ACT_ROUTE_POINT", + } + + + --- Creates a new routing state machine. + -- The task will route a controllable to a PointVec2 until the controllable is within the Range. + -- @param #ACT_ROUTE_POINT self + -- @param Core.Point#POINT_VEC2 The PointVec2 to Target. + -- @param #number Range The Distance to Target. + -- @param Core.Zone#ZONE_BASE Zone + function ACT_ROUTE_POINT:New( PointVec2, Range ) + local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_POINT + + self.PointVec2 = PointVec2 + self.Range = Range or 0 + + self.DisplayInterval = 30 + self.DisplayCount = 30 + self.DisplayMessage = true + self.DisplayTime = 10 -- 10 seconds is the default + + return self + end + + function ACT_ROUTE_POINT:Init( FsmRoute ) + + self.PointVec2 = FsmRoute.PointVec2 + self.Range = FsmRoute.Range or 0 + + self.DisplayInterval = 30 + self.DisplayCount = 30 + self.DisplayMessage = true + self.DisplayTime = 10 -- 10 seconds is the default + end + + --- Set PointVec2 + -- @param #ACT_ROUTE_POINT self + -- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to route to. + function ACT_ROUTE_POINT:SetPointVec2( PointVec2 ) + self:F2( { PointVec2 } ) + self.PointVec2 = PointVec2 + end + + --- Get PointVec2 + -- @param #ACT_ROUTE_POINT self + -- @return Core.Point#POINT_VEC2 PointVec2 The PointVec2 to route to. + function ACT_ROUTE_POINT:GetPointVec2() + self:F2( { self.PointVec2 } ) + return self.PointVec2 + end + + --- Set Range around PointVec2 + -- @param #ACT_ROUTE_POINT self + -- @param #number Range The Range to consider the arrival. Default is 10000 meters. + function ACT_ROUTE_POINT:SetRange( Range ) + self:F2( { self.Range } ) + self.Range = Range or 10000 + end + + --- Get Range around PointVec2 + -- @param #ACT_ROUTE_POINT self + -- @return #number The Range to consider the arrival. Default is 10000 meters. + function ACT_ROUTE_POINT:GetRange() + return self.Range + end + + --- Method override to check if the controllable has arrived. + -- @param #ACT_ROUTE_POINT self + -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit + -- @return #boolean + function ACT_ROUTE_POINT:onfuncHasArrived( ProcessUnit ) + + if ProcessUnit:IsAlive() then + local Distance = self.PointVec2:Get2DDistance( ProcessUnit:GetPointVec2() ) + + if Distance <= self.Range then + local RouteText = "You have arrived." + self:Message( RouteText ) + return true + end + end + + return false + end + + --- Task Events + + --- StateMachine callback function + -- @param #ACT_ROUTE_POINT self + -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit + -- @param #string Event + -- @param #string From + -- @param #string To + function ACT_ROUTE_POINT:onenterReporting( ProcessUnit, From, Event, To ) + + local TaskUnitPointVec2 = ProcessUnit:GetPointVec2() + local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( self.PointVec2 ) .. " km." + self:Message( RouteText ) + end + +end -- ACT_ROUTE_POINT + do -- ACT_ROUTE_ZONE @@ -184,7 +293,7 @@ do -- ACT_ROUTE_ZONE -- @type ACT_ROUTE_ZONE -- @field Tasking.Task#TASK TASK -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone + -- @field Core.Zone#ZONE_BASE Zone -- @extends #ACT_ROUTE ACT_ROUTE_ZONE = { ClassName = "ACT_ROUTE_ZONE", @@ -193,11 +302,11 @@ do -- ACT_ROUTE_ZONE --- Creates a new routing state machine. The task will route a controllable to a ZONE until the controllable is within that ZONE. -- @param #ACT_ROUTE_ZONE self - -- @param Core.Zone#ZONE_BASE TargetZone - function ACT_ROUTE_ZONE:New( TargetZone ) + -- @param Core.Zone#ZONE_BASE Zone + function ACT_ROUTE_ZONE:New( Zone ) local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE - self.TargetZone = TargetZone + self.Zone = Zone self.DisplayInterval = 30 self.DisplayCount = 30 @@ -209,7 +318,7 @@ do -- ACT_ROUTE_ZONE function ACT_ROUTE_ZONE:Init( FsmRoute ) - self.TargetZone = FsmRoute.TargetZone + self.Zone = FsmRoute.Zone self.DisplayInterval = 30 self.DisplayCount = 30 @@ -217,18 +326,32 @@ do -- ACT_ROUTE_ZONE self.DisplayTime = 10 -- 10 seconds is the default end + --- Set Zone + -- @param #ACT_ROUTE_ZONE self + -- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to. + function ACT_ROUTE_ZONE:SetZone( Zone ) + self.Zone = Zone + end + + --- Get Zone + -- @param #ACT_ROUTE_ZONE self + -- @return Core.Zone#ZONE_BASE Zone The Zone object where to route to. + function ACT_ROUTE_ZONE:GetZone() + return self.Zone + end + --- Method override to check if the controllable has arrived. -- @param #ACT_ROUTE self -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit -- @return #boolean function ACT_ROUTE_ZONE:onfuncHasArrived( ProcessUnit ) - if ProcessUnit:IsInZone( self.TargetZone ) then + if ProcessUnit:IsInZone( self.Zone ) then local RouteText = "You have arrived within the zone." self:Message( RouteText ) end - return ProcessUnit:IsInZone( self.TargetZone ) + return ProcessUnit:IsInZone( self.Zone ) end --- Task Events @@ -241,11 +364,11 @@ do -- ACT_ROUTE_ZONE -- @param #string To function ACT_ROUTE_ZONE:onenterReporting( ProcessUnit, From, Event, To ) - local ZoneVec2 = self.TargetZone:GetVec2() + local ZoneVec2 = self.Zone:GetVec2() local ZonePointVec2 = POINT_VEC2:New( ZoneVec2.x, ZoneVec2.y ) local TaskUnitVec2 = ProcessUnit:GetVec2() local TaskUnitPointVec2 = POINT_VEC2:New( TaskUnitVec2.x, TaskUnitVec2.y ) - local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( ZonePointVec2 ) .. " km to target." + local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( ZonePointVec2 ) .. " km." self:Message( RouteText ) end diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 8c1c6fad8..e047c76d1 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -4,9 +4,54 @@ -- -- === -- --- # 1) @{#BASE} class +-- The @{#BASE} class is the core root class from where every other class in moose is derived. -- --- All classes within the MOOSE framework are derived from the @{#BASE} class. +-- === +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) +-- YYYY-MM-DD: CLASS:**NewFunction( Params )** added +-- +-- Hereby the change log: +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * None. +-- +-- ### Authors: +-- +-- * **FlightControl**: Design & Programming +-- +-- @module Base + + + +local _TraceOnOff = true +local _TraceLevel = 1 +local _TraceAll = false +local _TraceClass = {} +local _TraceClassMethod = {} + +local _ClassID = 0 + +--- @type BASE +-- @field ClassName The name of the class. +-- @field ClassID The ID number of the class. +-- @field ClassNameAndID The name of the class concatenated with the ID number of the class. + +--- # 1) #BASE class +-- +-- All classes within the MOOSE framework are derived from the BASE class. -- -- BASE provides facilities for : -- @@ -166,50 +211,11 @@ -- -- * @{#BASE.Inherit}: Inherits from a class. -- * @{#BASE.GetParent}: Returns the parent object from the object it is handling, or nil if there is no parent object. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) --- YYYY-MM-DD: CLASS:**NewFunction( Params )** added --- --- Hereby the change log: -- -- === -- --- # **AUTHORS and CONTRIBUTIONS** +-- @field #BASE BASE -- --- ### Contributions: --- --- * None. --- --- ### Authors: --- --- * **FlightControl**: Design & Programming --- --- @module Base - - - -local _TraceOnOff = true -local _TraceLevel = 1 -local _TraceAll = false -local _TraceClass = {} -local _TraceClassMethod = {} - -local _ClassID = 0 - ---- The BASE Class --- @type BASE --- @field ClassName The name of the class. --- @field ClassID The ID number of the class. --- @field ClassNameAndID The name of the class concatenated with the ID number of the class. BASE = { ClassName = "BASE", ClassID = 0, @@ -256,12 +262,14 @@ function BASE:_Destructor() --self:EventRemoveAll() end + +-- THIS IS WHY WE NEED LUA 5.2 ... function BASE:_SetDestructor() -- TODO: Okay, this is really technical... -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak... -- Therefore, I am parking this logic until I've properly discussed all this with the community. - --[[ + local proxy = newproxy(true) local proxyMeta = getmetatable(proxy) @@ -276,7 +284,7 @@ function BASE:_SetDestructor() -- table is about to be garbage-collected - then the __gc hook -- will be invoked and the destructor called rawset( self, '__proxy', proxy ) - --]] + end --- This is the worker method to inherit from a parent class. @@ -292,7 +300,7 @@ function BASE:Inherit( Child, Parent ) setmetatable( Child, Parent ) Child.__index = Child - Child:_SetDestructor() + --Child:_SetDestructor() end --self:T( 'Inherited from ' .. Parent.ClassName ) return Child diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 798c5e881..3e9eea077 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -54,6 +54,8 @@ DATABASE = { PLAYERSJOINED = {}, CLIENTS = {}, AIRBASES = {}, + COUNTRY_ID = {}, + COUNTRY_NAME = {}, NavPoints = {}, } @@ -761,6 +763,9 @@ function DATABASE:_RegisterTemplates() local CountryName = string.upper(cntry_data.name) local CountryID = cntry_data.id + self.COUNTRY_ID[CountryName] = CountryID + self.COUNTRY_NAME[CountryID] = CountryName + --self.Units[coa_name][countryName] = {} --self.Units[coa_name][countryName]["countryId"] = cntry_data.id diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 58baff3d6..39b0738af 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -284,7 +284,7 @@ local _EVENTMETA = { }, [world.event.S_EVENT_TAKEOFF] = { Order = 1, - Event = "OnEventTakeOff", + Event = "OnEventTakeoff", Text = "S_EVENT_TAKEOFF" }, [world.event.S_EVENT_LAND] = { @@ -425,11 +425,11 @@ function EVENT:Init( EventID, EventClass ) -- Each event has a subtable of EventClasses, ordered by EventPriority. local EventPriority = EventClass:GetEventPriority() if not self.Events[EventID][EventPriority] then - self.Events[EventID][EventPriority] = {} + self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "k" } ) end if not self.Events[EventID][EventPriority][EventClass] then - self.Events[EventID][EventPriority][EventClass] = setmetatable( {}, { __mode = "k" } ) + self.Events[EventID][EventPriority][EventClass] = setmetatable( {}, { __mode = "v" } ) end return self.Events[EventID][EventPriority][EventClass] end @@ -499,11 +499,11 @@ end -- @param EventClass The instance of the class for which the event is. -- @param #function OnEventFunction -- @return #EVENT -function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, OnEventFunction ) +function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EventID ) self:F2( EventTemplate.name ) for EventUnitID, EventUnit in pairs( EventTemplate.units ) do - OnEventFunction( self, EventUnit.name, EventFunction, EventClass ) + self:OnEventForUnit( EventUnit.name, EventFunction, EventClass, EventID ) end return self end @@ -517,9 +517,9 @@ end function EVENT:OnEventGeneric( EventFunction, EventClass, EventID ) self:F2( { EventID } ) - local Event = self:Init( EventID, EventClass ) - Event.EventFunction = EventFunction - Event.EventClass = EventClass + local EventData = self:Init( EventID, EventClass ) + EventData.EventFunction = EventFunction + EventData.EventClass = EventClass return self end @@ -535,13 +535,13 @@ end function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID ) self:F2( UnitName ) - local Event = self:Init( EventID, EventClass ) - if not Event.EventUnit then - Event.EventUnit = {} + local EventData = self:Init( EventID, EventClass ) + if not EventData.EventUnit then + EventData.EventUnit = {} end - Event.EventUnit[UnitName] = {} - Event.EventUnit[UnitName].EventFunction = EventFunction - Event.EventUnit[UnitName].EventClass = EventClass + EventData.EventUnit[UnitName] = {} + EventData.EventUnit[UnitName].EventFunction = EventFunction + EventData.EventUnit[UnitName].EventClass = EventClass return self end @@ -576,51 +576,11 @@ do -- OnBirth function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnBirthForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Birth ) return self end - --- Set a new listener for an S_EVENT_BIRTH event, and registers the unit born. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnBirth( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_BIRTH ) - - return self - end - - --- Set a new listener for an S_EVENT_BIRTH event. - -- @param #EVENT self - -- @param #string EventDCSUnitName The id of the unit for the event to be handled. - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnBirthForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_BIRTH ) - - return self - end - - --- Stop listening to S_EVENT_BIRTH event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnBirthRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_BIRTH ) - - return self - end - - end do -- OnCrash @@ -634,49 +594,10 @@ do -- OnCrash function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnCrashForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Crash ) return self end - - --- Set a new listener for an S_EVENT_CRASH event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnCrash( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_CRASH ) - - return self - end - - --- Set a new listener for an S_EVENT_CRASH event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnCrashForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_CRASH ) - - return self - end - - --- Stop listening to S_EVENT_CRASH event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnCrashRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_CRASH ) - - return self - end end @@ -691,96 +612,13 @@ do -- OnDead function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnDeadForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Dead ) return self end - --- Set a new listener for an S_EVENT_DEAD event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnDead( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_DEAD ) - - return self - end - - - --- Set a new listener for an S_EVENT_DEAD event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnDeadForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_DEAD ) - - return self - end - - --- Stop listening to S_EVENT_DEAD event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnDeadRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_DEAD ) - - return self - end - - end -do -- OnPilotDead - - --- Set a new listener for an S_EVENT_PILOT_DEAD event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnPilotDead( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_PILOT_DEAD ) - - return self - end - - --- Set a new listener for an S_EVENT_PILOT_DEAD event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnPilotDeadForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_PILOT_DEAD ) - - return self - end - - --- Stop listening to S_EVENT_PILOT_DEAD event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnPilotDeadRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_PILOT_DEAD ) - - return self - end - -end do -- OnLand --- Create an OnLand event handler for a group @@ -792,38 +630,11 @@ do -- OnLand function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnLandForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Land ) return self end - --- Set a new listener for an S_EVENT_LAND event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnLandForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_LAND ) - - return self - end - - --- Stop listening to S_EVENT_LAND event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnLandRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_LAND ) - - return self - end - - end do -- OnTakeOff @@ -836,38 +647,11 @@ do -- OnTakeOff function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnTakeOffForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Takeoff ) return self end - --- Set a new listener for an S_EVENT_TAKEOFF event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnTakeOffForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_TAKEOFF ) - - return self - end - - --- Stop listening to S_EVENT_TAKEOFF event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnTakeOffRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_TAKEOFF ) - - return self - end - - end do -- OnEngineShutDown @@ -881,210 +665,11 @@ do -- OnEngineShutDown function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnEngineShutDownForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.EngineShutdown ) return self end - --- Set a new listener for an S_EVENT_ENGINE_SHUTDOWN event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnEngineShutDownForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_ENGINE_SHUTDOWN ) - - return self - end - - --- Stop listening to S_EVENT_ENGINE_SHUTDOWN event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnEngineShutDownRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_ENGINE_SHUTDOWN ) - - return self - end - -end - -do -- OnEngineStartUp - - --- Set a new listener for an S_EVENT_ENGINE_STARTUP event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnEngineStartUpForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_ENGINE_STARTUP ) - - return self - end - - --- Stop listening to S_EVENT_ENGINE_STARTUP event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnEngineStartUpRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_ENGINE_STARTUP ) - - return self - end - -end - -do -- OnShot - --- Set a new listener for an S_EVENT_SHOT event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnShot( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_SHOT ) - - return self - end - - --- Set a new listener for an S_EVENT_SHOT event for a unit. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnShotForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_SHOT ) - - return self - end - - --- Stop listening to S_EVENT_SHOT event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnShotRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_SHOT ) - - return self - end - - -end - -do -- OnHit - - --- Set a new listener for an S_EVENT_HIT event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnHit( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_HIT ) - - return self - end - - --- Set a new listener for an S_EVENT_HIT event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnHitForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_HIT ) - - return self - end - - --- Stop listening to S_EVENT_HIT event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnHitRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_HIT ) - - return self - end - -end - -do -- OnPlayerEnterUnit - - --- Set a new listener for an S_EVENT_PLAYER_ENTER_UNIT event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnPlayerEnterUnit( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_PLAYER_ENTER_UNIT ) - - return self - end - - --- Stop listening to S_EVENT_PLAYER_ENTER_UNIT event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnPlayerEnterRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_PLAYER_ENTER_UNIT ) - - return self - end - -end - -do -- OnPlayerLeaveUnit - --- Set a new listener for an S_EVENT_PLAYER_LEAVE_UNIT event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnPlayerLeaveUnit( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_PLAYER_LEAVE_UNIT ) - - return self - end - - --- Stop listening to S_EVENT_PLAYER_LEAVE_UNIT event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnPlayerLeaveRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_PLAYER_LEAVE_UNIT ) - - return self - end - end @@ -1102,6 +687,8 @@ function EVENT:onEvent( Event ) return errmsg end + self:E( _EVENTMETA[Event.id].Text, Event ) + if self and self.Events and self.Events[Event.id] then @@ -1200,6 +787,11 @@ function EVENT:onEvent( Event ) if Event.weapon then Event.Weapon = Event.weapon Event.WeaponName = Event.Weapon:getTypeName() + Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit! + Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName() + Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition() + Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category + Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName() --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() end @@ -1207,7 +799,9 @@ function EVENT:onEvent( Event ) local PriorityBegin = PriorityOrder == -1 and 5 or 1 local PriorityEnd = PriorityOrder == -1 and 1 or 5 - self:E( { _EVENTMETA[Event.id].Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) + if Event.IniObjectCategory ~= 3 then + self:E( { _EVENTMETA[Event.id].Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) + end for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do @@ -1228,8 +822,10 @@ function EVENT:onEvent( Event ) -- First test if a EventFunction is Set, otherwise search for the default function if EventData.EventUnit[Event.IniDCSUnitName].EventFunction then - self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) + end + local Result, Value = xpcall( function() return EventData.EventUnit[Event.IniDCSUnitName].EventFunction( EventClass, Event ) @@ -1242,8 +838,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end + local Result, Value = xpcall( function() return EventFunction( EventClass, Event ) @@ -1257,7 +855,9 @@ function EVENT:onEvent( Event ) -- First test if a EventFunction is Set, otherwise search for the default function if EventData.EventUnit[Event.TgtDCSUnitName].EventFunction then - self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) + end local Result, Value = xpcall( function() @@ -1271,8 +871,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end + local Result, Value = xpcall( function() return EventFunction( EventClass, Event ) @@ -1290,9 +892,11 @@ function EVENT:onEvent( Event ) if EventData.EventGroup[Event.IniGroupName] then -- First test if a EventFunction is Set, otherwise search for the default function if EventData.EventGroup[Event.IniGroupName].EventFunction then - - self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) - + + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) + end + local Result, Value = xpcall( function() return EventData.EventGroup[Event.IniGroupName].EventFunction( EventClass, Event ) @@ -1305,8 +909,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) + end + local Result, Value = xpcall( function() return EventFunction( EventClass, Event ) @@ -1318,8 +924,10 @@ function EVENT:onEvent( Event ) if EventData.EventGroup[Event.TgtGroupName] then if EventData.EventGroup[Event.TgtGroupName].EventFunction then - self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) + end + local Result, Value = xpcall( function() return EventData.EventGroup[Event.TgtGroupName].EventFunction( EventClass, Event ) @@ -1332,7 +940,9 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) + end local Result, Value = xpcall( function() @@ -1346,7 +956,7 @@ function EVENT:onEvent( Event ) -- If the EventData is not bound to a specific unit, then call the EventClass EventFunction. -- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon. - if Event.IniDCSUnit and not EventData.EventUnit then + if (Event.IniDCSUnit or Event.WeaponUNIT) and not EventData.EventUnit then if EventClass == EventData.EventClass then @@ -1354,8 +964,9 @@ function EVENT:onEvent( Event ) if EventData.EventFunction then -- There is an EventFunction defined, so call the EventFunction. - self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end local Result, Value = xpcall( function() return EventData.EventFunction( EventClass, Event ) @@ -1367,11 +978,14 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end + local Result, Value = xpcall( function() - return EventFunction( EventClass, Event ) + local Result, Value = EventFunction( EventClass, Event ) + return Result, Value end, ErrorHandler ) end end @@ -1385,6 +999,8 @@ function EVENT:onEvent( Event ) else self:E( { _EVENTMETA[Event.id].Text, Event } ) end + + Event = nil end --- The EVENTHANDLER structure diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index c643f56bc..8b6ef0331 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -46,227 +46,14 @@ -- I've reworked this development (taken the concept), and created a **hierarchical state machine** out of it, embedded within the DCS simulator. -- Additionally, I've added extendability and created an API that allows seamless FSM implementation. -- --- === --- --- # 1) @{#FSM} class, extends @{Base#BASE} --- --- ![Transition Rules and Transition Handlers and Event Triggers](..\Presentations\FSM\Dia3.JPG) +-- The following derived classes are available in the MOOSE framework, that implement a specialised form of a FSM: -- --- The FSM class is the base class of all FSM\_ derived classes. It implements the main functionality to define and execute Finite State Machines. --- The derived FSM\_ classes extend the Finite State Machine functionality to run a workflow process for a specific purpose or component. +-- * @{#FSM_TASK}: Models Finite State Machines for @{Task}s. +-- * @{#FSM_PROCESS}: Models Finite State Machines for @{Task} actions, which control @{Client}s. +-- * @{#FSM_CONTROLLABLE}: Models Finite State Machines for @{Controllable}s, which are @{Group}s, @{Unit}s, @{Client}s. +-- * @{#FSM_SET}: Models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here +-- for multiple objects or the position of the state machine in the process. -- --- Finite State Machines have **Transition Rules**, **Transition Handlers** and **Event Triggers**. --- --- The **Transition Rules** define the "Process Flow Boundaries", that is, --- the path that can be followed hopping from state to state upon triggered events. --- If an event is triggered, and there is no valid path found for that event, --- an error will be raised and the FSM will stop functioning. --- --- The **Transition Handlers** are special methods that can be defined by the mission designer, following a defined syntax. --- If the FSM object finds a method of such a handler, then the method will be called by the FSM, passing specific parameters. --- The method can then define its own custom logic to implement the FSM workflow, and to conduct other actions. --- --- The **Event Triggers** are methods that are defined by the FSM, which the mission designer can use to implement the workflow. --- Most of the time, these Event Triggers are used within the Transition Handler methods, so that a workflow is created running through the state machine. --- --- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation. --- The below documentation has a seperate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**. --- --- ## 1.1) FSM Linear Transitions --- --- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**. --- The Lineair transition rule evaluation will always be done from the **current state** of the FSM. --- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop. --- --- ### 1.1.1) FSM Transition Rules --- --- The FSM has transition rules that it follows and validates, as it walks the process. --- These rules define when an FSM can transition from a specific state towards an other specific state upon a triggered event. --- --- The method @{#FSM.AddTransition}() specifies a new possible Transition Rule for the FSM. --- --- The initial state can be defined using the method @{#FSM.SetStartState}(). The default start state of an FSM is "None". --- --- Find below an example of a Linear Transition Rule definition for an FSM. --- --- local Fsm3Switch = FSM:New() -- #FsmDemo --- FsmSwitch:SetStartState( "Off" ) --- FsmSwitch:AddTransition( "Off", "SwitchOn", "On" ) --- FsmSwitch:AddTransition( "Off", "SwitchMiddle", "Middle" ) --- FsmSwitch:AddTransition( "On", "SwitchOff", "Off" ) --- FsmSwitch:AddTransition( "Middle", "SwitchOff", "Off" ) --- --- The above code snippet models a 3-way switch Linear Transition: --- --- * It can be switched **On** by triggering event **SwitchOn**. --- * It can be switched to the **Middle** position, by triggering event **SwitchMiddle**. --- * It can be switched **Off** by triggering event **SwitchOff**. --- * Note that once the Switch is **On** or **Middle**, it can only be switched **Off**. --- --- ### Some additional comments: --- --- Note that Linear Transition Rules **can be declared in a few variations**: --- --- * The From states can be **a table of strings**, indicating that the transition rule will be valid **if the current state** of the FSM will be **one of the given From states**. --- * The From state can be a **"*"**, indicating that **the transition rule will always be valid**, regardless of the current state of the FSM. --- --- The below code snippet shows how the two last lines can be rewritten and consensed. --- --- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" ) --- --- ### 1.1.2) Transition Handling --- --- ![Transition Handlers](..\Presentations\FSM\Dia4.JPG) --- --- An FSM transitions in **4 moments** when an Event is being triggered and processed. --- The mission designer can define for each moment specific logic within methods implementations following a defined API syntax. --- These methods define the flow of the FSM process; because in those methods the FSM Internal Events will be triggered. --- --- * To handle **State** transition moments, create methods starting with OnLeave or OnEnter concatenated with the State name. --- * To handle **Event** transition moments, create methods starting with OnBefore or OnAfter concatenated with the Event name. --- --- **The OnLeave and OnBefore transition methods may return false, which will cancel the transition!** --- --- Transition Handler methods need to follow the above specified naming convention, but are also passed parameters from the FSM. --- These parameters are on the correct order: From, Event, To: --- --- * From = A string containing the From state. --- * Event = A string containing the Event name that was triggered. --- * To = A string containing the To state. --- --- On top, each of these methods can have a variable amount of parameters passed. See the example in section [1.1.3](#1.1.3\)-event-triggers). --- --- ### 1.1.3) Event Triggers --- --- ![Event Triggers](..\Presentations\FSM\Dia5.JPG) --- --- The FSM creates for each Event two **Event Trigger methods**. --- There are two modes how Events can be triggered, which is **synchronous** and **asynchronous**: --- --- * The method **FSM:Event()** triggers an Event that will be processed **synchronously** or **immediately**. --- * The method **FSM:__Event( __seconds__ )** triggers an Event that will be processed **asynchronously** over time, waiting __x seconds__. --- --- The destinction between these 2 Event Trigger methods are important to understand. An asynchronous call will "log" the Event Trigger to be executed at a later time. --- Processing will just continue. Synchronous Event Trigger methods are useful to change states of the FSM immediately, but may have a larger processing impact. --- --- The following example provides a little demonstration on the difference between synchronous and asynchronous Event Triggering. --- --- function FSM:OnAfterEvent( From, Event, To, Amount ) --- self:T( { Amount = Amount } ) --- end --- --- local Amount = 1 --- FSM:__Event( 5, Amount ) --- --- Amount = Amount + 1 --- FSM:Event( Text, Amount ) --- --- In this example, the **:OnAfterEvent**() Transition Handler implementation will get called when **Event** is being triggered. --- Before we go into more detail, let's look at the last 4 lines of the example. --- The last line triggers synchronously the **Event**, and passes Amount as a parameter. --- The 3rd last line of the example triggers asynchronously **Event**. --- Event will be processed after 5 seconds, and Amount is given as a parameter. --- --- The output of this little code fragment will be: --- --- * Amount = 2 --- * Amount = 2 --- --- Because ... When Event was asynchronously processed after 5 seconds, Amount was set to 2. So be careful when processing and passing values and objects in asynchronous processing! --- --- ### 1.1.4) Linear Transition Example --- --- This example is fully implemented in the MOOSE test mission on GITHUB: [FSM-100 - Transition Explanation](https://github.com/FlightControl-Master/MOOSE/blob/master/Moose%20Test%20Missions/FSM%20-%20Finite%20State%20Machine/FSM-100%20-%20Transition%20Explanation/FSM-100%20-%20Transition%20Explanation.lua) --- --- It models a unit standing still near Batumi, and flaring every 5 seconds while switching between a Green flare and a Red flare. --- The purpose of this example is not to show how exciting flaring is, but it demonstrates how a Linear Transition FSM can be build. --- Have a look at the source code. The source code is also further explained below in this section. --- --- The example creates a new FsmDemo object from class FSM. --- It will set the start state of FsmDemo to state **Green**. --- Two Linear Transition Rules are created, where upon the event **Switch**, --- the FsmDemo will transition from state **Green** to **Red** and from **Red** back to **Green**. --- --- ![Transition Example](..\Presentations\FSM\Dia6.JPG) --- --- local FsmDemo = FSM:New() -- #FsmDemo --- FsmDemo:SetStartState( "Green" ) --- FsmDemo:AddTransition( "Green", "Switch", "Red" ) --- FsmDemo:AddTransition( "Red", "Switch", "Green" ) --- --- In the above example, the FsmDemo could flare every 5 seconds a Green or a Red flare into the air. --- The next code implements this through the event handling method **OnAfterSwitch**. --- --- ![Transition Flow](..\Presentations\FSM\Dia7.JPG) --- --- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) --- self:T( { From, Event, To, FsmUnit } ) --- --- if From == "Green" then --- FsmUnit:Flare(FLARECOLOR.Green) --- else --- if From == "Red" then --- FsmUnit:Flare(FLARECOLOR.Red) --- end --- end --- self:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. --- end --- --- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the first Switch event to happen in 5 seconds. --- --- The OnAfterSwitch implements a loop. The last line of the code fragment triggers the Switch Event within 5 seconds. --- Upon the event execution (after 5 seconds), the OnAfterSwitch method is called of FsmDemo (cfr. the double point notation!!! ":"). --- The OnAfterSwitch method receives from the FSM the 3 transition parameter details ( From, Event, To ), --- and one additional parameter that was given when the event was triggered, which is in this case the Unit that is used within OnSwitchAfter. --- --- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) --- --- For debugging reasons the received parameters are traced within the DCS.log. --- --- self:T( { From, Event, To, FsmUnit } ) --- --- The method will check if the From state received is either "Green" or "Red" and will flare the respective color from the FsmUnit. --- --- if From == "Green" then --- FsmUnit:Flare(FLARECOLOR.Green) --- else --- if From == "Red" then --- FsmUnit:Flare(FLARECOLOR.Red) --- end --- end --- --- It is important that the Switch event is again triggered, otherwise, the FsmDemo would stop working after having the first Event being handled. --- --- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. --- --- The below code fragment extends the FsmDemo, demonstrating multiple **From states declared as a table**, adding a **Linear Transition Rule**. --- The new event **Stop** will cancel the Switching process. --- The transition for event Stop can be executed if the current state of the FSM is either "Red" or "Green". --- --- local FsmDemo = FSM:New() -- #FsmDemo --- FsmDemo:SetStartState( "Green" ) --- FsmDemo:AddTransition( "Green", "Switch", "Red" ) --- FsmDemo:AddTransition( "Red", "Switch", "Green" ) --- FsmDemo:AddTransition( { "Red", "Green" }, "Stop", "Stopped" ) --- --- The transition for event Stop can also be simplified, as any current state of the FSM is valid. --- --- FsmDemo:AddTransition( "*", "Stop", "Stopped" ) --- --- So... When FsmDemo:Stop() is being triggered, the state of FsmDemo will transition from Red or Green to Stopped. --- And there is no transition handling method defined for that transition, thus, no new event is being triggered causing the FsmDemo process flow to halt. --- --- ## 1.5) FSM Hierarchical Transitions --- --- Hierarchical Transitions allow to re-use readily available and implemented FSMs. --- This becomes in very useful for mission building, where mission designers build complex processes and workflows, --- combining smaller FSMs to one single FSM. --- --- The FSM can embed **Sub-FSMs** that will execute and return **multiple possible Return (End) States**. --- Depending upon **which state is returned**, the main FSM can continue the flow **triggering specific events**. --- --- The method @{#FSM.AddProcess}() adds a new Sub-FSM to the FSM. --- -- ==== -- -- # **API CHANGE HISTORY** @@ -300,8 +87,233 @@ do -- FSM --- FSM class - -- @type FSM + --- @type FSM -- @extends Core.Base#BASE + + + --- # 1) FSM class, extends @{Base#BASE} + -- + -- ![Transition Rules and Transition Handlers and Event Triggers](..\Presentations\FSM\Dia3.JPG) + -- + -- The FSM class is the base class of all FSM\_ derived classes. It implements the main functionality to define and execute Finite State Machines. + -- The derived FSM\_ classes extend the Finite State Machine functionality to run a workflow process for a specific purpose or component. + -- + -- Finite State Machines have **Transition Rules**, **Transition Handlers** and **Event Triggers**. + -- + -- The **Transition Rules** define the "Process Flow Boundaries", that is, + -- the path that can be followed hopping from state to state upon triggered events. + -- If an event is triggered, and there is no valid path found for that event, + -- an error will be raised and the FSM will stop functioning. + -- + -- The **Transition Handlers** are special methods that can be defined by the mission designer, following a defined syntax. + -- If the FSM object finds a method of such a handler, then the method will be called by the FSM, passing specific parameters. + -- The method can then define its own custom logic to implement the FSM workflow, and to conduct other actions. + -- + -- The **Event Triggers** are methods that are defined by the FSM, which the mission designer can use to implement the workflow. + -- Most of the time, these Event Triggers are used within the Transition Handler methods, so that a workflow is created running through the state machine. + -- + -- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation. + -- The below documentation has a seperate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**. + -- + -- ## 1.1) FSM Linear Transitions + -- + -- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**. + -- The Lineair transition rule evaluation will always be done from the **current state** of the FSM. + -- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop. + -- + -- ### 1.1.1) FSM Transition Rules + -- + -- The FSM has transition rules that it follows and validates, as it walks the process. + -- These rules define when an FSM can transition from a specific state towards an other specific state upon a triggered event. + -- + -- The method @{#FSM.AddTransition}() specifies a new possible Transition Rule for the FSM. + -- + -- The initial state can be defined using the method @{#FSM.SetStartState}(). The default start state of an FSM is "None". + -- + -- Find below an example of a Linear Transition Rule definition for an FSM. + -- + -- local Fsm3Switch = FSM:New() -- #FsmDemo + -- FsmSwitch:SetStartState( "Off" ) + -- FsmSwitch:AddTransition( "Off", "SwitchOn", "On" ) + -- FsmSwitch:AddTransition( "Off", "SwitchMiddle", "Middle" ) + -- FsmSwitch:AddTransition( "On", "SwitchOff", "Off" ) + -- FsmSwitch:AddTransition( "Middle", "SwitchOff", "Off" ) + -- + -- The above code snippet models a 3-way switch Linear Transition: + -- + -- * It can be switched **On** by triggering event **SwitchOn**. + -- * It can be switched to the **Middle** position, by triggering event **SwitchMiddle**. + -- * It can be switched **Off** by triggering event **SwitchOff**. + -- * Note that once the Switch is **On** or **Middle**, it can only be switched **Off**. + -- + -- ### Some additional comments: + -- + -- Note that Linear Transition Rules **can be declared in a few variations**: + -- + -- * The From states can be **a table of strings**, indicating that the transition rule will be valid **if the current state** of the FSM will be **one of the given From states**. + -- * The From state can be a **"*"**, indicating that **the transition rule will always be valid**, regardless of the current state of the FSM. + -- + -- The below code snippet shows how the two last lines can be rewritten and consensed. + -- + -- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" ) + -- + -- ### 1.1.2) Transition Handling + -- + -- ![Transition Handlers](..\Presentations\FSM\Dia4.JPG) + -- + -- An FSM transitions in **4 moments** when an Event is being triggered and processed. + -- The mission designer can define for each moment specific logic within methods implementations following a defined API syntax. + -- These methods define the flow of the FSM process; because in those methods the FSM Internal Events will be triggered. + -- + -- * To handle **State** transition moments, create methods starting with OnLeave or OnEnter concatenated with the State name. + -- * To handle **Event** transition moments, create methods starting with OnBefore or OnAfter concatenated with the Event name. + -- + -- **The OnLeave and OnBefore transition methods may return false, which will cancel the transition!** + -- + -- Transition Handler methods need to follow the above specified naming convention, but are also passed parameters from the FSM. + -- These parameters are on the correct order: From, Event, To: + -- + -- * From = A string containing the From state. + -- * Event = A string containing the Event name that was triggered. + -- * To = A string containing the To state. + -- + -- On top, each of these methods can have a variable amount of parameters passed. See the example in section [1.1.3](#1.1.3\)-event-triggers). + -- + -- ### 1.1.3) Event Triggers + -- + -- ![Event Triggers](..\Presentations\FSM\Dia5.JPG) + -- + -- The FSM creates for each Event two **Event Trigger methods**. + -- There are two modes how Events can be triggered, which is **synchronous** and **asynchronous**: + -- + -- * The method **FSM:Event()** triggers an Event that will be processed **synchronously** or **immediately**. + -- * The method **FSM:__Event( __seconds__ )** triggers an Event that will be processed **asynchronously** over time, waiting __x seconds__. + -- + -- The destinction between these 2 Event Trigger methods are important to understand. An asynchronous call will "log" the Event Trigger to be executed at a later time. + -- Processing will just continue. Synchronous Event Trigger methods are useful to change states of the FSM immediately, but may have a larger processing impact. + -- + -- The following example provides a little demonstration on the difference between synchronous and asynchronous Event Triggering. + -- + -- function FSM:OnAfterEvent( From, Event, To, Amount ) + -- self:T( { Amount = Amount } ) + -- end + -- + -- local Amount = 1 + -- FSM:__Event( 5, Amount ) + -- + -- Amount = Amount + 1 + -- FSM:Event( Text, Amount ) + -- + -- In this example, the **:OnAfterEvent**() Transition Handler implementation will get called when **Event** is being triggered. + -- Before we go into more detail, let's look at the last 4 lines of the example. + -- The last line triggers synchronously the **Event**, and passes Amount as a parameter. + -- The 3rd last line of the example triggers asynchronously **Event**. + -- Event will be processed after 5 seconds, and Amount is given as a parameter. + -- + -- The output of this little code fragment will be: + -- + -- * Amount = 2 + -- * Amount = 2 + -- + -- Because ... When Event was asynchronously processed after 5 seconds, Amount was set to 2. So be careful when processing and passing values and objects in asynchronous processing! + -- + -- ### 1.1.4) Linear Transition Example + -- + -- This example is fully implemented in the MOOSE test mission on GITHUB: [FSM-100 - Transition Explanation](https://github.com/FlightControl-Master/MOOSE/blob/master/Moose%20Test%20Missions/FSM%20-%20Finite%20State%20Machine/FSM-100%20-%20Transition%20Explanation/FSM-100%20-%20Transition%20Explanation.lua) + -- + -- It models a unit standing still near Batumi, and flaring every 5 seconds while switching between a Green flare and a Red flare. + -- The purpose of this example is not to show how exciting flaring is, but it demonstrates how a Linear Transition FSM can be build. + -- Have a look at the source code. The source code is also further explained below in this section. + -- + -- The example creates a new FsmDemo object from class FSM. + -- It will set the start state of FsmDemo to state **Green**. + -- Two Linear Transition Rules are created, where upon the event **Switch**, + -- the FsmDemo will transition from state **Green** to **Red** and from **Red** back to **Green**. + -- + -- ![Transition Example](..\Presentations\FSM\Dia6.JPG) + -- + -- local FsmDemo = FSM:New() -- #FsmDemo + -- FsmDemo:SetStartState( "Green" ) + -- FsmDemo:AddTransition( "Green", "Switch", "Red" ) + -- FsmDemo:AddTransition( "Red", "Switch", "Green" ) + -- + -- In the above example, the FsmDemo could flare every 5 seconds a Green or a Red flare into the air. + -- The next code implements this through the event handling method **OnAfterSwitch**. + -- + -- ![Transition Flow](..\Presentations\FSM\Dia7.JPG) + -- + -- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) + -- self:T( { From, Event, To, FsmUnit } ) + -- + -- if From == "Green" then + -- FsmUnit:Flare(FLARECOLOR.Green) + -- else + -- if From == "Red" then + -- FsmUnit:Flare(FLARECOLOR.Red) + -- end + -- end + -- self:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. + -- end + -- + -- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the first Switch event to happen in 5 seconds. + -- + -- The OnAfterSwitch implements a loop. The last line of the code fragment triggers the Switch Event within 5 seconds. + -- Upon the event execution (after 5 seconds), the OnAfterSwitch method is called of FsmDemo (cfr. the double point notation!!! ":"). + -- The OnAfterSwitch method receives from the FSM the 3 transition parameter details ( From, Event, To ), + -- and one additional parameter that was given when the event was triggered, which is in this case the Unit that is used within OnSwitchAfter. + -- + -- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) + -- + -- For debugging reasons the received parameters are traced within the DCS.log. + -- + -- self:T( { From, Event, To, FsmUnit } ) + -- + -- The method will check if the From state received is either "Green" or "Red" and will flare the respective color from the FsmUnit. + -- + -- if From == "Green" then + -- FsmUnit:Flare(FLARECOLOR.Green) + -- else + -- if From == "Red" then + -- FsmUnit:Flare(FLARECOLOR.Red) + -- end + -- end + -- + -- It is important that the Switch event is again triggered, otherwise, the FsmDemo would stop working after having the first Event being handled. + -- + -- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. + -- + -- The below code fragment extends the FsmDemo, demonstrating multiple **From states declared as a table**, adding a **Linear Transition Rule**. + -- The new event **Stop** will cancel the Switching process. + -- The transition for event Stop can be executed if the current state of the FSM is either "Red" or "Green". + -- + -- local FsmDemo = FSM:New() -- #FsmDemo + -- FsmDemo:SetStartState( "Green" ) + -- FsmDemo:AddTransition( "Green", "Switch", "Red" ) + -- FsmDemo:AddTransition( "Red", "Switch", "Green" ) + -- FsmDemo:AddTransition( { "Red", "Green" }, "Stop", "Stopped" ) + -- + -- The transition for event Stop can also be simplified, as any current state of the FSM is valid. + -- + -- FsmDemo:AddTransition( "*", "Stop", "Stopped" ) + -- + -- So... When FsmDemo:Stop() is being triggered, the state of FsmDemo will transition from Red or Green to Stopped. + -- And there is no transition handling method defined for that transition, thus, no new event is being triggered causing the FsmDemo process flow to halt. + -- + -- ## 1.5) FSM Hierarchical Transitions + -- + -- Hierarchical Transitions allow to re-use readily available and implemented FSMs. + -- This becomes in very useful for mission building, where mission designers build complex processes and workflows, + -- combining smaller FSMs to one single FSM. + -- + -- The FSM can embed **Sub-FSMs** that will execute and return **multiple possible Return (End) States**. + -- Depending upon **which state is returned**, the main FSM can continue the flow **triggering specific events**. + -- + -- The method @{#FSM.AddProcess}() adds a new Sub-FSM to the FSM. + -- + -- === + -- + -- @field #FSM FSM + -- FSM = { ClassName = "FSM", } @@ -420,7 +432,6 @@ do -- FSM for ProcessID, Process in pairs( self:GetProcesses() ) do if Process.From == From and Process.Event == Event then - self:T( Process ) return Process.fsm end end @@ -449,7 +460,7 @@ do -- FSM -- @param #number Score is a number providing the score of the status. -- @return #FSM self function FSM:AddScore( State, ScoreText, Score ) - self:F2( { State, ScoreText, Score } ) + self:F( { State, ScoreText, Score } ) self._Scores[State] = self._Scores[State] or {} self._Scores[State].ScoreText = ScoreText @@ -467,14 +478,15 @@ do -- FSM -- @param #number Score is a number providing the score of the status. -- @return #FSM self function FSM:AddScoreProcess( From, Event, State, ScoreText, Score ) - self:F2( { Event, State, ScoreText, Score } ) + self:F( { From, Event, State, ScoreText, Score } ) local Process = self:GetProcess( From, Event ) - self:T( { Process = Process._Name, Scores = Process._Scores, State = State, ScoreText = ScoreText, Score = Score } ) Process._Scores[State] = Process._Scores[State] or {} Process._Scores[State].ScoreText = ScoreText Process._Scores[State].Score = Score + + self:T( Process._Scores ) return Process end @@ -530,10 +542,20 @@ do -- FSM function FSM:_call_handler( handler, params, EventName ) + + local ErrorHandler = function( errmsg ) + + env.info( "Error in SCHEDULER function:" .. errmsg ) + if debug ~= nil then + env.info( debug.traceback() ) + end + + return errmsg + end if self[handler] then self:T( "Calling " .. handler ) self._EventSchedules[EventName] = nil - local Value = self[handler]( self, unpack(params) ) + local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler ) return Value end end @@ -710,10 +732,18 @@ end do -- FSM_CONTROLLABLE - --- FSM_CONTROLLABLE class - -- @type FSM_CONTROLLABLE + --- @type FSM_CONTROLLABLE -- @field Wrapper.Controllable#CONTROLLABLE Controllable -- @extends Core.Fsm#FSM + + --- # FSM_CONTROLLABLE, extends @{#FSM} + -- + -- FSM_CONTROLLABLE class models Finite State Machines for @{Controllable}s, which are @{Group}s, @{Unit}s, @{Client}s. + -- + -- === + -- + -- @field #FSM_CONTROLLABLE FSM_CONTROLLABLE + -- FSM_CONTROLLABLE = { ClassName = "FSM_CONTROLLABLE", } @@ -732,8 +762,66 @@ do -- FSM_CONTROLLABLE self:SetControllable( Controllable ) end + self:AddTransition( "*", "Stop", "Stopped" ) + + --- OnBefore Transition Handler for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] OnBeforeStop + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] OnAfterStop + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] Stop + -- @param #FSM_CONTROLLABLE self + + --- Asynchronous Event Trigger for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] __Stop + -- @param #FSM_CONTROLLABLE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#FSM_CONTROLLABLE] OnLeaveStopped + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Stopped. + -- @function [parent=#FSM_CONTROLLABLE] OnEnterStopped + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + return self end + + --- OnAfter Transition Handler for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] OnAfterStop + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function FSM_CONTROLLABLE:OnAfterStop(Controllable,From,Event,To) + + -- Clear all pending schedules + self.CallScheduler:Clear() + end --- Sets the CONTROLLABLE object that the FSM_CONTROLLABLE governs. -- @param #FSM_CONTROLLABLE self @@ -776,10 +864,19 @@ end do -- FSM_PROCESS - --- FSM_PROCESS class - -- @type FSM_PROCESS + --- @type FSM_PROCESS -- @field Tasking.Task#TASK Task -- @extends Core.Fsm#FSM_CONTROLLABLE + + + --- # FSM_PROCESS, extends @{#FSM} + -- + -- FSM_PROCESS class models Finite State Machines for @{Task} actions, which control @{Client}s. + -- + -- === + -- + -- @field #FSM_PROCESS FSM_PROCESS + -- FSM_PROCESS = { ClassName = "FSM_PROCESS", } @@ -801,12 +898,34 @@ do -- FSM_PROCESS function FSM_PROCESS:Init( FsmProcess ) self:T( "No Initialisation" ) end + + function FSM_PROCESS:_call_handler( handler, params, EventName ) + + local ErrorHandler = function( errmsg ) + + env.info( "Error in FSM_PROCESS call handler:" .. errmsg ) + if debug ~= nil then + env.info( debug.traceback() ) + end + + return errmsg + end + + if self[handler] then + self:F3( "Calling " .. handler ) + self._EventSchedules[EventName] = nil + local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler ) + return Value + --return self[handler]( self, self.Controllable, unpack( params ) ) + end + end --- Creates a new FSM_PROCESS object based on this FSM_PROCESS. -- @param #FSM_PROCESS self -- @return #FSM_PROCESS function FSM_PROCESS:Copy( Controllable, Task ) self:T( { self:GetClassNameAndID() } ) + local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS @@ -825,7 +944,7 @@ do -- FSM_PROCESS -- Copy Processes for ProcessID, Process in pairs( self:GetProcesses() ) do - self:T( { Process} ) + self:E( { Process} ) local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents ) end @@ -843,6 +962,22 @@ do -- FSM_PROCESS return NewFsm end + + --- Removes an FSM_PROCESS object. + -- @param #FSM_PROCESS self + -- @return #FSM_PROCESS + function FSM_PROCESS:Remove() + self:T( { self:GetClassNameAndID() } ) + + -- Copy Processes + for ProcessID, Process in pairs( self:GetProcesses() ) do + self:E( { Process} ) + Process.fsm:Remove() + Process.fsm = nil + end + + return self + end --- Sets the task of the process. -- @param #FSM_PROCESS self @@ -941,14 +1076,14 @@ end -- @param #string Event -- @param #string From -- @param #string To - function FSM_PROCESS:onstatechange( ProcessUnit, From, Event, To, Dummy ) + function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To, Dummy ) self:T( { ProcessUnit, From, Event, To, Dummy, self:IsTrace() } ) if self:IsTrace() then MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() end - self:T( self._Scores[To] ) + self:T( { Scores = self._Scores, To = To } ) -- TODO: This needs to be reworked with a callback functions allocated within Task, and set within the mission script from the Task Objects... if self._Scores[To] then @@ -968,6 +1103,15 @@ do -- FSM_TASK -- @type FSM_TASK -- @field Tasking.Task#TASK Task -- @extends Core.Fsm#FSM + + --- # FSM_TASK, extends @{#FSM} + -- + -- FSM_TASK class models Finite State Machines for @{Task}s. + -- + -- === + -- + -- @field #FSM_TASK FSM_TASK + -- FSM_TASK = { ClassName = "FSM_TASK", } @@ -1003,6 +1147,17 @@ do -- FSM_SET -- @type FSM_SET -- @field Core.Set#SET_BASE Set -- @extends Core.Fsm#FSM + + + --- # FSM_SET, extends @{#FSM} + -- + -- FSM_SET class models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here + -- for multiple objects or the position of the state machine in the process. + -- + -- === + -- + -- @field #FSM_SET FSM_SET + -- FSM_SET = { ClassName = "FSM_SET", } diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 5dbab4fce..25ea2989e 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -138,6 +138,8 @@ do -- MENU_BASE } --- Consructor + -- @param #MENU_BASE + -- @return #MENU_BASE function MENU_BASE:New( MenuText, ParentMenu ) local MenuParentPath = {} @@ -150,10 +152,43 @@ do -- MENU_BASE self.MenuPath = nil self.MenuText = MenuText self.MenuParentPath = MenuParentPath + self.Menus = {} + self.MenuCount = 0 + self.MenuRemoveParent = false + self.MenuTime = timer.getTime() return self end + --- Gets a @{Menu} from a parent @{Menu} + -- @param #MENU_BASE self + -- @param #string MenuText The text of the child menu. + -- @return #MENU_BASE + function MENU_BASE:GetMenu( MenuText ) + self:F( { self.Menus, MenuText } ) + return self.Menus[MenuText] + end + + --- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}. + -- @param #MENU_BASE self + -- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}. + -- @return #MENU_BASE + function MENU_BASE:SetRemoveParent( RemoveParent ) + self:F( { RemoveParent } ) + self.MenuRemoveParent = RemoveParent + return self + end + + + --- Sets a time stamp for later prevention of menu removal. + -- @param #MENU_BASE self + -- @param MenuTime + -- @return #MENU_BASE + function MENU_BASE:SetTime( MenuTime ) + self.MenuTime = MenuTime + return self + end + end do -- MENU_COMMAND_BASE @@ -161,7 +196,7 @@ do -- MENU_COMMAND_BASE --- The MENU_COMMAND_BASE class -- @type MENU_COMMAND_BASE -- @field #function MenuCallHandler - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_COMMAND_BASE = { ClassName = "MENU_COMMAND_BASE", CommandMenuFunction = nil, @@ -170,6 +205,8 @@ do -- MENU_COMMAND_BASE } --- Constructor + -- @param #MENU_COMMAND_BASE + -- @return #MENU_COMMAND_BASE function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) @@ -189,7 +226,7 @@ do -- MENU_MISSION --- The MENU_MISSION class -- @type MENU_MISSION - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_MISSION = { ClassName = "MENU_MISSION" } @@ -198,7 +235,7 @@ do -- MENU_MISSION -- @param #MENU_MISSION self -- @param #string MenuText The text for the menu. -- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other). - -- @return #MENU_MISSION self + -- @return #MENU_MISSION function MENU_MISSION:New( MenuText, ParentMenu ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) @@ -225,7 +262,7 @@ do -- MENU_MISSION --- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept! -- @param #MENU_MISSION self - -- @return #MENU_MISSION self + -- @return #MENU_MISSION function MENU_MISSION:RemoveSubMenus() self:F( self.MenuPath ) @@ -256,7 +293,7 @@ do -- MENU_MISSION_COMMAND --- The MENU_MISSION_COMMAND class -- @type MENU_MISSION_COMMAND - -- @extends Menu#MENU_COMMAND_BASE + -- @extends Core.Menu#MENU_COMMAND_BASE MENU_MISSION_COMMAND = { ClassName = "MENU_MISSION_COMMAND" } @@ -306,7 +343,7 @@ do -- MENU_COALITION --- The MENU_COALITION class -- @type MENU_COALITION - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the planes within the red coalition. -- -- To test, join the planes, then look at the other radio menus (Option F10). @@ -380,7 +417,7 @@ do -- MENU_COALITION --- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept! -- @param #MENU_COALITION self - -- @return #MENU_COALITION self + -- @return #MENU_COALITION function MENU_COALITION:RemoveSubMenus() self:F( self.MenuPath ) @@ -411,7 +448,7 @@ do -- MENU_COALITION_COMMAND --- The MENU_COALITION_COMMAND class -- @type MENU_COALITION_COMMAND - -- @extends Menu#MENU_COMMAND_BASE + -- @extends Core.Menu#MENU_COMMAND_BASE MENU_COALITION_COMMAND = { ClassName = "MENU_COALITION_COMMAND" } @@ -423,7 +460,7 @@ do -- MENU_COALITION_COMMAND -- @param Menu#MENU_COALITION ParentMenu The parent menu. -- @param CommandMenuFunction A function that is called when the menu key is pressed. -- @param CommandMenuArgument An argument for the function. There can only be ONE argument given. So multiple arguments must be wrapped into a table. See the below example how to do this. - -- @return #MENU_COALITION_COMMAND self + -- @return #MENU_COALITION_COMMAND function MENU_COALITION_COMMAND:New( Coalition, MenuText, ParentMenu, CommandMenuFunction, ... ) local self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) @@ -468,7 +505,7 @@ do -- MENU_CLIENT --- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters. -- @type MENU_CLIENT - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the two clients of planes. -- -- Each client will receive a different menu structure. @@ -609,7 +646,7 @@ do -- MENU_CLIENT --- The MENU_CLIENT_COMMAND class -- @type MENU_CLIENT_COMMAND - -- @extends Menu#MENU_COMMAND + -- @extends Core.Menu#MENU_COMMAND MENU_CLIENT_COMMAND = { ClassName = "MENU_CLIENT_COMMAND" } @@ -695,7 +732,7 @@ do --- The MENU_GROUP class -- @type MENU_GROUP - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the two groups of planes. -- -- Each group will receive a different menu structure. @@ -769,8 +806,6 @@ do self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) MenuGroup._Menus[Path] = self - self.Menus = {} - self.MenuGroup = MenuGroup self.Path = Path self.MenuGroupID = MenuGroup:GetID() @@ -780,8 +815,10 @@ do self:T( { "Adding Menu ", MenuText, self.MenuParentPath } ) self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuGroupID, MenuText, self.MenuParentPath ) - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self + if self.ParentMenu and self.ParentMenu.Menus then + self.ParentMenu.Menus[MenuText] = self + self:F( { self.ParentMenu.Menus, MenuText } ) + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 end end @@ -792,42 +829,56 @@ do --- Removes the sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self + -- @param MenuTime -- @return #MENU_GROUP self - function MENU_GROUP:RemoveSubMenus() - self:F( self.MenuPath ) + function MENU_GROUP:RemoveSubMenus( MenuTime ) + self:F2( { self.MenuPath, MenuTime, self.MenuTime } ) - for MenuID, Menu in pairs( self.Menus ) do - Menu:Remove() + self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } ) + for MenuText, Menu in pairs( self.Menus ) do + Menu:Remove( MenuTime ) end end - + + --- Removes the main menu and sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self + -- @param MenuTime -- @return #nil - function MENU_GROUP:Remove() - self:F( { self.MenuGroupID, self.MenuPath } ) + function MENU_GROUP:Remove( MenuTime ) + self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - self:RemoveSubMenus() - - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] + self:RemoveSubMenus( MenuTime ) - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuPath] = nil + if not MenuTime or self.MenuTime ~= MenuTime then + if self.MenuGroup._Menus[self.Path] then + self = self.MenuGroup._Menus[self.Path] + + missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) + if self.ParentMenu then + self.ParentMenu.Menus[self.MenuText] = nil + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 + if self.ParentMenu.MenuCount == 0 then + if self.MenuRemoveParent == true then + self:T( "Removing Parent Menu " ) + self.ParentMenu:Remove() + end + end + end + self:T( { "Removing Group Menu:", self.MenuGroup:GetName(), self.MenuGroup._Menus[self.Path].Path } ) + self.MenuGroup._Menus[self.Path] = nil + self = nil end - self:E( self.MenuGroup._Menus[self.Path] ) - self.MenuGroup._Menus[self.Path] = nil - self = nil end + return nil end --- The MENU_GROUP_COMMAND class -- @type MENU_GROUP_COMMAND - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_GROUP_COMMAND = { ClassName = "MENU_GROUP_COMMAND" } @@ -839,13 +890,14 @@ do -- @param ParentMenu The parent menu. -- @param CommandMenuFunction A function that is called when the menu key is pressed. -- @param CommandMenuArgument An argument for the function. - -- @return Menu#MENU_GROUP_COMMAND self + -- @return #MENU_GROUP_COMMAND function MENU_GROUP_COMMAND:New( MenuGroup, MenuText, ParentMenu, CommandMenuFunction, ... ) MenuGroup._Menus = MenuGroup._Menus or {} local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText if MenuGroup._Menus[Path] then self = MenuGroup._Menus[Path] + self:T( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } ) else self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) MenuGroup._Menus[Path] = self @@ -856,33 +908,45 @@ do self.MenuText = MenuText self.ParentMenu = ParentMenu - self:T( { "Adding Command Menu ", MenuText, self.MenuParentPath } ) + self:T( { "Adding Group Command Menu:", MenuGroup:GetName(), MenuText, self.MenuParentPath } ) self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler, arg ) - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self + if self.ParentMenu and self.ParentMenu.Menus then + self.ParentMenu.Menus[MenuText] = self + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 + self:F( { ParentMenu.Menus, MenuText } ) end end - --self:F( { MenuGroup:GetName(), MenuText, ParentMenu.MenuPath } ) - return self end --- Removes a menu structure for a group. -- @param #MENU_GROUP_COMMAND self + -- @param MenuTime -- @return #nil - function MENU_GROUP_COMMAND:Remove() - self:F( { self.MenuGroupID, self.MenuPath } ) + function MENU_GROUP_COMMAND:Remove( MenuTime ) + self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] - - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - self.ParentMenu.Menus[self.MenuPath] = nil - self:E( self.MenuGroup._Menus[self.Path] ) - self.MenuGroup._Menus[self.Path] = nil - self = nil + if not MenuTime or self.MenuTime ~= MenuTime then + if self.MenuGroup._Menus[self.Path] then + self = self.MenuGroup._Menus[self.Path] + + missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) + self:T( { "Removing Group Command Menu:", self.MenuGroup:GetName(), self.MenuText, self.Path, self.MenuGroup._Menus[self.Path].Path } ) + + self.ParentMenu.Menus[self.MenuText] = nil + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 + if self.ParentMenu.MenuCount == 0 then + if self.MenuRemoveParent == true then + self:T( "Removing Parent Menu " ) + self.ParentMenu:Remove() + end + end + + self.MenuGroup._Menus[self.Path] = nil + self = nil + end end return nil diff --git a/Moose Development/Moose/Core/Radio.lua b/Moose Development/Moose/Core/Radio.lua new file mode 100644 index 000000000..38735de28 --- /dev/null +++ b/Moose Development/Moose/Core/Radio.lua @@ -0,0 +1,330 @@ +--- **Core** - The RADIO class is responsible for **transmitting radio communications**. +-- +-- --- bitmap +-- +-- === +-- +-- What are radio communications in DCS ? +-- +-- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM), +-- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**. +-- +-- How to supply DCS my own Sound Files ? +-- +-- * Your sound files need to be encoded in **.ogg** or .wav, +-- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings, +-- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file), +-- * For simplicty sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission. +-- +-- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Unit#UNIT} or a @{Group#GROUP} or by any other @{Positionable#POSITIONABLE} +-- +-- * If the transmitter is a @{Unit#UNIT} or a @{Group#GROUP}, DCS will set the power of the transmission automatically, +-- * If the transmitter is any other @{Positionable#POSITIONABLE}, the transmisison can't be subtitled or looped. +-- +-- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft, +-- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below). +-- If a FC3 airacraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. +-- +-- === +-- +-- ### Authors: Hugues "Grey_Echo" Bousquet +-- +-- @module Radio + +--- # 1) RADIO class, extends @{Base#BASE} +-- +-- ## 1.1) RADIO usage +-- +-- There are 3 steps to a successful radio transmission. +-- +-- * First, you need to **"add" a @{#RADIO} object** to your @{Positionable#POSITIONABLE}. This is done using the @{Positionable#POSITIONABLE.GetRadio}() function, +-- * Then, you will **set the relevant parameters** to the transmission (see below), +-- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{Positionable#POSITIONABLE.Broadcast}() function. +-- +-- Methods to set relevant parameters for both a @{Unit#UNIT} or a @{Group#GROUP} or any other @{Positionable#POSITIONABLE} +-- +-- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"), +-- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission, +-- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission. +-- +-- Additional Methods to set relevant parameters if the transmiter is a @{Unit#UNIT} or a @{Group#GROUP} +-- +-- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped, +-- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration, +-- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call +-- +-- Additional Methods to set relevant parameters if the transmiter is any other @{Wrapper.Positionable#POSITIONABLE} +-- +-- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts +-- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call +-- +-- What is this power thing ? +-- +-- * If your transmission is sent by a @{Positionable#POSITIONABLE} other than a @{Unit#UNIT} or a @{Group#GROUP}, you can set the power of the antenna, +-- * Otherwise, DCS sets it automatically, depending on what's available on your Unit, +-- * If the player gets **too far** from the transmiter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**, +-- * This an automated DCS calculation you have no say on, +-- * For reference, a standard VOR station has a 100W antenna, a standard AA TACAN has a 120W antenna, and civilian ATC's antenna usually range between 300 and 500W, +-- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission. +-- +-- @type RADIO +-- @field Wrapper.Positionable#POSITIONABLE Positionable The transmiter +-- @field #string FileName Name of the sound file +-- @field #number Frequency Frequency of the transmission in Hz +-- @field #number Modulation Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM) +-- @field #string Subtitle Subtitle of the transmission +-- @field #number SubtitleDuration Duration of the Subtitle in seconds +-- @field #number Power Power of the antenna is Watts +-- @field #boolean Loop +-- @extends Core.Base#BASE +RADIO = { + ClassName = "RADIO", + FileName = "", + Frequency = 0, + Modulation = radio.modulation.AM, + Subtitle = "", + SubtitleDuration = 0, + Power = 100, + Loop = 0, +} + +--- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast +-- @param #RADIO self +-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. +-- @return #RADIO Radio +-- @return #nil If Positionable is invalid +-- @usage +-- -- If you want to create a RADIO, you probably should use @{Positionable#POSITIONABLE.GetRadio}() instead +function RADIO:New(Positionable) + local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO + + self:F(Positionable) + + if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid + self.Positionable = Positionable + return self + end + + self:E({"The passed positionable is invalid, no RADIO created", Positionable}) + return nil +end + +--- Check validity of the filename passed and sets RADIO.FileName +-- @param #RADIO self +-- @param #string FileName File name of the sound file (i.e. "Noise.ogg") +-- @return #RADIO self +function RADIO:SetFileName(FileName) + self:F2(FileName) + + if type(FileName) == "string" then + if FileName:find(".ogg") or FileName:find(".wav") then + if not FileName:find("l10n/DEFAULT/") then + FileName = "l10n/DEFAULT/" .. FileName + end + self.FileName = FileName + return self + end + end + + self:E({"File name invalid. Maybe something wrong with the extension ?", self.FileName}) + return self +end + +--- Check validity of the frequency passed and sets RADIO.Frequency +-- @param #RADIO self +-- @param #number Frequency in MHz (Ranges allowed for radio transmissions in DCS : 30-88 / 108-152 / 225-400MHz) +-- @return #RADIO self +function RADIO:SetFrequency(Frequency) + self:F2(Frequency) + if type(Frequency) == "number" then + -- If frequency is in range + if (Frequency >= 30 and Frequency < 88) or (Frequency >= 108 and Frequency < 152) or (Frequency >= 225 and Frequency < 400) then + self.Frequency = Frequency * 1000000 -- Conversion in Hz + -- If the RADIO is attached to a UNIT or a GROUP, we need to send the DCS Command "SetFrequency" to change the UNIT or GROUP frequency + if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then + self.Positionable:SetCommand({ + id = "SetFrequency", + params = { + frequency = self.Frequency, + modulation = self.Modulation, + } + }) + end + return self + end + end + self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", self.Frequency}) + return self +end + +--- Check validity of the frequency passed and sets RADIO.Modulation +-- @param #RADIO self +-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM +-- @return #RADIO self +function RADIO:SetModulation(Modulation) + self:F2(Modulation) + if type(Modulation) == "number" then + if Modulation == radio.modulation.AM or Modulation == radio.modulation.FM then --TODO Maybe make this future proof if ED decides to add an other modulation ? + self.Modulation = Modulation + return self + end + end + self:E({"Modulation is invalid. Use DCS's enum radio.modulation. Modulation unchanged.", self.Modulation}) + return self +end + +--- Check validity of the power passed and sets RADIO.Power +-- @param #RADIO self +-- @param #number Power in W +-- @return #RADIO self +function RADIO:SetPower(Power) + self:F2(Power) + if type(Power) == "number" then + self.Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that + return self + end + self:E({"Power is invalid. Power unchanged.", self.Power}) + return self +end + +--- Check validity of the loop passed and sets RADIO.Loop +-- @param #RADIO self +-- @param #boolean Loop +-- @return #RADIO self +-- @usage +function RADIO:SetLoop(Loop) + self:F2(Loop) + if type(Loop) == "boolean" then + self.Loop = Loop + return self + end + self:E({"Loop is invalid. Loop unchanged.", self.Loop}) + return self +end + +--- Check validity of the subtitle and the subtitleDuration passed and sets RADIO.subtitle and RADIO.subtitleDuration +-- @param #RADIO self +-- @param #string Subtitle +-- @param #number SubtitleDuration in s +-- @return #RADIO self +-- @usage +-- -- Both parameters are mandatory, since it wouldn't make much sense to change the Subtitle and not its duration +function RADIO:SetSubtitle(Subtitle, SubtitleDuration) + self:F2({Subtitle, SubtitleDuration}) + if type(Subtitle) == "string" then + self.Subtitle = Subtitle + else + self.Subtitle = "" + self:E({"Subtitle is invalid. Subtitle reset.", self.Subtitle}) + end + if type(SubtitleDuration) == "number" then + if math.floor(math.abs(SubtitleDuration)) == SubtitleDuration then + self.SubtitleDuration = SubtitleDuration + return self + end + end + self.SubtitleDuration = 0 + self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration}) +end + +--- Create a new transmission, that is to say, populate the RADIO with relevant data +-- @param #RADIO self +-- @param #string FileName +-- @param #number Frequency in MHz +-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM +-- @param #number Power in W +-- @return #RADIO self +-- @usage +-- -- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP, +-- but it will work with a UNIT or a GROUP anyway +-- -- Only the RADIO and the Filename are mandatory +function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power) + self:F({FileName, Frequency, Modulation, Power}) + + self:SetFileName(FileName) + if Frequency then self:SetFrequency(Frequency) end + if Modulation then self:SetModulation(Modulation) end + if Power then self:SetPower(Power) end + + return self +end + + +--- Create a new transmission, that is to say, populate the RADIO with relevant data +-- @param #RADIO self +-- @param #string FileName +-- @param #string Subtitle +-- @param #number SubtitleDuration in s +-- @param #number Frequency in MHz +-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM +-- @param #boolean Loop +-- @return #RADIO self +-- @usage +-- -- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP, +-- but it will work for any POSITIONABLE +-- -- Only the RADIO and the Filename are mandatory +function RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop) + self:F({FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop}) + + self:SetFileName(FileName) + if Subtitle then self:SetSubtitle(Subtitle) end + if SubtitleDuration then self:SetSubtitleDuration(SubtitleDuration) end + if Frequency then self:SetFrequency(Frequency) end + if Modulation then self:SetModulation(Modulation) end + if Loop then self:SetLoop(Loop) end + + return self +end + +--- Actually Broadcast the transmission +-- @param #RADIO self +-- @return #RADIO self +-- @usage +-- -- The Radio has to be populated with the new transmission before broadcasting. +-- -- Please use RADIO setters or either @{Radio#RADIO.NewGenericTransmission} or @{Radio#RADIO.NewUnitTransmission} +-- -- This class is in fact pretty smart, it determines the right DCS function to use depending on the type of POSITIONABLE +-- -- If the POSITIONABLE is not a UNIT or a GROUP, we use the generic (but limited) trigger.action.radioTransmission() +-- -- If the POSITIONABLE is a UNIT or a GROUP, we use the "TransmitMessage" Command +-- -- If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored. +-- -- If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration and Loop are ignored +function RADIO:Broadcast() + self:F() + -- If the POSITIONABLE is actually a UNIT or a GROUP, use the more complicated DCS command system + if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then + self:T2("Broadcasting from a UNIT or a GROUP") + self.Positionable:SetCommand({ + id = "TransmitMessage", + params = { + file = self.FileName, + duration = self.SubtitleDuration, + subtitle = self.Subtitle, + loop = self.Loop, + } + }) + else + -- If the POSITIONABLE is anything else, we revert to the general singleton function + self:T2("Broadcasting from a POSITIONABLE") + trigger.action.radioTransmission(self.FileName, self.Positionable:GetPositionVec3(), self.Modulation, false, self.Frequency, self.Power) + end + return self +end + +--- Stops a transmission +-- @param #RADIO self +-- @return #RADIO self +-- @usage +-- -- Especially usefull to stop the broadcast of looped transmissions +-- -- Only works with broadcasts from UNIT or GROUP +function RADIO:StopBroadcast() + self:F() + -- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command + if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then + self.Positionable:SetCommand({ + id = "StopTransmission", + params = {} + }) + else + self:E("This broadcast can't be stopped. It's not looped either, so please wait for the end of the sound file playback") + end + return self +end \ No newline at end of file diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index a414bcc54..a7962e677 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -64,7 +64,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr -- Initialize the ObjectSchedulers array, which is a weakly coupled table. -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. - self.ObjectSchedulers = self.ObjectSchedulers or {} -- setmetatable( {}, { __mode = "v" } ) + self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) -- or {} if Scheduler.MasterObject then self.ObjectSchedulers[self.CallID] = Scheduler @@ -181,11 +181,15 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID ) if CallID then local Schedule = self.Schedule[Scheduler] - Schedule[CallID].ScheduleID = timer.scheduleFunction( - Schedule[CallID].CallHandler, - CallID, - timer.getTime() + Schedule[CallID].Start - ) + -- Only start when there is no ScheduleID defined! + -- This prevents to "Start" the scheduler twice with the same CallID... + if not Schedule[CallID].ScheduleID then + Schedule[CallID].ScheduleID = timer.scheduleFunction( + Schedule[CallID].CallHandler, + CallID, + timer.getTime() + Schedule[CallID].Start + ) + end else for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do self:Start( Scheduler, CallID ) -- Recursive @@ -198,7 +202,12 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) if CallID then local Schedule = self.Schedule[Scheduler] - timer.removeFunction( Schedule[CallID].ScheduleID ) + -- Only stop when there is a ScheduleID defined for the CallID. + -- So, when the scheduler was stopped before, do nothing. + if Schedule[CallID].ScheduleID then + timer.removeFunction( Schedule[CallID].ScheduleID ) + Schedule[CallID].ScheduleID = nil + end else for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do self:Stop( Scheduler, CallID ) -- Recursive @@ -206,5 +215,13 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) end end +function SCHEDULEDISPATCHER:Clear( Scheduler ) + self:F2( { Scheduler = Scheduler } ) + + for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do + self:Stop( Scheduler, CallID ) -- Recursive + end +end + diff --git a/Moose Development/Moose/Core/Scheduler.lua b/Moose Development/Moose/Core/Scheduler.lua index 1ade4a5de..2244d9b1b 100644 --- a/Moose Development/Moose/Core/Scheduler.lua +++ b/Moose Development/Moose/Core/Scheduler.lua @@ -1,5 +1,9 @@ ---- This module contains the SCHEDULER class. +--- **Core** - SCHEDULER prepares and handles the **execution of functions over scheduled time (intervals)**. -- +-- ![Banner Image](..\Presentations\SCHEDULER\Dia1.JPG) +-- +-- === +-- -- # 1) @{Scheduler#SCHEDULER} class, extends @{Base#BASE} -- -- The @{Scheduler#SCHEDULER} class creates schedule. @@ -147,6 +151,13 @@ function SCHEDULER:Remove( ScheduleID ) _SCHEDULEDISPATCHER:Remove( self, ScheduleID ) end +--- Clears all pending schedules. +-- @param #SCHEDULER self +function SCHEDULER:Clear() + self:F3( ) + + _SCHEDULEDISPATCHER:Clear( self ) +end diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index f12e3312f..bfb6ac69e 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -1,222 +1,27 @@ ---- **Core** - SET classes define **collections** of objects to perform **bulk actions** and logically **group** objects. +--- **Core** - SET_ classes define **collections** of objects to perform **bulk actions** and logically **group** objects. +-- +-- ![Banner Image](..\Presentations\SET\Dia1.JPG) -- -- === -- --- 1) @{Set#SET_BASE} class, extends @{Base#BASE} --- ============================================== --- The @{Set#SET_BASE} class defines the core functions that define a collection of objects. --- A SET provides iterators to iterate the SET, but will **temporarily** yield the ForEach interator loop at defined **"intervals"** to the mail simulator loop. --- In this way, large loops can be done while not blocking the simulator main processing loop. --- The default **"yield interval"** is after 10 objects processed. --- The default **"time interval"** is after 0.001 seconds. +-- SET_ classes group objects of the same type into a collection, which is either: -- --- 1.1) Add or remove objects from the SET --- --------------------------------------- --- Some key core functions are @{Set#SET_BASE.Add} and @{Set#SET_BASE.Remove} to add or remove objects from the SET in your logic. --- --- 1.2) Define the SET iterator **"yield interval"** and the **"time interval"** --- ----------------------------------------------------------------------------- --- Modify the iterator intervals with the @{Set#SET_BASE.SetInteratorIntervals} method. --- You can set the **"yield interval"**, and the **"time interval"**. (See above). --- --- === --- --- 2) @{Set#SET_GROUP} class, extends @{Set#SET_BASE} --- ================================================== --- Mission designers can use the @{Set#SET_GROUP} class to build sets of groups belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Starting with certain prefix strings. --- --- 2.1) SET_GROUP construction method: --- ----------------------------------- --- Create a new SET_GROUP object with the @{#SET_GROUP.New} method: --- --- * @{#SET_GROUP.New}: Creates a new SET_GROUP object. --- --- 2.2) Add or Remove GROUP(s) from SET_GROUP: --- ------------------------------------------- --- GROUPS can be added and removed using the @{Set#SET_GROUP.AddGroupsByName} and @{Set#SET_GROUP.RemoveGroupsByName} respectively. --- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP. --- --- 2.3) SET_GROUP filter criteria: --- ------------------------------- --- You can set filter criteria to define the set of groups within the SET_GROUP. --- Filter criteria are defined by: --- --- * @{#SET_GROUP.FilterCoalitions}: Builds the SET_GROUP with the groups belonging to the coalition(s). --- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies). --- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the gruops belonging to the country(ies). --- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups starting with the same prefix string(s). +-- * Manually managed using the **:Add...()** or **:Remove...()** methods. The initial SET can be filtered with the **@{#SET_BASE.FilterOnce}()** method +-- * Dynamically updated when new objects are created or objects are destroyed using the **@{#SET_BASE.FilterStart}()** method. -- --- Once the filter criteria have been set for the SET_GROUP, you can start filtering using: +-- Various types of SET_ classes are available: -- --- * @{#SET_GROUP.FilterStart}: Starts the filtering of the groups within the SET_GROUP and add or remove GROUP objects **dynamically**. +-- * @{#SET_UNIT}: Defines a colleciton of @{Unit}s filtered by filter criteria. +-- * @{#SET_GROUP}: Defines a collection of @{Group}s filtered by filter criteria. +-- * @{#SET_CLIENT}: Defines a collection of @{Client}s filterd by filter criteria. +-- * @{#SET_AIRBASE}: Defines a collection of @{Airbase}s filtered by filter criteria. -- --- Planned filter criteria within development are (so these are not yet available): +-- These classes are derived from @{#SET_BASE}, which contains the main methods to manage SETs. -- --- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Zone#ZONE}. +-- A multitude of other methods are available in SET_ classes that allow to: -- --- 2.4) SET_GROUP iterators: --- ------------------------- --- Once the filters have been defined and the SET_GROUP has been built, you can iterate the SET_GROUP with the available iterator methods. --- The iterator methods will walk the SET_GROUP set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_GROUP: --- --- * @{#SET_GROUP.ForEachGroup}: Calls a function for each alive group it finds within the SET_GROUP. --- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupPartlyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. --- --- ==== --- --- 3) @{Set#SET_UNIT} class, extends @{Set#SET_BASE} --- =================================================== --- Mission designers can use the @{Set#SET_UNIT} class to build sets of units belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Unit types --- * Starting with certain prefix strings. --- --- 3.1) SET_UNIT construction method: --- ---------------------------------- --- Create a new SET_UNIT object with the @{#SET_UNIT.New} method: --- --- * @{#SET_UNIT.New}: Creates a new SET_UNIT object. --- --- 3.2) Add or Remove UNIT(s) from SET_UNIT: --- ----------------------------------------- --- UNITs can be added and removed using the @{Set#SET_UNIT.AddUnitsByName} and @{Set#SET_UNIT.RemoveUnitsByName} respectively. --- These methods take a single UNIT name or an array of UNIT names to be added or removed from SET_UNIT. --- --- 3.3) SET_UNIT filter criteria: --- ------------------------------ --- You can set filter criteria to define the set of units within the SET_UNIT. --- Filter criteria are defined by: --- --- * @{#SET_UNIT.FilterCoalitions}: Builds the SET_UNIT with the units belonging to the coalition(s). --- * @{#SET_UNIT.FilterCategories}: Builds the SET_UNIT with the units belonging to the category(ies). --- * @{#SET_UNIT.FilterTypes}: Builds the SET_UNIT with the units belonging to the unit type(s). --- * @{#SET_UNIT.FilterCountries}: Builds the SET_UNIT with the units belonging to the country(ies). --- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units starting with the same prefix string(s). --- --- Once the filter criteria have been set for the SET_UNIT, you can start filtering using: --- --- * @{#SET_UNIT.FilterStart}: Starts the filtering of the units within the SET_UNIT. --- --- Planned filter criteria within development are (so these are not yet available): --- --- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Zone#ZONE}. --- --- 3.4) SET_UNIT iterators: --- ------------------------ --- Once the filters have been defined and the SET_UNIT has been built, you can iterate the SET_UNIT with the available iterator methods. --- The iterator methods will walk the SET_UNIT set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_UNIT: --- --- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive unit it finds within the SET_UNIT. --- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. --- --- Planned iterators methods in development are (so these are not yet available): --- --- * @{#SET_UNIT.ForEachUnitInUnit}: Calls a function for each unit contained within the SET_UNIT. --- * @{#SET_UNIT.ForEachUnitCompletelyInZone}: Iterate and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function. --- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate and call an iterator function for each **alive** UNIT presence not in a @{Zone}, providing the UNIT and optional parameters to the called function. --- --- === --- --- 4) @{Set#SET_CLIENT} class, extends @{Set#SET_BASE} --- =================================================== --- Mission designers can use the @{Set#SET_CLIENT} class to build sets of units belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Client types --- * Starting with certain prefix strings. --- --- 4.1) SET_CLIENT construction method: --- ---------------------------------- --- Create a new SET_CLIENT object with the @{#SET_CLIENT.New} method: --- --- * @{#SET_CLIENT.New}: Creates a new SET_CLIENT object. --- --- 4.2) Add or Remove CLIENT(s) from SET_CLIENT: --- ----------------------------------------- --- CLIENTs can be added and removed using the @{Set#SET_CLIENT.AddClientsByName} and @{Set#SET_CLIENT.RemoveClientsByName} respectively. --- These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT. --- --- 4.3) SET_CLIENT filter criteria: --- ------------------------------ --- You can set filter criteria to define the set of clients within the SET_CLIENT. --- Filter criteria are defined by: --- --- * @{#SET_CLIENT.FilterCoalitions}: Builds the SET_CLIENT with the clients belonging to the coalition(s). --- * @{#SET_CLIENT.FilterCategories}: Builds the SET_CLIENT with the clients belonging to the category(ies). --- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s). --- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies). --- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients starting with the same prefix string(s). --- --- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using: --- --- * @{#SET_CLIENT.FilterStart}: Starts the filtering of the clients within the SET_CLIENT. --- --- Planned filter criteria within development are (so these are not yet available): --- --- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Zone#ZONE}. --- --- 4.4) SET_CLIENT iterators: --- ------------------------ --- Once the filters have been defined and the SET_CLIENT has been built, you can iterate the SET_CLIENT with the available iterator methods. --- The iterator methods will walk the SET_CLIENT set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_CLIENT: --- --- * @{#SET_CLIENT.ForEachClient}: Calls a function for each alive client it finds within the SET_CLIENT. --- --- ==== --- --- 5) @{Set#SET_AIRBASE} class, extends @{Set#SET_BASE} --- ==================================================== --- Mission designers can use the @{Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: --- --- * Coalitions --- --- 5.1) SET_AIRBASE construction --- ----------------------------- --- Create a new SET_AIRBASE object with the @{#SET_AIRBASE.New} method: --- --- * @{#SET_AIRBASE.New}: Creates a new SET_AIRBASE object. --- --- 5.2) Add or Remove AIRBASEs from SET_AIRBASE --- -------------------------------------------- --- AIRBASEs can be added and removed using the @{Set#SET_AIRBASE.AddAirbasesByName} and @{Set#SET_AIRBASE.RemoveAirbasesByName} respectively. --- These methods take a single AIRBASE name or an array of AIRBASE names to be added or removed from SET_AIRBASE. --- --- 5.3) SET_AIRBASE filter criteria --- -------------------------------- --- You can set filter criteria to define the set of clients within the SET_AIRBASE. --- Filter criteria are defined by: --- --- * @{#SET_AIRBASE.FilterCoalitions}: Builds the SET_AIRBASE with the airbases belonging to the coalition(s). --- --- Once the filter criteria have been set for the SET_AIRBASE, you can start filtering using: --- --- * @{#SET_AIRBASE.FilterStart}: Starts the filtering of the airbases within the SET_AIRBASE. --- --- 5.4) SET_AIRBASE iterators: --- --------------------------- --- Once the filters have been defined and the SET_AIRBASE has been built, you can iterate the SET_AIRBASE with the available iterator methods. --- The iterator methods will walk the SET_AIRBASE set, and call for each airbase within the set a function that you provide. --- The following iterator methods are currently available within the SET_AIRBASE: --- --- * @{#SET_AIRBASE.ForEachAirbase}: Calls a function for each airbase it finds within the SET_AIRBASE. --- --- ==== +-- * Validate the presence of objects in the SET. +-- * Trigger events when objects in the SET change a zone presence. -- -- ### Authors: -- @@ -228,20 +33,40 @@ -- @module Set ---- SET_BASE class --- @type SET_BASE +--- @type SET_BASE -- @field #table Filter -- @field #table Set -- @field #table List -- @field Core.Scheduler#SCHEDULER CallScheduler -- @extends Core.Base#BASE + + +--- # 1) SET_BASE class, extends @{Base#BASE} +-- The @{Set#SET_BASE} class defines the core functions that define a collection of objects. +-- A SET provides iterators to iterate the SET, but will **temporarily** yield the ForEach interator loop at defined **"intervals"** to the mail simulator loop. +-- In this way, large loops can be done while not blocking the simulator main processing loop. +-- The default **"yield interval"** is after 10 objects processed. +-- The default **"time interval"** is after 0.001 seconds. +-- +-- ## 1.1) Add or remove objects from the SET +-- +-- Some key core functions are @{Set#SET_BASE.Add} and @{Set#SET_BASE.Remove} to add or remove objects from the SET in your logic. +-- +-- ## 1.2) Define the SET iterator **"yield interval"** and the **"time interval"** +-- +-- Modify the iterator intervals with the @{Set#SET_BASE.SetInteratorIntervals} method. +-- You can set the **"yield interval"**, and the **"time interval"**. (See above). +-- +-- @field #SET_BASE SET_BASE SET_BASE = { ClassName = "SET_BASE", Filter = {}, Set = {}, List = {}, + Index = {}, } + --- Creates a new SET_BASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. -- @param #SET_BASE self -- @return #SET_BASE @@ -258,10 +83,14 @@ function SET_BASE:New( Database ) self.YieldInterval = 10 self.TimeInterval = 0.001 + self.Set = {} + self.List = {} self.List.__index = self.List self.List = setmetatable( { Count = 0 }, self.List ) + self.Index = {} + self.CallScheduler = SCHEDULER:New( self ) self:SetEventPriority( 2 ) @@ -313,6 +142,8 @@ function SET_BASE:Add( ObjectName, Object ) self.Set[ObjectName] = t._ + table.insert( self.Index, ObjectName ) + end --- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using the Object Name as the index. @@ -364,7 +195,15 @@ function SET_BASE:Remove( ObjectName ) t._prev = nil self.List.Count = self.List.Count - 1 + for Index, Key in ipairs( self.Index ) do + if Key == ObjectName then + table.remove( self.Index, Index ) + break + end + end + self.Set[ObjectName] = nil + end end @@ -384,12 +223,50 @@ function SET_BASE:Get( ObjectName ) end +--- Gets the first object from the @{Set#SET_BASE} and derived classes. +-- @param #SET_BASE self +-- @return Core.Base#BASE +function SET_BASE:GetFirst() + self:F() + + local ObjectName = self.Index[1] + local FirstObject = self.Set[ObjectName] + self:T3( { FirstObject } ) + return FirstObject +end + +--- Gets the last object from the @{Set#SET_BASE} and derived classes. +-- @param #SET_BASE self +-- @return Core.Base#BASE +function SET_BASE:GetLast() + self:F() + + local ObjectName = self.Index[#self.Index] + local LastObject = self.Set[ObjectName] + self:T3( { LastObject } ) + return LastObject +end + +--- Gets a random object from the @{Set#SET_BASE} and derived classes. +-- @param #SET_BASE self +-- @return Core.Base#BASE +function SET_BASE:GetRandom() + self:F() + + local RandomItem = self.Set[self.Index[math.random(#self.Index)]] + + self:T3( { RandomItem } ) + + return RandomItem +end + + --- Retrieves the amount of objects in the @{Set#SET_BASE} and derived classes. -- @param #SET_BASE self -- @return #number Count function SET_BASE:Count() - return self.List.Count + return #self.Index or 0 end @@ -652,7 +529,8 @@ function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArgumen return false end - self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + --self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + Schedule() return self end @@ -721,11 +599,61 @@ function SET_BASE:Flush() return ObjectNames end --- SET_GROUP ---- SET_GROUP class --- @type SET_GROUP --- @extends #SET_BASE +--- @type SET_GROUP +-- @extends Core.Set#SET_BASE + +--- # 2) SET_GROUP class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the @{Set#SET_GROUP} class to build sets of groups belonging to certain: +-- +-- * Coalitions +-- * Categories +-- * Countries +-- * Starting with certain prefix strings. +-- +-- ## 2.1) SET_GROUP constructor +-- +-- Create a new SET_GROUP object with the @{#SET_GROUP.New} method: +-- +-- * @{#SET_GROUP.New}: Creates a new SET_GROUP object. +-- +-- ## 2.2) Add or Remove GROUP(s) from SET_GROUP +-- +-- GROUPS can be added and removed using the @{Set#SET_GROUP.AddGroupsByName} and @{Set#SET_GROUP.RemoveGroupsByName} respectively. +-- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP. +-- +-- ## 2.3) SET_GROUP filter criteria +-- +-- You can set filter criteria to define the set of groups within the SET_GROUP. +-- Filter criteria are defined by: +-- +-- * @{#SET_GROUP.FilterCoalitions}: Builds the SET_GROUP with the groups belonging to the coalition(s). +-- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies). +-- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the gruops belonging to the country(ies). +-- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups starting with the same prefix string(s). +-- +-- Once the filter criteria have been set for the SET_GROUP, you can start filtering using: +-- +-- * @{#SET_GROUP.FilterStart}: Starts the filtering of the groups within the SET_GROUP and add or remove GROUP objects **dynamically**. +-- +-- Planned filter criteria within development are (so these are not yet available): +-- +-- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Zone#ZONE}. +-- +-- ## 2.4) SET_GROUP iterators +-- +-- Once the filters have been defined and the SET_GROUP has been built, you can iterate the SET_GROUP with the available iterator methods. +-- The iterator methods will walk the SET_GROUP set, and call for each element within the set a function that you provide. +-- The following iterator methods are currently available within the SET_GROUP: +-- +-- * @{#SET_GROUP.ForEachGroup}: Calls a function for each alive group it finds within the SET_GROUP. +-- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- * @{#SET_GROUP.ForEachGroupPartlyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- +-- === +-- @field #SET_GROUP SET_GROUP SET_GROUP = { ClassName = "SET_GROUP", Filter = { @@ -1091,9 +1019,73 @@ function SET_GROUP:IsIncludeObject( MooseGroup ) return MooseGroupInclude end ---- SET_UNIT class --- @type SET_UNIT +--- @type SET_UNIT -- @extends Core.Set#SET_BASE + +--- # 3) SET_UNIT class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the SET_UNIT class to build sets of units belonging to certain: +-- +-- * Coalitions +-- * Categories +-- * Countries +-- * Unit types +-- * Starting with certain prefix strings. +-- +-- ## 3.1) SET_UNIT constructor +-- +-- Create a new SET_UNIT object with the @{#SET_UNIT.New} method: +-- +-- * @{#SET_UNIT.New}: Creates a new SET_UNIT object. +-- +-- ## 3.2) Add or Remove UNIT(s) from SET_UNIT +-- +-- UNITs can be added and removed using the @{Set#SET_UNIT.AddUnitsByName} and @{Set#SET_UNIT.RemoveUnitsByName} respectively. +-- These methods take a single UNIT name or an array of UNIT names to be added or removed from SET_UNIT. +-- +-- ## 3.3) SET_UNIT filter criteria +-- +-- You can set filter criteria to define the set of units within the SET_UNIT. +-- Filter criteria are defined by: +-- +-- * @{#SET_UNIT.FilterCoalitions}: Builds the SET_UNIT with the units belonging to the coalition(s). +-- * @{#SET_UNIT.FilterCategories}: Builds the SET_UNIT with the units belonging to the category(ies). +-- * @{#SET_UNIT.FilterTypes}: Builds the SET_UNIT with the units belonging to the unit type(s). +-- * @{#SET_UNIT.FilterCountries}: Builds the SET_UNIT with the units belonging to the country(ies). +-- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units starting with the same prefix string(s). +-- +-- Once the filter criteria have been set for the SET_UNIT, you can start filtering using: +-- +-- * @{#SET_UNIT.FilterStart}: Starts the filtering of the units within the SET_UNIT. +-- +-- Planned filter criteria within development are (so these are not yet available): +-- +-- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Zone#ZONE}. +-- +-- ## 3.4) SET_UNIT iterators +-- +-- Once the filters have been defined and the SET_UNIT has been built, you can iterate the SET_UNIT with the available iterator methods. +-- The iterator methods will walk the SET_UNIT set, and call for each element within the set a function that you provide. +-- The following iterator methods are currently available within the SET_UNIT: +-- +-- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive unit it finds within the SET_UNIT. +-- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- +-- Planned iterators methods in development are (so these are not yet available): +-- +-- * @{#SET_UNIT.ForEachUnitInUnit}: Calls a function for each unit contained within the SET_UNIT. +-- * @{#SET_UNIT.ForEachUnitCompletelyInZone}: Iterate and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function. +-- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate and call an iterator function for each **alive** UNIT presence not in a @{Zone}, providing the UNIT and optional parameters to the called function. +-- +-- ## 3.5 ) SET_UNIT atomic methods +-- +-- Various methods exist for a SET_UNIT to perform actions or calculations and retrieve results from the SET_UNIT: +-- +-- * @{#SET_UNIT.GetTypeNames}(): Retrieve the type names of the @{Unit}s in the SET, delimited by a comma. +-- +-- === +-- @field #SET_UNIT SET_UNIT SET_UNIT = { ClassName = "SET_UNIT", Units = {}, @@ -1705,11 +1697,89 @@ function SET_UNIT:IsIncludeObject( MUnit ) end +--- Retrieve the type names of the @{Unit}s in the SET, delimited by an optional delimiter. +-- @param #SET_UNIT self +-- @param #string Delimiter (optional) The delimiter, which is default a comma. +-- @return #string The types of the @{Unit}s delimited. +function SET_UNIT:GetTypeNames( Delimiter ) + + Delimiter = Delimiter or ", " + local TypeReport = REPORT:New() + local Types = {} + + for UnitName, UnitData in pairs( self:GetSet() ) do + + local Unit = UnitData -- Wrapper.Unit#UNIT + local UnitTypeName = Unit:GetTypeName() + + if not Types[UnitTypeName] then + Types[UnitTypeName] = UnitTypeName + TypeReport:Add( UnitTypeName ) + end + end + + return TypeReport:Text( Delimiter ) +end + + --- SET_CLIENT ---- SET_CLIENT class --- @type SET_CLIENT + +--- @type SET_CLIENT -- @extends Core.Set#SET_BASE + + + +--- # 4) SET_CLIENT class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the @{Set#SET_CLIENT} class to build sets of units belonging to certain: +-- +-- * Coalitions +-- * Categories +-- * Countries +-- * Client types +-- * Starting with certain prefix strings. +-- +-- ## 4.1) SET_CLIENT constructor +-- +-- Create a new SET_CLIENT object with the @{#SET_CLIENT.New} method: +-- +-- * @{#SET_CLIENT.New}: Creates a new SET_CLIENT object. +-- +-- ## 4.2) Add or Remove CLIENT(s) from SET_CLIENT +-- +-- CLIENTs can be added and removed using the @{Set#SET_CLIENT.AddClientsByName} and @{Set#SET_CLIENT.RemoveClientsByName} respectively. +-- These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT. +-- +-- ## 4.3) SET_CLIENT filter criteria +-- +-- You can set filter criteria to define the set of clients within the SET_CLIENT. +-- Filter criteria are defined by: +-- +-- * @{#SET_CLIENT.FilterCoalitions}: Builds the SET_CLIENT with the clients belonging to the coalition(s). +-- * @{#SET_CLIENT.FilterCategories}: Builds the SET_CLIENT with the clients belonging to the category(ies). +-- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s). +-- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies). +-- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients starting with the same prefix string(s). +-- +-- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using: +-- +-- * @{#SET_CLIENT.FilterStart}: Starts the filtering of the clients within the SET_CLIENT. +-- +-- Planned filter criteria within development are (so these are not yet available): +-- +-- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Zone#ZONE}. +-- +-- ## 4.4) SET_CLIENT iterators +-- +-- Once the filters have been defined and the SET_CLIENT has been built, you can iterate the SET_CLIENT with the available iterator methods. +-- The iterator methods will walk the SET_CLIENT set, and call for each element within the set a function that you provide. +-- The following iterator methods are currently available within the SET_CLIENT: +-- +-- * @{#SET_CLIENT.ForEachClient}: Calls a function for each alive client it finds within the SET_CLIENT. +-- +-- === +-- @field #SET_CLIENT SET_CLIENT SET_CLIENT = { ClassName = "SET_CLIENT", Clients = {}, @@ -2062,11 +2132,47 @@ function SET_CLIENT:IsIncludeObject( MClient ) return MClientInclude end ---- SET_AIRBASE - ---- SET_AIRBASE class --- @type SET_AIRBASE +--- @type SET_AIRBASE -- @extends Core.Set#SET_BASE + +--- # 5) SET_AIRBASE class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the @{Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: +-- +-- * Coalitions +-- +-- ## 5.1) SET_AIRBASE constructor +-- +-- Create a new SET_AIRBASE object with the @{#SET_AIRBASE.New} method: +-- +-- * @{#SET_AIRBASE.New}: Creates a new SET_AIRBASE object. +-- +-- ## 5.2) Add or Remove AIRBASEs from SET_AIRBASE +-- +-- AIRBASEs can be added and removed using the @{Set#SET_AIRBASE.AddAirbasesByName} and @{Set#SET_AIRBASE.RemoveAirbasesByName} respectively. +-- These methods take a single AIRBASE name or an array of AIRBASE names to be added or removed from SET_AIRBASE. +-- +-- ## 5.3) SET_AIRBASE filter criteria +-- +-- You can set filter criteria to define the set of clients within the SET_AIRBASE. +-- Filter criteria are defined by: +-- +-- * @{#SET_AIRBASE.FilterCoalitions}: Builds the SET_AIRBASE with the airbases belonging to the coalition(s). +-- +-- Once the filter criteria have been set for the SET_AIRBASE, you can start filtering using: +-- +-- * @{#SET_AIRBASE.FilterStart}: Starts the filtering of the airbases within the SET_AIRBASE. +-- +-- ## 5.4) SET_AIRBASE iterators +-- +-- Once the filters have been defined and the SET_AIRBASE has been built, you can iterate the SET_AIRBASE with the available iterator methods. +-- The iterator methods will walk the SET_AIRBASE set, and call for each airbase within the set a function that you provide. +-- The following iterator methods are currently available within the SET_AIRBASE: +-- +-- * @{#SET_AIRBASE.ForEachAirbase}: Calls a function for each airbase it finds within the SET_AIRBASE. +-- +-- === +-- @field #SET_AIRBASE SET_AIRBASE SET_AIRBASE = { ClassName = "SET_AIRBASE", Airbases = {}, diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 4eb05b047..432a360a7 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1,5 +1,7 @@ --- **Core** - ZONE classes define **zones** within your mission of **various forms**, with **various capabilities**. -- +-- ![Banner Image](..\Presentations\ZONE\Dia1.JPG) +-- -- === -- -- There are essentially two core functions that zones accomodate: @@ -18,127 +20,16 @@ -- -- Each of these ZONE classes have a zone name, and specific parameters defining the zone type: -- --- * @{Zone#ZONE_BASE}: The ZONE_BASE class defining the base for all other zone classes. --- * @{Zone#ZONE_RADIUS}: The ZONE_RADIUS class defined by a zone name, a location and a radius. --- * @{Zone#ZONE}: The ZONE class, defined by the zone name as defined within the Mission Editor. --- * @{Zone#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Unit#UNIT} with a radius. --- * @{Zone#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. --- * @{Zone#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- * @{#ZONE_BASE}: The ZONE_BASE class defining the base for all other zone classes. +-- * @{#ZONE_RADIUS}: The ZONE_RADIUS class defined by a zone name, a location and a radius. +-- * @{#ZONE}: The ZONE class, defined by the zone name as defined within the Mission Editor. +-- * @{#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Unit#UNIT} with a radius. +-- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. +-- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- +-- === -- --- === --- --- # 1) @{Zone#ZONE_BASE} class, extends @{Base#BASE} --- --- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. --- --- ## 1.1) Each zone has a name: --- --- * @{#ZONE_BASE.GetName}(): Returns the name of the zone. --- --- ## 1.2) Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}: --- --- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a Vec2 is within the zone. --- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a Vec3 is within the zone. --- --- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones: --- --- * @{#ZONE_BASE.SetRandomizeProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% ) --- * @{#ZONE_BASE.GetRandomizeProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% ) --- * @{#ZONE_BASE.GetZoneMaybe}(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate. --- --- ## 1.4) A zone manages Vectors: --- --- * @{#ZONE_BASE.GetVec2}(): Returns the @{DCSTypes#Vec2} coordinate of the zone. --- * @{#ZONE_BASE.GetRandomVec2}(): Define a random @{DCSTypes#Vec2} within the zone. --- --- ## 1.5) A zone has a bounding square: --- --- * @{#ZONE_BASE.GetBoundingSquare}(): Get the outer most bounding square of the zone. --- --- ## 1.6) A zone can be marked: --- --- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color. --- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color. --- --- === --- --- # 2) @{Zone#ZONE_RADIUS} class, extends @{Zone#ZONE_BASE} --- --- The ZONE_RADIUS class defined by a zone name, a location and a radius. --- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties. --- --- ## 2.1) @{Zone#ZONE_RADIUS} constructor --- --- * @{#ZONE_RADIUS.New}(): Constructor. --- --- ## 2.2) Manage the radius of the zone --- --- * @{#ZONE_RADIUS.SetRadius}(): Sets the radius of the zone. --- * @{#ZONE_RADIUS.GetRadius}(): Returns the radius of the zone. --- --- ## 2.3) Manage the location of the zone --- --- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCSTypes#Vec2} of the zone. --- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCSTypes#Vec2} of the zone. --- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCSTypes#Vec3} of the zone, taking an additional height parameter. --- --- ## 2.4) Zone point randomization --- --- Various functions exist to find random points within the zone. --- --- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone. --- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Point#POINT_VEC2} object representing a random 2D point in the zone. --- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight. --- --- === --- --- # 3) @{Zone#ZONE} class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE class, defined by the zone name as defined within the Mission Editor. --- This class implements the inherited functions from {Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- # 4) @{Zone#ZONE_UNIT} class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- # 5) @{Zone#ZONE_GROUP} class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. The current leader of the group defines the center of the zone. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- # 6) @{Zone#ZONE_POLYGON_BASE} class, extends @{Zone#ZONE_BASE} --- --- The ZONE_POLYGON_BASE class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. --- --- ## 6.1) Zone point randomization --- --- Various functions exist to find random points within the zone. --- --- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone. --- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Point#POINT_VEC2} object representing a random 2D point within the zone. --- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. --- --- --- === --- --- # 7) @{Zone#ZONE_POLYGON} class, extends @{Zone#ZONE_POLYGON_BASE} --- --- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- ==== --- --- **API CHANGE HISTORY** --- ====================== +-- # **API CHANGE HISTORY** -- -- The underlying change log documents the API changes. Please read this carefully. The following notation is used: -- @@ -180,6 +71,43 @@ -- @field #string ZoneName Name of the zone. -- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. -- @extends Core.Base#BASE + + +--- # 1) ZONE_BASE class, extends @{Base#BASE} +-- +-- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. +-- +-- ## 1.1) Each zone has a name: +-- +-- * @{#ZONE_BASE.GetName}(): Returns the name of the zone. +-- +-- ## 1.2) Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}: +-- +-- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a Vec2 is within the zone. +-- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a Vec3 is within the zone. +-- +-- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones: +-- +-- * @{#ZONE_BASE.SetRandomizeProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% ) +-- * @{#ZONE_BASE.GetRandomizeProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% ) +-- * @{#ZONE_BASE.GetZoneMaybe}(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate. +-- +-- ## 1.4) A zone manages Vectors: +-- +-- * @{#ZONE_BASE.GetVec2}(): Returns the @{DCSTypes#Vec2} coordinate of the zone. +-- * @{#ZONE_BASE.GetRandomVec2}(): Define a random @{DCSTypes#Vec2} within the zone. +-- +-- ## 1.5) A zone has a bounding square: +-- +-- * @{#ZONE_BASE.GetBoundingSquare}(): Get the outer most bounding square of the zone. +-- +-- ## 1.6) A zone can be marked: +-- +-- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color. +-- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color. +-- +-- === +-- @field #ZONE_BASE ZONE_BASE ZONE_BASE = { ClassName = "ZONE_BASE", ZoneName = "", @@ -247,6 +175,58 @@ function ZONE_BASE:GetVec2() return nil end +--- Returns a @{Point#POINT_VEC2} of the zone. +-- @param #ZONE_BASE self +-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. +-- @return Core.Point#POINT_VEC2 The PointVec2 of the zone. +function ZONE_BASE:GetPointVec2() + self:F2( self.ZoneName ) + + local Vec2 = self:GetVec2() + + local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 ) + + self:T2( { PointVec2 } ) + + return PointVec2 +end + + +--- Returns the @{DCSTypes#Vec3} of the zone. +-- @param #ZONE_BASE self +-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. +-- @return Dcs.DCSTypes#Vec3 The Vec3 of the zone. +function ZONE_BASE:GetVec3( Height ) + self:F2( self.ZoneName ) + + Height = Height or 0 + + local Vec2 = self:GetVec2() + + local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y } + + self:T2( { Vec3 } ) + + return Vec3 +end + +--- Returns a @{Point#POINT_VEC3} of the zone. +-- @param #ZONE_BASE self +-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. +-- @return Core.Point#POINT_VEC3 The PointVec3 of the zone. +function ZONE_BASE:GetPointVec3( Height ) + self:F2( self.ZoneName ) + + local Vec3 = self:GetVec3( Height ) + + local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) + + self:T2( { PointVec3 } ) + + return PointVec3 +end + + --- Define a random @{DCSTypes#Vec2} within the zone. -- @param #ZONE_BASE self -- @return Dcs.DCSTypes#Vec2 The Vec2 coordinates. @@ -261,6 +241,13 @@ function ZONE_BASE:GetRandomPointVec2() return nil end +--- Define a random @{Point#POINT_VEC3} within the zone. +-- @param #ZONE_BASE self +-- @return Core.Point#POINT_VEC3 The PointVec3 coordinates. +function ZONE_BASE:GetRandomPointVec3() + return nil +end + --- Get the bounding square the zone. -- @param #ZONE_BASE self -- @return #nil The bounding square. @@ -324,6 +311,39 @@ end -- @field Dcs.DCSTypes#Vec2 Vec2 The current location of the zone. -- @field Dcs.DCSTypes#Distance Radius The radius of the zone. -- @extends Core.Zone#ZONE_BASE + +--- # 2) @{Zone#ZONE_RADIUS} class, extends @{Zone#ZONE_BASE} +-- +-- The ZONE_RADIUS class defined by a zone name, a location and a radius. +-- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties. +-- +-- ## 2.1) @{Zone#ZONE_RADIUS} constructor +-- +-- * @{#ZONE_RADIUS.New}(): Constructor. +-- +-- ## 2.2) Manage the radius of the zone +-- +-- * @{#ZONE_RADIUS.SetRadius}(): Sets the radius of the zone. +-- * @{#ZONE_RADIUS.GetRadius}(): Returns the radius of the zone. +-- +-- ## 2.3) Manage the location of the zone +-- +-- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCSTypes#Vec2} of the zone. +-- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCSTypes#Vec2} of the zone. +-- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCSTypes#Vec3} of the zone, taking an additional height parameter. +-- +-- ## 2.4) Zone point randomization +-- +-- Various functions exist to find random points within the zone. +-- +-- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone. +-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Point#POINT_VEC2} object representing a random 2D point in the zone. +-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight. +-- +-- === +-- +-- @field #ZONE_RADIUS ZONE_RADIUS +-- ZONE_RADIUS = { ClassName="ZONE_RADIUS", } @@ -347,8 +367,9 @@ end --- Bounds the zone with tires. -- @param #ZONE_RADIUS self -- @param #number Points (optional) The amount of points in the circle. +-- @param #boolean UnBound If true the tyres will be destroyed. -- @return #ZONE_RADIUS self -function ZONE_RADIUS:BoundZone( Points ) +function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound ) local Point = {} local Vec2 = self:GetVec2() @@ -364,8 +385,10 @@ function ZONE_RADIUS:BoundZone( Points ) Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() + local CountryName = _DATABASE.COUNTRY_NAME[CountryID] + local Tire = { - ["country"] = "USA", + ["country"] = CountryName, ["category"] = "Fortifications", ["canCargo"] = false, ["shape_name"] = "H-tyre_B_WF", @@ -377,7 +400,10 @@ function ZONE_RADIUS:BoundZone( Points ) ["heading"] = 0, } -- end of ["group"] - coalition.addStaticObject( country.id.USA, Tire ) + local Group = coalition.addStaticObject( CountryID, Tire ) + if UnBound and UnBound == true then + Group:destroy() + end end return self @@ -589,9 +615,19 @@ end ---- The ZONE class, defined by the zone name as defined within the Mission Editor. The location and the radius are automatically collected from the mission settings. -- @type ZONE -- @extends Core.Zone#ZONE_RADIUS + + +--- # 3) ZONE class, extends @{Zone#ZONE_RADIUS} +-- +-- The ZONE class, defined by the zone name as defined within the Mission Editor. +-- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. +-- +-- === +-- +-- @field #ZONE ZONE +-- ZONE = { ClassName="ZONE", } @@ -623,6 +659,16 @@ end -- @type ZONE_UNIT -- @field Wrapper.Unit#UNIT ZoneUNIT -- @extends Core.Zone#ZONE_RADIUS + +--- # 4) #ZONE_UNIT class, extends @{Zone#ZONE_RADIUS} +-- +-- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. +-- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. +-- +-- === +-- +-- @field #ZONE_UNIT ZONE_UNIT +-- ZONE_UNIT = { ClassName="ZONE_UNIT", } @@ -703,10 +749,20 @@ function ZONE_UNIT:GetVec3( Height ) return Vec3 end ---- The ZONE_GROUP class defined by a zone around a @{Group}, taking the average center point of all the units within the Group, with a radius. --- @type ZONE_GROUP +--- @type ZONE_GROUP -- @field Wrapper.Group#GROUP ZoneGROUP -- @extends Core.Zone#ZONE_RADIUS + + +--- # 5) #ZONE_GROUP class, extends @{Zone#ZONE_RADIUS} +-- +-- The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. The current leader of the group defines the center of the zone. +-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. +-- +-- === +-- +-- @field #ZONE_GROUP ZONE_GROUP +-- ZONE_GROUP = { ClassName="ZONE_GROUP", } @@ -760,12 +816,29 @@ end --- Polygons - ---- The ZONE_POLYGON_BASE class defined by an array of @{DCSTypes#Vec2}, forming a polygon. --- @type ZONE_POLYGON_BASE +--- @type ZONE_POLYGON_BASE -- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCSTypes#Vec2}. -- @extends Core.Zone#ZONE_BASE + + +--- # 6) ZONE_POLYGON_BASE class, extends @{Zone#ZONE_BASE} +-- +-- The ZONE_POLYGON_BASE class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. +-- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. +-- +-- ## 6.1) Zone point randomization +-- +-- Various functions exist to find random points within the zone. +-- +-- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone. +-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Point#POINT_VEC2} object representing a random 2D point within the zone. +-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. +-- +-- === +-- +-- @field #ZONE_POLYGON_BASE ZONE_POLYGON_BASE +-- ZONE_POLYGON_BASE = { ClassName="ZONE_POLYGON_BASE", } @@ -810,8 +883,9 @@ end --- Smokes the zone boundaries in a color. -- @param #ZONE_POLYGON_BASE self +-- @param #boolean UnBound If true, the tyres will be destroyed. -- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:BoundZone( ) +function ZONE_POLYGON_BASE:BoundZone( UnBound ) local i local j @@ -840,8 +914,11 @@ function ZONE_POLYGON_BASE:BoundZone( ) ["name"] = string.format( "%s-Tire #%0d", self:GetName(), ((i - 1) * Segments) + Segment ), ["heading"] = 0, } -- end of ["group"] - - coalition.addStaticObject( country.id.USA, Tire ) + + local Group = coalition.addStaticObject( country.id.USA, Tire ) + if UnBound and UnBound == true then + Group:destroy() + end end j = i @@ -995,12 +1072,19 @@ function ZONE_POLYGON_BASE:GetBoundingSquare() end - - - ---- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- @type ZONE_POLYGON +--- @type ZONE_POLYGON -- @extends Core.Zone#ZONE_POLYGON_BASE + + +--- # 7) ZONE_POLYGON class, extends @{Zone#ZONE_POLYGON_BASE} +-- +-- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. +-- +-- === +-- +-- @field #ZONE_POLYGON ZONE_POLYGON +-- ZONE_POLYGON = { ClassName="ZONE_POLYGON", } diff --git a/Moose Development/Moose/Functional/CleanUp.lua b/Moose Development/Moose/Functional/CleanUp.lua index feb6a6d55..25c8742db 100644 --- a/Moose Development/Moose/Functional/CleanUp.lua +++ b/Moose Development/Moose/Functional/CleanUp.lua @@ -29,7 +29,9 @@ CLEANUP = { -- or -- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 ) -- CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 ) -function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self, BASE:New() ) +function CLEANUP:New( ZoneNames, TimeInterval ) + + local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP self:F( { ZoneNames, TimeInterval } ) if type( ZoneNames ) == 'table' then @@ -41,7 +43,7 @@ function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self, self.TimeInterval = TimeInterval end - _EVENTDISPATCHER:OnBirth( self._OnEventBirth, self ) + self:HandleEvent( EVENTS.Birth ) self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval ) @@ -102,32 +104,24 @@ function CLEANUP:_DestroyMissile( MissileObject ) end end -function CLEANUP:_OnEventBirth( Event ) - self:F( { Event } ) +--- @param #CLEANUP self +-- @param Core.Event#EVENTDATA EventData +function CLEANUP:_OnEventBirth( EventData ) + self:F( { EventData } ) - self.CleanUpList[Event.IniDCSUnitName] = {} - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName - - _EVENTDISPATCHER:OnEngineShutDownForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) - _EVENTDISPATCHER:OnEngineStartUpForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) - _EVENTDISPATCHER:OnHitForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) - _EVENTDISPATCHER:OnPilotDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self ) - _EVENTDISPATCHER:OnDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self ) - _EVENTDISPATCHER:OnCrashForUnit( Event.IniDCSUnitName, self._EventCrash, self ) - _EVENTDISPATCHER:OnShotForUnit( Event.IniDCSUnitName, self._EventShot, self ) - - --self:AddEvent( world.event.S_EVENT_ENGINE_SHUTDOWN, self._EventAddForCleanUp ) - --self:AddEvent( world.event.S_EVENT_ENGINE_STARTUP, self._EventAddForCleanUp ) --- self:AddEvent( world.event.S_EVENT_HIT, self._EventAddForCleanUp ) -- , self._EventHitCleanUp ) --- self:AddEvent( world.event.S_EVENT_CRASH, self._EventCrash ) -- , self._EventHitCleanUp ) --- --self:AddEvent( world.event.S_EVENT_DEAD, self._EventCrash ) --- self:AddEvent( world.event.S_EVENT_SHOT, self._EventShot ) --- --- self:EnableEvents() + self.CleanUpList[EventData.IniDCSUnitName] = {} + self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniDCSUnit + self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniDCSGroup + self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName + self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName + EventData.IniUnit:HandleEvent( EVENTS.EngineShutdown , self._EventAddForCleanUp ) + EventData.IniUnit:HandleEvent( EVENTS.EngineStartup, self._EventAddForCleanUp ) + EventData.IniUnit:HandleEvent( EVENTS.Hit, self._EventAddForCleanUp ) + EventData.IniUnit:HandleEvent( EVENTS.PilotDead, self._EventCrash ) + EventData.IniUnit:HandleEvent( EVENTS.Dead, self._EventCrash ) + EventData.IniUnit:HandleEvent( EVENTS.Crash, self._EventCrash ) + EventData.IniUnit:HandleEvent( EVENTS.Shot, self._EventShot ) end diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index e458e8cb5..15d3efd4c 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -1,985 +1,2073 @@ ---- This module contains the DETECTION classes. +--- **Functional** - DETECTION_ classes model the detection of enemy units by FACs or RECCEs and group them according various methods. +-- +-- ![Banner Image](..\Presentations\DETECTION\Dia1.JPG) -- -- === -- --- 1) @{Detection#DETECTION_BASE} class, extends @{Base#BASE} --- ========================================================== --- The @{Detection#DETECTION_BASE} class defines the core functions to administer detected objects. --- The @{Detection#DETECTION_BASE} class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). +-- DETECTION classes facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units). +-- DETECTION uses the in-built detection capabilities of DCS World, but adds new functionalities. -- --- 1.1) DETECTION_BASE constructor --- ------------------------------- --- Construct a new DETECTION_BASE instance using the @{Detection#DETECTION_BASE.New}() method. +-- Please watch this [youtube video](https://youtu.be/C7p81dUwP-E) that explains the detection concepts. -- --- 1.2) DETECTION_BASE initialization --- ---------------------------------- --- By default, detection will return detected objects with all the detection sensors available. --- However, you can ask how the objects were found with specific detection methods. --- If you use one of the below methods, the detection will work with the detection method specified. --- You can specify to apply multiple detection methods. --- --- Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK: --- --- * @{Detection#DETECTION_BASE.InitDetectVisual}(): Detected using Visual. --- * @{Detection#DETECTION_BASE.InitDetectOptical}(): Detected using Optical. --- * @{Detection#DETECTION_BASE.InitDetectRadar}(): Detected using Radar. --- * @{Detection#DETECTION_BASE.InitDetectIRST}(): Detected using IRST. --- * @{Detection#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. --- * @{Detection#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. --- --- 1.3) Obtain objects detected by DETECTION_BASE --- ---------------------------------------------- --- DETECTION_BASE builds @{Set}s of objects detected. These @{Set#SET_BASE}s can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSets}(). --- The method will return a list (table) of @{Set#SET_BASE} objects. --- --- === --- --- 2) @{Detection#DETECTION_AREAS} class, extends @{Detection#DETECTION_BASE} --- =============================================================================== --- The @{Detection#DETECTION_AREAS} class will detect units within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s), --- and will build a list (table) of @{Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. --- The class is group the detected units within zones given a DetectedZoneRange parameter. --- A set with multiple detected zones will be created as there are groups of units detected. --- --- 2.1) Retrieve the Detected Unit sets and Detected Zones --- ------------------------------------------------------- --- The DetectedUnitSets methods are implemented in @{Detection#DECTECTION_BASE} and the DetectedZones methods is implemented in @{Detection#DETECTION_AREAS}. --- --- Retrieve the DetectedUnitSets with the method @{Detection#DETECTION_BASE.GetDetectedSets}(). A table will be return of @{Set#SET_UNIT}s. --- To understand the amount of sets created, use the method @{Detection#DETECTION_BASE.GetDetectedSetCount}(). --- If you want to obtain a specific set from the DetectedSets, use the method @{Detection#DETECTION_BASE.GetDetectedSet}() with a given index. --- --- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Detection#DETECTION_BASE.GetDetectionZones}(). --- To understand the amount of zones created, use the method @{Detection#DETECTION_BASE.GetDetectionZoneCount}(). --- If you want to obtain a specific zone from the DetectedZones, use the method @{Detection#DETECTION_BASE.GetDetectionZone}() with a given index. --- --- 1.4) Flare or Smoke detected units --- ---------------------------------- --- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedUnits}() or @{Detection#DETECTION_AREAS.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place. --- --- 1.5) Flare or Smoke detected zones --- ---------------------------------- --- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedZones}() or @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to flare or smoke the detected zones when a new detection has taken place. --- --- === -- -- ### Contributions: -- --- * Mechanist : Concept & Testing +-- * Mechanist : Early concept of DETECTION_AREAS. -- -- ### Authors: -- --- * FlightControl : Design & Programming +-- * FlightControl : Analysis, Design, Programming, Testing -- -- @module Detection +do -- DETECTION_BASE ---- DETECTION_BASE class --- @type DETECTION_BASE --- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. --- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. --- @field #number DetectionRun --- @extends Core.Base#BASE -DETECTION_BASE = { - ClassName = "DETECTION_BASE", - DetectionSetGroup = nil, - DetectionRange = nil, - DetectedObjects = {}, - DetectionRun = 0, - DetectedObjectsIdentified = {}, -} - ---- @type DETECTION_BASE.DetectedObjects --- @list <#DETECTION_BASE.DetectedObject> - ---- @type DETECTION_BASE.DetectedObject --- @field #string Name --- @field #boolean Visible --- @field #string Type --- @field #number Distance --- @field #boolean Identified - ---- DETECTION constructor. --- @param #DETECTION_BASE self --- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @param Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @return #DETECTION_BASE self -function DETECTION_BASE:New( DetectionSetGroup, DetectionRange ) - - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) + --- # 1) DETECTION_BASE class, extends @{Fsm#FSM} + -- + -- The DETECTION_BASE class defines the core functions to administer detected objects. + -- The DETECTION_BASE class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). + -- + -- ## 1.1) DETECTION_BASE constructor + -- + -- Construct a new DETECTION_BASE instance using the @{#DETECTION_BASE.New}() method. + -- + -- ## 1.2) DETECTION_BASE initialization + -- + -- By default, detection will return detected objects with all the detection sensors available. + -- However, you can ask how the objects were found with specific detection methods. + -- If you use one of the below methods, the detection will work with the detection method specified. + -- You can specify to apply multiple detection methods. + -- + -- Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK: + -- + -- * @{#DETECTION_BASE.InitDetectVisual}(): Detected using Visual. + -- * @{#DETECTION_BASE.InitDetectOptical}(): Detected using Optical. + -- * @{#DETECTION_BASE.InitDetectRadar}(): Detected using Radar. + -- * @{#DETECTION_BASE.InitDetectIRST}(): Detected using IRST. + -- * @{#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. + -- * @{#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. + -- + -- ## 1.3) DETECTION_BASE derived classes group the detected units into a DetectedItems[] list + -- + -- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later + -- of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains + -- a SET_UNIT object that contains the detected units that belong to that group. + -- + -- Derived classes will apply different methods to group the detected units. + -- Examples are per area, per quadrant, per distance, per type. + -- See further the derived DETECTION classes on which grouping methods are currently supported. + -- + -- Various methods exist how to retrieve the grouped items from a DETECTION_BASE derived class: + -- + -- * The method @{Detection#DETECTION_BASE.GetDetectedItems}() retrieves the DetectedItems[] list. + -- * A DetectedItem from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedItem}( DetectedItemIndex ). + -- Note that this method returns a DetectedItem element from the list, that contains a Set variable and further information + -- about the DetectedItem that is set by the DETECTION_BASE derived classes, used to group the DetectedItem. + -- * A DetectedSet from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSet}( DetectedItemIndex ). + -- This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ). + -- + -- ## 1.4) Apply additional Filters to fine-tune the detected objects + -- + -- By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means. + -- That being said, the DCS World detection algorithm can sometimes be unrealistic. + -- Especially for a visual detection, DCS World is able to report within 1 second a detailed detection of a group of 20 units (including types of the units) that are 10 kilometers away, using only visual capabilities. + -- Additionally, trees and other obstacles are not accounted during the DCS World detection. + -- + -- Therefore, an additional (optional) filtering has been built into the DETECTION_BASE class, that can be set for visual detected units. + -- For electronic detection, this filtering is not applied, only for visually detected targets. + -- + -- The following additional filtering can be applied for visual filtering: + -- + -- * A probability factor per kilometer distance. + -- * A probability factor based on the alpha angle between the detected object and the unit detecting. + -- A detection from a higher altitude allows for better detection than when on the ground. + -- * Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult. + -- The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects additing a probability factor per zone. + -- + -- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. + -- Only when you experience unrealistic behaviour in your missions, these filters could be applied. + -- + -- ### 1.4.1 ) Distance visual detection probability + -- + -- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. + -- Also, the speed of accurate detection plays a role. + -- + -- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. + -- + -- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: + -- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. + -- + -- Note that based on this probability factor, not only the detection but also the **type** of the unit will be applied! + -- + -- Use the method @{Detection#DETECTION_BASE.SetDistanceProbability}() to set the probability factor upon a 10 km distance. + -- + -- ### 1.4.2 ) Alpha Angle visual detection probability + -- + -- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. + -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. + -- + -- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. + -- + -- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: + -- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% + -- + -- Use the method @{Detection#DETECTION_BASE.SetAlphaAngleProbability}() to set the probability factor if 0°. + -- + -- ### 1.4.3 ) Cloudy Zones detection probability + -- + -- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. + -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission + -- zones that reflect cloudy areas where detected units may not be so easily visually detected. + -- + -- Use the method @{Detection#DETECTION_BASE.SetZoneProbability}() to set for a defined number of zones, the probability factors. + -- + -- Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take + -- from the DETECTION_BASE to calculate the presence of the detected unit within each zone. + -- Expecially for ZONE_POLYGON, try to limit the amount of nodes of the polygon! + -- + -- Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for + -- AI not to detect so easily targets within a forrest or village rich area. + -- + -- ## 1.5 ) Accept / Reject detected units + -- + -- DETECTION_BASE can accept or reject successful detections based on the location of the detected object, + -- if it is located in range or located inside or outside of specific zones. + -- + -- ### 1.5.1 ) Detection acceptance of within range limit + -- + -- A range can be set that will limit a successful detection for a unit. + -- Use the method @{Detection#DETECTION_BASE.SetAcceptRange}() to apply a range in meters till where detected units will be accepted. + -- + -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. + -- + -- -- Build a detect object. + -- local Detection = DETECTION_BASE:New( SetGroup ) + -- + -- -- This will accept detected units if the range is below 5000 meters. + -- Detection:SetAcceptRange( 5000 ) + -- + -- -- Start the Detection. + -- Detection:Start() + -- + -- + -- ### 1.5.2 ) Detection acceptance if within zone(s). + -- + -- Specific ZONE_BASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONE_BASE object(s). + -- Use the method @{Detection#DETECTION_BASE.SetAcceptZones}() will accept detected units if they are within the specified zones. + -- + -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. + -- + -- -- Search fo the zones where units are to be accepted. + -- local ZoneAccept1 = ZONE:New( "AcceptZone1" ) + -- local ZoneAccept2 = ZONE:New( "AcceptZone2" ) + -- + -- -- Build a detect object. + -- local Detection = DETECTION_BASE:New( SetGroup ) + -- + -- -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2. + -- Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) + -- + -- -- Start the Detection. + -- Detection:Start() + -- + -- ### 1.5.3 ) Detection rejectance if within zone(s). + -- + -- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s). + -- Use the method @{Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones. + -- An example of how to use the method is shown below. + -- + -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. + -- + -- -- Search fo the zones where units are to be rejected. + -- local ZoneReject1 = ZONE:New( "RejectZone1" ) + -- local ZoneReject2 = ZONE:New( "RejectZone2" ) + -- + -- -- Build a detect object. + -- local Detection = DETECTION_BASE:New( SetGroup ) + -- + -- -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2. + -- Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) + -- + -- -- Start the Detection. + -- Detection:Start() + -- + -- ## 1.6) DETECTION_BASE is a Finite State Machine + -- + -- Various Events and State Transitions can be tailored using DETECTION_BASE. + -- + -- ### 1.6.1) DETECTION_BASE States + -- + -- * **Detecting**: The detection is running. + -- * **Stopped**: The detection is stopped. + -- + -- ### 1.6.2) DETECTION_BASE Events + -- + -- * **Start**: Start the detection process. + -- * **Detect**: Detect new units. + -- * **Detected**: New units have been detected. + -- * **Stop**: Stop the detection process. + -- + -- @type DETECTION_BASE + -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. + -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. + -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. + -- @field #number DetectionRun + -- @extends Core.Fsm#FSM + DETECTION_BASE = { + ClassName = "DETECTION_BASE", + DetectionSetGroup = nil, + DetectionRange = nil, + DetectedObjects = {}, + DetectionRun = 0, + DetectedObjectsIdentified = {}, + DetectedItems = {}, + } - self.DetectionSetGroup = DetectionSetGroup - self.DetectionRange = DetectionRange + --- @type DETECTION_BASE.DetectedObjects + -- @list <#DETECTION_BASE.DetectedObject> - self:InitDetectVisual( false ) - self:InitDetectOptical( false ) - self:InitDetectRadar( false ) - self:InitDetectRWR( false ) - self:InitDetectIRST( false ) - self:InitDetectDLINK( false ) + --- @type DETECTION_BASE.DetectedObject + -- @field #string Name + -- @field #boolean Visible + -- @field #string Type + -- @field #number Distance + -- @field #boolean Identified - return self -end - ---- Detect Visual. --- @param #DETECTION_BASE self --- @param #boolean DetectVisual --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectVisual( DetectVisual ) - - self.DetectVisual = DetectVisual -end - ---- Detect Optical. --- @param #DETECTION_BASE self --- @param #boolean DetectOptical --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectOptical( DetectOptical ) - self:F2() - - self.DetectOptical = DetectOptical -end - ---- Detect Radar. --- @param #DETECTION_BASE self --- @param #boolean DetectRadar --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectRadar( DetectRadar ) - self:F2() - - self.DetectRadar = DetectRadar -end - ---- Detect IRST. --- @param #DETECTION_BASE self --- @param #boolean DetectIRST --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectIRST( DetectIRST ) - self:F2() - - self.DetectIRST = DetectIRST -end - ---- Detect RWR. --- @param #DETECTION_BASE self --- @param #boolean DetectRWR --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectRWR( DetectRWR ) - self:F2() - - self.DetectRWR = DetectRWR -end - ---- Detect DLINK. --- @param #DETECTION_BASE self --- @param #boolean DetectDLINK --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectDLINK( DetectDLINK ) - self:F2() - - self.DetectDLINK = DetectDLINK -end - ---- Determines if a detected object has already been identified during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject --- @return #boolean true if already identified. -function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) - self:F3( DetectedObject.Name ) - - local DetectedObjectName = DetectedObject.Name - local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true - self:T3( DetectedObjectIdentified ) - return DetectedObjectIdentified -end - ---- Identifies a detected object during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject -function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) - self:F( DetectedObject.Name ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = true -end - ---- UnIdentify a detected object during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject -function DETECTION_BASE:UnIdentifyDetectedObject( DetectedObject ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = false -end - ---- UnIdentify all detected objects during detection processing. --- @param #DETECTION_BASE self -function DETECTION_BASE:UnIdentifyAllDetectedObjects() - - self.DetectedObjectsIdentified = {} -- Table will be garbage collected. -end - ---- Gets a detected object with a given name. --- @param #DETECTION_BASE self --- @param #string ObjectName --- @return #DETECTION_BASE.DetectedObject -function DETECTION_BASE:GetDetectedObject( ObjectName ) - self:F3( ObjectName ) + --- @type DETECTION_BASE.DetectedItems + -- @list <#DETECTION_BASE.DetectedItem> - if ObjectName then - local DetectedObject = self.DetectedObjects[ObjectName] + --- @type DETECTION_BASE.DetectedItem + -- @field Core.Set#SET_UNIT Set + -- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. + -- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. + -- @field #boolean Changed Documents if the detected area has changes. + -- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). + -- @field #number ItemID -- The identifier of the detected area. + -- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. + -- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - -- Only return detected objects that are alive! - local DetectedUnit = UNIT:FindByName( ObjectName ) - if DetectedUnit and DetectedUnit:IsAlive() then - if self:IsDetectedObjectIdentified( DetectedObject ) == false then - return DetectedObject + + --- DETECTION constructor. + -- @param #DETECTION_BASE self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @return #DETECTION_BASE self + function DETECTION_BASE:New( DetectionSetGroup ) + + -- Inherits from BASE + local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_BASE + + self.DetectedItemCount = 0 + self.DetectedItemMax = 0 + self.DetectedItems = {} + + self.DetectionSetGroup = DetectionSetGroup + + self.DetectionInterval = 30 + + self:InitDetectVisual( true ) + self:InitDetectOptical( false ) + self:InitDetectRadar( false ) + self:InitDetectRWR( false ) + self:InitDetectIRST( false ) + self:InitDetectDLINK( false ) + + -- Create FSM transitions. + + self:SetStartState( "Stopped" ) + self.CountryID = DetectionSetGroup:GetFirst():GetCountry() + + self:AddTransition( "Stopped", "Start", "Detecting") + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnLeaveStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnEnterStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Start. + -- @function [parent=#DETECTION_BASE] OnBeforeStart + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Start. + -- @function [parent=#DETECTION_BASE] OnAfterStart + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Start. + -- @function [parent=#DETECTION_BASE] Start + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Start. + -- @function [parent=#DETECTION_BASE] __Start + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Detecting. + -- @function [parent=#DETECTION_BASE] OnLeaveDetecting + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Detecting. + -- @function [parent=#DETECTION_BASE] OnEnterDetecting + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + self:AddTransition( "Detecting", "Detect", "Detecting" ) + self:AddTransition( "Detecting", "DetectionGroup", "Detecting" ) + + --- OnBefore Transition Handler for Event Detect. + -- @function [parent=#DETECTION_BASE] OnBeforeDetect + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Detect. + -- @function [parent=#DETECTION_BASE] OnAfterDetect + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Detect. + -- @function [parent=#DETECTION_BASE] Detect + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Detect. + -- @function [parent=#DETECTION_BASE] __Detect + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "Detecting", "Detected", "Detecting" ) + + --- OnBefore Transition Handler for Event Detected. + -- @function [parent=#DETECTION_BASE] OnBeforeDetected + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Detected. + -- @function [parent=#DETECTION_BASE] OnAfterDetected + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Detected. + -- @function [parent=#DETECTION_BASE] Detected + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Detected. + -- @function [parent=#DETECTION_BASE] __Detected + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "*", "Stop", "Stopped" ) + + --- OnBefore Transition Handler for Event Stop. + -- @function [parent=#DETECTION_BASE] OnBeforeStop + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Stop. + -- @function [parent=#DETECTION_BASE] OnAfterStop + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Stop. + -- @function [parent=#DETECTION_BASE] Stop + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Stop. + -- @function [parent=#DETECTION_BASE] __Stop + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnLeaveStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnEnterStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + return self + end + + do -- State Transition Handling + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function DETECTION_BASE:onafterStart(From,Event,To) + self:__Detect(0.1) + end + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function DETECTION_BASE:onafterDetect(From,Event,To) + self:E( {From,Event,To}) + + local DetectDelay = 0.1 + self.DetectionCount = 0 + self.DetectionRun = 0 + self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table + + self.DetectionSetGroup:Flush() + + for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do + self:E( {DetectionGroupData}) + self:__DetectionGroup( DetectDelay, DetectionGroupData ) -- Process each detection asynchronously. + self.DetectionCount = self.DetectionCount + 1 + DetectDelay = DetectDelay + 0.1 end end + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. + function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup ) + self:E( {From,Event,To}) + + self.DetectionRun = self.DetectionRun + 1 + + local HasDetectedObjects = false + + if DetectionGroup:IsAlive() then + + self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) + + local DetectionGroupName = DetectionGroup:GetName() + + local DetectedUnits = {} + + local DetectedTargets = DetectionGroup:GetDetectedTargets( + self.DetectVisual, + self.DetectOptical, + self.DetectRadar, + self.DetectIRST, + self.DetectRWR, + self.DetectDLINK + ) + + self:T( DetectedTargets ) + + for DetectionObjectID, Detection in pairs( DetectedTargets ) do + local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object + self:T2( DetectedObject ) + + if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then + + local DetectionAccepted = true + + local DetectedObjectName = DetectedObject:getName() + + local DetectedObjectVec3 = DetectedObject:getPoint() + local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z } + local DetectionGroupVec3 = DetectionGroup:GetVec3() + local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z } + + local Distance = ( ( DetectedObjectVec3.x - DetectionGroupVec3.x )^2 + + ( DetectedObjectVec3.y - DetectionGroupVec3.y )^2 + + ( DetectedObjectVec3.z - DetectionGroupVec3.z )^2 + ) ^ 0.5 / 1000 + + self:T( { "Detected Target", DetectionGroupName, DetectedObjectName, Distance } ) + + -- Calculate Acceptance + + if self.AcceptRange and Distance > self.AcceptRange then + DetectionAccepted = false + end + + if self.AcceptZones then + for AcceptZoneID, AcceptZone in pairs( self.AcceptZones ) do + local AcceptZone = AcceptZone -- Core.Zone#ZONE_BASE + if AcceptZone:IsPointVec2InZone( DetectedObjectVec2 ) == false then + DetectionAccepted = false + end + end + end + + if self.RejectZones then + for RejectZoneID, RejectZone in pairs( self.RejectZones ) do + local RejectZone = RejectZone -- Core.Zone#ZONE_BASE + if RejectZone:IsPointVec2InZone( DetectedObjectVec2 ) == true then + DetectionAccepted = false + end + end + end + + -- Calculate additional probabilities + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.DistanceProbability then + local DistanceFactor = Distance / 4 + local DistanceProbabilityReversed = ( 1 - self.DistanceProbability ) * DistanceFactor + local DistanceProbability = 1 - DistanceProbabilityReversed + DistanceProbability = DistanceProbability * 30 / 300 + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, DistanceProbability } ) + if Probability > DistanceProbability then + DetectionAccepted = false + end + end + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.AlphaAngleProbability then + local NormalVec2 = { x = DetectedObjectVec2.x - DetectionGroupVec2.x, y = DetectedObjectVec2.y - DetectionGroupVec2.y } + local AlphaAngle = math.atan2( NormalVec2.y, NormalVec2.x ) + local Sinus = math.sin( AlphaAngle ) + local AlphaAngleProbabilityReversed = ( 1 - self.AlphaAngleProbability ) * ( 1 - Sinus ) + local AlphaAngleProbability = 1 - AlphaAngleProbabilityReversed + + AlphaAngleProbability = AlphaAngleProbability * 30 / 300 + + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, AlphaAngleProbability } ) + if Probability > AlphaAngleProbability then + DetectionAccepted = false + end + + end + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.ZoneProbability then + + for ZoneDataID, ZoneData in pairs( self.ZoneProbability ) do + self:E({ZoneData}) + local ZoneObject = ZoneData[1] -- Core.Zone#ZONE_BASE + local ZoneProbability = ZoneData[2] -- #number + ZoneProbability = ZoneProbability * 30 / 300 + + if ZoneObject:IsPointVec2InZone( DetectedObjectVec2 ) == true then + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, ZoneProbability } ) + if Probability > ZoneProbability then + DetectionAccepted = false + break + end + end + end + end + + if DetectionAccepted then + + HasDetectedObjects = true + + if not self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = {} + end + self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName + self.DetectedObjects[DetectedObjectName].Visible = Detection.visible + self.DetectedObjects[DetectedObjectName].Type = Detection.type + self.DetectedObjects[DetectedObjectName].Distance = Distance + + local DetectedUnit = UNIT:FindByName( DetectedObjectName ) + + DetectedUnits[DetectedObjectName] = DetectedUnit + else + -- if beyond the DetectionRange then nullify... + if self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = nil + end + end + end + + self:T2( self.DetectedObjects ) + end + + if HasDetectedObjects then + self:__Detected( 0.1, DetectedUnits ) + end + + end + + if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then + self:__Detect( self.DetectionInterval ) + + self:T( "--> Create Detection Sets" ) + self:CreateDetectionSets() + end + + end + + + end + + do -- Initialization methods + + --- Detect Visual. + -- @param #DETECTION_BASE self + -- @param #boolean DetectVisual + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectVisual( DetectVisual ) + + self.DetectVisual = DetectVisual + end + + --- Detect Optical. + -- @param #DETECTION_BASE self + -- @param #boolean DetectOptical + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectOptical( DetectOptical ) + self:F2() + + self.DetectOptical = DetectOptical + end + + --- Detect Radar. + -- @param #DETECTION_BASE self + -- @param #boolean DetectRadar + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectRadar( DetectRadar ) + self:F2() + + self.DetectRadar = DetectRadar + end + + --- Detect IRST. + -- @param #DETECTION_BASE self + -- @param #boolean DetectIRST + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectIRST( DetectIRST ) + self:F2() + + self.DetectIRST = DetectIRST + end + + --- Detect RWR. + -- @param #DETECTION_BASE self + -- @param #boolean DetectRWR + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectRWR( DetectRWR ) + self:F2() + + self.DetectRWR = DetectRWR + end + + --- Detect DLINK. + -- @param #DETECTION_BASE self + -- @param #boolean DetectDLINK + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectDLINK( DetectDLINK ) + self:F2() + + self.DetectDLINK = DetectDLINK + end + + end + + do + + --- Set the detection interval time in seconds. + -- @param #DETECTION_BASE self + -- @param #number DetectionInterval Interval in seconds. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetDetectionInterval( DetectionInterval ) + self:F2() + + self.DetectionInterval = DetectionInterval + + return self + end + + end + + do -- Accept / Reject detected units + + --- Accept detections if within a range in meters. + -- @param #DETECTION_BASE self + -- @param #number AcceptRange Accept a detection if the unit is within the AcceptRange in meters. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAcceptRange( AcceptRange ) + self:F2() + + self.AcceptRange = AcceptRange + + return self + end + + --- Accept detections if within the specified zone(s). + -- @param #DETECTION_BASE self + -- @param AcceptZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAcceptZones( AcceptZones ) + self:F2() + + if type( AcceptZones ) == "table" then + self.AcceptZones = AcceptZones + else + self.AcceptZones = { AcceptZones } + end + + return self + end + + --- Reject detections if within the specified zone(s). + -- @param #DETECTION_BASE self + -- @param RejectZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetRejectZones( RejectZones ) + self:F2() + + if type( RejectZones ) == "table" then + self.RejectZones = RejectZones + else + self.RejectZones = { RejectZones } + end + + return self + end + + end + + do -- Probability methods + + --- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. + -- Also, the speed of accurate detection plays a role. + -- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. + -- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: + -- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. + -- @param #DETECTION_BASE self + -- @param DistanceProbability The probability factor. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetDistanceProbability( DistanceProbability ) + self:F2() + + self.DistanceProbability = DistanceProbability + + return self + end + + + --- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. + -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. + -- + -- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. + -- + -- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: + -- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% + -- @param #DETECTION_BASE self + -- @param AlphaAngleProbability The probability factor. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAlphaAngleProbability( AlphaAngleProbability ) + self:F2() + + self.AlphaAngleProbability = AlphaAngleProbability + + return self + end + + --- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. + -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission + -- zones that reflect cloudy areas where detected units may not be so easily visually detected. + -- @param #DETECTION_BASE self + -- @param ZoneArray Aray of a The ZONE_BASE object and a ZoneProbability pair.. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetZoneProbability( ZoneArray ) + self:F2() + + self.ZoneProbability = ZoneArray + + return self + end + + + end + + do -- Change processing + + --- Accepts changes from the detected item. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #DETECTION_BASE self + function DETECTION_BASE:AcceptChanges( DetectedItem ) + + DetectedItem.Changed = false + DetectedItem.Changes = {} + + return self + end + + --- Add a change to the detected zone. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @param #string ChangeCode + -- @return #DETECTION_BASE self + function DETECTION_BASE:AddChangeItem( DetectedItem, ChangeCode, ItemUnitType ) + + DetectedItem.Changed = true + local ItemID = DetectedItem.ItemID + + DetectedItem.Changes = DetectedItem.Changes or {} + DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} + DetectedItem.Changes[ChangeCode].ItemID = ItemID + DetectedItem.Changes[ChangeCode].ItemUnitType = ItemUnitType + + self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ItemUnitType } ) + + return self + end + + + --- Add a change to the detected zone. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @param #string ChangeCode + -- @param #string ChangeUnitType + -- @return #DETECTION_BASE self + function DETECTION_BASE:AddChangeUnit( DetectedItem, ChangeCode, ChangeUnitType ) + + DetectedItem.Changed = true + local ItemID = DetectedItem.ItemID + + DetectedItem.Changes = DetectedItem.Changes or {} + DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} + DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] or 0 + DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] + 1 + DetectedItem.Changes[ChangeCode].ItemID = ItemID + + self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ChangeUnitType } ) + + return self + end + + + end + + do -- Threat + + --- Returns if there are friendlies nearby the FAC units ... + -- @param #DETECTION_BASE self + -- @return #boolean trhe if there are friendlies nearby + function DETECTION_BASE:IsFriendliesNearBy( DetectedItem ) + + self:T3( DetectedItem.FriendliesNearBy ) + return DetectedItem.FriendliesNearBy or false + end + + --- Background worker function to determine if there are friendlies nearby ... + -- @param #DETECTION_BASE self + function DETECTION_BASE:ReportFriendliesNearBy( ReportGroupData ) + self:F2() + + local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = ReportGroupData.DetectedItem.Set + local DetectedUnit = DetectedSet:GetFirst() + + DetectedItem.FriendliesNearBy = false + + if DetectedUnit then + + + local SphereSearch = { + id = world.VolumeType.SPHERE, + params = { + point = DetectedUnit:GetVec3(), + radius = 6000, + } + + } + + --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit + -- @param Wrapper.Group#GROUP ReportGroup + -- @param Set#SET_GROUP ReportSetGroup + local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) + + local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = ReportGroupData.DetectedItem.Set + local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT + local ReportSetGroup = ReportGroupData.ReportSetGroup + + local EnemyCoalition = DetectedUnit:GetCoalition() + + local FoundUnitCoalition = FoundDCSUnit:getCoalition() + local FoundUnitName = FoundDCSUnit:getName() + local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() + local EnemyUnitName = DetectedUnit:GetName() + local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil + + self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) + + if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then + DetectedItem.FriendliesNearBy = true + return false + end + + return true + end + + world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) + end + end + + end + + --- Determines if a detected object has already been identified during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + -- @return #boolean true if already identified. + function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) + self:F3( DetectedObject.Name ) + + local DetectedObjectName = DetectedObject.Name + local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true + self:T3( DetectedObjectIdentified ) + return DetectedObjectIdentified + end + + --- Identifies a detected object during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) + self:F( { "Identified:", DetectedObject.Name } ) + + local DetectedObjectName = DetectedObject.Name + self.DetectedObjectsIdentified[DetectedObjectName] = true + end + + --- UnIdentify a detected object during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + function DETECTION_BASE:UnIdentifyDetectedObject( DetectedObject ) + + local DetectedObjectName = DetectedObject.Name + self.DetectedObjectsIdentified[DetectedObjectName] = false + end + + --- UnIdentify all detected objects during detection processing. + -- @param #DETECTION_BASE self + function DETECTION_BASE:UnIdentifyAllDetectedObjects() + + self.DetectedObjectsIdentified = {} -- Table will be garbage collected. + end + + --- Gets a detected object with a given name. + -- @param #DETECTION_BASE self + -- @param #string ObjectName + -- @return #DETECTION_BASE.DetectedObject + function DETECTION_BASE:GetDetectedObject( ObjectName ) + self:F( ObjectName ) + + if ObjectName then + local DetectedObject = self.DetectedObjects[ObjectName] + + -- Only return detected objects that are alive! + local DetectedUnit = UNIT:FindByName( ObjectName ) + if DetectedUnit and DetectedUnit:IsAlive() then + if self:IsDetectedObjectIdentified( DetectedObject ) == false then + return DetectedObject + end + end + end + + return nil + end + + + --- Adds a new DetectedItem to the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #string DetectedItemIndex The index of the DetectedItem. + -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:AddDetectedItem( DetectedItemIndex, Set ) + + local DetectedItem = {} + self.DetectedItemCount = self.DetectedItemCount + 1 + self.DetectedItemMax = self.DetectedItemMax + 1 + + if DetectedItemIndex then + self.DetectedItems[DetectedItemIndex] = DetectedItem + else + self.DetectedItems[self.DetectedItemCount] = DetectedItem + end + + DetectedItem.Set = Set or SET_UNIT:New() + DetectedItem.ItemID = self.DetectedItemMax + DetectedItem.Removed = false + + return DetectedItem + end + + --- Adds a new DetectedItem to the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #string DetectedItemIndex The index of the DetectedItem. + -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. + -- @param Core.Zone#ZONE_UNIT Zone (optional) The Zone to be added where the Units are located. + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:AddDetectedItemZone( DetectedItemIndex, Set, Zone ) + + local DetectedItem = self:AddDetectedItem( DetectedItemIndex, Set ) + + DetectedItem.Zone = Zone + + return DetectedItem + end + + --- Removes an existing DetectedItem from the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #number DetectedItemIndex The index or position in the DetectedItems list where the item needs to be removed. + function DETECTION_BASE:RemoveDetectedItem( DetectedItemIndex ) + + self.DetectedItemCount = self.DetectedItemCount - 1 + self.DetectedItems[DetectedItemIndex] = nil + end + + + --- Get the detected @{Set#SET_BASE}s. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE.DetectedItems + function DETECTION_BASE:GetDetectedItems() + + return self.DetectedItems + end + + --- Get the amount of SETs with detected objects. + -- @param #DETECTION_BASE self + -- @return #number Count + function DETECTION_BASE:GetDetectedItemsCount() + + local DetectedCount = self.DetectedItemCount + return DetectedCount + end + + --- Get a detected item using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:GetDetectedItem( Index ) + + local DetectedItem = self.DetectedItems[Index] + if DetectedItem then + return DetectedItem + end + + return nil + end + + --- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return Core.Set#SET_UNIT DetectedSet + function DETECTION_BASE:GetDetectedSet( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + local DetectedSetUnit = DetectedItem.Set + if DetectedSetUnit then + return DetectedSetUnit + end + + return nil + end + + do -- Zones + + --- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return Core.Zone#ZONE_UNIT DetectedZone + function DETECTION_BASE:GetDetectedZone( Index ) + + local DetectedZone = self.DetectedItems[Index].Zone + if DetectedZone then + return DetectedZone + end + + return nil + end + + end + + + --- Report summary of a detected item using a given numeric index. + -- @param #DETECTION_BASE self + -- @param Index + -- @return #string + function DETECTION_BASE:DetectedItemReportSummary( Index ) + self:F( Index ) + return nil + end + + --- Report detailed of a detectedion result. + -- @param #DETECTION_BASE self + -- @return #string + function DETECTION_BASE:DetectedReportDetailed() + self:F() + return nil + end + + --- Get the detection Groups. + -- @param #DETECTION_BASE self + -- @return Wrapper.Group#GROUP + function DETECTION_BASE:GetDetectionSetGroup() + + local DetectionSetGroup = self.DetectionSetGroup + return DetectionSetGroup + end + + --- Make a DetectionSet table. This function will be overridden in the derived clsses. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE self + function DETECTION_BASE:CreateDetectionSets() + self:F2() + + self:E( "Error, in DETECTION_BASE class..." ) + + end + + + --- Schedule the DETECTION construction. + -- @param #DETECTION_BASE self + -- @param #number DelayTime The delay in seconds to wait the reporting. + -- @param #number RepeatInterval The repeat interval in seconds for the reporting to happen repeatedly. + -- @return #DETECTION_BASE self + function DETECTION_BASE:Schedule( DelayTime, RepeatInterval ) + self:F2() + + self.ScheduleDelayTime = DelayTime + self.ScheduleRepeatInterval = RepeatInterval + + self.DetectionScheduler = SCHEDULER:New( self, self._DetectionScheduler, { self, "Detection" }, DelayTime, RepeatInterval ) + return self + end + +end + +do -- DETECTION_UNITS + + --- # 2) DETECTION_UNITS class, extends @{Detection#DETECTION_BASE} + -- + -- The DETECTION_UNITS class will detect units within the battle zone. + -- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. + -- Beware that when the amount of units detected is large, the DetectedItems list will be large also. + -- + -- @type DETECTION_UNITS + -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are detected. + -- @extends #DETECTION_BASE + DETECTION_UNITS = { + ClassName = "DETECTION_UNITS", + DetectionRange = nil, + } + + --- DETECTION_UNITS constructor. + -- @param Functional.Detection#DETECTION_UNITS self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @return Functional.Detection#DETECTION_UNITS self + function DETECTION_UNITS:New( DetectionSetGroup ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_UNITS + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self + end + + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_UNITS self + -- @param #DETECTION_UNITS.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_UNITS:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do + + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." + end + + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." + end + + end + + return table.concat( MT, "\n" ) + + end + + + --- Create the DetectedItems list from the DetectedObjects table. + -- For each DetectedItem, a one field array is created containing the Unit detected. + -- @param #DETECTION_UNITS self + -- @return #DETECTION_UNITS self + function DETECTION_UNITS:CreateDetectionSets() + self:F2( #self.DetectedObjects ) + + -- Loop the current detected items, and check if each object still exists and is detected. + + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + + local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedTypeName = DetectedItem.Type + + for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + + local DetectedObject = nil + self:E( DetectedUnit ) + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Yes, the DetectedUnit is still detected or exists. Flag as identified. + self:IdentifyDetectedObject( DetectedObject ) + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) + DetectedItemSet:Remove( DetectedUnitName ) + end + end + end + + + -- Now we need to loop through the unidentified detected units and add these... These are all new items. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do + + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + if DetectedObject then + self:T( { "Detected Unit #", DetectedUnitName } ) + + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + if DetectedUnit then + local DetectedTypeName = DetectedUnit:GetTypeName() + local DetectedItem = self:GetDetectedItem( DetectedUnitName ) + if not DetectedItem then + self:T( "Added new DetectedItem" ) + DetectedItem = self:AddDetectedItem( DetectedUnitName ) + DetectedItem.Type = DetectedUnit:GetTypeName() + DetectedItem.Name = DetectedObjectData.Name + DetectedItem.Visible = DetectedObjectData.Visible + DetectedItem.Distance = DetectedObjectData.Distance + end + + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) + end + end + end + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + --self:NearestFAC( DetectedItem ) + end + + end + + --- Report summary of a DetectedItem using a given numeric index. + -- @param #DETECTION_UNITS self + -- @param Index + -- @return #string + function DETECTION_UNITS:DetectedItemReportSummary( Index ) + self:F( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + local DetectedSet = self:GetDetectedSet( Index ) + + self:T( DetectedSet ) + if DetectedSet then + local ReportSummary = "" + local UnitDistanceText = "" + local UnitCategoryText = "" + + local DetectedItemUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT + + if DetectedItemUnit and DetectedItemUnit:IsAlive() then + self:T(DetectedItemUnit) + + local UnitCategoryName = DetectedItemUnit:GetCategoryName() or "" + local UnitCategoryType = DetectedItemUnit:GetTypeName() or "" + + if DetectedItem.Type and UnitCategoryName and UnitCategoryType then + UnitCategoryText = UnitCategoryName .. " (" .. UnitCategoryType .. ") at " + else + UnitCategoryText = "Unknown target at " + end + + if DetectedItem.Visible == false then + UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " estimated km" + else + UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " km, visual contact" + end + + local DetectedItemPointVec3 = DetectedItemUnit:GetPointVec3() + local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) + + local ThreatLevelA2G = DetectedItemUnit:GetThreatLevel( DetectedItem ) + + ReportSummary = string.format( + "%s - Threat [%s] (%2d) - %s%s", + DetectedItemPointLL, + string.rep( "■", ThreatLevelA2G ), + ThreatLevelA2G, + UnitCategoryText, + UnitDistanceText + ) + end + + self:T( ReportSummary ) + + return ReportSummary + end end - return nil + --- Report detailed of a detection result. + -- @param #DETECTION_UNITS self + -- @return #string + function DETECTION_UNITS:DetectedReportDetailed() + self:F() + + local Report = REPORT:New( "Detected units:" ) + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItemID ) + Report:Add( ReportSummary ) + end + + local ReportText = Report:Text() + + return ReportText + end + end ---- Get the detected @{Set#SET_BASE}s. --- @param #DETECTION_BASE self --- @return #DETECTION_BASE.DetectedSets DetectedSets -function DETECTION_BASE:GetDetectedSets() +do -- DETECTION_TYPES - local DetectionSets = self.DetectedSets - return DetectionSets -end - ---- Get the amount of SETs with detected objects. --- @param #DETECTION_BASE self --- @return #number Count -function DETECTION_BASE:GetDetectedSetCount() - - local DetectionSetCount = #self.DetectedSets - return DetectionSetCount -end - ---- Get a SET of detected objects using a given numeric index. --- @param #DETECTION_BASE self --- @param #number Index --- @return Core.Set#SET_BASE -function DETECTION_BASE:GetDetectedSet( Index ) - - local DetectionSet = self.DetectedSets[Index] - if DetectionSet then - return DetectionSet + --- # 3) DETECTION_TYPES class, extends @{Detection#DETECTION_BASE} + -- + -- The DETECTION_TYPES class will detect units within the battle zone. + -- It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. + -- Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. + -- Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also. + -- + -- @type DETECTION_TYPES + -- @extends #DETECTION_BASE + DETECTION_TYPES = { + ClassName = "DETECTION_TYPES", + DetectionRange = nil, + } + + --- DETECTION_TYPES constructor. + -- @param Functional.Detection#DETECTION_TYPES self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Recce role. + -- @return Functional.Detection#DETECTION_TYPES self + function DETECTION_TYPES:New( DetectionSetGroup ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_TYPES + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self end - return nil -end - ---- Get the detection Groups. --- @param #DETECTION_BASE self --- @return Wrapper.Group#GROUP -function DETECTION_BASE:GetDetectionSetGroup() - - local DetectionSetGroup = self.DetectionSetGroup - return DetectionSetGroup -end - ---- Make a DetectionSet table. This function will be overridden in the derived clsses. --- @param #DETECTION_BASE self --- @return #DETECTION_BASE self -function DETECTION_BASE:CreateDetectionSets() - self:F2() - - self:E( "Error, in DETECTION_BASE class..." ) - -end - - ---- Schedule the DETECTION construction. --- @param #DETECTION_BASE self --- @param #number DelayTime The delay in seconds to wait the reporting. --- @param #number RepeatInterval The repeat interval in seconds for the reporting to happen repeatedly. --- @return #DETECTION_BASE self -function DETECTION_BASE:Schedule( DelayTime, RepeatInterval ) - self:F2() - - self.ScheduleDelayTime = DelayTime - self.ScheduleRepeatInterval = RepeatInterval + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_TYPES self + -- @param #DETECTION_TYPES.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_TYPES:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do - self.DetectionScheduler = SCHEDULER:New( self, self._DetectionScheduler, { self, "Detection" }, DelayTime, RepeatInterval ) - return self -end - - ---- Form @{Set}s of detected @{Unit#UNIT}s in an array of @{Set#SET_BASE}s. --- @param #DETECTION_BASE self -function DETECTION_BASE:_DetectionScheduler( SchedulerName ) - self:F2( { SchedulerName } ) + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." + end - self.DetectionRun = self.DetectionRun + 1 - - self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table - - for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - local DetectionGroup = DetectionGroupData -- Wrapper.Group#GROUP - - if DetectionGroup:IsAlive() then - - local DetectionGroupName = DetectionGroup:GetName() + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." + end - local DetectionDetectedTargets = DetectionGroup:GetDetectedTargets( - self.DetectVisual, - self.DetectOptical, - self.DetectRadar, - self.DetectIRST, - self.DetectRWR, - self.DetectDLINK + end + + return table.concat( MT, "\n" ) + + end + + + --- Create the DetectedItems list from the DetectedObjects table. + -- For each DetectedItem, a one field array is created containing the Unit detected. + -- @param #DETECTION_TYPES self + -- @return #DETECTION_TYPES self + function DETECTION_TYPES:CreateDetectionSets() + self:F2( #self.DetectedObjects ) + + -- Loop the current detected items, and check if each object still exists and is detected. + + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + + local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedTypeName = DetectedItem.Type + + for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + + local DetectedObject = nil + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Yes, the DetectedUnit is still detected or exists. Flag as identified. + self:IdentifyDetectedObject( DetectedObject ) + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) + DetectedItemSet:Remove( DetectedUnitName ) + end + end + end + + + -- Now we need to loop through the unidentified detected units and add these... These are all new items. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do + + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + if DetectedObject then + self:T( { "Detected Unit #", DetectedUnitName } ) + + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + if DetectedUnit then + local DetectedTypeName = DetectedUnit:GetTypeName() + local DetectedItem = self:GetDetectedItem( DetectedTypeName ) + if not DetectedItem then + DetectedItem = self:AddDetectedItem( DetectedTypeName ) + DetectedItem.Type = DetectedUnit:GetTypeName() + end + + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) + end + end + end + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + --self:NearestFAC( DetectedItem ) + end + + end + + --- Report summary of a DetectedItem using a given numeric index. + -- @param #DETECTION_TYPES self + -- @param Index + -- @return #string + function DETECTION_TYPES:DetectedItemReportSummary( DetectedTypeName ) + self:F( DetectedTypeName ) + + local DetectedItem = self:GetDetectedItem( DetectedTypeName ) + local DetectedSet = self:GetDetectedSet( DetectedTypeName ) + + self:T( DetectedItem ) + if DetectedItem then + + local ThreatLevelA2G = DetectedSet:CalculateThreatLevelA2G() + local DetectedItemsCount = DetectedSet:Count() + local DetectedItemType = DetectedItem.Type + + local ReportSummary = string.format( + "Threat [%s] (%2d) - %2d of %s", + string.rep( "■", ThreatLevelA2G ), + ThreatLevelA2G, + DetectedItemsCount, + DetectedItemType + ) + self:T( ReportSummary ) + + return ReportSummary + end + end + + --- Report detailed of a detection result. + -- @param #DETECTION_TYPES self + -- @return #string + function DETECTION_TYPES:DetectedReportDetailed() + self:F() + + local Report = REPORT:New( "Detected types:" ) + for DetectedItemTypeName, DetectedItem in pairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItemTypeName ) + Report:Add( ReportSummary ) + end + + local ReportText = Report:Text() + + return ReportText + end + +end + + +do -- DETECTION_AREAS + + --- # 4) DETECTION_AREAS class, extends @{Detection#DETECTION_BASE} + -- + -- The DETECTION_AREAS class will detect units within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s), + -- and will build a list (table) of @{Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. + -- The class is group the detected units within zones given a DetectedZoneRange parameter. + -- A set with multiple detected zones will be created as there are groups of units detected. + -- + -- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones + -- + -- The methods to manage the DetectedItems[].Set(s) are implemented in @{Detection#DECTECTION_BASE} and + -- the methods to manage the DetectedItems[].Zone(s) is implemented in @{Detection#DETECTION_AREAS}. + -- + -- Retrieve the DetectedItems[].Set with the method @{Detection#DETECTION_BASE.GetDetectedSet}(). A @{Set#SET_UNIT} object will be returned. + -- + -- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Detection#DETECTION_BASE.GetDetectionZones}(). + -- To understand the amount of zones created, use the method @{Detection#DETECTION_BASE.GetDetectionZoneCount}(). + -- If you want to obtain a specific zone from the DetectedZones, use the method @{Detection#DETECTION_BASE.GetDetectionZone}() with a given index. + -- + -- ## 4.4) Flare or Smoke detected units + -- + -- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedUnits}() or @{Detection#DETECTION_AREAS.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place. + -- + -- ## 4.5) Flare or Smoke or Bound detected zones + -- + -- Use the methods: + -- + -- * @{Detection#DETECTION_AREAS.FlareDetectedZones}() to flare in a color + -- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to smoke in a color + -- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to bound with a tire with a white flag + -- + -- the detected zones when a new detection has taken place. + -- + -- @type DETECTION_AREAS + -- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. + -- @extends #DETECTION_BASE + DETECTION_AREAS = { + ClassName = "DETECTION_AREAS", + DetectionZoneRange = nil, + } + + + --- DETECTION_AREAS constructor. + -- @param #DETECTION_AREAS self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @return #DETECTION_AREAS + function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) + + self.DetectionZoneRange = DetectionZoneRange + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self + end + + --- Report summary of a detected item using a given numeric index. + -- @param #DETECTION_AREAS self + -- @param Index + -- @return #string + function DETECTION_AREAS:DetectedItemReportSummary( Index ) + self:F( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + if DetectedItem then + local DetectedSet = self:GetDetectedSet( Index ) + local ReportSummaryItem + + local DetectedZone = self:GetDetectedZone( Index ) + local DetectedItemPointVec3 = DetectedZone:GetPointVec3() + local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) + + local ThreatLevelA2G = self:GetTreatLevelA2G( DetectedItem ) + local DetectedItemsCount = DetectedSet:Count() + local DetectedItemsTypes = DetectedSet:GetTypeNames() + + local ReportSummary = string.format( + "%s - Threat [%s] (%2d) - %2d of %s", + DetectedItemPointLL, + string.rep( "■", ThreatLevelA2G ), + ThreatLevelA2G, + DetectedItemsCount, + DetectedItemsTypes ) - for DetectionDetectedTargetID, DetectionDetectedTarget in pairs( DetectionDetectedTargets ) do - local DetectionObject = DetectionDetectedTarget.object -- Dcs.DCSWrapper.Object#Object - self:T2( DetectionObject ) + return ReportSummary + end + + return nil + end + + + --- Returns if there are friendlies nearby the FAC units ... + -- @param #DETECTION_AREAS self + -- @return #boolean trhe if there are friendlies nearby + function DETECTION_AREAS:IsFriendliesNearBy( DetectedItem ) + + self:T3( DetectedItem.FriendliesNearBy ) + return DetectedItem.FriendliesNearBy or false + end + + --- Calculate the maxium A2G threat level of the DetectedItem. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedItem ) + + local MaxThreatLevelA2G = 0 + for UnitName, UnitData in pairs( DetectedItem.Set:GetSet() ) do + local ThreatUnit = UnitData -- Wrapper.Unit#UNIT + local ThreatLevelA2G = ThreatUnit:GetThreatLevel() + if ThreatLevelA2G > MaxThreatLevelA2G then + MaxThreatLevelA2G = ThreatLevelA2G + end + end + + self:T3( MaxThreatLevelA2G ) + DetectedItem.MaxThreatLevelA2G = MaxThreatLevelA2G + + end + + --- Find the nearest FAC of the DetectedItem. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return Wrapper.Unit#UNIT The nearest FAC unit + function DETECTION_AREAS:NearestFAC( DetectedItem ) + + local NearestFAC = nil + local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) + + for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do + for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do + local FACUnit = FACUnitData -- Wrapper.Unit#UNIT + if FACUnit:IsActive() then + local Vec3 = FACUnit:GetVec3() + local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) + local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) + if Distance < MinDistance then + MinDistance = Distance + NearestFAC = FACUnit + end + end + end + end + + DetectedItem.NearestFAC = NearestFAC + + end + + --- Returns the A2G threat level of the units in the DetectedItem + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #number a scale from 0 to 10. + function DETECTION_AREAS:GetTreatLevelA2G( DetectedItem ) + + self:T3( DetectedItem.MaxThreatLevelA2G ) + return DetectedItem.MaxThreatLevelA2G + end + + + + --- Smoke the detected units + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:SmokeDetectedUnits() + self:F2() + + self._SmokeDetectedUnits = true + return self + end + + --- Flare the detected units + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:FlareDetectedUnits() + self:F2() + + self._FlareDetectedUnits = true + return self + end + + --- Smoke the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:SmokeDetectedZones() + self:F2() + + self._SmokeDetectedZones = true + return self + end + + --- Flare the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:FlareDetectedZones() + self:F2() + + self._FlareDetectedZones = true + return self + end + + --- Bound the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:BoundDetectedZones() + self:F2() + + self._BoundDetectedZones = true + return self + end + + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_AREAS:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do + + if ChangeCode == "AA" then + MT[#MT+1] = "Detected new area " .. ChangeData.ItemID .. ". The center target is a " .. ChangeData.ItemUnitType .. "." + end + + if ChangeCode == "RAU" then + MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". Removed the center target." + end + + if ChangeCode == "AAU" then + MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". The new center target is a " .. ChangeData.ItemUnitType .. "." + end + + if ChangeCode == "RA" then + MT[#MT+1] = "Removed old area " .. ChangeData.ItemID .. ". No more targets in this area." + end + + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = "Detected for area " .. ChangeData.ItemID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." + end + + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = "Removed for area " .. ChangeData.ItemID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." + end + + end + + return table.concat( MT, "\n" ) + + end + + + --- Make a DetectionSet table. This function will be overridden in the derived clsses. + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:CreateDetectionSets() + self:F2() + + + self:T( "Checking Detected Items for new Detected Units ..." ) + -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. + -- Regroup when needed, split groups when needed. + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + if DetectedItem then + + self:T( { "Detected Item ID:", DetectedItemID } ) - if DetectionObject and DetectionObject:isExist() and DetectionObject.id_ < 50000000 then - - local DetectionDetectedObjectName = DetectionObject:getName() - - local DetectionDetectedObjectPositionVec3 = DetectionObject:getPoint() - local DetectionGroupVec3 = DetectionGroup:GetVec3() - - local Distance = ( ( DetectionDetectedObjectPositionVec3.x - DetectionGroupVec3.x )^2 + - ( DetectionDetectedObjectPositionVec3.y - DetectionGroupVec3.y )^2 + - ( DetectionDetectedObjectPositionVec3.z - DetectionGroupVec3.z )^2 - ) ^ 0.5 / 1000 - - self:T2( { DetectionGroupName, DetectionDetectedObjectName, Distance } ) - - if Distance <= self.DetectionRange then - - if not self.DetectedObjects[DetectionDetectedObjectName] then - self.DetectedObjects[DetectionDetectedObjectName] = {} - end - self.DetectedObjects[DetectionDetectedObjectName].Name = DetectionDetectedObjectName - self.DetectedObjects[DetectionDetectedObjectName].Visible = DetectionDetectedTarget.visible - self.DetectedObjects[DetectionDetectedObjectName].Type = DetectionDetectedTarget.type - self.DetectedObjects[DetectionDetectedObjectName].Distance = DetectionDetectedTarget.distance - else - -- if beyond the DetectionRange then nullify... - if self.DetectedObjects[DetectionDetectedObjectName] then - self.DetectedObjects[DetectionDetectedObjectName] = nil - end - end - end - end - self:T2( self.DetectedObjects ) - - -- okay, now we have a list of detected object names ... - -- Sort the table based on distance ... - table.sort( self.DetectedObjects, function( a, b ) return a.Distance < b.Distance end ) - end - end - - if self.DetectedObjects then - self:CreateDetectionSets() - end - - return true -end - - - ---- DETECTION_AREAS class --- @type DETECTION_AREAS --- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. --- @field #DETECTION_AREAS.DetectedAreas DetectedAreas A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. --- @extends Functional.Detection#DETECTION_BASE -DETECTION_AREAS = { - ClassName = "DETECTION_AREAS", - DetectedAreas = { n = 0 }, - DetectionZoneRange = nil, -} - ---- @type DETECTION_AREAS.DetectedAreas --- @list <#DETECTION_AREAS.DetectedArea> - ---- @type DETECTION_AREAS.DetectedArea --- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. --- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. --- @field #boolean Changed Documents if the detected area has changes. --- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). --- @field #number AreaID -- The identifier of the detected area. --- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. --- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - - ---- DETECTION_AREAS constructor. --- @param Functional.Detection#DETECTION_AREAS self --- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @param Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. --- @return Functional.Detection#DETECTION_AREAS self -function DETECTION_AREAS:New( DetectionSetGroup, DetectionRange, DetectionZoneRange ) - - -- Inherits from DETECTION_BASE - local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup, DetectionRange ) ) - - self.DetectionZoneRange = DetectionZoneRange - - self._SmokeDetectedUnits = false - self._FlareDetectedUnits = false - self._SmokeDetectedZones = false - self._FlareDetectedZones = false - - self:Schedule( 10, 10 ) - - return self -end - ---- Add a detected @{#DETECTION_AREAS.DetectedArea}. --- @param Core.Set#SET_UNIT Set -- The Set of Units in the detected area. --- @param Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. --- @return #DETECTION_AREAS.DetectedArea DetectedArea -function DETECTION_AREAS:AddDetectedArea( Set, Zone ) - local DetectedAreas = self:GetDetectedAreas() - DetectedAreas.n = self:GetDetectedAreaCount() + 1 - DetectedAreas[DetectedAreas.n] = {} - local DetectedArea = DetectedAreas[DetectedAreas.n] - DetectedArea.Set = Set - DetectedArea.Zone = Zone - DetectedArea.Removed = false - DetectedArea.AreaID = DetectedAreas.n - - return DetectedArea -end - ---- Remove a detected @{#DETECTION_AREAS.DetectedArea} with a given Index. --- @param #DETECTION_AREAS self --- @param #number Index The Index of the detection are to be removed. --- @return #nil -function DETECTION_AREAS:RemoveDetectedArea( Index ) - local DetectedAreas = self:GetDetectedAreas() - local DetectedAreaCount = self:GetDetectedAreaCount() - local DetectedArea = DetectedAreas[Index] - local DetectedAreaSet = DetectedArea.Set - DetectedArea[Index] = nil - return nil -end - - ---- Get the detected @{#DETECTION_AREAS.DetectedAreas}. --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS.DetectedAreas DetectedAreas -function DETECTION_AREAS:GetDetectedAreas() - - local DetectedAreas = self.DetectedAreas - return DetectedAreas -end - ---- Get the amount of @{#DETECTION_AREAS.DetectedAreas}. --- @param #DETECTION_AREAS self --- @return #number DetectedAreaCount -function DETECTION_AREAS:GetDetectedAreaCount() - - local DetectedAreaCount = self.DetectedAreas.n - return DetectedAreaCount -end - ---- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. --- @param #DETECTION_AREAS self --- @param #number Index --- @return Core.Set#SET_UNIT DetectedSet -function DETECTION_AREAS:GetDetectedSet( Index ) - - local DetectedSetUnit = self.DetectedAreas[Index].Set - if DetectedSetUnit then - return DetectedSetUnit - end - - return nil -end - ---- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. --- @param #DETECTION_AREAS self --- @param #number Index --- @return Core.Zone#ZONE_UNIT DetectedZone -function DETECTION_AREAS:GetDetectedZone( Index ) - - local DetectedZone = self.DetectedAreas[Index].Zone - if DetectedZone then - return DetectedZone - end - - return nil -end - ---- Background worker function to determine if there are friendlies nearby ... --- @param #DETECTION_AREAS self --- @param Wrapper.Unit#UNIT ReportUnit -function DETECTION_AREAS:ReportFriendliesNearBy( ReportGroupData ) - self:F2() - - local DetectedArea = ReportGroupData.DetectedArea -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = ReportGroupData.DetectedArea.Set - local DetectedZone = ReportGroupData.DetectedArea.Zone - local DetectedZoneUnit = DetectedZone.ZoneUNIT - - DetectedArea.FriendliesNearBy = false - - local SphereSearch = { - id = world.VolumeType.SPHERE, - params = { - point = DetectedZoneUnit:GetVec3(), - radius = 6000, - } - - } - - --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit - -- @param Wrapper.Group#GROUP ReportGroup - -- @param Set#SET_GROUP ReportSetGroup - local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) - - local DetectedArea = ReportGroupData.DetectedArea -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = ReportGroupData.DetectedArea.Set - local DetectedZone = ReportGroupData.DetectedArea.Zone - local DetectedZoneUnit = DetectedZone.ZoneUNIT -- Wrapper.Unit#UNIT - local ReportSetGroup = ReportGroupData.ReportSetGroup - - local EnemyCoalition = DetectedZoneUnit:GetCoalition() - - local FoundUnitCoalition = FoundDCSUnit:getCoalition() - local FoundUnitName = FoundDCSUnit:getName() - local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() - local EnemyUnitName = DetectedZoneUnit:GetName() - local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil - - self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) - - if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then - DetectedArea.FriendliesNearBy = true - return false - end - - return true - end - - world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) - -end - - - ---- Returns if there are friendlies nearby the FAC units ... --- @param #DETECTION_AREAS self --- @return #boolean trhe if there are friendlies nearby -function DETECTION_AREAS:IsFriendliesNearBy( DetectedArea ) - - self:T3( DetectedArea.FriendliesNearBy ) - return DetectedArea.FriendliesNearBy or false -end - ---- Calculate the maxium A2G threat level of the DetectedArea. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea -function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedArea ) - - local MaxThreatLevelA2G = 0 - for UnitName, UnitData in pairs( DetectedArea.Set:GetSet() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - local ThreatLevelA2G = ThreatUnit:GetThreatLevel() - if ThreatLevelA2G > MaxThreatLevelA2G then - MaxThreatLevelA2G = ThreatLevelA2G - end - end - - self:T3( MaxThreatLevelA2G ) - DetectedArea.MaxThreatLevelA2G = MaxThreatLevelA2G - -end - ---- Find the nearest FAC of the DetectedArea. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return Wrapper.Unit#UNIT The nearest FAC unit -function DETECTION_AREAS:NearestFAC( DetectedArea ) - - local NearestFAC = nil - local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) - - for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do - local FACUnit = FACUnitData -- Wrapper.Unit#UNIT - if FACUnit:IsActive() then - local Vec3 = FACUnit:GetVec3() - local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) - local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) - if Distance < MinDistance then - MinDistance = Distance - NearestFAC = FACUnit - end - end - end - end - - DetectedArea.NearestFAC = NearestFAC - -end - ---- Returns the A2G threat level of the units in the DetectedArea --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #number a scale from 0 to 10. -function DETECTION_AREAS:GetTreatLevelA2G( DetectedArea ) - - self:T3( DetectedArea.MaxThreatLevelA2G ) - return DetectedArea.MaxThreatLevelA2G -end - - - ---- Smoke the detected units --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:SmokeDetectedUnits() - self:F2() - - self._SmokeDetectedUnits = true - return self -end - ---- Flare the detected units --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:FlareDetectedUnits() - self:F2() - - self._FlareDetectedUnits = true - return self -end - ---- Smoke the detected zones --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:SmokeDetectedZones() - self:F2() - - self._SmokeDetectedZones = true - return self -end - ---- Flare the detected zones --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:FlareDetectedZones() - self:F2() - - self._FlareDetectedZones = true - return self -end - ---- Add a change to the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @param #string ChangeCode --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AddChangeArea( DetectedArea, ChangeCode, AreaUnitType ) - - DetectedArea.Changed = true - local AreaID = DetectedArea.AreaID - - DetectedArea.Changes = DetectedArea.Changes or {} - DetectedArea.Changes[ChangeCode] = DetectedArea.Changes[ChangeCode] or {} - DetectedArea.Changes[ChangeCode].AreaID = AreaID - DetectedArea.Changes[ChangeCode].AreaUnitType = AreaUnitType - - self:T( { "Change on Detection Area:", DetectedArea.AreaID, ChangeCode, AreaUnitType } ) - - return self -end - - ---- Add a change to the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @param #string ChangeCode --- @param #string ChangeUnitType --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AddChangeUnit( DetectedArea, ChangeCode, ChangeUnitType ) - - DetectedArea.Changed = true - local AreaID = DetectedArea.AreaID - - DetectedArea.Changes = DetectedArea.Changes or {} - DetectedArea.Changes[ChangeCode] = DetectedArea.Changes[ChangeCode] or {} - DetectedArea.Changes[ChangeCode][ChangeUnitType] = DetectedArea.Changes[ChangeCode][ChangeUnitType] or 0 - DetectedArea.Changes[ChangeCode][ChangeUnitType] = DetectedArea.Changes[ChangeCode][ChangeUnitType] + 1 - DetectedArea.Changes[ChangeCode].AreaID = AreaID - - self:T( { "Change on Detection Area:", DetectedArea.AreaID, ChangeCode, ChangeUnitType } ) - - return self -end - ---- Make text documenting the changes of the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #string The Changes text -function DETECTION_AREAS:GetChangeText( DetectedArea ) - self:F( DetectedArea ) - - local MT = {} - - for ChangeCode, ChangeData in pairs( DetectedArea.Changes ) do - - if ChangeCode == "AA" then - MT[#MT+1] = "Detected new area " .. ChangeData.AreaID .. ". The center target is a " .. ChangeData.AreaUnitType .. "." - end - - if ChangeCode == "RAU" then - MT[#MT+1] = "Changed area " .. ChangeData.AreaID .. ". Removed the center target." - end - - if ChangeCode == "AAU" then - MT[#MT+1] = "Changed area " .. ChangeData.AreaID .. ". The new center target is a " .. ChangeData.AreaUnitType "." - end - - if ChangeCode == "RA" then - MT[#MT+1] = "Removed old area " .. ChangeData.AreaID .. ". No more targets in this area." - end - - if ChangeCode == "AU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "AreaID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Detected for area " .. ChangeData.AreaID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - if ChangeCode == "RU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "AreaID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Removed for area " .. ChangeData.AreaID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - end - - return table.concat( MT, "\n" ) - -end - - ---- Accepts changes from the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AcceptChanges( DetectedArea ) - - DetectedArea.Changed = false - DetectedArea.Changes = {} - - return self -end - - ---- Make a DetectionSet table. This function will be overridden in the derived clsses. --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:CreateDetectionSets() - self:F2() - - -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. - -- Regroup when needed, split groups when needed. - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do - - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - if DetectedArea then - - local DetectedSet = DetectedArea.Set - - local AreaExists = false -- This flag will determine of the detected area is still existing. - - -- First test if the center unit is detected in the detection area. - self:T3( DetectedArea.Zone.ZoneUNIT.UnitName ) - local DetectedZoneObject = self:GetDetectedObject( DetectedArea.Zone.ZoneUNIT.UnitName ) - self:T3( { "Detecting Zone Object", DetectedArea.AreaID, DetectedArea.Zone, DetectedZoneObject } ) - - if DetectedZoneObject then - - --self:IdentifyDetectedObject( DetectedZoneObject ) - AreaExists = true - - - - else - -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. - -- First remove the center unit from the set. - DetectedSet:RemoveUnitsByName( DetectedArea.Zone.ZoneUNIT.UnitName ) - - self:AddChangeArea( DetectedArea, 'RAU', "Dummy" ) + local DetectedSet = DetectedItem.Set - -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) - - -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. - -- If the DetectedUnit was already identified, DetectedObject will be nil. - if DetectedObject then - self:IdentifyDetectedObject( DetectedObject ) - AreaExists = true - - -- Assign the Unit as the new center unit of the detected area. - DetectedArea.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) - - self:AddChangeArea( DetectedArea, "AAU", DetectedArea.Zone.ZoneUNIT:GetTypeName() ) - - -- We don't need to add the DetectedObject to the area set, because it is already there ... - break - end - end - end - - -- Now we've determined the center unit of the area, now we can iterate the units in the detected area. - -- Note that the position of the area may have moved due to the center unit repositioning. - -- If no center unit was identified, then the detected area does not exist anymore and should be deleted, as there are no valid units that can be the center unit. - if AreaExists then - - -- ok, we found the center unit of the area, now iterate through the detected area set and see which units are still within the center unit zone ... - -- Those units within the zone are flagged as Identified. - -- If a unit was not found in the set, remove it from the set. This may be added later to other existing or new sets. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = nil - if DetectedUnit:IsAlive() then - --self:E(DetectedUnit:GetName()) - DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) - end - if DetectedObject then - - -- Check if the DetectedUnit is within the DetectedArea.Zone - if DetectedUnit:IsInZone( DetectedArea.Zone ) then + local AreaExists = false -- This flag will determine of the detected area is still existing. - -- Yes, the DetectedUnit is within the DetectedArea.Zone, no changes, DetectedUnit can be kept within the Set. - self:IdentifyDetectedObject( DetectedObject ) - - else - -- No, the DetectedUnit is not within the DetectedArea.Zone, remove DetectedUnit from the Set. - DetectedSet:Remove( DetectedUnitName ) - self:AddChangeUnit( DetectedArea, "RU", DetectedUnit:GetTypeName() ) - end - - else - -- There was no DetectedObject, remove DetectedUnit from the Set. - self:AddChangeUnit( DetectedArea, "RU", "destroyed target" ) - DetectedSet:Remove( DetectedUnitName ) - - -- The DetectedObject has been identified, because it does not exist ... - -- self:IdentifyDetectedObject( DetectedObject ) - end - end - else - self:RemoveDetectedArea( DetectedAreaID ) - self:AddChangeArea( DetectedArea, "RA" ) - end - end - end - - -- We iterated through the existing detection areas and: - -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. - -- - We recentered the detection area to new center units where it was needed. - -- - -- Now we need to loop through the unidentified detected units and see where they belong: - -- - They can be added to a new detection area and become the new center unit. - -- - They can be added to a new detection area. - for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - - local DetectedObject = self:GetDetectedObject( DetectedUnitName ) - - if DetectedObject then - - -- We found an unidentified unit outside of any existing detection area. - local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT - - local AddedToDetectionArea = false - - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do + -- First test if the center unit is detected in the detection area. + self:T3( { "Zone Center Unit:", DetectedItem.Zone.ZoneUNIT.UnitName } ) + local DetectedZoneObject = self:GetDetectedObject( DetectedItem.Zone.ZoneUNIT.UnitName ) + self:T3( { "Detected Zone Object:", DetectedItem.Zone:GetName(), DetectedZoneObject } ) - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - if DetectedArea then - self:T( "Detection Area #" .. DetectedArea.AreaID ) - local DetectedSet = DetectedArea.Set - if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedArea.Zone ) then - self:IdentifyDetectedObject( DetectedObject ) - DetectedSet:AddUnit( DetectedUnit ) - AddedToDetectionArea = true - self:AddChangeUnit( DetectedArea, "AU", DetectedUnit:GetTypeName() ) + if DetectedZoneObject then + + --self:IdentifyDetectedObject( DetectedZoneObject ) + AreaExists = true + + + + else + -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. + -- First remove the center unit from the set. + DetectedSet:RemoveUnitsByName( DetectedItem.Zone.ZoneUNIT.UnitName ) + + self:AddChangeItem( DetectedItem, 'RAU', "Dummy" ) + + -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. + for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do + + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) + + -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. + -- If the DetectedUnit was already identified, DetectedObject will be nil. + if DetectedObject then + self:IdentifyDetectedObject( DetectedObject ) + AreaExists = true + + DetectedItem.Zone:BoundZone( 12, self.CountryID, true) + + -- Assign the Unit as the new center unit of the detected area. + DetectedItem.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) + + self:AddChangeItem( DetectedItem, "AAU", DetectedItem.Zone.ZoneUNIT:GetTypeName() ) + + -- We don't need to add the DetectedObject to the area set, because it is already there ... + break + end end end + + -- Now we've determined the center unit of the area, now we can iterate the units in the detected area. + -- Note that the position of the area may have moved due to the center unit repositioning. + -- If no center unit was identified, then the detected area does not exist anymore and should be deleted, as there are no valid units that can be the center unit. + if AreaExists then + + -- ok, we found the center unit of the area, now iterate through the detected area set and see which units are still within the center unit zone ... + -- Those units within the zone are flagged as Identified. + -- If a unit was not found in the set, remove it from the set. This may be added later to other existing or new sets. + for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do + + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + local DetectedObject = nil + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Check if the DetectedUnit is within the DetectedItem.Zone + if DetectedUnit:IsInZone( DetectedItem.Zone ) then + + -- Yes, the DetectedUnit is within the DetectedItem.Zone, no changes, DetectedUnit can be kept within the Set. + self:IdentifyDetectedObject( DetectedObject ) + + else + -- No, the DetectedUnit is not within the DetectedItem.Zone, remove DetectedUnit from the Set. + DetectedSet:Remove( DetectedUnitName ) + self:AddChangeUnit( DetectedItem, "RU", DetectedUnit:GetTypeName() ) + end + + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", "destroyed target" ) + DetectedSet:Remove( DetectedUnitName ) + + -- The DetectedObject has been identified, because it does not exist ... + -- self:IdentifyDetectedObject( DetectedObject ) + end + end + else + DetectedItem.Zone:BoundZone( 12, self.CountryID, true) + self:RemoveDetectedItem( DetectedItemID ) + self:AddChangeItem( DetectedItem, "RA" ) + end end + end - if AddedToDetectionArea == false then + -- We iterated through the existing detection areas and: + -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. + -- - We recentered the detection area to new center units where it was needed. + -- + -- Now we need to loop through the unidentified detected units and see where they belong: + -- - They can be added to a new detection area and become the new center unit. + -- - They can be added to a new detection area. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - -- New detection area - local DetectedArea = self:AddDetectedArea( - SET_UNIT:New(), - ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) - ) - --self:E( DetectedArea.Zone.ZoneUNIT.UnitName ) - DetectedArea.Set:AddUnit( DetectedUnit ) - self:AddChangeArea( DetectedArea, "AA", DetectedUnit:GetTypeName() ) - end - end - end + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + + if DetectedObject then - -- Now all the tests should have been build, now make some smoke and flares... - -- We also report here the friendlies within the detected areas. - - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do - - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone - - self:ReportFriendliesNearBy( { DetectedArea = DetectedArea, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table - self:CalculateThreatLevelA2G( DetectedArea ) -- Calculate A2G threat level - self:NearestFAC( DetectedArea ) - - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedZone.ZoneUNIT:SmokeRed() - end - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit ) - if DetectedUnit:IsAlive() then - self:T( "Detected Set #" .. DetectedArea.AreaID .. ":" .. DetectedUnit:GetName() ) - if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then - DetectedUnit:FlareGreen() - end - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedUnit:SmokeGreen() + -- We found an unidentified unit outside of any existing detection area. + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + local AddedToDetectionArea = false + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + if DetectedItem then + self:T( "Detection Area #" .. DetectedItem.ItemID ) + local DetectedSet = DetectedItem.Set + if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedItem.Zone ) then + self:IdentifyDetectedObject( DetectedObject ) + DetectedSet:AddUnit( DetectedUnit ) + AddedToDetectionArea = true + self:AddChangeUnit( DetectedItem, "AU", DetectedUnit:GetTypeName() ) + end end end + + if AddedToDetectionArea == false then + + -- New detection area + local DetectedItem = self:AddDetectedItemZone( nil, + SET_UNIT:New(), + ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) + ) + --self:E( DetectedItem.Zone.ZoneUNIT.UnitName ) + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeItem( DetectedItem, "AA", DetectedUnit:GetTypeName() ) + end end - ) - if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then - DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) end - if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then - DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) + + -- Now all the tests should have been build, now make some smoke and flares... + -- We also report here the friendlies within the detected areas. + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + self:CalculateThreatLevelA2G( DetectedItem ) -- Calculate A2G threat level + self:NearestFAC( DetectedItem ) + + if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then + DetectedZone.ZoneUNIT:SmokeRed() + end + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit ) + if DetectedUnit:IsAlive() then + self:T( "Detected Set #" .. DetectedItem.ItemID .. ":" .. DetectedUnit:GetName() ) + if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then + DetectedUnit:FlareGreen() + end + if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then + DetectedUnit:SmokeGreen() + end + end + end + ) + if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then + DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) + end + if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then + DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) + end + + if DETECTION_AREAS._BoundDetectedZones or self._BoundDetectedZones then + DetectedZone:BoundZone( 12, self.CountryID ) + end end + end - -end - - + +end diff --git a/Moose Development/Moose/Functional/Escort.lua b/Moose Development/Moose/Functional/Escort.lua index 2f159a561..dc2d62c44 100644 --- a/Moose Development/Moose/Functional/Escort.lua +++ b/Moose Development/Moose/Functional/Escort.lua @@ -84,7 +84,7 @@ -- -- ESCORT initialization methods. -- ============================== --- The following menus are created within the RADIO MENU of an active unit hosted by a player: +-- The following menus are created within the RADIO MENU (F10) of an active unit hosted by a player: -- -- * @{#ESCORT.MenuFollowAt}: Creates a menu to make the escort follow the client. -- * @{#ESCORT.MenuHoldAtEscortPosition}: Creates a menu to hold the escort at its current position. @@ -128,6 +128,7 @@ -- @Field Dcs.DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the EscortGroup. -- @field Dcs.DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the EscortGroup. -- @field Core.Menu#MENU_CLIENT EscortMenuResumeMission +-- @field Functional.Detection#DETECTION_BASE Detection ESCORT = { ClassName = "ESCORT", EscortName = nil, -- The Escort Name @@ -176,14 +177,22 @@ ESCORT = { -- -- Now use these 2 objects to construct the new EscortPlanes object. -- EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." ) function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) - local self = BASE:Inherit( self, BASE:New() ) + + local self = BASE:Inherit( self, BASE:New() ) -- #ESCORT self:F( { EscortClient, EscortGroup, EscortName } ) self.EscortClient = EscortClient -- Wrapper.Client#CLIENT self.EscortGroup = EscortGroup -- Wrapper.Group#GROUP self.EscortName = EscortName self.EscortBriefing = EscortBriefing - + + self.EscortSetGroup = SET_GROUP:New() + self.EscortSetGroup:AddObject( self.EscortGroup ) + self.EscortSetGroup:Flush() + self.Detection = DETECTION_UNITS:New( self.EscortSetGroup, 15000 ) + + self.EscortGroup.Detection = self.Detection + -- Set EscortGroup known at EscortClient. if not self.EscortClient._EscortGroups then self.EscortClient._EscortGroups = {} @@ -193,7 +202,7 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) self.EscortClient._EscortGroups[EscortGroup:GetName()] = {} self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup = self.EscortGroup self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName = self.EscortName - self.EscortClient._EscortGroups[EscortGroup:GetName()].Targets = {} + self.EscortClient._EscortGroups[EscortGroup:GetName()].Detection = self.EscortGroup.Detection end self.EscortMenu = MENU_CLIENT:New( self.EscortClient, self.EscortName ) @@ -218,13 +227,30 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) self.FollowDistance = 100 self.CT1 = 0 self.GT1 = 0 - self.FollowScheduler = SCHEDULER:New( self, self._FollowScheduler, {}, 1, .5, .01 ) - self.EscortMode = ESCORT.MODE.MISSION - self.FollowScheduler:Stop() + self.FollowScheduler, self.FollowSchedule = SCHEDULER:New( self, self._FollowScheduler, {}, 1, .5, .01 ) + self.FollowScheduler:Stop( self.FollowSchedule ) + + self.EscortMode = ESCORT.MODE.MISSION + + return self end +--- Set a Detection method for the EscortClient to be reported upon. +-- Detection methods are based on the derived classes from DETECTION_BASE. +-- @param #ESCORT self +-- @param Function.Detection#DETECTION_BASE Detection +function ESCORT:SetDetection( Detection ) + + self.Detection = Detection + self.EscortGroup.Detection = self.Detection + self.EscortClient._EscortGroups[self.EscortGroup:GetName()].Detection = self.EscortGroup.Detection + + Detection:__Start( 1 ) + +end + --- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to. -- This allows to visualize where the escort is flying to. -- @param #ESCORT self @@ -282,7 +308,7 @@ function ESCORT:MenuFollowAt( Distance ) self.EscortMenuJoinUpAndFollow = {} end - self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, { ParamSelf = self, ParamDistance = Distance } ) + self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, self, Distance ) self.EscortMode = ESCORT.MODE.FOLLOW end @@ -340,11 +366,10 @@ function ESCORT:MenuHoldAtEscortPosition( Height, Seconds, MenuTextFormat ) MenuText, self.EscortMenuHold, ESCORT._HoldPosition, - { ParamSelf = self, - ParamOrbitGroup = self.EscortGroup, - ParamHeight = Height, - ParamSeconds = Seconds - } + self, + self.EscortGroup, + Height, + Seconds ) end @@ -461,9 +486,8 @@ function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat ) MenuText, self.EscortMenuScan, ESCORT._ScanTargets, - { ParamSelf = self, - ParamScanDuration = 30 - } + self, + 30 ) end @@ -493,11 +517,11 @@ function ESCORT:MenuFlare( MenuTextFormat ) end if not self.EscortMenuFlare then - self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, { ParamSelf = self } ) - self.EscortMenuFlareGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Green, ParamMessage = "Released a green flare!" } ) - self.EscortMenuFlareRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Red, ParamMessage = "Released a red flare!" } ) - self.EscortMenuFlareWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.White, ParamMessage = "Released a white flare!" } ) - self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Yellow, ParamMessage = "Released a yellow flare!" } ) + self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, self ) + self.EscortMenuFlareGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Green, "Released a green flare!" ) + self.EscortMenuFlareRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Red, "Released a red flare!" ) + self.EscortMenuFlareWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.White, "Released a white flare!" ) + self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Yellow, "Released a yellow flare!" ) end return self @@ -526,12 +550,12 @@ function ESCORT:MenuSmoke( MenuTextFormat ) end if not self.EscortMenuSmoke then - self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, { ParamSelf = self } ) - self.EscortMenuSmokeGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Green, ParamMessage = "Releasing green smoke!" } ) - self.EscortMenuSmokeRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Red, ParamMessage = "Releasing red smoke!" } ) - self.EscortMenuSmokeWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.White, ParamMessage = "Releasing white smoke!" } ) - self.EscortMenuSmokeOrange = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Orange, ParamMessage = "Releasing orange smoke!" } ) - self.EscortMenuSmokeBlue = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Blue, ParamMessage = "Releasing blue smoke!" } ) + self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, self ) + self.EscortMenuSmokeGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Green, "Releasing green smoke!" ) + self.EscortMenuSmokeRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Red, "Releasing red smoke!" ) + self.EscortMenuSmokeWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.White, "Releasing white smoke!" ) + self.EscortMenuSmokeOrange = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Orange, "Releasing orange smoke!" ) + self.EscortMenuSmokeBlue = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Blue, "Releasing blue smoke!" ) end end @@ -556,9 +580,9 @@ function ESCORT:MenuReportTargets( Seconds ) end -- Report Targets - self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, { ParamSelf = self } ) - self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = true } ) - self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = false, } ) + self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, self ) + self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, true ) + self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, false ) -- Attack Targets self.EscortMenuAttackNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Attack targets", self.EscortMenu ) @@ -595,16 +619,16 @@ function ESCORT:MenuROE( MenuTextFormat ) -- Rules of Engagement self.EscortMenuROE = MENU_CLIENT:New( self.EscortClient, "ROE", self.EscortMenu ) if self.EscortGroup:OptionROEHoldFirePossible() then - self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEHoldFire(), ParamMessage = "Holding weapons!" } ) + self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEHoldFire(), "Holding weapons!" ) end if self.EscortGroup:OptionROEReturnFirePossible() then - self.EscortMenuROEReturnFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Return Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEReturnFire(), ParamMessage = "Returning fire!" } ) + self.EscortMenuROEReturnFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Return Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEReturnFire(), "Returning fire!" ) end if self.EscortGroup:OptionROEOpenFirePossible() then - self.EscortMenuROEOpenFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Open Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEOpenFire(), ParamMessage = "Opening fire on designated targets!!" } ) + self.EscortMenuROEOpenFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Open Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEOpenFire(), "Opening fire on designated targets!!" ) end if self.EscortGroup:OptionROEWeaponFreePossible() then - self.EscortMenuROEWeaponFree = MENU_CLIENT_COMMAND:New( self.EscortClient, "Weapon Free", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEWeaponFree(), ParamMessage = "Opening fire on targets of opportunity!" } ) + self.EscortMenuROEWeaponFree = MENU_CLIENT_COMMAND:New( self.EscortClient, "Weapon Free", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEWeaponFree(), "Opening fire on targets of opportunity!" ) end end @@ -624,16 +648,16 @@ function ESCORT:MenuEvasion( MenuTextFormat ) -- Reaction to Threats self.EscortMenuEvasion = MENU_CLIENT:New( self.EscortClient, "Evasion", self.EscortMenu ) if self.EscortGroup:OptionROTNoReactionPossible() then - self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTNoReaction(), ParamMessage = "Fighting until death!" } ) + self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTNoReaction(), "Fighting until death!" ) end if self.EscortGroup:OptionROTPassiveDefensePossible() then - self.EscortMenuEvasionPassiveDefense = MENU_CLIENT_COMMAND:New( self.EscortClient, "Use flares, chaff and jammers", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTPassiveDefense(), ParamMessage = "Defending using jammers, chaff and flares!" } ) + self.EscortMenuEvasionPassiveDefense = MENU_CLIENT_COMMAND:New( self.EscortClient, "Use flares, chaff and jammers", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTPassiveDefense(), "Defending using jammers, chaff and flares!" ) end if self.EscortGroup:OptionROTEvadeFirePossible() then - self.EscortMenuEvasionEvadeFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Evade enemy fire", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTEvadeFire(), ParamMessage = "Evading on enemy fire!" } ) + self.EscortMenuEvasionEvadeFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Evade enemy fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTEvadeFire(), "Evading on enemy fire!" ) end if self.EscortGroup:OptionROTVerticalPossible() then - self.EscortMenuOptionEvasionVertical = MENU_CLIENT_COMMAND:New( self.EscortClient, "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTVertical(), ParamMessage = "Evading on enemy fire with vertical manoeuvres!" } ) + self.EscortMenuOptionEvasionVertical = MENU_CLIENT_COMMAND:New( self.EscortClient, "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTVertical(), "Evading on enemy fire with vertical manoeuvres!" ) end end end @@ -658,18 +682,14 @@ end --- @param #MENUPARAM MenuParam -function ESCORT._HoldPosition( MenuParam ) +function ESCORT:_HoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local OrbitGroup = MenuParam.ParamOrbitGroup -- Wrapper.Group#GROUP local OrbitUnit = OrbitGroup:GetUnit(1) -- Wrapper.Unit#UNIT - local OrbitHeight = MenuParam.ParamHeight - local OrbitSeconds = MenuParam.ParamSeconds -- Not implemented yet - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) local PointFrom = {} local GroupVec3 = EscortGroup:GetUnit(1):GetVec3() @@ -702,13 +722,12 @@ function ESCORT._HoldPosition( MenuParam ) end --- @param #MENUPARAM MenuParam -function ESCORT._JoinUpAndFollow( MenuParam ) +function ESCORT:_JoinUpAndFollow( Distance ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - self.Distance = MenuParam.ParamDistance + self.Distance = Distance self:JoinUpAndFollow( EscortGroup, EscortClient, self.Distance ) end @@ -721,7 +740,7 @@ end function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) self:F( { EscortGroup, EscortClient, Distance } ) - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) EscortGroup:OptionROEHoldFire() EscortGroup:OptionROTPassiveDefense() @@ -730,44 +749,35 @@ function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) self.CT1 = 0 self.GT1 = 0 - self.FollowScheduler:Start() + self.FollowScheduler:Start( self.FollowSchedule ) EscortGroup:MessageToClient( "Rejoining and Following at " .. Distance .. "!", 30, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._Flare( MenuParam ) +function ESCORT:_Flare( Color, Message ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local Color = MenuParam.ParamColor - local Message = MenuParam.ParamMessage - EscortGroup:GetUnit(1):Flare( Color ) EscortGroup:MessageToClient( Message, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._Smoke( MenuParam ) +function ESCORT:_Smoke( Color, Message ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local Color = MenuParam.ParamColor - local Message = MenuParam.ParamMessage - EscortGroup:GetUnit(1):Smoke( Color ) EscortGroup:MessageToClient( Message, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ReportNearbyTargetsNow( MenuParam ) +function ESCORT:_ReportNearbyTargetsNow() - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient @@ -775,17 +785,16 @@ function ESCORT._ReportNearbyTargetsNow( MenuParam ) end -function ESCORT._SwitchReportNearbyTargets( MenuParam ) +function ESCORT:_SwitchReportNearbyTargets( ReportTargets ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - self.ReportTargets = MenuParam.ParamReportTargets + self.ReportTargets = ReportTargets if self.ReportTargets then if not self.ReportTargetsScheduler then - self.ReportTargetsScheduler = SCHEDULER:New( self, self._ReportTargetsScheduler, {}, 1, 30 ) + self.ReportTargetsScheduler:Schedule( self, self._ReportTargetsScheduler, {}, 1, 30 ) end else routines.removeFunction( self.ReportTargetsScheduler ) @@ -794,40 +803,31 @@ function ESCORT._SwitchReportNearbyTargets( MenuParam ) end --- @param #MENUPARAM MenuParam -function ESCORT._ScanTargets( MenuParam ) +function ESCORT:_ScanTargets( ScanDuration ) - local self = MenuParam.ParamSelf - local EscortGroup = self.EscortGroup + local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP local EscortClient = self.EscortClient - local ScanDuration = MenuParam.ParamScanDuration - - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroup:IsHelicopter() then - SCHEDULER:New( EscortGroup, EscortGroup.PushTask, - { EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 200, 20 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ) - }, - 1 - ) + EscortGroup:PushTask( + EscortGroup:TaskControlled( + EscortGroup:TaskOrbitCircle( 200, 20 ), + EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) + ), 1 ) elseif EscortGroup:IsAirPlane() then - SCHEDULER:New( EscortGroup, EscortGroup.PushTask, - { EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 1000, 500 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ) - }, - 1 - ) + EscortGroup:PushTask( + EscortGroup:TaskControlled( + EscortGroup:TaskOrbitCircle( 1000, 500 ), + EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) + ), 1 ) end EscortGroup:MessageToClient( "Scanning targets for " .. ScanDuration .. " seconds.", ScanDuration, EscortClient ) if self.EscortMode == ESCORT.MODE.FOLLOW then - self.FollowScheduler:Start() + self.FollowScheduler:Start( self.FollowSchedule ) end end @@ -844,124 +844,157 @@ function _Resume( EscortGroup ) end ---- @param #MENUPARAM MenuParam -function ESCORT._AttackTarget( MenuParam ) +--- @param #ESCORT self +-- @param #number DetectedItemID +function ESCORT:_AttackTarget( DetectedItemID ) - local self = MenuParam.ParamSelf - local EscortGroup = self.EscortGroup + local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP + self:E( EscortGroup ) local EscortClient = self.EscortClient - local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT - self.FollowScheduler:Stop() - - self:T( AttackUnit ) + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroup:IsAir() then EscortGroup:OptionROEOpenFire() EscortGroup:OptionROTPassiveDefense() EscortGroup:SetState( EscortGroup, "Escort", self ) - SCHEDULER:New( EscortGroup, - EscortGroup.PushTask, - { EscortGroup:TaskCombo( - { EscortGroup:TaskAttackUnit( AttackUnit ), - EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit, Tasks ) + if DetectedUnit:IsAlive() then + Tasks[#Tasks+1] = EscortGroup:TaskAttackUnit( DetectedUnit ) + end + end, Tasks + ) + + Tasks[#Tasks+1] = EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } ) + + EscortGroup:SetTask( + EscortGroup:TaskCombo( + Tasks + ), 1 ) + else - SCHEDULER:New( EscortGroup, - EscortGroup.PushTask, - { EscortGroup:TaskCombo( - { EscortGroup:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit, Tasks ) + if DetectedUnit:IsAlive() then + Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) + end + end, Tasks + ) + + EscortGroup:SetTask( + EscortGroup:TaskCombo( + Tasks + ), 1 ) + end EscortGroup:MessageToClient( "Engaging Designated Unit!", 10, EscortClient ) end ---- @param #MENUPARAM MenuParam -function ESCORT._AssistTarget( MenuParam ) +--- +-- @param #number DetectedItemID +function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItemID ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortGroupAttack = MenuParam.ParamEscortGroup - local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT - self.FollowScheduler:Stop() - - self:T( AttackUnit ) + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroupAttack:IsAir() then EscortGroupAttack:OptionROEOpenFire() EscortGroupAttack:OptionROTVertical() - SCHDULER:New( EscortGroupAttack, - EscortGroupAttack.PushTask, - { EscortGroupAttack:TaskCombo( - { EscortGroupAttack:TaskAttackUnit( AttackUnit ), - EscortGroupAttack:TaskOrbitCircle( 500, 350 ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit, Tasks ) + if DetectedUnit:IsAlive() then + Tasks[#Tasks+1] = EscortGroupAttack:TaskAttackUnit( DetectedUnit ) + end + end, Tasks + ) + + Tasks[#Tasks+1] = EscortGroupAttack:TaskOrbitCircle( 500, 350 ) + + EscortGroupAttack:SetTask( + EscortGroupAttack:TaskCombo( + Tasks + ), 1 ) + else - SCHEDULER:New( EscortGroupAttack, - EscortGroupAttack.PushTask, - { EscortGroupAttack:TaskCombo( - { EscortGroupAttack:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) - } - ) - }, 10 + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit, Tasks ) + if DetectedUnit:IsAlive() then + Tasks[#Tasks+1] = EscortGroupAttack:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) + end + end, Tasks + ) + + EscortGroupAttack:SetTask( + EscortGroupAttack:TaskCombo( + Tasks + ), 1 ) + end + EscortGroupAttack:MessageToClient( "Assisting with the destroying the enemy unit!", 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ROE( MenuParam ) +function ESCORT:_ROE( EscortROEFunction, EscortROEMessage ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortROEFunction = MenuParam.ParamFunction - local EscortROEMessage = MenuParam.ParamMessage - pcall( function() EscortROEFunction() end ) EscortGroup:MessageToClient( EscortROEMessage, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ROT( MenuParam ) +function ESCORT:_ROT( EscortROTFunction, EscortROTMessage ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortROTFunction = MenuParam.ParamFunction - local EscortROTMessage = MenuParam.ParamMessage - pcall( function() EscortROTFunction() end ) EscortGroup:MessageToClient( EscortROTMessage, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ResumeMission( MenuParam ) +function ESCORT:_ResumeMission( WayPoint ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local WayPoint = MenuParam.ParamWayPoint - - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) local WayPoints = EscortGroup:GetTaskRoute() self:T( WayPoint, WayPoints ) @@ -1105,176 +1138,244 @@ function ESCORT:_ReportTargetsScheduler() self:F( self.EscortGroup:GetName() ) if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then - local EscortGroupName = self.EscortGroup:GetName() - local EscortTargets = self.EscortGroup:GetDetectedTargets() - local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets + if true then - local EscortTargetMessages = "" - for EscortTargetID, EscortTarget in pairs( EscortTargets ) do - local EscortObject = EscortTarget.object - self:T( EscortObject ) - if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then + local EscortGroupName = self.EscortGroup:GetName() + + self.EscortMenuAttackNearbyTargets:RemoveSubMenus() - local EscortTargetUnit = UNIT:Find( EscortObject ) - local EscortTargetUnitName = EscortTargetUnit:GetName() - - - - -- local EscortTargetIsDetected, - -- EscortTargetIsVisible, - -- EscortTargetLastTime, - -- EscortTargetKnowType, - -- EscortTargetKnowDistance, - -- EscortTargetLastPos, - -- EscortTargetLastVelocity - -- = self.EscortGroup:IsTargetDetected( EscortObject ) - -- - -- self:T( { EscortTargetIsDetected, - -- EscortTargetIsVisible, - -- EscortTargetLastTime, - -- EscortTargetKnowType, - -- EscortTargetKnowDistance, - -- EscortTargetLastPos, - -- EscortTargetLastVelocity } ) - - - local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3() - local EscortVec3 = self.EscortGroup:GetVec3() - local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + - ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + - ( EscortTargetUnitVec3.z - EscortVec3.z )^2 - ) ^ 0.5 / 1000 - - self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } ) - - if Distance <= 15 then - - if not ClientEscortTargets[EscortTargetUnitName] then - ClientEscortTargets[EscortTargetUnitName] = {} - end - ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit - ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible - ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type - ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance - else - if ClientEscortTargets[EscortTargetUnitName] then - ClientEscortTargets[EscortTargetUnitName] = nil - end - end + if self.EscortMenuTargetAssistance then + self.EscortMenuTargetAssistance:RemoveSubMenus() end - end - self:T( { "Sorting Targets Table:", ClientEscortTargets } ) - table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end ) - self:T( { "Sorted Targets Table:", ClientEscortTargets } ) + local DetectedItems = self.Detection:GetDetectedItems() + self:E( DetectedItems ) - -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. - self.EscortMenuAttackNearbyTargets:RemoveSubMenus() + local DetectedTargets = false + + local DetectedMsgs = {} + + for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do - if self.EscortMenuTargetAssistance then - self.EscortMenuTargetAssistance:RemoveSubMenus() - end + local ClientEscortTargets = EscortGroupData.Detection - --for MenuIndex = 1, #self.EscortMenuAttackTargets do - -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } ) - -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove() - --end + for DetectedItemID, DetectedItem in ipairs( DetectedItems ) do + self:E( { DetectedItemID, DetectedItem } ) + -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. + + local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID ) - - if ClientEscortTargets then - for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do - - for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do - - if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then - - local EscortTargetMessage = "" - local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName() - local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName() - if ClientEscortTargetData.type then - EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at " - else - EscortTargetMessage = EscortTargetMessage .. "Unknown target at " - end - - local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3() - local EscortVec3 = self.EscortGroup:GetVec3() - local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + - ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + - ( EscortTargetUnitVec3.z - EscortVec3.z )^2 - ) ^ 0.5 / 1000 - - self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } ) - if ClientEscortTargetData.visible == false then - EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km" - else - EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km" - end - - if ClientEscortTargetData.visible then - EscortTargetMessage = EscortTargetMessage .. ", visual" - end - - if ClientEscortGroupName == EscortGroupName then - - MENU_CLIENT_COMMAND:New( self.EscortClient, - EscortTargetMessage, - self.EscortMenuAttackNearbyTargets, - ESCORT._AttackTarget, - { ParamSelf = self, - ParamUnit = ClientEscortTargetData.AttackUnit - } - ) - EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage - else - if self.EscortMenuTargetAssistance then - local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) - MENU_CLIENT_COMMAND:New( self.EscortClient, - EscortTargetMessage, - MenuTargetAssistance, - ESCORT._AssistTarget, - { ParamSelf = self, - ParamEscortGroup = EscortGroupData.EscortGroup, - ParamUnit = ClientEscortTargetData.AttackUnit - } - ) - end - end + if ClientEscortGroupName == EscortGroupName then + + DetectedMsgs[#DetectedMsgs+1] = DetectedItemReportSummary + + MENU_CLIENT_COMMAND:New( self.EscortClient, + DetectedItemReportSummary, + self.EscortMenuAttackNearbyTargets, + ESCORT._AttackTarget, + self, + DetectedItemID + ) else - ClientEscortTargetData = nil + if self.EscortMenuTargetAssistance then + + self:T( DetectedItemReportSummary ) + local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) + MENU_CLIENT_COMMAND:New( self.EscortClient, + DetectedItemReportSummary, + MenuTargetAssistance, + ESCORT._AssistTarget, + self, + EscortGroupData.EscortGroup, + DetectedItemID + ) + end end + + DetectedTargets = true + end end - - if EscortTargetMessages ~= "" and self.ReportTargets == true then - self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient ) + self:E( DetectedMsgs ) + if DetectedTargets then + self.EscortGroup:MessageToClient( "Detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient ) else - self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient ) + self.EscortGroup:MessageToClient( "No targets detected.", 10, self.EscortClient ) end + + return true + else +-- local EscortGroupName = self.EscortGroup:GetName() +-- local EscortTargets = self.EscortGroup:GetDetectedTargets() +-- +-- local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets +-- +-- local EscortTargetMessages = "" +-- for EscortTargetID, EscortTarget in pairs( EscortTargets ) do +-- local EscortObject = EscortTarget.object +-- self:T( EscortObject ) +-- if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then +-- +-- local EscortTargetUnit = UNIT:Find( EscortObject ) +-- local EscortTargetUnitName = EscortTargetUnit:GetName() +-- +-- +-- +-- -- local EscortTargetIsDetected, +-- -- EscortTargetIsVisible, +-- -- EscortTargetLastTime, +-- -- EscortTargetKnowType, +-- -- EscortTargetKnowDistance, +-- -- EscortTargetLastPos, +-- -- EscortTargetLastVelocity +-- -- = self.EscortGroup:IsTargetDetected( EscortObject ) +-- -- +-- -- self:T( { EscortTargetIsDetected, +-- -- EscortTargetIsVisible, +-- -- EscortTargetLastTime, +-- -- EscortTargetKnowType, +-- -- EscortTargetKnowDistance, +-- -- EscortTargetLastPos, +-- -- EscortTargetLastVelocity } ) +-- +-- +-- local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3() +-- local EscortVec3 = self.EscortGroup:GetVec3() +-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + +-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + +-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2 +-- ) ^ 0.5 / 1000 +-- +-- self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } ) +-- +-- if Distance <= 15 then +-- +-- if not ClientEscortTargets[EscortTargetUnitName] then +-- ClientEscortTargets[EscortTargetUnitName] = {} +-- end +-- ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit +-- ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible +-- ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type +-- ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance +-- else +-- if ClientEscortTargets[EscortTargetUnitName] then +-- ClientEscortTargets[EscortTargetUnitName] = nil +-- end +-- end +-- end +-- end +-- +-- self:T( { "Sorting Targets Table:", ClientEscortTargets } ) +-- table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end ) +-- self:T( { "Sorted Targets Table:", ClientEscortTargets } ) +-- +-- -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. +-- self.EscortMenuAttackNearbyTargets:RemoveSubMenus() +-- +-- if self.EscortMenuTargetAssistance then +-- self.EscortMenuTargetAssistance:RemoveSubMenus() +-- end +-- +-- --for MenuIndex = 1, #self.EscortMenuAttackTargets do +-- -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } ) +-- -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove() +-- --end +-- +-- +-- if ClientEscortTargets then +-- for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do +-- +-- for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do +-- +-- if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then +-- +-- local EscortTargetMessage = "" +-- local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName() +-- local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName() +-- if ClientEscortTargetData.type then +-- EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at " +-- else +-- EscortTargetMessage = EscortTargetMessage .. "Unknown target at " +-- end +-- +-- local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3() +-- local EscortVec3 = self.EscortGroup:GetVec3() +-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + +-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + +-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2 +-- ) ^ 0.5 / 1000 +-- +-- self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } ) +-- if ClientEscortTargetData.visible == false then +-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km" +-- else +-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km" +-- end +-- +-- if ClientEscortTargetData.visible then +-- EscortTargetMessage = EscortTargetMessage .. ", visual" +-- end +-- +-- if ClientEscortGroupName == EscortGroupName then +-- +-- MENU_CLIENT_COMMAND:New( self.EscortClient, +-- EscortTargetMessage, +-- self.EscortMenuAttackNearbyTargets, +-- ESCORT._AttackTarget, +-- { ParamSelf = self, +-- ParamUnit = ClientEscortTargetData.AttackUnit +-- } +-- ) +-- EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage +-- else +-- if self.EscortMenuTargetAssistance then +-- local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) +-- MENU_CLIENT_COMMAND:New( self.EscortClient, +-- EscortTargetMessage, +-- MenuTargetAssistance, +-- ESCORT._AssistTarget, +-- self, +-- EscortGroupData.EscortGroup, +-- ClientEscortTargetData.AttackUnit +-- ) +-- end +-- end +-- else +-- ClientEscortTargetData = nil +-- end +-- end +-- end +-- +-- if EscortTargetMessages ~= "" and self.ReportTargets == true then +-- self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient ) +-- else +-- self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient ) +-- end +-- end +-- +-- if self.EscortMenuResumeMission then +-- self.EscortMenuResumeMission:RemoveSubMenus() +-- +-- -- if self.EscortMenuResumeWayPoints then +-- -- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do +-- -- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } ) +-- -- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove() +-- -- end +-- -- end +-- +-- local TaskPoints = self:RegisterRoute() +-- for WayPointID, WayPoint in pairs( TaskPoints ) do +-- local EscortVec3 = self.EscortGroup:GetVec3() +-- local Distance = ( ( WayPoint.x - EscortVec3.x )^2 + +-- ( WayPoint.y - EscortVec3.z )^2 +-- ) ^ 0.5 / 1000 +-- MENU_CLIENT_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } ) +-- end +-- end +-- +-- return true end - - if self.EscortMenuResumeMission then - self.EscortMenuResumeMission:RemoveSubMenus() - - -- if self.EscortMenuResumeWayPoints then - -- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do - -- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } ) - -- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove() - -- end - -- end - - local TaskPoints = self:RegisterRoute() - for WayPointID, WayPoint in pairs( TaskPoints ) do - local EscortVec3 = self.EscortGroup:GetVec3() - local Distance = ( ( WayPoint.x - EscortVec3.x )^2 + - ( WayPoint.y - EscortVec3.z )^2 - ) ^ 0.5 / 1000 - MENU_CLIENT_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } ) - end - end - - return true end return false diff --git a/Moose Development/Moose/Functional/MissileTrainer.lua b/Moose Development/Moose/Functional/MissileTrainer.lua index 556690aed..511cbca44 100644 --- a/Moose Development/Moose/Functional/MissileTrainer.lua +++ b/Moose Development/Moose/Functional/MissileTrainer.lua @@ -171,7 +171,7 @@ function MISSILETRAINER:New( Distance, Briefing ) self.Distance = Distance / 1000 - _EVENTDISPATCHER:OnShot( self._EventShot, self ) + self:HandleEvent( EVENTS.Shot ) self.DBClients = SET_CLIENT:New():FilterStart() @@ -449,14 +449,14 @@ end --- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. -- @param #MISSILETRAINER self --- @param Core.Event#EVENTDATA Event -function MISSILETRAINER:_EventShot( Event ) - self:F( { Event } ) +-- @param Core.Event#EVENTDATA EventData +function MISSILETRAINER:OnEventShot( EVentData ) + self:F( { EVentData } ) - local TrainerSourceDCSUnit = Event.IniDCSUnit - local TrainerSourceDCSUnitName = Event.IniDCSUnitName - local TrainerWeapon = Event.Weapon -- Identify the weapon fired - local TrainerWeaponName = Event.WeaponName -- return weapon type + local TrainerSourceDCSUnit = EVentData.IniDCSUnit + local TrainerSourceDCSUnitName = EVentData.IniDCSUnitName + local TrainerWeapon = EVentData.Weapon -- Identify the weapon fired + local TrainerWeaponName = EVentData.WeaponName -- return weapon type self:T( "Missile Launched = " .. TrainerWeaponName ) diff --git a/Moose Development/Moose/Functional/Movement.lua b/Moose Development/Moose/Functional/Movement.lua index 9ea15f0ae..72bc21c62 100644 --- a/Moose Development/Moose/Functional/Movement.lua +++ b/Moose Development/Moose/Functional/Movement.lua @@ -3,10 +3,11 @@ -- Performance: If in a DCSRTE there are a lot of moving GROUND units, then in a multi player mission, this WILL create lag if -- the main DCS execution core of your CPU is fully utilized. So, this class will limit the amount of simultaneous moving GROUND units -- on defined intervals (currently every minute). --- @module MOVEMENT +-- @module Movement --- the MOVEMENT class --- @type +-- @type MOVEMENT +-- @extends Core.Base#BASE MOVEMENT = { ClassName = "MOVEMENT", } @@ -20,7 +21,7 @@ MOVEMENT = { -- Movement_US_Platoons = MOVEMENT:New( { 'US Tank Platoon Left', 'US Tank Platoon Middle', 'US Tank Platoon Right', 'US CH-47D Troops' }, 15 ) function MOVEMENT:New( MovePrefixes, MoveMaximum ) - local self = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, BASE:New() ) -- #MOVEMENT self:F( { MovePrefixes, MoveMaximum } ) if type( MovePrefixes ) == 'table' then @@ -33,7 +34,7 @@ function MOVEMENT:New( MovePrefixes, MoveMaximum ) self.AliveUnits = 0 -- Contains the counter how many units are currently alive self.MoveUnits = {} -- Reflects if the Moving for this MovePrefixes is going to be scheduled or not. - _EVENTDISPATCHER:OnBirth( self.OnBirth, self ) + self:HandleEvent( EVENTS.Birth ) -- self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth ) -- @@ -60,24 +61,26 @@ end --- Captures the birth events when new Units were spawned. -- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. -function MOVEMENT:OnBirth( Event ) - self:F( { Event } ) +-- @param #MOVEMENT self +-- @param Core.Event#EVENTDATA self +function MOVEMENT:OnEventBirth( EventData ) + self:F( { EventData } ) if timer.getTime0() < timer.getAbsTime() then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line - if Event.IniDCSUnit then - self:T( "Birth object : " .. Event.IniDCSUnitName ) - if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then + if EventData.IniDCSUnit then + self:T( "Birth object : " .. EventData.IniDCSUnitName ) + if EventData.IniDCSGroup and EventData.IniDCSGroup:isExist() then for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do - if string.find( Event.IniDCSUnitName, MovePrefix, 1, true ) then + if string.find( EventData.IniDCSUnitName, MovePrefix, 1, true ) then self.AliveUnits = self.AliveUnits + 1 - self.MoveUnits[Event.IniDCSUnitName] = Event.IniDCSGroupName + self.MoveUnits[EventData.IniDCSUnitName] = EventData.IniDCSGroupName self:T( self.AliveUnits ) end end end end - _EVENTDISPATCHER:OnCrashForUnit( Event.IniDCSUnitName, self.OnDeadOrCrash, self ) - _EVENTDISPATCHER:OnDeadForUnit( Event.IniDCSUnitName, self.OnDeadOrCrash, self ) + + EventData.IniUnit:HandleEvent( EVENTS.DEAD, self.OnDeadOrCrash ) end end diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index e58adeac2..244b36957 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -5,8 +5,6 @@ -- -- === -- --- # 1) @{Scoring#SCORING} class, extends @{Base#BASE} --- -- The @{#SCORING} class administers the scoring of player achievements, -- and creates a CSV file logging the scoring events and results for use at team or squadron websites. -- @@ -57,6 +55,8 @@ -- Use the radio menu F10 to consult the scores while running the mission. -- Scores can be reported for your user, or an overall score can be reported of all players currently active in the mission. -- +-- # 1) @{Scoring#SCORING} class, extends @{Base#BASE} +-- -- ## 1.1) Set the destroy score or penalty scale -- -- Score scales can be set for scores granted when enemies or friendlies are destroyed. @@ -86,8 +86,6 @@ -- For example, this can be done as follows: -- -- Scoring:RemoveUnitScore( UNIT:FindByName( "Unit #001" ) ) --- --- -- -- ## 1.3) Define destruction zones that will give extra scores. -- @@ -546,6 +544,7 @@ function SCORING:_AddPlayerFromUnit( UnitData ) local UnitCategory = UnitDesc.category local UnitCoalition = UnitData:GetCoalition() local UnitTypeName = UnitData:GetTypeName() + local UnitThreatLevel, UnitThreatType = UnitData:GetThreatLevel() self:T( { PlayerName, UnitName, UnitCategory, UnitCoalition, UnitTypeName } ) @@ -586,6 +585,8 @@ function SCORING:_AddPlayerFromUnit( UnitData ) self.Players[PlayerName].UnitCategory = UnitCategory self.Players[PlayerName].UnitType = UnitTypeName self.Players[PlayerName].UNIT = UnitData + self.Players[PlayerName].ThreatLevel = UnitThreatLevel + self.Players[PlayerName].ThreatType = UnitThreatType if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 then if self.Players[PlayerName].PenaltyWarning < 1 then @@ -844,6 +845,7 @@ function SCORING:_EventOnHit( Event ) PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT + PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() -- Only grant hit scores if there was more than one second between the last hit. if timer.getTime() - PlayerHit.TimeStamp > 1 then @@ -882,7 +884,7 @@ function SCORING:_EventOnHit( Event ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) end - self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else Player.Score = Player.Score + 1 PlayerHit.Score = PlayerHit.Score + 1 @@ -915,7 +917,7 @@ function SCORING:_EventOnHit( Event ) ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - self:ScoreCSV( InitPlayerName, "", "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) + self:ScoreCSV( InitPlayerName, "", "HIT_SCORE", 1, 0, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) end end end @@ -923,6 +925,89 @@ function SCORING:_EventOnHit( Event ) elseif InitPlayerName == nil then -- It is an AI hitting a player??? end + + -- It is a weapon initiated by a player, that is hitting something + -- This seems to occur only with scenery and static objects. + if Event.WeaponPlayerName ~= nil then + self:_AddPlayerFromUnit( Event.WeaponUNIT ) + if self.Players[Event.WeaponPlayerName] then -- This should normally not happen, but i'll test it anyway. + if TargetPlayerName ~= nil then -- It is a player hitting another player ... + self:_AddPlayerFromUnit( TargetUNIT ) + end + + self:T( "Hitting Scenery" ) + + -- What is he hitting? + if TargetCategory then + + -- A scenery or static got hit, score it. + -- Player contains the score data from self.Players[WeaponPlayerName] + local Player = self.Players[Event.WeaponPlayerName] + + -- Ensure there is a hit table per TargetCategory and TargetUnitName. + Player.Hit[TargetCategory] = Player.Hit[TargetCategory] or {} + Player.Hit[TargetCategory][TargetUnitName] = Player.Hit[TargetCategory][TargetUnitName] or {} + + -- PlayerHit contains the score counters and data per unit that was hit. + local PlayerHit = Player.Hit[TargetCategory][TargetUnitName] + + PlayerHit.Score = PlayerHit.Score or 0 + PlayerHit.Penalty = PlayerHit.Penalty or 0 + PlayerHit.ScoreHit = PlayerHit.ScoreHit or 0 + PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 + PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 + PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT + PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() + + -- Only grant hit scores if there was more than one second between the last hit. + if timer.getTime() - PlayerHit.TimeStamp > 1 then + PlayerHit.TimeStamp = timer.getTime() + + local Score = 0 + + if InitCoalition then -- A coalition object was hit, probably a static. + if InitCoalition == TargetCoalition then + -- TODO: Penalty according scale + Player.Penalty = Player.Penalty + 10 + PlayerHit.Penalty = PlayerHit.Penalty + 10 + PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 + + MESSAGE + :New( "Player '" .. Event.WeaponPlayerName .. "' hit a friendly target " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. + "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + else + Player.Score = Player.Score + 1 + PlayerHit.Score = PlayerHit.Score + 1 + PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 + MESSAGE + :New( "Player '" .. Event.WeaponPlayerName .. "' hit an enemy target " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. + "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + end + else -- A scenery object was hit. + MESSAGE + :New( "Player '" .. Event.WeaponPlayerName .. "' hit a scenery object.", + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + self:ScoreCSV( Event.WeaponPlayerName, "", "HIT_SCORE", 1, 0, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, "", "Scenery", TargetUnitType ) + end + end + end + end + end end --- Track DEAD or CRASH events for the scoring. @@ -979,8 +1064,13 @@ function SCORING:_EventOnDeadOrCrash( Event ) self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) + local Destroyed = false + -- What is the player destroying? - if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? + if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 then -- Was there a hit for this unit for this player before registered??? + + local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel + local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType Player.Destroy[TargetCategory] = Player.Destroy[TargetCategory] or {} Player.Destroy[TargetCategory][TargetType] = Player.Destroy[TargetCategory][TargetType] or {} @@ -994,8 +1084,9 @@ function SCORING:_EventOnDeadOrCrash( Event ) if TargetCoalition then if InitCoalition == TargetCoalition then - local ThreatLevelTarget, ThreatTypeTarget = TargetUnit:GetThreatLevel() - local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 + local ThreatLevelTarget = TargetThreatLevel + local ThreatTypeTarget = TargetThreatType + local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1 local ThreatPenalty = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyPenalty / 10 ) self:E( { ThreatLevel = ThreatPenalty, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) @@ -1022,11 +1113,14 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) end + + Destroyed = true self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else - - local ThreatLevelTarget, ThreatTypeTarget = TargetUnit:GetThreatLevel() - local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 + + local ThreatLevelTarget = TargetThreatLevel + local ThreatTypeTarget = TargetThreatType + local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1 local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 ) self:E( { ThreatLevel = ThreatScore, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) @@ -1053,6 +1147,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) end + Destroyed = true self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, ThreatScore, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) local UnitName = TargetUnit:GetName() @@ -1068,6 +1163,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + Destroyed = true end -- Check if there are Zones where the destruction happened. @@ -1086,6 +1182,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + Destroyed = true end end @@ -1107,10 +1204,18 @@ function SCORING:_EventOnDeadOrCrash( Event ) ) :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) + Destroyed = true self:ScoreCSV( PlayerName, "", "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) end end end + + -- Delete now the hit cache if the target was destroyed. + -- Otherwise points will be granted every time a target gets killed by the players that hit that target. + -- This is only relevant for player to player destroys. + if Destroyed then + Player.Hit[TargetCategory][TargetUnitName].TimeStamp = 0 + end end end end diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index 3b05ac9a0..da4bf2d0f 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -36,25 +36,28 @@ function SEAD:New( SEADGroupPrefixes ) else self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes end - _EVENTDISPATCHER:OnShot( self.EventShot, self ) + + self:HandleEvent( EVENTS.Shot ) return self end --- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. -- @see SEAD -function SEAD:EventShot( Event ) - self:F( { Event } ) +-- @param #SEAD +-- @param Core.Event#EVENTDATA EventData +function SEAD:OnEventShot( EventData ) + self:F( { EventData } ) - local SEADUnit = Event.IniDCSUnit - local SEADUnitName = Event.IniDCSUnitName - local SEADWeapon = Event.Weapon -- Identify the weapon fired - local SEADWeaponName = Event.WeaponName -- return weapon type + local SEADUnit = EventData.IniDCSUnit + local SEADUnitName = EventData.IniDCSUnitName + local SEADWeapon = EventData.Weapon -- Identify the weapon fired + local SEADWeaponName = EventData.WeaponName -- return weapon type -- Start of the 2nd loop self:T( "Missile Launched = " .. SEADWeaponName ) if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD local _evade = math.random (1,100) -- random number for chance of evading action - local _targetMim = Event.Weapon:getTarget() -- Identify target + local _targetMim = EventData.Weapon:getTarget() -- Identify target local _targetMimname = Unit.getName(_targetMim) local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) local _targetMimgroupName = _targetMimgroup:getName() diff --git a/Moose Development/Moose/Functional/Spawn.lua b/Moose Development/Moose/Functional/Spawn.lua index 64d8855b1..55cdcbbdd 100644 --- a/Moose Development/Moose/Functional/Spawn.lua +++ b/Moose Development/Moose/Functional/Spawn.lua @@ -233,6 +233,8 @@ function SPAWN:New( SpawnTemplatePrefix ) error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) end + self:SetEventPriority( 5 ) + return self end @@ -274,6 +276,8 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix ) error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) end + self:SetEventPriority( 5 ) + return self end @@ -573,16 +577,15 @@ function SPAWN:InitArray( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) self.SpawnGroups[SpawnGroupID].Visible = true - _EVENTDISPATCHER:OnBirthForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnBirth, self ) - _EVENTDISPATCHER:OnCrashForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnDeadOrCrash, self ) - _EVENTDISPATCHER:OnDeadForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnDeadOrCrash, self ) - + self:HandleEvent( EVENTS.Birth, self._OnBirth ) + self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) if self.Repeat then - _EVENTDISPATCHER:OnTakeOffForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnTakeOff, self ) - _EVENTDISPATCHER:OnLandForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnLand, self ) + self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff ) + self:HandleEvent( EVENTS.Land, self._OnLand ) end if self.RepeatOnEngineShutDown then - _EVENTDISPATCHER:OnEngineShutDownForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnEngineShutDown, self ) + self:HandleEvent( EVENTS.EngineShutdown, self._OnEngineShutDown ) end self.SpawnGroups[SpawnGroupID].Group = _DATABASE:Spawn( self.SpawnGroups[SpawnGroupID].SpawnTemplate ) @@ -722,18 +725,16 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) end end - _EVENTDISPATCHER:OnBirthForTemplate( SpawnTemplate, self._OnBirth, self ) - _EVENTDISPATCHER:OnCrashForTemplate( SpawnTemplate, self._OnDeadOrCrash, self ) - _EVENTDISPATCHER:OnDeadForTemplate( SpawnTemplate, self._OnDeadOrCrash, self ) - + self:HandleEvent( EVENTS.Birth, self._OnBirth ) + self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) if self.Repeat then - _EVENTDISPATCHER:OnTakeOffForTemplate( SpawnTemplate, self._OnTakeOff, self ) - _EVENTDISPATCHER:OnLandForTemplate( SpawnTemplate, self._OnLand, self ) + self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff ) + self:HandleEvent( EVENTS.Land, self._OnLand ) end if self.RepeatOnEngineShutDown then - _EVENTDISPATCHER:OnEngineShutDownForTemplate( SpawnTemplate, self._OnEngineShutDown, self ) + self:HandleEvent( EVENTS.EngineShutdown, self._OnEngineShutDown ) end - self:T3( SpawnTemplate.name ) self.SpawnGroups[self.SpawnIndex].Group = _DATABASE:Spawn( SpawnTemplate ) @@ -744,6 +745,8 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) SpawnGroup:SetAIOnOff( self.AIOnOff ) end + + self:T3( SpawnTemplate.name ) -- If there is a SpawnFunction hook defined, call it. if self.SpawnFunctionHook then @@ -1115,27 +1118,6 @@ function SPAWN:GetGroupFromIndex( SpawnIndex ) end end ---- Get the group index from a DCSUnit. --- The method will search for a #-mark, and will return the index behind the #-mark of the DCSUnit. --- It will return nil of no prefix was found. --- @param #SPAWN self --- @param Dcs.DCSWrapper.Unit#Unit DCSUnit The @{DCSUnit} to be searched. --- @return #string The prefix --- @return #nil Nothing found -function SPAWN:_GetGroupIndexFromDCSUnit( DCSUnit ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, DCSUnit } ) - - local SpawnUnitName = ( DCSUnit and DCSUnit:getName() ) or nil - if SpawnUnitName then - local IndexString = string.match( SpawnUnitName, "#.*-" ):sub( 2, -2 ) - if IndexString then - local Index = tonumber( IndexString ) - return Index - end - end - - return nil -end --- Return the prefix of a SpawnUnit. -- The method will search for a #-mark, and will return the text before the #-mark. @@ -1144,49 +1126,28 @@ end -- @param Dcs.DCSWrapper.Unit#UNIT DCSUnit The @{DCSUnit} to be searched. -- @return #string The prefix -- @return #nil Nothing found -function SPAWN:_GetPrefixFromDCSUnit( DCSUnit ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, DCSUnit } ) +function SPAWN:_GetPrefixFromGroup( SpawnGroup ) + self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) - local DCSGroup = DCSUnit:getGroup() - local DCSUnitName = ( DCSGroup and DCSGroup:getName() ) or nil - if DCSUnitName then - local SpawnPrefix = string.match( DCSUnitName, ".*#" ) - if SpawnPrefix then - SpawnPrefix = SpawnPrefix:sub( 1, -2 ) - end - return SpawnPrefix - end - - return nil -end - ---- Return the group within the SpawnGroups collection with input a DCSUnit. --- @param #SPAWN self --- @param Dcs.DCSWrapper.Unit#Unit DCSUnit The @{DCSUnit} to be searched. --- @return Wrapper.Group#GROUP The Group --- @return #nil Nothing found -function SPAWN:_GetGroupFromDCSUnit( DCSUnit ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, DCSUnit } ) - - local SpawnPrefix = self:_GetPrefixFromDCSUnit( DCSUnit ) - - if self.SpawnTemplatePrefix == SpawnPrefix or ( self.SpawnAliasPrefix and self.SpawnAliasPrefix == SpawnPrefix ) then - local SpawnGroupIndex = self:_GetGroupIndexFromDCSUnit( DCSUnit ) - local SpawnGroup = self.SpawnGroups[SpawnGroupIndex].Group - self:T( SpawnGroup ) - return SpawnGroup - end - - return nil + local GroupName = SpawnGroup:GetName() + if GroupName then + local SpawnPrefix = string.match( GroupName, ".*#" ) + if SpawnPrefix then + SpawnPrefix = SpawnPrefix:sub( 1, -2 ) + end + return SpawnPrefix + end + + return nil end --- Get the index from a given group. -- The function will search the name of the group for a #, and will return the number behind the #-mark. function SPAWN:GetSpawnIndexFromGroup( SpawnGroup ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) + self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) - local IndexString = string.match( SpawnGroup:GetName(), "#.*$" ):sub( 2 ) + local IndexString = string.match( SpawnGroup:GetName(), "#(%d*)$" ):sub( 2 ) local Index = tonumber( IndexString ) self:T3( IndexString, Index ) @@ -1511,17 +1472,18 @@ end -- TODO Need to delete this... _DATABASE does this now ... --- @param #SPAWN self --- @param Core.Event#EVENTDATA Event -function SPAWN:_OnBirth( Event ) +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnBirth( EventData ) + self:F( self.SpawnTemplatePrefix ) - if timer.getTime0() < timer.getAbsTime() then - if Event.IniDCSUnit then - local EventPrefix = self:_GetPrefixFromDCSUnit( Event.IniDCSUnit ) - self:T( { "Birth Event:", EventPrefix, self.SpawnTemplatePrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self.AliveUnits = self.AliveUnits + 1 - self:T( "Alive Units: " .. self.AliveUnits ) - end + local SpawnGroup = EventData.IniGroup + + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "Birth Event:", EventPrefix, self.SpawnTemplatePrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + self.AliveUnits = self.AliveUnits + 1 + self:T( "Alive Units: " .. self.AliveUnits ) end end @@ -1531,13 +1493,15 @@ end -- @todo Need to delete this... _DATABASE does this now ... --- @param #SPAWN self --- @param Core.Event#EVENTDATA Event -function SPAWN:_OnDeadOrCrash( Event ) - self:F( self.SpawnTemplatePrefix, Event ) +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnDeadOrCrash( EventData ) + self:F( self.SpawnTemplatePrefix ) - if Event.IniDCSUnit then - local EventPrefix = self:_GetPrefixFromDCSUnit( Event.IniDCSUnit ) - self:T( { "Dead event: " .. EventPrefix, self.SpawnTemplatePrefix } ) + local SpawnGroup = EventData.IniGroup + + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "Dead event: " .. EventPrefix } ) if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then self.AliveUnits = self.AliveUnits - 1 self:T( "Alive Units: " .. self.AliveUnits ) @@ -1547,34 +1511,37 @@ end --- Will detect AIR Units taking off... When the event takes place, the spawned Group is registered as airborne... -- This is needed to ensure that Re-SPAWNing only is done for landed AIR Groups. --- @todo Need to test for AIR Groups only... -function SPAWN:_OnTakeOff( event ) - self:F( self.SpawnTemplatePrefix, event ) +-- @param #SPAWN self +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnTakeOff( EventData ) + self:F( self.SpawnTemplatePrefix ) - if event.initiator and event.initiator:getName() then - local SpawnGroup = self:_GetGroupFromDCSUnit( event.initiator ) - if SpawnGroup then - self:T( { "TakeOff event: " .. event.initiator:getName(), event } ) - self:T( "self.Landed = false" ) - self.Landed = false - end + local SpawnGroup = EventData.IniGroup + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "TakeOff event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + self:T( "self.Landed = false" ) + SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", false ) + end end end --- Will detect AIR Units landing... When the event takes place, the spawned Group is registered as landed. -- This is needed to ensure that Re-SPAWNing is only done for landed AIR Groups. --- @todo Need to test for AIR Groups only... -function SPAWN:_OnLand( event ) - self:F( self.SpawnTemplatePrefix, event ) +-- @param #SPAWN self +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnLand( EventData ) + self:F( self.SpawnTemplatePrefix ) - local SpawnUnit = event.initiator - if SpawnUnit and SpawnUnit:isExist() and Object.getCategory(SpawnUnit) == Object.Category.UNIT then - local SpawnGroup = self:_GetGroupFromDCSUnit( SpawnUnit ) - if SpawnGroup then - self:T( { "Landed event:" .. SpawnUnit:getName(), event } ) - self.Landed = true - self:T( "self.Landed = true" ) - if self.Landed and self.RepeatOnLanding then + local SpawnGroup = EventData.IniGroup + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "Land event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + -- TODO: Check if this is the last unit of the group that lands. + SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", true ) + if self.RepeatOnLanding then local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) self:T( { "Landed:", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) self:ReSpawn( SpawnGroupIndex ) @@ -1587,18 +1554,18 @@ end -- When the event takes place, and the method @{RepeatOnEngineShutDown} was called, the spawned Group will Re-SPAWN. -- But only when the Unit was registered to have landed. -- @param #SPAWN self --- @see _OnTakeOff --- @see _OnLand --- @todo Need to test for AIR Groups only... -function SPAWN:_OnEngineShutDown( event ) - self:F( self.SpawnTemplatePrefix, event ) +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnEngineShutDown( EventData ) + self:F( self.SpawnTemplatePrefix ) - local SpawnUnit = event.initiator - if SpawnUnit and SpawnUnit:isExist() and Object.getCategory(SpawnUnit) == Object.Category.UNIT then - local SpawnGroup = self:_GetGroupFromDCSUnit( SpawnUnit ) - if SpawnGroup then - self:T( { "EngineShutDown event: " .. SpawnUnit:getName(), event } ) - if self.Landed and self.RepeatOnEngineShutDown then + local SpawnGroup = EventData.IniGroup + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "EngineShutdown event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + -- todo: test if on the runway + local Landed = SpawnGroup:GetState( SpawnGroup, "Spawn_Landed" ) + if Landed and self.RepeatOnEngineShutDown then local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) self:T( { "EngineShutDown: ", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) self:ReSpawn( SpawnGroupIndex ) diff --git a/Moose Development/Moose/Moose.lua b/Moose Development/Moose/Moose.lua index 8b715ee9f..fe1d1e2bf 100644 --- a/Moose Development/Moose/Moose.lua +++ b/Moose Development/Moose/Moose.lua @@ -17,6 +17,7 @@ Include.File( "Core/Set" ) Include.File( "Core/Point" ) Include.File( "Core/Message" ) Include.File( "Core/Fsm" ) +Include.File( "Core/Radio" ) --- Wrapper Classes Include.File( "Wrapper/Object" ) @@ -59,7 +60,7 @@ Include.File( "Tasking/CommandCenter" ) Include.File( "Tasking/Mission" ) Include.File( "Tasking/Task" ) Include.File( "Tasking/DetectionManager" ) -Include.File( "Tasking/Task_SEAD" ) +Include.File( "Tasking/Task_A2G_Dispatcher") Include.File( "Tasking/Task_A2G" ) diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 967aa84b1..09ad2ab23 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -20,7 +20,9 @@ function REPORT:New( Title ) local self = BASE:Inherit( self, BASE:New() ) self.Report = {} - self.Report[#self.Report+1] = Title + if Title then + self.Report[#self.Report+1] = Title + end return self end @@ -31,11 +33,17 @@ end -- @return #REPORT function REPORT:Add( Text ) self.Report[#self.Report+1] = Text - return self.Report[#self.Report+1] + return self.Report[#self.Report] end -function REPORT:Text() - return table.concat( self.Report, "\n" ) +--- Produces the text of the report, taking into account an optional delimeter, which is \n by default. +-- @param #REPORT self +-- @param #string Delimiter (optional) A delimiter text. +-- @return #string The report text. +function REPORT:Text( Delimiter ) + Delimiter = Delimiter or "\n" + local ReportText = table.concat( self.Report, Delimiter ) or "" + return ReportText end --- The COMMANDCENTER class @@ -68,22 +76,23 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) self:HandleEvent( EVENTS.Birth, --- @param #COMMANDCENTER self - --- @param Core.Event#EVENTDATA EventData + -- @param Core.Event#EVENTDATA EventData function( self, EventData ) - self:E( { EventData } ) - local EventGroup = GROUP:Find( EventData.IniDCSGroup ) - if EventGroup and self:HasGroup( EventGroup ) then - local MenuReporting = MENU_GROUP:New( EventGroup, "Reporting", self.CommandCenterMenu ) - local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Summary Report", MenuReporting, self.ReportSummary, self, EventGroup ) - local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) - self:ReportSummary( EventGroup ) - end - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! - Mission:JoinUnit( PlayerUnit, PlayerGroup ) - Mission:ReportDetails() + if EventData.IniObjectCategory == 1 then + local EventGroup = GROUP:Find( EventData.IniDCSGroup ) + if EventGroup and self:HasGroup( EventGroup ) then + local MenuReporting = MENU_GROUP:New( EventGroup, "Reporting", self.CommandCenterMenu ) + local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Summary Report", MenuReporting, self.ReportSummary, self, EventGroup ) + local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) + self:ReportSummary( EventGroup ) + end + local PlayerUnit = EventData.IniUnit + for MissionID, Mission in pairs( self:GetMissions() ) do + local Mission = Mission -- Tasking.Mission#MISSION + local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! + Mission:JoinUnit( PlayerUnit, PlayerGroup ) + Mission:ReportDetails() + end end end @@ -193,17 +202,26 @@ function COMMANDCENTER:SetMenu() self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "Command Center (" .. self:GetName() .. ")" ) + local MenuTime = timer.getTime() for MissionID, Mission in pairs( self:GetMissions() ) do local Mission = Mission -- Tasking.Mission#MISSION - Mission:RemoveMenu() + Mission:SetMenu( MenuTime ) + end + + for MissionID, Mission in pairs( self:GetMissions() ) do + local Mission = Mission -- Tasking.Mission#MISSION + Mission:RemoveMenu( MenuTime ) end - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - Mission:SetMenu() - end end +--- Gets the commandcenter menu structure governed by the HQ command center. +-- @param #COMMANDCENTER self +-- @return Core.Menu#MENU_COALITION +function COMMANDCENTER:GetMenu() + self:F() + return self.CommandCenterMenu +end --- Checks of the COMMANDCENTER has a GROUP. -- @param #COMMANDCENTER self @@ -224,6 +242,14 @@ function COMMANDCENTER:HasGroup( MissionGroup ) return Has end +--- Send a CC message to the coalition of the CC. +-- @param #COMMANDCENTER self +function COMMANDCENTER:MessageToAll( Message ) + + self:GetPositionable():MessageToAll( Message, 20, self:GetName() ) + +end + --- Send a CC message to a GROUP. -- @param #COMMANDCENTER self -- @param #string Message @@ -231,7 +257,8 @@ end -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. function COMMANDCENTER:MessageToGroup( Message, TaskGroup, Name ) - local Prefix = Name and "@ Group (" .. Name .. "): " or '' + local Prefix = "@ Group" + Prefix = Prefix .. ( Name and " (" .. Name .. "): " or '' ) Message = Prefix .. Message self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() ) @@ -247,6 +274,7 @@ function COMMANDCENTER:MessageToCoalition( Message ) end + --- Report the status of all MISSIONs to a GROUP. -- Each Mission is listed, with an indication how many Tasks are still to be completed. -- @param #COMMANDCENTER self diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index b7bcc07b5..7601701a0 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -2,7 +2,7 @@ -- -- === -- --- 1) @{DetectionManager#DETECTION_MANAGER} class, extends @{Base#BASE} +-- 1) @{DetectionManager#DETECTION_MANAGER} class, extends @{Fsm#FSM} -- ==================================================================== -- The @{DetectionManager#DETECTION_MANAGER} class defines the core functions to report detected objects to groups. -- Reportings can be done in several manners, and it is up to the derived classes if DETECTION_MANAGER to model the reporting behaviour. @@ -34,23 +34,6 @@ -- ------------------------------- -- The @{DetectionManager#DETECTION_REPORTING.New}() method creates a new DETECTION_REPORTING instance. -- --- === --- --- 3) @{#DETECTION_DISPATCHER} class, extends @{#DETECTION_MANAGER} --- ================================================================ --- The @{#DETECTION_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of FAC (groups). --- The FAC will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. --- Find a summary below describing for which situation a task type is created: --- --- * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter. --- * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter. --- * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars. --- --- Other task types will follow... --- --- 3.1) DETECTION_DISPATCHER constructor: --- -------------------------------------- --- The @{#DETECTION_DISPATCHER.New}() method creates a new DETECTION_DISPATCHER instance. -- -- === -- @@ -65,7 +48,7 @@ do -- DETECTION MANAGER -- @type DETECTION_MANAGER -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. - -- @extends Base#BASE + -- @extends Core.Fsm#FSM DETECTION_MANAGER = { ClassName = "DETECTION_MANAGER", SetGroup = nil, @@ -80,17 +63,37 @@ do -- DETECTION MANAGER function DETECTION_MANAGER:New( SetGroup, Detection ) -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) -- Functional.Detection#DETECTION_MANAGER + local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_MANAGER self.SetGroup = SetGroup self.Detection = Detection + self:SetStartState( "Stopped" ) + self:AddTransition( "Stopped", "Start", "Started" ) + self:AddTransition( "Started", "Stop", "Stopped" ) + self:AddTransition( "Started", "Report", "Started" ) + self:SetReportInterval( 30 ) self:SetReportDisplayTime( 25 ) - + + Detection:__Start( 1 ) + return self end + function DETECTION_MANAGER:onafterStart( From, Event, To ) + self:Report() + end + + function DETECTION_MANAGER:onafterReport( From, Event, To ) + + self:E( "onafterReport" ) + + self:__Report( -self._ReportInterval ) + + self:ProcessDetected( self.Detection ) + end + --- Set the reporting time interval. -- @param #DETECTION_MANAGER self -- @param #number ReportInterval The interval in seconds when a report needs to be done. @@ -121,51 +124,14 @@ do -- DETECTION MANAGER return self._ReportDisplayTime end - - --- Reports the detected items to the @{Set#SET_GROUP}. -- @param #DETECTION_MANAGER self -- @param Functional.Detection#DETECTION_BASE Detection -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:ReportDetected( Detection ) - self:F2() + function DETECTION_MANAGER:ProcessDetected( Detection ) + self:E() end - - --- Schedule the FAC reporting. - -- @param #DETECTION_MANAGER self - -- @param #number DelayTime The delay in seconds to wait the reporting. - -- @param #number ReportInterval The repeat interval in seconds for the reporting to happen repeatedly. - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:Schedule( DelayTime, ReportInterval ) - self:F2() - - self._ScheduleDelayTime = DelayTime - - self:SetReportInterval( ReportInterval ) - - self.FacScheduler = SCHEDULER:New(self, self._FacScheduler, { self, "DetectionManager" }, self._ScheduleDelayTime, self._ReportInterval ) - return self - end - - --- Report the detected @{Unit#UNIT}s detected within the @{Detection#DETECTION_BASE} object to the @{Set#SET_GROUP}s. - -- @param #DETECTION_MANAGER self - function DETECTION_MANAGER:_FacScheduler( SchedulerName ) - self:F2( { SchedulerName } ) - - return self:ProcessDetected( self.Detection ) - --- self.SetGroup:ForEachGroup( --- --- @param Wrapper.Group#GROUP Group --- function( Group ) --- if Group:IsAlive() then --- return self:ProcessDetected( self.Detection ) --- end --- end --- ) - --- return true - end end @@ -250,259 +216,3 @@ do -- DETECTION_REPORTING end -do -- DETECTION_DISPATCHER - - --- DETECTION_DISPATCHER class. - -- @type DETECTION_DISPATCHER - -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. - -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. - -- @field Tasking.Mission#MISSION Mission - -- @field Wrapper.Group#GROUP CommandCenter - -- @extends Tasking.DetectionManager#DETECTION_MANAGER - DETECTION_DISPATCHER = { - ClassName = "DETECTION_DISPATCHER", - Mission = nil, - CommandCenter = nil, - Detection = nil, - } - - - --- DETECTION_DISPATCHER constructor. - -- @param #DETECTION_DISPATCHER self - -- @param Set#SET_GROUP SetGroup - -- @param Functional.Detection#DETECTION_BASE Detection - -- @return #DETECTION_DISPATCHER self - function DETECTION_DISPATCHER:New( Mission, CommandCenter, SetGroup, Detection ) - - -- Inherits from DETECTION_MANAGER - local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #DETECTION_DISPATCHER - - self.Detection = Detection - self.CommandCenter = CommandCenter - self.Mission = Mission - - self:Schedule( 30 ) - return self - end - - - --- Creates a SEAD task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea - -- @return Set#SET_UNIT TargetSetUnit: The target set of units. - -- @return #nil If there are no targets to be set. - function DETECTION_DISPATCHER:EvaluateSEAD( DetectedArea ) - self:F( { DetectedArea.AreaID } ) - - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone - - -- Determine if the set has radar targets. If it does, construct a SEAD task. - local RadarCount = DetectedSet:HasSEAD() - - if RadarCount > 0 then - - -- Here we're doing something advanced... We're copying the DetectedSet, but making a new Set only with SEADable Radar units in it. - local TargetSetUnit = SET_UNIT:New() - TargetSetUnit:SetDatabase( DetectedSet ) - TargetSetUnit:FilterHasSEAD() - TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - - return TargetSetUnit - end - - return nil - end - - --- Creates a CAS task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea - -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateCAS( DetectedArea ) - self:F( { DetectedArea.AreaID } ) - - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone - - - -- Determine if the set has radar targets. If it does, construct a SEAD task. - local GroundUnitCount = DetectedSet:HasGroundUnits() - local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedArea ) - - if GroundUnitCount > 0 and FriendliesNearBy == true then - - -- Copy the Set - local TargetSetUnit = SET_UNIT:New() - TargetSetUnit:SetDatabase( DetectedSet ) - TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - - return TargetSetUnit - end - - return nil - end - - --- Creates a BAI task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea - -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateBAI( DetectedArea, FriendlyCoalition ) - self:F( { DetectedArea.AreaID } ) - - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone - - - -- Determine if the set has radar targets. If it does, construct a SEAD task. - local GroundUnitCount = DetectedSet:HasGroundUnits() - local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedArea ) - - if GroundUnitCount > 0 and FriendliesNearBy == false then - - -- Copy the Set - local TargetSetUnit = SET_UNIT:New() - TargetSetUnit:SetDatabase( DetectedSet ) - TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - - return TargetSetUnit - end - - return nil - end - - --- Evaluates the removal of the Task from the Mission. - -- Can only occur when the DetectedArea is Changed AND the state of the Task is "Planned". - -- @param #DETECTION_DISPATCHER self - -- @param Tasking.Mission#MISSION Mission - -- @param Tasking.Task#TASK Task - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea - -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedArea ) - - if Task then - if Task:IsStatePlanned() and DetectedArea.Changed == true then - self:E( "Removing Tasking: " .. Task:GetTaskName() ) - Task = Mission:RemoveTask( Task ) - end - end - - return Task - end - - - --- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS Detection The detection created by the @{Detection#DETECTION_AREAS} object. - -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. - function DETECTION_DISPATCHER:ProcessDetected( Detection ) - self:F2() - - local AreaMsg = {} - local TaskMsg = {} - local ChangeMsg = {} - - local Mission = self.Mission - - --- First we need to the detected targets. - for DetectedAreaID, DetectedAreaData in ipairs( Detection:GetDetectedAreas() ) do - - local DetectedArea = DetectedAreaData -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone - self:E( { "Targets in DetectedArea", DetectedArea.AreaID, DetectedSet:Count(), tostring( DetectedArea ) } ) - DetectedSet:Flush() - - local AreaID = DetectedArea.AreaID - - -- Evaluate SEAD Tasking - local SEADTask = Mission:GetTask( "SEAD." .. AreaID ) - SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedArea ) - if not SEADTask then - local TargetSetUnit = self:EvaluateSEAD( DetectedArea ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - SEADTask = Mission:AddTask( TASK_SEAD:New( Mission, self.SetGroup, "SEAD." .. AreaID, TargetSetUnit , DetectedZone ) ) - end - end - if SEADTask and SEADTask:IsStatePlanned() then - self:E( "Planned" ) - --SEADTask:SetPlannedMenu() - TaskMsg[#TaskMsg+1] = " - " .. SEADTask:GetStateString() .. " SEAD " .. AreaID .. " - " .. SEADTask.TargetSetUnit:GetUnitTypesText() - end - - -- Evaluate CAS Tasking - local CASTask = Mission:GetTask( "CAS." .. AreaID ) - CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedArea ) - if not CASTask then - local TargetSetUnit = self:EvaluateCAS( DetectedArea ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - CASTask = Mission:AddTask( TASK_A2G:New( Mission, self.SetGroup, "CAS." .. AreaID, "CAS", TargetSetUnit , DetectedZone, DetectedArea.NearestFAC ) ) - end - end - if CASTask and CASTask:IsStatePlanned() then - --CASTask:SetPlannedMenu() - TaskMsg[#TaskMsg+1] = " - " .. CASTask:GetStateString() .. " CAS " .. AreaID .. " - " .. CASTask.TargetSetUnit:GetUnitTypesText() - end - - -- Evaluate BAI Tasking - local BAITask = Mission:GetTask( "BAI." .. AreaID ) - BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedArea ) - if not BAITask then - local TargetSetUnit = self:EvaluateBAI( DetectedArea, self.CommandCenter:GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - BAITask = Mission:AddTask( TASK_A2G:New( Mission, self.SetGroup, "BAI." .. AreaID, "BAI", TargetSetUnit , DetectedZone, DetectedArea.NearestFAC ) ) - end - end - if BAITask and BAITask:IsStatePlanned() then - --BAITask:SetPlannedMenu() - TaskMsg[#TaskMsg+1] = " - " .. BAITask:GetStateString() .. " BAI " .. AreaID .. " - " .. BAITask.TargetSetUnit:GetUnitTypesText() - end - - if #TaskMsg > 0 then - - local ThreatLevel = Detection:GetTreatLevelA2G( DetectedArea ) - - local DetectedAreaVec3 = DetectedZone:GetVec3() - local DetectedAreaPointVec3 = POINT_VEC3:New( DetectedAreaVec3.x, DetectedAreaVec3.y, DetectedAreaVec3.z ) - local DetectedAreaPointLL = DetectedAreaPointVec3:ToStringLL( 3, true ) - AreaMsg[#AreaMsg+1] = string.format( " - Area #%d - %s - Threat Level [%s] (%2d)", - DetectedAreaID, - DetectedAreaPointLL, - string.rep( "■", ThreatLevel ), - ThreatLevel - ) - - -- Loop through the changes ... - local ChangeText = Detection:GetChangeText( DetectedArea ) - - if ChangeText ~= "" then - ChangeMsg[#ChangeMsg+1] = string.gsub( string.gsub( ChangeText, "\n", "%1 - " ), "^.", " - %1" ) - end - end - - -- OK, so the tasking has been done, now delete the changes reported for the area. - Detection:AcceptChanges( DetectedArea ) - - end - - -- TODO set menus using the HQ coordinator - Mission:GetCommandCenter():SetMenu() - - if #AreaMsg > 0 then - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if not TaskGroup:GetState( TaskGroup, "Assigned" ) then - self.CommandCenter:MessageToGroup( - string.format( "HQ Reporting - Target areas for mission '%s':\nAreas:\n%s\n\nTasks:\n%s\n\nChanges:\n%s ", - self.Mission:GetName(), - table.concat( AreaMsg, "\n" ), - table.concat( TaskMsg, "\n" ), - table.concat( ChangeMsg, "\n" ) - ), self:GetReportDisplayTime(), TaskGroup - ) - end - end - end - - return true - end - -end \ No newline at end of file diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 12fd65689..10e194ff2 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -12,22 +12,6 @@ MISSION = { ClassName = "MISSION", Name = "", MissionStatus = "PENDING", - _Clients = {}, - TaskMenus = {}, - TaskCategoryMenus = {}, - TaskTypeMenus = {}, - _ActiveTasks = {}, - GoalFunction = nil, - MissionReportTrigger = 0, - MissionProgressTrigger = 0, - MissionReportShow = false, - MissionReportFlash = false, - MissionTimeInterval = 0, - MissionCoalition = "", - SUCCESS = 1, - FAILED = 2, - REPEAT = 3, - _GoalTasks = {} } --- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. @@ -45,10 +29,184 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi self:SetStartState( "Idle" ) self:AddTransition( "Idle", "Start", "Ongoing" ) + + --- OnLeave Transition Handler for State Idle. + -- @function [parent=#MISSION] OnLeaveIdle + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Idle. + -- @function [parent=#MISSION] OnEnterIdle + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnLeave Transition Handler for State Ongoing. + -- @function [parent=#MISSION] OnLeaveOngoing + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Ongoing. + -- @function [parent=#MISSION] OnEnterOngoing + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Start. + -- @function [parent=#MISSION] OnBeforeStart + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Start. + -- @function [parent=#MISSION] OnAfterStart + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Start. + -- @function [parent=#MISSION] Start + -- @param #MISSION self + + --- Asynchronous Event Trigger for Event Start. + -- @function [parent=#MISSION] __Start + -- @param #MISSION self + -- @param #number Delay The delay in seconds. + self:AddTransition( "Ongoing", "Stop", "Idle" ) + + --- OnLeave Transition Handler for State Idle. + -- @function [parent=#MISSION] OnLeaveIdle + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Idle. + -- @function [parent=#MISSION] OnEnterIdle + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Stop. + -- @function [parent=#MISSION] OnBeforeStop + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Stop. + -- @function [parent=#MISSION] OnAfterStop + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Stop. + -- @function [parent=#MISSION] Stop + -- @param #MISSION self + + --- Asynchronous Event Trigger for Event Stop. + -- @function [parent=#MISSION] __Stop + -- @param #MISSION self + -- @param #number Delay The delay in seconds. + self:AddTransition( "Ongoing", "Complete", "Completed" ) + + --- OnLeave Transition Handler for State Completed. + -- @function [parent=#MISSION] OnLeaveCompleted + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Completed. + -- @function [parent=#MISSION] OnEnterCompleted + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Complete. + -- @function [parent=#MISSION] OnBeforeComplete + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Complete. + -- @function [parent=#MISSION] OnAfterComplete + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Complete. + -- @function [parent=#MISSION] Complete + -- @param #MISSION self + + --- Asynchronous Event Trigger for Event Complete. + -- @function [parent=#MISSION] __Complete + -- @param #MISSION self + -- @param #number Delay The delay in seconds. + self:AddTransition( "*", "Fail", "Failed" ) + --- OnLeave Transition Handler for State Failed. + -- @function [parent=#MISSION] OnLeaveFailed + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Failed. + -- @function [parent=#MISSION] OnEnterFailed + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Fail. + -- @function [parent=#MISSION] OnBeforeFail + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Fail. + -- @function [parent=#MISSION] OnAfterFail + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Fail. + -- @function [parent=#MISSION] Fail + -- @param #MISSION self + + --- Asynchronous Event Trigger for Event Fail. + -- @function [parent=#MISSION] __Fail + -- @param #MISSION self + -- @param #number Delay The delay in seconds. + self:T( { MissionName, MissionPriority, MissionBriefing, MissionCoalition } ) self.CommandCenter = CommandCenter @@ -60,14 +218,18 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi self.MissionCoalition = MissionCoalition self.Tasks = {} + + -- Private implementations + + return self end ---- FSM function for a MISSION +-- FSM function for a MISSION -- @param #MISSION self --- @param #string Event -- @param #string From +-- @param #string Event -- @param #string To function MISSION:onbeforeComplete( From, Event, To ) @@ -80,10 +242,10 @@ function MISSION:onbeforeComplete( From, Event, To ) return true -- Allow Mission completion. end ---- FSM function for a MISSION +-- FSM function for a MISSION -- @param #MISSION self --- @param #string Event -- @param #string From +-- @param #string Event -- @param #string To function MISSION:onenterCompleted( From, Event, To ) @@ -201,23 +363,25 @@ end --- Sets the Planned Task menu. -- @param #MISSION self -function MISSION:SetMenu() +-- @param #number MenuTime +function MISSION:SetMenu( MenuTime ) self:F() - for _, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - Task:SetMenu() + for _, TaskData in pairs( self:GetTasks() ) do + local Task = TaskData -- Tasking.Task#TASK + Task:SetMenu( MenuTime ) end end --- Removes the Planned Task menu. -- @param #MISSION self -function MISSION:RemoveMenu() +-- @param #number MenuTime +function MISSION:RemoveMenu( MenuTime ) self:F() for _, Task in pairs( self:GetTasks() ) do local Task = Task -- Tasking.Task#TASK - Task:RemoveMenu() + Task:RemoveMenu( MenuTime ) end end @@ -229,20 +393,6 @@ function MISSION:GetCommandCenter() return self.CommandCenter end ---- Sets the Assigned Task menu. --- @param #MISSION self --- @param Tasking.Task#TASK Task --- @param #string MenuText The menu text. --- @return #MISSION self -function MISSION:SetAssignedMenu( Task ) - - for _, Task in pairs( self.Tasks ) do - local Task = Task -- Tasking.Task#TASK - Task:RemoveMenu() - Task:SetAssignedMenu() - end - -end --- Removes a Task menu. -- @param #MISSION self @@ -258,28 +408,18 @@ end -- @param #MISSION self -- @param Wrapper.Group#GROUP TaskGroup -- @return Core.Menu#MENU_COALITION self -function MISSION:GetMissionMenu( TaskGroup ) +function MISSION:GetMenu( TaskGroup ) local CommandCenter = self:GetCommandCenter() - local CommandCenterMenu = CommandCenter.CommandCenterMenu + local CommandCenterMenu = CommandCenter:GetMenu() local MissionName = self:GetName() - - local TaskGroupName = TaskGroup:GetName() - local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ) + local MissionMenu = CommandCenterMenu:GetMenu( MissionName ) return MissionMenu end ---- Clears the mission menu for the coalition. --- @param #MISSION self --- @return #MISSION self -function MISSION:ClearMissionMenu() - self.MissionMenu:Remove() - self.MissionMenu = nil -end - --- Get the TASK identified by the TaskNumber from the Mission. This function is useful in GoalFunctions. -- @param #string TaskName The Name of the @{Task} within the @{Mission}. -- @return Tasking.Task#TASK The Task @@ -350,76 +490,44 @@ function MISSION:GetNextTaskID( Task ) return self.Tasks[TaskName].n end - - ---- old stuff - ---- Returns if a Mission has completed. --- @return bool +--- Is the @{Mission} **Completed**. +-- @param #MISSION self +-- @return #boolean function MISSION:IsCompleted() - self:F() - return self.MissionStatus == "ACCOMPLISHED" + return self:Is( "Completed" ) end ---- Set a Mission to completed. -function MISSION:Completed() - self:F() - self.MissionStatus = "ACCOMPLISHED" - self:StatusToClients() +--- Is the @{Mission} **Idle**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsIdle() + return self:Is( "Idle" ) end ---- Returns if a Mission is ongoing. --- treturn bool +--- Is the @{Mission} **Ongoing**. +-- @param #MISSION self +-- @return #boolean function MISSION:IsOngoing() - self:F() - return self.MissionStatus == "ONGOING" + return self:Is( "Ongoing" ) end ---- Set a Mission to ongoing. -function MISSION:Ongoing() - self:F() - self.MissionStatus = "ONGOING" - --self:StatusToClients() +--- Is the @{Mission} **Failed**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsFailed() + return self:Is( "Failed" ) end ---- Returns if a Mission is pending. --- treturn bool -function MISSION:IsPending() - self:F() - return self.MissionStatus == "PENDING" -end - ---- Set a Mission to pending. -function MISSION:Pending() - self:F() - self.MissionStatus = "PENDING" - self:StatusToClients() -end - ---- Returns if a Mission has failed. --- treturn bool -function MISSION:IsFailed() - self:F() - return self.MissionStatus == "FAILED" -end - ---- Set a Mission to failed. -function MISSION:Failed() - self:F() - self.MissionStatus = "FAILED" - self:StatusToClients() -end - ---- Send the status of the MISSION to all Clients. -function MISSION:StatusToClients() - self:F() - if self.MissionReportFlash then - for ClientID, Client in pairs( self._Clients ) do - Client:Message( self.MissionCoalition .. ' "' .. self.Name .. '": ' .. self.MissionStatus .. '! ( ' .. self.MissionPriority .. ' mission ) ', 10, "Mission Command: Mission Status") - end - end +--- Is the @{Mission} **Hold**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsHold() + return self:Is( "Hold" ) end +--- Validates if the Mission has a Group +-- @param #MISSION +-- @return #boolean true if the Mission has a Group. function MISSION:HasGroup( TaskGroup ) local Has = false @@ -512,107 +620,6 @@ function MISSION:ReportDetails() return Report:Text() end ---- Report the status of all MISSIONs to all active Clients. -function MISSION:ReportToAll() - self:F() - - local AlivePlayers = '' - for ClientID, Client in pairs( self._Clients ) do - if Client:GetDCSGroup() then - if Client:GetClientGroupDCSUnit() then - if Client:GetClientGroupDCSUnit():getLife() > 0.0 then - if AlivePlayers == '' then - AlivePlayers = ' Players: ' .. Client:GetClientGroupDCSUnit():getPlayerName() - else - AlivePlayers = AlivePlayers .. ' / ' .. Client:GetClientGroupDCSUnit():getPlayerName() - end - end - end - end - end - local Tasks = self:GetTasks() - local TaskText = "" - for TaskID, TaskData in pairs( Tasks ) do - TaskText = TaskText .. " - Task " .. TaskID .. ": " .. TaskData.Name .. ": " .. TaskData:GetGoalProgress() .. "\n" - end - MESSAGE:New( self.MissionCoalition .. ' "' .. self.Name .. '": ' .. self.MissionStatus .. ' ( ' .. self.MissionPriority .. ' mission )' .. AlivePlayers .. "\n" .. TaskText:gsub("\n$",""), 10, "Mission Command: Mission Report" ):ToAll() -end - - ---- Add a goal function to a MISSION. Goal functions are called when a @{TASK} within a mission has been completed. --- @param function GoalFunction is the function defined by the mission designer to evaluate whether a certain goal has been reached after a @{TASK} finishes within the @{MISSION}. A GoalFunction must accept 2 parameters: Mission, Client, which contains the current MISSION object and the current CLIENT object respectively. --- @usage --- PatriotActivation = { --- { "US SAM Patriot Zerti", false }, --- { "US SAM Patriot Zegduleti", false }, --- { "US SAM Patriot Gvleti", false } --- } --- --- function DeployPatriotTroopsGoal( Mission, Client ) --- --- --- -- Check if the cargo is all deployed for mission success. --- for CargoID, CargoData in pairs( Mission._Cargos ) do --- if Group.getByName( CargoData.CargoGroupName ) then --- CargoGroup = Group.getByName( CargoData.CargoGroupName ) --- if CargoGroup then --- -- Check if the cargo is ready to activate --- CurrentLandingZoneID = routines.IsUnitInZones( CargoGroup:getUnits()[1], Mission:GetTask( 2 ).LandingZones ) -- The second task is the Deploytask to measure mission success upon --- if CurrentLandingZoneID then --- if PatriotActivation[CurrentLandingZoneID][2] == false then --- -- Now check if this is a new Mission Task to be completed... --- trigger.action.setGroupAIOn( Group.getByName( PatriotActivation[CurrentLandingZoneID][1] ) ) --- PatriotActivation[CurrentLandingZoneID][2] = true --- MessageToBlue( "Mission Command: Message to all airborne units! The " .. PatriotActivation[CurrentLandingZoneID][1] .. " is armed. Our air defenses are now stronger.", 60, "BLUE/PatriotDefense" ) --- MessageToRed( "Mission Command: Our satellite systems are detecting additional NATO air defenses. To all airborne units: Take care!!!", 60, "RED/PatriotDefense" ) --- Mission:GetTask( 2 ):AddGoalCompletion( "Patriots activated", PatriotActivation[CurrentLandingZoneID][1], 1 ) -- Register Patriot activation as part of mission goal. --- end --- end --- end --- end --- end --- end --- --- local Mission = MISSIONSCHEDULER.AddMission( 'NATO Transport Troops', 'Operational', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.', 'NATO' ) --- Mission:AddGoalFunction( DeployPatriotTroopsGoal ) -function MISSION:AddGoalFunction( GoalFunction ) - self:F() - self.GoalFunction = GoalFunction -end - ---- Register a new @{CLIENT} to participate within the mission. --- @param CLIENT Client is the @{CLIENT} object. The object must have been instantiated with @{CLIENT:New}. --- @return CLIENT --- @usage --- Add a number of Client objects to the Mission. --- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*HOT-Deploy Troops 1', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*RAMP-Deploy Troops 3', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*HOT-Deploy Troops 2', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*RAMP-Deploy Troops 4', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() ) -function MISSION:AddClient( Client ) - self:F( { Client } ) - - local Valid = true - - if Valid then - self._Clients[Client.ClientName] = Client - end - - return Client -end - ---- Find a @{CLIENT} object within the @{MISSION} by its ClientName. --- @param CLIENT ClientName is a string defining the Client Group as defined within the ME. --- @return CLIENT --- @usage --- -- Seach for Client "Bomber" within the Mission. --- local BomberClient = Mission:FindClient( "Bomber" ) -function MISSION:FindClient( ClientName ) - self:F( { self._Clients[ClientName] } ) - return self._Clients[ClientName] -end - - --- Get all the TASKs from the Mission. This function is useful in GoalFunctions. -- @return {TASK,...} Structure of TASKS with the @{TASK} number as the key. -- @usage @@ -626,330 +633,3 @@ function MISSION:GetTasks() end ---[[ - _TransportExecuteStage: Defines the different stages of Transport unload/load execution. This table is internal and is used to control the validity of Transport load/unload timing. - - - _TransportExecuteStage.EXECUTING - - _TransportExecuteStage.SUCCESS - - _TransportExecuteStage.FAILED - ---]] -_TransportExecuteStage = { - NONE = 0, - EXECUTING = 1, - SUCCESS = 2, - FAILED = 3 -} - - ---- The MISSIONSCHEDULER is an OBJECT and is the main scheduler of ALL active MISSIONs registered within this scheduler. It's workings are considered internal and is automatically created when the Mission.lua file is included. --- @type MISSIONSCHEDULER --- @field #MISSIONSCHEDULER.MISSIONS Missions -MISSIONSCHEDULER = { - Missions = {}, - MissionCount = 0, - TimeIntervalCount = 0, - TimeIntervalShow = 150, - TimeSeconds = 14400, - TimeShow = 5 -} - ---- @type MISSIONSCHEDULER.MISSIONS --- @list <#MISSION> Mission - ---- This is the main MISSIONSCHEDULER Scheduler function. It is considered internal and is automatically created when the Mission.lua file is included. -function MISSIONSCHEDULER.Scheduler() - - - -- loop through the missions in the TransportTasks - for MissionName, MissionData in pairs( MISSIONSCHEDULER.Missions ) do - - local Mission = MissionData -- #MISSION - - if not Mission:IsCompleted() then - - -- This flag will monitor if for this mission, there are clients alive. If this flag is still false at the end of the loop, the mission status will be set to Pending (if not Failed or Completed). - local ClientsAlive = false - - for ClientID, ClientData in pairs( Mission._Clients ) do - - local Client = ClientData -- Wrapper.Client#CLIENT - - if Client:IsAlive() then - - -- There is at least one Client that is alive... So the Mission status is set to Ongoing. - ClientsAlive = true - - -- If this Client was not registered as Alive before: - -- 1. We register the Client as Alive. - -- 2. We initialize the Client Tasks and make a link to the original Mission Task. - -- 3. We initialize the Cargos. - -- 4. We flag the Mission as Ongoing. - if not Client.ClientAlive then - Client.ClientAlive = true - Client.ClientBriefingShown = false - for TaskNumber, Task in pairs( Mission._Tasks ) do - -- Note that this a deepCopy. Each client must have their own Tasks with own Stages!!! - Client._Tasks[TaskNumber] = routines.utils.deepCopy( Mission._Tasks[TaskNumber] ) - -- Each MissionTask must point to the original Mission. - Client._Tasks[TaskNumber].MissionTask = Mission._Tasks[TaskNumber] - Client._Tasks[TaskNumber].Cargos = Mission._Tasks[TaskNumber].Cargos - Client._Tasks[TaskNumber].LandingZones = Mission._Tasks[TaskNumber].LandingZones - end - - Mission:Ongoing() - end - - - -- For each Client, check for each Task the state and evolve the mission. - -- This flag will indicate if the Task of the Client is Complete. - local TaskComplete = false - - for TaskNumber, Task in pairs( Client._Tasks ) do - - if not Task.Stage then - Task:SetStage( 1 ) - end - - - local TransportTime = timer.getTime() - - if not Task:IsDone() then - - if Task:Goal() then - Task:ShowGoalProgress( Mission, Client ) - end - - --env.info( 'Scheduler: Mission = ' .. Mission.Name .. ' / Client = ' .. Client.ClientName .. ' / Task = ' .. Task.Name .. ' / Stage = ' .. Task.ActiveStage .. ' - ' .. Task.Stage.Name .. ' - ' .. Task.Stage.StageType ) - - -- Action - if Task:StageExecute() then - Task.Stage:Execute( Mission, Client, Task ) - end - - -- Wait until execution is finished - if Task.ExecuteStage == _TransportExecuteStage.EXECUTING then - Task.Stage:Executing( Mission, Client, Task ) - end - - -- Validate completion or reverse to earlier stage - if Task.Time + Task.Stage.WaitTime <= TransportTime then - Task:SetStage( Task.Stage:Validate( Mission, Client, Task ) ) - end - - if Task:IsDone() then - --env.info( 'Scheduler: Mission '.. Mission.Name .. ' Task ' .. Task.Name .. ' Stage ' .. Task.Stage.Name .. ' done. TaskComplete = ' .. string.format ( "%s", TaskComplete and "true" or "false" ) ) - TaskComplete = true -- when a task is not yet completed, a mission cannot be completed - - else - -- break only if this task is not yet done, so that future task are not yet activated. - TaskComplete = false -- when a task is not yet completed, a mission cannot be completed - --env.info( 'Scheduler: Mission "'.. Mission.Name .. '" Task "' .. Task.Name .. '" Stage "' .. Task.Stage.Name .. '" break. TaskComplete = ' .. string.format ( "%s", TaskComplete and "true" or "false" ) ) - break - end - - if TaskComplete then - - if Mission.GoalFunction ~= nil then - Mission.GoalFunction( Mission, Client ) - end - if MISSIONSCHEDULER.Scoring then - MISSIONSCHEDULER.Scoring:_AddMissionTaskScore( Client:GetClientGroupDCSUnit(), Mission.Name, 25 ) - end - --- if not Mission:IsCompleted() then --- end - end - end - end - - local MissionComplete = true - for TaskNumber, Task in pairs( Mission._Tasks ) do - if Task:Goal() then --- Task:ShowGoalProgress( Mission, Client ) - if Task:IsGoalReached() then - else - MissionComplete = false - end - else - MissionComplete = false -- If there is no goal, the mission should never be ended. The goal status will be set somewhere else. - end - end - - if MissionComplete then - Mission:Completed() - if MISSIONSCHEDULER.Scoring then - MISSIONSCHEDULER.Scoring:_AddMissionScore( Mission.Name, 100 ) - end - else - if TaskComplete then - -- Reset for new tasking of active client - Client.ClientAlive = false -- Reset the client tasks. - end - end - - - else - if Client.ClientAlive then - env.info( 'Scheduler: Client "' .. Client.ClientName .. '" is inactive.' ) - Client.ClientAlive = false - - -- This is tricky. If we sanitize Client._Tasks before sanitizing Client._Tasks[TaskNumber].MissionTask, then the original MissionTask will be sanitized, and will be lost within the garbage collector. - -- So first sanitize Client._Tasks[TaskNumber].MissionTask, after that, sanitize only the whole _Tasks structure... - --Client._Tasks[TaskNumber].MissionTask = nil - --Client._Tasks = nil - end - end - end - - -- If all Clients of this Mission are not activated, then the Mission status needs to be put back into Pending status. - -- But only if the Mission was Ongoing. In case the Mission is Completed or Failed, the Mission status may not be changed. In these cases, this will be the last run of this Mission in the Scheduler. - if ClientsAlive == false then - if Mission:IsOngoing() then - -- Mission status back to pending... - Mission:Pending() - end - end - end - - Mission:StatusToClients() - - if Mission:ReportTrigger() then - Mission:ReportToAll() - end - end - - return true -end - ---- Start the MISSIONSCHEDULER. -function MISSIONSCHEDULER.Start() - if MISSIONSCHEDULER ~= nil then - --MISSIONSCHEDULER.SchedulerId = routines.scheduleFunction( MISSIONSCHEDULER.Scheduler, { }, 0, 2 ) - MISSIONSCHEDULER.SchedulerId = SCHEDULER:New( nil, MISSIONSCHEDULER.Scheduler, { }, 0, 2 ) - end -end - ---- Stop the MISSIONSCHEDULER. -function MISSIONSCHEDULER.Stop() - if MISSIONSCHEDULER.SchedulerId then - routines.removeFunction(MISSIONSCHEDULER.SchedulerId) - MISSIONSCHEDULER.SchedulerId = nil - end -end - ---- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. --- @param Mission is the MISSION object instantiated by @{MISSION:New}. --- @return MISSION --- @usage --- -- Declare a mission. --- Mission = MISSION:New( 'Russia Transport Troops SA-6', --- 'Operational', --- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', --- 'Russia' ) --- MISSIONSCHEDULER:AddMission( Mission ) -function MISSIONSCHEDULER.AddMission( Mission ) - MISSIONSCHEDULER.Missions[Mission.Name] = Mission - MISSIONSCHEDULER.MissionCount = MISSIONSCHEDULER.MissionCount + 1 - -- Add an overall AI Client for the AI tasks... This AI Client will facilitate the Events in the background for each Task. - --MissionAdd:AddClient( CLIENT:Register( 'AI' ) ) - - return Mission -end - ---- Remove a MISSION from the MISSIONSCHEDULER. --- @param MissionName is the name of the MISSION given at declaration using @{AddMission}. --- @usage --- -- Declare a mission. --- Mission = MISSION:New( 'Russia Transport Troops SA-6', --- 'Operational', --- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', --- 'Russia' ) --- MISSIONSCHEDULER:AddMission( Mission ) --- --- -- Now remove the Mission. --- MISSIONSCHEDULER:RemoveMission( 'Russia Transport Troops SA-6' ) -function MISSIONSCHEDULER.RemoveMission( MissionName ) - MISSIONSCHEDULER.Missions[MissionName] = nil - MISSIONSCHEDULER.MissionCount = MISSIONSCHEDULER.MissionCount - 1 -end - ---- Find a MISSION within the MISSIONSCHEDULER. --- @param MissionName is the name of the MISSION given at declaration using @{AddMission}. --- @return MISSION --- @usage --- -- Declare a mission. --- Mission = MISSION:New( 'Russia Transport Troops SA-6', --- 'Operational', --- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', --- 'Russia' ) --- MISSIONSCHEDULER:AddMission( Mission ) --- --- -- Now find the Mission. --- MissionFind = MISSIONSCHEDULER:FindMission( 'Russia Transport Troops SA-6' ) -function MISSIONSCHEDULER.FindMission( MissionName ) - return MISSIONSCHEDULER.Missions[MissionName] -end - --- Internal function used by the MISSIONSCHEDULER menu. -function MISSIONSCHEDULER.ReportMissionsShow( ) - for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do - Mission.MissionReportShow = true - Mission.MissionReportFlash = false - end -end - --- Internal function used by the MISSIONSCHEDULER menu. -function MISSIONSCHEDULER.ReportMissionsFlash( TimeInterval ) - local Count = 0 - for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do - Mission.MissionReportShow = false - Mission.MissionReportFlash = true - Mission.MissionReportTrigger = timer.getTime() + Count * TimeInterval - Mission.MissionTimeInterval = MISSIONSCHEDULER.MissionCount * TimeInterval - env.info( "TimeInterval = " .. Mission.MissionTimeInterval ) - Count = Count + 1 - end -end - --- Internal function used by the MISSIONSCHEDULER menu. -function MISSIONSCHEDULER.ReportMissionsHide( Prm ) - for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do - Mission.MissionReportShow = false - Mission.MissionReportFlash = false - end -end - ---- Enables a MENU option in the communications menu under F10 to control the status of the active missions. --- This function should be called only once when starting the MISSIONSCHEDULER. -function MISSIONSCHEDULER.ReportMenu() - local ReportMenu = SUBMENU:New( 'Status' ) - local ReportMenuShow = COMMANDMENU:New( 'Show Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsShow, 0 ) - local ReportMenuFlash = COMMANDMENU:New('Flash Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsFlash, 120 ) - local ReportMenuHide = COMMANDMENU:New( 'Hide Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsHide, 0 ) -end - ---- Show the remaining mission time. -function MISSIONSCHEDULER:TimeShow() - self.TimeIntervalCount = self.TimeIntervalCount + 1 - if self.TimeIntervalCount >= self.TimeTriggerShow then - local TimeMsg = string.format("%00d", ( self.TimeSeconds / 60 ) - ( timer.getTime() / 60 )) .. ' minutes left until mission reload.' - MESSAGE:New( TimeMsg, self.TimeShow, "Mission time" ):ToAll() - self.TimeIntervalCount = 0 - end -end - -function MISSIONSCHEDULER:Time( TimeSeconds, TimeIntervalShow, TimeShow ) - - self.TimeIntervalCount = 0 - self.TimeSeconds = TimeSeconds - self.TimeIntervalShow = TimeIntervalShow - self.TimeShow = TimeShow -end - ---- Adds a mission scoring to the game. -function MISSIONSCHEDULER:Scoring( Scoring ) - - self.Scoring = Scoring -end - diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 4151d6e98..0f3931b1c 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -185,7 +185,6 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() - Mission:AddTask( self ) return self end @@ -193,9 +192,13 @@ end --- Get the Task FSM Process Template -- @param #TASK self -- @return Core.Fsm#FSM_PROCESS -function TASK:GetUnitProcess() +function TASK:GetUnitProcess( TaskUnit ) - return self.FsmTemplate + if TaskUnit then + return self:GetStateMachine( TaskUnit ) + else + return self.FsmTemplate + end end --- Sets the Task FSM Process Template @@ -228,7 +231,7 @@ function TASK:JoinUnit( PlayerUnit, PlayerGroup ) -- If the PlayerGroup is not assigned to the Task, the menu needs to be set. In that case, the PlayerUnit will become the GroupPlayer leader. if self:IsStatePlanned() or self:IsStateReplanned() then self:SetMenuForGroup( PlayerGroup ) - self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() ) + --self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() ) end if self:IsStateAssigned() then local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) @@ -270,10 +273,11 @@ function TASK:AbortUnit( PlayerUnit ) self:MessageToGroups( PlayerUnit:GetPlayerName() .. " aborted Task " .. self:GetName() ) self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) if #PlayerGroup:GetUnits() == 1 then + self:UnAssignFromGroup( PlayerGroup ) PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) self:RemoveMenuForGroup( PlayerGroup ) end - self:PlayerAborted( PlayerUnit ) + self:Abort() end end end @@ -339,7 +343,7 @@ end ---- Assign the @{Task}to a @{Group}. +--- Assign the @{Task} to a @{Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @return #TASK @@ -350,7 +354,11 @@ function TASK:AssignToGroup( TaskGroup ) TaskGroup:SetState( TaskGroup, "Assigned", self ) - self:RemoveMenuForGroup( TaskGroup ) + local Mission = self:GetMission() + local MissionMenu = Mission:GetMenu( TaskGroup ) + MissionMenu:RemoveSubMenus() + + --self:RemoveMenuForGroup( TaskGroup ) self:SetAssignedMenuForGroup( TaskGroup ) local TaskUnits = TaskGroup:GetUnits() @@ -390,6 +398,7 @@ function TASK:AssignToUnit( TaskUnit ) self:E({"Address FsmUnit", tostring( FsmUnit ) } ) FsmUnit:SetStartState( "Planned" ) + FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow. return self @@ -400,7 +409,7 @@ end -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self function TASK:UnAssignFromUnit( TaskUnit ) - self:F( TaskUnit ) + self:F( TaskUnit:GetName() ) self:RemoveStateMachine( TaskUnit ) @@ -447,28 +456,37 @@ function TASK:SendBriefingToAssignedGroups() end ---- Assign the @{Task} from the @{Group}s. +--- UnAssign the @{Task} from the @{Group}s. -- @param #TASK self function TASK:UnAssignFromGroups() self:F2() for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do + self:UnAssignFromGroup( TaskGroup ) + end +end - TaskGroup:SetState( TaskGroup, "Assigned", nil ) +--- UnAssign the @{Task} from a @{Group}. +-- @param #TASK self +function TASK:UnAssignFromGroup( TaskGroup ) + self:F2( { TaskGroup } ) + + TaskGroup:SetState( TaskGroup, "Assigned", nil ) - self:RemoveMenuForGroup( TaskGroup ) + self:RemoveAssignedMenuForGroup( TaskGroup ) - local TaskUnits = TaskGroup:GetUnits() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - if PlayerName ~= nil or PlayerName ~= "" then - self:UnAssignFromUnit( TaskUnit ) - end + local TaskUnits = TaskGroup:GetUnits() + for UnitID, UnitData in pairs( TaskUnits ) do + local TaskUnit = UnitData -- Wrapper.Unit#UNIT + local PlayerName = TaskUnit:GetPlayerName() + if PlayerName ~= nil or PlayerName ~= "" then + self:UnAssignFromUnit( TaskUnit ) end end end + + --- Returns if the @{Task} is assigned to the Group. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup @@ -479,10 +497,12 @@ function TASK:IsAssignedToGroup( TaskGroup ) if self:IsStateAssigned() then if TaskGroup:GetState( TaskGroup, "Assigned" ) == self then + self:T( { "Task is assigned to:", TaskGroup:GetName() } ) return true end end + self:T( { "Task is not assigned to:", TaskGroup:GetName() } ) return false end @@ -511,37 +531,36 @@ end --- Set the menu options of the @{Task} to all the groups in the SetGroup. -- @param #TASK self -function TASK:SetMenu() +-- @param #number MenuTime +-- @return #TASK +function TASK:SetMenu( MenuTime ) self:F() self.SetGroup:Flush() - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if self:IsStatePlanned() or self:IsStateReplanned() then - self:SetMenuForGroup( TaskGroup ) + for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do + local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP + if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then + if self:IsStatePlanned() or self:IsStateReplanned() then + self:SetMenuForGroup( TaskGroup, MenuTime ) + end end end end ---- Remove the menu options of the @{Task} to all the groups in the SetGroup. --- @param #TASK self --- @return #TASK self -function TASK:RemoveMenu() - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - self:RemoveMenuForGroup( TaskGroup ) - end -end - --- Set the Menu for a Group -- @param #TASK self -function TASK:SetMenuForGroup( TaskGroup ) +-- @param #number MenuTime +-- @return #TASK +function TASK:SetMenuForGroup( TaskGroup, MenuTime ) - if not self:IsAssignedToGroup( TaskGroup ) then - self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName() ) + if not TaskGroup:GetState( TaskGroup, "Assigned" ) then + self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName(), MenuTime ) else - self:SetAssignedMenuForGroup( TaskGroup ) + if not self:IsAssignedToGroup( TaskGroup ) then + self:SetAssignedMenuForGroup( TaskGroup, MenuTime ) + end end end @@ -550,16 +569,24 @@ end -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @param #string MenuText The menu text. +-- @param #number MenuTime -- @return #TASK self -function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText ) +function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText, MenuTime ) self:E( TaskGroup:GetName() ) local Mission = self:GetMission() - local MissionMenu = Mission:GetMissionMenu( TaskGroup ) + local MissionName = Mission:GetName() + local CommandCenter = Mission:GetCommandCenter() + local CommandCenterMenu = CommandCenter:GetMenu() + + local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime ) + + + local MissionMenu = Mission:GetMenu( TaskGroup ) local TaskType = self:GetType() - local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ) + local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ):SetTime( MenuTime ) + local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ):SetTime( MenuTime ):SetRemoveParent( true ) return self end @@ -567,32 +594,84 @@ end --- Set the assigned menu options of the @{Task}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime -- @return #TASK self -function TASK:SetAssignedMenuForGroup( TaskGroup ) +function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) self:E( TaskGroup:GetName() ) local Mission = self:GetMission() - local MissionMenu = Mission:GetMissionMenu( TaskGroup ) + local MissionMenu = Mission:GetMenu( TaskGroup ) self:E( { MissionMenu = MissionMenu } ) - local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, { self = self, TaskGroup = TaskGroup } ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, { self = self, TaskGroup = TaskGroup } ) + local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ) + local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ) return self end +--- Remove the menu options of the @{Task} to all the groups in the SetGroup. +-- @param #TASK self +-- @param #number MenuTime +-- @return #TASK +function TASK:RemoveMenu( MenuTime ) + self:F() + + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + local TaskGroup = TaskGroup -- Wrapper.Group#GROUP + if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then + if not self:IsAssignedToGroup( TaskGroup ) then + self:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) + end + end + end +end + + --- Remove the menu option of the @{Task} for a @{Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime -- @return #TASK self -function TASK:RemoveMenuForGroup( TaskGroup ) +function TASK:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) + self:F() local Mission = self:GetMission() local MissionName = Mission:GetName() + + local MissionMenu = Mission:GetMenu( TaskGroup ) + + if MissionMenu then + local TaskType = self:GetType() + local TypeMenu = MissionMenu:GetMenu( TaskType ) + + if TypeMenu then + local TaskMenu = TypeMenu:GetMenu( self:GetTaskName() ) + if TaskMenu then + TaskMenu:Remove( MenuTime ) + end + end + end + +end - local MissionMenu = Mission:GetMissionMenu( TaskGroup ) - MissionMenu:Remove() +--- Remove the assigned menu option of the @{Task} for a @{Group}. +-- @param #TASK self +-- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime +-- @return #TASK self +function TASK:RemoveAssignedMenuForGroup( TaskGroup ) + self:F() + + local Mission = self:GetMission() + local MissionName = Mission:GetName() + + local MissionMenu = Mission:GetMenu( TaskGroup ) + + if MissionMenu then + MissionMenu:RemoveSubMenus() + end + end function TASK.MenuAssignToGroup( MenuParam ) @@ -605,19 +684,21 @@ function TASK.MenuAssignToGroup( MenuParam ) self:AssignToGroup( TaskGroup ) end -function TASK.MenuTaskStatus( MenuParam ) +--- Report the task status. +-- @param #TASK self +function TASK:MenuTaskStatus( TaskGroup ) - local self = MenuParam.self - local TaskGroup = MenuParam.TaskGroup + local ReportText = self:ReportDetails() - --self:AssignToGroup( TaskGroup ) + self:T( ReportText ) + self:GetMission():GetCommandCenter():MessageToGroup( ReportText, TaskGroup ) + end -function TASK.MenuTaskAbort( MenuParam ) +--- Report the task status. +-- @param #TASK self +function TASK:MenuTaskAbort( TaskGroup ) - local self = MenuParam.self - local TaskGroup = MenuParam.TaskGroup - self:Abort() end @@ -662,15 +743,26 @@ end --- Add a FiniteStateMachine to @{Task} with key Task@{Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit +-- @param Core.Fsm#FSM_PROCESS Fsm -- @return #TASK self function TASK:SetStateMachine( TaskUnit, Fsm ) - self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) + self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil, Fsm:GetClassNameAndID() } ) self.Fsm[TaskUnit] = Fsm return Fsm end +--- Gets the FiniteStateMachine of @{Task} with key Task@{Unit} +-- @param #TASK self +-- @param Wrapper.Unit#UNIT TaskUnit +-- @return Core.Fsm#FSM_PROCESS +function TASK:GetStateMachine( TaskUnit ) + self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) + + return self.Fsm[TaskUnit] +end + --- Remove FiniteStateMachines from @{Task} with key Task@{Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit @@ -678,9 +770,15 @@ end function TASK:RemoveStateMachine( TaskUnit ) self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) + self:E( self.Fsm ) + for TaskUnitT, Fsm in pairs( self.Fsm ) do + self:E( TaskUnitT ) + end + self.Fsm[TaskUnit] = nil + collectgarbage() - self:T( "Garbage Collected, Processes should be finalized now ...") + self:E( "Garbage Collected, Processes should be finalized now ...") end --- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task} @@ -795,6 +893,32 @@ function TASK:IsStatePlanned() return self:Is( "Planned" ) end +--- Sets a @{Task} to status **Aborted**. +-- @param #TASK self +function TASK:StateAborted() + self:SetState( self, "State", "Aborted" ) + return self +end + +--- Is the @{Task} status **Aborted**. +-- @param #TASK self +function TASK:IsStateAborted() + return self:Is( "Aborted" ) +end + +--- Sets a @{Task} to status **Cancelled**. +-- @param #TASK self +function TASK:StateCancelled() + self:SetState( self, "State", "Cancelled" ) + return self +end + +--- Is the @{Task} status **Cancelled**. +-- @param #TASK self +function TASK:IsStateCancelled() + return self:Is( "Cancelled" ) +end + --- Sets a @{Task} to status **Assigned**. -- @param #TASK self function TASK:StateAssigned() @@ -857,11 +981,17 @@ end -- @param #string Event -- @param #string From -- @param #string To -function TASK:onenterAssigned( From, Event, To ) +function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName ) - self:E("Task Assigned") + self:E( { "Task Assigned", self.Dispatcher } ) self:MessageToGroups( "Task " .. self:GetName() .. " has been assigned to your group." ) + + if self.Dispatcher then + self:E( "Firing Assign event " ) + self.Dispatcher:Assign( self, PlayerUnit, PlayerName ) + end + self:GetMission():__Start( 1 ) end @@ -875,7 +1005,7 @@ function TASK:onenterSuccess( From, Event, To ) self:E( "Task Success" ) - self:MessageToGroups( "Task " .. self:GetName() .. " is successful! Good job!" ) + self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is successful! Good job!" ) self:UnAssignFromGroups() self:GetMission():__Complete( 1 ) @@ -973,6 +1103,18 @@ function TASK:onbeforeTimeOut( From, Event, To ) return false end +do -- Dispatcher + + --- Set dispatcher of a task + -- @param #TASK self + -- @param Tasking.DetectionManager#DETECTION_MANAGER Dispatcher + -- @return #TASK + function TASK:SetDispatcher( Dispatcher ) + self.Dispatcher = Dispatcher + end + +end + do -- Reporting --- Create a summary report of the Task. @@ -1009,24 +1151,19 @@ function TASK:ReportDetails() -- Determine the status of the Task. local State = self:GetState() - -- Loop each Unit active in the Task, and find Player Names. local PlayerNames = {} - for PlayerGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do - local Player = PlayerGroup -- Wrapper.Group#GROUP - for PlayerUnitID, PlayerUnit in pairs( PlayerGroup:GetUnits() ) do - local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT - if PlayerUnit and PlayerUnit:IsAlive() then - local PlayerName = PlayerUnit:GetPlayerName() - PlayerNames[#PlayerNames+1] = PlayerName - end + local PlayerReport = REPORT:New( " - Players:" ) + for PlayerGroupID, PlayerGroupData in pairs( self:GetGroups():GetSet() ) do + local PlayerGroup = PlayerGroupData -- Wrapper.Group#GROUP + PlayerNames = PlayerGroup:GetPlayerNames() + if PlayerNames then + PlayerReport:Add( " -- Group " .. PlayerGroup:GetCallsign() .. ": " .. table.concat( PlayerNames, ", " ) ) end - local PlayerNameText = table.concat( PlayerNames, ", " ) - Report:Add( "Task " .. Name .. " - State '" .. State .. "' - Players " .. PlayerNameText ) end -- Loop each Process in the Task, and find Reporting Details. - + Report:Add( string.format( " - Task %s\n -- State '%s'\n%s", Name, State, PlayerReport:Text() ) ) return Report:Text() end diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 3eee41d0d..662ab0d2b 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -1,28 +1,76 @@ ---- (AI) (SP) (MP) Tasking for Air to Ground Processes. +--- **Tasking** - The TASK_A2G models tasks for players in Air to Ground engagements. -- --- 1) @{#TASK_A2G} class, extends @{Task#TASK} --- ================================================= --- The @{#TASK_A2G} class defines a CAS or BAI task of a @{Set} of Target Units, --- located at a Target Zone, based on the tasking capabilities defined in @{Task#TASK}. +-- ![Banner Image](..\Presentations\TASK_A2G\Dia1.JPG) +-- +-- +-- # 1) @{Task_A2G#TASK_A2G} class, extends @{Task#TASK} +-- +-- The @{#TASK_A2G} class defines Air To Ground tasks for a @{Set} of Target Units, +-- based on the tasking capabilities defined in @{Task#TASK}. -- The TASK_A2G is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses: -- -- * **None**: Start of the process --- * **Planned**: The SEAD task is planned. Upon Planned, the sub-process @{Process_Fsm.Assign#ACT_ASSIGN_ACCEPT} is started to accept the task. --- * **Assigned**: The SEAD task is assigned to a @{Group#GROUP}. Upon Assigned, the sub-process @{Process_Fsm.Route#ACT_ROUTE} is started to route the active Units in the Group to the attack zone. --- * **Success**: The SEAD task is successfully completed. Upon Success, the sub-process @{Process_SEAD#PROCESS_SEAD} is started to follow-up successful SEADing of the targets assigned in the task. --- * **Failed**: The SEAD task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. +-- * **Planned**: The A2G task is planned. +-- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}. +-- * **Success**: The A2G task is successfully completed. +-- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. +-- +-- # 1.1) Set the scoring of achievements in an A2G attack. +-- +-- Scoring or penalties can be given in the following circumstances: +-- +-- * @{#TASK_A2G.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed. +-- * @{#TASK_A2G.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed. +-- * @{#TASK_A2G.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed. +-- +-- # 2) @{Task_A2G#TASK_SEAD} class, extends @{Task_A2G#TASK_A2G} +-- +-- The @{#TASK_SEAD} class defines a SEAD task for a @{Set} of Target Units. -- -- === -- --- ### Authors: FlightControl - Design and Programming +-- # 3) @{Task_A2G#TASK_CAS} class, extends @{Task_A2G#TASK_A2G} -- +-- The @{#TASK_CAS} class defines a CAS task for a @{Set} of Target Units. +-- +-- === +-- +-- # 4) @{Task_A2G#TASK_BAI} class, extends @{Task_A2G#TASK_A2G} +-- +-- The @{#TASK_BAI} class defines a BAI task for a @{Set} of Target Units. +-- +-- ==== +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-03-09: Revised version. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **[WingThor]**: Concept, Advice & Testing. +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- -- @module Task_A2G - do -- TASK_A2G --- The TASK_A2G class -- @type TASK_A2G + -- @field Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK TASK_A2G = { ClassName = "TASK_A2G", @@ -33,52 +81,361 @@ do -- TASK_A2G -- @param Tasking.Mission#MISSION Mission -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. - -- @param #string TaskType BAI or CAS -- @param Set#SET_UNIT UnitSetTargets - -- @param Core.Zone#ZONE_BASE TargetZone + -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. + -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. + -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. -- @return #TASK_A2G self - function TASK_A2G:New( Mission, SetGroup, TaskName, TaskType, TargetSetUnit, TargetZone, FACUnit ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) + function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType ) + local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) -- Tasking.Task#TASK_A2G self:F() self.TargetSetUnit = TargetSetUnit - self.TargetZone = TargetZone - self.FACUnit = FACUnit - - local A2GUnitProcess = self:GetUnitProcess() + self.TaskType = TaskType - A2GUnitProcess:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "Attack the Area" ), { Assigned = "Route", Rejected = "Eject" } ) - A2GUnitProcess:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } ) - A2GUnitProcess:AddTransition( "Rejected", "Eject", "Planned" ) - A2GUnitProcess:AddTransition( "Arrived", "Update", "Updated" ) - A2GUnitProcess:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "Attack" ), { Accounted = "Success" } ) - A2GUnitProcess:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) ) - --Fsm:AddProcess ( "Updated", "JTAC", PROCESS_JTAC:New( self, TaskUnit, self.TargetSetUnit, self.FACUnit ) ) - A2GUnitProcess:AddTransition( "Accounted", "Success", "Success" ) - A2GUnitProcess:AddTransition( "Failed", "Fail", "Failed" ) + Mission:AddTask( self ) - function A2GUnitProcess:onenterUpdated( TaskUnit ) - self:E( { self } ) - self:Account() - self:Smoke() + local Fsm = self:GetUnitProcess() + + + Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToRendezVous", Rejected = "Reject" } ) + + Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) + Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) + Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) + + Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" ) + + Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" ) + Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" ) + + Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, self.TaskType ), { Accounted = "Success" } ) + Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" ) + Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} ) + Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} ) + Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" ) + + Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) + Fsm:AddTransition( "Accounted", "Success", "Success" ) + Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) + Fsm:AddTransition( "Failed", "Fail", "Failed" ) + + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2G#TASK_A2G Task + function Fsm:onafterRouteToRendezVous( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- Determine the first Unit from the self.RendezVousSetUnit + + if Task:GetRendezVousZone( TaskUnit ) then + self:__RouteToRendezVousZone( 0.1 ) + else + if Task:GetRendezVousPointVec2( TaskUnit ) then + self:__RouteToRendezVousPoint( 0.1 ) + else + self:__ArriveAtRendezVous( 0.1 ) + end + end end + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task#TASK_A2G Task + function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- Determine the first Unit from the self.TargetSetUnit + + self:__Engage( 0.1 ) + end + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task#TASK_A2G Task + function Fsm:onafterEngage( TaskUnit, Task ) + self:E( { self } ) + self:__Account( 0.1 ) + self:__RouteToTarget(0.1 ) + self:__RouteToTargets( -10 ) + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2G#TASK_A2G Task + function Fsm:onafterRouteToTarget( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- Determine the first Unit from the self.TargetSetUnit + + if Task:GetTargetZone( TaskUnit ) then + self:__RouteToTargetZone( 0.1 ) + else + local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT + if TargetUnit then + local PointVec2 = TargetUnit:GetPointVec2() + self:T( { TargetPointVec2 = PointVec2, PointVec2:GetX(), PointVec2:GetAlt(), PointVec2:GetZ() } ) + Task:SetTargetPointVec2( TargetUnit:GetPointVec2(), TaskUnit ) + end + self:__RouteToTargetPoint( 0.1 ) + end + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2G#TASK_A2G Task + function Fsm:onafterRouteToTargets( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT + if TargetUnit then + Task:SetTargetPointVec2( TargetUnit:GetPointVec2(), TaskUnit ) + end + self:__RouteToTargets( -10 ) + end - --_EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventPlayerLeaveUnit, self ) - --_EVENTDISPATCHER:OnDead( self._EventDead, self ) - --_EVENTDISPATCHER:OnCrash( self._EventDead, self ) - --_EVENTDISPATCHER:OnPilotDead( self._EventDead, self ) - return self + end - --- @param #TASK_A2G self + --- @param #TASK_A2G self function TASK_A2G:GetPlannedMenuText() return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" end + + --- @param #TASK_A2G self + -- @param Core.Point#POINT_VEC2 RendezVousPointVec2 The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map. + -- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2G:SetRendezVousPointVec2( RendezVousPointVec2, RendezVousRange, TaskUnit ) + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + ActRouteRendezVous:SetPointVec2( RendezVousPointVec2 ) + ActRouteRendezVous:SetRange( RendezVousRange ) + end + + --- @param #TASK_A2G self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Point#POINT_VEC2 The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map. + -- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. + function TASK_A2G:GetRendezVousPointVec2( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + return ActRouteRendezVous:GetPointVec2(), ActRouteRendezVous:GetRange() + end + + + + --- @param #TASK_A2G self + -- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2G:SetRendezVousZone( RendezVousZone, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + ActRouteRendezVous:SetZone( RendezVousZone ) + end + + --- @param #TASK_A2G self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Zone#ZONE_BASE The Zone object where the RendezVous is located on the map. + function TASK_A2G:GetRendezVousZone( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + return ActRouteRendezVous:GetZone() + end + + --- @param #TASK_A2G self + -- @param Core.Point#POINT_VEC2 TargetPointVec2 The PointVec2 object where the Target is located on the map. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2G:SetTargetPointVec2( TargetPointVec2, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + ActRouteTarget:SetPointVec2( TargetPointVec2 ) + end + + + --- @param #TASK_A2G self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Point#POINT_VEC2 The PointVec2 object where the Target is located on the map. + function TASK_A2G:GetTargetPointVec2( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + return ActRouteTarget:GetPointVec2() end + --- @param #TASK_A2G self + -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2G:SetTargetZone( TargetZone, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + ActRouteTarget:SetZone( TargetZone ) + end + + + --- @param #TASK_A2G self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. + function TASK_A2G:GetTargetZone( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + return ActRouteTarget:GetZone() + end + + --- Set a score when a target in scope of the A2G attack, has been destroyed . + -- @param #TASK_A2G self + -- @param #string Text The text to display to the player, when the target has been destroyed. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G + function TASK_A2G:SetScoreOnDestroy( Text, Score, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScoreProcess( "Engaging", "Account", "Account", Text, Score ) + + return self + end + + --- Set a score when all the targets in scope of the A2G attack, have been destroyed. + -- @param #TASK_A2G self + -- @param #string Text The text to display to the player, when all targets hav been destroyed. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G + function TASK_A2G:SetScoreOnSuccess( Text, Score, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Success", Text, Score ) + + return self + end + + --- Set a penalty when the A2G attack has failed. + -- @param #TASK_A2G self + -- @param #string Text The text to display to the player, when the A2G attack has failed. + -- @param #number Penalty The penalty in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G + function TASK_A2G:SetPenaltyOnFailed( Text, Penalty, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Failed", Text, Penalty ) + + return self + end + + +end + + +do -- TASK_SEAD + + --- The TASK_SEAD class + -- @type TASK_SEAD + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + TASK_SEAD = { + ClassName = "TASK_SEAD", + } + + --- Instantiates a new TASK_SEAD. + -- @param #TASK_SEAD self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Set#SET_UNIT UnitSetTargets + -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. + -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. + -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. + -- @return #TASK_SEAD self + function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit ) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD" ) ) -- #TASK_SEAD + self:F() + + return self + end + +end + +do -- TASK_BAI + + --- The TASK_BAI class + -- @type TASK_BAI + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + TASK_BAI = { + ClassName = "TASK_BAI", + } + + --- Instantiates a new TASK_BAI. + -- @param #TASK_BAI self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Set#SET_UNIT UnitSetTargets + -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. + -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. + -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. + -- @return #TASK_BAI self + function TASK_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit ) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI" ) ) -- #TASK_BAI + self:F() + + return self + end + +end + +do -- TASK_CAS + + --- The TASK_CAS class + -- @type TASK_CAS + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + TASK_CAS = { + ClassName = "TASK_CAS", + } + + --- Instantiates a new TASK_CAS. + -- @param #TASK_CAS self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Set#SET_UNIT UnitSetTargets + -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. + -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. + -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. + -- @return #TASK_CAS self + function TASK_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit ) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS" ) ) -- #TASK_CAS + self:F() + + return self + end + +end diff --git a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua new file mode 100644 index 000000000..a30c335ba --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua @@ -0,0 +1,302 @@ +--- **Tasking** - The TASK_A2G_DISPATCHER creates and manages player TASK_A2G tasks based on detected targets. +-- +-- === +-- +-- # 1) @{#TASK_A2G_DISPATCHER} class, extends @{#DETECTION_MANAGER} +-- +-- The @{#TASK_A2G_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of FAC (groups). +-- The FAC will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. +-- Find a summary below describing for which situation a task type is created: +-- +-- * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter. +-- * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter. +-- * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars. +-- +-- Other task types will follow... +-- +-- 3.1) TASK_A2G_DISPATCHER constructor: +-- -------------------------------------- +-- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK_A2G_DISPATCHER instance. +-- +-- === +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-03-09: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module Task_A2G_Dispatcher + +do -- TASK_A2G_DISPATCHER + + --- TASK_A2G_DISPATCHER class. + -- @type TASK_A2G_DISPATCHER + -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. + -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. + -- @field Tasking.Mission#MISSION Mission + -- @extends Tasking.DetectionManager#DETECTION_MANAGER + TASK_A2G_DISPATCHER = { + ClassName = "TASK_A2G_DISPATCHER", + Mission = nil, + Detection = nil, + } + + + --- TASK_A2G_DISPATCHER constructor. + -- @param #TASK_A2G_DISPATCHER self + -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. + -- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. + -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. + -- @return #TASK_A2G_DISPATCHER self + function TASK_A2G_DISPATCHER:New( Mission, SetGroup, Detection ) + + -- Inherits from DETECTION_MANAGER + local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER + + self.Detection = Detection + self.Mission = Mission + + self:AddTransition( "Started", "Assign", "Started" ) + + --- OnAfter Transition Handler for Event Assign. + -- @function [parent=#TASK_A2G_DISPATCHER] OnAfterAssign + -- @param #TASK_A2G_DISPATCHER self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Tasking.Task_A2G#TASK_A2G Task + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param #string PlayerName + + self:__Start( 5 ) + + return self + end + + + --- Creates a SEAD task when there are targets for it. + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem + -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return #nil If there are no targets to be set. + function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem ) + self:F( { DetectedItem.ItemID } ) + + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + -- Determine if the set has radar targets. If it does, construct a SEAD task. + local RadarCount = DetectedSet:HasSEAD() + + if RadarCount > 0 then + + -- Here we're doing something advanced... We're copying the DetectedSet, but making a new Set only with SEADable Radar units in it. + local TargetSetUnit = SET_UNIT:New() + TargetSetUnit:SetDatabase( DetectedSet ) + TargetSetUnit:FilterHasSEAD() + TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. + + return TargetSetUnit + end + + return nil + end + + --- Creates a CAS task when there are targets for it. + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem + -- @return Tasking.Task#TASK + function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem ) + self:F( { DetectedItem.ItemID } ) + + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + + -- Determine if the set has radar targets. If it does, construct a SEAD task. + local GroundUnitCount = DetectedSet:HasGroundUnits() + local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) + + if GroundUnitCount > 0 and FriendliesNearBy == true then + + -- Copy the Set + local TargetSetUnit = SET_UNIT:New() + TargetSetUnit:SetDatabase( DetectedSet ) + TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. + + return TargetSetUnit + end + + return nil + end + + --- Creates a BAI task when there are targets for it. + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem + -- @return Tasking.Task#TASK + function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition ) + self:F( { DetectedItem.ItemID } ) + + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + + -- Determine if the set has radar targets. If it does, construct a SEAD task. + local GroundUnitCount = DetectedSet:HasGroundUnits() + local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) + + if GroundUnitCount > 0 and FriendliesNearBy == false then + + -- Copy the Set + local TargetSetUnit = SET_UNIT:New() + TargetSetUnit:SetDatabase( DetectedSet ) + TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. + + return TargetSetUnit + end + + return nil + end + + --- Evaluates the removal of the Task from the Mission. + -- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned". + -- @param #TASK_A2G_DISPATCHER self + -- @param Tasking.Mission#MISSION Mission + -- @param Tasking.Task#TASK Task + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem + -- @return Tasking.Task#TASK + function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedItem ) + + if Task then + if Task:IsStatePlanned() and DetectedItem.Changed == true then + self:E( "Removing Tasking: " .. Task:GetTaskName() ) + Task = Mission:RemoveTask( Task ) + end + end + + return Task + end + + + --- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}. + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. + -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. + function TASK_A2G_DISPATCHER:ProcessDetected( Detection ) + self:E() + + local AreaMsg = {} + local TaskMsg = {} + local ChangeMsg = {} + + local Mission = self.Mission + local ReportSEAD = REPORT:New( "- SEAD Tasks:") + local ReportCAS = REPORT:New( "- CAS Tasks:") + local ReportBAI = REPORT:New( "- BAI Tasks:") + local ReportChanges = REPORT:New( " - Changes:" ) + + --- First we need to the detected targets. + for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do + + local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set -- Functional.Detection#DETECTION_BASE.DetectedSet + local DetectedZone = DetectedItem.Zone + self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) + DetectedSet:Flush() + + local ItemID = DetectedItem.ItemID + + -- Evaluate SEAD Tasking + local SEADTask = Mission:GetTask( string.format( "SEAD.%03d", ItemID ) ) + SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedItem ) + if not SEADTask then + local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... + if TargetSetUnit then + local Task = TASK_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", ItemID ), TargetSetUnit ) + Task:SetTargetZone( DetectedZone ) + Task:SetDispatcher( self ) + SEADTask = Mission:AddTask( Task ) + end + end + if SEADTask and SEADTask:IsStatePlanned() then + ReportSEAD:Add( string.format( " - %s.%02d - %s", "SEAD", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) + end + + -- Evaluate CAS Tasking + local CASTask = Mission:GetTask( string.format( "CAS.%03d", ItemID ) ) + CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedItem ) + if not CASTask then + local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... + if TargetSetUnit then + local Task = TASK_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", ItemID ), TargetSetUnit ) + Task:SetTargetZone( DetectedZone ) + Task:SetDispatcher( self ) + CASTask = Mission:AddTask( Task ) + end + end + if CASTask and CASTask:IsStatePlanned() then + ReportCAS:Add( string.format( " - %s.%02d - %s", "CAS", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) + end + + -- Evaluate BAI Tasking + local BAITask = Mission:GetTask( string.format( "BAI.%03d", ItemID ) ) + BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedItem ) + if not BAITask then + local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed... + if TargetSetUnit then + local Task = TASK_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", ItemID ), TargetSetUnit ) + Task:SetTargetZone( DetectedZone ) + Task:SetDispatcher( self ) + BAITask = Mission:AddTask( Task ) + end + end + if BAITask and BAITask:IsStatePlanned() then + ReportBAI:Add( string.format( " - %s.%02d - %s", "BAI", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) + end + + + -- Loop through the changes ... + local ChangeText = Detection:GetChangeText( DetectedItem ) + ReportChanges:Add( ChangeText ) + + + -- OK, so the tasking has been done, now delete the changes reported for the area. + Detection:AcceptChanges( DetectedItem ) + + end + + -- TODO set menus using the HQ coordinator + Mission:GetCommandCenter():SetMenu() + + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + if not TaskGroup:GetState( TaskGroup, "Assigned" ) then + Mission:GetCommandCenter():MessageToGroup( + string.format( "HQ Reporting - Planned tasks for mission '%s':\n%s\n", + self.Mission:GetName(), + string.format( "%s\n\n%s\n\n%s\n\n%s", ReportSEAD:Text(), ReportCAS:Text(), ReportBAI:Text(), ReportChanges:Text() + ) + ), TaskGroup + ) + end + end + + return true + end + +end \ No newline at end of file diff --git a/Moose Development/Moose/Tasking/Task_Pickup.lua b/Moose Development/Moose/Tasking/Task_Pickup.lua index 9754ca417..0e1b103c5 100644 --- a/Moose Development/Moose/Tasking/Task_Pickup.lua +++ b/Moose Development/Moose/Tasking/Task_Pickup.lua @@ -41,11 +41,6 @@ do -- TASK_PICKUP local self = BASE:Inherit( self, TASK:New( Mission, AssignedSetGroup, TaskName, TaskType, "PICKUP" ) ) self:F() - _EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventPlayerLeaveUnit, self ) - _EVENTDISPATCHER:OnDead( self._EventDead, self ) - _EVENTDISPATCHER:OnCrash( self._EventDead, self ) - _EVENTDISPATCHER:OnPilotDead( self._EventDead, self ) - return self end diff --git a/Moose Development/Moose/Tasking/Task_SEAD.lua b/Moose Development/Moose/Tasking/Task_SEAD.lua deleted file mode 100644 index cd40cd71e..000000000 --- a/Moose Development/Moose/Tasking/Task_SEAD.lua +++ /dev/null @@ -1,78 +0,0 @@ ---- This module contains the TASK_SEAD classes. --- --- 1) @{#TASK_SEAD} class, extends @{Task#TASK} --- ================================================= --- The @{#TASK_SEAD} class defines a SEAD task for a @{Set} of Target Units, located at a Target Zone, --- based on the tasking capabilities defined in @{Task#TASK}. --- The TASK_SEAD is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses: --- --- * **None**: Start of the process --- * **Planned**: The SEAD task is planned. Upon Planned, the sub-process @{Process_Fsm.Assign#ACT_ASSIGN_ACCEPT} is started to accept the task. --- * **Assigned**: The SEAD task is assigned to a @{Group#GROUP}. Upon Assigned, the sub-process @{Process_Fsm.Route#ACT_ROUTE} is started to route the active Units in the Group to the attack zone. --- * **Success**: The SEAD task is successfully completed. Upon Success, the sub-process @{Process_SEAD#PROCESS_SEAD} is started to follow-up successful SEADing of the targets assigned in the task. --- * **Failed**: The SEAD task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. --- --- === --- --- ### Authors: FlightControl - Design and Programming --- --- @module Task_SEAD - - - -do -- TASK_SEAD - - --- The TASK_SEAD class - -- @type TASK_SEAD - -- @field Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - TASK_SEAD = { - ClassName = "TASK_SEAD", - } - - --- Instantiates a new TASK_SEAD. - -- @param #TASK_SEAD self - -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. - -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets - -- @param Core.Zone#ZONE_BASE TargetZone - -- @return #TASK_SEAD self - function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TargetZone ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, "SEAD" ) ) -- Tasking.Task_SEAD#TASK_SEAD - self:F() - - self.TargetSetUnit = TargetSetUnit - self.TargetZone = TargetZone - - local Fsm = self:GetUnitProcess() - - Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "Route", Rejected = "Eject" } ) - Fsm:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } ) - Fsm:AddTransition( "Rejected", "Eject", "Planned" ) - Fsm:AddTransition( "Arrived", "Update", "Updated" ) - Fsm:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "SEAD" ), { Accounted = "Success" } ) - Fsm:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) ) - Fsm:AddTransition( "Accounted", "Success", "Success" ) - Fsm:AddTransition( "Failed", "Fail", "Failed" ) - - function Fsm:onenterUpdated( TaskUnit ) - self:E( { self } ) - self:Account() - self:Smoke() - end - --- _EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventPlayerLeaveUnit, self ) --- _EVENTDISPATCHER:OnDead( self._EventDead, self ) --- _EVENTDISPATCHER:OnCrash( self._EventDead, self ) --- _EVENTDISPATCHER:OnPilotDead( self._EventDead, self ) - - return self - end - - --- @param #TASK_SEAD self - function TASK_SEAD:GetPlannedMenuText() - return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" - end - -end diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 4d08b3fea..afab71f1d 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -73,7 +73,7 @@ CLIENT = { -- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() ) -- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() ) -- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() ) -function CLIENT:Find( DCSUnit ) +function CLIENT:Find( DCSUnit, Error ) local ClientName = DCSUnit:getName() local ClientFound = _DATABASE:FindClient( ClientName ) @@ -82,7 +82,9 @@ function CLIENT:Find( DCSUnit ) return ClientFound end - error( "CLIENT not found for: " .. ClientName ) + if not Error then + error( "CLIENT not found for: " .. ClientName ) + end end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index dd8fabff0..b81cda295 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -457,12 +457,20 @@ function CONTROLLABLE:CommandSwitchWayPoint( FromWayPoint, ToWayPoint ) return CommandSwitchWayPoint end ---- Perform stop route command +--- Create a stop route command, which returns a string containing the command. +-- Use the result in the method @{#CONTROLLABLE.SetCommand}(). +-- A value of true will make the ground group stop, a value of false will make it continue. +-- Note that this can only work on GROUP level, although individual UNITs can be commanded, the whole GROUP will react. +-- +-- Example missions: +-- +-- * GRP-310 +-- -- @param #CONTROLLABLE self --- @param #boolean StopRoute +-- @param #boolean StopRoute true if the ground unit needs to stop, false if it needs to continue to move. -- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:CommandStopRoute( StopRoute, Index ) - self:F2( { StopRoute, Index } ) +function CONTROLLABLE:CommandStopRoute( StopRoute ) + self:F2( { StopRoute } ) local CommandStopRoute = { id = 'StopRoute', @@ -536,47 +544,35 @@ function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, At return DCSTask end - --- (AIR) Attack the Unit. -- @param #CONTROLLABLE self --- @param Wrapper.Unit#UNIT AttackUnit The unit. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. +-- @param Wrapper.Unit#UNIT AttackUnit The UNIT. +-- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. -- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks. --- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. +-- @param #number Altitude (optional) The altitude from where to attack. +-- @param #boolean Visible (optional) not a clue. +-- @param #number WeaponType (optional) The WeaponType. -- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskAttackUnit( AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack ) - self:F2( { self.ControllableName, AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack } ) - - -- AttackUnit = { - -- id = 'AttackUnit', - -- params = { - -- unitId = Unit.ID, - -- weaponType = number, - -- expend = enum AI.Task.WeaponExpend - -- attackQty = number, - -- direction = Azimuth, - -- attackQtyLimit = boolean, - -- controllableAttack = boolean, - -- } - -- } +function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType ) + self:F2( { self.ControllableName, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType } ) local DCSTask DCSTask = { id = 'AttackUnit', params = { - altitudeEnabled = true, unitId = AttackUnit:GetID(), - attackQtyLimit = AttackQtyLimit or false, - attackQty = AttackQty or 2, + groupAttack = GroupAttack or false, + visible = Visible or false, expend = WeaponExpend or "Auto", - altitude = 2000, - directionEnabled = true, - groupAttack = true, - --weaponType = WeaponType or 1073741822, - direction = Direction or 0, + directionEnabled = Direction and true or false, + direction = Direction, + altitudeEnabled = Altitude and true or false, + altitude = Altitude or 30, + attackQtyLimit = AttackQty and true or false, + attackQty = AttackQty, + weaponType = WeaponType } } @@ -1191,7 +1187,7 @@ function CONTROLLABLE:EnRouteTaskEngageGroup( AttackGroup, Priority, WeaponType, end ---- (AIR) Attack the Unit. +--- (AIR) Search and attack the Unit. -- @param #CONTROLLABLE self -- @param Wrapper.Unit#UNIT EngageUnit The UNIT. -- @param #number Priority (optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. @@ -1661,90 +1657,6 @@ function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation ) return nil end ---- (AIR) Return the Controllable to an @{Airbase#AIRBASE} --- A speed can be given in km/h. --- A given formation can be given. --- @param #CONTROLLABLE self --- @param Wrapper.Airbase#AIRBASE ReturnAirbase The @{Airbase#AIRBASE} to return to. --- @param #number Speed (optional) The speed. --- @return #string The route -function CONTROLLABLE:RouteReturnToAirbase( ReturnAirbase, Speed ) - self:F2( { ReturnAirbase, Speed } ) - --- Example --- [4] = --- { --- ["alt"] = 45, --- ["type"] = "Land", --- ["action"] = "Landing", --- ["alt_type"] = "BARO", --- ["formation_template"] = "", --- ["properties"] = --- { --- ["vnav"] = 1, --- ["scale"] = 0, --- ["angle"] = 0, --- ["vangle"] = 0, --- ["steer"] = 2, --- }, -- end of ["properties"] --- ["ETA"] = 527.81058817743, --- ["airdromeId"] = 12, --- ["y"] = 243127.2973737, --- ["x"] = -5406.2803440839, --- ["name"] = "DictKey_WptName_53", --- ["speed"] = 138.88888888889, --- ["ETA_locked"] = false, --- ["task"] = --- { --- ["id"] = "ComboTask", --- ["params"] = --- { --- ["tasks"] = --- { --- }, -- end of ["tasks"] --- }, -- end of ["params"] --- }, -- end of ["task"] --- ["speed_locked"] = true, --- }, -- end of [4] - - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - - local ControllablePoint = self:GetVec2() - local ControllableVelocity = self:GetMaxVelocity() - - local PointFrom = {} - PointFrom.x = ControllablePoint.x - PointFrom.y = ControllablePoint.y - PointFrom.type = "Turning Point" - PointFrom.action = "Turning Point" - PointFrom.speed = ControllableVelocity - - - local PointTo = {} - local AirbasePoint = ReturnAirbase:GetVec2() - - PointTo.x = AirbasePoint.x - PointTo.y = AirbasePoint.y - PointTo.type = "Land" - PointTo.action = "Landing" - PointTo.airdromeId = ReturnAirbase:GetID()-- Airdrome ID - self:T(PointTo.airdromeId) - --PointTo.alt = 0 - - local Points = { PointFrom, PointTo } - - self:T3( Points ) - - local Route = { points = Points, } - - return Route - end - - return nil -end -- Commands @@ -1785,6 +1697,8 @@ function CONTROLLABLE:GetTaskRoute() return routines.utils.deepCopy( _DATABASE.Templates.Controllables[self.ControllableName].Template.route.points ) end + + --- Return the route of a controllable by using the @{Database#DATABASE} class. -- @param #CONTROLLABLE self -- @param #number Begin The route point from where the copy will start. The base route point is 0. diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 4669911e3..c905231b4 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1,8 +1,8 @@ ---- This module contains the GROUP class. +--- **Wrapper** -- GROUP is a wrapper class for the DCS Class Group. -- --- 1) @{Group#GROUP} class, extends @{Controllable#CONTROLLABLE} --- ============================================================= --- The @{Group#GROUP} class is a wrapper class to handle the DCS Group objects: +-- === +-- +-- The @{#GROUP} class is a wrapper class to handle the DCS Group objects: -- -- * Support all DCS Group APIs. -- * Enhance with Group specific APIs not in the DCS Group API set. @@ -11,60 +11,8 @@ -- -- **IMPORTANT: ONE SHOULD NEVER SANATIZE these GROUP OBJECT REFERENCES! (make the GROUP object references nil).** -- --- 1.1) GROUP reference methods --- ----------------------- --- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object. --- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the @{SPAWN} class). --- --- The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference --- using the DCS Group or the DCS GroupName. --- --- Another thing to know is that GROUP objects do not "contain" the DCS Group object. --- The GROUP methods will reference the DCS Group object by name when it is needed during API execution. --- If the DCS Group object does not exist or is nil, the GROUP methods will return nil and log an exception in the DCS.log file. --- --- The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance: --- --- * @{#GROUP.Find}(): Find a GROUP instance from the _DATABASE object using a DCS Group object. --- * @{#GROUP.FindByName}(): Find a GROUP instance from the _DATABASE object using a DCS Group name. --- --- ## 1.2) GROUP task methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} task methods section for a description of the task methods. --- --- ### 1.2.4) Obtain the mission from group templates +-- See the detailed documentation on the GROUP class. -- --- Group templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a group and assign it to another: --- --- * @{Controllable#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template. --- --- ## 1.3) GROUP Command methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} command methods section for a description of the command methods. --- --- ## 1.4) GROUP option methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} option methods section for a description of the option methods. --- --- ## 1.5) GROUP Zone validation methods --- --- The group can be validated whether it is completely, partly or not within a @{Zone}. --- Use the following Zone validation methods on the group: --- --- * @{#GROUP.IsCompletelyInZone}: Returns true if all units of the group are within a @{Zone}. --- * @{#GROUP.IsPartlyInZone}: Returns true if some units of the group are within a @{Zone}. --- * @{#GROUP.IsNotInZone}: Returns true if none of the group units of the group are within a @{Zone}. --- --- The zone can be of any @{Zone} class derived from @{Zone#ZONE_BASE}. So, these methods are polymorphic to the zones tested on. --- --- ## 1.6) GROUP AI methods --- --- A GROUP has AI methods to control the AI activation. --- --- * @{#GROUP.SetAIOnOff}(): Turns the GROUP AI On or Off. --- * @{#GROUP.SetAIOn}(): Turns the GROUP AI On. --- * @{#GROUP.SetAIOff}(): Turns the GROUP AI Off. --- -- ==== -- -- # **API CHANGE HISTORY** @@ -76,6 +24,8 @@ -- -- Hereby the change log: -- +-- 2017-03-26: GROUP:**RouteRTB( RTBAirbase, Speed )** added. +-- -- 2017-03-07: GROUP:**HandleEvent( Event, EventFunction )** added. -- 2017-03-07: GROUP:**UnHandleEvent( Event )** added. -- @@ -100,10 +50,66 @@ -- @module Group -- @author FlightControl ---- The GROUP class --- @type GROUP +--- @type GROUP -- @extends Wrapper.Controllable#CONTROLLABLE -- @field #string GroupName The name of the group. + +--- +-- # GROUP class, extends @{Controllable#CONTROLLABLE} +-- +-- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object. +-- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the @{SPAWN} class). +-- +-- The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference +-- using the DCS Group or the DCS GroupName. +-- +-- Another thing to know is that GROUP objects do not "contain" the DCS Group object. +-- The GROUP methods will reference the DCS Group object by name when it is needed during API execution. +-- If the DCS Group object does not exist or is nil, the GROUP methods will return nil and log an exception in the DCS.log file. +-- +-- The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance: +-- +-- * @{#GROUP.Find}(): Find a GROUP instance from the _DATABASE object using a DCS Group object. +-- * @{#GROUP.FindByName}(): Find a GROUP instance from the _DATABASE object using a DCS Group name. +-- +-- ## GROUP task methods +-- +-- A GROUP is a @{Controllable}. See the @{Controllable} task methods section for a description of the task methods. +-- +-- ### Obtain the mission from group templates +-- +-- Group templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a group and assign it to another: +-- +-- * @{Controllable#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template. +-- +-- ## GROUP Command methods +-- +-- A GROUP is a @{Controllable}. See the @{Controllable} command methods section for a description of the command methods. +-- +-- ## GROUP option methods +-- +-- A GROUP is a @{Controllable}. See the @{Controllable} option methods section for a description of the option methods. +-- +-- ## GROUP Zone validation methods +-- +-- The group can be validated whether it is completely, partly or not within a @{Zone}. +-- Use the following Zone validation methods on the group: +-- +-- * @{#GROUP.IsCompletelyInZone}: Returns true if all units of the group are within a @{Zone}. +-- * @{#GROUP.IsPartlyInZone}: Returns true if some units of the group are within a @{Zone}. +-- * @{#GROUP.IsNotInZone}: Returns true if none of the group units of the group are within a @{Zone}. +-- +-- The zone can be of any @{Zone} class derived from @{Zone#ZONE_BASE}. So, these methods are polymorphic to the zones tested on. +-- +-- ## GROUP AI methods +-- +-- A GROUP has AI methods to control the AI activation. +-- +-- * @{#GROUP.SetAIOnOff}(): Turns the GROUP AI On or Off. +-- * @{#GROUP.SetAIOn}(): Turns the GROUP AI On. +-- * @{#GROUP.SetAIOff}(): Turns the GROUP AI Off. +-- +-- @field #GROUP GROUP GROUP = { ClassName = "GROUP", } @@ -113,7 +119,7 @@ GROUP = { -- @param Dcs.DCSWrapper.Group#Group GroupName The DCS Group name -- @return #GROUP self function GROUP:Register( GroupName ) - local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) + self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) self:F2( GroupName ) self.GroupName = GroupName @@ -177,19 +183,33 @@ function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3 return nil end ---- Returns if the DCS Group is alive. --- When the group exists at run-time, this method will return true, otherwise false. +--- Returns if the Group is alive. +-- The Group must: +-- +-- * Exist at run-time. +-- * Has at least one unit. +-- +-- When the first @{Unit} of the Group is active, it will return true. +-- If the first @{Unit} of the Group is inactive, it will return false. +-- -- @param #GROUP self --- @return #boolean true if the DCS Group is alive. +-- @return #boolean true if the Group is alive and active. +-- @return #boolean false if the Group is alive but inactive. +-- @return #nil if the group does not exist anymore. function GROUP:IsAlive() self:F2( self.GroupName ) - local DCSGroup = self:GetDCSObject() + local DCSGroup = self:GetDCSObject() -- Dcs.DCSGroup#Group if DCSGroup then - local GroupIsAlive = DCSGroup:isExist() and DCSGroup:getUnit(1) ~= nil - self:T3( GroupIsAlive ) - return GroupIsAlive + if DCSGroup:isExist() then + local DCSUnit = DCSGroup:getUnit(1) -- Dcs.DCSUnit#Unit + if DCSUnit then + local GroupIsAlive = DCSUnit:isActive() + self:T3( GroupIsAlive ) + return GroupIsAlive + end + end end return nil @@ -231,7 +251,7 @@ function GROUP:GetCategory() return nil end ---- Returns the category name of the DCS Group. +--- Returns the category name of the #GROUP. -- @param #GROUP self -- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship function GROUP:GetCategoryName() @@ -436,6 +456,7 @@ function GROUP:GetVec2() end --- Returns the current Vec3 vector of the first DCS Unit in the GROUP. +-- @param #GROUP self -- @return Dcs.DCSTypes#Vec3 Current Vec3 of the first DCS Unit of the GROUP. function GROUP:GetVec3() self:F2( self.GroupName ) @@ -445,7 +466,65 @@ function GROUP:GetVec3() return GroupVec3 end +--- Returns a POINT_VEC2 object indicating the point in 2D of the first UNIT of the GROUP within the mission. +-- @param #GROUP self +-- @return Core.Point#POINT_VEC2 The 2D point vector of the first DCS Unit of the GROUP. +-- @return #nil The first UNIT is not existing or alive. +function GROUP:GetPointVec2() + self:F2(self.GroupName) + local FirstUnit = self:GetUnit(1) + + if FirstUnit then + local FirstUnitPointVec2 = FirstUnit:GetPointVec2() + self:T3(FirstUnitPointVec2) + return FirstUnitPointVec2 + end + + return nil +end + +--- Returns a random @{DCSTypes#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP. +-- @param #GROUP self +-- @param #number Radius +-- @return Dcs.DCSTypes#Vec3 The random 3D point vector around the first UNIT of the GROUP. +-- @return #nil The GROUP is invalid or empty +-- @usage +-- -- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP +function GROUP:GetRandomVec3(Radius) + self:F2(self.GroupName) + + local FirstUnit = self:GetUnit(1) + + if FirstUnit then + local FirstUnitRandomPointVec3 = FirstUnit:GetRandomVec3(Radius) + self:T3(FirstUnitRandomPointVec3) + return FirstUnitRandomPointVec3 + end + + return nil +end + +--- Returns the mean heading of every UNIT in the GROUP in degrees +-- @param #GROUP self +-- @return #number mean heading of the GROUP +-- @return #nil The first UNIT is not existing or alive. +function GROUP:GetHeading() + self:F2(self.GroupName) + + local GroupSize = self:GetSize() + local HeadingAccumulator = 0 + + if GroupSize then + for i = 1, GroupSize do + HeadingAccumulator = HeadingAccumulator + self:GetUnit(i):GetHeading() + end + return math.floor(HeadingAccumulator / GroupSize) + end + + return nil + +end do -- Is Zone methods @@ -895,6 +974,76 @@ function GROUP:InAir() return nil end +do -- Route methods + + --- (AIR) Return the Group to an @{Airbase#AIRBASE}. + -- The following things are to be taken into account: + -- + -- * The group is respawned to achieve the RTB, there may be side artefacts as a result of this. (Like weapons suddenly come back). + -- * A group consisting out of more than one unit, may rejoin formation when respawned. + -- * A speed can be given in km/h. If no speed is specified, the maximum speed of the first unit will be taken to return to base. + -- * When there is no @{Airbase} object specified, the group will return to the home base if the route of the group is pinned at take-off or at landing to a base. + -- * When there is no @{Airbase} object specified and the group route is not pinned to any airbase, it will return to the nearest airbase. + -- + -- @param #GROUP self + -- @param Wrapper.Airbase#AIRBASE RTBAirbase (optional) The @{Airbase} to return to. If blank, the controllable will return to the nearest friendly airbase. + -- @param #number Speed (optional) The Speed, if no Speed is given, the maximum Speed of the first unit is selected. + -- @return #GROUP + function GROUP:RouteRTB( RTBAirbase, Speed ) + self:F2( { RTBAirbase, Speed } ) + + local DCSGroup = self:GetDCSObject() + + if DCSGroup then + + if RTBAirbase then + + local GroupPoint = self:GetVec2() + local GroupVelocity = self:GetUnit(1):GetDesc().speedMax + + local PointFrom = {} + PointFrom.x = GroupPoint.x + PointFrom.y = GroupPoint.y + PointFrom.type = "Turning Point" + PointFrom.action = "Turning Point" + PointFrom.speed = GroupVelocity + + + local PointTo = {} + local AirbasePointVec2 = RTBAirbase:GetPointVec2() + local AirbaseAirPoint = AirbasePointVec2:RoutePointAir( + POINT_VEC3.RoutePointAltType.BARO, + "Land", + "Landing", + Speed or self:GetUnit(1):GetDesc().speedMax + ) + + AirbaseAirPoint["airdromeId"] = RTBAirbase:GetID() + AirbaseAirPoint["speed_locked"] = true, + + self:E(AirbaseAirPoint ) + + local Points = { PointFrom, AirbaseAirPoint } + + self:T3( Points ) + + local Template = self:GetTemplate() + Template.route.points = Points + self:Respawn( Template ) + + self:Route( Points ) + + self:Respawn(Template) + else + self:ClearTasks() + end + end + + return self + end + +end + function GROUP:OnReSpawn( ReSpawnFunction ) self.ReSpawnFunction = ReSpawnFunction @@ -926,3 +1075,29 @@ do -- Event Handling end end + +do -- Players + + --- Get player names + -- @param #GROUP self + -- @return #table The group has players, an array of player names is returned. + -- @return #nil The group has no players + function GROUP:GetPlayerNames() + + local PlayerNames = nil + + local Units = self:GetUnits() + for UnitID, UnitData in pairs( Units ) do + local Unit = UnitData -- Wrapper.Unit#UNIT + local PlayerName = Unit:GetPlayerName() + if PlayerName and PlayerName ~= "" then + PlayerNames = PlayerNames or {} + table.insert( PlayerNames, PlayerName ) + end + end + + self:F( PlayerNames ) + return PlayerNames + end + +end \ No newline at end of file diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index edcd3ffff..08a22b044 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -58,17 +58,19 @@ function IDENTIFIABLE:New( IdentifiableName ) return self end ---- Returns if the Identifiable is alive. +--- Returns if the Identifiable is alive. +-- If the Identifiable is not alive, nil is returned. +-- If the Identifiable is alive, true is returned. -- @param #IDENTIFIABLE self -- @return #boolean true if Identifiable is alive. --- @return #nil The DCS Identifiable is not existing or alive. +-- @return #nil if the Identifiable is not existing or is not alive. function IDENTIFIABLE:IsAlive() self:F3( self.IdentifiableName ) - local DCSIdentifiable = self:GetDCSObject() + local DCSIdentifiable = self:GetDCSObject() -- Dcs.DCSObject#Object if DCSIdentifiable then - local IdentifiableIsAlive = DCSIdentifiable:isExist() + local IdentifiableIsAlive = DCSIdentifiable:isExist() return IdentifiableIsAlive end diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 9be429b8f..e6bd95cd3 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -135,8 +135,11 @@ end --- Returns a random @{DCSTypes#Vec3} vector within a range, indicating the point in 3D of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self +-- @param #number Radius -- @return Dcs.DCSTypes#Vec3 The 3D point vector of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. +-- @usage +-- -- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP function POSITIONABLE:GetRandomVec3( Radius ) self:F2( self.PositionableName ) @@ -144,14 +147,20 @@ function POSITIONABLE:GetRandomVec3( Radius ) if DCSPositionable then local PositionablePointVec3 = DCSPositionable:getPosition().p - local PositionableRandomVec3 = {} - local angle = math.random() * math.pi*2; - PositionableRandomVec3.x = PositionablePointVec3.x + math.cos( angle ) * math.random() * Radius; - PositionableRandomVec3.y = PositionablePointVec3.y - PositionableRandomVec3.z = PositionablePointVec3.z + math.sin( angle ) * math.random() * Radius; - self:T3( PositionableRandomVec3 ) - return PositionableRandomVec3 + if Radius then + local PositionableRandomVec3 = {} + local angle = math.random() * math.pi*2; + PositionableRandomVec3.x = PositionablePointVec3.x + math.cos( angle ) * math.random() * Radius; + PositionableRandomVec3.y = PositionablePointVec3.y + PositionableRandomVec3.z = PositionablePointVec3.z + math.sin( angle ) * math.random() * Radius; + + self:T3( PositionableRandomVec3 ) + return PositionableRandomVec3 + else + self:E("Radius is nil, returning the PointVec3 of the POSITIONABLE", PositionablePointVec3) + return PositionablePointVec3 + end end return nil @@ -219,6 +228,7 @@ end --- Returns the POSITIONABLE heading in degrees. -- @param Wrapper.Positionable#POSITIONABLE self -- @return #number The POSTIONABLE heading +-- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetHeading() local DCSPositionable = self:GetDCSObject() @@ -432,7 +442,11 @@ function POSITIONABLE:Message( Message, Duration, Name ) return nil end - - - - +--- Create a @{Radio#RADIO}, to allow radio transmission for this POSITIONABLE. +-- Set parameters with the methods provided, then use RADIO:Broadcast() to actually broadcast the message +-- @param #POSITIONABLE self +-- @return #RADIO Radio +function POSITIONABLE:GetRadio() + self:F2(self) + return RADIO:New(self) +end diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index ea457ace0..0bedb8918 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -56,7 +56,7 @@ function STATIC:FindByName( StaticName, RaiseError ) self.StaticName = StaticName if StaticFound then - StaticFound:F( { StaticName } ) + StaticFound:F3( { StaticName } ) return StaticFound end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index ad0fc58be..68be9a981 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -1,7 +1,7 @@ ---- This module contains the UNIT class. +--- **Wrapper** - UNIT is a wrapper class for the DCS Class Unit. +-- +-- === -- --- 1) @{#UNIT} class, extends @{Controllable#CONTROLLABLE} --- =========================================================== -- The @{#UNIT} class is a wrapper class to handle the DCS Unit objects: -- -- * Support all DCS Unit APIs. @@ -9,9 +9,14 @@ -- * Handle local Unit Controller. -- * Manage the "state" of the DCS Unit. -- --- --- 1.1) UNIT reference methods --- ---------------------- +-- @module Unit + +--- @type UNIT +-- @extends Wrapper.Controllable#CONTROLLABLE + +--- +-- # UNIT class, extends @{Controllable#CONTROLLABLE} +-- -- For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _@{DATABASE} object. -- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Unit objects are spawned (using the @{SPAWN} class). -- @@ -29,15 +34,15 @@ -- -- IMPORTANT: ONE SHOULD NEVER SANATIZE these UNIT OBJECT REFERENCES! (make the UNIT object references nil). -- --- 1.2) DCS UNIT APIs --- ------------------ +-- ## DCS UNIT APIs +-- -- The DCS Unit APIs are used extensively within MOOSE. The UNIT class has for each DCS Unit API a corresponding method. -- To be able to distinguish easily in your code the difference between a UNIT API call and a DCS Unit API call, -- the first letter of the method is also capitalized. So, by example, the DCS Unit method @{DCSWrapper.Unit#Unit.getName}() -- is implemented in the UNIT class as @{#UNIT.GetName}(). -- --- 1.3) Smoke, Flare Units --- ----------------------- +-- ## Smoke, Flare Units +-- -- The UNIT class provides methods to smoke or flare units easily. -- The @{#UNIT.SmokeBlue}(), @{#UNIT.SmokeGreen}(),@{#UNIT.SmokeOrange}(), @{#UNIT.SmokeRed}(), @{#UNIT.SmokeRed}() methods -- will smoke the unit in the corresponding color. Note that smoking a unit is done at the current position of the DCS Unit. @@ -45,36 +50,29 @@ -- The @{#UNIT.FlareGreen}(), @{#UNIT.FlareRed}(), @{#UNIT.FlareWhite}(), @{#UNIT.FlareYellow}() -- methods will fire off a flare in the air with the corresponding color. Note that a flare is a one-off shot and its effect is of very short duration. -- --- 1.4) Location Position, Point --- ----------------------------- +-- ## Location Position, Point +-- -- The UNIT class provides methods to obtain the current point or position of the DCS Unit. -- The @{#UNIT.GetPointVec2}(), @{#UNIT.GetVec3}() will obtain the current **location** of the DCS Unit in a Vec2 (2D) or a **point** in a Vec3 (3D) vector respectively. -- If you want to obtain the complete **3D position** including ori�ntation and direction vectors, consult the @{#UNIT.GetPositionVec3}() method respectively. -- --- 1.5) Test if alive --- ------------------ +-- ## Test if alive +-- -- The @{#UNIT.IsAlive}(), @{#UNIT.IsActive}() methods determines if the DCS Unit is alive, meaning, it is existing and active. -- --- 1.6) Test for proximity --- ----------------------- +-- ## Test for proximity +-- -- The UNIT class contains methods to test the location or proximity against zones or other objects. -- --- ### 1.6.1) Zones +-- ### Zones +-- -- To test whether the Unit is within a **zone**, use the @{#UNIT.IsInZone}() or the @{#UNIT.IsNotInZone}() methods. Any zone can be tested on, but the zone must be derived from @{Zone#ZONE_BASE}. -- --- ### 1.6.2) Units +-- ### Units +-- -- Test if another DCS Unit is within a given radius of the current DCS Unit, use the @{#UNIT.OtherUnitInRadius}() method. -- --- @module Unit --- @author FlightControl - - - - - ---- The UNIT class --- @type UNIT --- @extends Wrapper.Controllable#CONTROLLABLE +-- @field #UNIT UNIT UNIT = { ClassName="UNIT", } @@ -216,7 +214,7 @@ function UNIT:ReSpawn( SpawnVec3, Heading ) end -- Remove obscolete units from the group structure - i = 1 + local i = 1 while i <= #SpawnGroupTemplate.units do local UnitTemplateData = SpawnGroupTemplate.units[i] @@ -252,6 +250,27 @@ function UNIT:IsActive() return nil end +--- Returns if the Unit is alive. +-- If the Unit is not alive, nil is returned. +-- If the Unit is alive and active, true is returned. +-- If the Unit is alive but not active, false is returned. +-- @param #UNIT self +-- @return #boolean true if Unit is alive and active. +-- @return #boolean false if Unit is alive but not active. +-- @return #nil if the Unit is not existing or is not alive. +function UNIT:IsAlive() + self:F3( self.UnitName ) + + local DCSUnit = self:GetDCSObject() -- Dcs.DCSUnit#Unit + + if DCSUnit then + local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive() + return UnitIsAlive + end + + return nil +end + --- Returns the Unit's callsign - the localized string. @@ -522,6 +541,31 @@ function UNIT:GetLife0() return nil end +--- Returns the category name of the #UNIT. +-- @param #UNIT self +-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship +function UNIT:GetCategoryName() + self:F3( self.UnitName ) + + local DCSUnit = self:GetDCSObject() + if DCSUnit then + local CategoryNames = { + [Unit.Category.AIRPLANE] = "Airplane", + [Unit.Category.HELICOPTER] = "Helicopter", + [Unit.Category.GROUND_UNIT] = "Ground Unit", + [Unit.Category.SHIP] = "Ship", + [Unit.Category.STRUCTURE] = "Structure", + } + local UnitCategory = DCSUnit:getDesc().category + self:T3( UnitCategory ) + + return CategoryNames[UnitCategory] + end + + return nil +end + + --- Returns the Unit's A2G threat level on a scale from 1 to 10 ... -- The following threat levels are foreseen: -- @@ -540,14 +584,14 @@ end function UNIT:GetThreatLevel() local Attributes = self:GetDesc().attributes - self:E( Attributes ) + self:T( Attributes ) local ThreatLevel = 0 local ThreatText = "" if self:IsGround() then - self:E( "Ground" ) + self:T( "Ground" ) local ThreatLevels = { "Unarmed", @@ -585,7 +629,7 @@ function UNIT:GetThreatLevel() if self:IsAir() then - self:E( "Air" ) + self:T( "Air" ) local ThreatLevels = { "Unarmed", @@ -619,7 +663,7 @@ function UNIT:GetThreatLevel() if self:IsShip() then - self:E( "Ship" ) + self:T( "Ship" ) --["Aircraft Carriers"] = {"Heavy armed ships",}, --["Cruisers"] = {"Heavy armed ships",}, diff --git a/Moose Development/ReleaseNotes.txt b/Moose Development/ReleaseNotes.txt index 4b2258255..64a580998 100644 --- a/Moose Development/ReleaseNotes.txt +++ b/Moose Development/ReleaseNotes.txt @@ -1,4 +1,4 @@ -2017-02-08 +2017-02-18 - Reworked some vector functions. -- POINT_VEC3:NewFromVec2( Vec2, LandHeightAdd ) added. @@ -6,9 +6,13 @@ -- ZONE_RADIUS:GetRandomPointVec3( inner, outer ) added. -- ZONE_POLYGON_BASE:GetRandomPointVec2() added. -- ZONE_POLYGON_BASE:GetRandomPointVec3() added. - - +2017-02-17 + + - Added ACT_ROUTE_POINT + -- Routes a controllable to a point with a defined distance. + -- Upon arrived within the engagement distance, an arrival text is shown. + 2016-12-06 - Renamed the documentation references following the structure of the files. diff --git a/Moose Mission Setup/Moose Mission Template/Moose_Mission_Template.miz b/Moose Mission Setup/Moose Mission Template/Moose_Mission_Template.miz index af6c3b818..5a3142fba 100644 Binary files a/Moose Mission Setup/Moose Mission Template/Moose_Mission_Template.miz and b/Moose Mission Setup/Moose Mission Template/Moose_Mission_Template.miz differ diff --git a/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua b/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua index fcb1e53a6..0135a9cc2 100644 --- a/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua +++ b/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua @@ -1,5 +1,5 @@ env.info( '*** MOOSE STATIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20170315_1916' ) +env.info( 'Moose Generation Timestamp: 20170328_1316' ) local base = _G Include = {} @@ -2796,9 +2796,54 @@ end -- -- === -- --- # 1) @{#BASE} class +-- The @{#BASE} class is the core root class from where every other class in moose is derived. -- --- All classes within the MOOSE framework are derived from the @{#BASE} class. +-- === +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) +-- YYYY-MM-DD: CLASS:**NewFunction( Params )** added +-- +-- Hereby the change log: +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * None. +-- +-- ### Authors: +-- +-- * **FlightControl**: Design & Programming +-- +-- @module Base + + + +local _TraceOnOff = true +local _TraceLevel = 1 +local _TraceAll = false +local _TraceClass = {} +local _TraceClassMethod = {} + +local _ClassID = 0 + +--- @type BASE +-- @field ClassName The name of the class. +-- @field ClassID The ID number of the class. +-- @field ClassNameAndID The name of the class concatenated with the ID number of the class. + +--- # 1) #BASE class +-- +-- All classes within the MOOSE framework are derived from the BASE class. -- -- BASE provides facilities for : -- @@ -2958,50 +3003,11 @@ end -- -- * @{#BASE.Inherit}: Inherits from a class. -- * @{#BASE.GetParent}: Returns the parent object from the object it is handling, or nil if there is no parent object. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) --- YYYY-MM-DD: CLASS:**NewFunction( Params )** added --- --- Hereby the change log: -- -- === -- --- # **AUTHORS and CONTRIBUTIONS** +-- @field #BASE BASE -- --- ### Contributions: --- --- * None. --- --- ### Authors: --- --- * **FlightControl**: Design & Programming --- --- @module Base - - - -local _TraceOnOff = true -local _TraceLevel = 1 -local _TraceAll = false -local _TraceClass = {} -local _TraceClassMethod = {} - -local _ClassID = 0 - ---- The BASE Class --- @type BASE --- @field ClassName The name of the class. --- @field ClassID The ID number of the class. --- @field ClassNameAndID The name of the class concatenated with the ID number of the class. BASE = { ClassName = "BASE", ClassID = 0, @@ -3048,12 +3054,14 @@ function BASE:_Destructor() --self:EventRemoveAll() end + +-- THIS IS WHY WE NEED LUA 5.2 ... function BASE:_SetDestructor() -- TODO: Okay, this is really technical... -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak... -- Therefore, I am parking this logic until I've properly discussed all this with the community. - --[[ + local proxy = newproxy(true) local proxyMeta = getmetatable(proxy) @@ -3068,7 +3076,7 @@ function BASE:_SetDestructor() -- table is about to be garbage-collected - then the __gc hook -- will be invoked and the destructor called rawset( self, '__proxy', proxy ) - --]] + end --- This is the worker method to inherit from a parent class. @@ -3084,7 +3092,7 @@ function BASE:Inherit( Child, Parent ) setmetatable( Child, Parent ) Child.__index = Child - Child:_SetDestructor() + --Child:_SetDestructor() end --self:T( 'Inherited from ' .. Parent.ClassName ) return Child @@ -3696,8 +3704,12 @@ end ---- This module contains the SCHEDULER class. +--- **Core** - SCHEDULER prepares and handles the **execution of functions over scheduled time (intervals)**. -- +-- ![Banner Image](..\Presentations\SCHEDULER\Dia1.JPG) +-- +-- === +-- -- # 1) @{Scheduler#SCHEDULER} class, extends @{Base#BASE} -- -- The @{Scheduler#SCHEDULER} class creates schedule. @@ -3845,6 +3857,13 @@ function SCHEDULER:Remove( ScheduleID ) _SCHEDULEDISPATCHER:Remove( self, ScheduleID ) end +--- Clears all pending schedules. +-- @param #SCHEDULER self +function SCHEDULER:Clear() + self:F3( ) + + _SCHEDULEDISPATCHER:Clear( self ) +end @@ -3925,7 +3944,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr -- Initialize the ObjectSchedulers array, which is a weakly coupled table. -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. - self.ObjectSchedulers = self.ObjectSchedulers or {} -- setmetatable( {}, { __mode = "v" } ) + self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) -- or {} if Scheduler.MasterObject then self.ObjectSchedulers[self.CallID] = Scheduler @@ -4042,11 +4061,15 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID ) if CallID then local Schedule = self.Schedule[Scheduler] - Schedule[CallID].ScheduleID = timer.scheduleFunction( - Schedule[CallID].CallHandler, - CallID, - timer.getTime() + Schedule[CallID].Start - ) + -- Only start when there is no ScheduleID defined! + -- This prevents to "Start" the scheduler twice with the same CallID... + if not Schedule[CallID].ScheduleID then + Schedule[CallID].ScheduleID = timer.scheduleFunction( + Schedule[CallID].CallHandler, + CallID, + timer.getTime() + Schedule[CallID].Start + ) + end else for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do self:Start( Scheduler, CallID ) -- Recursive @@ -4059,7 +4082,12 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) if CallID then local Schedule = self.Schedule[Scheduler] - timer.removeFunction( Schedule[CallID].ScheduleID ) + -- Only stop when there is a ScheduleID defined for the CallID. + -- So, when the scheduler was stopped before, do nothing. + if Schedule[CallID].ScheduleID then + timer.removeFunction( Schedule[CallID].ScheduleID ) + Schedule[CallID].ScheduleID = nil + end else for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do self:Stop( Scheduler, CallID ) -- Recursive @@ -4067,6 +4095,14 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) end end +function SCHEDULEDISPATCHER:Clear( Scheduler ) + self:F2( { Scheduler = Scheduler } ) + + for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do + self:Stop( Scheduler, CallID ) -- Recursive + end +end + --- **Core** - EVENT models DCS **event dispatching** using a **publish-subscribe** model. @@ -4355,7 +4391,7 @@ local _EVENTMETA = { }, [world.event.S_EVENT_TAKEOFF] = { Order = 1, - Event = "OnEventTakeOff", + Event = "OnEventTakeoff", Text = "S_EVENT_TAKEOFF" }, [world.event.S_EVENT_LAND] = { @@ -4496,11 +4532,11 @@ function EVENT:Init( EventID, EventClass ) -- Each event has a subtable of EventClasses, ordered by EventPriority. local EventPriority = EventClass:GetEventPriority() if not self.Events[EventID][EventPriority] then - self.Events[EventID][EventPriority] = {} + self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "k" } ) end if not self.Events[EventID][EventPriority][EventClass] then - self.Events[EventID][EventPriority][EventClass] = setmetatable( {}, { __mode = "k" } ) + self.Events[EventID][EventPriority][EventClass] = setmetatable( {}, { __mode = "v" } ) end return self.Events[EventID][EventPriority][EventClass] end @@ -4570,11 +4606,11 @@ end -- @param EventClass The instance of the class for which the event is. -- @param #function OnEventFunction -- @return #EVENT -function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, OnEventFunction ) +function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EventID ) self:F2( EventTemplate.name ) for EventUnitID, EventUnit in pairs( EventTemplate.units ) do - OnEventFunction( self, EventUnit.name, EventFunction, EventClass ) + self:OnEventForUnit( EventUnit.name, EventFunction, EventClass, EventID ) end return self end @@ -4588,9 +4624,9 @@ end function EVENT:OnEventGeneric( EventFunction, EventClass, EventID ) self:F2( { EventID } ) - local Event = self:Init( EventID, EventClass ) - Event.EventFunction = EventFunction - Event.EventClass = EventClass + local EventData = self:Init( EventID, EventClass ) + EventData.EventFunction = EventFunction + EventData.EventClass = EventClass return self end @@ -4606,13 +4642,13 @@ end function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID ) self:F2( UnitName ) - local Event = self:Init( EventID, EventClass ) - if not Event.EventUnit then - Event.EventUnit = {} + local EventData = self:Init( EventID, EventClass ) + if not EventData.EventUnit then + EventData.EventUnit = {} end - Event.EventUnit[UnitName] = {} - Event.EventUnit[UnitName].EventFunction = EventFunction - Event.EventUnit[UnitName].EventClass = EventClass + EventData.EventUnit[UnitName] = {} + EventData.EventUnit[UnitName].EventFunction = EventFunction + EventData.EventUnit[UnitName].EventClass = EventClass return self end @@ -4647,51 +4683,11 @@ do -- OnBirth function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnBirthForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Birth ) return self end - --- Set a new listener for an S_EVENT_BIRTH event, and registers the unit born. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnBirth( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_BIRTH ) - - return self - end - - --- Set a new listener for an S_EVENT_BIRTH event. - -- @param #EVENT self - -- @param #string EventDCSUnitName The id of the unit for the event to be handled. - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnBirthForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_BIRTH ) - - return self - end - - --- Stop listening to S_EVENT_BIRTH event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnBirthRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_BIRTH ) - - return self - end - - end do -- OnCrash @@ -4705,49 +4701,10 @@ do -- OnCrash function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnCrashForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Crash ) return self end - - --- Set a new listener for an S_EVENT_CRASH event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnCrash( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_CRASH ) - - return self - end - - --- Set a new listener for an S_EVENT_CRASH event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnCrashForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_CRASH ) - - return self - end - - --- Stop listening to S_EVENT_CRASH event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnCrashRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_CRASH ) - - return self - end end @@ -4762,96 +4719,13 @@ do -- OnDead function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnDeadForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Dead ) return self end - --- Set a new listener for an S_EVENT_DEAD event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnDead( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_DEAD ) - - return self - end - - - --- Set a new listener for an S_EVENT_DEAD event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnDeadForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_DEAD ) - - return self - end - - --- Stop listening to S_EVENT_DEAD event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnDeadRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_DEAD ) - - return self - end - - end -do -- OnPilotDead - - --- Set a new listener for an S_EVENT_PILOT_DEAD event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnPilotDead( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_PILOT_DEAD ) - - return self - end - - --- Set a new listener for an S_EVENT_PILOT_DEAD event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnPilotDeadForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_PILOT_DEAD ) - - return self - end - - --- Stop listening to S_EVENT_PILOT_DEAD event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnPilotDeadRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_PILOT_DEAD ) - - return self - end - -end do -- OnLand --- Create an OnLand event handler for a group @@ -4863,38 +4737,11 @@ do -- OnLand function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnLandForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Land ) return self end - --- Set a new listener for an S_EVENT_LAND event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnLandForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_LAND ) - - return self - end - - --- Stop listening to S_EVENT_LAND event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnLandRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_LAND ) - - return self - end - - end do -- OnTakeOff @@ -4907,38 +4754,11 @@ do -- OnTakeOff function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnTakeOffForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Takeoff ) return self end - --- Set a new listener for an S_EVENT_TAKEOFF event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnTakeOffForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_TAKEOFF ) - - return self - end - - --- Stop listening to S_EVENT_TAKEOFF event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnTakeOffRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_TAKEOFF ) - - return self - end - - end do -- OnEngineShutDown @@ -4952,210 +4772,11 @@ do -- OnEngineShutDown function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnEngineShutDownForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.EngineShutdown ) return self end - --- Set a new listener for an S_EVENT_ENGINE_SHUTDOWN event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnEngineShutDownForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_ENGINE_SHUTDOWN ) - - return self - end - - --- Stop listening to S_EVENT_ENGINE_SHUTDOWN event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnEngineShutDownRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_ENGINE_SHUTDOWN ) - - return self - end - -end - -do -- OnEngineStartUp - - --- Set a new listener for an S_EVENT_ENGINE_STARTUP event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnEngineStartUpForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_ENGINE_STARTUP ) - - return self - end - - --- Stop listening to S_EVENT_ENGINE_STARTUP event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnEngineStartUpRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_ENGINE_STARTUP ) - - return self - end - -end - -do -- OnShot - --- Set a new listener for an S_EVENT_SHOT event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnShot( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_SHOT ) - - return self - end - - --- Set a new listener for an S_EVENT_SHOT event for a unit. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnShotForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_SHOT ) - - return self - end - - --- Stop listening to S_EVENT_SHOT event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnShotRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_SHOT ) - - return self - end - - -end - -do -- OnHit - - --- Set a new listener for an S_EVENT_HIT event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnHit( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_HIT ) - - return self - end - - --- Set a new listener for an S_EVENT_HIT event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnHitForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_HIT ) - - return self - end - - --- Stop listening to S_EVENT_HIT event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnHitRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_HIT ) - - return self - end - -end - -do -- OnPlayerEnterUnit - - --- Set a new listener for an S_EVENT_PLAYER_ENTER_UNIT event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnPlayerEnterUnit( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_PLAYER_ENTER_UNIT ) - - return self - end - - --- Stop listening to S_EVENT_PLAYER_ENTER_UNIT event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnPlayerEnterRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_PLAYER_ENTER_UNIT ) - - return self - end - -end - -do -- OnPlayerLeaveUnit - --- Set a new listener for an S_EVENT_PLAYER_LEAVE_UNIT event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnPlayerLeaveUnit( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_PLAYER_LEAVE_UNIT ) - - return self - end - - --- Stop listening to S_EVENT_PLAYER_LEAVE_UNIT event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnPlayerLeaveRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_PLAYER_LEAVE_UNIT ) - - return self - end - end @@ -5173,6 +4794,8 @@ function EVENT:onEvent( Event ) return errmsg end + self:E( _EVENTMETA[Event.id].Text, Event ) + if self and self.Events and self.Events[Event.id] then @@ -5271,6 +4894,11 @@ function EVENT:onEvent( Event ) if Event.weapon then Event.Weapon = Event.weapon Event.WeaponName = Event.Weapon:getTypeName() + Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit! + Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName() + Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition() + Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category + Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName() --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() end @@ -5278,7 +4906,9 @@ function EVENT:onEvent( Event ) local PriorityBegin = PriorityOrder == -1 and 5 or 1 local PriorityEnd = PriorityOrder == -1 and 1 or 5 - self:E( { _EVENTMETA[Event.id].Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) + if Event.IniObjectCategory ~= 3 then + self:E( { _EVENTMETA[Event.id].Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) + end for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do @@ -5299,8 +4929,10 @@ function EVENT:onEvent( Event ) -- First test if a EventFunction is Set, otherwise search for the default function if EventData.EventUnit[Event.IniDCSUnitName].EventFunction then - self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) + end + local Result, Value = xpcall( function() return EventData.EventUnit[Event.IniDCSUnitName].EventFunction( EventClass, Event ) @@ -5313,8 +4945,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end + local Result, Value = xpcall( function() return EventFunction( EventClass, Event ) @@ -5328,7 +4962,9 @@ function EVENT:onEvent( Event ) -- First test if a EventFunction is Set, otherwise search for the default function if EventData.EventUnit[Event.TgtDCSUnitName].EventFunction then - self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) + end local Result, Value = xpcall( function() @@ -5342,8 +4978,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end + local Result, Value = xpcall( function() return EventFunction( EventClass, Event ) @@ -5361,9 +4999,11 @@ function EVENT:onEvent( Event ) if EventData.EventGroup[Event.IniGroupName] then -- First test if a EventFunction is Set, otherwise search for the default function if EventData.EventGroup[Event.IniGroupName].EventFunction then - - self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) - + + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) + end + local Result, Value = xpcall( function() return EventData.EventGroup[Event.IniGroupName].EventFunction( EventClass, Event ) @@ -5376,8 +5016,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) + end + local Result, Value = xpcall( function() return EventFunction( EventClass, Event ) @@ -5389,8 +5031,10 @@ function EVENT:onEvent( Event ) if EventData.EventGroup[Event.TgtGroupName] then if EventData.EventGroup[Event.TgtGroupName].EventFunction then - self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) + end + local Result, Value = xpcall( function() return EventData.EventGroup[Event.TgtGroupName].EventFunction( EventClass, Event ) @@ -5403,7 +5047,9 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) + end local Result, Value = xpcall( function() @@ -5417,7 +5063,7 @@ function EVENT:onEvent( Event ) -- If the EventData is not bound to a specific unit, then call the EventClass EventFunction. -- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon. - if Event.IniDCSUnit and not EventData.EventUnit then + if (Event.IniDCSUnit or Event.WeaponUNIT) and not EventData.EventUnit then if EventClass == EventData.EventClass then @@ -5425,8 +5071,9 @@ function EVENT:onEvent( Event ) if EventData.EventFunction then -- There is an EventFunction defined, so call the EventFunction. - self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end local Result, Value = xpcall( function() return EventData.EventFunction( EventClass, Event ) @@ -5438,11 +5085,14 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end + local Result, Value = xpcall( function() - return EventFunction( EventClass, Event ) + local Result, Value = EventFunction( EventClass, Event ) + return Result, Value end, ErrorHandler ) end end @@ -5456,6 +5106,8 @@ function EVENT:onEvent( Event ) else self:E( { _EVENTMETA[Event.id].Text, Event } ) end + + Event = nil end --- The EVENTHANDLER structure @@ -5613,6 +5265,8 @@ do -- MENU_BASE } --- Consructor + -- @param #MENU_BASE + -- @return #MENU_BASE function MENU_BASE:New( MenuText, ParentMenu ) local MenuParentPath = {} @@ -5625,10 +5279,43 @@ do -- MENU_BASE self.MenuPath = nil self.MenuText = MenuText self.MenuParentPath = MenuParentPath + self.Menus = {} + self.MenuCount = 0 + self.MenuRemoveParent = false + self.MenuTime = timer.getTime() return self end + --- Gets a @{Menu} from a parent @{Menu} + -- @param #MENU_BASE self + -- @param #string MenuText The text of the child menu. + -- @return #MENU_BASE + function MENU_BASE:GetMenu( MenuText ) + self:F( { self.Menus, MenuText } ) + return self.Menus[MenuText] + end + + --- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}. + -- @param #MENU_BASE self + -- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}. + -- @return #MENU_BASE + function MENU_BASE:SetRemoveParent( RemoveParent ) + self:F( { RemoveParent } ) + self.MenuRemoveParent = RemoveParent + return self + end + + + --- Sets a time stamp for later prevention of menu removal. + -- @param #MENU_BASE self + -- @param MenuTime + -- @return #MENU_BASE + function MENU_BASE:SetTime( MenuTime ) + self.MenuTime = MenuTime + return self + end + end do -- MENU_COMMAND_BASE @@ -5636,7 +5323,7 @@ do -- MENU_COMMAND_BASE --- The MENU_COMMAND_BASE class -- @type MENU_COMMAND_BASE -- @field #function MenuCallHandler - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_COMMAND_BASE = { ClassName = "MENU_COMMAND_BASE", CommandMenuFunction = nil, @@ -5645,6 +5332,8 @@ do -- MENU_COMMAND_BASE } --- Constructor + -- @param #MENU_COMMAND_BASE + -- @return #MENU_COMMAND_BASE function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) @@ -5664,7 +5353,7 @@ do -- MENU_MISSION --- The MENU_MISSION class -- @type MENU_MISSION - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_MISSION = { ClassName = "MENU_MISSION" } @@ -5673,7 +5362,7 @@ do -- MENU_MISSION -- @param #MENU_MISSION self -- @param #string MenuText The text for the menu. -- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other). - -- @return #MENU_MISSION self + -- @return #MENU_MISSION function MENU_MISSION:New( MenuText, ParentMenu ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) @@ -5700,7 +5389,7 @@ do -- MENU_MISSION --- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept! -- @param #MENU_MISSION self - -- @return #MENU_MISSION self + -- @return #MENU_MISSION function MENU_MISSION:RemoveSubMenus() self:F( self.MenuPath ) @@ -5731,7 +5420,7 @@ do -- MENU_MISSION_COMMAND --- The MENU_MISSION_COMMAND class -- @type MENU_MISSION_COMMAND - -- @extends Menu#MENU_COMMAND_BASE + -- @extends Core.Menu#MENU_COMMAND_BASE MENU_MISSION_COMMAND = { ClassName = "MENU_MISSION_COMMAND" } @@ -5781,7 +5470,7 @@ do -- MENU_COALITION --- The MENU_COALITION class -- @type MENU_COALITION - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the planes within the red coalition. -- -- To test, join the planes, then look at the other radio menus (Option F10). @@ -5855,7 +5544,7 @@ do -- MENU_COALITION --- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept! -- @param #MENU_COALITION self - -- @return #MENU_COALITION self + -- @return #MENU_COALITION function MENU_COALITION:RemoveSubMenus() self:F( self.MenuPath ) @@ -5886,7 +5575,7 @@ do -- MENU_COALITION_COMMAND --- The MENU_COALITION_COMMAND class -- @type MENU_COALITION_COMMAND - -- @extends Menu#MENU_COMMAND_BASE + -- @extends Core.Menu#MENU_COMMAND_BASE MENU_COALITION_COMMAND = { ClassName = "MENU_COALITION_COMMAND" } @@ -5898,7 +5587,7 @@ do -- MENU_COALITION_COMMAND -- @param Menu#MENU_COALITION ParentMenu The parent menu. -- @param CommandMenuFunction A function that is called when the menu key is pressed. -- @param CommandMenuArgument An argument for the function. There can only be ONE argument given. So multiple arguments must be wrapped into a table. See the below example how to do this. - -- @return #MENU_COALITION_COMMAND self + -- @return #MENU_COALITION_COMMAND function MENU_COALITION_COMMAND:New( Coalition, MenuText, ParentMenu, CommandMenuFunction, ... ) local self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) @@ -5943,7 +5632,7 @@ do -- MENU_CLIENT --- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters. -- @type MENU_CLIENT - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the two clients of planes. -- -- Each client will receive a different menu structure. @@ -6084,7 +5773,7 @@ do -- MENU_CLIENT --- The MENU_CLIENT_COMMAND class -- @type MENU_CLIENT_COMMAND - -- @extends Menu#MENU_COMMAND + -- @extends Core.Menu#MENU_COMMAND MENU_CLIENT_COMMAND = { ClassName = "MENU_CLIENT_COMMAND" } @@ -6170,7 +5859,7 @@ do --- The MENU_GROUP class -- @type MENU_GROUP - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the two groups of planes. -- -- Each group will receive a different menu structure. @@ -6244,8 +5933,6 @@ do self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) MenuGroup._Menus[Path] = self - self.Menus = {} - self.MenuGroup = MenuGroup self.Path = Path self.MenuGroupID = MenuGroup:GetID() @@ -6255,8 +5942,10 @@ do self:T( { "Adding Menu ", MenuText, self.MenuParentPath } ) self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuGroupID, MenuText, self.MenuParentPath ) - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self + if self.ParentMenu and self.ParentMenu.Menus then + self.ParentMenu.Menus[MenuText] = self + self:F( { self.ParentMenu.Menus, MenuText } ) + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 end end @@ -6267,42 +5956,56 @@ do --- Removes the sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self + -- @param MenuTime -- @return #MENU_GROUP self - function MENU_GROUP:RemoveSubMenus() - self:F( self.MenuPath ) + function MENU_GROUP:RemoveSubMenus( MenuTime ) + self:F2( { self.MenuPath, MenuTime, self.MenuTime } ) - for MenuID, Menu in pairs( self.Menus ) do - Menu:Remove() + self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } ) + for MenuText, Menu in pairs( self.Menus ) do + Menu:Remove( MenuTime ) end end - + + --- Removes the main menu and sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self + -- @param MenuTime -- @return #nil - function MENU_GROUP:Remove() - self:F( { self.MenuGroupID, self.MenuPath } ) + function MENU_GROUP:Remove( MenuTime ) + self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - self:RemoveSubMenus() - - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] + self:RemoveSubMenus( MenuTime ) - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuPath] = nil + if not MenuTime or self.MenuTime ~= MenuTime then + if self.MenuGroup._Menus[self.Path] then + self = self.MenuGroup._Menus[self.Path] + + missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) + if self.ParentMenu then + self.ParentMenu.Menus[self.MenuText] = nil + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 + if self.ParentMenu.MenuCount == 0 then + if self.MenuRemoveParent == true then + self:T( "Removing Parent Menu " ) + self.ParentMenu:Remove() + end + end + end + self:T( { "Removing Group Menu:", self.MenuGroup:GetName(), self.MenuGroup._Menus[self.Path].Path } ) + self.MenuGroup._Menus[self.Path] = nil + self = nil end - self:E( self.MenuGroup._Menus[self.Path] ) - self.MenuGroup._Menus[self.Path] = nil - self = nil end + return nil end --- The MENU_GROUP_COMMAND class -- @type MENU_GROUP_COMMAND - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_GROUP_COMMAND = { ClassName = "MENU_GROUP_COMMAND" } @@ -6314,13 +6017,14 @@ do -- @param ParentMenu The parent menu. -- @param CommandMenuFunction A function that is called when the menu key is pressed. -- @param CommandMenuArgument An argument for the function. - -- @return Menu#MENU_GROUP_COMMAND self + -- @return #MENU_GROUP_COMMAND function MENU_GROUP_COMMAND:New( MenuGroup, MenuText, ParentMenu, CommandMenuFunction, ... ) MenuGroup._Menus = MenuGroup._Menus or {} local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText if MenuGroup._Menus[Path] then self = MenuGroup._Menus[Path] + self:T( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } ) else self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) MenuGroup._Menus[Path] = self @@ -6331,33 +6035,45 @@ do self.MenuText = MenuText self.ParentMenu = ParentMenu - self:T( { "Adding Command Menu ", MenuText, self.MenuParentPath } ) + self:T( { "Adding Group Command Menu:", MenuGroup:GetName(), MenuText, self.MenuParentPath } ) self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler, arg ) - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self + if self.ParentMenu and self.ParentMenu.Menus then + self.ParentMenu.Menus[MenuText] = self + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 + self:F( { ParentMenu.Menus, MenuText } ) end end - --self:F( { MenuGroup:GetName(), MenuText, ParentMenu.MenuPath } ) - return self end --- Removes a menu structure for a group. -- @param #MENU_GROUP_COMMAND self + -- @param MenuTime -- @return #nil - function MENU_GROUP_COMMAND:Remove() - self:F( { self.MenuGroupID, self.MenuPath } ) + function MENU_GROUP_COMMAND:Remove( MenuTime ) + self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] - - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - self.ParentMenu.Menus[self.MenuPath] = nil - self:E( self.MenuGroup._Menus[self.Path] ) - self.MenuGroup._Menus[self.Path] = nil - self = nil + if not MenuTime or self.MenuTime ~= MenuTime then + if self.MenuGroup._Menus[self.Path] then + self = self.MenuGroup._Menus[self.Path] + + missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) + self:T( { "Removing Group Command Menu:", self.MenuGroup:GetName(), self.MenuText, self.Path, self.MenuGroup._Menus[self.Path].Path } ) + + self.ParentMenu.Menus[self.MenuText] = nil + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 + if self.ParentMenu.MenuCount == 0 then + if self.MenuRemoveParent == true then + self:T( "Removing Parent Menu " ) + self.ParentMenu:Remove() + end + end + + self.MenuGroup._Menus[self.Path] = nil + self = nil + end end return nil @@ -6367,6 +6083,8 @@ end --- **Core** - ZONE classes define **zones** within your mission of **various forms**, with **various capabilities**. -- +-- ![Banner Image](..\Presentations\ZONE\Dia1.JPG) +-- -- === -- -- There are essentially two core functions that zones accomodate: @@ -6385,127 +6103,16 @@ end -- -- Each of these ZONE classes have a zone name, and specific parameters defining the zone type: -- --- * @{Zone#ZONE_BASE}: The ZONE_BASE class defining the base for all other zone classes. --- * @{Zone#ZONE_RADIUS}: The ZONE_RADIUS class defined by a zone name, a location and a radius. --- * @{Zone#ZONE}: The ZONE class, defined by the zone name as defined within the Mission Editor. --- * @{Zone#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Unit#UNIT} with a radius. --- * @{Zone#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. --- * @{Zone#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- * @{#ZONE_BASE}: The ZONE_BASE class defining the base for all other zone classes. +-- * @{#ZONE_RADIUS}: The ZONE_RADIUS class defined by a zone name, a location and a radius. +-- * @{#ZONE}: The ZONE class, defined by the zone name as defined within the Mission Editor. +-- * @{#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Unit#UNIT} with a radius. +-- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. +-- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- +-- === -- --- === --- --- # 1) @{Zone#ZONE_BASE} class, extends @{Base#BASE} --- --- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. --- --- ## 1.1) Each zone has a name: --- --- * @{#ZONE_BASE.GetName}(): Returns the name of the zone. --- --- ## 1.2) Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}: --- --- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a Vec2 is within the zone. --- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a Vec3 is within the zone. --- --- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones: --- --- * @{#ZONE_BASE.SetRandomizeProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% ) --- * @{#ZONE_BASE.GetRandomizeProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% ) --- * @{#ZONE_BASE.GetZoneMaybe}(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate. --- --- ## 1.4) A zone manages Vectors: --- --- * @{#ZONE_BASE.GetVec2}(): Returns the @{DCSTypes#Vec2} coordinate of the zone. --- * @{#ZONE_BASE.GetRandomVec2}(): Define a random @{DCSTypes#Vec2} within the zone. --- --- ## 1.5) A zone has a bounding square: --- --- * @{#ZONE_BASE.GetBoundingSquare}(): Get the outer most bounding square of the zone. --- --- ## 1.6) A zone can be marked: --- --- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color. --- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color. --- --- === --- --- # 2) @{Zone#ZONE_RADIUS} class, extends @{Zone#ZONE_BASE} --- --- The ZONE_RADIUS class defined by a zone name, a location and a radius. --- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties. --- --- ## 2.1) @{Zone#ZONE_RADIUS} constructor --- --- * @{#ZONE_RADIUS.New}(): Constructor. --- --- ## 2.2) Manage the radius of the zone --- --- * @{#ZONE_RADIUS.SetRadius}(): Sets the radius of the zone. --- * @{#ZONE_RADIUS.GetRadius}(): Returns the radius of the zone. --- --- ## 2.3) Manage the location of the zone --- --- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCSTypes#Vec2} of the zone. --- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCSTypes#Vec2} of the zone. --- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCSTypes#Vec3} of the zone, taking an additional height parameter. --- --- ## 2.4) Zone point randomization --- --- Various functions exist to find random points within the zone. --- --- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone. --- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Point#POINT_VEC2} object representing a random 2D point in the zone. --- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight. --- --- === --- --- # 3) @{Zone#ZONE} class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE class, defined by the zone name as defined within the Mission Editor. --- This class implements the inherited functions from {Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- # 4) @{Zone#ZONE_UNIT} class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- # 5) @{Zone#ZONE_GROUP} class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. The current leader of the group defines the center of the zone. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- # 6) @{Zone#ZONE_POLYGON_BASE} class, extends @{Zone#ZONE_BASE} --- --- The ZONE_POLYGON_BASE class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. --- --- ## 6.1) Zone point randomization --- --- Various functions exist to find random points within the zone. --- --- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone. --- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Point#POINT_VEC2} object representing a random 2D point within the zone. --- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. --- --- --- === --- --- # 7) @{Zone#ZONE_POLYGON} class, extends @{Zone#ZONE_POLYGON_BASE} --- --- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- ==== --- --- **API CHANGE HISTORY** --- ====================== +-- # **API CHANGE HISTORY** -- -- The underlying change log documents the API changes. Please read this carefully. The following notation is used: -- @@ -6547,6 +6154,43 @@ end -- @field #string ZoneName Name of the zone. -- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. -- @extends Core.Base#BASE + + +--- # 1) ZONE_BASE class, extends @{Base#BASE} +-- +-- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. +-- +-- ## 1.1) Each zone has a name: +-- +-- * @{#ZONE_BASE.GetName}(): Returns the name of the zone. +-- +-- ## 1.2) Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}: +-- +-- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a Vec2 is within the zone. +-- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a Vec3 is within the zone. +-- +-- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones: +-- +-- * @{#ZONE_BASE.SetRandomizeProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% ) +-- * @{#ZONE_BASE.GetRandomizeProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% ) +-- * @{#ZONE_BASE.GetZoneMaybe}(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate. +-- +-- ## 1.4) A zone manages Vectors: +-- +-- * @{#ZONE_BASE.GetVec2}(): Returns the @{DCSTypes#Vec2} coordinate of the zone. +-- * @{#ZONE_BASE.GetRandomVec2}(): Define a random @{DCSTypes#Vec2} within the zone. +-- +-- ## 1.5) A zone has a bounding square: +-- +-- * @{#ZONE_BASE.GetBoundingSquare}(): Get the outer most bounding square of the zone. +-- +-- ## 1.6) A zone can be marked: +-- +-- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color. +-- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color. +-- +-- === +-- @field #ZONE_BASE ZONE_BASE ZONE_BASE = { ClassName = "ZONE_BASE", ZoneName = "", @@ -6614,6 +6258,58 @@ function ZONE_BASE:GetVec2() return nil end +--- Returns a @{Point#POINT_VEC2} of the zone. +-- @param #ZONE_BASE self +-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. +-- @return Core.Point#POINT_VEC2 The PointVec2 of the zone. +function ZONE_BASE:GetPointVec2() + self:F2( self.ZoneName ) + + local Vec2 = self:GetVec2() + + local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 ) + + self:T2( { PointVec2 } ) + + return PointVec2 +end + + +--- Returns the @{DCSTypes#Vec3} of the zone. +-- @param #ZONE_BASE self +-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. +-- @return Dcs.DCSTypes#Vec3 The Vec3 of the zone. +function ZONE_BASE:GetVec3( Height ) + self:F2( self.ZoneName ) + + Height = Height or 0 + + local Vec2 = self:GetVec2() + + local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y } + + self:T2( { Vec3 } ) + + return Vec3 +end + +--- Returns a @{Point#POINT_VEC3} of the zone. +-- @param #ZONE_BASE self +-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. +-- @return Core.Point#POINT_VEC3 The PointVec3 of the zone. +function ZONE_BASE:GetPointVec3( Height ) + self:F2( self.ZoneName ) + + local Vec3 = self:GetVec3( Height ) + + local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) + + self:T2( { PointVec3 } ) + + return PointVec3 +end + + --- Define a random @{DCSTypes#Vec2} within the zone. -- @param #ZONE_BASE self -- @return Dcs.DCSTypes#Vec2 The Vec2 coordinates. @@ -6628,6 +6324,13 @@ function ZONE_BASE:GetRandomPointVec2() return nil end +--- Define a random @{Point#POINT_VEC3} within the zone. +-- @param #ZONE_BASE self +-- @return Core.Point#POINT_VEC3 The PointVec3 coordinates. +function ZONE_BASE:GetRandomPointVec3() + return nil +end + --- Get the bounding square the zone. -- @param #ZONE_BASE self -- @return #nil The bounding square. @@ -6691,6 +6394,39 @@ end -- @field Dcs.DCSTypes#Vec2 Vec2 The current location of the zone. -- @field Dcs.DCSTypes#Distance Radius The radius of the zone. -- @extends Core.Zone#ZONE_BASE + +--- # 2) @{Zone#ZONE_RADIUS} class, extends @{Zone#ZONE_BASE} +-- +-- The ZONE_RADIUS class defined by a zone name, a location and a radius. +-- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties. +-- +-- ## 2.1) @{Zone#ZONE_RADIUS} constructor +-- +-- * @{#ZONE_RADIUS.New}(): Constructor. +-- +-- ## 2.2) Manage the radius of the zone +-- +-- * @{#ZONE_RADIUS.SetRadius}(): Sets the radius of the zone. +-- * @{#ZONE_RADIUS.GetRadius}(): Returns the radius of the zone. +-- +-- ## 2.3) Manage the location of the zone +-- +-- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCSTypes#Vec2} of the zone. +-- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCSTypes#Vec2} of the zone. +-- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCSTypes#Vec3} of the zone, taking an additional height parameter. +-- +-- ## 2.4) Zone point randomization +-- +-- Various functions exist to find random points within the zone. +-- +-- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone. +-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Point#POINT_VEC2} object representing a random 2D point in the zone. +-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight. +-- +-- === +-- +-- @field #ZONE_RADIUS ZONE_RADIUS +-- ZONE_RADIUS = { ClassName="ZONE_RADIUS", } @@ -6714,8 +6450,9 @@ end --- Bounds the zone with tires. -- @param #ZONE_RADIUS self -- @param #number Points (optional) The amount of points in the circle. +-- @param #boolean UnBound If true the tyres will be destroyed. -- @return #ZONE_RADIUS self -function ZONE_RADIUS:BoundZone( Points ) +function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound ) local Point = {} local Vec2 = self:GetVec2() @@ -6731,8 +6468,10 @@ function ZONE_RADIUS:BoundZone( Points ) Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() + local CountryName = _DATABASE.COUNTRY_NAME[CountryID] + local Tire = { - ["country"] = "USA", + ["country"] = CountryName, ["category"] = "Fortifications", ["canCargo"] = false, ["shape_name"] = "H-tyre_B_WF", @@ -6744,7 +6483,10 @@ function ZONE_RADIUS:BoundZone( Points ) ["heading"] = 0, } -- end of ["group"] - coalition.addStaticObject( country.id.USA, Tire ) + local Group = coalition.addStaticObject( CountryID, Tire ) + if UnBound and UnBound == true then + Group:destroy() + end end return self @@ -6956,9 +6698,19 @@ end ---- The ZONE class, defined by the zone name as defined within the Mission Editor. The location and the radius are automatically collected from the mission settings. -- @type ZONE -- @extends Core.Zone#ZONE_RADIUS + + +--- # 3) ZONE class, extends @{Zone#ZONE_RADIUS} +-- +-- The ZONE class, defined by the zone name as defined within the Mission Editor. +-- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. +-- +-- === +-- +-- @field #ZONE ZONE +-- ZONE = { ClassName="ZONE", } @@ -6990,6 +6742,16 @@ end -- @type ZONE_UNIT -- @field Wrapper.Unit#UNIT ZoneUNIT -- @extends Core.Zone#ZONE_RADIUS + +--- # 4) #ZONE_UNIT class, extends @{Zone#ZONE_RADIUS} +-- +-- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. +-- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. +-- +-- === +-- +-- @field #ZONE_UNIT ZONE_UNIT +-- ZONE_UNIT = { ClassName="ZONE_UNIT", } @@ -7070,10 +6832,20 @@ function ZONE_UNIT:GetVec3( Height ) return Vec3 end ---- The ZONE_GROUP class defined by a zone around a @{Group}, taking the average center point of all the units within the Group, with a radius. --- @type ZONE_GROUP +--- @type ZONE_GROUP -- @field Wrapper.Group#GROUP ZoneGROUP -- @extends Core.Zone#ZONE_RADIUS + + +--- # 5) #ZONE_GROUP class, extends @{Zone#ZONE_RADIUS} +-- +-- The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. The current leader of the group defines the center of the zone. +-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. +-- +-- === +-- +-- @field #ZONE_GROUP ZONE_GROUP +-- ZONE_GROUP = { ClassName="ZONE_GROUP", } @@ -7127,12 +6899,29 @@ end --- Polygons - ---- The ZONE_POLYGON_BASE class defined by an array of @{DCSTypes#Vec2}, forming a polygon. --- @type ZONE_POLYGON_BASE +--- @type ZONE_POLYGON_BASE -- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCSTypes#Vec2}. -- @extends Core.Zone#ZONE_BASE + + +--- # 6) ZONE_POLYGON_BASE class, extends @{Zone#ZONE_BASE} +-- +-- The ZONE_POLYGON_BASE class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. +-- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. +-- +-- ## 6.1) Zone point randomization +-- +-- Various functions exist to find random points within the zone. +-- +-- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone. +-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Point#POINT_VEC2} object representing a random 2D point within the zone. +-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. +-- +-- === +-- +-- @field #ZONE_POLYGON_BASE ZONE_POLYGON_BASE +-- ZONE_POLYGON_BASE = { ClassName="ZONE_POLYGON_BASE", } @@ -7177,8 +6966,9 @@ end --- Smokes the zone boundaries in a color. -- @param #ZONE_POLYGON_BASE self +-- @param #boolean UnBound If true, the tyres will be destroyed. -- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:BoundZone( ) +function ZONE_POLYGON_BASE:BoundZone( UnBound ) local i local j @@ -7207,8 +6997,11 @@ function ZONE_POLYGON_BASE:BoundZone( ) ["name"] = string.format( "%s-Tire #%0d", self:GetName(), ((i - 1) * Segments) + Segment ), ["heading"] = 0, } -- end of ["group"] - - coalition.addStaticObject( country.id.USA, Tire ) + + local Group = coalition.addStaticObject( country.id.USA, Tire ) + if UnBound and UnBound == true then + Group:destroy() + end end j = i @@ -7362,12 +7155,19 @@ function ZONE_POLYGON_BASE:GetBoundingSquare() end - - - ---- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- @type ZONE_POLYGON +--- @type ZONE_POLYGON -- @extends Core.Zone#ZONE_POLYGON_BASE + + +--- # 7) ZONE_POLYGON class, extends @{Zone#ZONE_POLYGON_BASE} +-- +-- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. +-- +-- === +-- +-- @field #ZONE_POLYGON ZONE_POLYGON +-- ZONE_POLYGON = { ClassName="ZONE_POLYGON", } @@ -7444,6 +7244,8 @@ DATABASE = { PLAYERSJOINED = {}, CLIENTS = {}, AIRBASES = {}, + COUNTRY_ID = {}, + COUNTRY_NAME = {}, NavPoints = {}, } @@ -8151,6 +7953,9 @@ function DATABASE:_RegisterTemplates() local CountryName = string.upper(cntry_data.name) local CountryID = cntry_data.id + self.COUNTRY_ID[CountryName] = CountryID + self.COUNTRY_NAME[CountryID] = CountryName + --self.Units[coa_name][countryName] = {} --self.Units[coa_name][countryName]["countryId"] = cntry_data.id @@ -8192,225 +7997,30 @@ end ---- **Core** - SET classes define **collections** of objects to perform **bulk actions** and logically **group** objects. +--- **Core** - SET_ classes define **collections** of objects to perform **bulk actions** and logically **group** objects. +-- +-- ![Banner Image](..\Presentations\SET\Dia1.JPG) -- -- === -- --- 1) @{Set#SET_BASE} class, extends @{Base#BASE} --- ============================================== --- The @{Set#SET_BASE} class defines the core functions that define a collection of objects. --- A SET provides iterators to iterate the SET, but will **temporarily** yield the ForEach interator loop at defined **"intervals"** to the mail simulator loop. --- In this way, large loops can be done while not blocking the simulator main processing loop. --- The default **"yield interval"** is after 10 objects processed. --- The default **"time interval"** is after 0.001 seconds. +-- SET_ classes group objects of the same type into a collection, which is either: -- --- 1.1) Add or remove objects from the SET --- --------------------------------------- --- Some key core functions are @{Set#SET_BASE.Add} and @{Set#SET_BASE.Remove} to add or remove objects from the SET in your logic. --- --- 1.2) Define the SET iterator **"yield interval"** and the **"time interval"** --- ----------------------------------------------------------------------------- --- Modify the iterator intervals with the @{Set#SET_BASE.SetInteratorIntervals} method. --- You can set the **"yield interval"**, and the **"time interval"**. (See above). --- --- === --- --- 2) @{Set#SET_GROUP} class, extends @{Set#SET_BASE} --- ================================================== --- Mission designers can use the @{Set#SET_GROUP} class to build sets of groups belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Starting with certain prefix strings. --- --- 2.1) SET_GROUP construction method: --- ----------------------------------- --- Create a new SET_GROUP object with the @{#SET_GROUP.New} method: --- --- * @{#SET_GROUP.New}: Creates a new SET_GROUP object. --- --- 2.2) Add or Remove GROUP(s) from SET_GROUP: --- ------------------------------------------- --- GROUPS can be added and removed using the @{Set#SET_GROUP.AddGroupsByName} and @{Set#SET_GROUP.RemoveGroupsByName} respectively. --- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP. --- --- 2.3) SET_GROUP filter criteria: --- ------------------------------- --- You can set filter criteria to define the set of groups within the SET_GROUP. --- Filter criteria are defined by: --- --- * @{#SET_GROUP.FilterCoalitions}: Builds the SET_GROUP with the groups belonging to the coalition(s). --- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies). --- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the gruops belonging to the country(ies). --- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups starting with the same prefix string(s). +-- * Manually managed using the **:Add...()** or **:Remove...()** methods. The initial SET can be filtered with the **@{#SET_BASE.FilterOnce}()** method +-- * Dynamically updated when new objects are created or objects are destroyed using the **@{#SET_BASE.FilterStart}()** method. -- --- Once the filter criteria have been set for the SET_GROUP, you can start filtering using: +-- Various types of SET_ classes are available: -- --- * @{#SET_GROUP.FilterStart}: Starts the filtering of the groups within the SET_GROUP and add or remove GROUP objects **dynamically**. +-- * @{#SET_UNIT}: Defines a colleciton of @{Unit}s filtered by filter criteria. +-- * @{#SET_GROUP}: Defines a collection of @{Group}s filtered by filter criteria. +-- * @{#SET_CLIENT}: Defines a collection of @{Client}s filterd by filter criteria. +-- * @{#SET_AIRBASE}: Defines a collection of @{Airbase}s filtered by filter criteria. -- --- Planned filter criteria within development are (so these are not yet available): +-- These classes are derived from @{#SET_BASE}, which contains the main methods to manage SETs. -- --- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Zone#ZONE}. +-- A multitude of other methods are available in SET_ classes that allow to: -- --- 2.4) SET_GROUP iterators: --- ------------------------- --- Once the filters have been defined and the SET_GROUP has been built, you can iterate the SET_GROUP with the available iterator methods. --- The iterator methods will walk the SET_GROUP set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_GROUP: --- --- * @{#SET_GROUP.ForEachGroup}: Calls a function for each alive group it finds within the SET_GROUP. --- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupPartlyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. --- --- ==== --- --- 3) @{Set#SET_UNIT} class, extends @{Set#SET_BASE} --- =================================================== --- Mission designers can use the @{Set#SET_UNIT} class to build sets of units belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Unit types --- * Starting with certain prefix strings. --- --- 3.1) SET_UNIT construction method: --- ---------------------------------- --- Create a new SET_UNIT object with the @{#SET_UNIT.New} method: --- --- * @{#SET_UNIT.New}: Creates a new SET_UNIT object. --- --- 3.2) Add or Remove UNIT(s) from SET_UNIT: --- ----------------------------------------- --- UNITs can be added and removed using the @{Set#SET_UNIT.AddUnitsByName} and @{Set#SET_UNIT.RemoveUnitsByName} respectively. --- These methods take a single UNIT name or an array of UNIT names to be added or removed from SET_UNIT. --- --- 3.3) SET_UNIT filter criteria: --- ------------------------------ --- You can set filter criteria to define the set of units within the SET_UNIT. --- Filter criteria are defined by: --- --- * @{#SET_UNIT.FilterCoalitions}: Builds the SET_UNIT with the units belonging to the coalition(s). --- * @{#SET_UNIT.FilterCategories}: Builds the SET_UNIT with the units belonging to the category(ies). --- * @{#SET_UNIT.FilterTypes}: Builds the SET_UNIT with the units belonging to the unit type(s). --- * @{#SET_UNIT.FilterCountries}: Builds the SET_UNIT with the units belonging to the country(ies). --- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units starting with the same prefix string(s). --- --- Once the filter criteria have been set for the SET_UNIT, you can start filtering using: --- --- * @{#SET_UNIT.FilterStart}: Starts the filtering of the units within the SET_UNIT. --- --- Planned filter criteria within development are (so these are not yet available): --- --- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Zone#ZONE}. --- --- 3.4) SET_UNIT iterators: --- ------------------------ --- Once the filters have been defined and the SET_UNIT has been built, you can iterate the SET_UNIT with the available iterator methods. --- The iterator methods will walk the SET_UNIT set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_UNIT: --- --- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive unit it finds within the SET_UNIT. --- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. --- --- Planned iterators methods in development are (so these are not yet available): --- --- * @{#SET_UNIT.ForEachUnitInUnit}: Calls a function for each unit contained within the SET_UNIT. --- * @{#SET_UNIT.ForEachUnitCompletelyInZone}: Iterate and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function. --- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate and call an iterator function for each **alive** UNIT presence not in a @{Zone}, providing the UNIT and optional parameters to the called function. --- --- === --- --- 4) @{Set#SET_CLIENT} class, extends @{Set#SET_BASE} --- =================================================== --- Mission designers can use the @{Set#SET_CLIENT} class to build sets of units belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Client types --- * Starting with certain prefix strings. --- --- 4.1) SET_CLIENT construction method: --- ---------------------------------- --- Create a new SET_CLIENT object with the @{#SET_CLIENT.New} method: --- --- * @{#SET_CLIENT.New}: Creates a new SET_CLIENT object. --- --- 4.2) Add or Remove CLIENT(s) from SET_CLIENT: --- ----------------------------------------- --- CLIENTs can be added and removed using the @{Set#SET_CLIENT.AddClientsByName} and @{Set#SET_CLIENT.RemoveClientsByName} respectively. --- These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT. --- --- 4.3) SET_CLIENT filter criteria: --- ------------------------------ --- You can set filter criteria to define the set of clients within the SET_CLIENT. --- Filter criteria are defined by: --- --- * @{#SET_CLIENT.FilterCoalitions}: Builds the SET_CLIENT with the clients belonging to the coalition(s). --- * @{#SET_CLIENT.FilterCategories}: Builds the SET_CLIENT with the clients belonging to the category(ies). --- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s). --- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies). --- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients starting with the same prefix string(s). --- --- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using: --- --- * @{#SET_CLIENT.FilterStart}: Starts the filtering of the clients within the SET_CLIENT. --- --- Planned filter criteria within development are (so these are not yet available): --- --- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Zone#ZONE}. --- --- 4.4) SET_CLIENT iterators: --- ------------------------ --- Once the filters have been defined and the SET_CLIENT has been built, you can iterate the SET_CLIENT with the available iterator methods. --- The iterator methods will walk the SET_CLIENT set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_CLIENT: --- --- * @{#SET_CLIENT.ForEachClient}: Calls a function for each alive client it finds within the SET_CLIENT. --- --- ==== --- --- 5) @{Set#SET_AIRBASE} class, extends @{Set#SET_BASE} --- ==================================================== --- Mission designers can use the @{Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: --- --- * Coalitions --- --- 5.1) SET_AIRBASE construction --- ----------------------------- --- Create a new SET_AIRBASE object with the @{#SET_AIRBASE.New} method: --- --- * @{#SET_AIRBASE.New}: Creates a new SET_AIRBASE object. --- --- 5.2) Add or Remove AIRBASEs from SET_AIRBASE --- -------------------------------------------- --- AIRBASEs can be added and removed using the @{Set#SET_AIRBASE.AddAirbasesByName} and @{Set#SET_AIRBASE.RemoveAirbasesByName} respectively. --- These methods take a single AIRBASE name or an array of AIRBASE names to be added or removed from SET_AIRBASE. --- --- 5.3) SET_AIRBASE filter criteria --- -------------------------------- --- You can set filter criteria to define the set of clients within the SET_AIRBASE. --- Filter criteria are defined by: --- --- * @{#SET_AIRBASE.FilterCoalitions}: Builds the SET_AIRBASE with the airbases belonging to the coalition(s). --- --- Once the filter criteria have been set for the SET_AIRBASE, you can start filtering using: --- --- * @{#SET_AIRBASE.FilterStart}: Starts the filtering of the airbases within the SET_AIRBASE. --- --- 5.4) SET_AIRBASE iterators: --- --------------------------- --- Once the filters have been defined and the SET_AIRBASE has been built, you can iterate the SET_AIRBASE with the available iterator methods. --- The iterator methods will walk the SET_AIRBASE set, and call for each airbase within the set a function that you provide. --- The following iterator methods are currently available within the SET_AIRBASE: --- --- * @{#SET_AIRBASE.ForEachAirbase}: Calls a function for each airbase it finds within the SET_AIRBASE. --- --- ==== +-- * Validate the presence of objects in the SET. +-- * Trigger events when objects in the SET change a zone presence. -- -- ### Authors: -- @@ -8422,20 +8032,40 @@ end -- @module Set ---- SET_BASE class --- @type SET_BASE +--- @type SET_BASE -- @field #table Filter -- @field #table Set -- @field #table List -- @field Core.Scheduler#SCHEDULER CallScheduler -- @extends Core.Base#BASE + + +--- # 1) SET_BASE class, extends @{Base#BASE} +-- The @{Set#SET_BASE} class defines the core functions that define a collection of objects. +-- A SET provides iterators to iterate the SET, but will **temporarily** yield the ForEach interator loop at defined **"intervals"** to the mail simulator loop. +-- In this way, large loops can be done while not blocking the simulator main processing loop. +-- The default **"yield interval"** is after 10 objects processed. +-- The default **"time interval"** is after 0.001 seconds. +-- +-- ## 1.1) Add or remove objects from the SET +-- +-- Some key core functions are @{Set#SET_BASE.Add} and @{Set#SET_BASE.Remove} to add or remove objects from the SET in your logic. +-- +-- ## 1.2) Define the SET iterator **"yield interval"** and the **"time interval"** +-- +-- Modify the iterator intervals with the @{Set#SET_BASE.SetInteratorIntervals} method. +-- You can set the **"yield interval"**, and the **"time interval"**. (See above). +-- +-- @field #SET_BASE SET_BASE SET_BASE = { ClassName = "SET_BASE", Filter = {}, Set = {}, List = {}, + Index = {}, } + --- Creates a new SET_BASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. -- @param #SET_BASE self -- @return #SET_BASE @@ -8452,10 +8082,14 @@ function SET_BASE:New( Database ) self.YieldInterval = 10 self.TimeInterval = 0.001 + self.Set = {} + self.List = {} self.List.__index = self.List self.List = setmetatable( { Count = 0 }, self.List ) + self.Index = {} + self.CallScheduler = SCHEDULER:New( self ) self:SetEventPriority( 2 ) @@ -8507,6 +8141,8 @@ function SET_BASE:Add( ObjectName, Object ) self.Set[ObjectName] = t._ + table.insert( self.Index, ObjectName ) + end --- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using the Object Name as the index. @@ -8558,7 +8194,15 @@ function SET_BASE:Remove( ObjectName ) t._prev = nil self.List.Count = self.List.Count - 1 + for Index, Key in ipairs( self.Index ) do + if Key == ObjectName then + table.remove( self.Index, Index ) + break + end + end + self.Set[ObjectName] = nil + end end @@ -8578,12 +8222,50 @@ function SET_BASE:Get( ObjectName ) end +--- Gets the first object from the @{Set#SET_BASE} and derived classes. +-- @param #SET_BASE self +-- @return Core.Base#BASE +function SET_BASE:GetFirst() + self:F() + + local ObjectName = self.Index[1] + local FirstObject = self.Set[ObjectName] + self:T3( { FirstObject } ) + return FirstObject +end + +--- Gets the last object from the @{Set#SET_BASE} and derived classes. +-- @param #SET_BASE self +-- @return Core.Base#BASE +function SET_BASE:GetLast() + self:F() + + local ObjectName = self.Index[#self.Index] + local LastObject = self.Set[ObjectName] + self:T3( { LastObject } ) + return LastObject +end + +--- Gets a random object from the @{Set#SET_BASE} and derived classes. +-- @param #SET_BASE self +-- @return Core.Base#BASE +function SET_BASE:GetRandom() + self:F() + + local RandomItem = self.Set[self.Index[math.random(#self.Index)]] + + self:T3( { RandomItem } ) + + return RandomItem +end + + --- Retrieves the amount of objects in the @{Set#SET_BASE} and derived classes. -- @param #SET_BASE self -- @return #number Count function SET_BASE:Count() - return self.List.Count + return #self.Index or 0 end @@ -8846,7 +8528,8 @@ function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArgumen return false end - self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + --self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + Schedule() return self end @@ -8915,11 +8598,61 @@ function SET_BASE:Flush() return ObjectNames end --- SET_GROUP ---- SET_GROUP class --- @type SET_GROUP --- @extends #SET_BASE +--- @type SET_GROUP +-- @extends Core.Set#SET_BASE + +--- # 2) SET_GROUP class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the @{Set#SET_GROUP} class to build sets of groups belonging to certain: +-- +-- * Coalitions +-- * Categories +-- * Countries +-- * Starting with certain prefix strings. +-- +-- ## 2.1) SET_GROUP constructor +-- +-- Create a new SET_GROUP object with the @{#SET_GROUP.New} method: +-- +-- * @{#SET_GROUP.New}: Creates a new SET_GROUP object. +-- +-- ## 2.2) Add or Remove GROUP(s) from SET_GROUP +-- +-- GROUPS can be added and removed using the @{Set#SET_GROUP.AddGroupsByName} and @{Set#SET_GROUP.RemoveGroupsByName} respectively. +-- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP. +-- +-- ## 2.3) SET_GROUP filter criteria +-- +-- You can set filter criteria to define the set of groups within the SET_GROUP. +-- Filter criteria are defined by: +-- +-- * @{#SET_GROUP.FilterCoalitions}: Builds the SET_GROUP with the groups belonging to the coalition(s). +-- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies). +-- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the gruops belonging to the country(ies). +-- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups starting with the same prefix string(s). +-- +-- Once the filter criteria have been set for the SET_GROUP, you can start filtering using: +-- +-- * @{#SET_GROUP.FilterStart}: Starts the filtering of the groups within the SET_GROUP and add or remove GROUP objects **dynamically**. +-- +-- Planned filter criteria within development are (so these are not yet available): +-- +-- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Zone#ZONE}. +-- +-- ## 2.4) SET_GROUP iterators +-- +-- Once the filters have been defined and the SET_GROUP has been built, you can iterate the SET_GROUP with the available iterator methods. +-- The iterator methods will walk the SET_GROUP set, and call for each element within the set a function that you provide. +-- The following iterator methods are currently available within the SET_GROUP: +-- +-- * @{#SET_GROUP.ForEachGroup}: Calls a function for each alive group it finds within the SET_GROUP. +-- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- * @{#SET_GROUP.ForEachGroupPartlyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- +-- === +-- @field #SET_GROUP SET_GROUP SET_GROUP = { ClassName = "SET_GROUP", Filter = { @@ -9285,9 +9018,73 @@ function SET_GROUP:IsIncludeObject( MooseGroup ) return MooseGroupInclude end ---- SET_UNIT class --- @type SET_UNIT +--- @type SET_UNIT -- @extends Core.Set#SET_BASE + +--- # 3) SET_UNIT class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the SET_UNIT class to build sets of units belonging to certain: +-- +-- * Coalitions +-- * Categories +-- * Countries +-- * Unit types +-- * Starting with certain prefix strings. +-- +-- ## 3.1) SET_UNIT constructor +-- +-- Create a new SET_UNIT object with the @{#SET_UNIT.New} method: +-- +-- * @{#SET_UNIT.New}: Creates a new SET_UNIT object. +-- +-- ## 3.2) Add or Remove UNIT(s) from SET_UNIT +-- +-- UNITs can be added and removed using the @{Set#SET_UNIT.AddUnitsByName} and @{Set#SET_UNIT.RemoveUnitsByName} respectively. +-- These methods take a single UNIT name or an array of UNIT names to be added or removed from SET_UNIT. +-- +-- ## 3.3) SET_UNIT filter criteria +-- +-- You can set filter criteria to define the set of units within the SET_UNIT. +-- Filter criteria are defined by: +-- +-- * @{#SET_UNIT.FilterCoalitions}: Builds the SET_UNIT with the units belonging to the coalition(s). +-- * @{#SET_UNIT.FilterCategories}: Builds the SET_UNIT with the units belonging to the category(ies). +-- * @{#SET_UNIT.FilterTypes}: Builds the SET_UNIT with the units belonging to the unit type(s). +-- * @{#SET_UNIT.FilterCountries}: Builds the SET_UNIT with the units belonging to the country(ies). +-- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units starting with the same prefix string(s). +-- +-- Once the filter criteria have been set for the SET_UNIT, you can start filtering using: +-- +-- * @{#SET_UNIT.FilterStart}: Starts the filtering of the units within the SET_UNIT. +-- +-- Planned filter criteria within development are (so these are not yet available): +-- +-- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Zone#ZONE}. +-- +-- ## 3.4) SET_UNIT iterators +-- +-- Once the filters have been defined and the SET_UNIT has been built, you can iterate the SET_UNIT with the available iterator methods. +-- The iterator methods will walk the SET_UNIT set, and call for each element within the set a function that you provide. +-- The following iterator methods are currently available within the SET_UNIT: +-- +-- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive unit it finds within the SET_UNIT. +-- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- +-- Planned iterators methods in development are (so these are not yet available): +-- +-- * @{#SET_UNIT.ForEachUnitInUnit}: Calls a function for each unit contained within the SET_UNIT. +-- * @{#SET_UNIT.ForEachUnitCompletelyInZone}: Iterate and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function. +-- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate and call an iterator function for each **alive** UNIT presence not in a @{Zone}, providing the UNIT and optional parameters to the called function. +-- +-- ## 3.5 ) SET_UNIT atomic methods +-- +-- Various methods exist for a SET_UNIT to perform actions or calculations and retrieve results from the SET_UNIT: +-- +-- * @{#SET_UNIT.GetTypeNames}(): Retrieve the type names of the @{Unit}s in the SET, delimited by a comma. +-- +-- === +-- @field #SET_UNIT SET_UNIT SET_UNIT = { ClassName = "SET_UNIT", Units = {}, @@ -9899,11 +9696,89 @@ function SET_UNIT:IsIncludeObject( MUnit ) end +--- Retrieve the type names of the @{Unit}s in the SET, delimited by an optional delimiter. +-- @param #SET_UNIT self +-- @param #string Delimiter (optional) The delimiter, which is default a comma. +-- @return #string The types of the @{Unit}s delimited. +function SET_UNIT:GetTypeNames( Delimiter ) + + Delimiter = Delimiter or ", " + local TypeReport = REPORT:New() + local Types = {} + + for UnitName, UnitData in pairs( self:GetSet() ) do + + local Unit = UnitData -- Wrapper.Unit#UNIT + local UnitTypeName = Unit:GetTypeName() + + if not Types[UnitTypeName] then + Types[UnitTypeName] = UnitTypeName + TypeReport:Add( UnitTypeName ) + end + end + + return TypeReport:Text( Delimiter ) +end + + --- SET_CLIENT ---- SET_CLIENT class --- @type SET_CLIENT + +--- @type SET_CLIENT -- @extends Core.Set#SET_BASE + + + +--- # 4) SET_CLIENT class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the @{Set#SET_CLIENT} class to build sets of units belonging to certain: +-- +-- * Coalitions +-- * Categories +-- * Countries +-- * Client types +-- * Starting with certain prefix strings. +-- +-- ## 4.1) SET_CLIENT constructor +-- +-- Create a new SET_CLIENT object with the @{#SET_CLIENT.New} method: +-- +-- * @{#SET_CLIENT.New}: Creates a new SET_CLIENT object. +-- +-- ## 4.2) Add or Remove CLIENT(s) from SET_CLIENT +-- +-- CLIENTs can be added and removed using the @{Set#SET_CLIENT.AddClientsByName} and @{Set#SET_CLIENT.RemoveClientsByName} respectively. +-- These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT. +-- +-- ## 4.3) SET_CLIENT filter criteria +-- +-- You can set filter criteria to define the set of clients within the SET_CLIENT. +-- Filter criteria are defined by: +-- +-- * @{#SET_CLIENT.FilterCoalitions}: Builds the SET_CLIENT with the clients belonging to the coalition(s). +-- * @{#SET_CLIENT.FilterCategories}: Builds the SET_CLIENT with the clients belonging to the category(ies). +-- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s). +-- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies). +-- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients starting with the same prefix string(s). +-- +-- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using: +-- +-- * @{#SET_CLIENT.FilterStart}: Starts the filtering of the clients within the SET_CLIENT. +-- +-- Planned filter criteria within development are (so these are not yet available): +-- +-- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Zone#ZONE}. +-- +-- ## 4.4) SET_CLIENT iterators +-- +-- Once the filters have been defined and the SET_CLIENT has been built, you can iterate the SET_CLIENT with the available iterator methods. +-- The iterator methods will walk the SET_CLIENT set, and call for each element within the set a function that you provide. +-- The following iterator methods are currently available within the SET_CLIENT: +-- +-- * @{#SET_CLIENT.ForEachClient}: Calls a function for each alive client it finds within the SET_CLIENT. +-- +-- === +-- @field #SET_CLIENT SET_CLIENT SET_CLIENT = { ClassName = "SET_CLIENT", Clients = {}, @@ -10256,11 +10131,47 @@ function SET_CLIENT:IsIncludeObject( MClient ) return MClientInclude end ---- SET_AIRBASE - ---- SET_AIRBASE class --- @type SET_AIRBASE +--- @type SET_AIRBASE -- @extends Core.Set#SET_BASE + +--- # 5) SET_AIRBASE class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the @{Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: +-- +-- * Coalitions +-- +-- ## 5.1) SET_AIRBASE constructor +-- +-- Create a new SET_AIRBASE object with the @{#SET_AIRBASE.New} method: +-- +-- * @{#SET_AIRBASE.New}: Creates a new SET_AIRBASE object. +-- +-- ## 5.2) Add or Remove AIRBASEs from SET_AIRBASE +-- +-- AIRBASEs can be added and removed using the @{Set#SET_AIRBASE.AddAirbasesByName} and @{Set#SET_AIRBASE.RemoveAirbasesByName} respectively. +-- These methods take a single AIRBASE name or an array of AIRBASE names to be added or removed from SET_AIRBASE. +-- +-- ## 5.3) SET_AIRBASE filter criteria +-- +-- You can set filter criteria to define the set of clients within the SET_AIRBASE. +-- Filter criteria are defined by: +-- +-- * @{#SET_AIRBASE.FilterCoalitions}: Builds the SET_AIRBASE with the airbases belonging to the coalition(s). +-- +-- Once the filter criteria have been set for the SET_AIRBASE, you can start filtering using: +-- +-- * @{#SET_AIRBASE.FilterStart}: Starts the filtering of the airbases within the SET_AIRBASE. +-- +-- ## 5.4) SET_AIRBASE iterators +-- +-- Once the filters have been defined and the SET_AIRBASE has been built, you can iterate the SET_AIRBASE with the available iterator methods. +-- The iterator methods will walk the SET_AIRBASE set, and call for each airbase within the set a function that you provide. +-- The following iterator methods are currently available within the SET_AIRBASE: +-- +-- * @{#SET_AIRBASE.ForEachAirbase}: Calls a function for each airbase it finds within the SET_AIRBASE. +-- +-- === +-- @field #SET_AIRBASE SET_AIRBASE SET_AIRBASE = { ClassName = "SET_AIRBASE", Airbases = {}, @@ -11694,227 +11605,14 @@ end -- I've reworked this development (taken the concept), and created a **hierarchical state machine** out of it, embedded within the DCS simulator. -- Additionally, I've added extendability and created an API that allows seamless FSM implementation. -- --- === --- --- # 1) @{#FSM} class, extends @{Base#BASE} --- --- ![Transition Rules and Transition Handlers and Event Triggers](..\Presentations\FSM\Dia3.JPG) +-- The following derived classes are available in the MOOSE framework, that implement a specialised form of a FSM: -- --- The FSM class is the base class of all FSM\_ derived classes. It implements the main functionality to define and execute Finite State Machines. --- The derived FSM\_ classes extend the Finite State Machine functionality to run a workflow process for a specific purpose or component. +-- * @{#FSM_TASK}: Models Finite State Machines for @{Task}s. +-- * @{#FSM_PROCESS}: Models Finite State Machines for @{Task} actions, which control @{Client}s. +-- * @{#FSM_CONTROLLABLE}: Models Finite State Machines for @{Controllable}s, which are @{Group}s, @{Unit}s, @{Client}s. +-- * @{#FSM_SET}: Models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here +-- for multiple objects or the position of the state machine in the process. -- --- Finite State Machines have **Transition Rules**, **Transition Handlers** and **Event Triggers**. --- --- The **Transition Rules** define the "Process Flow Boundaries", that is, --- the path that can be followed hopping from state to state upon triggered events. --- If an event is triggered, and there is no valid path found for that event, --- an error will be raised and the FSM will stop functioning. --- --- The **Transition Handlers** are special methods that can be defined by the mission designer, following a defined syntax. --- If the FSM object finds a method of such a handler, then the method will be called by the FSM, passing specific parameters. --- The method can then define its own custom logic to implement the FSM workflow, and to conduct other actions. --- --- The **Event Triggers** are methods that are defined by the FSM, which the mission designer can use to implement the workflow. --- Most of the time, these Event Triggers are used within the Transition Handler methods, so that a workflow is created running through the state machine. --- --- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation. --- The below documentation has a seperate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**. --- --- ## 1.1) FSM Linear Transitions --- --- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**. --- The Lineair transition rule evaluation will always be done from the **current state** of the FSM. --- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop. --- --- ### 1.1.1) FSM Transition Rules --- --- The FSM has transition rules that it follows and validates, as it walks the process. --- These rules define when an FSM can transition from a specific state towards an other specific state upon a triggered event. --- --- The method @{#FSM.AddTransition}() specifies a new possible Transition Rule for the FSM. --- --- The initial state can be defined using the method @{#FSM.SetStartState}(). The default start state of an FSM is "None". --- --- Find below an example of a Linear Transition Rule definition for an FSM. --- --- local Fsm3Switch = FSM:New() -- #FsmDemo --- FsmSwitch:SetStartState( "Off" ) --- FsmSwitch:AddTransition( "Off", "SwitchOn", "On" ) --- FsmSwitch:AddTransition( "Off", "SwitchMiddle", "Middle" ) --- FsmSwitch:AddTransition( "On", "SwitchOff", "Off" ) --- FsmSwitch:AddTransition( "Middle", "SwitchOff", "Off" ) --- --- The above code snippet models a 3-way switch Linear Transition: --- --- * It can be switched **On** by triggering event **SwitchOn**. --- * It can be switched to the **Middle** position, by triggering event **SwitchMiddle**. --- * It can be switched **Off** by triggering event **SwitchOff**. --- * Note that once the Switch is **On** or **Middle**, it can only be switched **Off**. --- --- ### Some additional comments: --- --- Note that Linear Transition Rules **can be declared in a few variations**: --- --- * The From states can be **a table of strings**, indicating that the transition rule will be valid **if the current state** of the FSM will be **one of the given From states**. --- * The From state can be a **"*"**, indicating that **the transition rule will always be valid**, regardless of the current state of the FSM. --- --- The below code snippet shows how the two last lines can be rewritten and consensed. --- --- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" ) --- --- ### 1.1.2) Transition Handling --- --- ![Transition Handlers](..\Presentations\FSM\Dia4.JPG) --- --- An FSM transitions in **4 moments** when an Event is being triggered and processed. --- The mission designer can define for each moment specific logic within methods implementations following a defined API syntax. --- These methods define the flow of the FSM process; because in those methods the FSM Internal Events will be triggered. --- --- * To handle **State** transition moments, create methods starting with OnLeave or OnEnter concatenated with the State name. --- * To handle **Event** transition moments, create methods starting with OnBefore or OnAfter concatenated with the Event name. --- --- **The OnLeave and OnBefore transition methods may return false, which will cancel the transition!** --- --- Transition Handler methods need to follow the above specified naming convention, but are also passed parameters from the FSM. --- These parameters are on the correct order: From, Event, To: --- --- * From = A string containing the From state. --- * Event = A string containing the Event name that was triggered. --- * To = A string containing the To state. --- --- On top, each of these methods can have a variable amount of parameters passed. See the example in section [1.1.3](#1.1.3\)-event-triggers). --- --- ### 1.1.3) Event Triggers --- --- ![Event Triggers](..\Presentations\FSM\Dia5.JPG) --- --- The FSM creates for each Event two **Event Trigger methods**. --- There are two modes how Events can be triggered, which is **synchronous** and **asynchronous**: --- --- * The method **FSM:Event()** triggers an Event that will be processed **synchronously** or **immediately**. --- * The method **FSM:__Event( __seconds__ )** triggers an Event that will be processed **asynchronously** over time, waiting __x seconds__. --- --- The destinction between these 2 Event Trigger methods are important to understand. An asynchronous call will "log" the Event Trigger to be executed at a later time. --- Processing will just continue. Synchronous Event Trigger methods are useful to change states of the FSM immediately, but may have a larger processing impact. --- --- The following example provides a little demonstration on the difference between synchronous and asynchronous Event Triggering. --- --- function FSM:OnAfterEvent( From, Event, To, Amount ) --- self:T( { Amount = Amount } ) --- end --- --- local Amount = 1 --- FSM:__Event( 5, Amount ) --- --- Amount = Amount + 1 --- FSM:Event( Text, Amount ) --- --- In this example, the **:OnAfterEvent**() Transition Handler implementation will get called when **Event** is being triggered. --- Before we go into more detail, let's look at the last 4 lines of the example. --- The last line triggers synchronously the **Event**, and passes Amount as a parameter. --- The 3rd last line of the example triggers asynchronously **Event**. --- Event will be processed after 5 seconds, and Amount is given as a parameter. --- --- The output of this little code fragment will be: --- --- * Amount = 2 --- * Amount = 2 --- --- Because ... When Event was asynchronously processed after 5 seconds, Amount was set to 2. So be careful when processing and passing values and objects in asynchronous processing! --- --- ### 1.1.4) Linear Transition Example --- --- This example is fully implemented in the MOOSE test mission on GITHUB: [FSM-100 - Transition Explanation](https://github.com/FlightControl-Master/MOOSE/blob/master/Moose%20Test%20Missions/FSM%20-%20Finite%20State%20Machine/FSM-100%20-%20Transition%20Explanation/FSM-100%20-%20Transition%20Explanation.lua) --- --- It models a unit standing still near Batumi, and flaring every 5 seconds while switching between a Green flare and a Red flare. --- The purpose of this example is not to show how exciting flaring is, but it demonstrates how a Linear Transition FSM can be build. --- Have a look at the source code. The source code is also further explained below in this section. --- --- The example creates a new FsmDemo object from class FSM. --- It will set the start state of FsmDemo to state **Green**. --- Two Linear Transition Rules are created, where upon the event **Switch**, --- the FsmDemo will transition from state **Green** to **Red** and from **Red** back to **Green**. --- --- ![Transition Example](..\Presentations\FSM\Dia6.JPG) --- --- local FsmDemo = FSM:New() -- #FsmDemo --- FsmDemo:SetStartState( "Green" ) --- FsmDemo:AddTransition( "Green", "Switch", "Red" ) --- FsmDemo:AddTransition( "Red", "Switch", "Green" ) --- --- In the above example, the FsmDemo could flare every 5 seconds a Green or a Red flare into the air. --- The next code implements this through the event handling method **OnAfterSwitch**. --- --- ![Transition Flow](..\Presentations\FSM\Dia7.JPG) --- --- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) --- self:T( { From, Event, To, FsmUnit } ) --- --- if From == "Green" then --- FsmUnit:Flare(FLARECOLOR.Green) --- else --- if From == "Red" then --- FsmUnit:Flare(FLARECOLOR.Red) --- end --- end --- self:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. --- end --- --- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the first Switch event to happen in 5 seconds. --- --- The OnAfterSwitch implements a loop. The last line of the code fragment triggers the Switch Event within 5 seconds. --- Upon the event execution (after 5 seconds), the OnAfterSwitch method is called of FsmDemo (cfr. the double point notation!!! ":"). --- The OnAfterSwitch method receives from the FSM the 3 transition parameter details ( From, Event, To ), --- and one additional parameter that was given when the event was triggered, which is in this case the Unit that is used within OnSwitchAfter. --- --- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) --- --- For debugging reasons the received parameters are traced within the DCS.log. --- --- self:T( { From, Event, To, FsmUnit } ) --- --- The method will check if the From state received is either "Green" or "Red" and will flare the respective color from the FsmUnit. --- --- if From == "Green" then --- FsmUnit:Flare(FLARECOLOR.Green) --- else --- if From == "Red" then --- FsmUnit:Flare(FLARECOLOR.Red) --- end --- end --- --- It is important that the Switch event is again triggered, otherwise, the FsmDemo would stop working after having the first Event being handled. --- --- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. --- --- The below code fragment extends the FsmDemo, demonstrating multiple **From states declared as a table**, adding a **Linear Transition Rule**. --- The new event **Stop** will cancel the Switching process. --- The transition for event Stop can be executed if the current state of the FSM is either "Red" or "Green". --- --- local FsmDemo = FSM:New() -- #FsmDemo --- FsmDemo:SetStartState( "Green" ) --- FsmDemo:AddTransition( "Green", "Switch", "Red" ) --- FsmDemo:AddTransition( "Red", "Switch", "Green" ) --- FsmDemo:AddTransition( { "Red", "Green" }, "Stop", "Stopped" ) --- --- The transition for event Stop can also be simplified, as any current state of the FSM is valid. --- --- FsmDemo:AddTransition( "*", "Stop", "Stopped" ) --- --- So... When FsmDemo:Stop() is being triggered, the state of FsmDemo will transition from Red or Green to Stopped. --- And there is no transition handling method defined for that transition, thus, no new event is being triggered causing the FsmDemo process flow to halt. --- --- ## 1.5) FSM Hierarchical Transitions --- --- Hierarchical Transitions allow to re-use readily available and implemented FSMs. --- This becomes in very useful for mission building, where mission designers build complex processes and workflows, --- combining smaller FSMs to one single FSM. --- --- The FSM can embed **Sub-FSMs** that will execute and return **multiple possible Return (End) States**. --- Depending upon **which state is returned**, the main FSM can continue the flow **triggering specific events**. --- --- The method @{#FSM.AddProcess}() adds a new Sub-FSM to the FSM. --- -- ==== -- -- # **API CHANGE HISTORY** @@ -11948,8 +11646,233 @@ end do -- FSM --- FSM class - -- @type FSM + --- @type FSM -- @extends Core.Base#BASE + + + --- # 1) FSM class, extends @{Base#BASE} + -- + -- ![Transition Rules and Transition Handlers and Event Triggers](..\Presentations\FSM\Dia3.JPG) + -- + -- The FSM class is the base class of all FSM\_ derived classes. It implements the main functionality to define and execute Finite State Machines. + -- The derived FSM\_ classes extend the Finite State Machine functionality to run a workflow process for a specific purpose or component. + -- + -- Finite State Machines have **Transition Rules**, **Transition Handlers** and **Event Triggers**. + -- + -- The **Transition Rules** define the "Process Flow Boundaries", that is, + -- the path that can be followed hopping from state to state upon triggered events. + -- If an event is triggered, and there is no valid path found for that event, + -- an error will be raised and the FSM will stop functioning. + -- + -- The **Transition Handlers** are special methods that can be defined by the mission designer, following a defined syntax. + -- If the FSM object finds a method of such a handler, then the method will be called by the FSM, passing specific parameters. + -- The method can then define its own custom logic to implement the FSM workflow, and to conduct other actions. + -- + -- The **Event Triggers** are methods that are defined by the FSM, which the mission designer can use to implement the workflow. + -- Most of the time, these Event Triggers are used within the Transition Handler methods, so that a workflow is created running through the state machine. + -- + -- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation. + -- The below documentation has a seperate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**. + -- + -- ## 1.1) FSM Linear Transitions + -- + -- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**. + -- The Lineair transition rule evaluation will always be done from the **current state** of the FSM. + -- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop. + -- + -- ### 1.1.1) FSM Transition Rules + -- + -- The FSM has transition rules that it follows and validates, as it walks the process. + -- These rules define when an FSM can transition from a specific state towards an other specific state upon a triggered event. + -- + -- The method @{#FSM.AddTransition}() specifies a new possible Transition Rule for the FSM. + -- + -- The initial state can be defined using the method @{#FSM.SetStartState}(). The default start state of an FSM is "None". + -- + -- Find below an example of a Linear Transition Rule definition for an FSM. + -- + -- local Fsm3Switch = FSM:New() -- #FsmDemo + -- FsmSwitch:SetStartState( "Off" ) + -- FsmSwitch:AddTransition( "Off", "SwitchOn", "On" ) + -- FsmSwitch:AddTransition( "Off", "SwitchMiddle", "Middle" ) + -- FsmSwitch:AddTransition( "On", "SwitchOff", "Off" ) + -- FsmSwitch:AddTransition( "Middle", "SwitchOff", "Off" ) + -- + -- The above code snippet models a 3-way switch Linear Transition: + -- + -- * It can be switched **On** by triggering event **SwitchOn**. + -- * It can be switched to the **Middle** position, by triggering event **SwitchMiddle**. + -- * It can be switched **Off** by triggering event **SwitchOff**. + -- * Note that once the Switch is **On** or **Middle**, it can only be switched **Off**. + -- + -- ### Some additional comments: + -- + -- Note that Linear Transition Rules **can be declared in a few variations**: + -- + -- * The From states can be **a table of strings**, indicating that the transition rule will be valid **if the current state** of the FSM will be **one of the given From states**. + -- * The From state can be a **"*"**, indicating that **the transition rule will always be valid**, regardless of the current state of the FSM. + -- + -- The below code snippet shows how the two last lines can be rewritten and consensed. + -- + -- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" ) + -- + -- ### 1.1.2) Transition Handling + -- + -- ![Transition Handlers](..\Presentations\FSM\Dia4.JPG) + -- + -- An FSM transitions in **4 moments** when an Event is being triggered and processed. + -- The mission designer can define for each moment specific logic within methods implementations following a defined API syntax. + -- These methods define the flow of the FSM process; because in those methods the FSM Internal Events will be triggered. + -- + -- * To handle **State** transition moments, create methods starting with OnLeave or OnEnter concatenated with the State name. + -- * To handle **Event** transition moments, create methods starting with OnBefore or OnAfter concatenated with the Event name. + -- + -- **The OnLeave and OnBefore transition methods may return false, which will cancel the transition!** + -- + -- Transition Handler methods need to follow the above specified naming convention, but are also passed parameters from the FSM. + -- These parameters are on the correct order: From, Event, To: + -- + -- * From = A string containing the From state. + -- * Event = A string containing the Event name that was triggered. + -- * To = A string containing the To state. + -- + -- On top, each of these methods can have a variable amount of parameters passed. See the example in section [1.1.3](#1.1.3\)-event-triggers). + -- + -- ### 1.1.3) Event Triggers + -- + -- ![Event Triggers](..\Presentations\FSM\Dia5.JPG) + -- + -- The FSM creates for each Event two **Event Trigger methods**. + -- There are two modes how Events can be triggered, which is **synchronous** and **asynchronous**: + -- + -- * The method **FSM:Event()** triggers an Event that will be processed **synchronously** or **immediately**. + -- * The method **FSM:__Event( __seconds__ )** triggers an Event that will be processed **asynchronously** over time, waiting __x seconds__. + -- + -- The destinction between these 2 Event Trigger methods are important to understand. An asynchronous call will "log" the Event Trigger to be executed at a later time. + -- Processing will just continue. Synchronous Event Trigger methods are useful to change states of the FSM immediately, but may have a larger processing impact. + -- + -- The following example provides a little demonstration on the difference between synchronous and asynchronous Event Triggering. + -- + -- function FSM:OnAfterEvent( From, Event, To, Amount ) + -- self:T( { Amount = Amount } ) + -- end + -- + -- local Amount = 1 + -- FSM:__Event( 5, Amount ) + -- + -- Amount = Amount + 1 + -- FSM:Event( Text, Amount ) + -- + -- In this example, the **:OnAfterEvent**() Transition Handler implementation will get called when **Event** is being triggered. + -- Before we go into more detail, let's look at the last 4 lines of the example. + -- The last line triggers synchronously the **Event**, and passes Amount as a parameter. + -- The 3rd last line of the example triggers asynchronously **Event**. + -- Event will be processed after 5 seconds, and Amount is given as a parameter. + -- + -- The output of this little code fragment will be: + -- + -- * Amount = 2 + -- * Amount = 2 + -- + -- Because ... When Event was asynchronously processed after 5 seconds, Amount was set to 2. So be careful when processing and passing values and objects in asynchronous processing! + -- + -- ### 1.1.4) Linear Transition Example + -- + -- This example is fully implemented in the MOOSE test mission on GITHUB: [FSM-100 - Transition Explanation](https://github.com/FlightControl-Master/MOOSE/blob/master/Moose%20Test%20Missions/FSM%20-%20Finite%20State%20Machine/FSM-100%20-%20Transition%20Explanation/FSM-100%20-%20Transition%20Explanation.lua) + -- + -- It models a unit standing still near Batumi, and flaring every 5 seconds while switching between a Green flare and a Red flare. + -- The purpose of this example is not to show how exciting flaring is, but it demonstrates how a Linear Transition FSM can be build. + -- Have a look at the source code. The source code is also further explained below in this section. + -- + -- The example creates a new FsmDemo object from class FSM. + -- It will set the start state of FsmDemo to state **Green**. + -- Two Linear Transition Rules are created, where upon the event **Switch**, + -- the FsmDemo will transition from state **Green** to **Red** and from **Red** back to **Green**. + -- + -- ![Transition Example](..\Presentations\FSM\Dia6.JPG) + -- + -- local FsmDemo = FSM:New() -- #FsmDemo + -- FsmDemo:SetStartState( "Green" ) + -- FsmDemo:AddTransition( "Green", "Switch", "Red" ) + -- FsmDemo:AddTransition( "Red", "Switch", "Green" ) + -- + -- In the above example, the FsmDemo could flare every 5 seconds a Green or a Red flare into the air. + -- The next code implements this through the event handling method **OnAfterSwitch**. + -- + -- ![Transition Flow](..\Presentations\FSM\Dia7.JPG) + -- + -- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) + -- self:T( { From, Event, To, FsmUnit } ) + -- + -- if From == "Green" then + -- FsmUnit:Flare(FLARECOLOR.Green) + -- else + -- if From == "Red" then + -- FsmUnit:Flare(FLARECOLOR.Red) + -- end + -- end + -- self:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. + -- end + -- + -- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the first Switch event to happen in 5 seconds. + -- + -- The OnAfterSwitch implements a loop. The last line of the code fragment triggers the Switch Event within 5 seconds. + -- Upon the event execution (after 5 seconds), the OnAfterSwitch method is called of FsmDemo (cfr. the double point notation!!! ":"). + -- The OnAfterSwitch method receives from the FSM the 3 transition parameter details ( From, Event, To ), + -- and one additional parameter that was given when the event was triggered, which is in this case the Unit that is used within OnSwitchAfter. + -- + -- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) + -- + -- For debugging reasons the received parameters are traced within the DCS.log. + -- + -- self:T( { From, Event, To, FsmUnit } ) + -- + -- The method will check if the From state received is either "Green" or "Red" and will flare the respective color from the FsmUnit. + -- + -- if From == "Green" then + -- FsmUnit:Flare(FLARECOLOR.Green) + -- else + -- if From == "Red" then + -- FsmUnit:Flare(FLARECOLOR.Red) + -- end + -- end + -- + -- It is important that the Switch event is again triggered, otherwise, the FsmDemo would stop working after having the first Event being handled. + -- + -- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. + -- + -- The below code fragment extends the FsmDemo, demonstrating multiple **From states declared as a table**, adding a **Linear Transition Rule**. + -- The new event **Stop** will cancel the Switching process. + -- The transition for event Stop can be executed if the current state of the FSM is either "Red" or "Green". + -- + -- local FsmDemo = FSM:New() -- #FsmDemo + -- FsmDemo:SetStartState( "Green" ) + -- FsmDemo:AddTransition( "Green", "Switch", "Red" ) + -- FsmDemo:AddTransition( "Red", "Switch", "Green" ) + -- FsmDemo:AddTransition( { "Red", "Green" }, "Stop", "Stopped" ) + -- + -- The transition for event Stop can also be simplified, as any current state of the FSM is valid. + -- + -- FsmDemo:AddTransition( "*", "Stop", "Stopped" ) + -- + -- So... When FsmDemo:Stop() is being triggered, the state of FsmDemo will transition from Red or Green to Stopped. + -- And there is no transition handling method defined for that transition, thus, no new event is being triggered causing the FsmDemo process flow to halt. + -- + -- ## 1.5) FSM Hierarchical Transitions + -- + -- Hierarchical Transitions allow to re-use readily available and implemented FSMs. + -- This becomes in very useful for mission building, where mission designers build complex processes and workflows, + -- combining smaller FSMs to one single FSM. + -- + -- The FSM can embed **Sub-FSMs** that will execute and return **multiple possible Return (End) States**. + -- Depending upon **which state is returned**, the main FSM can continue the flow **triggering specific events**. + -- + -- The method @{#FSM.AddProcess}() adds a new Sub-FSM to the FSM. + -- + -- === + -- + -- @field #FSM FSM + -- FSM = { ClassName = "FSM", } @@ -12068,7 +11991,6 @@ do -- FSM for ProcessID, Process in pairs( self:GetProcesses() ) do if Process.From == From and Process.Event == Event then - self:T( Process ) return Process.fsm end end @@ -12097,7 +12019,7 @@ do -- FSM -- @param #number Score is a number providing the score of the status. -- @return #FSM self function FSM:AddScore( State, ScoreText, Score ) - self:F2( { State, ScoreText, Score } ) + self:F( { State, ScoreText, Score } ) self._Scores[State] = self._Scores[State] or {} self._Scores[State].ScoreText = ScoreText @@ -12115,14 +12037,15 @@ do -- FSM -- @param #number Score is a number providing the score of the status. -- @return #FSM self function FSM:AddScoreProcess( From, Event, State, ScoreText, Score ) - self:F2( { Event, State, ScoreText, Score } ) + self:F( { From, Event, State, ScoreText, Score } ) local Process = self:GetProcess( From, Event ) - self:T( { Process = Process._Name, Scores = Process._Scores, State = State, ScoreText = ScoreText, Score = Score } ) Process._Scores[State] = Process._Scores[State] or {} Process._Scores[State].ScoreText = ScoreText Process._Scores[State].Score = Score + + self:T( Process._Scores ) return Process end @@ -12178,10 +12101,20 @@ do -- FSM function FSM:_call_handler( handler, params, EventName ) + + local ErrorHandler = function( errmsg ) + + env.info( "Error in SCHEDULER function:" .. errmsg ) + if debug ~= nil then + env.info( debug.traceback() ) + end + + return errmsg + end if self[handler] then self:T( "Calling " .. handler ) self._EventSchedules[EventName] = nil - local Value = self[handler]( self, unpack(params) ) + local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler ) return Value end end @@ -12358,10 +12291,18 @@ end do -- FSM_CONTROLLABLE - --- FSM_CONTROLLABLE class - -- @type FSM_CONTROLLABLE + --- @type FSM_CONTROLLABLE -- @field Wrapper.Controllable#CONTROLLABLE Controllable -- @extends Core.Fsm#FSM + + --- # FSM_CONTROLLABLE, extends @{#FSM} + -- + -- FSM_CONTROLLABLE class models Finite State Machines for @{Controllable}s, which are @{Group}s, @{Unit}s, @{Client}s. + -- + -- === + -- + -- @field #FSM_CONTROLLABLE FSM_CONTROLLABLE + -- FSM_CONTROLLABLE = { ClassName = "FSM_CONTROLLABLE", } @@ -12380,8 +12321,66 @@ do -- FSM_CONTROLLABLE self:SetControllable( Controllable ) end + self:AddTransition( "*", "Stop", "Stopped" ) + + --- OnBefore Transition Handler for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] OnBeforeStop + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] OnAfterStop + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] Stop + -- @param #FSM_CONTROLLABLE self + + --- Asynchronous Event Trigger for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] __Stop + -- @param #FSM_CONTROLLABLE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#FSM_CONTROLLABLE] OnLeaveStopped + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Stopped. + -- @function [parent=#FSM_CONTROLLABLE] OnEnterStopped + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + return self end + + --- OnAfter Transition Handler for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] OnAfterStop + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function FSM_CONTROLLABLE:OnAfterStop(Controllable,From,Event,To) + + -- Clear all pending schedules + self.CallScheduler:Clear() + end --- Sets the CONTROLLABLE object that the FSM_CONTROLLABLE governs. -- @param #FSM_CONTROLLABLE self @@ -12424,10 +12423,19 @@ end do -- FSM_PROCESS - --- FSM_PROCESS class - -- @type FSM_PROCESS + --- @type FSM_PROCESS -- @field Tasking.Task#TASK Task -- @extends Core.Fsm#FSM_CONTROLLABLE + + + --- # FSM_PROCESS, extends @{#FSM} + -- + -- FSM_PROCESS class models Finite State Machines for @{Task} actions, which control @{Client}s. + -- + -- === + -- + -- @field #FSM_PROCESS FSM_PROCESS + -- FSM_PROCESS = { ClassName = "FSM_PROCESS", } @@ -12449,12 +12457,34 @@ do -- FSM_PROCESS function FSM_PROCESS:Init( FsmProcess ) self:T( "No Initialisation" ) end + + function FSM_PROCESS:_call_handler( handler, params, EventName ) + + local ErrorHandler = function( errmsg ) + + env.info( "Error in FSM_PROCESS call handler:" .. errmsg ) + if debug ~= nil then + env.info( debug.traceback() ) + end + + return errmsg + end + + if self[handler] then + self:F3( "Calling " .. handler ) + self._EventSchedules[EventName] = nil + local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler ) + return Value + --return self[handler]( self, self.Controllable, unpack( params ) ) + end + end --- Creates a new FSM_PROCESS object based on this FSM_PROCESS. -- @param #FSM_PROCESS self -- @return #FSM_PROCESS function FSM_PROCESS:Copy( Controllable, Task ) self:T( { self:GetClassNameAndID() } ) + local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS @@ -12473,7 +12503,7 @@ do -- FSM_PROCESS -- Copy Processes for ProcessID, Process in pairs( self:GetProcesses() ) do - self:T( { Process} ) + self:E( { Process} ) local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents ) end @@ -12491,6 +12521,22 @@ do -- FSM_PROCESS return NewFsm end + + --- Removes an FSM_PROCESS object. + -- @param #FSM_PROCESS self + -- @return #FSM_PROCESS + function FSM_PROCESS:Remove() + self:T( { self:GetClassNameAndID() } ) + + -- Copy Processes + for ProcessID, Process in pairs( self:GetProcesses() ) do + self:E( { Process} ) + Process.fsm:Remove() + Process.fsm = nil + end + + return self + end --- Sets the task of the process. -- @param #FSM_PROCESS self @@ -12589,14 +12635,14 @@ end -- @param #string Event -- @param #string From -- @param #string To - function FSM_PROCESS:onstatechange( ProcessUnit, From, Event, To, Dummy ) + function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To, Dummy ) self:T( { ProcessUnit, From, Event, To, Dummy, self:IsTrace() } ) if self:IsTrace() then MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() end - self:T( self._Scores[To] ) + self:T( { Scores = self._Scores, To = To } ) -- TODO: This needs to be reworked with a callback functions allocated within Task, and set within the mission script from the Task Objects... if self._Scores[To] then @@ -12616,6 +12662,15 @@ do -- FSM_TASK -- @type FSM_TASK -- @field Tasking.Task#TASK Task -- @extends Core.Fsm#FSM + + --- # FSM_TASK, extends @{#FSM} + -- + -- FSM_TASK class models Finite State Machines for @{Task}s. + -- + -- === + -- + -- @field #FSM_TASK FSM_TASK + -- FSM_TASK = { ClassName = "FSM_TASK", } @@ -12651,6 +12706,17 @@ do -- FSM_SET -- @type FSM_SET -- @field Core.Set#SET_BASE Set -- @extends Core.Fsm#FSM + + + --- # FSM_SET, extends @{#FSM} + -- + -- FSM_SET class models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here + -- for multiple objects or the position of the state machine in the process. + -- + -- === + -- + -- @field #FSM_SET FSM_SET + -- FSM_SET = { ClassName = "FSM_SET", } @@ -12698,7 +12764,336 @@ do -- FSM_SET end -- FSM_SET ---- This module contains the OBJECT class. +--- **Core** - The RADIO class is responsible for **transmitting radio communications**. +-- +-- --- bitmap +-- +-- === +-- +-- What are radio communications in DCS ? +-- +-- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM), +-- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**. +-- +-- How to supply DCS my own Sound Files ? +-- +-- * Your sound files need to be encoded in **.ogg** or .wav, +-- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings, +-- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file), +-- * For simplicty sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission. +-- +-- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Unit#UNIT} or a @{Group#GROUP} or by any other @{Positionable#POSITIONABLE} +-- +-- * If the transmitter is a @{Unit#UNIT} or a @{Group#GROUP}, DCS will set the power of the transmission automatically, +-- * If the transmitter is any other @{Positionable#POSITIONABLE}, the transmisison can't be subtitled or looped. +-- +-- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft, +-- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below). +-- If a FC3 airacraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. +-- +-- === +-- +-- ### Authors: Hugues "Grey_Echo" Bousquet +-- +-- @module Radio + +--- # 1) RADIO class, extends @{Base#BASE} +-- +-- ## 1.1) RADIO usage +-- +-- There are 3 steps to a successful radio transmission. +-- +-- * First, you need to **"add" a @{#RADIO} object** to your @{Positionable#POSITIONABLE}. This is done using the @{Positionable#POSITIONABLE.GetRadio}() function, +-- * Then, you will **set the relevant parameters** to the transmission (see below), +-- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{Positionable#POSITIONABLE.Broadcast}() function. +-- +-- Methods to set relevant parameters for both a @{Unit#UNIT} or a @{Group#GROUP} or any other @{Positionable#POSITIONABLE} +-- +-- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"), +-- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission, +-- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission. +-- +-- Additional Methods to set relevant parameters if the transmiter is a @{Unit#UNIT} or a @{Group#GROUP} +-- +-- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped, +-- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration, +-- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call +-- +-- Additional Methods to set relevant parameters if the transmiter is any other @{Wrapper.Positionable#POSITIONABLE} +-- +-- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts +-- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call +-- +-- What is this power thing ? +-- +-- * If your transmission is sent by a @{Positionable#POSITIONABLE} other than a @{Unit#UNIT} or a @{Group#GROUP}, you can set the power of the antenna, +-- * Otherwise, DCS sets it automatically, depending on what's available on your Unit, +-- * If the player gets **too far** from the transmiter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**, +-- * This an automated DCS calculation you have no say on, +-- * For reference, a standard VOR station has a 100W antenna, a standard AA TACAN has a 120W antenna, and civilian ATC's antenna usually range between 300 and 500W, +-- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission. +-- +-- @type RADIO +-- @field Wrapper.Positionable#POSITIONABLE Positionable The transmiter +-- @field #string FileName Name of the sound file +-- @field #number Frequency Frequency of the transmission in Hz +-- @field #number Modulation Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM) +-- @field #string Subtitle Subtitle of the transmission +-- @field #number SubtitleDuration Duration of the Subtitle in seconds +-- @field #number Power Power of the antenna is Watts +-- @field #boolean Loop +-- @extends Core.Base#BASE +RADIO = { + ClassName = "RADIO", + FileName = "", + Frequency = 0, + Modulation = radio.modulation.AM, + Subtitle = "", + SubtitleDuration = 0, + Power = 100, + Loop = 0, +} + +--- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast +-- @param #RADIO self +-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. +-- @return #RADIO Radio +-- @return #nil If Positionable is invalid +-- @usage +-- -- If you want to create a RADIO, you probably should use @{Positionable#POSITIONABLE.GetRadio}() instead +function RADIO:New(Positionable) + local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO + + self:F(Positionable) + + if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid + self.Positionable = Positionable + return self + end + + self:E({"The passed positionable is invalid, no RADIO created", Positionable}) + return nil +end + +--- Check validity of the filename passed and sets RADIO.FileName +-- @param #RADIO self +-- @param #string FileName File name of the sound file (i.e. "Noise.ogg") +-- @return #RADIO self +function RADIO:SetFileName(FileName) + self:F2(FileName) + + if type(FileName) == "string" then + if FileName:find(".ogg") or FileName:find(".wav") then + if not FileName:find("l10n/DEFAULT/") then + FileName = "l10n/DEFAULT/" .. FileName + end + self.FileName = FileName + return self + end + end + + self:E({"File name invalid. Maybe something wrong with the extension ?", self.FileName}) + return self +end + +--- Check validity of the frequency passed and sets RADIO.Frequency +-- @param #RADIO self +-- @param #number Frequency in MHz (Ranges allowed for radio transmissions in DCS : 30-88 / 108-152 / 225-400MHz) +-- @return #RADIO self +function RADIO:SetFrequency(Frequency) + self:F2(Frequency) + if type(Frequency) == "number" then + -- If frequency is in range + if (Frequency >= 30 and Frequency < 88) or (Frequency >= 108 and Frequency < 152) or (Frequency >= 225 and Frequency < 400) then + self.Frequency = Frequency * 1000000 -- Conversion in Hz + -- If the RADIO is attached to a UNIT or a GROUP, we need to send the DCS Command "SetFrequency" to change the UNIT or GROUP frequency + if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then + self.Positionable:SetCommand({ + id = "SetFrequency", + params = { + frequency = self.Frequency, + modulation = self.Modulation, + } + }) + end + return self + end + end + self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", self.Frequency}) + return self +end + +--- Check validity of the frequency passed and sets RADIO.Modulation +-- @param #RADIO self +-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM +-- @return #RADIO self +function RADIO:SetModulation(Modulation) + self:F2(Modulation) + if type(Modulation) == "number" then + if Modulation == radio.modulation.AM or Modulation == radio.modulation.FM then --TODO Maybe make this future proof if ED decides to add an other modulation ? + self.Modulation = Modulation + return self + end + end + self:E({"Modulation is invalid. Use DCS's enum radio.modulation. Modulation unchanged.", self.Modulation}) + return self +end + +--- Check validity of the power passed and sets RADIO.Power +-- @param #RADIO self +-- @param #number Power in W +-- @return #RADIO self +function RADIO:SetPower(Power) + self:F2(Power) + if type(Power) == "number" then + self.Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that + return self + end + self:E({"Power is invalid. Power unchanged.", self.Power}) + return self +end + +--- Check validity of the loop passed and sets RADIO.Loop +-- @param #RADIO self +-- @param #boolean Loop +-- @return #RADIO self +-- @usage +function RADIO:SetLoop(Loop) + self:F2(Loop) + if type(Loop) == "boolean" then + self.Loop = Loop + return self + end + self:E({"Loop is invalid. Loop unchanged.", self.Loop}) + return self +end + +--- Check validity of the subtitle and the subtitleDuration passed and sets RADIO.subtitle and RADIO.subtitleDuration +-- @param #RADIO self +-- @param #string Subtitle +-- @param #number SubtitleDuration in s +-- @return #RADIO self +-- @usage +-- -- Both parameters are mandatory, since it wouldn't make much sense to change the Subtitle and not its duration +function RADIO:SetSubtitle(Subtitle, SubtitleDuration) + self:F2({Subtitle, SubtitleDuration}) + if type(Subtitle) == "string" then + self.Subtitle = Subtitle + else + self.Subtitle = "" + self:E({"Subtitle is invalid. Subtitle reset.", self.Subtitle}) + end + if type(SubtitleDuration) == "number" then + if math.floor(math.abs(SubtitleDuration)) == SubtitleDuration then + self.SubtitleDuration = SubtitleDuration + return self + end + end + self.SubtitleDuration = 0 + self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration}) +end + +--- Create a new transmission, that is to say, populate the RADIO with relevant data +-- @param #RADIO self +-- @param #string FileName +-- @param #number Frequency in MHz +-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM +-- @param #number Power in W +-- @return #RADIO self +-- @usage +-- -- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP, +-- but it will work with a UNIT or a GROUP anyway +-- -- Only the RADIO and the Filename are mandatory +function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power) + self:F({FileName, Frequency, Modulation, Power}) + + self:SetFileName(FileName) + if Frequency then self:SetFrequency(Frequency) end + if Modulation then self:SetModulation(Modulation) end + if Power then self:SetPower(Power) end + + return self +end + + +--- Create a new transmission, that is to say, populate the RADIO with relevant data +-- @param #RADIO self +-- @param #string FileName +-- @param #string Subtitle +-- @param #number SubtitleDuration in s +-- @param #number Frequency in MHz +-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM +-- @param #boolean Loop +-- @return #RADIO self +-- @usage +-- -- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP, +-- but it will work for any POSITIONABLE +-- -- Only the RADIO and the Filename are mandatory +function RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop) + self:F({FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop}) + + self:SetFileName(FileName) + if Subtitle then self:SetSubtitle(Subtitle) end + if SubtitleDuration then self:SetSubtitleDuration(SubtitleDuration) end + if Frequency then self:SetFrequency(Frequency) end + if Modulation then self:SetModulation(Modulation) end + if Loop then self:SetLoop(Loop) end + + return self +end + +--- Actually Broadcast the transmission +-- @param #RADIO self +-- @return #RADIO self +-- @usage +-- -- The Radio has to be populated with the new transmission before broadcasting. +-- -- Please use RADIO setters or either @{Radio#RADIO.NewGenericTransmission} or @{Radio#RADIO.NewUnitTransmission} +-- -- This class is in fact pretty smart, it determines the right DCS function to use depending on the type of POSITIONABLE +-- -- If the POSITIONABLE is not a UNIT or a GROUP, we use the generic (but limited) trigger.action.radioTransmission() +-- -- If the POSITIONABLE is a UNIT or a GROUP, we use the "TransmitMessage" Command +-- -- If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored. +-- -- If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration and Loop are ignored +function RADIO:Broadcast() + self:F() + -- If the POSITIONABLE is actually a UNIT or a GROUP, use the more complicated DCS command system + if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then + self:T2("Broadcasting from a UNIT or a GROUP") + self.Positionable:SetCommand({ + id = "TransmitMessage", + params = { + file = self.FileName, + duration = self.SubtitleDuration, + subtitle = self.Subtitle, + loop = self.Loop, + } + }) + else + -- If the POSITIONABLE is anything else, we revert to the general singleton function + self:T2("Broadcasting from a POSITIONABLE") + trigger.action.radioTransmission(self.FileName, self.Positionable:GetPositionVec3(), self.Modulation, false, self.Frequency, self.Power) + end + return self +end + +--- Stops a transmission +-- @param #RADIO self +-- @return #RADIO self +-- @usage +-- -- Especially usefull to stop the broadcast of looped transmissions +-- -- Only works with broadcasts from UNIT or GROUP +function RADIO:StopBroadcast() + self:F() + -- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command + if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then + self.Positionable:SetCommand({ + id = "StopTransmission", + params = {} + }) + else + self:E("This broadcast can't be stopped. It's not looped either, so please wait for the end of the sound file playback") + end + return self +end--- This module contains the OBJECT class. -- -- 1) @{Object#OBJECT} class, extends @{Base#BASE} -- =========================================================== @@ -12846,17 +13241,19 @@ function IDENTIFIABLE:New( IdentifiableName ) return self end ---- Returns if the Identifiable is alive. +--- Returns if the Identifiable is alive. +-- If the Identifiable is not alive, nil is returned. +-- If the Identifiable is alive, true is returned. -- @param #IDENTIFIABLE self -- @return #boolean true if Identifiable is alive. --- @return #nil The DCS Identifiable is not existing or alive. +-- @return #nil if the Identifiable is not existing or is not alive. function IDENTIFIABLE:IsAlive() self:F3( self.IdentifiableName ) - local DCSIdentifiable = self:GetDCSObject() + local DCSIdentifiable = self:GetDCSObject() -- Dcs.DCSObject#Object if DCSIdentifiable then - local IdentifiableIsAlive = DCSIdentifiable:isExist() + local IdentifiableIsAlive = DCSIdentifiable:isExist() return IdentifiableIsAlive end @@ -13146,8 +13543,11 @@ end --- Returns a random @{DCSTypes#Vec3} vector within a range, indicating the point in 3D of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self +-- @param #number Radius -- @return Dcs.DCSTypes#Vec3 The 3D point vector of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. +-- @usage +-- -- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP function POSITIONABLE:GetRandomVec3( Radius ) self:F2( self.PositionableName ) @@ -13155,14 +13555,20 @@ function POSITIONABLE:GetRandomVec3( Radius ) if DCSPositionable then local PositionablePointVec3 = DCSPositionable:getPosition().p - local PositionableRandomVec3 = {} - local angle = math.random() * math.pi*2; - PositionableRandomVec3.x = PositionablePointVec3.x + math.cos( angle ) * math.random() * Radius; - PositionableRandomVec3.y = PositionablePointVec3.y - PositionableRandomVec3.z = PositionablePointVec3.z + math.sin( angle ) * math.random() * Radius; - self:T3( PositionableRandomVec3 ) - return PositionableRandomVec3 + if Radius then + local PositionableRandomVec3 = {} + local angle = math.random() * math.pi*2; + PositionableRandomVec3.x = PositionablePointVec3.x + math.cos( angle ) * math.random() * Radius; + PositionableRandomVec3.y = PositionablePointVec3.y + PositionableRandomVec3.z = PositionablePointVec3.z + math.sin( angle ) * math.random() * Radius; + + self:T3( PositionableRandomVec3 ) + return PositionableRandomVec3 + else + self:E("Radius is nil, returning the PointVec3 of the POSITIONABLE", PositionablePointVec3) + return PositionablePointVec3 + end end return nil @@ -13230,6 +13636,7 @@ end --- Returns the POSITIONABLE heading in degrees. -- @param Wrapper.Positionable#POSITIONABLE self -- @return #number The POSTIONABLE heading +-- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetHeading() local DCSPositionable = self:GetDCSObject() @@ -13443,10 +13850,14 @@ function POSITIONABLE:Message( Message, Duration, Name ) return nil end - - - - +--- Create a @{Radio#RADIO}, to allow radio transmission for this POSITIONABLE. +-- Set parameters with the methods provided, then use RADIO:Broadcast() to actually broadcast the message +-- @param #POSITIONABLE self +-- @return #RADIO Radio +function POSITIONABLE:GetRadio() + self:F2(self) + return RADIO:New(self) +end --- This module contains the CONTROLLABLE class. -- -- 1) @{Controllable#CONTROLLABLE} class, extends @{Positionable#POSITIONABLE} @@ -13906,12 +14317,20 @@ function CONTROLLABLE:CommandSwitchWayPoint( FromWayPoint, ToWayPoint ) return CommandSwitchWayPoint end ---- Perform stop route command +--- Create a stop route command, which returns a string containing the command. +-- Use the result in the method @{#CONTROLLABLE.SetCommand}(). +-- A value of true will make the ground group stop, a value of false will make it continue. +-- Note that this can only work on GROUP level, although individual UNITs can be commanded, the whole GROUP will react. +-- +-- Example missions: +-- +-- * GRP-310 +-- -- @param #CONTROLLABLE self --- @param #boolean StopRoute +-- @param #boolean StopRoute true if the ground unit needs to stop, false if it needs to continue to move. -- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:CommandStopRoute( StopRoute, Index ) - self:F2( { StopRoute, Index } ) +function CONTROLLABLE:CommandStopRoute( StopRoute ) + self:F2( { StopRoute } ) local CommandStopRoute = { id = 'StopRoute', @@ -13985,47 +14404,35 @@ function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, At return DCSTask end - --- (AIR) Attack the Unit. -- @param #CONTROLLABLE self --- @param Wrapper.Unit#UNIT AttackUnit The unit. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. +-- @param Wrapper.Unit#UNIT AttackUnit The UNIT. +-- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. -- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks. --- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. +-- @param #number Altitude (optional) The altitude from where to attack. +-- @param #boolean Visible (optional) not a clue. +-- @param #number WeaponType (optional) The WeaponType. -- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskAttackUnit( AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack ) - self:F2( { self.ControllableName, AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack } ) - - -- AttackUnit = { - -- id = 'AttackUnit', - -- params = { - -- unitId = Unit.ID, - -- weaponType = number, - -- expend = enum AI.Task.WeaponExpend - -- attackQty = number, - -- direction = Azimuth, - -- attackQtyLimit = boolean, - -- controllableAttack = boolean, - -- } - -- } +function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType ) + self:F2( { self.ControllableName, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType } ) local DCSTask DCSTask = { id = 'AttackUnit', params = { - altitudeEnabled = true, unitId = AttackUnit:GetID(), - attackQtyLimit = AttackQtyLimit or false, - attackQty = AttackQty or 2, + groupAttack = GroupAttack or false, + visible = Visible or false, expend = WeaponExpend or "Auto", - altitude = 2000, - directionEnabled = true, - groupAttack = true, - --weaponType = WeaponType or 1073741822, - direction = Direction or 0, + directionEnabled = Direction and true or false, + direction = Direction, + altitudeEnabled = Altitude and true or false, + altitude = Altitude or 30, + attackQtyLimit = AttackQty and true or false, + attackQty = AttackQty, + weaponType = WeaponType } } @@ -14640,7 +15047,7 @@ function CONTROLLABLE:EnRouteTaskEngageGroup( AttackGroup, Priority, WeaponType, end ---- (AIR) Attack the Unit. +--- (AIR) Search and attack the Unit. -- @param #CONTROLLABLE self -- @param Wrapper.Unit#UNIT EngageUnit The UNIT. -- @param #number Priority (optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. @@ -15110,90 +15517,6 @@ function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation ) return nil end ---- (AIR) Return the Controllable to an @{Airbase#AIRBASE} --- A speed can be given in km/h. --- A given formation can be given. --- @param #CONTROLLABLE self --- @param Wrapper.Airbase#AIRBASE ReturnAirbase The @{Airbase#AIRBASE} to return to. --- @param #number Speed (optional) The speed. --- @return #string The route -function CONTROLLABLE:RouteReturnToAirbase( ReturnAirbase, Speed ) - self:F2( { ReturnAirbase, Speed } ) - --- Example --- [4] = --- { --- ["alt"] = 45, --- ["type"] = "Land", --- ["action"] = "Landing", --- ["alt_type"] = "BARO", --- ["formation_template"] = "", --- ["properties"] = --- { --- ["vnav"] = 1, --- ["scale"] = 0, --- ["angle"] = 0, --- ["vangle"] = 0, --- ["steer"] = 2, --- }, -- end of ["properties"] --- ["ETA"] = 527.81058817743, --- ["airdromeId"] = 12, --- ["y"] = 243127.2973737, --- ["x"] = -5406.2803440839, --- ["name"] = "DictKey_WptName_53", --- ["speed"] = 138.88888888889, --- ["ETA_locked"] = false, --- ["task"] = --- { --- ["id"] = "ComboTask", --- ["params"] = --- { --- ["tasks"] = --- { --- }, -- end of ["tasks"] --- }, -- end of ["params"] --- }, -- end of ["task"] --- ["speed_locked"] = true, --- }, -- end of [4] - - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - - local ControllablePoint = self:GetVec2() - local ControllableVelocity = self:GetMaxVelocity() - - local PointFrom = {} - PointFrom.x = ControllablePoint.x - PointFrom.y = ControllablePoint.y - PointFrom.type = "Turning Point" - PointFrom.action = "Turning Point" - PointFrom.speed = ControllableVelocity - - - local PointTo = {} - local AirbasePoint = ReturnAirbase:GetVec2() - - PointTo.x = AirbasePoint.x - PointTo.y = AirbasePoint.y - PointTo.type = "Land" - PointTo.action = "Landing" - PointTo.airdromeId = ReturnAirbase:GetID()-- Airdrome ID - self:T(PointTo.airdromeId) - --PointTo.alt = 0 - - local Points = { PointFrom, PointTo } - - self:T3( Points ) - - local Route = { points = Points, } - - return Route - end - - return nil -end -- Commands @@ -15234,6 +15557,8 @@ function CONTROLLABLE:GetTaskRoute() return routines.utils.deepCopy( _DATABASE.Templates.Controllables[self.ControllableName].Template.route.points ) end + + --- Return the route of a controllable by using the @{Database#DATABASE} class. -- @param #CONTROLLABLE self -- @param #number Begin The route point from where the copy will start. The base route point is 0. @@ -15757,11 +16082,11 @@ function CONTROLLABLE:WayPointExecute( WayPoint, WaitTime ) return self end --- Message APIs--- This module contains the GROUP class. +-- Message APIs--- **Wrapper** -- GROUP is a wrapper class for the DCS Class Group. -- --- 1) @{Group#GROUP} class, extends @{Controllable#CONTROLLABLE} --- ============================================================= --- The @{Group#GROUP} class is a wrapper class to handle the DCS Group objects: +-- === +-- +-- The @{#GROUP} class is a wrapper class to handle the DCS Group objects: -- -- * Support all DCS Group APIs. -- * Enhance with Group specific APIs not in the DCS Group API set. @@ -15770,60 +16095,8 @@ end -- -- **IMPORTANT: ONE SHOULD NEVER SANATIZE these GROUP OBJECT REFERENCES! (make the GROUP object references nil).** -- --- 1.1) GROUP reference methods --- ----------------------- --- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object. --- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the @{SPAWN} class). --- --- The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference --- using the DCS Group or the DCS GroupName. --- --- Another thing to know is that GROUP objects do not "contain" the DCS Group object. --- The GROUP methods will reference the DCS Group object by name when it is needed during API execution. --- If the DCS Group object does not exist or is nil, the GROUP methods will return nil and log an exception in the DCS.log file. --- --- The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance: --- --- * @{#GROUP.Find}(): Find a GROUP instance from the _DATABASE object using a DCS Group object. --- * @{#GROUP.FindByName}(): Find a GROUP instance from the _DATABASE object using a DCS Group name. --- --- ## 1.2) GROUP task methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} task methods section for a description of the task methods. --- --- ### 1.2.4) Obtain the mission from group templates +-- See the detailed documentation on the GROUP class. -- --- Group templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a group and assign it to another: --- --- * @{Controllable#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template. --- --- ## 1.3) GROUP Command methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} command methods section for a description of the command methods. --- --- ## 1.4) GROUP option methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} option methods section for a description of the option methods. --- --- ## 1.5) GROUP Zone validation methods --- --- The group can be validated whether it is completely, partly or not within a @{Zone}. --- Use the following Zone validation methods on the group: --- --- * @{#GROUP.IsCompletelyInZone}: Returns true if all units of the group are within a @{Zone}. --- * @{#GROUP.IsPartlyInZone}: Returns true if some units of the group are within a @{Zone}. --- * @{#GROUP.IsNotInZone}: Returns true if none of the group units of the group are within a @{Zone}. --- --- The zone can be of any @{Zone} class derived from @{Zone#ZONE_BASE}. So, these methods are polymorphic to the zones tested on. --- --- ## 1.6) GROUP AI methods --- --- A GROUP has AI methods to control the AI activation. --- --- * @{#GROUP.SetAIOnOff}(): Turns the GROUP AI On or Off. --- * @{#GROUP.SetAIOn}(): Turns the GROUP AI On. --- * @{#GROUP.SetAIOff}(): Turns the GROUP AI Off. --- -- ==== -- -- # **API CHANGE HISTORY** @@ -15835,6 +16108,8 @@ end -- -- Hereby the change log: -- +-- 2017-03-26: GROUP:**RouteRTB( RTBAirbase, Speed )** added. +-- -- 2017-03-07: GROUP:**HandleEvent( Event, EventFunction )** added. -- 2017-03-07: GROUP:**UnHandleEvent( Event )** added. -- @@ -15859,10 +16134,66 @@ end -- @module Group -- @author FlightControl ---- The GROUP class --- @type GROUP +--- @type GROUP -- @extends Wrapper.Controllable#CONTROLLABLE -- @field #string GroupName The name of the group. + +--- +-- # GROUP class, extends @{Controllable#CONTROLLABLE} +-- +-- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object. +-- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the @{SPAWN} class). +-- +-- The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference +-- using the DCS Group or the DCS GroupName. +-- +-- Another thing to know is that GROUP objects do not "contain" the DCS Group object. +-- The GROUP methods will reference the DCS Group object by name when it is needed during API execution. +-- If the DCS Group object does not exist or is nil, the GROUP methods will return nil and log an exception in the DCS.log file. +-- +-- The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance: +-- +-- * @{#GROUP.Find}(): Find a GROUP instance from the _DATABASE object using a DCS Group object. +-- * @{#GROUP.FindByName}(): Find a GROUP instance from the _DATABASE object using a DCS Group name. +-- +-- ## GROUP task methods +-- +-- A GROUP is a @{Controllable}. See the @{Controllable} task methods section for a description of the task methods. +-- +-- ### Obtain the mission from group templates +-- +-- Group templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a group and assign it to another: +-- +-- * @{Controllable#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template. +-- +-- ## GROUP Command methods +-- +-- A GROUP is a @{Controllable}. See the @{Controllable} command methods section for a description of the command methods. +-- +-- ## GROUP option methods +-- +-- A GROUP is a @{Controllable}. See the @{Controllable} option methods section for a description of the option methods. +-- +-- ## GROUP Zone validation methods +-- +-- The group can be validated whether it is completely, partly or not within a @{Zone}. +-- Use the following Zone validation methods on the group: +-- +-- * @{#GROUP.IsCompletelyInZone}: Returns true if all units of the group are within a @{Zone}. +-- * @{#GROUP.IsPartlyInZone}: Returns true if some units of the group are within a @{Zone}. +-- * @{#GROUP.IsNotInZone}: Returns true if none of the group units of the group are within a @{Zone}. +-- +-- The zone can be of any @{Zone} class derived from @{Zone#ZONE_BASE}. So, these methods are polymorphic to the zones tested on. +-- +-- ## GROUP AI methods +-- +-- A GROUP has AI methods to control the AI activation. +-- +-- * @{#GROUP.SetAIOnOff}(): Turns the GROUP AI On or Off. +-- * @{#GROUP.SetAIOn}(): Turns the GROUP AI On. +-- * @{#GROUP.SetAIOff}(): Turns the GROUP AI Off. +-- +-- @field #GROUP GROUP GROUP = { ClassName = "GROUP", } @@ -15872,7 +16203,7 @@ GROUP = { -- @param Dcs.DCSWrapper.Group#Group GroupName The DCS Group name -- @return #GROUP self function GROUP:Register( GroupName ) - local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) + self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) self:F2( GroupName ) self.GroupName = GroupName @@ -15936,19 +16267,33 @@ function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3 return nil end ---- Returns if the DCS Group is alive. --- When the group exists at run-time, this method will return true, otherwise false. +--- Returns if the Group is alive. +-- The Group must: +-- +-- * Exist at run-time. +-- * Has at least one unit. +-- +-- When the first @{Unit} of the Group is active, it will return true. +-- If the first @{Unit} of the Group is inactive, it will return false. +-- -- @param #GROUP self --- @return #boolean true if the DCS Group is alive. +-- @return #boolean true if the Group is alive and active. +-- @return #boolean false if the Group is alive but inactive. +-- @return #nil if the group does not exist anymore. function GROUP:IsAlive() self:F2( self.GroupName ) - local DCSGroup = self:GetDCSObject() + local DCSGroup = self:GetDCSObject() -- Dcs.DCSGroup#Group if DCSGroup then - local GroupIsAlive = DCSGroup:isExist() and DCSGroup:getUnit(1) ~= nil - self:T3( GroupIsAlive ) - return GroupIsAlive + if DCSGroup:isExist() then + local DCSUnit = DCSGroup:getUnit(1) -- Dcs.DCSUnit#Unit + if DCSUnit then + local GroupIsAlive = DCSUnit:isActive() + self:T3( GroupIsAlive ) + return GroupIsAlive + end + end end return nil @@ -15990,7 +16335,7 @@ function GROUP:GetCategory() return nil end ---- Returns the category name of the DCS Group. +--- Returns the category name of the #GROUP. -- @param #GROUP self -- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship function GROUP:GetCategoryName() @@ -16195,6 +16540,7 @@ function GROUP:GetVec2() end --- Returns the current Vec3 vector of the first DCS Unit in the GROUP. +-- @param #GROUP self -- @return Dcs.DCSTypes#Vec3 Current Vec3 of the first DCS Unit of the GROUP. function GROUP:GetVec3() self:F2( self.GroupName ) @@ -16204,7 +16550,65 @@ function GROUP:GetVec3() return GroupVec3 end +--- Returns a POINT_VEC2 object indicating the point in 2D of the first UNIT of the GROUP within the mission. +-- @param #GROUP self +-- @return Core.Point#POINT_VEC2 The 2D point vector of the first DCS Unit of the GROUP. +-- @return #nil The first UNIT is not existing or alive. +function GROUP:GetPointVec2() + self:F2(self.GroupName) + local FirstUnit = self:GetUnit(1) + + if FirstUnit then + local FirstUnitPointVec2 = FirstUnit:GetPointVec2() + self:T3(FirstUnitPointVec2) + return FirstUnitPointVec2 + end + + return nil +end + +--- Returns a random @{DCSTypes#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP. +-- @param #GROUP self +-- @param #number Radius +-- @return Dcs.DCSTypes#Vec3 The random 3D point vector around the first UNIT of the GROUP. +-- @return #nil The GROUP is invalid or empty +-- @usage +-- -- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP +function GROUP:GetRandomVec3(Radius) + self:F2(self.GroupName) + + local FirstUnit = self:GetUnit(1) + + if FirstUnit then + local FirstUnitRandomPointVec3 = FirstUnit:GetRandomVec3(Radius) + self:T3(FirstUnitRandomPointVec3) + return FirstUnitRandomPointVec3 + end + + return nil +end + +--- Returns the mean heading of every UNIT in the GROUP in degrees +-- @param #GROUP self +-- @return #number mean heading of the GROUP +-- @return #nil The first UNIT is not existing or alive. +function GROUP:GetHeading() + self:F2(self.GroupName) + + local GroupSize = self:GetSize() + local HeadingAccumulator = 0 + + if GroupSize then + for i = 1, GroupSize do + HeadingAccumulator = HeadingAccumulator + self:GetUnit(i):GetHeading() + end + return math.floor(HeadingAccumulator / GroupSize) + end + + return nil + +end do -- Is Zone methods @@ -16654,6 +17058,76 @@ function GROUP:InAir() return nil end +do -- Route methods + + --- (AIR) Return the Group to an @{Airbase#AIRBASE}. + -- The following things are to be taken into account: + -- + -- * The group is respawned to achieve the RTB, there may be side artefacts as a result of this. (Like weapons suddenly come back). + -- * A group consisting out of more than one unit, may rejoin formation when respawned. + -- * A speed can be given in km/h. If no speed is specified, the maximum speed of the first unit will be taken to return to base. + -- * When there is no @{Airbase} object specified, the group will return to the home base if the route of the group is pinned at take-off or at landing to a base. + -- * When there is no @{Airbase} object specified and the group route is not pinned to any airbase, it will return to the nearest airbase. + -- + -- @param #GROUP self + -- @param Wrapper.Airbase#AIRBASE RTBAirbase (optional) The @{Airbase} to return to. If blank, the controllable will return to the nearest friendly airbase. + -- @param #number Speed (optional) The Speed, if no Speed is given, the maximum Speed of the first unit is selected. + -- @return #GROUP + function GROUP:RouteRTB( RTBAirbase, Speed ) + self:F2( { RTBAirbase, Speed } ) + + local DCSGroup = self:GetDCSObject() + + if DCSGroup then + + if RTBAirbase then + + local GroupPoint = self:GetVec2() + local GroupVelocity = self:GetUnit(1):GetDesc().speedMax + + local PointFrom = {} + PointFrom.x = GroupPoint.x + PointFrom.y = GroupPoint.y + PointFrom.type = "Turning Point" + PointFrom.action = "Turning Point" + PointFrom.speed = GroupVelocity + + + local PointTo = {} + local AirbasePointVec2 = RTBAirbase:GetPointVec2() + local AirbaseAirPoint = AirbasePointVec2:RoutePointAir( + POINT_VEC3.RoutePointAltType.BARO, + "Land", + "Landing", + Speed or self:GetUnit(1):GetDesc().speedMax + ) + + AirbaseAirPoint["airdromeId"] = RTBAirbase:GetID() + AirbaseAirPoint["speed_locked"] = true, + + self:E(AirbaseAirPoint ) + + local Points = { PointFrom, AirbaseAirPoint } + + self:T3( Points ) + + local Template = self:GetTemplate() + Template.route.points = Points + self:Respawn( Template ) + + self:Route( Points ) + + self:Respawn(Template) + else + self:ClearTasks() + end + end + + return self + end + +end + function GROUP:OnReSpawn( ReSpawnFunction ) self.ReSpawnFunction = ReSpawnFunction @@ -16685,10 +17159,35 @@ do -- Event Handling end end ---- This module contains the UNIT class. + +do -- Players + + --- Get player names + -- @param #GROUP self + -- @return #table The group has players, an array of player names is returned. + -- @return #nil The group has no players + function GROUP:GetPlayerNames() + + local PlayerNames = nil + + local Units = self:GetUnits() + for UnitID, UnitData in pairs( Units ) do + local Unit = UnitData -- Wrapper.Unit#UNIT + local PlayerName = Unit:GetPlayerName() + if PlayerName and PlayerName ~= "" then + PlayerNames = PlayerNames or {} + table.insert( PlayerNames, PlayerName ) + end + end + + self:F( PlayerNames ) + return PlayerNames + end + +end--- **Wrapper** - UNIT is a wrapper class for the DCS Class Unit. +-- +-- === -- --- 1) @{#UNIT} class, extends @{Controllable#CONTROLLABLE} --- =========================================================== -- The @{#UNIT} class is a wrapper class to handle the DCS Unit objects: -- -- * Support all DCS Unit APIs. @@ -16696,9 +17195,14 @@ end -- * Handle local Unit Controller. -- * Manage the "state" of the DCS Unit. -- --- --- 1.1) UNIT reference methods --- ---------------------- +-- @module Unit + +--- @type UNIT +-- @extends Wrapper.Controllable#CONTROLLABLE + +--- +-- # UNIT class, extends @{Controllable#CONTROLLABLE} +-- -- For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _@{DATABASE} object. -- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Unit objects are spawned (using the @{SPAWN} class). -- @@ -16716,15 +17220,15 @@ end -- -- IMPORTANT: ONE SHOULD NEVER SANATIZE these UNIT OBJECT REFERENCES! (make the UNIT object references nil). -- --- 1.2) DCS UNIT APIs --- ------------------ +-- ## DCS UNIT APIs +-- -- The DCS Unit APIs are used extensively within MOOSE. The UNIT class has for each DCS Unit API a corresponding method. -- To be able to distinguish easily in your code the difference between a UNIT API call and a DCS Unit API call, -- the first letter of the method is also capitalized. So, by example, the DCS Unit method @{DCSWrapper.Unit#Unit.getName}() -- is implemented in the UNIT class as @{#UNIT.GetName}(). -- --- 1.3) Smoke, Flare Units --- ----------------------- +-- ## Smoke, Flare Units +-- -- The UNIT class provides methods to smoke or flare units easily. -- The @{#UNIT.SmokeBlue}(), @{#UNIT.SmokeGreen}(),@{#UNIT.SmokeOrange}(), @{#UNIT.SmokeRed}(), @{#UNIT.SmokeRed}() methods -- will smoke the unit in the corresponding color. Note that smoking a unit is done at the current position of the DCS Unit. @@ -16732,36 +17236,29 @@ end -- The @{#UNIT.FlareGreen}(), @{#UNIT.FlareRed}(), @{#UNIT.FlareWhite}(), @{#UNIT.FlareYellow}() -- methods will fire off a flare in the air with the corresponding color. Note that a flare is a one-off shot and its effect is of very short duration. -- --- 1.4) Location Position, Point --- ----------------------------- +-- ## Location Position, Point +-- -- The UNIT class provides methods to obtain the current point or position of the DCS Unit. -- The @{#UNIT.GetPointVec2}(), @{#UNIT.GetVec3}() will obtain the current **location** of the DCS Unit in a Vec2 (2D) or a **point** in a Vec3 (3D) vector respectively. -- If you want to obtain the complete **3D position** including ori�ntation and direction vectors, consult the @{#UNIT.GetPositionVec3}() method respectively. -- --- 1.5) Test if alive --- ------------------ +-- ## Test if alive +-- -- The @{#UNIT.IsAlive}(), @{#UNIT.IsActive}() methods determines if the DCS Unit is alive, meaning, it is existing and active. -- --- 1.6) Test for proximity --- ----------------------- +-- ## Test for proximity +-- -- The UNIT class contains methods to test the location or proximity against zones or other objects. -- --- ### 1.6.1) Zones +-- ### Zones +-- -- To test whether the Unit is within a **zone**, use the @{#UNIT.IsInZone}() or the @{#UNIT.IsNotInZone}() methods. Any zone can be tested on, but the zone must be derived from @{Zone#ZONE_BASE}. -- --- ### 1.6.2) Units +-- ### Units +-- -- Test if another DCS Unit is within a given radius of the current DCS Unit, use the @{#UNIT.OtherUnitInRadius}() method. -- --- @module Unit --- @author FlightControl - - - - - ---- The UNIT class --- @type UNIT --- @extends Wrapper.Controllable#CONTROLLABLE +-- @field #UNIT UNIT UNIT = { ClassName="UNIT", } @@ -16903,7 +17400,7 @@ function UNIT:ReSpawn( SpawnVec3, Heading ) end -- Remove obscolete units from the group structure - i = 1 + local i = 1 while i <= #SpawnGroupTemplate.units do local UnitTemplateData = SpawnGroupTemplate.units[i] @@ -16939,6 +17436,27 @@ function UNIT:IsActive() return nil end +--- Returns if the Unit is alive. +-- If the Unit is not alive, nil is returned. +-- If the Unit is alive and active, true is returned. +-- If the Unit is alive but not active, false is returned. +-- @param #UNIT self +-- @return #boolean true if Unit is alive and active. +-- @return #boolean false if Unit is alive but not active. +-- @return #nil if the Unit is not existing or is not alive. +function UNIT:IsAlive() + self:F3( self.UnitName ) + + local DCSUnit = self:GetDCSObject() -- Dcs.DCSUnit#Unit + + if DCSUnit then + local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive() + return UnitIsAlive + end + + return nil +end + --- Returns the Unit's callsign - the localized string. @@ -17209,6 +17727,31 @@ function UNIT:GetLife0() return nil end +--- Returns the category name of the #UNIT. +-- @param #UNIT self +-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship +function UNIT:GetCategoryName() + self:F3( self.UnitName ) + + local DCSUnit = self:GetDCSObject() + if DCSUnit then + local CategoryNames = { + [Unit.Category.AIRPLANE] = "Airplane", + [Unit.Category.HELICOPTER] = "Helicopter", + [Unit.Category.GROUND_UNIT] = "Ground Unit", + [Unit.Category.SHIP] = "Ship", + [Unit.Category.STRUCTURE] = "Structure", + } + local UnitCategory = DCSUnit:getDesc().category + self:T3( UnitCategory ) + + return CategoryNames[UnitCategory] + end + + return nil +end + + --- Returns the Unit's A2G threat level on a scale from 1 to 10 ... -- The following threat levels are foreseen: -- @@ -17227,14 +17770,14 @@ end function UNIT:GetThreatLevel() local Attributes = self:GetDesc().attributes - self:E( Attributes ) + self:T( Attributes ) local ThreatLevel = 0 local ThreatText = "" if self:IsGround() then - self:E( "Ground" ) + self:T( "Ground" ) local ThreatLevels = { "Unarmed", @@ -17272,7 +17815,7 @@ function UNIT:GetThreatLevel() if self:IsAir() then - self:E( "Air" ) + self:T( "Air" ) local ThreatLevels = { "Unarmed", @@ -17306,7 +17849,7 @@ function UNIT:GetThreatLevel() if self:IsShip() then - self:E( "Ship" ) + self:T( "Ship" ) --["Aircraft Carriers"] = {"Heavy armed ships",}, --["Cruisers"] = {"Heavy armed ships",}, @@ -17709,7 +18252,7 @@ CLIENT = { -- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() ) -- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() ) -- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() ) -function CLIENT:Find( DCSUnit ) +function CLIENT:Find( DCSUnit, Error ) local ClientName = DCSUnit:getName() local ClientFound = _DATABASE:FindClient( ClientName ) @@ -17718,7 +18261,9 @@ function CLIENT:Find( DCSUnit ) return ClientFound end - error( "CLIENT not found for: " .. ClientName ) + if not Error then + error( "CLIENT not found for: " .. ClientName ) + end end @@ -18165,7 +18710,7 @@ function STATIC:FindByName( StaticName, RaiseError ) self.StaticName = StaticName if StaticFound then - StaticFound:F( { StaticName } ) + StaticFound:F3( { StaticName } ) return StaticFound end @@ -18352,8 +18897,6 @@ end -- -- === -- --- # 1) @{Scoring#SCORING} class, extends @{Base#BASE} --- -- The @{#SCORING} class administers the scoring of player achievements, -- and creates a CSV file logging the scoring events and results for use at team or squadron websites. -- @@ -18404,6 +18947,8 @@ end -- Use the radio menu F10 to consult the scores while running the mission. -- Scores can be reported for your user, or an overall score can be reported of all players currently active in the mission. -- +-- # 1) @{Scoring#SCORING} class, extends @{Base#BASE} +-- -- ## 1.1) Set the destroy score or penalty scale -- -- Score scales can be set for scores granted when enemies or friendlies are destroyed. @@ -18433,8 +18978,6 @@ end -- For example, this can be done as follows: -- -- Scoring:RemoveUnitScore( UNIT:FindByName( "Unit #001" ) ) --- --- -- -- ## 1.3) Define destruction zones that will give extra scores. -- @@ -18893,6 +19436,7 @@ function SCORING:_AddPlayerFromUnit( UnitData ) local UnitCategory = UnitDesc.category local UnitCoalition = UnitData:GetCoalition() local UnitTypeName = UnitData:GetTypeName() + local UnitThreatLevel, UnitThreatType = UnitData:GetThreatLevel() self:T( { PlayerName, UnitName, UnitCategory, UnitCoalition, UnitTypeName } ) @@ -18933,6 +19477,8 @@ function SCORING:_AddPlayerFromUnit( UnitData ) self.Players[PlayerName].UnitCategory = UnitCategory self.Players[PlayerName].UnitType = UnitTypeName self.Players[PlayerName].UNIT = UnitData + self.Players[PlayerName].ThreatLevel = UnitThreatLevel + self.Players[PlayerName].ThreatType = UnitThreatType if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 then if self.Players[PlayerName].PenaltyWarning < 1 then @@ -19191,6 +19737,7 @@ function SCORING:_EventOnHit( Event ) PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT + PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() -- Only grant hit scores if there was more than one second between the last hit. if timer.getTime() - PlayerHit.TimeStamp > 1 then @@ -19229,7 +19776,7 @@ function SCORING:_EventOnHit( Event ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) end - self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else Player.Score = Player.Score + 1 PlayerHit.Score = PlayerHit.Score + 1 @@ -19262,7 +19809,7 @@ function SCORING:_EventOnHit( Event ) ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - self:ScoreCSV( InitPlayerName, "", "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) + self:ScoreCSV( InitPlayerName, "", "HIT_SCORE", 1, 0, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) end end end @@ -19270,6 +19817,89 @@ function SCORING:_EventOnHit( Event ) elseif InitPlayerName == nil then -- It is an AI hitting a player??? end + + -- It is a weapon initiated by a player, that is hitting something + -- This seems to occur only with scenery and static objects. + if Event.WeaponPlayerName ~= nil then + self:_AddPlayerFromUnit( Event.WeaponUNIT ) + if self.Players[Event.WeaponPlayerName] then -- This should normally not happen, but i'll test it anyway. + if TargetPlayerName ~= nil then -- It is a player hitting another player ... + self:_AddPlayerFromUnit( TargetUNIT ) + end + + self:T( "Hitting Scenery" ) + + -- What is he hitting? + if TargetCategory then + + -- A scenery or static got hit, score it. + -- Player contains the score data from self.Players[WeaponPlayerName] + local Player = self.Players[Event.WeaponPlayerName] + + -- Ensure there is a hit table per TargetCategory and TargetUnitName. + Player.Hit[TargetCategory] = Player.Hit[TargetCategory] or {} + Player.Hit[TargetCategory][TargetUnitName] = Player.Hit[TargetCategory][TargetUnitName] or {} + + -- PlayerHit contains the score counters and data per unit that was hit. + local PlayerHit = Player.Hit[TargetCategory][TargetUnitName] + + PlayerHit.Score = PlayerHit.Score or 0 + PlayerHit.Penalty = PlayerHit.Penalty or 0 + PlayerHit.ScoreHit = PlayerHit.ScoreHit or 0 + PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 + PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 + PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT + PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() + + -- Only grant hit scores if there was more than one second between the last hit. + if timer.getTime() - PlayerHit.TimeStamp > 1 then + PlayerHit.TimeStamp = timer.getTime() + + local Score = 0 + + if InitCoalition then -- A coalition object was hit, probably a static. + if InitCoalition == TargetCoalition then + -- TODO: Penalty according scale + Player.Penalty = Player.Penalty + 10 + PlayerHit.Penalty = PlayerHit.Penalty + 10 + PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 + + MESSAGE + :New( "Player '" .. Event.WeaponPlayerName .. "' hit a friendly target " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. + "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + else + Player.Score = Player.Score + 1 + PlayerHit.Score = PlayerHit.Score + 1 + PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 + MESSAGE + :New( "Player '" .. Event.WeaponPlayerName .. "' hit an enemy target " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. + "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + end + else -- A scenery object was hit. + MESSAGE + :New( "Player '" .. Event.WeaponPlayerName .. "' hit a scenery object.", + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + self:ScoreCSV( Event.WeaponPlayerName, "", "HIT_SCORE", 1, 0, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, "", "Scenery", TargetUnitType ) + end + end + end + end + end end --- Track DEAD or CRASH events for the scoring. @@ -19326,8 +19956,13 @@ function SCORING:_EventOnDeadOrCrash( Event ) self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) + local Destroyed = false + -- What is the player destroying? - if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? + if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 then -- Was there a hit for this unit for this player before registered??? + + local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel + local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType Player.Destroy[TargetCategory] = Player.Destroy[TargetCategory] or {} Player.Destroy[TargetCategory][TargetType] = Player.Destroy[TargetCategory][TargetType] or {} @@ -19341,8 +19976,9 @@ function SCORING:_EventOnDeadOrCrash( Event ) if TargetCoalition then if InitCoalition == TargetCoalition then - local ThreatLevelTarget, ThreatTypeTarget = TargetUnit:GetThreatLevel() - local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 + local ThreatLevelTarget = TargetThreatLevel + local ThreatTypeTarget = TargetThreatType + local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1 local ThreatPenalty = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyPenalty / 10 ) self:E( { ThreatLevel = ThreatPenalty, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) @@ -19369,11 +20005,14 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) end + + Destroyed = true self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else - - local ThreatLevelTarget, ThreatTypeTarget = TargetUnit:GetThreatLevel() - local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 + + local ThreatLevelTarget = TargetThreatLevel + local ThreatTypeTarget = TargetThreatType + local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1 local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 ) self:E( { ThreatLevel = ThreatScore, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) @@ -19400,6 +20039,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) end + Destroyed = true self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, ThreatScore, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) local UnitName = TargetUnit:GetName() @@ -19415,6 +20055,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + Destroyed = true end -- Check if there are Zones where the destruction happened. @@ -19433,6 +20074,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + Destroyed = true end end @@ -19454,10 +20096,18 @@ function SCORING:_EventOnDeadOrCrash( Event ) ) :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) + Destroyed = true self:ScoreCSV( PlayerName, "", "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) end end end + + -- Delete now the hit cache if the target was destroyed. + -- Otherwise points will be granted every time a target gets killed by the players that hit that target. + -- This is only relevant for player to player destroys. + if Destroyed then + Player.Hit[TargetCategory][TargetUnitName].TimeStamp = 0 + end end end end @@ -20009,7 +20659,9 @@ CLEANUP = { -- or -- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 ) -- CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 ) -function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self, BASE:New() ) +function CLEANUP:New( ZoneNames, TimeInterval ) + + local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP self:F( { ZoneNames, TimeInterval } ) if type( ZoneNames ) == 'table' then @@ -20021,7 +20673,7 @@ function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self, self.TimeInterval = TimeInterval end - _EVENTDISPATCHER:OnBirth( self._OnEventBirth, self ) + self:HandleEvent( EVENTS.Birth ) self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval ) @@ -20082,32 +20734,24 @@ function CLEANUP:_DestroyMissile( MissileObject ) end end -function CLEANUP:_OnEventBirth( Event ) - self:F( { Event } ) +--- @param #CLEANUP self +-- @param Core.Event#EVENTDATA EventData +function CLEANUP:_OnEventBirth( EventData ) + self:F( { EventData } ) - self.CleanUpList[Event.IniDCSUnitName] = {} - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName - - _EVENTDISPATCHER:OnEngineShutDownForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) - _EVENTDISPATCHER:OnEngineStartUpForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) - _EVENTDISPATCHER:OnHitForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) - _EVENTDISPATCHER:OnPilotDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self ) - _EVENTDISPATCHER:OnDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self ) - _EVENTDISPATCHER:OnCrashForUnit( Event.IniDCSUnitName, self._EventCrash, self ) - _EVENTDISPATCHER:OnShotForUnit( Event.IniDCSUnitName, self._EventShot, self ) - - --self:AddEvent( world.event.S_EVENT_ENGINE_SHUTDOWN, self._EventAddForCleanUp ) - --self:AddEvent( world.event.S_EVENT_ENGINE_STARTUP, self._EventAddForCleanUp ) --- self:AddEvent( world.event.S_EVENT_HIT, self._EventAddForCleanUp ) -- , self._EventHitCleanUp ) --- self:AddEvent( world.event.S_EVENT_CRASH, self._EventCrash ) -- , self._EventHitCleanUp ) --- --self:AddEvent( world.event.S_EVENT_DEAD, self._EventCrash ) --- self:AddEvent( world.event.S_EVENT_SHOT, self._EventShot ) --- --- self:EnableEvents() + self.CleanUpList[EventData.IniDCSUnitName] = {} + self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniDCSUnit + self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniDCSGroup + self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName + self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName + EventData.IniUnit:HandleEvent( EVENTS.EngineShutdown , self._EventAddForCleanUp ) + EventData.IniUnit:HandleEvent( EVENTS.EngineStartup, self._EventAddForCleanUp ) + EventData.IniUnit:HandleEvent( EVENTS.Hit, self._EventAddForCleanUp ) + EventData.IniUnit:HandleEvent( EVENTS.PilotDead, self._EventCrash ) + EventData.IniUnit:HandleEvent( EVENTS.Dead, self._EventCrash ) + EventData.IniUnit:HandleEvent( EVENTS.Crash, self._EventCrash ) + EventData.IniUnit:HandleEvent( EVENTS.Shot, self._EventShot ) end @@ -20533,6 +21177,8 @@ function SPAWN:New( SpawnTemplatePrefix ) error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) end + self:SetEventPriority( 5 ) + return self end @@ -20574,6 +21220,8 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix ) error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) end + self:SetEventPriority( 5 ) + return self end @@ -20873,16 +21521,15 @@ function SPAWN:InitArray( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) self.SpawnGroups[SpawnGroupID].Visible = true - _EVENTDISPATCHER:OnBirthForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnBirth, self ) - _EVENTDISPATCHER:OnCrashForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnDeadOrCrash, self ) - _EVENTDISPATCHER:OnDeadForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnDeadOrCrash, self ) - + self:HandleEvent( EVENTS.Birth, self._OnBirth ) + self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) if self.Repeat then - _EVENTDISPATCHER:OnTakeOffForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnTakeOff, self ) - _EVENTDISPATCHER:OnLandForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnLand, self ) + self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff ) + self:HandleEvent( EVENTS.Land, self._OnLand ) end if self.RepeatOnEngineShutDown then - _EVENTDISPATCHER:OnEngineShutDownForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnEngineShutDown, self ) + self:HandleEvent( EVENTS.EngineShutdown, self._OnEngineShutDown ) end self.SpawnGroups[SpawnGroupID].Group = _DATABASE:Spawn( self.SpawnGroups[SpawnGroupID].SpawnTemplate ) @@ -21022,18 +21669,16 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) end end - _EVENTDISPATCHER:OnBirthForTemplate( SpawnTemplate, self._OnBirth, self ) - _EVENTDISPATCHER:OnCrashForTemplate( SpawnTemplate, self._OnDeadOrCrash, self ) - _EVENTDISPATCHER:OnDeadForTemplate( SpawnTemplate, self._OnDeadOrCrash, self ) - + self:HandleEvent( EVENTS.Birth, self._OnBirth ) + self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) if self.Repeat then - _EVENTDISPATCHER:OnTakeOffForTemplate( SpawnTemplate, self._OnTakeOff, self ) - _EVENTDISPATCHER:OnLandForTemplate( SpawnTemplate, self._OnLand, self ) + self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff ) + self:HandleEvent( EVENTS.Land, self._OnLand ) end if self.RepeatOnEngineShutDown then - _EVENTDISPATCHER:OnEngineShutDownForTemplate( SpawnTemplate, self._OnEngineShutDown, self ) + self:HandleEvent( EVENTS.EngineShutdown, self._OnEngineShutDown ) end - self:T3( SpawnTemplate.name ) self.SpawnGroups[self.SpawnIndex].Group = _DATABASE:Spawn( SpawnTemplate ) @@ -21044,6 +21689,8 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) SpawnGroup:SetAIOnOff( self.AIOnOff ) end + + self:T3( SpawnTemplate.name ) -- If there is a SpawnFunction hook defined, call it. if self.SpawnFunctionHook then @@ -21415,27 +22062,6 @@ function SPAWN:GetGroupFromIndex( SpawnIndex ) end end ---- Get the group index from a DCSUnit. --- The method will search for a #-mark, and will return the index behind the #-mark of the DCSUnit. --- It will return nil of no prefix was found. --- @param #SPAWN self --- @param Dcs.DCSWrapper.Unit#Unit DCSUnit The @{DCSUnit} to be searched. --- @return #string The prefix --- @return #nil Nothing found -function SPAWN:_GetGroupIndexFromDCSUnit( DCSUnit ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, DCSUnit } ) - - local SpawnUnitName = ( DCSUnit and DCSUnit:getName() ) or nil - if SpawnUnitName then - local IndexString = string.match( SpawnUnitName, "#.*-" ):sub( 2, -2 ) - if IndexString then - local Index = tonumber( IndexString ) - return Index - end - end - - return nil -end --- Return the prefix of a SpawnUnit. -- The method will search for a #-mark, and will return the text before the #-mark. @@ -21444,49 +22070,28 @@ end -- @param Dcs.DCSWrapper.Unit#UNIT DCSUnit The @{DCSUnit} to be searched. -- @return #string The prefix -- @return #nil Nothing found -function SPAWN:_GetPrefixFromDCSUnit( DCSUnit ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, DCSUnit } ) +function SPAWN:_GetPrefixFromGroup( SpawnGroup ) + self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) - local DCSGroup = DCSUnit:getGroup() - local DCSUnitName = ( DCSGroup and DCSGroup:getName() ) or nil - if DCSUnitName then - local SpawnPrefix = string.match( DCSUnitName, ".*#" ) - if SpawnPrefix then - SpawnPrefix = SpawnPrefix:sub( 1, -2 ) - end - return SpawnPrefix - end - - return nil -end - ---- Return the group within the SpawnGroups collection with input a DCSUnit. --- @param #SPAWN self --- @param Dcs.DCSWrapper.Unit#Unit DCSUnit The @{DCSUnit} to be searched. --- @return Wrapper.Group#GROUP The Group --- @return #nil Nothing found -function SPAWN:_GetGroupFromDCSUnit( DCSUnit ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, DCSUnit } ) - - local SpawnPrefix = self:_GetPrefixFromDCSUnit( DCSUnit ) - - if self.SpawnTemplatePrefix == SpawnPrefix or ( self.SpawnAliasPrefix and self.SpawnAliasPrefix == SpawnPrefix ) then - local SpawnGroupIndex = self:_GetGroupIndexFromDCSUnit( DCSUnit ) - local SpawnGroup = self.SpawnGroups[SpawnGroupIndex].Group - self:T( SpawnGroup ) - return SpawnGroup - end - - return nil + local GroupName = SpawnGroup:GetName() + if GroupName then + local SpawnPrefix = string.match( GroupName, ".*#" ) + if SpawnPrefix then + SpawnPrefix = SpawnPrefix:sub( 1, -2 ) + end + return SpawnPrefix + end + + return nil end --- Get the index from a given group. -- The function will search the name of the group for a #, and will return the number behind the #-mark. function SPAWN:GetSpawnIndexFromGroup( SpawnGroup ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) + self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) - local IndexString = string.match( SpawnGroup:GetName(), "#.*$" ):sub( 2 ) + local IndexString = string.match( SpawnGroup:GetName(), "#(%d*)$" ):sub( 2 ) local Index = tonumber( IndexString ) self:T3( IndexString, Index ) @@ -21811,17 +22416,18 @@ end -- TODO Need to delete this... _DATABASE does this now ... --- @param #SPAWN self --- @param Core.Event#EVENTDATA Event -function SPAWN:_OnBirth( Event ) +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnBirth( EventData ) + self:F( self.SpawnTemplatePrefix ) - if timer.getTime0() < timer.getAbsTime() then - if Event.IniDCSUnit then - local EventPrefix = self:_GetPrefixFromDCSUnit( Event.IniDCSUnit ) - self:T( { "Birth Event:", EventPrefix, self.SpawnTemplatePrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self.AliveUnits = self.AliveUnits + 1 - self:T( "Alive Units: " .. self.AliveUnits ) - end + local SpawnGroup = EventData.IniGroup + + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "Birth Event:", EventPrefix, self.SpawnTemplatePrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + self.AliveUnits = self.AliveUnits + 1 + self:T( "Alive Units: " .. self.AliveUnits ) end end @@ -21831,13 +22437,15 @@ end -- @todo Need to delete this... _DATABASE does this now ... --- @param #SPAWN self --- @param Core.Event#EVENTDATA Event -function SPAWN:_OnDeadOrCrash( Event ) - self:F( self.SpawnTemplatePrefix, Event ) +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnDeadOrCrash( EventData ) + self:F( self.SpawnTemplatePrefix ) - if Event.IniDCSUnit then - local EventPrefix = self:_GetPrefixFromDCSUnit( Event.IniDCSUnit ) - self:T( { "Dead event: " .. EventPrefix, self.SpawnTemplatePrefix } ) + local SpawnGroup = EventData.IniGroup + + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "Dead event: " .. EventPrefix } ) if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then self.AliveUnits = self.AliveUnits - 1 self:T( "Alive Units: " .. self.AliveUnits ) @@ -21847,34 +22455,37 @@ end --- Will detect AIR Units taking off... When the event takes place, the spawned Group is registered as airborne... -- This is needed to ensure that Re-SPAWNing only is done for landed AIR Groups. --- @todo Need to test for AIR Groups only... -function SPAWN:_OnTakeOff( event ) - self:F( self.SpawnTemplatePrefix, event ) +-- @param #SPAWN self +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnTakeOff( EventData ) + self:F( self.SpawnTemplatePrefix ) - if event.initiator and event.initiator:getName() then - local SpawnGroup = self:_GetGroupFromDCSUnit( event.initiator ) - if SpawnGroup then - self:T( { "TakeOff event: " .. event.initiator:getName(), event } ) - self:T( "self.Landed = false" ) - self.Landed = false - end + local SpawnGroup = EventData.IniGroup + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "TakeOff event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + self:T( "self.Landed = false" ) + SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", false ) + end end end --- Will detect AIR Units landing... When the event takes place, the spawned Group is registered as landed. -- This is needed to ensure that Re-SPAWNing is only done for landed AIR Groups. --- @todo Need to test for AIR Groups only... -function SPAWN:_OnLand( event ) - self:F( self.SpawnTemplatePrefix, event ) +-- @param #SPAWN self +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnLand( EventData ) + self:F( self.SpawnTemplatePrefix ) - local SpawnUnit = event.initiator - if SpawnUnit and SpawnUnit:isExist() and Object.getCategory(SpawnUnit) == Object.Category.UNIT then - local SpawnGroup = self:_GetGroupFromDCSUnit( SpawnUnit ) - if SpawnGroup then - self:T( { "Landed event:" .. SpawnUnit:getName(), event } ) - self.Landed = true - self:T( "self.Landed = true" ) - if self.Landed and self.RepeatOnLanding then + local SpawnGroup = EventData.IniGroup + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "Land event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + -- TODO: Check if this is the last unit of the group that lands. + SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", true ) + if self.RepeatOnLanding then local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) self:T( { "Landed:", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) self:ReSpawn( SpawnGroupIndex ) @@ -21887,18 +22498,18 @@ end -- When the event takes place, and the method @{RepeatOnEngineShutDown} was called, the spawned Group will Re-SPAWN. -- But only when the Unit was registered to have landed. -- @param #SPAWN self --- @see _OnTakeOff --- @see _OnLand --- @todo Need to test for AIR Groups only... -function SPAWN:_OnEngineShutDown( event ) - self:F( self.SpawnTemplatePrefix, event ) +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnEngineShutDown( EventData ) + self:F( self.SpawnTemplatePrefix ) - local SpawnUnit = event.initiator - if SpawnUnit and SpawnUnit:isExist() and Object.getCategory(SpawnUnit) == Object.Category.UNIT then - local SpawnGroup = self:_GetGroupFromDCSUnit( SpawnUnit ) - if SpawnGroup then - self:T( { "EngineShutDown event: " .. SpawnUnit:getName(), event } ) - if self.Landed and self.RepeatOnEngineShutDown then + local SpawnGroup = EventData.IniGroup + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "EngineShutdown event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + -- todo: test if on the runway + local Landed = SpawnGroup:GetState( SpawnGroup, "Spawn_Landed" ) + if Landed and self.RepeatOnEngineShutDown then local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) self:T( { "EngineShutDown: ", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) self:ReSpawn( SpawnGroupIndex ) @@ -21987,10 +22598,11 @@ end -- Performance: If in a DCSRTE there are a lot of moving GROUND units, then in a multi player mission, this WILL create lag if -- the main DCS execution core of your CPU is fully utilized. So, this class will limit the amount of simultaneous moving GROUND units -- on defined intervals (currently every minute). --- @module MOVEMENT +-- @module Movement --- the MOVEMENT class --- @type +-- @type MOVEMENT +-- @extends Core.Base#BASE MOVEMENT = { ClassName = "MOVEMENT", } @@ -22004,7 +22616,7 @@ MOVEMENT = { -- Movement_US_Platoons = MOVEMENT:New( { 'US Tank Platoon Left', 'US Tank Platoon Middle', 'US Tank Platoon Right', 'US CH-47D Troops' }, 15 ) function MOVEMENT:New( MovePrefixes, MoveMaximum ) - local self = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, BASE:New() ) -- #MOVEMENT self:F( { MovePrefixes, MoveMaximum } ) if type( MovePrefixes ) == 'table' then @@ -22017,7 +22629,7 @@ function MOVEMENT:New( MovePrefixes, MoveMaximum ) self.AliveUnits = 0 -- Contains the counter how many units are currently alive self.MoveUnits = {} -- Reflects if the Moving for this MovePrefixes is going to be scheduled or not. - _EVENTDISPATCHER:OnBirth( self.OnBirth, self ) + self:HandleEvent( EVENTS.Birth ) -- self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth ) -- @@ -22044,24 +22656,26 @@ end --- Captures the birth events when new Units were spawned. -- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. -function MOVEMENT:OnBirth( Event ) - self:F( { Event } ) +-- @param #MOVEMENT self +-- @param Core.Event#EVENTDATA self +function MOVEMENT:OnEventBirth( EventData ) + self:F( { EventData } ) if timer.getTime0() < timer.getAbsTime() then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line - if Event.IniDCSUnit then - self:T( "Birth object : " .. Event.IniDCSUnitName ) - if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then + if EventData.IniDCSUnit then + self:T( "Birth object : " .. EventData.IniDCSUnitName ) + if EventData.IniDCSGroup and EventData.IniDCSGroup:isExist() then for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do - if string.find( Event.IniDCSUnitName, MovePrefix, 1, true ) then + if string.find( EventData.IniDCSUnitName, MovePrefix, 1, true ) then self.AliveUnits = self.AliveUnits + 1 - self.MoveUnits[Event.IniDCSUnitName] = Event.IniDCSGroupName + self.MoveUnits[EventData.IniDCSUnitName] = EventData.IniDCSGroupName self:T( self.AliveUnits ) end end end end - _EVENTDISPATCHER:OnCrashForUnit( Event.IniDCSUnitName, self.OnDeadOrCrash, self ) - _EVENTDISPATCHER:OnDeadForUnit( Event.IniDCSUnitName, self.OnDeadOrCrash, self ) + + EventData.IniUnit:HandleEvent( EVENTS.DEAD, self.OnDeadOrCrash ) end end @@ -22148,25 +22762,28 @@ function SEAD:New( SEADGroupPrefixes ) else self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes end - _EVENTDISPATCHER:OnShot( self.EventShot, self ) + + self:HandleEvent( EVENTS.Shot ) return self end --- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. -- @see SEAD -function SEAD:EventShot( Event ) - self:F( { Event } ) +-- @param #SEAD +-- @param Core.Event#EVENTDATA EventData +function SEAD:OnEventShot( EventData ) + self:F( { EventData } ) - local SEADUnit = Event.IniDCSUnit - local SEADUnitName = Event.IniDCSUnitName - local SEADWeapon = Event.Weapon -- Identify the weapon fired - local SEADWeaponName = Event.WeaponName -- return weapon type + local SEADUnit = EventData.IniDCSUnit + local SEADUnitName = EventData.IniDCSUnitName + local SEADWeapon = EventData.Weapon -- Identify the weapon fired + local SEADWeaponName = EventData.WeaponName -- return weapon type -- Start of the 2nd loop self:T( "Missile Launched = " .. SEADWeaponName ) if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD local _evade = math.random (1,100) -- random number for chance of evading action - local _targetMim = Event.Weapon:getTarget() -- Identify target + local _targetMim = EventData.Weapon:getTarget() -- Identify target local _targetMimname = Unit.getName(_targetMim) local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) local _targetMimgroupName = _targetMimgroup:getName() @@ -22325,7 +22942,7 @@ end -- -- ESCORT initialization methods. -- ============================== --- The following menus are created within the RADIO MENU of an active unit hosted by a player: +-- The following menus are created within the RADIO MENU (F10) of an active unit hosted by a player: -- -- * @{#ESCORT.MenuFollowAt}: Creates a menu to make the escort follow the client. -- * @{#ESCORT.MenuHoldAtEscortPosition}: Creates a menu to hold the escort at its current position. @@ -22369,6 +22986,7 @@ end -- @Field Dcs.DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the EscortGroup. -- @field Dcs.DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the EscortGroup. -- @field Core.Menu#MENU_CLIENT EscortMenuResumeMission +-- @field Functional.Detection#DETECTION_BASE Detection ESCORT = { ClassName = "ESCORT", EscortName = nil, -- The Escort Name @@ -22417,14 +23035,22 @@ ESCORT = { -- -- Now use these 2 objects to construct the new EscortPlanes object. -- EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." ) function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) - local self = BASE:Inherit( self, BASE:New() ) + + local self = BASE:Inherit( self, BASE:New() ) -- #ESCORT self:F( { EscortClient, EscortGroup, EscortName } ) self.EscortClient = EscortClient -- Wrapper.Client#CLIENT self.EscortGroup = EscortGroup -- Wrapper.Group#GROUP self.EscortName = EscortName self.EscortBriefing = EscortBriefing - + + self.EscortSetGroup = SET_GROUP:New() + self.EscortSetGroup:AddObject( self.EscortGroup ) + self.EscortSetGroup:Flush() + self.Detection = DETECTION_UNITS:New( self.EscortSetGroup, 15000 ) + + self.EscortGroup.Detection = self.Detection + -- Set EscortGroup known at EscortClient. if not self.EscortClient._EscortGroups then self.EscortClient._EscortGroups = {} @@ -22434,7 +23060,7 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) self.EscortClient._EscortGroups[EscortGroup:GetName()] = {} self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup = self.EscortGroup self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName = self.EscortName - self.EscortClient._EscortGroups[EscortGroup:GetName()].Targets = {} + self.EscortClient._EscortGroups[EscortGroup:GetName()].Detection = self.EscortGroup.Detection end self.EscortMenu = MENU_CLIENT:New( self.EscortClient, self.EscortName ) @@ -22459,13 +23085,30 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) self.FollowDistance = 100 self.CT1 = 0 self.GT1 = 0 - self.FollowScheduler = SCHEDULER:New( self, self._FollowScheduler, {}, 1, .5, .01 ) - self.EscortMode = ESCORT.MODE.MISSION - self.FollowScheduler:Stop() + self.FollowScheduler, self.FollowSchedule = SCHEDULER:New( self, self._FollowScheduler, {}, 1, .5, .01 ) + self.FollowScheduler:Stop( self.FollowSchedule ) + + self.EscortMode = ESCORT.MODE.MISSION + + return self end +--- Set a Detection method for the EscortClient to be reported upon. +-- Detection methods are based on the derived classes from DETECTION_BASE. +-- @param #ESCORT self +-- @param Function.Detection#DETECTION_BASE Detection +function ESCORT:SetDetection( Detection ) + + self.Detection = Detection + self.EscortGroup.Detection = self.Detection + self.EscortClient._EscortGroups[self.EscortGroup:GetName()].Detection = self.EscortGroup.Detection + + Detection:__Start( 1 ) + +end + --- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to. -- This allows to visualize where the escort is flying to. -- @param #ESCORT self @@ -22523,7 +23166,7 @@ function ESCORT:MenuFollowAt( Distance ) self.EscortMenuJoinUpAndFollow = {} end - self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, { ParamSelf = self, ParamDistance = Distance } ) + self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, self, Distance ) self.EscortMode = ESCORT.MODE.FOLLOW end @@ -22581,11 +23224,10 @@ function ESCORT:MenuHoldAtEscortPosition( Height, Seconds, MenuTextFormat ) MenuText, self.EscortMenuHold, ESCORT._HoldPosition, - { ParamSelf = self, - ParamOrbitGroup = self.EscortGroup, - ParamHeight = Height, - ParamSeconds = Seconds - } + self, + self.EscortGroup, + Height, + Seconds ) end @@ -22702,9 +23344,8 @@ function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat ) MenuText, self.EscortMenuScan, ESCORT._ScanTargets, - { ParamSelf = self, - ParamScanDuration = 30 - } + self, + 30 ) end @@ -22734,11 +23375,11 @@ function ESCORT:MenuFlare( MenuTextFormat ) end if not self.EscortMenuFlare then - self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, { ParamSelf = self } ) - self.EscortMenuFlareGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Green, ParamMessage = "Released a green flare!" } ) - self.EscortMenuFlareRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Red, ParamMessage = "Released a red flare!" } ) - self.EscortMenuFlareWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.White, ParamMessage = "Released a white flare!" } ) - self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Yellow, ParamMessage = "Released a yellow flare!" } ) + self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, self ) + self.EscortMenuFlareGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Green, "Released a green flare!" ) + self.EscortMenuFlareRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Red, "Released a red flare!" ) + self.EscortMenuFlareWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.White, "Released a white flare!" ) + self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Yellow, "Released a yellow flare!" ) end return self @@ -22767,12 +23408,12 @@ function ESCORT:MenuSmoke( MenuTextFormat ) end if not self.EscortMenuSmoke then - self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, { ParamSelf = self } ) - self.EscortMenuSmokeGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Green, ParamMessage = "Releasing green smoke!" } ) - self.EscortMenuSmokeRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Red, ParamMessage = "Releasing red smoke!" } ) - self.EscortMenuSmokeWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.White, ParamMessage = "Releasing white smoke!" } ) - self.EscortMenuSmokeOrange = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Orange, ParamMessage = "Releasing orange smoke!" } ) - self.EscortMenuSmokeBlue = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Blue, ParamMessage = "Releasing blue smoke!" } ) + self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, self ) + self.EscortMenuSmokeGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Green, "Releasing green smoke!" ) + self.EscortMenuSmokeRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Red, "Releasing red smoke!" ) + self.EscortMenuSmokeWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.White, "Releasing white smoke!" ) + self.EscortMenuSmokeOrange = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Orange, "Releasing orange smoke!" ) + self.EscortMenuSmokeBlue = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Blue, "Releasing blue smoke!" ) end end @@ -22797,9 +23438,9 @@ function ESCORT:MenuReportTargets( Seconds ) end -- Report Targets - self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, { ParamSelf = self } ) - self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = true } ) - self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = false, } ) + self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, self ) + self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, true ) + self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, false ) -- Attack Targets self.EscortMenuAttackNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Attack targets", self.EscortMenu ) @@ -22836,16 +23477,16 @@ function ESCORT:MenuROE( MenuTextFormat ) -- Rules of Engagement self.EscortMenuROE = MENU_CLIENT:New( self.EscortClient, "ROE", self.EscortMenu ) if self.EscortGroup:OptionROEHoldFirePossible() then - self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEHoldFire(), ParamMessage = "Holding weapons!" } ) + self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEHoldFire(), "Holding weapons!" ) end if self.EscortGroup:OptionROEReturnFirePossible() then - self.EscortMenuROEReturnFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Return Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEReturnFire(), ParamMessage = "Returning fire!" } ) + self.EscortMenuROEReturnFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Return Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEReturnFire(), "Returning fire!" ) end if self.EscortGroup:OptionROEOpenFirePossible() then - self.EscortMenuROEOpenFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Open Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEOpenFire(), ParamMessage = "Opening fire on designated targets!!" } ) + self.EscortMenuROEOpenFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Open Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEOpenFire(), "Opening fire on designated targets!!" ) end if self.EscortGroup:OptionROEWeaponFreePossible() then - self.EscortMenuROEWeaponFree = MENU_CLIENT_COMMAND:New( self.EscortClient, "Weapon Free", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEWeaponFree(), ParamMessage = "Opening fire on targets of opportunity!" } ) + self.EscortMenuROEWeaponFree = MENU_CLIENT_COMMAND:New( self.EscortClient, "Weapon Free", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEWeaponFree(), "Opening fire on targets of opportunity!" ) end end @@ -22865,16 +23506,16 @@ function ESCORT:MenuEvasion( MenuTextFormat ) -- Reaction to Threats self.EscortMenuEvasion = MENU_CLIENT:New( self.EscortClient, "Evasion", self.EscortMenu ) if self.EscortGroup:OptionROTNoReactionPossible() then - self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTNoReaction(), ParamMessage = "Fighting until death!" } ) + self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTNoReaction(), "Fighting until death!" ) end if self.EscortGroup:OptionROTPassiveDefensePossible() then - self.EscortMenuEvasionPassiveDefense = MENU_CLIENT_COMMAND:New( self.EscortClient, "Use flares, chaff and jammers", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTPassiveDefense(), ParamMessage = "Defending using jammers, chaff and flares!" } ) + self.EscortMenuEvasionPassiveDefense = MENU_CLIENT_COMMAND:New( self.EscortClient, "Use flares, chaff and jammers", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTPassiveDefense(), "Defending using jammers, chaff and flares!" ) end if self.EscortGroup:OptionROTEvadeFirePossible() then - self.EscortMenuEvasionEvadeFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Evade enemy fire", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTEvadeFire(), ParamMessage = "Evading on enemy fire!" } ) + self.EscortMenuEvasionEvadeFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Evade enemy fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTEvadeFire(), "Evading on enemy fire!" ) end if self.EscortGroup:OptionROTVerticalPossible() then - self.EscortMenuOptionEvasionVertical = MENU_CLIENT_COMMAND:New( self.EscortClient, "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTVertical(), ParamMessage = "Evading on enemy fire with vertical manoeuvres!" } ) + self.EscortMenuOptionEvasionVertical = MENU_CLIENT_COMMAND:New( self.EscortClient, "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTVertical(), "Evading on enemy fire with vertical manoeuvres!" ) end end end @@ -22899,18 +23540,14 @@ end --- @param #MENUPARAM MenuParam -function ESCORT._HoldPosition( MenuParam ) +function ESCORT:_HoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local OrbitGroup = MenuParam.ParamOrbitGroup -- Wrapper.Group#GROUP local OrbitUnit = OrbitGroup:GetUnit(1) -- Wrapper.Unit#UNIT - local OrbitHeight = MenuParam.ParamHeight - local OrbitSeconds = MenuParam.ParamSeconds -- Not implemented yet - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) local PointFrom = {} local GroupVec3 = EscortGroup:GetUnit(1):GetVec3() @@ -22943,13 +23580,12 @@ function ESCORT._HoldPosition( MenuParam ) end --- @param #MENUPARAM MenuParam -function ESCORT._JoinUpAndFollow( MenuParam ) +function ESCORT:_JoinUpAndFollow( Distance ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - self.Distance = MenuParam.ParamDistance + self.Distance = Distance self:JoinUpAndFollow( EscortGroup, EscortClient, self.Distance ) end @@ -22962,7 +23598,7 @@ end function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) self:F( { EscortGroup, EscortClient, Distance } ) - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) EscortGroup:OptionROEHoldFire() EscortGroup:OptionROTPassiveDefense() @@ -22971,44 +23607,35 @@ function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) self.CT1 = 0 self.GT1 = 0 - self.FollowScheduler:Start() + self.FollowScheduler:Start( self.FollowSchedule ) EscortGroup:MessageToClient( "Rejoining and Following at " .. Distance .. "!", 30, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._Flare( MenuParam ) +function ESCORT:_Flare( Color, Message ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local Color = MenuParam.ParamColor - local Message = MenuParam.ParamMessage - EscortGroup:GetUnit(1):Flare( Color ) EscortGroup:MessageToClient( Message, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._Smoke( MenuParam ) +function ESCORT:_Smoke( Color, Message ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local Color = MenuParam.ParamColor - local Message = MenuParam.ParamMessage - EscortGroup:GetUnit(1):Smoke( Color ) EscortGroup:MessageToClient( Message, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ReportNearbyTargetsNow( MenuParam ) +function ESCORT:_ReportNearbyTargetsNow() - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient @@ -23016,17 +23643,16 @@ function ESCORT._ReportNearbyTargetsNow( MenuParam ) end -function ESCORT._SwitchReportNearbyTargets( MenuParam ) +function ESCORT:_SwitchReportNearbyTargets( ReportTargets ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - self.ReportTargets = MenuParam.ParamReportTargets + self.ReportTargets = ReportTargets if self.ReportTargets then if not self.ReportTargetsScheduler then - self.ReportTargetsScheduler = SCHEDULER:New( self, self._ReportTargetsScheduler, {}, 1, 30 ) + self.ReportTargetsScheduler:Schedule( self, self._ReportTargetsScheduler, {}, 1, 30 ) end else routines.removeFunction( self.ReportTargetsScheduler ) @@ -23035,40 +23661,31 @@ function ESCORT._SwitchReportNearbyTargets( MenuParam ) end --- @param #MENUPARAM MenuParam -function ESCORT._ScanTargets( MenuParam ) +function ESCORT:_ScanTargets( ScanDuration ) - local self = MenuParam.ParamSelf - local EscortGroup = self.EscortGroup + local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP local EscortClient = self.EscortClient - local ScanDuration = MenuParam.ParamScanDuration - - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroup:IsHelicopter() then - SCHEDULER:New( EscortGroup, EscortGroup.PushTask, - { EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 200, 20 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ) - }, - 1 - ) + EscortGroup:PushTask( + EscortGroup:TaskControlled( + EscortGroup:TaskOrbitCircle( 200, 20 ), + EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) + ), 1 ) elseif EscortGroup:IsAirPlane() then - SCHEDULER:New( EscortGroup, EscortGroup.PushTask, - { EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 1000, 500 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ) - }, - 1 - ) + EscortGroup:PushTask( + EscortGroup:TaskControlled( + EscortGroup:TaskOrbitCircle( 1000, 500 ), + EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) + ), 1 ) end EscortGroup:MessageToClient( "Scanning targets for " .. ScanDuration .. " seconds.", ScanDuration, EscortClient ) if self.EscortMode == ESCORT.MODE.FOLLOW then - self.FollowScheduler:Start() + self.FollowScheduler:Start( self.FollowSchedule ) end end @@ -23085,124 +23702,157 @@ function _Resume( EscortGroup ) end ---- @param #MENUPARAM MenuParam -function ESCORT._AttackTarget( MenuParam ) +--- @param #ESCORT self +-- @param #number DetectedItemID +function ESCORT:_AttackTarget( DetectedItemID ) - local self = MenuParam.ParamSelf - local EscortGroup = self.EscortGroup + local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP + self:E( EscortGroup ) local EscortClient = self.EscortClient - local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT - self.FollowScheduler:Stop() - - self:T( AttackUnit ) + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroup:IsAir() then EscortGroup:OptionROEOpenFire() EscortGroup:OptionROTPassiveDefense() EscortGroup:SetState( EscortGroup, "Escort", self ) - SCHEDULER:New( EscortGroup, - EscortGroup.PushTask, - { EscortGroup:TaskCombo( - { EscortGroup:TaskAttackUnit( AttackUnit ), - EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit, Tasks ) + if DetectedUnit:IsAlive() then + Tasks[#Tasks+1] = EscortGroup:TaskAttackUnit( DetectedUnit ) + end + end, Tasks + ) + + Tasks[#Tasks+1] = EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } ) + + EscortGroup:SetTask( + EscortGroup:TaskCombo( + Tasks + ), 1 ) + else - SCHEDULER:New( EscortGroup, - EscortGroup.PushTask, - { EscortGroup:TaskCombo( - { EscortGroup:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit, Tasks ) + if DetectedUnit:IsAlive() then + Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) + end + end, Tasks + ) + + EscortGroup:SetTask( + EscortGroup:TaskCombo( + Tasks + ), 1 ) + end EscortGroup:MessageToClient( "Engaging Designated Unit!", 10, EscortClient ) end ---- @param #MENUPARAM MenuParam -function ESCORT._AssistTarget( MenuParam ) +--- +-- @param #number DetectedItemID +function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItemID ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortGroupAttack = MenuParam.ParamEscortGroup - local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT - self.FollowScheduler:Stop() - - self:T( AttackUnit ) + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroupAttack:IsAir() then EscortGroupAttack:OptionROEOpenFire() EscortGroupAttack:OptionROTVertical() - SCHDULER:New( EscortGroupAttack, - EscortGroupAttack.PushTask, - { EscortGroupAttack:TaskCombo( - { EscortGroupAttack:TaskAttackUnit( AttackUnit ), - EscortGroupAttack:TaskOrbitCircle( 500, 350 ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit, Tasks ) + if DetectedUnit:IsAlive() then + Tasks[#Tasks+1] = EscortGroupAttack:TaskAttackUnit( DetectedUnit ) + end + end, Tasks + ) + + Tasks[#Tasks+1] = EscortGroupAttack:TaskOrbitCircle( 500, 350 ) + + EscortGroupAttack:SetTask( + EscortGroupAttack:TaskCombo( + Tasks + ), 1 ) + else - SCHEDULER:New( EscortGroupAttack, - EscortGroupAttack.PushTask, - { EscortGroupAttack:TaskCombo( - { EscortGroupAttack:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) - } - ) - }, 10 + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit, Tasks ) + if DetectedUnit:IsAlive() then + Tasks[#Tasks+1] = EscortGroupAttack:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) + end + end, Tasks + ) + + EscortGroupAttack:SetTask( + EscortGroupAttack:TaskCombo( + Tasks + ), 1 ) + end + EscortGroupAttack:MessageToClient( "Assisting with the destroying the enemy unit!", 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ROE( MenuParam ) +function ESCORT:_ROE( EscortROEFunction, EscortROEMessage ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortROEFunction = MenuParam.ParamFunction - local EscortROEMessage = MenuParam.ParamMessage - pcall( function() EscortROEFunction() end ) EscortGroup:MessageToClient( EscortROEMessage, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ROT( MenuParam ) +function ESCORT:_ROT( EscortROTFunction, EscortROTMessage ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortROTFunction = MenuParam.ParamFunction - local EscortROTMessage = MenuParam.ParamMessage - pcall( function() EscortROTFunction() end ) EscortGroup:MessageToClient( EscortROTMessage, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ResumeMission( MenuParam ) +function ESCORT:_ResumeMission( WayPoint ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local WayPoint = MenuParam.ParamWayPoint - - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) local WayPoints = EscortGroup:GetTaskRoute() self:T( WayPoint, WayPoints ) @@ -23346,176 +23996,244 @@ function ESCORT:_ReportTargetsScheduler() self:F( self.EscortGroup:GetName() ) if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then - local EscortGroupName = self.EscortGroup:GetName() - local EscortTargets = self.EscortGroup:GetDetectedTargets() - local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets + if true then - local EscortTargetMessages = "" - for EscortTargetID, EscortTarget in pairs( EscortTargets ) do - local EscortObject = EscortTarget.object - self:T( EscortObject ) - if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then + local EscortGroupName = self.EscortGroup:GetName() + + self.EscortMenuAttackNearbyTargets:RemoveSubMenus() - local EscortTargetUnit = UNIT:Find( EscortObject ) - local EscortTargetUnitName = EscortTargetUnit:GetName() - - - - -- local EscortTargetIsDetected, - -- EscortTargetIsVisible, - -- EscortTargetLastTime, - -- EscortTargetKnowType, - -- EscortTargetKnowDistance, - -- EscortTargetLastPos, - -- EscortTargetLastVelocity - -- = self.EscortGroup:IsTargetDetected( EscortObject ) - -- - -- self:T( { EscortTargetIsDetected, - -- EscortTargetIsVisible, - -- EscortTargetLastTime, - -- EscortTargetKnowType, - -- EscortTargetKnowDistance, - -- EscortTargetLastPos, - -- EscortTargetLastVelocity } ) - - - local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3() - local EscortVec3 = self.EscortGroup:GetVec3() - local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + - ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + - ( EscortTargetUnitVec3.z - EscortVec3.z )^2 - ) ^ 0.5 / 1000 - - self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } ) - - if Distance <= 15 then - - if not ClientEscortTargets[EscortTargetUnitName] then - ClientEscortTargets[EscortTargetUnitName] = {} - end - ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit - ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible - ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type - ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance - else - if ClientEscortTargets[EscortTargetUnitName] then - ClientEscortTargets[EscortTargetUnitName] = nil - end - end + if self.EscortMenuTargetAssistance then + self.EscortMenuTargetAssistance:RemoveSubMenus() end - end - self:T( { "Sorting Targets Table:", ClientEscortTargets } ) - table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end ) - self:T( { "Sorted Targets Table:", ClientEscortTargets } ) + local DetectedItems = self.Detection:GetDetectedItems() + self:E( DetectedItems ) - -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. - self.EscortMenuAttackNearbyTargets:RemoveSubMenus() + local DetectedTargets = false + + local DetectedMsgs = {} + + for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do - if self.EscortMenuTargetAssistance then - self.EscortMenuTargetAssistance:RemoveSubMenus() - end + local ClientEscortTargets = EscortGroupData.Detection - --for MenuIndex = 1, #self.EscortMenuAttackTargets do - -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } ) - -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove() - --end + for DetectedItemID, DetectedItem in ipairs( DetectedItems ) do + self:E( { DetectedItemID, DetectedItem } ) + -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. + + local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID ) - - if ClientEscortTargets then - for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do - - for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do - - if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then - - local EscortTargetMessage = "" - local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName() - local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName() - if ClientEscortTargetData.type then - EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at " - else - EscortTargetMessage = EscortTargetMessage .. "Unknown target at " - end - - local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3() - local EscortVec3 = self.EscortGroup:GetVec3() - local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + - ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + - ( EscortTargetUnitVec3.z - EscortVec3.z )^2 - ) ^ 0.5 / 1000 - - self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } ) - if ClientEscortTargetData.visible == false then - EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km" - else - EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km" - end - - if ClientEscortTargetData.visible then - EscortTargetMessage = EscortTargetMessage .. ", visual" - end - - if ClientEscortGroupName == EscortGroupName then - - MENU_CLIENT_COMMAND:New( self.EscortClient, - EscortTargetMessage, - self.EscortMenuAttackNearbyTargets, - ESCORT._AttackTarget, - { ParamSelf = self, - ParamUnit = ClientEscortTargetData.AttackUnit - } - ) - EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage - else - if self.EscortMenuTargetAssistance then - local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) - MENU_CLIENT_COMMAND:New( self.EscortClient, - EscortTargetMessage, - MenuTargetAssistance, - ESCORT._AssistTarget, - { ParamSelf = self, - ParamEscortGroup = EscortGroupData.EscortGroup, - ParamUnit = ClientEscortTargetData.AttackUnit - } - ) - end - end + if ClientEscortGroupName == EscortGroupName then + + DetectedMsgs[#DetectedMsgs+1] = DetectedItemReportSummary + + MENU_CLIENT_COMMAND:New( self.EscortClient, + DetectedItemReportSummary, + self.EscortMenuAttackNearbyTargets, + ESCORT._AttackTarget, + self, + DetectedItemID + ) else - ClientEscortTargetData = nil + if self.EscortMenuTargetAssistance then + + self:T( DetectedItemReportSummary ) + local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) + MENU_CLIENT_COMMAND:New( self.EscortClient, + DetectedItemReportSummary, + MenuTargetAssistance, + ESCORT._AssistTarget, + self, + EscortGroupData.EscortGroup, + DetectedItemID + ) + end end + + DetectedTargets = true + end end - - if EscortTargetMessages ~= "" and self.ReportTargets == true then - self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient ) + self:E( DetectedMsgs ) + if DetectedTargets then + self.EscortGroup:MessageToClient( "Detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient ) else - self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient ) + self.EscortGroup:MessageToClient( "No targets detected.", 10, self.EscortClient ) end + + return true + else +-- local EscortGroupName = self.EscortGroup:GetName() +-- local EscortTargets = self.EscortGroup:GetDetectedTargets() +-- +-- local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets +-- +-- local EscortTargetMessages = "" +-- for EscortTargetID, EscortTarget in pairs( EscortTargets ) do +-- local EscortObject = EscortTarget.object +-- self:T( EscortObject ) +-- if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then +-- +-- local EscortTargetUnit = UNIT:Find( EscortObject ) +-- local EscortTargetUnitName = EscortTargetUnit:GetName() +-- +-- +-- +-- -- local EscortTargetIsDetected, +-- -- EscortTargetIsVisible, +-- -- EscortTargetLastTime, +-- -- EscortTargetKnowType, +-- -- EscortTargetKnowDistance, +-- -- EscortTargetLastPos, +-- -- EscortTargetLastVelocity +-- -- = self.EscortGroup:IsTargetDetected( EscortObject ) +-- -- +-- -- self:T( { EscortTargetIsDetected, +-- -- EscortTargetIsVisible, +-- -- EscortTargetLastTime, +-- -- EscortTargetKnowType, +-- -- EscortTargetKnowDistance, +-- -- EscortTargetLastPos, +-- -- EscortTargetLastVelocity } ) +-- +-- +-- local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3() +-- local EscortVec3 = self.EscortGroup:GetVec3() +-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + +-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + +-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2 +-- ) ^ 0.5 / 1000 +-- +-- self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } ) +-- +-- if Distance <= 15 then +-- +-- if not ClientEscortTargets[EscortTargetUnitName] then +-- ClientEscortTargets[EscortTargetUnitName] = {} +-- end +-- ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit +-- ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible +-- ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type +-- ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance +-- else +-- if ClientEscortTargets[EscortTargetUnitName] then +-- ClientEscortTargets[EscortTargetUnitName] = nil +-- end +-- end +-- end +-- end +-- +-- self:T( { "Sorting Targets Table:", ClientEscortTargets } ) +-- table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end ) +-- self:T( { "Sorted Targets Table:", ClientEscortTargets } ) +-- +-- -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. +-- self.EscortMenuAttackNearbyTargets:RemoveSubMenus() +-- +-- if self.EscortMenuTargetAssistance then +-- self.EscortMenuTargetAssistance:RemoveSubMenus() +-- end +-- +-- --for MenuIndex = 1, #self.EscortMenuAttackTargets do +-- -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } ) +-- -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove() +-- --end +-- +-- +-- if ClientEscortTargets then +-- for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do +-- +-- for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do +-- +-- if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then +-- +-- local EscortTargetMessage = "" +-- local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName() +-- local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName() +-- if ClientEscortTargetData.type then +-- EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at " +-- else +-- EscortTargetMessage = EscortTargetMessage .. "Unknown target at " +-- end +-- +-- local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3() +-- local EscortVec3 = self.EscortGroup:GetVec3() +-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + +-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + +-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2 +-- ) ^ 0.5 / 1000 +-- +-- self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } ) +-- if ClientEscortTargetData.visible == false then +-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km" +-- else +-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km" +-- end +-- +-- if ClientEscortTargetData.visible then +-- EscortTargetMessage = EscortTargetMessage .. ", visual" +-- end +-- +-- if ClientEscortGroupName == EscortGroupName then +-- +-- MENU_CLIENT_COMMAND:New( self.EscortClient, +-- EscortTargetMessage, +-- self.EscortMenuAttackNearbyTargets, +-- ESCORT._AttackTarget, +-- { ParamSelf = self, +-- ParamUnit = ClientEscortTargetData.AttackUnit +-- } +-- ) +-- EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage +-- else +-- if self.EscortMenuTargetAssistance then +-- local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) +-- MENU_CLIENT_COMMAND:New( self.EscortClient, +-- EscortTargetMessage, +-- MenuTargetAssistance, +-- ESCORT._AssistTarget, +-- self, +-- EscortGroupData.EscortGroup, +-- ClientEscortTargetData.AttackUnit +-- ) +-- end +-- end +-- else +-- ClientEscortTargetData = nil +-- end +-- end +-- end +-- +-- if EscortTargetMessages ~= "" and self.ReportTargets == true then +-- self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient ) +-- else +-- self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient ) +-- end +-- end +-- +-- if self.EscortMenuResumeMission then +-- self.EscortMenuResumeMission:RemoveSubMenus() +-- +-- -- if self.EscortMenuResumeWayPoints then +-- -- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do +-- -- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } ) +-- -- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove() +-- -- end +-- -- end +-- +-- local TaskPoints = self:RegisterRoute() +-- for WayPointID, WayPoint in pairs( TaskPoints ) do +-- local EscortVec3 = self.EscortGroup:GetVec3() +-- local Distance = ( ( WayPoint.x - EscortVec3.x )^2 + +-- ( WayPoint.y - EscortVec3.z )^2 +-- ) ^ 0.5 / 1000 +-- MENU_CLIENT_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } ) +-- end +-- end +-- +-- return true end - - if self.EscortMenuResumeMission then - self.EscortMenuResumeMission:RemoveSubMenus() - - -- if self.EscortMenuResumeWayPoints then - -- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do - -- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } ) - -- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove() - -- end - -- end - - local TaskPoints = self:RegisterRoute() - for WayPointID, WayPoint in pairs( TaskPoints ) do - local EscortVec3 = self.EscortGroup:GetVec3() - local Distance = ( ( WayPoint.x - EscortVec3.x )^2 + - ( WayPoint.y - EscortVec3.z )^2 - ) ^ 0.5 / 1000 - MENU_CLIENT_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } ) - end - end - - return true end return false @@ -23693,7 +24411,7 @@ function MISSILETRAINER:New( Distance, Briefing ) self.Distance = Distance / 1000 - _EVENTDISPATCHER:OnShot( self._EventShot, self ) + self:HandleEvent( EVENTS.Shot ) self.DBClients = SET_CLIENT:New():FilterStart() @@ -23971,14 +24689,14 @@ end --- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. -- @param #MISSILETRAINER self --- @param Core.Event#EVENTDATA Event -function MISSILETRAINER:_EventShot( Event ) - self:F( { Event } ) +-- @param Core.Event#EVENTDATA EventData +function MISSILETRAINER:OnEventShot( EVentData ) + self:F( { EVentData } ) - local TrainerSourceDCSUnit = Event.IniDCSUnit - local TrainerSourceDCSUnitName = Event.IniDCSUnitName - local TrainerWeapon = Event.Weapon -- Identify the weapon fired - local TrainerWeaponName = Event.WeaponName -- return weapon type + local TrainerSourceDCSUnit = EVentData.IniDCSUnit + local TrainerSourceDCSUnitName = EVentData.IniDCSUnitName + local TrainerWeapon = EVentData.Weapon -- Identify the weapon fired + local TrainerWeaponName = EVentData.WeaponName -- return weapon type self:T( "Missile Launched = " .. TrainerWeaponName ) @@ -25422,991 +26140,2079 @@ end - --- This module contains the DETECTION classes. + --- **Functional** - DETECTION_ classes model the detection of enemy units by FACs or RECCEs and group them according various methods. +-- +-- ![Banner Image](..\Presentations\DETECTION\Dia1.JPG) -- -- === -- --- 1) @{Detection#DETECTION_BASE} class, extends @{Base#BASE} --- ========================================================== --- The @{Detection#DETECTION_BASE} class defines the core functions to administer detected objects. --- The @{Detection#DETECTION_BASE} class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). +-- DETECTION classes facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units). +-- DETECTION uses the in-built detection capabilities of DCS World, but adds new functionalities. -- --- 1.1) DETECTION_BASE constructor --- ------------------------------- --- Construct a new DETECTION_BASE instance using the @{Detection#DETECTION_BASE.New}() method. +-- Please watch this [youtube video](https://youtu.be/C7p81dUwP-E) that explains the detection concepts. -- --- 1.2) DETECTION_BASE initialization --- ---------------------------------- --- By default, detection will return detected objects with all the detection sensors available. --- However, you can ask how the objects were found with specific detection methods. --- If you use one of the below methods, the detection will work with the detection method specified. --- You can specify to apply multiple detection methods. --- --- Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK: --- --- * @{Detection#DETECTION_BASE.InitDetectVisual}(): Detected using Visual. --- * @{Detection#DETECTION_BASE.InitDetectOptical}(): Detected using Optical. --- * @{Detection#DETECTION_BASE.InitDetectRadar}(): Detected using Radar. --- * @{Detection#DETECTION_BASE.InitDetectIRST}(): Detected using IRST. --- * @{Detection#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. --- * @{Detection#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. --- --- 1.3) Obtain objects detected by DETECTION_BASE --- ---------------------------------------------- --- DETECTION_BASE builds @{Set}s of objects detected. These @{Set#SET_BASE}s can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSets}(). --- The method will return a list (table) of @{Set#SET_BASE} objects. --- --- === --- --- 2) @{Detection#DETECTION_AREAS} class, extends @{Detection#DETECTION_BASE} --- =============================================================================== --- The @{Detection#DETECTION_AREAS} class will detect units within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s), --- and will build a list (table) of @{Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. --- The class is group the detected units within zones given a DetectedZoneRange parameter. --- A set with multiple detected zones will be created as there are groups of units detected. --- --- 2.1) Retrieve the Detected Unit sets and Detected Zones --- ------------------------------------------------------- --- The DetectedUnitSets methods are implemented in @{Detection#DECTECTION_BASE} and the DetectedZones methods is implemented in @{Detection#DETECTION_AREAS}. --- --- Retrieve the DetectedUnitSets with the method @{Detection#DETECTION_BASE.GetDetectedSets}(). A table will be return of @{Set#SET_UNIT}s. --- To understand the amount of sets created, use the method @{Detection#DETECTION_BASE.GetDetectedSetCount}(). --- If you want to obtain a specific set from the DetectedSets, use the method @{Detection#DETECTION_BASE.GetDetectedSet}() with a given index. --- --- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Detection#DETECTION_BASE.GetDetectionZones}(). --- To understand the amount of zones created, use the method @{Detection#DETECTION_BASE.GetDetectionZoneCount}(). --- If you want to obtain a specific zone from the DetectedZones, use the method @{Detection#DETECTION_BASE.GetDetectionZone}() with a given index. --- --- 1.4) Flare or Smoke detected units --- ---------------------------------- --- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedUnits}() or @{Detection#DETECTION_AREAS.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place. --- --- 1.5) Flare or Smoke detected zones --- ---------------------------------- --- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedZones}() or @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to flare or smoke the detected zones when a new detection has taken place. --- --- === -- -- ### Contributions: -- --- * Mechanist : Concept & Testing +-- * Mechanist : Early concept of DETECTION_AREAS. -- -- ### Authors: -- --- * FlightControl : Design & Programming +-- * FlightControl : Analysis, Design, Programming, Testing -- -- @module Detection +do -- DETECTION_BASE ---- DETECTION_BASE class --- @type DETECTION_BASE --- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. --- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. --- @field #number DetectionRun --- @extends Core.Base#BASE -DETECTION_BASE = { - ClassName = "DETECTION_BASE", - DetectionSetGroup = nil, - DetectionRange = nil, - DetectedObjects = {}, - DetectionRun = 0, - DetectedObjectsIdentified = {}, -} - ---- @type DETECTION_BASE.DetectedObjects --- @list <#DETECTION_BASE.DetectedObject> - ---- @type DETECTION_BASE.DetectedObject --- @field #string Name --- @field #boolean Visible --- @field #string Type --- @field #number Distance --- @field #boolean Identified - ---- DETECTION constructor. --- @param #DETECTION_BASE self --- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @param Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @return #DETECTION_BASE self -function DETECTION_BASE:New( DetectionSetGroup, DetectionRange ) - - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) + --- # 1) DETECTION_BASE class, extends @{Fsm#FSM} + -- + -- The DETECTION_BASE class defines the core functions to administer detected objects. + -- The DETECTION_BASE class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). + -- + -- ## 1.1) DETECTION_BASE constructor + -- + -- Construct a new DETECTION_BASE instance using the @{#DETECTION_BASE.New}() method. + -- + -- ## 1.2) DETECTION_BASE initialization + -- + -- By default, detection will return detected objects with all the detection sensors available. + -- However, you can ask how the objects were found with specific detection methods. + -- If you use one of the below methods, the detection will work with the detection method specified. + -- You can specify to apply multiple detection methods. + -- + -- Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK: + -- + -- * @{#DETECTION_BASE.InitDetectVisual}(): Detected using Visual. + -- * @{#DETECTION_BASE.InitDetectOptical}(): Detected using Optical. + -- * @{#DETECTION_BASE.InitDetectRadar}(): Detected using Radar. + -- * @{#DETECTION_BASE.InitDetectIRST}(): Detected using IRST. + -- * @{#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. + -- * @{#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. + -- + -- ## 1.3) DETECTION_BASE derived classes group the detected units into a DetectedItems[] list + -- + -- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later + -- of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains + -- a SET_UNIT object that contains the detected units that belong to that group. + -- + -- Derived classes will apply different methods to group the detected units. + -- Examples are per area, per quadrant, per distance, per type. + -- See further the derived DETECTION classes on which grouping methods are currently supported. + -- + -- Various methods exist how to retrieve the grouped items from a DETECTION_BASE derived class: + -- + -- * The method @{Detection#DETECTION_BASE.GetDetectedItems}() retrieves the DetectedItems[] list. + -- * A DetectedItem from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedItem}( DetectedItemIndex ). + -- Note that this method returns a DetectedItem element from the list, that contains a Set variable and further information + -- about the DetectedItem that is set by the DETECTION_BASE derived classes, used to group the DetectedItem. + -- * A DetectedSet from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSet}( DetectedItemIndex ). + -- This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ). + -- + -- ## 1.4) Apply additional Filters to fine-tune the detected objects + -- + -- By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means. + -- That being said, the DCS World detection algorithm can sometimes be unrealistic. + -- Especially for a visual detection, DCS World is able to report within 1 second a detailed detection of a group of 20 units (including types of the units) that are 10 kilometers away, using only visual capabilities. + -- Additionally, trees and other obstacles are not accounted during the DCS World detection. + -- + -- Therefore, an additional (optional) filtering has been built into the DETECTION_BASE class, that can be set for visual detected units. + -- For electronic detection, this filtering is not applied, only for visually detected targets. + -- + -- The following additional filtering can be applied for visual filtering: + -- + -- * A probability factor per kilometer distance. + -- * A probability factor based on the alpha angle between the detected object and the unit detecting. + -- A detection from a higher altitude allows for better detection than when on the ground. + -- * Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult. + -- The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects additing a probability factor per zone. + -- + -- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. + -- Only when you experience unrealistic behaviour in your missions, these filters could be applied. + -- + -- ### 1.4.1 ) Distance visual detection probability + -- + -- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. + -- Also, the speed of accurate detection plays a role. + -- + -- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. + -- + -- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: + -- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. + -- + -- Note that based on this probability factor, not only the detection but also the **type** of the unit will be applied! + -- + -- Use the method @{Detection#DETECTION_BASE.SetDistanceProbability}() to set the probability factor upon a 10 km distance. + -- + -- ### 1.4.2 ) Alpha Angle visual detection probability + -- + -- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. + -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. + -- + -- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. + -- + -- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: + -- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% + -- + -- Use the method @{Detection#DETECTION_BASE.SetAlphaAngleProbability}() to set the probability factor if 0°. + -- + -- ### 1.4.3 ) Cloudy Zones detection probability + -- + -- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. + -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission + -- zones that reflect cloudy areas where detected units may not be so easily visually detected. + -- + -- Use the method @{Detection#DETECTION_BASE.SetZoneProbability}() to set for a defined number of zones, the probability factors. + -- + -- Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take + -- from the DETECTION_BASE to calculate the presence of the detected unit within each zone. + -- Expecially for ZONE_POLYGON, try to limit the amount of nodes of the polygon! + -- + -- Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for + -- AI not to detect so easily targets within a forrest or village rich area. + -- + -- ## 1.5 ) Accept / Reject detected units + -- + -- DETECTION_BASE can accept or reject successful detections based on the location of the detected object, + -- if it is located in range or located inside or outside of specific zones. + -- + -- ### 1.5.1 ) Detection acceptance of within range limit + -- + -- A range can be set that will limit a successful detection for a unit. + -- Use the method @{Detection#DETECTION_BASE.SetAcceptRange}() to apply a range in meters till where detected units will be accepted. + -- + -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. + -- + -- -- Build a detect object. + -- local Detection = DETECTION_BASE:New( SetGroup ) + -- + -- -- This will accept detected units if the range is below 5000 meters. + -- Detection:SetAcceptRange( 5000 ) + -- + -- -- Start the Detection. + -- Detection:Start() + -- + -- + -- ### 1.5.2 ) Detection acceptance if within zone(s). + -- + -- Specific ZONE_BASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONE_BASE object(s). + -- Use the method @{Detection#DETECTION_BASE.SetAcceptZones}() will accept detected units if they are within the specified zones. + -- + -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. + -- + -- -- Search fo the zones where units are to be accepted. + -- local ZoneAccept1 = ZONE:New( "AcceptZone1" ) + -- local ZoneAccept2 = ZONE:New( "AcceptZone2" ) + -- + -- -- Build a detect object. + -- local Detection = DETECTION_BASE:New( SetGroup ) + -- + -- -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2. + -- Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) + -- + -- -- Start the Detection. + -- Detection:Start() + -- + -- ### 1.5.3 ) Detection rejectance if within zone(s). + -- + -- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s). + -- Use the method @{Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones. + -- An example of how to use the method is shown below. + -- + -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. + -- + -- -- Search fo the zones where units are to be rejected. + -- local ZoneReject1 = ZONE:New( "RejectZone1" ) + -- local ZoneReject2 = ZONE:New( "RejectZone2" ) + -- + -- -- Build a detect object. + -- local Detection = DETECTION_BASE:New( SetGroup ) + -- + -- -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2. + -- Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) + -- + -- -- Start the Detection. + -- Detection:Start() + -- + -- ## 1.6) DETECTION_BASE is a Finite State Machine + -- + -- Various Events and State Transitions can be tailored using DETECTION_BASE. + -- + -- ### 1.6.1) DETECTION_BASE States + -- + -- * **Detecting**: The detection is running. + -- * **Stopped**: The detection is stopped. + -- + -- ### 1.6.2) DETECTION_BASE Events + -- + -- * **Start**: Start the detection process. + -- * **Detect**: Detect new units. + -- * **Detected**: New units have been detected. + -- * **Stop**: Stop the detection process. + -- + -- @type DETECTION_BASE + -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. + -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. + -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. + -- @field #number DetectionRun + -- @extends Core.Fsm#FSM + DETECTION_BASE = { + ClassName = "DETECTION_BASE", + DetectionSetGroup = nil, + DetectionRange = nil, + DetectedObjects = {}, + DetectionRun = 0, + DetectedObjectsIdentified = {}, + DetectedItems = {}, + } - self.DetectionSetGroup = DetectionSetGroup - self.DetectionRange = DetectionRange + --- @type DETECTION_BASE.DetectedObjects + -- @list <#DETECTION_BASE.DetectedObject> - self:InitDetectVisual( false ) - self:InitDetectOptical( false ) - self:InitDetectRadar( false ) - self:InitDetectRWR( false ) - self:InitDetectIRST( false ) - self:InitDetectDLINK( false ) + --- @type DETECTION_BASE.DetectedObject + -- @field #string Name + -- @field #boolean Visible + -- @field #string Type + -- @field #number Distance + -- @field #boolean Identified - return self -end - ---- Detect Visual. --- @param #DETECTION_BASE self --- @param #boolean DetectVisual --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectVisual( DetectVisual ) - - self.DetectVisual = DetectVisual -end - ---- Detect Optical. --- @param #DETECTION_BASE self --- @param #boolean DetectOptical --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectOptical( DetectOptical ) - self:F2() - - self.DetectOptical = DetectOptical -end - ---- Detect Radar. --- @param #DETECTION_BASE self --- @param #boolean DetectRadar --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectRadar( DetectRadar ) - self:F2() - - self.DetectRadar = DetectRadar -end - ---- Detect IRST. --- @param #DETECTION_BASE self --- @param #boolean DetectIRST --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectIRST( DetectIRST ) - self:F2() - - self.DetectIRST = DetectIRST -end - ---- Detect RWR. --- @param #DETECTION_BASE self --- @param #boolean DetectRWR --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectRWR( DetectRWR ) - self:F2() - - self.DetectRWR = DetectRWR -end - ---- Detect DLINK. --- @param #DETECTION_BASE self --- @param #boolean DetectDLINK --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectDLINK( DetectDLINK ) - self:F2() - - self.DetectDLINK = DetectDLINK -end - ---- Determines if a detected object has already been identified during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject --- @return #boolean true if already identified. -function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) - self:F3( DetectedObject.Name ) - - local DetectedObjectName = DetectedObject.Name - local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true - self:T3( DetectedObjectIdentified ) - return DetectedObjectIdentified -end - ---- Identifies a detected object during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject -function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) - self:F( DetectedObject.Name ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = true -end - ---- UnIdentify a detected object during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject -function DETECTION_BASE:UnIdentifyDetectedObject( DetectedObject ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = false -end - ---- UnIdentify all detected objects during detection processing. --- @param #DETECTION_BASE self -function DETECTION_BASE:UnIdentifyAllDetectedObjects() - - self.DetectedObjectsIdentified = {} -- Table will be garbage collected. -end - ---- Gets a detected object with a given name. --- @param #DETECTION_BASE self --- @param #string ObjectName --- @return #DETECTION_BASE.DetectedObject -function DETECTION_BASE:GetDetectedObject( ObjectName ) - self:F3( ObjectName ) + --- @type DETECTION_BASE.DetectedItems + -- @list <#DETECTION_BASE.DetectedItem> - if ObjectName then - local DetectedObject = self.DetectedObjects[ObjectName] + --- @type DETECTION_BASE.DetectedItem + -- @field Core.Set#SET_UNIT Set + -- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. + -- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. + -- @field #boolean Changed Documents if the detected area has changes. + -- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). + -- @field #number ItemID -- The identifier of the detected area. + -- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. + -- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - -- Only return detected objects that are alive! - local DetectedUnit = UNIT:FindByName( ObjectName ) - if DetectedUnit and DetectedUnit:IsAlive() then - if self:IsDetectedObjectIdentified( DetectedObject ) == false then - return DetectedObject + + --- DETECTION constructor. + -- @param #DETECTION_BASE self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @return #DETECTION_BASE self + function DETECTION_BASE:New( DetectionSetGroup ) + + -- Inherits from BASE + local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_BASE + + self.DetectedItemCount = 0 + self.DetectedItemMax = 0 + self.DetectedItems = {} + + self.DetectionSetGroup = DetectionSetGroup + + self.DetectionInterval = 30 + + self:InitDetectVisual( true ) + self:InitDetectOptical( false ) + self:InitDetectRadar( false ) + self:InitDetectRWR( false ) + self:InitDetectIRST( false ) + self:InitDetectDLINK( false ) + + -- Create FSM transitions. + + self:SetStartState( "Stopped" ) + self.CountryID = DetectionSetGroup:GetFirst():GetCountry() + + self:AddTransition( "Stopped", "Start", "Detecting") + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnLeaveStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnEnterStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Start. + -- @function [parent=#DETECTION_BASE] OnBeforeStart + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Start. + -- @function [parent=#DETECTION_BASE] OnAfterStart + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Start. + -- @function [parent=#DETECTION_BASE] Start + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Start. + -- @function [parent=#DETECTION_BASE] __Start + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Detecting. + -- @function [parent=#DETECTION_BASE] OnLeaveDetecting + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Detecting. + -- @function [parent=#DETECTION_BASE] OnEnterDetecting + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + self:AddTransition( "Detecting", "Detect", "Detecting" ) + self:AddTransition( "Detecting", "DetectionGroup", "Detecting" ) + + --- OnBefore Transition Handler for Event Detect. + -- @function [parent=#DETECTION_BASE] OnBeforeDetect + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Detect. + -- @function [parent=#DETECTION_BASE] OnAfterDetect + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Detect. + -- @function [parent=#DETECTION_BASE] Detect + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Detect. + -- @function [parent=#DETECTION_BASE] __Detect + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "Detecting", "Detected", "Detecting" ) + + --- OnBefore Transition Handler for Event Detected. + -- @function [parent=#DETECTION_BASE] OnBeforeDetected + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Detected. + -- @function [parent=#DETECTION_BASE] OnAfterDetected + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Detected. + -- @function [parent=#DETECTION_BASE] Detected + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Detected. + -- @function [parent=#DETECTION_BASE] __Detected + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "*", "Stop", "Stopped" ) + + --- OnBefore Transition Handler for Event Stop. + -- @function [parent=#DETECTION_BASE] OnBeforeStop + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Stop. + -- @function [parent=#DETECTION_BASE] OnAfterStop + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Stop. + -- @function [parent=#DETECTION_BASE] Stop + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Stop. + -- @function [parent=#DETECTION_BASE] __Stop + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnLeaveStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnEnterStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + return self + end + + do -- State Transition Handling + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function DETECTION_BASE:onafterStart(From,Event,To) + self:__Detect(0.1) + end + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function DETECTION_BASE:onafterDetect(From,Event,To) + self:E( {From,Event,To}) + + local DetectDelay = 0.1 + self.DetectionCount = 0 + self.DetectionRun = 0 + self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table + + self.DetectionSetGroup:Flush() + + for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do + self:E( {DetectionGroupData}) + self:__DetectionGroup( DetectDelay, DetectionGroupData ) -- Process each detection asynchronously. + self.DetectionCount = self.DetectionCount + 1 + DetectDelay = DetectDelay + 0.1 end end + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. + function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup ) + self:E( {From,Event,To}) + + self.DetectionRun = self.DetectionRun + 1 + + local HasDetectedObjects = false + + if DetectionGroup:IsAlive() then + + self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) + + local DetectionGroupName = DetectionGroup:GetName() + + local DetectedUnits = {} + + local DetectedTargets = DetectionGroup:GetDetectedTargets( + self.DetectVisual, + self.DetectOptical, + self.DetectRadar, + self.DetectIRST, + self.DetectRWR, + self.DetectDLINK + ) + + self:T( DetectedTargets ) + + for DetectionObjectID, Detection in pairs( DetectedTargets ) do + local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object + self:T2( DetectedObject ) + + if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then + + local DetectionAccepted = true + + local DetectedObjectName = DetectedObject:getName() + + local DetectedObjectVec3 = DetectedObject:getPoint() + local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z } + local DetectionGroupVec3 = DetectionGroup:GetVec3() + local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z } + + local Distance = ( ( DetectedObjectVec3.x - DetectionGroupVec3.x )^2 + + ( DetectedObjectVec3.y - DetectionGroupVec3.y )^2 + + ( DetectedObjectVec3.z - DetectionGroupVec3.z )^2 + ) ^ 0.5 / 1000 + + self:T( { "Detected Target", DetectionGroupName, DetectedObjectName, Distance } ) + + -- Calculate Acceptance + + if self.AcceptRange and Distance > self.AcceptRange then + DetectionAccepted = false + end + + if self.AcceptZones then + for AcceptZoneID, AcceptZone in pairs( self.AcceptZones ) do + local AcceptZone = AcceptZone -- Core.Zone#ZONE_BASE + if AcceptZone:IsPointVec2InZone( DetectedObjectVec2 ) == false then + DetectionAccepted = false + end + end + end + + if self.RejectZones then + for RejectZoneID, RejectZone in pairs( self.RejectZones ) do + local RejectZone = RejectZone -- Core.Zone#ZONE_BASE + if RejectZone:IsPointVec2InZone( DetectedObjectVec2 ) == true then + DetectionAccepted = false + end + end + end + + -- Calculate additional probabilities + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.DistanceProbability then + local DistanceFactor = Distance / 4 + local DistanceProbabilityReversed = ( 1 - self.DistanceProbability ) * DistanceFactor + local DistanceProbability = 1 - DistanceProbabilityReversed + DistanceProbability = DistanceProbability * 30 / 300 + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, DistanceProbability } ) + if Probability > DistanceProbability then + DetectionAccepted = false + end + end + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.AlphaAngleProbability then + local NormalVec2 = { x = DetectedObjectVec2.x - DetectionGroupVec2.x, y = DetectedObjectVec2.y - DetectionGroupVec2.y } + local AlphaAngle = math.atan2( NormalVec2.y, NormalVec2.x ) + local Sinus = math.sin( AlphaAngle ) + local AlphaAngleProbabilityReversed = ( 1 - self.AlphaAngleProbability ) * ( 1 - Sinus ) + local AlphaAngleProbability = 1 - AlphaAngleProbabilityReversed + + AlphaAngleProbability = AlphaAngleProbability * 30 / 300 + + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, AlphaAngleProbability } ) + if Probability > AlphaAngleProbability then + DetectionAccepted = false + end + + end + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.ZoneProbability then + + for ZoneDataID, ZoneData in pairs( self.ZoneProbability ) do + self:E({ZoneData}) + local ZoneObject = ZoneData[1] -- Core.Zone#ZONE_BASE + local ZoneProbability = ZoneData[2] -- #number + ZoneProbability = ZoneProbability * 30 / 300 + + if ZoneObject:IsPointVec2InZone( DetectedObjectVec2 ) == true then + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, ZoneProbability } ) + if Probability > ZoneProbability then + DetectionAccepted = false + break + end + end + end + end + + if DetectionAccepted then + + HasDetectedObjects = true + + if not self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = {} + end + self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName + self.DetectedObjects[DetectedObjectName].Visible = Detection.visible + self.DetectedObjects[DetectedObjectName].Type = Detection.type + self.DetectedObjects[DetectedObjectName].Distance = Distance + + local DetectedUnit = UNIT:FindByName( DetectedObjectName ) + + DetectedUnits[DetectedObjectName] = DetectedUnit + else + -- if beyond the DetectionRange then nullify... + if self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = nil + end + end + end + + self:T2( self.DetectedObjects ) + end + + if HasDetectedObjects then + self:__Detected( 0.1, DetectedUnits ) + end + + end + + if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then + self:__Detect( self.DetectionInterval ) + + self:T( "--> Create Detection Sets" ) + self:CreateDetectionSets() + end + + end + + + end + + do -- Initialization methods + + --- Detect Visual. + -- @param #DETECTION_BASE self + -- @param #boolean DetectVisual + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectVisual( DetectVisual ) + + self.DetectVisual = DetectVisual + end + + --- Detect Optical. + -- @param #DETECTION_BASE self + -- @param #boolean DetectOptical + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectOptical( DetectOptical ) + self:F2() + + self.DetectOptical = DetectOptical + end + + --- Detect Radar. + -- @param #DETECTION_BASE self + -- @param #boolean DetectRadar + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectRadar( DetectRadar ) + self:F2() + + self.DetectRadar = DetectRadar + end + + --- Detect IRST. + -- @param #DETECTION_BASE self + -- @param #boolean DetectIRST + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectIRST( DetectIRST ) + self:F2() + + self.DetectIRST = DetectIRST + end + + --- Detect RWR. + -- @param #DETECTION_BASE self + -- @param #boolean DetectRWR + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectRWR( DetectRWR ) + self:F2() + + self.DetectRWR = DetectRWR + end + + --- Detect DLINK. + -- @param #DETECTION_BASE self + -- @param #boolean DetectDLINK + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectDLINK( DetectDLINK ) + self:F2() + + self.DetectDLINK = DetectDLINK + end + + end + + do + + --- Set the detection interval time in seconds. + -- @param #DETECTION_BASE self + -- @param #number DetectionInterval Interval in seconds. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetDetectionInterval( DetectionInterval ) + self:F2() + + self.DetectionInterval = DetectionInterval + + return self + end + + end + + do -- Accept / Reject detected units + + --- Accept detections if within a range in meters. + -- @param #DETECTION_BASE self + -- @param #number AcceptRange Accept a detection if the unit is within the AcceptRange in meters. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAcceptRange( AcceptRange ) + self:F2() + + self.AcceptRange = AcceptRange + + return self + end + + --- Accept detections if within the specified zone(s). + -- @param #DETECTION_BASE self + -- @param AcceptZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAcceptZones( AcceptZones ) + self:F2() + + if type( AcceptZones ) == "table" then + self.AcceptZones = AcceptZones + else + self.AcceptZones = { AcceptZones } + end + + return self + end + + --- Reject detections if within the specified zone(s). + -- @param #DETECTION_BASE self + -- @param RejectZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetRejectZones( RejectZones ) + self:F2() + + if type( RejectZones ) == "table" then + self.RejectZones = RejectZones + else + self.RejectZones = { RejectZones } + end + + return self + end + + end + + do -- Probability methods + + --- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. + -- Also, the speed of accurate detection plays a role. + -- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. + -- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: + -- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. + -- @param #DETECTION_BASE self + -- @param DistanceProbability The probability factor. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetDistanceProbability( DistanceProbability ) + self:F2() + + self.DistanceProbability = DistanceProbability + + return self + end + + + --- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. + -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. + -- + -- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. + -- + -- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: + -- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% + -- @param #DETECTION_BASE self + -- @param AlphaAngleProbability The probability factor. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAlphaAngleProbability( AlphaAngleProbability ) + self:F2() + + self.AlphaAngleProbability = AlphaAngleProbability + + return self + end + + --- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. + -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission + -- zones that reflect cloudy areas where detected units may not be so easily visually detected. + -- @param #DETECTION_BASE self + -- @param ZoneArray Aray of a The ZONE_BASE object and a ZoneProbability pair.. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetZoneProbability( ZoneArray ) + self:F2() + + self.ZoneProbability = ZoneArray + + return self + end + + + end + + do -- Change processing + + --- Accepts changes from the detected item. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #DETECTION_BASE self + function DETECTION_BASE:AcceptChanges( DetectedItem ) + + DetectedItem.Changed = false + DetectedItem.Changes = {} + + return self + end + + --- Add a change to the detected zone. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @param #string ChangeCode + -- @return #DETECTION_BASE self + function DETECTION_BASE:AddChangeItem( DetectedItem, ChangeCode, ItemUnitType ) + + DetectedItem.Changed = true + local ItemID = DetectedItem.ItemID + + DetectedItem.Changes = DetectedItem.Changes or {} + DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} + DetectedItem.Changes[ChangeCode].ItemID = ItemID + DetectedItem.Changes[ChangeCode].ItemUnitType = ItemUnitType + + self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ItemUnitType } ) + + return self + end + + + --- Add a change to the detected zone. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @param #string ChangeCode + -- @param #string ChangeUnitType + -- @return #DETECTION_BASE self + function DETECTION_BASE:AddChangeUnit( DetectedItem, ChangeCode, ChangeUnitType ) + + DetectedItem.Changed = true + local ItemID = DetectedItem.ItemID + + DetectedItem.Changes = DetectedItem.Changes or {} + DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} + DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] or 0 + DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] + 1 + DetectedItem.Changes[ChangeCode].ItemID = ItemID + + self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ChangeUnitType } ) + + return self + end + + + end + + do -- Threat + + --- Returns if there are friendlies nearby the FAC units ... + -- @param #DETECTION_BASE self + -- @return #boolean trhe if there are friendlies nearby + function DETECTION_BASE:IsFriendliesNearBy( DetectedItem ) + + self:T3( DetectedItem.FriendliesNearBy ) + return DetectedItem.FriendliesNearBy or false + end + + --- Background worker function to determine if there are friendlies nearby ... + -- @param #DETECTION_BASE self + function DETECTION_BASE:ReportFriendliesNearBy( ReportGroupData ) + self:F2() + + local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = ReportGroupData.DetectedItem.Set + local DetectedUnit = DetectedSet:GetFirst() + + DetectedItem.FriendliesNearBy = false + + if DetectedUnit then + + + local SphereSearch = { + id = world.VolumeType.SPHERE, + params = { + point = DetectedUnit:GetVec3(), + radius = 6000, + } + + } + + --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit + -- @param Wrapper.Group#GROUP ReportGroup + -- @param Set#SET_GROUP ReportSetGroup + local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) + + local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = ReportGroupData.DetectedItem.Set + local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT + local ReportSetGroup = ReportGroupData.ReportSetGroup + + local EnemyCoalition = DetectedUnit:GetCoalition() + + local FoundUnitCoalition = FoundDCSUnit:getCoalition() + local FoundUnitName = FoundDCSUnit:getName() + local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() + local EnemyUnitName = DetectedUnit:GetName() + local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil + + self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) + + if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then + DetectedItem.FriendliesNearBy = true + return false + end + + return true + end + + world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) + end + end + + end + + --- Determines if a detected object has already been identified during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + -- @return #boolean true if already identified. + function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) + self:F3( DetectedObject.Name ) + + local DetectedObjectName = DetectedObject.Name + local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true + self:T3( DetectedObjectIdentified ) + return DetectedObjectIdentified + end + + --- Identifies a detected object during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) + self:F( { "Identified:", DetectedObject.Name } ) + + local DetectedObjectName = DetectedObject.Name + self.DetectedObjectsIdentified[DetectedObjectName] = true + end + + --- UnIdentify a detected object during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + function DETECTION_BASE:UnIdentifyDetectedObject( DetectedObject ) + + local DetectedObjectName = DetectedObject.Name + self.DetectedObjectsIdentified[DetectedObjectName] = false + end + + --- UnIdentify all detected objects during detection processing. + -- @param #DETECTION_BASE self + function DETECTION_BASE:UnIdentifyAllDetectedObjects() + + self.DetectedObjectsIdentified = {} -- Table will be garbage collected. + end + + --- Gets a detected object with a given name. + -- @param #DETECTION_BASE self + -- @param #string ObjectName + -- @return #DETECTION_BASE.DetectedObject + function DETECTION_BASE:GetDetectedObject( ObjectName ) + self:F( ObjectName ) + + if ObjectName then + local DetectedObject = self.DetectedObjects[ObjectName] + + -- Only return detected objects that are alive! + local DetectedUnit = UNIT:FindByName( ObjectName ) + if DetectedUnit and DetectedUnit:IsAlive() then + if self:IsDetectedObjectIdentified( DetectedObject ) == false then + return DetectedObject + end + end + end + + return nil + end + + + --- Adds a new DetectedItem to the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #string DetectedItemIndex The index of the DetectedItem. + -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:AddDetectedItem( DetectedItemIndex, Set ) + + local DetectedItem = {} + self.DetectedItemCount = self.DetectedItemCount + 1 + self.DetectedItemMax = self.DetectedItemMax + 1 + + if DetectedItemIndex then + self.DetectedItems[DetectedItemIndex] = DetectedItem + else + self.DetectedItems[self.DetectedItemCount] = DetectedItem + end + + DetectedItem.Set = Set or SET_UNIT:New() + DetectedItem.ItemID = self.DetectedItemMax + DetectedItem.Removed = false + + return DetectedItem + end + + --- Adds a new DetectedItem to the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #string DetectedItemIndex The index of the DetectedItem. + -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. + -- @param Core.Zone#ZONE_UNIT Zone (optional) The Zone to be added where the Units are located. + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:AddDetectedItemZone( DetectedItemIndex, Set, Zone ) + + local DetectedItem = self:AddDetectedItem( DetectedItemIndex, Set ) + + DetectedItem.Zone = Zone + + return DetectedItem + end + + --- Removes an existing DetectedItem from the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #number DetectedItemIndex The index or position in the DetectedItems list where the item needs to be removed. + function DETECTION_BASE:RemoveDetectedItem( DetectedItemIndex ) + + self.DetectedItemCount = self.DetectedItemCount - 1 + self.DetectedItems[DetectedItemIndex] = nil + end + + + --- Get the detected @{Set#SET_BASE}s. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE.DetectedItems + function DETECTION_BASE:GetDetectedItems() + + return self.DetectedItems + end + + --- Get the amount of SETs with detected objects. + -- @param #DETECTION_BASE self + -- @return #number Count + function DETECTION_BASE:GetDetectedItemsCount() + + local DetectedCount = self.DetectedItemCount + return DetectedCount + end + + --- Get a detected item using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:GetDetectedItem( Index ) + + local DetectedItem = self.DetectedItems[Index] + if DetectedItem then + return DetectedItem + end + + return nil + end + + --- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return Core.Set#SET_UNIT DetectedSet + function DETECTION_BASE:GetDetectedSet( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + local DetectedSetUnit = DetectedItem.Set + if DetectedSetUnit then + return DetectedSetUnit + end + + return nil + end + + do -- Zones + + --- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return Core.Zone#ZONE_UNIT DetectedZone + function DETECTION_BASE:GetDetectedZone( Index ) + + local DetectedZone = self.DetectedItems[Index].Zone + if DetectedZone then + return DetectedZone + end + + return nil + end + + end + + + --- Report summary of a detected item using a given numeric index. + -- @param #DETECTION_BASE self + -- @param Index + -- @return #string + function DETECTION_BASE:DetectedItemReportSummary( Index ) + self:F( Index ) + return nil + end + + --- Report detailed of a detectedion result. + -- @param #DETECTION_BASE self + -- @return #string + function DETECTION_BASE:DetectedReportDetailed() + self:F() + return nil + end + + --- Get the detection Groups. + -- @param #DETECTION_BASE self + -- @return Wrapper.Group#GROUP + function DETECTION_BASE:GetDetectionSetGroup() + + local DetectionSetGroup = self.DetectionSetGroup + return DetectionSetGroup + end + + --- Make a DetectionSet table. This function will be overridden in the derived clsses. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE self + function DETECTION_BASE:CreateDetectionSets() + self:F2() + + self:E( "Error, in DETECTION_BASE class..." ) + + end + + + --- Schedule the DETECTION construction. + -- @param #DETECTION_BASE self + -- @param #number DelayTime The delay in seconds to wait the reporting. + -- @param #number RepeatInterval The repeat interval in seconds for the reporting to happen repeatedly. + -- @return #DETECTION_BASE self + function DETECTION_BASE:Schedule( DelayTime, RepeatInterval ) + self:F2() + + self.ScheduleDelayTime = DelayTime + self.ScheduleRepeatInterval = RepeatInterval + + self.DetectionScheduler = SCHEDULER:New( self, self._DetectionScheduler, { self, "Detection" }, DelayTime, RepeatInterval ) + return self + end + +end + +do -- DETECTION_UNITS + + --- # 2) DETECTION_UNITS class, extends @{Detection#DETECTION_BASE} + -- + -- The DETECTION_UNITS class will detect units within the battle zone. + -- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. + -- Beware that when the amount of units detected is large, the DetectedItems list will be large also. + -- + -- @type DETECTION_UNITS + -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are detected. + -- @extends #DETECTION_BASE + DETECTION_UNITS = { + ClassName = "DETECTION_UNITS", + DetectionRange = nil, + } + + --- DETECTION_UNITS constructor. + -- @param Functional.Detection#DETECTION_UNITS self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @return Functional.Detection#DETECTION_UNITS self + function DETECTION_UNITS:New( DetectionSetGroup ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_UNITS + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self + end + + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_UNITS self + -- @param #DETECTION_UNITS.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_UNITS:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do + + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." + end + + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." + end + + end + + return table.concat( MT, "\n" ) + + end + + + --- Create the DetectedItems list from the DetectedObjects table. + -- For each DetectedItem, a one field array is created containing the Unit detected. + -- @param #DETECTION_UNITS self + -- @return #DETECTION_UNITS self + function DETECTION_UNITS:CreateDetectionSets() + self:F2( #self.DetectedObjects ) + + -- Loop the current detected items, and check if each object still exists and is detected. + + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + + local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedTypeName = DetectedItem.Type + + for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + + local DetectedObject = nil + self:E( DetectedUnit ) + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Yes, the DetectedUnit is still detected or exists. Flag as identified. + self:IdentifyDetectedObject( DetectedObject ) + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) + DetectedItemSet:Remove( DetectedUnitName ) + end + end + end + + + -- Now we need to loop through the unidentified detected units and add these... These are all new items. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do + + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + if DetectedObject then + self:T( { "Detected Unit #", DetectedUnitName } ) + + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + if DetectedUnit then + local DetectedTypeName = DetectedUnit:GetTypeName() + local DetectedItem = self:GetDetectedItem( DetectedUnitName ) + if not DetectedItem then + self:T( "Added new DetectedItem" ) + DetectedItem = self:AddDetectedItem( DetectedUnitName ) + DetectedItem.Type = DetectedUnit:GetTypeName() + DetectedItem.Name = DetectedObjectData.Name + DetectedItem.Visible = DetectedObjectData.Visible + DetectedItem.Distance = DetectedObjectData.Distance + end + + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) + end + end + end + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + --self:NearestFAC( DetectedItem ) + end + + end + + --- Report summary of a DetectedItem using a given numeric index. + -- @param #DETECTION_UNITS self + -- @param Index + -- @return #string + function DETECTION_UNITS:DetectedItemReportSummary( Index ) + self:F( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + local DetectedSet = self:GetDetectedSet( Index ) + + self:T( DetectedSet ) + if DetectedSet then + local ReportSummary = "" + local UnitDistanceText = "" + local UnitCategoryText = "" + + local DetectedItemUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT + + if DetectedItemUnit and DetectedItemUnit:IsAlive() then + self:T(DetectedItemUnit) + + local UnitCategoryName = DetectedItemUnit:GetCategoryName() or "" + local UnitCategoryType = DetectedItemUnit:GetTypeName() or "" + + if DetectedItem.Type and UnitCategoryName and UnitCategoryType then + UnitCategoryText = UnitCategoryName .. " (" .. UnitCategoryType .. ") at " + else + UnitCategoryText = "Unknown target at " + end + + if DetectedItem.Visible == false then + UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " estimated km" + else + UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " km, visual contact" + end + + local DetectedItemPointVec3 = DetectedItemUnit:GetPointVec3() + local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) + + local ThreatLevelA2G = DetectedItemUnit:GetThreatLevel( DetectedItem ) + + ReportSummary = string.format( + "%s - Threat [%s] (%2d) - %s%s", + DetectedItemPointLL, + string.rep( "■", ThreatLevelA2G ), + ThreatLevelA2G, + UnitCategoryText, + UnitDistanceText + ) + end + + self:T( ReportSummary ) + + return ReportSummary + end end - return nil + --- Report detailed of a detection result. + -- @param #DETECTION_UNITS self + -- @return #string + function DETECTION_UNITS:DetectedReportDetailed() + self:F() + + local Report = REPORT:New( "Detected units:" ) + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItemID ) + Report:Add( ReportSummary ) + end + + local ReportText = Report:Text() + + return ReportText + end + end ---- Get the detected @{Set#SET_BASE}s. --- @param #DETECTION_BASE self --- @return #DETECTION_BASE.DetectedSets DetectedSets -function DETECTION_BASE:GetDetectedSets() +do -- DETECTION_TYPES - local DetectionSets = self.DetectedSets - return DetectionSets -end - ---- Get the amount of SETs with detected objects. --- @param #DETECTION_BASE self --- @return #number Count -function DETECTION_BASE:GetDetectedSetCount() - - local DetectionSetCount = #self.DetectedSets - return DetectionSetCount -end - ---- Get a SET of detected objects using a given numeric index. --- @param #DETECTION_BASE self --- @param #number Index --- @return Core.Set#SET_BASE -function DETECTION_BASE:GetDetectedSet( Index ) - - local DetectionSet = self.DetectedSets[Index] - if DetectionSet then - return DetectionSet + --- # 3) DETECTION_TYPES class, extends @{Detection#DETECTION_BASE} + -- + -- The DETECTION_TYPES class will detect units within the battle zone. + -- It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. + -- Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. + -- Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also. + -- + -- @type DETECTION_TYPES + -- @extends #DETECTION_BASE + DETECTION_TYPES = { + ClassName = "DETECTION_TYPES", + DetectionRange = nil, + } + + --- DETECTION_TYPES constructor. + -- @param Functional.Detection#DETECTION_TYPES self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Recce role. + -- @return Functional.Detection#DETECTION_TYPES self + function DETECTION_TYPES:New( DetectionSetGroup ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_TYPES + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self end - return nil -end - ---- Get the detection Groups. --- @param #DETECTION_BASE self --- @return Wrapper.Group#GROUP -function DETECTION_BASE:GetDetectionSetGroup() - - local DetectionSetGroup = self.DetectionSetGroup - return DetectionSetGroup -end - ---- Make a DetectionSet table. This function will be overridden in the derived clsses. --- @param #DETECTION_BASE self --- @return #DETECTION_BASE self -function DETECTION_BASE:CreateDetectionSets() - self:F2() - - self:E( "Error, in DETECTION_BASE class..." ) - -end - - ---- Schedule the DETECTION construction. --- @param #DETECTION_BASE self --- @param #number DelayTime The delay in seconds to wait the reporting. --- @param #number RepeatInterval The repeat interval in seconds for the reporting to happen repeatedly. --- @return #DETECTION_BASE self -function DETECTION_BASE:Schedule( DelayTime, RepeatInterval ) - self:F2() - - self.ScheduleDelayTime = DelayTime - self.ScheduleRepeatInterval = RepeatInterval + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_TYPES self + -- @param #DETECTION_TYPES.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_TYPES:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do - self.DetectionScheduler = SCHEDULER:New( self, self._DetectionScheduler, { self, "Detection" }, DelayTime, RepeatInterval ) - return self -end - - ---- Form @{Set}s of detected @{Unit#UNIT}s in an array of @{Set#SET_BASE}s. --- @param #DETECTION_BASE self -function DETECTION_BASE:_DetectionScheduler( SchedulerName ) - self:F2( { SchedulerName } ) + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." + end - self.DetectionRun = self.DetectionRun + 1 - - self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table - - for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - local DetectionGroup = DetectionGroupData -- Wrapper.Group#GROUP - - if DetectionGroup:IsAlive() then - - local DetectionGroupName = DetectionGroup:GetName() + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." + end - local DetectionDetectedTargets = DetectionGroup:GetDetectedTargets( - self.DetectVisual, - self.DetectOptical, - self.DetectRadar, - self.DetectIRST, - self.DetectRWR, - self.DetectDLINK + end + + return table.concat( MT, "\n" ) + + end + + + --- Create the DetectedItems list from the DetectedObjects table. + -- For each DetectedItem, a one field array is created containing the Unit detected. + -- @param #DETECTION_TYPES self + -- @return #DETECTION_TYPES self + function DETECTION_TYPES:CreateDetectionSets() + self:F2( #self.DetectedObjects ) + + -- Loop the current detected items, and check if each object still exists and is detected. + + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + + local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedTypeName = DetectedItem.Type + + for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + + local DetectedObject = nil + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Yes, the DetectedUnit is still detected or exists. Flag as identified. + self:IdentifyDetectedObject( DetectedObject ) + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) + DetectedItemSet:Remove( DetectedUnitName ) + end + end + end + + + -- Now we need to loop through the unidentified detected units and add these... These are all new items. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do + + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + if DetectedObject then + self:T( { "Detected Unit #", DetectedUnitName } ) + + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + if DetectedUnit then + local DetectedTypeName = DetectedUnit:GetTypeName() + local DetectedItem = self:GetDetectedItem( DetectedTypeName ) + if not DetectedItem then + DetectedItem = self:AddDetectedItem( DetectedTypeName ) + DetectedItem.Type = DetectedUnit:GetTypeName() + end + + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) + end + end + end + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + --self:NearestFAC( DetectedItem ) + end + + end + + --- Report summary of a DetectedItem using a given numeric index. + -- @param #DETECTION_TYPES self + -- @param Index + -- @return #string + function DETECTION_TYPES:DetectedItemReportSummary( DetectedTypeName ) + self:F( DetectedTypeName ) + + local DetectedItem = self:GetDetectedItem( DetectedTypeName ) + local DetectedSet = self:GetDetectedSet( DetectedTypeName ) + + self:T( DetectedItem ) + if DetectedItem then + + local ThreatLevelA2G = DetectedSet:CalculateThreatLevelA2G() + local DetectedItemsCount = DetectedSet:Count() + local DetectedItemType = DetectedItem.Type + + local ReportSummary = string.format( + "Threat [%s] (%2d) - %2d of %s", + string.rep( "■", ThreatLevelA2G ), + ThreatLevelA2G, + DetectedItemsCount, + DetectedItemType + ) + self:T( ReportSummary ) + + return ReportSummary + end + end + + --- Report detailed of a detection result. + -- @param #DETECTION_TYPES self + -- @return #string + function DETECTION_TYPES:DetectedReportDetailed() + self:F() + + local Report = REPORT:New( "Detected types:" ) + for DetectedItemTypeName, DetectedItem in pairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItemTypeName ) + Report:Add( ReportSummary ) + end + + local ReportText = Report:Text() + + return ReportText + end + +end + + +do -- DETECTION_AREAS + + --- # 4) DETECTION_AREAS class, extends @{Detection#DETECTION_BASE} + -- + -- The DETECTION_AREAS class will detect units within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s), + -- and will build a list (table) of @{Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. + -- The class is group the detected units within zones given a DetectedZoneRange parameter. + -- A set with multiple detected zones will be created as there are groups of units detected. + -- + -- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones + -- + -- The methods to manage the DetectedItems[].Set(s) are implemented in @{Detection#DECTECTION_BASE} and + -- the methods to manage the DetectedItems[].Zone(s) is implemented in @{Detection#DETECTION_AREAS}. + -- + -- Retrieve the DetectedItems[].Set with the method @{Detection#DETECTION_BASE.GetDetectedSet}(). A @{Set#SET_UNIT} object will be returned. + -- + -- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Detection#DETECTION_BASE.GetDetectionZones}(). + -- To understand the amount of zones created, use the method @{Detection#DETECTION_BASE.GetDetectionZoneCount}(). + -- If you want to obtain a specific zone from the DetectedZones, use the method @{Detection#DETECTION_BASE.GetDetectionZone}() with a given index. + -- + -- ## 4.4) Flare or Smoke detected units + -- + -- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedUnits}() or @{Detection#DETECTION_AREAS.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place. + -- + -- ## 4.5) Flare or Smoke or Bound detected zones + -- + -- Use the methods: + -- + -- * @{Detection#DETECTION_AREAS.FlareDetectedZones}() to flare in a color + -- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to smoke in a color + -- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to bound with a tire with a white flag + -- + -- the detected zones when a new detection has taken place. + -- + -- @type DETECTION_AREAS + -- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. + -- @extends #DETECTION_BASE + DETECTION_AREAS = { + ClassName = "DETECTION_AREAS", + DetectionZoneRange = nil, + } + + + --- DETECTION_AREAS constructor. + -- @param #DETECTION_AREAS self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @return #DETECTION_AREAS + function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) + + self.DetectionZoneRange = DetectionZoneRange + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self + end + + --- Report summary of a detected item using a given numeric index. + -- @param #DETECTION_AREAS self + -- @param Index + -- @return #string + function DETECTION_AREAS:DetectedItemReportSummary( Index ) + self:F( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + if DetectedItem then + local DetectedSet = self:GetDetectedSet( Index ) + local ReportSummaryItem + + local DetectedZone = self:GetDetectedZone( Index ) + local DetectedItemPointVec3 = DetectedZone:GetPointVec3() + local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) + + local ThreatLevelA2G = self:GetTreatLevelA2G( DetectedItem ) + local DetectedItemsCount = DetectedSet:Count() + local DetectedItemsTypes = DetectedSet:GetTypeNames() + + local ReportSummary = string.format( + "%s - Threat [%s] (%2d) - %2d of %s", + DetectedItemPointLL, + string.rep( "■", ThreatLevelA2G ), + ThreatLevelA2G, + DetectedItemsCount, + DetectedItemsTypes ) - for DetectionDetectedTargetID, DetectionDetectedTarget in pairs( DetectionDetectedTargets ) do - local DetectionObject = DetectionDetectedTarget.object -- Dcs.DCSWrapper.Object#Object - self:T2( DetectionObject ) + return ReportSummary + end + + return nil + end + + + --- Returns if there are friendlies nearby the FAC units ... + -- @param #DETECTION_AREAS self + -- @return #boolean trhe if there are friendlies nearby + function DETECTION_AREAS:IsFriendliesNearBy( DetectedItem ) + + self:T3( DetectedItem.FriendliesNearBy ) + return DetectedItem.FriendliesNearBy or false + end + + --- Calculate the maxium A2G threat level of the DetectedItem. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedItem ) + + local MaxThreatLevelA2G = 0 + for UnitName, UnitData in pairs( DetectedItem.Set:GetSet() ) do + local ThreatUnit = UnitData -- Wrapper.Unit#UNIT + local ThreatLevelA2G = ThreatUnit:GetThreatLevel() + if ThreatLevelA2G > MaxThreatLevelA2G then + MaxThreatLevelA2G = ThreatLevelA2G + end + end + + self:T3( MaxThreatLevelA2G ) + DetectedItem.MaxThreatLevelA2G = MaxThreatLevelA2G + + end + + --- Find the nearest FAC of the DetectedItem. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return Wrapper.Unit#UNIT The nearest FAC unit + function DETECTION_AREAS:NearestFAC( DetectedItem ) + + local NearestFAC = nil + local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) + + for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do + for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do + local FACUnit = FACUnitData -- Wrapper.Unit#UNIT + if FACUnit:IsActive() then + local Vec3 = FACUnit:GetVec3() + local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) + local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) + if Distance < MinDistance then + MinDistance = Distance + NearestFAC = FACUnit + end + end + end + end + + DetectedItem.NearestFAC = NearestFAC + + end + + --- Returns the A2G threat level of the units in the DetectedItem + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #number a scale from 0 to 10. + function DETECTION_AREAS:GetTreatLevelA2G( DetectedItem ) + + self:T3( DetectedItem.MaxThreatLevelA2G ) + return DetectedItem.MaxThreatLevelA2G + end + + + + --- Smoke the detected units + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:SmokeDetectedUnits() + self:F2() + + self._SmokeDetectedUnits = true + return self + end + + --- Flare the detected units + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:FlareDetectedUnits() + self:F2() + + self._FlareDetectedUnits = true + return self + end + + --- Smoke the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:SmokeDetectedZones() + self:F2() + + self._SmokeDetectedZones = true + return self + end + + --- Flare the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:FlareDetectedZones() + self:F2() + + self._FlareDetectedZones = true + return self + end + + --- Bound the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:BoundDetectedZones() + self:F2() + + self._BoundDetectedZones = true + return self + end + + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_AREAS:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do + + if ChangeCode == "AA" then + MT[#MT+1] = "Detected new area " .. ChangeData.ItemID .. ". The center target is a " .. ChangeData.ItemUnitType .. "." + end + + if ChangeCode == "RAU" then + MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". Removed the center target." + end + + if ChangeCode == "AAU" then + MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". The new center target is a " .. ChangeData.ItemUnitType .. "." + end + + if ChangeCode == "RA" then + MT[#MT+1] = "Removed old area " .. ChangeData.ItemID .. ". No more targets in this area." + end + + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = "Detected for area " .. ChangeData.ItemID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." + end + + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = "Removed for area " .. ChangeData.ItemID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." + end + + end + + return table.concat( MT, "\n" ) + + end + + + --- Make a DetectionSet table. This function will be overridden in the derived clsses. + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:CreateDetectionSets() + self:F2() + + + self:T( "Checking Detected Items for new Detected Units ..." ) + -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. + -- Regroup when needed, split groups when needed. + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + if DetectedItem then + + self:T( { "Detected Item ID:", DetectedItemID } ) - if DetectionObject and DetectionObject:isExist() and DetectionObject.id_ < 50000000 then - - local DetectionDetectedObjectName = DetectionObject:getName() - - local DetectionDetectedObjectPositionVec3 = DetectionObject:getPoint() - local DetectionGroupVec3 = DetectionGroup:GetVec3() - - local Distance = ( ( DetectionDetectedObjectPositionVec3.x - DetectionGroupVec3.x )^2 + - ( DetectionDetectedObjectPositionVec3.y - DetectionGroupVec3.y )^2 + - ( DetectionDetectedObjectPositionVec3.z - DetectionGroupVec3.z )^2 - ) ^ 0.5 / 1000 - - self:T2( { DetectionGroupName, DetectionDetectedObjectName, Distance } ) - - if Distance <= self.DetectionRange then - - if not self.DetectedObjects[DetectionDetectedObjectName] then - self.DetectedObjects[DetectionDetectedObjectName] = {} - end - self.DetectedObjects[DetectionDetectedObjectName].Name = DetectionDetectedObjectName - self.DetectedObjects[DetectionDetectedObjectName].Visible = DetectionDetectedTarget.visible - self.DetectedObjects[DetectionDetectedObjectName].Type = DetectionDetectedTarget.type - self.DetectedObjects[DetectionDetectedObjectName].Distance = DetectionDetectedTarget.distance - else - -- if beyond the DetectionRange then nullify... - if self.DetectedObjects[DetectionDetectedObjectName] then - self.DetectedObjects[DetectionDetectedObjectName] = nil - end - end - end - end - self:T2( self.DetectedObjects ) - - -- okay, now we have a list of detected object names ... - -- Sort the table based on distance ... - table.sort( self.DetectedObjects, function( a, b ) return a.Distance < b.Distance end ) - end - end - - if self.DetectedObjects then - self:CreateDetectionSets() - end - - return true -end - - - ---- DETECTION_AREAS class --- @type DETECTION_AREAS --- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. --- @field #DETECTION_AREAS.DetectedAreas DetectedAreas A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. --- @extends Functional.Detection#DETECTION_BASE -DETECTION_AREAS = { - ClassName = "DETECTION_AREAS", - DetectedAreas = { n = 0 }, - DetectionZoneRange = nil, -} - ---- @type DETECTION_AREAS.DetectedAreas --- @list <#DETECTION_AREAS.DetectedArea> - ---- @type DETECTION_AREAS.DetectedArea --- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. --- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. --- @field #boolean Changed Documents if the detected area has changes. --- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). --- @field #number AreaID -- The identifier of the detected area. --- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. --- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - - ---- DETECTION_AREAS constructor. --- @param Functional.Detection#DETECTION_AREAS self --- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @param Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. --- @return Functional.Detection#DETECTION_AREAS self -function DETECTION_AREAS:New( DetectionSetGroup, DetectionRange, DetectionZoneRange ) - - -- Inherits from DETECTION_BASE - local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup, DetectionRange ) ) - - self.DetectionZoneRange = DetectionZoneRange - - self._SmokeDetectedUnits = false - self._FlareDetectedUnits = false - self._SmokeDetectedZones = false - self._FlareDetectedZones = false - - self:Schedule( 10, 10 ) - - return self -end - ---- Add a detected @{#DETECTION_AREAS.DetectedArea}. --- @param Core.Set#SET_UNIT Set -- The Set of Units in the detected area. --- @param Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. --- @return #DETECTION_AREAS.DetectedArea DetectedArea -function DETECTION_AREAS:AddDetectedArea( Set, Zone ) - local DetectedAreas = self:GetDetectedAreas() - DetectedAreas.n = self:GetDetectedAreaCount() + 1 - DetectedAreas[DetectedAreas.n] = {} - local DetectedArea = DetectedAreas[DetectedAreas.n] - DetectedArea.Set = Set - DetectedArea.Zone = Zone - DetectedArea.Removed = false - DetectedArea.AreaID = DetectedAreas.n - - return DetectedArea -end - ---- Remove a detected @{#DETECTION_AREAS.DetectedArea} with a given Index. --- @param #DETECTION_AREAS self --- @param #number Index The Index of the detection are to be removed. --- @return #nil -function DETECTION_AREAS:RemoveDetectedArea( Index ) - local DetectedAreas = self:GetDetectedAreas() - local DetectedAreaCount = self:GetDetectedAreaCount() - local DetectedArea = DetectedAreas[Index] - local DetectedAreaSet = DetectedArea.Set - DetectedArea[Index] = nil - return nil -end - - ---- Get the detected @{#DETECTION_AREAS.DetectedAreas}. --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS.DetectedAreas DetectedAreas -function DETECTION_AREAS:GetDetectedAreas() - - local DetectedAreas = self.DetectedAreas - return DetectedAreas -end - ---- Get the amount of @{#DETECTION_AREAS.DetectedAreas}. --- @param #DETECTION_AREAS self --- @return #number DetectedAreaCount -function DETECTION_AREAS:GetDetectedAreaCount() - - local DetectedAreaCount = self.DetectedAreas.n - return DetectedAreaCount -end - ---- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. --- @param #DETECTION_AREAS self --- @param #number Index --- @return Core.Set#SET_UNIT DetectedSet -function DETECTION_AREAS:GetDetectedSet( Index ) - - local DetectedSetUnit = self.DetectedAreas[Index].Set - if DetectedSetUnit then - return DetectedSetUnit - end - - return nil -end - ---- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. --- @param #DETECTION_AREAS self --- @param #number Index --- @return Core.Zone#ZONE_UNIT DetectedZone -function DETECTION_AREAS:GetDetectedZone( Index ) - - local DetectedZone = self.DetectedAreas[Index].Zone - if DetectedZone then - return DetectedZone - end - - return nil -end - ---- Background worker function to determine if there are friendlies nearby ... --- @param #DETECTION_AREAS self --- @param Wrapper.Unit#UNIT ReportUnit -function DETECTION_AREAS:ReportFriendliesNearBy( ReportGroupData ) - self:F2() - - local DetectedArea = ReportGroupData.DetectedArea -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = ReportGroupData.DetectedArea.Set - local DetectedZone = ReportGroupData.DetectedArea.Zone - local DetectedZoneUnit = DetectedZone.ZoneUNIT - - DetectedArea.FriendliesNearBy = false - - local SphereSearch = { - id = world.VolumeType.SPHERE, - params = { - point = DetectedZoneUnit:GetVec3(), - radius = 6000, - } - - } - - --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit - -- @param Wrapper.Group#GROUP ReportGroup - -- @param Set#SET_GROUP ReportSetGroup - local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) - - local DetectedArea = ReportGroupData.DetectedArea -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = ReportGroupData.DetectedArea.Set - local DetectedZone = ReportGroupData.DetectedArea.Zone - local DetectedZoneUnit = DetectedZone.ZoneUNIT -- Wrapper.Unit#UNIT - local ReportSetGroup = ReportGroupData.ReportSetGroup - - local EnemyCoalition = DetectedZoneUnit:GetCoalition() - - local FoundUnitCoalition = FoundDCSUnit:getCoalition() - local FoundUnitName = FoundDCSUnit:getName() - local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() - local EnemyUnitName = DetectedZoneUnit:GetName() - local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil - - self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) - - if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then - DetectedArea.FriendliesNearBy = true - return false - end - - return true - end - - world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) - -end - - - ---- Returns if there are friendlies nearby the FAC units ... --- @param #DETECTION_AREAS self --- @return #boolean trhe if there are friendlies nearby -function DETECTION_AREAS:IsFriendliesNearBy( DetectedArea ) - - self:T3( DetectedArea.FriendliesNearBy ) - return DetectedArea.FriendliesNearBy or false -end - ---- Calculate the maxium A2G threat level of the DetectedArea. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea -function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedArea ) - - local MaxThreatLevelA2G = 0 - for UnitName, UnitData in pairs( DetectedArea.Set:GetSet() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - local ThreatLevelA2G = ThreatUnit:GetThreatLevel() - if ThreatLevelA2G > MaxThreatLevelA2G then - MaxThreatLevelA2G = ThreatLevelA2G - end - end - - self:T3( MaxThreatLevelA2G ) - DetectedArea.MaxThreatLevelA2G = MaxThreatLevelA2G - -end - ---- Find the nearest FAC of the DetectedArea. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return Wrapper.Unit#UNIT The nearest FAC unit -function DETECTION_AREAS:NearestFAC( DetectedArea ) - - local NearestFAC = nil - local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) - - for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do - local FACUnit = FACUnitData -- Wrapper.Unit#UNIT - if FACUnit:IsActive() then - local Vec3 = FACUnit:GetVec3() - local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) - local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) - if Distance < MinDistance then - MinDistance = Distance - NearestFAC = FACUnit - end - end - end - end - - DetectedArea.NearestFAC = NearestFAC - -end - ---- Returns the A2G threat level of the units in the DetectedArea --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #number a scale from 0 to 10. -function DETECTION_AREAS:GetTreatLevelA2G( DetectedArea ) - - self:T3( DetectedArea.MaxThreatLevelA2G ) - return DetectedArea.MaxThreatLevelA2G -end - - - ---- Smoke the detected units --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:SmokeDetectedUnits() - self:F2() - - self._SmokeDetectedUnits = true - return self -end - ---- Flare the detected units --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:FlareDetectedUnits() - self:F2() - - self._FlareDetectedUnits = true - return self -end - ---- Smoke the detected zones --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:SmokeDetectedZones() - self:F2() - - self._SmokeDetectedZones = true - return self -end - ---- Flare the detected zones --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:FlareDetectedZones() - self:F2() - - self._FlareDetectedZones = true - return self -end - ---- Add a change to the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @param #string ChangeCode --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AddChangeArea( DetectedArea, ChangeCode, AreaUnitType ) - - DetectedArea.Changed = true - local AreaID = DetectedArea.AreaID - - DetectedArea.Changes = DetectedArea.Changes or {} - DetectedArea.Changes[ChangeCode] = DetectedArea.Changes[ChangeCode] or {} - DetectedArea.Changes[ChangeCode].AreaID = AreaID - DetectedArea.Changes[ChangeCode].AreaUnitType = AreaUnitType - - self:T( { "Change on Detection Area:", DetectedArea.AreaID, ChangeCode, AreaUnitType } ) - - return self -end - - ---- Add a change to the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @param #string ChangeCode --- @param #string ChangeUnitType --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AddChangeUnit( DetectedArea, ChangeCode, ChangeUnitType ) - - DetectedArea.Changed = true - local AreaID = DetectedArea.AreaID - - DetectedArea.Changes = DetectedArea.Changes or {} - DetectedArea.Changes[ChangeCode] = DetectedArea.Changes[ChangeCode] or {} - DetectedArea.Changes[ChangeCode][ChangeUnitType] = DetectedArea.Changes[ChangeCode][ChangeUnitType] or 0 - DetectedArea.Changes[ChangeCode][ChangeUnitType] = DetectedArea.Changes[ChangeCode][ChangeUnitType] + 1 - DetectedArea.Changes[ChangeCode].AreaID = AreaID - - self:T( { "Change on Detection Area:", DetectedArea.AreaID, ChangeCode, ChangeUnitType } ) - - return self -end - ---- Make text documenting the changes of the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #string The Changes text -function DETECTION_AREAS:GetChangeText( DetectedArea ) - self:F( DetectedArea ) - - local MT = {} - - for ChangeCode, ChangeData in pairs( DetectedArea.Changes ) do - - if ChangeCode == "AA" then - MT[#MT+1] = "Detected new area " .. ChangeData.AreaID .. ". The center target is a " .. ChangeData.AreaUnitType .. "." - end - - if ChangeCode == "RAU" then - MT[#MT+1] = "Changed area " .. ChangeData.AreaID .. ". Removed the center target." - end - - if ChangeCode == "AAU" then - MT[#MT+1] = "Changed area " .. ChangeData.AreaID .. ". The new center target is a " .. ChangeData.AreaUnitType "." - end - - if ChangeCode == "RA" then - MT[#MT+1] = "Removed old area " .. ChangeData.AreaID .. ". No more targets in this area." - end - - if ChangeCode == "AU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "AreaID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Detected for area " .. ChangeData.AreaID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - if ChangeCode == "RU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "AreaID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Removed for area " .. ChangeData.AreaID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - end - - return table.concat( MT, "\n" ) - -end - - ---- Accepts changes from the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AcceptChanges( DetectedArea ) - - DetectedArea.Changed = false - DetectedArea.Changes = {} - - return self -end - - ---- Make a DetectionSet table. This function will be overridden in the derived clsses. --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:CreateDetectionSets() - self:F2() - - -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. - -- Regroup when needed, split groups when needed. - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do - - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - if DetectedArea then - - local DetectedSet = DetectedArea.Set - - local AreaExists = false -- This flag will determine of the detected area is still existing. - - -- First test if the center unit is detected in the detection area. - self:T3( DetectedArea.Zone.ZoneUNIT.UnitName ) - local DetectedZoneObject = self:GetDetectedObject( DetectedArea.Zone.ZoneUNIT.UnitName ) - self:T3( { "Detecting Zone Object", DetectedArea.AreaID, DetectedArea.Zone, DetectedZoneObject } ) - - if DetectedZoneObject then - - --self:IdentifyDetectedObject( DetectedZoneObject ) - AreaExists = true - - - - else - -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. - -- First remove the center unit from the set. - DetectedSet:RemoveUnitsByName( DetectedArea.Zone.ZoneUNIT.UnitName ) - - self:AddChangeArea( DetectedArea, 'RAU', "Dummy" ) + local DetectedSet = DetectedItem.Set - -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) - - -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. - -- If the DetectedUnit was already identified, DetectedObject will be nil. - if DetectedObject then - self:IdentifyDetectedObject( DetectedObject ) - AreaExists = true - - -- Assign the Unit as the new center unit of the detected area. - DetectedArea.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) - - self:AddChangeArea( DetectedArea, "AAU", DetectedArea.Zone.ZoneUNIT:GetTypeName() ) - - -- We don't need to add the DetectedObject to the area set, because it is already there ... - break - end - end - end - - -- Now we've determined the center unit of the area, now we can iterate the units in the detected area. - -- Note that the position of the area may have moved due to the center unit repositioning. - -- If no center unit was identified, then the detected area does not exist anymore and should be deleted, as there are no valid units that can be the center unit. - if AreaExists then - - -- ok, we found the center unit of the area, now iterate through the detected area set and see which units are still within the center unit zone ... - -- Those units within the zone are flagged as Identified. - -- If a unit was not found in the set, remove it from the set. This may be added later to other existing or new sets. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = nil - if DetectedUnit:IsAlive() then - --self:E(DetectedUnit:GetName()) - DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) - end - if DetectedObject then - - -- Check if the DetectedUnit is within the DetectedArea.Zone - if DetectedUnit:IsInZone( DetectedArea.Zone ) then + local AreaExists = false -- This flag will determine of the detected area is still existing. - -- Yes, the DetectedUnit is within the DetectedArea.Zone, no changes, DetectedUnit can be kept within the Set. - self:IdentifyDetectedObject( DetectedObject ) - - else - -- No, the DetectedUnit is not within the DetectedArea.Zone, remove DetectedUnit from the Set. - DetectedSet:Remove( DetectedUnitName ) - self:AddChangeUnit( DetectedArea, "RU", DetectedUnit:GetTypeName() ) - end - - else - -- There was no DetectedObject, remove DetectedUnit from the Set. - self:AddChangeUnit( DetectedArea, "RU", "destroyed target" ) - DetectedSet:Remove( DetectedUnitName ) - - -- The DetectedObject has been identified, because it does not exist ... - -- self:IdentifyDetectedObject( DetectedObject ) - end - end - else - self:RemoveDetectedArea( DetectedAreaID ) - self:AddChangeArea( DetectedArea, "RA" ) - end - end - end - - -- We iterated through the existing detection areas and: - -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. - -- - We recentered the detection area to new center units where it was needed. - -- - -- Now we need to loop through the unidentified detected units and see where they belong: - -- - They can be added to a new detection area and become the new center unit. - -- - They can be added to a new detection area. - for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - - local DetectedObject = self:GetDetectedObject( DetectedUnitName ) - - if DetectedObject then - - -- We found an unidentified unit outside of any existing detection area. - local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT - - local AddedToDetectionArea = false - - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do + -- First test if the center unit is detected in the detection area. + self:T3( { "Zone Center Unit:", DetectedItem.Zone.ZoneUNIT.UnitName } ) + local DetectedZoneObject = self:GetDetectedObject( DetectedItem.Zone.ZoneUNIT.UnitName ) + self:T3( { "Detected Zone Object:", DetectedItem.Zone:GetName(), DetectedZoneObject } ) - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - if DetectedArea then - self:T( "Detection Area #" .. DetectedArea.AreaID ) - local DetectedSet = DetectedArea.Set - if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedArea.Zone ) then - self:IdentifyDetectedObject( DetectedObject ) - DetectedSet:AddUnit( DetectedUnit ) - AddedToDetectionArea = true - self:AddChangeUnit( DetectedArea, "AU", DetectedUnit:GetTypeName() ) + if DetectedZoneObject then + + --self:IdentifyDetectedObject( DetectedZoneObject ) + AreaExists = true + + + + else + -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. + -- First remove the center unit from the set. + DetectedSet:RemoveUnitsByName( DetectedItem.Zone.ZoneUNIT.UnitName ) + + self:AddChangeItem( DetectedItem, 'RAU', "Dummy" ) + + -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. + for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do + + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) + + -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. + -- If the DetectedUnit was already identified, DetectedObject will be nil. + if DetectedObject then + self:IdentifyDetectedObject( DetectedObject ) + AreaExists = true + + DetectedItem.Zone:BoundZone( 12, self.CountryID, true) + + -- Assign the Unit as the new center unit of the detected area. + DetectedItem.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) + + self:AddChangeItem( DetectedItem, "AAU", DetectedItem.Zone.ZoneUNIT:GetTypeName() ) + + -- We don't need to add the DetectedObject to the area set, because it is already there ... + break + end end end + + -- Now we've determined the center unit of the area, now we can iterate the units in the detected area. + -- Note that the position of the area may have moved due to the center unit repositioning. + -- If no center unit was identified, then the detected area does not exist anymore and should be deleted, as there are no valid units that can be the center unit. + if AreaExists then + + -- ok, we found the center unit of the area, now iterate through the detected area set and see which units are still within the center unit zone ... + -- Those units within the zone are flagged as Identified. + -- If a unit was not found in the set, remove it from the set. This may be added later to other existing or new sets. + for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do + + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + local DetectedObject = nil + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Check if the DetectedUnit is within the DetectedItem.Zone + if DetectedUnit:IsInZone( DetectedItem.Zone ) then + + -- Yes, the DetectedUnit is within the DetectedItem.Zone, no changes, DetectedUnit can be kept within the Set. + self:IdentifyDetectedObject( DetectedObject ) + + else + -- No, the DetectedUnit is not within the DetectedItem.Zone, remove DetectedUnit from the Set. + DetectedSet:Remove( DetectedUnitName ) + self:AddChangeUnit( DetectedItem, "RU", DetectedUnit:GetTypeName() ) + end + + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", "destroyed target" ) + DetectedSet:Remove( DetectedUnitName ) + + -- The DetectedObject has been identified, because it does not exist ... + -- self:IdentifyDetectedObject( DetectedObject ) + end + end + else + DetectedItem.Zone:BoundZone( 12, self.CountryID, true) + self:RemoveDetectedItem( DetectedItemID ) + self:AddChangeItem( DetectedItem, "RA" ) + end end + end - if AddedToDetectionArea == false then + -- We iterated through the existing detection areas and: + -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. + -- - We recentered the detection area to new center units where it was needed. + -- + -- Now we need to loop through the unidentified detected units and see where they belong: + -- - They can be added to a new detection area and become the new center unit. + -- - They can be added to a new detection area. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - -- New detection area - local DetectedArea = self:AddDetectedArea( - SET_UNIT:New(), - ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) - ) - --self:E( DetectedArea.Zone.ZoneUNIT.UnitName ) - DetectedArea.Set:AddUnit( DetectedUnit ) - self:AddChangeArea( DetectedArea, "AA", DetectedUnit:GetTypeName() ) - end - end - end + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + + if DetectedObject then - -- Now all the tests should have been build, now make some smoke and flares... - -- We also report here the friendlies within the detected areas. - - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do - - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone - - self:ReportFriendliesNearBy( { DetectedArea = DetectedArea, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table - self:CalculateThreatLevelA2G( DetectedArea ) -- Calculate A2G threat level - self:NearestFAC( DetectedArea ) - - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedZone.ZoneUNIT:SmokeRed() - end - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit ) - if DetectedUnit:IsAlive() then - self:T( "Detected Set #" .. DetectedArea.AreaID .. ":" .. DetectedUnit:GetName() ) - if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then - DetectedUnit:FlareGreen() - end - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedUnit:SmokeGreen() + -- We found an unidentified unit outside of any existing detection area. + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + local AddedToDetectionArea = false + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + if DetectedItem then + self:T( "Detection Area #" .. DetectedItem.ItemID ) + local DetectedSet = DetectedItem.Set + if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedItem.Zone ) then + self:IdentifyDetectedObject( DetectedObject ) + DetectedSet:AddUnit( DetectedUnit ) + AddedToDetectionArea = true + self:AddChangeUnit( DetectedItem, "AU", DetectedUnit:GetTypeName() ) + end end end + + if AddedToDetectionArea == false then + + -- New detection area + local DetectedItem = self:AddDetectedItemZone( nil, + SET_UNIT:New(), + ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) + ) + --self:E( DetectedItem.Zone.ZoneUNIT.UnitName ) + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeItem( DetectedItem, "AA", DetectedUnit:GetTypeName() ) + end end - ) - if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then - DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) end - if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then - DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) + + -- Now all the tests should have been build, now make some smoke and flares... + -- We also report here the friendlies within the detected areas. + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + self:CalculateThreatLevelA2G( DetectedItem ) -- Calculate A2G threat level + self:NearestFAC( DetectedItem ) + + if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then + DetectedZone.ZoneUNIT:SmokeRed() + end + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit ) + if DetectedUnit:IsAlive() then + self:T( "Detected Set #" .. DetectedItem.ItemID .. ":" .. DetectedUnit:GetName() ) + if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then + DetectedUnit:FlareGreen() + end + if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then + DetectedUnit:SmokeGreen() + end + end + end + ) + if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then + DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) + end + if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then + DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) + end + + if DETECTION_AREAS._BoundDetectedZones or self._BoundDetectedZones then + DetectedZone:BoundZone( 12, self.CountryID ) + end end + end - -end - - + +end --- Single-Player:**No** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** -- **AI Balancing will replace in multi player missions -- non-occupied human slots with AI groups, in order to provide an engaging simulation environment, -- even when there are hardly any players in the mission.** @@ -26713,14 +28519,73 @@ end ---- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Air** -- --- **Air Patrolling or Staging.** +--- **AI** -- **Air Patrolling or Staging.** -- -- ![Banner Image](..\Presentations\AI_PATROL\Dia1.JPG) -- -- === -- --- # 1) @{#AI_PATROL_ZONE} class, extends @{Fsm#FSM_CONTROLLABLE} +-- AI PATROL classes makes AI Controllables execute an Patrol. +-- +-- There are the following types of PATROL classes defined: +-- +-- * @{#AI_PATROL_ZONE}: Perform a PATROL in a zone. +-- +-- ==== +-- +-- # **OPEN ISSUES** +-- +-- 2017-01-17: When Spawned AI is located at an airbase, it will be routed first back to the airbase after take-off. +-- +-- 2016-01-17: +-- -- Fixed problem with AI returning to base too early and unexpected. +-- -- ReSpawning of AI will reset the AI_PATROL and derived classes. +-- -- Checked the correct workings of SCHEDULER, and it DOES work correctly. +-- +-- ==== +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-01-17: Rename of class: **AI\_PATROL\_ZONE** is the new name for the old _AI\_PATROLZONE_. +-- +-- 2017-01-15: Complete revision. AI_PATROL_ZONE is the base class for other AI_PATROL like classes. +-- +-- 2016-09-01: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review. +-- +-- ### Authors: +-- +-- * **FlightControl**: Design & Programming. +-- +-- @module AI_Patrol + +--- AI_PATROL_ZONE class +-- @type AI_PATROL_ZONE +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. +-- @field Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @field Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. +-- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @field Functional.Spawn#SPAWN CoordTest +-- @extends Core.Fsm#FSM_CONTROLLABLE + +--- # 1) @{#AI_PATROL_ZONE} class, extends @{Fsm#FSM_CONTROLLABLE} -- -- The @{#AI_PATROL_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}. -- @@ -26821,59 +28686,10 @@ end -- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB). -- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this proces in place. -- --- ==== --- --- # **OPEN ISSUES** --- --- 2017-01-17: When Spawned AI is located at an airbase, it will be routed first back to the airbase after take-off. --- --- 2016-01-17: --- -- Fixed problem with AI returning to base too early and unexpected. --- -- ReSpawning of AI will reset the AI_PATROL and derived classes. --- -- Checked the correct workings of SCHEDULER, and it DOES work correctly. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-17: Rename of class: **AI\_PATROL\_ZONE** is the new name for the old _AI\_PATROLZONE_. --- --- 2017-01-15: Complete revision. AI_PATROL_ZONE is the base class for other AI_PATROL like classes. --- --- 2016-09-01: Initial class and API. --- -- === -- --- # **AUTHORS and CONTRIBUTIONS** +-- @field #AI_PATROL_ZONE AI_PATROL_ZONE -- --- ### Contributions: --- --- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review. --- --- ### Authors: --- --- * **FlightControl**: Design & Programming. --- --- @module AI_Patrol - ---- AI_PATROL_ZONE class --- @type AI_PATROL_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @field Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @field Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. --- @field Functional.Spawn#SPAWN CoordTest --- @extends Core.Fsm#FSM_CONTROLLABLE AI_PATROL_ZONE = { ClassName = "AI_PATROL_ZONE", } @@ -27652,14 +29468,55 @@ function AI_PATROL_ZONE:OnPilotDead( EventData ) self:__PilotDead( 1, EventData ) end end ---- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Air** -- --- **Provide Close Air Support to friendly ground troops.** +--- **AI** -- **Provide Close Air Support to friendly ground troops.** -- -- ![Banner Image](..\Presentations\AI_CAS\Dia1.JPG) -- -- === +-- +-- AI CAS classes makes AI Controllables execute a Close Air Support. +-- +-- There are the following types of CAS classes defined: +-- +-- * @{#AI_CAS_ZONE}: Perform a CAS in a zone. +-- +-- === -- --- # 1) @{#AI_CAS_ZONE} class, extends @{AI_Patrol#AI_PATROL_ZONE} +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-01-15: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. +-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module AI_Cas + + +--- AI_CAS_ZONE class +-- @type AI_CAS_ZONE +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. +-- @extends AI.AI_Patrol#AI_PATROL_ZONE + +--- # 1) @{#AI_CAS_ZONE} class, extends @{AI_Patrol#AI_PATROL_ZONE} -- -- @{#AI_CAS_ZONE} derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. -- @@ -27744,42 +29601,11 @@ end -- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Unit}. -- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Unit}s assigned in the CAS task. -- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-15: Initial class and API. --- +-- -- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. --- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module AI_Cas - - ---- AI_CAS_ZONE class --- @type AI_CAS_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. --- @extends AI.AI_Patrol#AI_PATROL_ZONE +-- +-- @field #AI_CAS_ZONE AI_CAS_ZONE +-- AI_CAS_ZONE = { ClassName = "AI_CAS_ZONE", } @@ -27815,11 +29641,6 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. - -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. - -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. - -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. - -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @return #boolean Return false to cancel Transition. @@ -27830,18 +29651,15 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. - -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. - -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. - -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. - -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- Synchronous Event Trigger for Event Engage. -- @function [parent=#AI_CAS_ZONE] Engage -- @param #AI_CAS_ZONE self -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. + -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. + -- If parameter is not defined the unit / controllable will choose expend on its own discretion. + -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. @@ -27850,8 +29668,10 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #AI_CAS_ZONE self -- @param #number Delay The delay in seconds. -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. + -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. + -- If parameter is not defined the unit / controllable will choose expend on its own discretion. + -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. @@ -28062,7 +29882,7 @@ function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To ) if Detected == true then self:E( {"Target: ", DetectedUnit } ) self.DetectedUnits[DetectedUnit] = false - local AttackTask = Controllable:EnRouteTaskEngageUnit( DetectedUnit, 1, true, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil ) + local AttackTask = Controllable:TaskAttackUnit( DetectedUnit, false, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil ) self.Controllable:PushTask( AttackTask, 1 ) end end @@ -28093,8 +29913,8 @@ end -- @param #string Event The Event string. -- @param #string To The To State string. -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. +-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, @@ -28112,7 +29932,6 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, self.EngageDirection = EngageDirection if Controllable:IsAlive() then - local EngageRoute = {} @@ -28133,29 +29952,28 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, EngageRoute[#EngageRoute+1] = CurrentRoutePoint - - if self.Controllable:IsNotInZone( self.EngageZone ) then - - -- Find a random 2D point in EngageZone. - local ToEngageZoneVec2 = self.EngageZone:GetRandomVec2() - self:T2( ToEngageZoneVec2 ) - - -- Obtain a 3D @{Point} from the 2D point + altitude. - local ToEngageZonePointVec3 = POINT_VEC3:New( ToEngageZoneVec2.x, self.EngageAltitude, ToEngageZoneVec2.y ) - - -- Create a route point of type air. - local ToEngageZoneRoutePoint = ToEngageZonePointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - self.EngageSpeed, - true - ) - - EngageRoute[#EngageRoute+1] = ToEngageZoneRoutePoint + local AttackTasks = {} + for DetectedUnitID, DetectedUnit in pairs( self.DetectedUnits ) do + local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT + self:T( DetectedUnit ) + if DetectedUnit:IsAlive() then + if DetectedUnit:IsInZone( self.EngageZone ) then + self:E( {"Engaging ", DetectedUnit } ) + AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit, + true, + EngageWeaponExpend, + EngageAttackQty, + EngageDirection + ) + end + else + self.DetectedUnits[DetectedUnit] = nil + end end - + + EngageRoute[1].task = Controllable:TaskCombo( AttackTasks ) + --- Define a random point in the @{Zone}. The AI will fly to that point within the zone. --- Find a random 2D point in EngageZone. @@ -28174,45 +29992,25 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, true ) - --ToTargetPointVec3:SmokeBlue() - EngageRoute[#EngageRoute+1] = ToTargetRoutePoint + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + Controllable:WayPointInitialize( EngageRoute ) + --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... + Controllable:SetState( Controllable, "EngageZone", self ) + + Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" ) + + --- NOW ROUTE THE GROUP! + Controllable:WayPointExecute( 1 ) Controllable:OptionROEOpenFire() Controllable:OptionROTVertical() - --- local AttackTasks = {} --- --- for DetectedUnitID, DetectedUnit in pairs( self.DetectedUnits ) do --- local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT --- self:T( DetectedUnit ) --- if DetectedUnit:IsAlive() then --- if DetectedUnit:IsInZone( self.EngageZone ) then --- self:E( {"Engaging ", DetectedUnit } ) --- AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) --- end --- else --- self.DetectedUnits[DetectedUnit] = nil --- end --- end --- --- EngageRoute[1].task = Controllable:TaskCombo( AttackTasks ) - - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - self.Controllable:WayPointInitialize( EngageRoute ) - --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... - self.Controllable:SetState( self.Controllable, "EngageZone", self ) - - self.Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" ) - - --- NOW ROUTE THE GROUP! - self.Controllable:WayPointExecute( 1 ) - - self:SetDetectionInterval( 10 ) + self:SetDetectionInterval( 2 ) self:SetDetectionActivated() - self:__Target( -10 ) -- Start Targetting + self:__Target( -2 ) -- Start Targetting end end @@ -28255,13 +30053,57 @@ function AI_CAS_ZONE:OnEventDead( EventData ) end ---- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Air** -- **Execute Combat Air Patrol (CAP).** +--- **AI** - **Execute Combat Air Patrol (CAP).** -- -- ![Banner Image](..\Presentations\AI_CAP\Dia1.JPG) -- -- === +-- +-- AI CAP classes makes AI Controllables execute a Combat Air Patrol. +-- +-- There are the following types of CAP classes defined: +-- +-- * @{#AI_CAP_ZONE}: Perform a CAP in a zone. +-- +-- ==== -- --- # 1) @{#AI_CAP_ZONE} class, extends @{AI_CAP#AI_PATROL_ZONE} +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-01-15: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. +-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. +-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. +-- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing. +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module AI_Cap + + +--- @type AI_CAP_ZONE +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. +-- @extends AI.AI_Patrol#AI_PATROL_ZONE + + +--- # 1) @{#AI_CAP_ZONE} class, extends @{AI_CAP#AI_PATROL_ZONE} -- -- The @{#AI_CAP_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group} -- and automatically engage any airborne enemies that are within a certain range or within a certain zone. @@ -28339,44 +30181,11 @@ end -- An optional @{Zone} can be set, -- that will define when the AI will engage with the detected airborne enemy targets. -- Use the method @{AI_Cap#AI_CAP_ZONE.SetEngageZone}() to define that Zone. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-15: Initial class and API. --- +-- -- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. --- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. --- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. --- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module AI_Cap - - ---- AI_CAP_ZONE class --- @type AI_CAP_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. --- @extends AI.AI_Patrol#AI_PATROL_ZONE +-- +-- @field #AI_CAP_ZONE AI_CAP_ZONE +-- AI_CAP_ZONE = { ClassName = "AI_CAP_ZONE", } @@ -30014,7 +31823,7 @@ do -- ACT_ASSIGN_ACCEPT self:Message( "You are assigned to the task " .. self.Task:GetName() ) - self.Task:Assign() + self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() ) end end -- ACT_ASSIGN_ACCEPT @@ -30213,7 +32022,7 @@ do -- ACT_ROUTE -- @type ACT_ROUTE -- @field Tasking.Task#TASK TASK -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone + -- @field Core.Zone#ZONE_BASE Zone -- @extends Core.Fsm#FSM_PROCESS ACT_ROUTE = { ClassName = "ACT_ROUTE", @@ -30309,6 +32118,115 @@ do -- ACT_ROUTE end -- ACT_ROUTE +do -- ACT_ROUTE_POINT + + --- ACT_ROUTE_POINT class + -- @type ACT_ROUTE_POINT + -- @field Tasking.Task#TASK TASK + -- @extends #ACT_ROUTE + ACT_ROUTE_POINT = { + ClassName = "ACT_ROUTE_POINT", + } + + + --- Creates a new routing state machine. + -- The task will route a controllable to a PointVec2 until the controllable is within the Range. + -- @param #ACT_ROUTE_POINT self + -- @param Core.Point#POINT_VEC2 The PointVec2 to Target. + -- @param #number Range The Distance to Target. + -- @param Core.Zone#ZONE_BASE Zone + function ACT_ROUTE_POINT:New( PointVec2, Range ) + local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_POINT + + self.PointVec2 = PointVec2 + self.Range = Range or 0 + + self.DisplayInterval = 30 + self.DisplayCount = 30 + self.DisplayMessage = true + self.DisplayTime = 10 -- 10 seconds is the default + + return self + end + + function ACT_ROUTE_POINT:Init( FsmRoute ) + + self.PointVec2 = FsmRoute.PointVec2 + self.Range = FsmRoute.Range or 0 + + self.DisplayInterval = 30 + self.DisplayCount = 30 + self.DisplayMessage = true + self.DisplayTime = 10 -- 10 seconds is the default + end + + --- Set PointVec2 + -- @param #ACT_ROUTE_POINT self + -- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to route to. + function ACT_ROUTE_POINT:SetPointVec2( PointVec2 ) + self:F2( { PointVec2 } ) + self.PointVec2 = PointVec2 + end + + --- Get PointVec2 + -- @param #ACT_ROUTE_POINT self + -- @return Core.Point#POINT_VEC2 PointVec2 The PointVec2 to route to. + function ACT_ROUTE_POINT:GetPointVec2() + self:F2( { self.PointVec2 } ) + return self.PointVec2 + end + + --- Set Range around PointVec2 + -- @param #ACT_ROUTE_POINT self + -- @param #number Range The Range to consider the arrival. Default is 10000 meters. + function ACT_ROUTE_POINT:SetRange( Range ) + self:F2( { self.Range } ) + self.Range = Range or 10000 + end + + --- Get Range around PointVec2 + -- @param #ACT_ROUTE_POINT self + -- @return #number The Range to consider the arrival. Default is 10000 meters. + function ACT_ROUTE_POINT:GetRange() + return self.Range + end + + --- Method override to check if the controllable has arrived. + -- @param #ACT_ROUTE_POINT self + -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit + -- @return #boolean + function ACT_ROUTE_POINT:onfuncHasArrived( ProcessUnit ) + + if ProcessUnit:IsAlive() then + local Distance = self.PointVec2:Get2DDistance( ProcessUnit:GetPointVec2() ) + + if Distance <= self.Range then + local RouteText = "You have arrived." + self:Message( RouteText ) + return true + end + end + + return false + end + + --- Task Events + + --- StateMachine callback function + -- @param #ACT_ROUTE_POINT self + -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit + -- @param #string Event + -- @param #string From + -- @param #string To + function ACT_ROUTE_POINT:onenterReporting( ProcessUnit, From, Event, To ) + + local TaskUnitPointVec2 = ProcessUnit:GetPointVec2() + local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( self.PointVec2 ) .. " km." + self:Message( RouteText ) + end + +end -- ACT_ROUTE_POINT + do -- ACT_ROUTE_ZONE @@ -30316,7 +32234,7 @@ do -- ACT_ROUTE_ZONE -- @type ACT_ROUTE_ZONE -- @field Tasking.Task#TASK TASK -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone + -- @field Core.Zone#ZONE_BASE Zone -- @extends #ACT_ROUTE ACT_ROUTE_ZONE = { ClassName = "ACT_ROUTE_ZONE", @@ -30325,11 +32243,11 @@ do -- ACT_ROUTE_ZONE --- Creates a new routing state machine. The task will route a controllable to a ZONE until the controllable is within that ZONE. -- @param #ACT_ROUTE_ZONE self - -- @param Core.Zone#ZONE_BASE TargetZone - function ACT_ROUTE_ZONE:New( TargetZone ) + -- @param Core.Zone#ZONE_BASE Zone + function ACT_ROUTE_ZONE:New( Zone ) local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE - self.TargetZone = TargetZone + self.Zone = Zone self.DisplayInterval = 30 self.DisplayCount = 30 @@ -30341,7 +32259,7 @@ do -- ACT_ROUTE_ZONE function ACT_ROUTE_ZONE:Init( FsmRoute ) - self.TargetZone = FsmRoute.TargetZone + self.Zone = FsmRoute.Zone self.DisplayInterval = 30 self.DisplayCount = 30 @@ -30349,18 +32267,32 @@ do -- ACT_ROUTE_ZONE self.DisplayTime = 10 -- 10 seconds is the default end + --- Set Zone + -- @param #ACT_ROUTE_ZONE self + -- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to. + function ACT_ROUTE_ZONE:SetZone( Zone ) + self.Zone = Zone + end + + --- Get Zone + -- @param #ACT_ROUTE_ZONE self + -- @return Core.Zone#ZONE_BASE Zone The Zone object where to route to. + function ACT_ROUTE_ZONE:GetZone() + return self.Zone + end + --- Method override to check if the controllable has arrived. -- @param #ACT_ROUTE self -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit -- @return #boolean function ACT_ROUTE_ZONE:onfuncHasArrived( ProcessUnit ) - if ProcessUnit:IsInZone( self.TargetZone ) then + if ProcessUnit:IsInZone( self.Zone ) then local RouteText = "You have arrived within the zone." self:Message( RouteText ) end - return ProcessUnit:IsInZone( self.TargetZone ) + return ProcessUnit:IsInZone( self.Zone ) end --- Task Events @@ -30373,83 +32305,18 @@ do -- ACT_ROUTE_ZONE -- @param #string To function ACT_ROUTE_ZONE:onenterReporting( ProcessUnit, From, Event, To ) - local ZoneVec2 = self.TargetZone:GetVec2() + local ZoneVec2 = self.Zone:GetVec2() local ZonePointVec2 = POINT_VEC2:New( ZoneVec2.x, ZoneVec2.y ) local TaskUnitVec2 = ProcessUnit:GetVec2() local TaskUnitPointVec2 = POINT_VEC2:New( TaskUnitVec2.x, TaskUnitVec2.y ) - local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( ZonePointVec2 ) .. " km to target." + local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( ZonePointVec2 ) .. " km." self:Message( RouteText ) end end -- ACT_ROUTE_ZONE ---- (SP) (MP) (FSM) Account for (Detect, count and report) DCS events occuring on DCS objects (units). +--- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occuring on @{Unit}s. -- --- === --- --- # @{#ACT_ACCOUNT} FSM class, extends @{Fsm#FSM_PROCESS} --- --- ## ACT_ACCOUNT state machine: --- --- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. --- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. --- Each derived class follows exactly the same process, using the same events and following the same state transitions, --- but will have **different implementation behaviour** upon each event or state transition. --- --- ### ACT_ACCOUNT **Events**: --- --- These are the events defined in this class: --- --- * **Start**: The process is started. The process will go into the Report state. --- * **Event**: A relevant event has occured that needs to be accounted for. The process will go into the Account state. --- * **Report**: The process is reporting to the player the accounting status of the DCS events. --- * **More**: There are more DCS events that need to be accounted for. The process will go back into the Report state. --- * **NoMore**: There are no more DCS events that need to be accounted for. The process will go into the Success state. --- --- ### ACT_ACCOUNT **Event methods**: --- --- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process. --- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine: --- --- * **Immediate**: The event method has exactly the name of the event. --- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. --- --- ### ACT_ACCOUNT **States**: --- --- * **Assigned**: The player is assigned to the task. This is the initialization state for the process. --- * **Waiting**: the process is waiting for a DCS event to occur within the simulator. This state is set automatically. --- * **Report**: The process is Reporting to the players in the group of the unit. This state is set automatically every 30 seconds. --- * **Account**: The relevant DCS event has occurred, and is accounted for. --- * **Success (*)**: All DCS events were accounted for. --- * **Failed (*)**: The process has failed. --- --- (*) End states of the process. --- --- ### ACT_ACCOUNT state transition methods: --- --- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. --- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Before** the state transition. --- The state transition method needs to start with the name **OnBefore + the name of the state**. --- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, --- but then you'll need to specify your own logic using the AIControllable! --- --- * **After** the state transition. --- The state transition method needs to start with the name **OnAfter + the name of the state**. --- These state transition methods need to provide a return value, which is specified at the function description. --- --- # 1) @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Fsm.Account#ACT_ACCOUNT} --- --- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units. --- The process is given a @{Set} of units that will be tracked upon successful destruction. --- The process will end after each target has been successfully destroyed. --- Each successful dead will trigger an Account state transition that can be scored, modified or administered. --- --- --- ## ACT_ACCOUNT_DEADS constructor: --- --- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object. +-- ![Banner Image](..\Presentations\ACT_ACCOUNT\Dia1.JPG) -- -- === -- @@ -30458,7 +32325,51 @@ end -- ACT_ROUTE_ZONE do -- ACT_ACCOUNT - --- ACT_ACCOUNT class + --- # @{#ACT_ACCOUNT} FSM class, extends @{Fsm#FSM_PROCESS} + -- + -- ## ACT_ACCOUNT state machine: + -- + -- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. + -- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. + -- Each derived class follows exactly the same process, using the same events and following the same state transitions, + -- but will have **different implementation behaviour** upon each event or state transition. + -- + -- ### ACT_ACCOUNT States + -- + -- * **Asigned**: The player is assigned. + -- * **Waiting**: Waiting for an event. + -- * **Report**: Reporting. + -- * **Account**: Account for an event. + -- * **Accounted**: All events have been accounted for, end of the process. + -- * **Failed**: Failed the process. + -- + -- ### ACT_ACCOUNT Events + -- + -- * **Start**: Start the process. + -- * **Wait**: Wait for an event. + -- * **Report**: Report the status of the accounting. + -- * **Event**: An event happened, process the event. + -- * **More**: More targets. + -- * **NoMore (*)**: No more targets. + -- * **Fail (*)**: The action process has failed. + -- + -- (*) End states of the process. + -- + -- ### ACT_ACCOUNT state transition methods: + -- + -- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. + -- There are 2 moments when state transition methods will be called by the state machine: + -- + -- * **Before** the state transition. + -- The state transition method needs to start with the name **OnBefore + the name of the state**. + -- If the state transition method returns false, then the processing of the state transition will not be done! + -- If you want to change the behaviour of the AIControllable at this event, return false, + -- but then you'll need to specify your own logic using the AIControllable! + -- + -- * **After** the state transition. + -- The state transition method needs to start with the name **OnAfter + the name of the state**. + -- These state transition methods need to provide a return value, which is specified at the function description. + -- -- @type ACT_ACCOUNT -- @field Set#SET_UNIT TargetSetUnit -- @extends Core.Fsm#FSM_PROCESS @@ -30540,7 +32451,18 @@ end -- ACT_ACCOUNT do -- ACT_ACCOUNT_DEADS - --- ACT_ACCOUNT_DEADS class + --- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Fsm.Account#ACT_ACCOUNT} + -- + -- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units. + -- The process is given a @{Set} of units that will be tracked upon successful destruction. + -- The process will end after each target has been successfully destroyed. + -- Each successful dead will trigger an Account state transition that can be scored, modified or administered. + -- + -- + -- ## ACT_ACCOUNT_DEADS constructor: + -- + -- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object. + -- -- @type ACT_ACCOUNT_DEADS -- @field Set#SET_UNIT TargetSetUnit -- @extends #ACT_ACCOUNT @@ -30576,15 +32498,6 @@ do -- ACT_ACCOUNT_DEADS self.TaskName = FsmAccount.TaskName end - - - function ACT_ACCOUNT_DEADS:_Destructor() - self:E("_Destructor") - - self:EventRemoveAll() - - end - --- Process Events --- StateMachine callback function @@ -30593,7 +32506,7 @@ do -- ACT_ACCOUNT_DEADS -- @param #string Event -- @param #string From -- @param #string To - function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, From, Event, To ) + function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, Task, From, Event, To ) self:E( { ProcessUnit, From, Event, To } ) self:Message( "Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed." ) @@ -30606,18 +32519,21 @@ do -- ACT_ACCOUNT_DEADS -- @param #string Event -- @param #string From -- @param #string To - function ACT_ACCOUNT_DEADS:onenterAccount( ProcessUnit, From, Event, To, EventData ) + function ACT_ACCOUNT_DEADS:onenterAccount( ProcessUnit, Task, From, Event, To, EventData ) self:T( { ProcessUnit, EventData, From, Event, To } ) self:T({self.Controllable}) self.TargetSetUnit:Flush() + self:T( { "Before sending Message", EventData.IniUnitName, self.TargetSetUnit:FindUnit( EventData.IniUnitName ) } ) if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then + self:T( "Sending Message" ) local TaskGroup = ProcessUnit:GetGroup() - self.TargetSetUnit:RemoveUnitsByName( EventData.IniUnitName ) + self.TargetSetUnit:Remove( EventData.IniUnitName ) self:Message( "You hit a target. Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." ) end + self:T( { "After sending Message" } ) end --- StateMachine callback function @@ -30626,7 +32542,7 @@ do -- ACT_ACCOUNT_DEADS -- @param #string Event -- @param #string From -- @param #string To - function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, From, Event, To, EventData ) + function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To ) if self.TargetSetUnit:Count() > 0 then self:__More( 1 ) @@ -30643,7 +32559,7 @@ do -- ACT_ACCOUNT_DEADS self:T( { "EventDead", EventData } ) if EventData.IniDCSUnit then - self:__Event( 1, EventData ) + self:Event( EventData ) end end @@ -30758,7 +32674,7 @@ do -- ACT_ASSIST function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To ) local ProcessGroup = ProcessUnit:GetGroup() - local MissionMenu = self:GetMission():GetMissionMenu( ProcessGroup ) + local MissionMenu = self:GetMission():GetMenu( ProcessGroup ) local function MenuSmoke( MenuParam ) self:E( MenuParam ) @@ -30775,6 +32691,17 @@ do -- ACT_ASSIST self.MenuSmokeRed = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop Red smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Red } ) self.MenuSmokeWhite = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop White smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.White } ) end + + --- StateMachine callback function + -- @param #ACT_ASSIST self + -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit + -- @param #string Event + -- @param #string From + -- @param #string To + function ACT_ASSIST:onafterStop( ProcessUnit, From, Event, To ) + + self.Menu:Remove() -- When stopped, remove the menus + end end @@ -30875,7 +32802,9 @@ function REPORT:New( Title ) local self = BASE:Inherit( self, BASE:New() ) self.Report = {} - self.Report[#self.Report+1] = Title + if Title then + self.Report[#self.Report+1] = Title + end return self end @@ -30886,11 +32815,17 @@ end -- @return #REPORT function REPORT:Add( Text ) self.Report[#self.Report+1] = Text - return self.Report[#self.Report+1] + return self.Report[#self.Report] end -function REPORT:Text() - return table.concat( self.Report, "\n" ) +--- Produces the text of the report, taking into account an optional delimeter, which is \n by default. +-- @param #REPORT self +-- @param #string Delimiter (optional) A delimiter text. +-- @return #string The report text. +function REPORT:Text( Delimiter ) + Delimiter = Delimiter or "\n" + local ReportText = table.concat( self.Report, Delimiter ) or "" + return ReportText end --- The COMMANDCENTER class @@ -30923,22 +32858,23 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) self:HandleEvent( EVENTS.Birth, --- @param #COMMANDCENTER self - --- @param Core.Event#EVENTDATA EventData + -- @param Core.Event#EVENTDATA EventData function( self, EventData ) - self:E( { EventData } ) - local EventGroup = GROUP:Find( EventData.IniDCSGroup ) - if EventGroup and self:HasGroup( EventGroup ) then - local MenuReporting = MENU_GROUP:New( EventGroup, "Reporting", self.CommandCenterMenu ) - local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Summary Report", MenuReporting, self.ReportSummary, self, EventGroup ) - local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) - self:ReportSummary( EventGroup ) - end - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! - Mission:JoinUnit( PlayerUnit, PlayerGroup ) - Mission:ReportDetails() + if EventData.IniObjectCategory == 1 then + local EventGroup = GROUP:Find( EventData.IniDCSGroup ) + if EventGroup and self:HasGroup( EventGroup ) then + local MenuReporting = MENU_GROUP:New( EventGroup, "Reporting", self.CommandCenterMenu ) + local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Summary Report", MenuReporting, self.ReportSummary, self, EventGroup ) + local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) + self:ReportSummary( EventGroup ) + end + local PlayerUnit = EventData.IniUnit + for MissionID, Mission in pairs( self:GetMissions() ) do + local Mission = Mission -- Tasking.Mission#MISSION + local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! + Mission:JoinUnit( PlayerUnit, PlayerGroup ) + Mission:ReportDetails() + end end end @@ -31048,17 +32984,26 @@ function COMMANDCENTER:SetMenu() self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "Command Center (" .. self:GetName() .. ")" ) + local MenuTime = timer.getTime() for MissionID, Mission in pairs( self:GetMissions() ) do local Mission = Mission -- Tasking.Mission#MISSION - Mission:RemoveMenu() + Mission:SetMenu( MenuTime ) + end + + for MissionID, Mission in pairs( self:GetMissions() ) do + local Mission = Mission -- Tasking.Mission#MISSION + Mission:RemoveMenu( MenuTime ) end - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - Mission:SetMenu() - end end +--- Gets the commandcenter menu structure governed by the HQ command center. +-- @param #COMMANDCENTER self +-- @return Core.Menu#MENU_COALITION +function COMMANDCENTER:GetMenu() + self:F() + return self.CommandCenterMenu +end --- Checks of the COMMANDCENTER has a GROUP. -- @param #COMMANDCENTER self @@ -31079,6 +33024,14 @@ function COMMANDCENTER:HasGroup( MissionGroup ) return Has end +--- Send a CC message to the coalition of the CC. +-- @param #COMMANDCENTER self +function COMMANDCENTER:MessageToAll( Message ) + + self:GetPositionable():MessageToAll( Message, 20, self:GetName() ) + +end + --- Send a CC message to a GROUP. -- @param #COMMANDCENTER self -- @param #string Message @@ -31086,7 +33039,8 @@ end -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. function COMMANDCENTER:MessageToGroup( Message, TaskGroup, Name ) - local Prefix = Name and "@ Group (" .. Name .. "): " or '' + local Prefix = "@ Group" + Prefix = Prefix .. ( Name and " (" .. Name .. "): " or '' ) Message = Prefix .. Message self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() ) @@ -31102,6 +33056,7 @@ function COMMANDCENTER:MessageToCoalition( Message ) end + --- Report the status of all MISSIONs to a GROUP. -- Each Mission is listed, with an indication how many Tasks are still to be completed. -- @param #COMMANDCENTER self @@ -31149,22 +33104,6 @@ MISSION = { ClassName = "MISSION", Name = "", MissionStatus = "PENDING", - _Clients = {}, - TaskMenus = {}, - TaskCategoryMenus = {}, - TaskTypeMenus = {}, - _ActiveTasks = {}, - GoalFunction = nil, - MissionReportTrigger = 0, - MissionProgressTrigger = 0, - MissionReportShow = false, - MissionReportFlash = false, - MissionTimeInterval = 0, - MissionCoalition = "", - SUCCESS = 1, - FAILED = 2, - REPEAT = 3, - _GoalTasks = {} } --- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. @@ -31182,10 +33121,184 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi self:SetStartState( "Idle" ) self:AddTransition( "Idle", "Start", "Ongoing" ) + + --- OnLeave Transition Handler for State Idle. + -- @function [parent=#MISSION] OnLeaveIdle + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Idle. + -- @function [parent=#MISSION] OnEnterIdle + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnLeave Transition Handler for State Ongoing. + -- @function [parent=#MISSION] OnLeaveOngoing + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Ongoing. + -- @function [parent=#MISSION] OnEnterOngoing + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Start. + -- @function [parent=#MISSION] OnBeforeStart + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Start. + -- @function [parent=#MISSION] OnAfterStart + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Start. + -- @function [parent=#MISSION] Start + -- @param #MISSION self + + --- Asynchronous Event Trigger for Event Start. + -- @function [parent=#MISSION] __Start + -- @param #MISSION self + -- @param #number Delay The delay in seconds. + self:AddTransition( "Ongoing", "Stop", "Idle" ) + + --- OnLeave Transition Handler for State Idle. + -- @function [parent=#MISSION] OnLeaveIdle + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Idle. + -- @function [parent=#MISSION] OnEnterIdle + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Stop. + -- @function [parent=#MISSION] OnBeforeStop + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Stop. + -- @function [parent=#MISSION] OnAfterStop + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Stop. + -- @function [parent=#MISSION] Stop + -- @param #MISSION self + + --- Asynchronous Event Trigger for Event Stop. + -- @function [parent=#MISSION] __Stop + -- @param #MISSION self + -- @param #number Delay The delay in seconds. + self:AddTransition( "Ongoing", "Complete", "Completed" ) + + --- OnLeave Transition Handler for State Completed. + -- @function [parent=#MISSION] OnLeaveCompleted + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Completed. + -- @function [parent=#MISSION] OnEnterCompleted + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Complete. + -- @function [parent=#MISSION] OnBeforeComplete + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Complete. + -- @function [parent=#MISSION] OnAfterComplete + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Complete. + -- @function [parent=#MISSION] Complete + -- @param #MISSION self + + --- Asynchronous Event Trigger for Event Complete. + -- @function [parent=#MISSION] __Complete + -- @param #MISSION self + -- @param #number Delay The delay in seconds. + self:AddTransition( "*", "Fail", "Failed" ) + --- OnLeave Transition Handler for State Failed. + -- @function [parent=#MISSION] OnLeaveFailed + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Failed. + -- @function [parent=#MISSION] OnEnterFailed + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Fail. + -- @function [parent=#MISSION] OnBeforeFail + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Fail. + -- @function [parent=#MISSION] OnAfterFail + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Fail. + -- @function [parent=#MISSION] Fail + -- @param #MISSION self + + --- Asynchronous Event Trigger for Event Fail. + -- @function [parent=#MISSION] __Fail + -- @param #MISSION self + -- @param #number Delay The delay in seconds. + self:T( { MissionName, MissionPriority, MissionBriefing, MissionCoalition } ) self.CommandCenter = CommandCenter @@ -31197,14 +33310,18 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi self.MissionCoalition = MissionCoalition self.Tasks = {} + + -- Private implementations + + return self end ---- FSM function for a MISSION +-- FSM function for a MISSION -- @param #MISSION self --- @param #string Event -- @param #string From +-- @param #string Event -- @param #string To function MISSION:onbeforeComplete( From, Event, To ) @@ -31217,10 +33334,10 @@ function MISSION:onbeforeComplete( From, Event, To ) return true -- Allow Mission completion. end ---- FSM function for a MISSION +-- FSM function for a MISSION -- @param #MISSION self --- @param #string Event -- @param #string From +-- @param #string Event -- @param #string To function MISSION:onenterCompleted( From, Event, To ) @@ -31338,23 +33455,25 @@ end --- Sets the Planned Task menu. -- @param #MISSION self -function MISSION:SetMenu() +-- @param #number MenuTime +function MISSION:SetMenu( MenuTime ) self:F() - for _, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - Task:SetMenu() + for _, TaskData in pairs( self:GetTasks() ) do + local Task = TaskData -- Tasking.Task#TASK + Task:SetMenu( MenuTime ) end end --- Removes the Planned Task menu. -- @param #MISSION self -function MISSION:RemoveMenu() +-- @param #number MenuTime +function MISSION:RemoveMenu( MenuTime ) self:F() for _, Task in pairs( self:GetTasks() ) do local Task = Task -- Tasking.Task#TASK - Task:RemoveMenu() + Task:RemoveMenu( MenuTime ) end end @@ -31366,20 +33485,6 @@ function MISSION:GetCommandCenter() return self.CommandCenter end ---- Sets the Assigned Task menu. --- @param #MISSION self --- @param Tasking.Task#TASK Task --- @param #string MenuText The menu text. --- @return #MISSION self -function MISSION:SetAssignedMenu( Task ) - - for _, Task in pairs( self.Tasks ) do - local Task = Task -- Tasking.Task#TASK - Task:RemoveMenu() - Task:SetAssignedMenu() - end - -end --- Removes a Task menu. -- @param #MISSION self @@ -31395,28 +33500,18 @@ end -- @param #MISSION self -- @param Wrapper.Group#GROUP TaskGroup -- @return Core.Menu#MENU_COALITION self -function MISSION:GetMissionMenu( TaskGroup ) +function MISSION:GetMenu( TaskGroup ) local CommandCenter = self:GetCommandCenter() - local CommandCenterMenu = CommandCenter.CommandCenterMenu + local CommandCenterMenu = CommandCenter:GetMenu() local MissionName = self:GetName() - - local TaskGroupName = TaskGroup:GetName() - local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ) + local MissionMenu = CommandCenterMenu:GetMenu( MissionName ) return MissionMenu end ---- Clears the mission menu for the coalition. --- @param #MISSION self --- @return #MISSION self -function MISSION:ClearMissionMenu() - self.MissionMenu:Remove() - self.MissionMenu = nil -end - --- Get the TASK identified by the TaskNumber from the Mission. This function is useful in GoalFunctions. -- @param #string TaskName The Name of the @{Task} within the @{Mission}. -- @return Tasking.Task#TASK The Task @@ -31487,76 +33582,44 @@ function MISSION:GetNextTaskID( Task ) return self.Tasks[TaskName].n end - - ---- old stuff - ---- Returns if a Mission has completed. --- @return bool +--- Is the @{Mission} **Completed**. +-- @param #MISSION self +-- @return #boolean function MISSION:IsCompleted() - self:F() - return self.MissionStatus == "ACCOMPLISHED" + return self:Is( "Completed" ) end ---- Set a Mission to completed. -function MISSION:Completed() - self:F() - self.MissionStatus = "ACCOMPLISHED" - self:StatusToClients() +--- Is the @{Mission} **Idle**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsIdle() + return self:Is( "Idle" ) end ---- Returns if a Mission is ongoing. --- treturn bool +--- Is the @{Mission} **Ongoing**. +-- @param #MISSION self +-- @return #boolean function MISSION:IsOngoing() - self:F() - return self.MissionStatus == "ONGOING" + return self:Is( "Ongoing" ) end ---- Set a Mission to ongoing. -function MISSION:Ongoing() - self:F() - self.MissionStatus = "ONGOING" - --self:StatusToClients() +--- Is the @{Mission} **Failed**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsFailed() + return self:Is( "Failed" ) end ---- Returns if a Mission is pending. --- treturn bool -function MISSION:IsPending() - self:F() - return self.MissionStatus == "PENDING" -end - ---- Set a Mission to pending. -function MISSION:Pending() - self:F() - self.MissionStatus = "PENDING" - self:StatusToClients() -end - ---- Returns if a Mission has failed. --- treturn bool -function MISSION:IsFailed() - self:F() - return self.MissionStatus == "FAILED" -end - ---- Set a Mission to failed. -function MISSION:Failed() - self:F() - self.MissionStatus = "FAILED" - self:StatusToClients() -end - ---- Send the status of the MISSION to all Clients. -function MISSION:StatusToClients() - self:F() - if self.MissionReportFlash then - for ClientID, Client in pairs( self._Clients ) do - Client:Message( self.MissionCoalition .. ' "' .. self.Name .. '": ' .. self.MissionStatus .. '! ( ' .. self.MissionPriority .. ' mission ) ', 10, "Mission Command: Mission Status") - end - end +--- Is the @{Mission} **Hold**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsHold() + return self:Is( "Hold" ) end +--- Validates if the Mission has a Group +-- @param #MISSION +-- @return #boolean true if the Mission has a Group. function MISSION:HasGroup( TaskGroup ) local Has = false @@ -31649,107 +33712,6 @@ function MISSION:ReportDetails() return Report:Text() end ---- Report the status of all MISSIONs to all active Clients. -function MISSION:ReportToAll() - self:F() - - local AlivePlayers = '' - for ClientID, Client in pairs( self._Clients ) do - if Client:GetDCSGroup() then - if Client:GetClientGroupDCSUnit() then - if Client:GetClientGroupDCSUnit():getLife() > 0.0 then - if AlivePlayers == '' then - AlivePlayers = ' Players: ' .. Client:GetClientGroupDCSUnit():getPlayerName() - else - AlivePlayers = AlivePlayers .. ' / ' .. Client:GetClientGroupDCSUnit():getPlayerName() - end - end - end - end - end - local Tasks = self:GetTasks() - local TaskText = "" - for TaskID, TaskData in pairs( Tasks ) do - TaskText = TaskText .. " - Task " .. TaskID .. ": " .. TaskData.Name .. ": " .. TaskData:GetGoalProgress() .. "\n" - end - MESSAGE:New( self.MissionCoalition .. ' "' .. self.Name .. '": ' .. self.MissionStatus .. ' ( ' .. self.MissionPriority .. ' mission )' .. AlivePlayers .. "\n" .. TaskText:gsub("\n$",""), 10, "Mission Command: Mission Report" ):ToAll() -end - - ---- Add a goal function to a MISSION. Goal functions are called when a @{TASK} within a mission has been completed. --- @param function GoalFunction is the function defined by the mission designer to evaluate whether a certain goal has been reached after a @{TASK} finishes within the @{MISSION}. A GoalFunction must accept 2 parameters: Mission, Client, which contains the current MISSION object and the current CLIENT object respectively. --- @usage --- PatriotActivation = { --- { "US SAM Patriot Zerti", false }, --- { "US SAM Patriot Zegduleti", false }, --- { "US SAM Patriot Gvleti", false } --- } --- --- function DeployPatriotTroopsGoal( Mission, Client ) --- --- --- -- Check if the cargo is all deployed for mission success. --- for CargoID, CargoData in pairs( Mission._Cargos ) do --- if Group.getByName( CargoData.CargoGroupName ) then --- CargoGroup = Group.getByName( CargoData.CargoGroupName ) --- if CargoGroup then --- -- Check if the cargo is ready to activate --- CurrentLandingZoneID = routines.IsUnitInZones( CargoGroup:getUnits()[1], Mission:GetTask( 2 ).LandingZones ) -- The second task is the Deploytask to measure mission success upon --- if CurrentLandingZoneID then --- if PatriotActivation[CurrentLandingZoneID][2] == false then --- -- Now check if this is a new Mission Task to be completed... --- trigger.action.setGroupAIOn( Group.getByName( PatriotActivation[CurrentLandingZoneID][1] ) ) --- PatriotActivation[CurrentLandingZoneID][2] = true --- MessageToBlue( "Mission Command: Message to all airborne units! The " .. PatriotActivation[CurrentLandingZoneID][1] .. " is armed. Our air defenses are now stronger.", 60, "BLUE/PatriotDefense" ) --- MessageToRed( "Mission Command: Our satellite systems are detecting additional NATO air defenses. To all airborne units: Take care!!!", 60, "RED/PatriotDefense" ) --- Mission:GetTask( 2 ):AddGoalCompletion( "Patriots activated", PatriotActivation[CurrentLandingZoneID][1], 1 ) -- Register Patriot activation as part of mission goal. --- end --- end --- end --- end --- end --- end --- --- local Mission = MISSIONSCHEDULER.AddMission( 'NATO Transport Troops', 'Operational', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.', 'NATO' ) --- Mission:AddGoalFunction( DeployPatriotTroopsGoal ) -function MISSION:AddGoalFunction( GoalFunction ) - self:F() - self.GoalFunction = GoalFunction -end - ---- Register a new @{CLIENT} to participate within the mission. --- @param CLIENT Client is the @{CLIENT} object. The object must have been instantiated with @{CLIENT:New}. --- @return CLIENT --- @usage --- Add a number of Client objects to the Mission. --- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*HOT-Deploy Troops 1', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*RAMP-Deploy Troops 3', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*HOT-Deploy Troops 2', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*RAMP-Deploy Troops 4', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() ) -function MISSION:AddClient( Client ) - self:F( { Client } ) - - local Valid = true - - if Valid then - self._Clients[Client.ClientName] = Client - end - - return Client -end - ---- Find a @{CLIENT} object within the @{MISSION} by its ClientName. --- @param CLIENT ClientName is a string defining the Client Group as defined within the ME. --- @return CLIENT --- @usage --- -- Seach for Client "Bomber" within the Mission. --- local BomberClient = Mission:FindClient( "Bomber" ) -function MISSION:FindClient( ClientName ) - self:F( { self._Clients[ClientName] } ) - return self._Clients[ClientName] -end - - --- Get all the TASKs from the Mission. This function is useful in GoalFunctions. -- @return {TASK,...} Structure of TASKS with the @{TASK} number as the key. -- @usage @@ -31763,333 +33725,6 @@ function MISSION:GetTasks() end ---[[ - _TransportExecuteStage: Defines the different stages of Transport unload/load execution. This table is internal and is used to control the validity of Transport load/unload timing. - - - _TransportExecuteStage.EXECUTING - - _TransportExecuteStage.SUCCESS - - _TransportExecuteStage.FAILED - ---]] -_TransportExecuteStage = { - NONE = 0, - EXECUTING = 1, - SUCCESS = 2, - FAILED = 3 -} - - ---- The MISSIONSCHEDULER is an OBJECT and is the main scheduler of ALL active MISSIONs registered within this scheduler. It's workings are considered internal and is automatically created when the Mission.lua file is included. --- @type MISSIONSCHEDULER --- @field #MISSIONSCHEDULER.MISSIONS Missions -MISSIONSCHEDULER = { - Missions = {}, - MissionCount = 0, - TimeIntervalCount = 0, - TimeIntervalShow = 150, - TimeSeconds = 14400, - TimeShow = 5 -} - ---- @type MISSIONSCHEDULER.MISSIONS --- @list <#MISSION> Mission - ---- This is the main MISSIONSCHEDULER Scheduler function. It is considered internal and is automatically created when the Mission.lua file is included. -function MISSIONSCHEDULER.Scheduler() - - - -- loop through the missions in the TransportTasks - for MissionName, MissionData in pairs( MISSIONSCHEDULER.Missions ) do - - local Mission = MissionData -- #MISSION - - if not Mission:IsCompleted() then - - -- This flag will monitor if for this mission, there are clients alive. If this flag is still false at the end of the loop, the mission status will be set to Pending (if not Failed or Completed). - local ClientsAlive = false - - for ClientID, ClientData in pairs( Mission._Clients ) do - - local Client = ClientData -- Wrapper.Client#CLIENT - - if Client:IsAlive() then - - -- There is at least one Client that is alive... So the Mission status is set to Ongoing. - ClientsAlive = true - - -- If this Client was not registered as Alive before: - -- 1. We register the Client as Alive. - -- 2. We initialize the Client Tasks and make a link to the original Mission Task. - -- 3. We initialize the Cargos. - -- 4. We flag the Mission as Ongoing. - if not Client.ClientAlive then - Client.ClientAlive = true - Client.ClientBriefingShown = false - for TaskNumber, Task in pairs( Mission._Tasks ) do - -- Note that this a deepCopy. Each client must have their own Tasks with own Stages!!! - Client._Tasks[TaskNumber] = routines.utils.deepCopy( Mission._Tasks[TaskNumber] ) - -- Each MissionTask must point to the original Mission. - Client._Tasks[TaskNumber].MissionTask = Mission._Tasks[TaskNumber] - Client._Tasks[TaskNumber].Cargos = Mission._Tasks[TaskNumber].Cargos - Client._Tasks[TaskNumber].LandingZones = Mission._Tasks[TaskNumber].LandingZones - end - - Mission:Ongoing() - end - - - -- For each Client, check for each Task the state and evolve the mission. - -- This flag will indicate if the Task of the Client is Complete. - local TaskComplete = false - - for TaskNumber, Task in pairs( Client._Tasks ) do - - if not Task.Stage then - Task:SetStage( 1 ) - end - - - local TransportTime = timer.getTime() - - if not Task:IsDone() then - - if Task:Goal() then - Task:ShowGoalProgress( Mission, Client ) - end - - --env.info( 'Scheduler: Mission = ' .. Mission.Name .. ' / Client = ' .. Client.ClientName .. ' / Task = ' .. Task.Name .. ' / Stage = ' .. Task.ActiveStage .. ' - ' .. Task.Stage.Name .. ' - ' .. Task.Stage.StageType ) - - -- Action - if Task:StageExecute() then - Task.Stage:Execute( Mission, Client, Task ) - end - - -- Wait until execution is finished - if Task.ExecuteStage == _TransportExecuteStage.EXECUTING then - Task.Stage:Executing( Mission, Client, Task ) - end - - -- Validate completion or reverse to earlier stage - if Task.Time + Task.Stage.WaitTime <= TransportTime then - Task:SetStage( Task.Stage:Validate( Mission, Client, Task ) ) - end - - if Task:IsDone() then - --env.info( 'Scheduler: Mission '.. Mission.Name .. ' Task ' .. Task.Name .. ' Stage ' .. Task.Stage.Name .. ' done. TaskComplete = ' .. string.format ( "%s", TaskComplete and "true" or "false" ) ) - TaskComplete = true -- when a task is not yet completed, a mission cannot be completed - - else - -- break only if this task is not yet done, so that future task are not yet activated. - TaskComplete = false -- when a task is not yet completed, a mission cannot be completed - --env.info( 'Scheduler: Mission "'.. Mission.Name .. '" Task "' .. Task.Name .. '" Stage "' .. Task.Stage.Name .. '" break. TaskComplete = ' .. string.format ( "%s", TaskComplete and "true" or "false" ) ) - break - end - - if TaskComplete then - - if Mission.GoalFunction ~= nil then - Mission.GoalFunction( Mission, Client ) - end - if MISSIONSCHEDULER.Scoring then - MISSIONSCHEDULER.Scoring:_AddMissionTaskScore( Client:GetClientGroupDCSUnit(), Mission.Name, 25 ) - end - --- if not Mission:IsCompleted() then --- end - end - end - end - - local MissionComplete = true - for TaskNumber, Task in pairs( Mission._Tasks ) do - if Task:Goal() then --- Task:ShowGoalProgress( Mission, Client ) - if Task:IsGoalReached() then - else - MissionComplete = false - end - else - MissionComplete = false -- If there is no goal, the mission should never be ended. The goal status will be set somewhere else. - end - end - - if MissionComplete then - Mission:Completed() - if MISSIONSCHEDULER.Scoring then - MISSIONSCHEDULER.Scoring:_AddMissionScore( Mission.Name, 100 ) - end - else - if TaskComplete then - -- Reset for new tasking of active client - Client.ClientAlive = false -- Reset the client tasks. - end - end - - - else - if Client.ClientAlive then - env.info( 'Scheduler: Client "' .. Client.ClientName .. '" is inactive.' ) - Client.ClientAlive = false - - -- This is tricky. If we sanitize Client._Tasks before sanitizing Client._Tasks[TaskNumber].MissionTask, then the original MissionTask will be sanitized, and will be lost within the garbage collector. - -- So first sanitize Client._Tasks[TaskNumber].MissionTask, after that, sanitize only the whole _Tasks structure... - --Client._Tasks[TaskNumber].MissionTask = nil - --Client._Tasks = nil - end - end - end - - -- If all Clients of this Mission are not activated, then the Mission status needs to be put back into Pending status. - -- But only if the Mission was Ongoing. In case the Mission is Completed or Failed, the Mission status may not be changed. In these cases, this will be the last run of this Mission in the Scheduler. - if ClientsAlive == false then - if Mission:IsOngoing() then - -- Mission status back to pending... - Mission:Pending() - end - end - end - - Mission:StatusToClients() - - if Mission:ReportTrigger() then - Mission:ReportToAll() - end - end - - return true -end - ---- Start the MISSIONSCHEDULER. -function MISSIONSCHEDULER.Start() - if MISSIONSCHEDULER ~= nil then - --MISSIONSCHEDULER.SchedulerId = routines.scheduleFunction( MISSIONSCHEDULER.Scheduler, { }, 0, 2 ) - MISSIONSCHEDULER.SchedulerId = SCHEDULER:New( nil, MISSIONSCHEDULER.Scheduler, { }, 0, 2 ) - end -end - ---- Stop the MISSIONSCHEDULER. -function MISSIONSCHEDULER.Stop() - if MISSIONSCHEDULER.SchedulerId then - routines.removeFunction(MISSIONSCHEDULER.SchedulerId) - MISSIONSCHEDULER.SchedulerId = nil - end -end - ---- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. --- @param Mission is the MISSION object instantiated by @{MISSION:New}. --- @return MISSION --- @usage --- -- Declare a mission. --- Mission = MISSION:New( 'Russia Transport Troops SA-6', --- 'Operational', --- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', --- 'Russia' ) --- MISSIONSCHEDULER:AddMission( Mission ) -function MISSIONSCHEDULER.AddMission( Mission ) - MISSIONSCHEDULER.Missions[Mission.Name] = Mission - MISSIONSCHEDULER.MissionCount = MISSIONSCHEDULER.MissionCount + 1 - -- Add an overall AI Client for the AI tasks... This AI Client will facilitate the Events in the background for each Task. - --MissionAdd:AddClient( CLIENT:Register( 'AI' ) ) - - return Mission -end - ---- Remove a MISSION from the MISSIONSCHEDULER. --- @param MissionName is the name of the MISSION given at declaration using @{AddMission}. --- @usage --- -- Declare a mission. --- Mission = MISSION:New( 'Russia Transport Troops SA-6', --- 'Operational', --- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', --- 'Russia' ) --- MISSIONSCHEDULER:AddMission( Mission ) --- --- -- Now remove the Mission. --- MISSIONSCHEDULER:RemoveMission( 'Russia Transport Troops SA-6' ) -function MISSIONSCHEDULER.RemoveMission( MissionName ) - MISSIONSCHEDULER.Missions[MissionName] = nil - MISSIONSCHEDULER.MissionCount = MISSIONSCHEDULER.MissionCount - 1 -end - ---- Find a MISSION within the MISSIONSCHEDULER. --- @param MissionName is the name of the MISSION given at declaration using @{AddMission}. --- @return MISSION --- @usage --- -- Declare a mission. --- Mission = MISSION:New( 'Russia Transport Troops SA-6', --- 'Operational', --- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', --- 'Russia' ) --- MISSIONSCHEDULER:AddMission( Mission ) --- --- -- Now find the Mission. --- MissionFind = MISSIONSCHEDULER:FindMission( 'Russia Transport Troops SA-6' ) -function MISSIONSCHEDULER.FindMission( MissionName ) - return MISSIONSCHEDULER.Missions[MissionName] -end - --- Internal function used by the MISSIONSCHEDULER menu. -function MISSIONSCHEDULER.ReportMissionsShow( ) - for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do - Mission.MissionReportShow = true - Mission.MissionReportFlash = false - end -end - --- Internal function used by the MISSIONSCHEDULER menu. -function MISSIONSCHEDULER.ReportMissionsFlash( TimeInterval ) - local Count = 0 - for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do - Mission.MissionReportShow = false - Mission.MissionReportFlash = true - Mission.MissionReportTrigger = timer.getTime() + Count * TimeInterval - Mission.MissionTimeInterval = MISSIONSCHEDULER.MissionCount * TimeInterval - env.info( "TimeInterval = " .. Mission.MissionTimeInterval ) - Count = Count + 1 - end -end - --- Internal function used by the MISSIONSCHEDULER menu. -function MISSIONSCHEDULER.ReportMissionsHide( Prm ) - for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do - Mission.MissionReportShow = false - Mission.MissionReportFlash = false - end -end - ---- Enables a MENU option in the communications menu under F10 to control the status of the active missions. --- This function should be called only once when starting the MISSIONSCHEDULER. -function MISSIONSCHEDULER.ReportMenu() - local ReportMenu = SUBMENU:New( 'Status' ) - local ReportMenuShow = COMMANDMENU:New( 'Show Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsShow, 0 ) - local ReportMenuFlash = COMMANDMENU:New('Flash Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsFlash, 120 ) - local ReportMenuHide = COMMANDMENU:New( 'Hide Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsHide, 0 ) -end - ---- Show the remaining mission time. -function MISSIONSCHEDULER:TimeShow() - self.TimeIntervalCount = self.TimeIntervalCount + 1 - if self.TimeIntervalCount >= self.TimeTriggerShow then - local TimeMsg = string.format("%00d", ( self.TimeSeconds / 60 ) - ( timer.getTime() / 60 )) .. ' minutes left until mission reload.' - MESSAGE:New( TimeMsg, self.TimeShow, "Mission time" ):ToAll() - self.TimeIntervalCount = 0 - end -end - -function MISSIONSCHEDULER:Time( TimeSeconds, TimeIntervalShow, TimeShow ) - - self.TimeIntervalCount = 0 - self.TimeSeconds = TimeSeconds - self.TimeIntervalShow = TimeIntervalShow - self.TimeShow = TimeShow -end - ---- Adds a mission scoring to the game. -function MISSIONSCHEDULER:Scoring( Scoring ) - - self.Scoring = Scoring -end - --- This module contains the TASK class. -- -- 1) @{#TASK} class, extends @{Base#BASE} @@ -32277,7 +33912,6 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() - Mission:AddTask( self ) return self end @@ -32285,9 +33919,13 @@ end --- Get the Task FSM Process Template -- @param #TASK self -- @return Core.Fsm#FSM_PROCESS -function TASK:GetUnitProcess() +function TASK:GetUnitProcess( TaskUnit ) - return self.FsmTemplate + if TaskUnit then + return self:GetStateMachine( TaskUnit ) + else + return self.FsmTemplate + end end --- Sets the Task FSM Process Template @@ -32320,7 +33958,7 @@ function TASK:JoinUnit( PlayerUnit, PlayerGroup ) -- If the PlayerGroup is not assigned to the Task, the menu needs to be set. In that case, the PlayerUnit will become the GroupPlayer leader. if self:IsStatePlanned() or self:IsStateReplanned() then self:SetMenuForGroup( PlayerGroup ) - self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() ) + --self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() ) end if self:IsStateAssigned() then local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) @@ -32362,10 +34000,11 @@ function TASK:AbortUnit( PlayerUnit ) self:MessageToGroups( PlayerUnit:GetPlayerName() .. " aborted Task " .. self:GetName() ) self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) if #PlayerGroup:GetUnits() == 1 then + self:UnAssignFromGroup( PlayerGroup ) PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) self:RemoveMenuForGroup( PlayerGroup ) end - self:PlayerAborted( PlayerUnit ) + self:Abort() end end end @@ -32431,7 +34070,7 @@ end ---- Assign the @{Task}to a @{Group}. +--- Assign the @{Task} to a @{Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @return #TASK @@ -32442,7 +34081,11 @@ function TASK:AssignToGroup( TaskGroup ) TaskGroup:SetState( TaskGroup, "Assigned", self ) - self:RemoveMenuForGroup( TaskGroup ) + local Mission = self:GetMission() + local MissionMenu = Mission:GetMenu( TaskGroup ) + MissionMenu:RemoveSubMenus() + + --self:RemoveMenuForGroup( TaskGroup ) self:SetAssignedMenuForGroup( TaskGroup ) local TaskUnits = TaskGroup:GetUnits() @@ -32482,6 +34125,7 @@ function TASK:AssignToUnit( TaskUnit ) self:E({"Address FsmUnit", tostring( FsmUnit ) } ) FsmUnit:SetStartState( "Planned" ) + FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow. return self @@ -32492,7 +34136,7 @@ end -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self function TASK:UnAssignFromUnit( TaskUnit ) - self:F( TaskUnit ) + self:F( TaskUnit:GetName() ) self:RemoveStateMachine( TaskUnit ) @@ -32539,28 +34183,37 @@ function TASK:SendBriefingToAssignedGroups() end ---- Assign the @{Task} from the @{Group}s. +--- UnAssign the @{Task} from the @{Group}s. -- @param #TASK self function TASK:UnAssignFromGroups() self:F2() for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do + self:UnAssignFromGroup( TaskGroup ) + end +end - TaskGroup:SetState( TaskGroup, "Assigned", nil ) +--- UnAssign the @{Task} from a @{Group}. +-- @param #TASK self +function TASK:UnAssignFromGroup( TaskGroup ) + self:F2( { TaskGroup } ) + + TaskGroup:SetState( TaskGroup, "Assigned", nil ) - self:RemoveMenuForGroup( TaskGroup ) + self:RemoveAssignedMenuForGroup( TaskGroup ) - local TaskUnits = TaskGroup:GetUnits() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - if PlayerName ~= nil or PlayerName ~= "" then - self:UnAssignFromUnit( TaskUnit ) - end + local TaskUnits = TaskGroup:GetUnits() + for UnitID, UnitData in pairs( TaskUnits ) do + local TaskUnit = UnitData -- Wrapper.Unit#UNIT + local PlayerName = TaskUnit:GetPlayerName() + if PlayerName ~= nil or PlayerName ~= "" then + self:UnAssignFromUnit( TaskUnit ) end end end + + --- Returns if the @{Task} is assigned to the Group. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup @@ -32571,10 +34224,12 @@ function TASK:IsAssignedToGroup( TaskGroup ) if self:IsStateAssigned() then if TaskGroup:GetState( TaskGroup, "Assigned" ) == self then + self:T( { "Task is assigned to:", TaskGroup:GetName() } ) return true end end + self:T( { "Task is not assigned to:", TaskGroup:GetName() } ) return false end @@ -32603,37 +34258,36 @@ end --- Set the menu options of the @{Task} to all the groups in the SetGroup. -- @param #TASK self -function TASK:SetMenu() +-- @param #number MenuTime +-- @return #TASK +function TASK:SetMenu( MenuTime ) self:F() self.SetGroup:Flush() - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if self:IsStatePlanned() or self:IsStateReplanned() then - self:SetMenuForGroup( TaskGroup ) + for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do + local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP + if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then + if self:IsStatePlanned() or self:IsStateReplanned() then + self:SetMenuForGroup( TaskGroup, MenuTime ) + end end end end ---- Remove the menu options of the @{Task} to all the groups in the SetGroup. --- @param #TASK self --- @return #TASK self -function TASK:RemoveMenu() - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - self:RemoveMenuForGroup( TaskGroup ) - end -end - --- Set the Menu for a Group -- @param #TASK self -function TASK:SetMenuForGroup( TaskGroup ) +-- @param #number MenuTime +-- @return #TASK +function TASK:SetMenuForGroup( TaskGroup, MenuTime ) - if not self:IsAssignedToGroup( TaskGroup ) then - self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName() ) + if not TaskGroup:GetState( TaskGroup, "Assigned" ) then + self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName(), MenuTime ) else - self:SetAssignedMenuForGroup( TaskGroup ) + if not self:IsAssignedToGroup( TaskGroup ) then + self:SetAssignedMenuForGroup( TaskGroup, MenuTime ) + end end end @@ -32642,16 +34296,24 @@ end -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @param #string MenuText The menu text. +-- @param #number MenuTime -- @return #TASK self -function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText ) +function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText, MenuTime ) self:E( TaskGroup:GetName() ) local Mission = self:GetMission() - local MissionMenu = Mission:GetMissionMenu( TaskGroup ) + local MissionName = Mission:GetName() + local CommandCenter = Mission:GetCommandCenter() + local CommandCenterMenu = CommandCenter:GetMenu() + + local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime ) + + + local MissionMenu = Mission:GetMenu( TaskGroup ) local TaskType = self:GetType() - local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ) + local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ):SetTime( MenuTime ) + local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ):SetTime( MenuTime ):SetRemoveParent( true ) return self end @@ -32659,32 +34321,84 @@ end --- Set the assigned menu options of the @{Task}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime -- @return #TASK self -function TASK:SetAssignedMenuForGroup( TaskGroup ) +function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) self:E( TaskGroup:GetName() ) local Mission = self:GetMission() - local MissionMenu = Mission:GetMissionMenu( TaskGroup ) + local MissionMenu = Mission:GetMenu( TaskGroup ) self:E( { MissionMenu = MissionMenu } ) - local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, { self = self, TaskGroup = TaskGroup } ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, { self = self, TaskGroup = TaskGroup } ) + local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ) + local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ) return self end +--- Remove the menu options of the @{Task} to all the groups in the SetGroup. +-- @param #TASK self +-- @param #number MenuTime +-- @return #TASK +function TASK:RemoveMenu( MenuTime ) + self:F() + + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + local TaskGroup = TaskGroup -- Wrapper.Group#GROUP + if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then + if not self:IsAssignedToGroup( TaskGroup ) then + self:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) + end + end + end +end + + --- Remove the menu option of the @{Task} for a @{Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime -- @return #TASK self -function TASK:RemoveMenuForGroup( TaskGroup ) +function TASK:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) + self:F() local Mission = self:GetMission() local MissionName = Mission:GetName() + + local MissionMenu = Mission:GetMenu( TaskGroup ) + + if MissionMenu then + local TaskType = self:GetType() + local TypeMenu = MissionMenu:GetMenu( TaskType ) + + if TypeMenu then + local TaskMenu = TypeMenu:GetMenu( self:GetTaskName() ) + if TaskMenu then + TaskMenu:Remove( MenuTime ) + end + end + end + +end - local MissionMenu = Mission:GetMissionMenu( TaskGroup ) - MissionMenu:Remove() +--- Remove the assigned menu option of the @{Task} for a @{Group}. +-- @param #TASK self +-- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime +-- @return #TASK self +function TASK:RemoveAssignedMenuForGroup( TaskGroup ) + self:F() + + local Mission = self:GetMission() + local MissionName = Mission:GetName() + + local MissionMenu = Mission:GetMenu( TaskGroup ) + + if MissionMenu then + MissionMenu:RemoveSubMenus() + end + end function TASK.MenuAssignToGroup( MenuParam ) @@ -32697,19 +34411,21 @@ function TASK.MenuAssignToGroup( MenuParam ) self:AssignToGroup( TaskGroup ) end -function TASK.MenuTaskStatus( MenuParam ) +--- Report the task status. +-- @param #TASK self +function TASK:MenuTaskStatus( TaskGroup ) - local self = MenuParam.self - local TaskGroup = MenuParam.TaskGroup + local ReportText = self:ReportDetails() - --self:AssignToGroup( TaskGroup ) + self:T( ReportText ) + self:GetMission():GetCommandCenter():MessageToGroup( ReportText, TaskGroup ) + end -function TASK.MenuTaskAbort( MenuParam ) +--- Report the task status. +-- @param #TASK self +function TASK:MenuTaskAbort( TaskGroup ) - local self = MenuParam.self - local TaskGroup = MenuParam.TaskGroup - self:Abort() end @@ -32754,15 +34470,26 @@ end --- Add a FiniteStateMachine to @{Task} with key Task@{Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit +-- @param Core.Fsm#FSM_PROCESS Fsm -- @return #TASK self function TASK:SetStateMachine( TaskUnit, Fsm ) - self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) + self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil, Fsm:GetClassNameAndID() } ) self.Fsm[TaskUnit] = Fsm return Fsm end +--- Gets the FiniteStateMachine of @{Task} with key Task@{Unit} +-- @param #TASK self +-- @param Wrapper.Unit#UNIT TaskUnit +-- @return Core.Fsm#FSM_PROCESS +function TASK:GetStateMachine( TaskUnit ) + self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) + + return self.Fsm[TaskUnit] +end + --- Remove FiniteStateMachines from @{Task} with key Task@{Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit @@ -32770,9 +34497,15 @@ end function TASK:RemoveStateMachine( TaskUnit ) self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) + self:E( self.Fsm ) + for TaskUnitT, Fsm in pairs( self.Fsm ) do + self:E( TaskUnitT ) + end + self.Fsm[TaskUnit] = nil + collectgarbage() - self:T( "Garbage Collected, Processes should be finalized now ...") + self:E( "Garbage Collected, Processes should be finalized now ...") end --- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task} @@ -32887,6 +34620,32 @@ function TASK:IsStatePlanned() return self:Is( "Planned" ) end +--- Sets a @{Task} to status **Aborted**. +-- @param #TASK self +function TASK:StateAborted() + self:SetState( self, "State", "Aborted" ) + return self +end + +--- Is the @{Task} status **Aborted**. +-- @param #TASK self +function TASK:IsStateAborted() + return self:Is( "Aborted" ) +end + +--- Sets a @{Task} to status **Cancelled**. +-- @param #TASK self +function TASK:StateCancelled() + self:SetState( self, "State", "Cancelled" ) + return self +end + +--- Is the @{Task} status **Cancelled**. +-- @param #TASK self +function TASK:IsStateCancelled() + return self:Is( "Cancelled" ) +end + --- Sets a @{Task} to status **Assigned**. -- @param #TASK self function TASK:StateAssigned() @@ -32949,11 +34708,17 @@ end -- @param #string Event -- @param #string From -- @param #string To -function TASK:onenterAssigned( From, Event, To ) +function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName ) - self:E("Task Assigned") + self:E( { "Task Assigned", self.Dispatcher } ) self:MessageToGroups( "Task " .. self:GetName() .. " has been assigned to your group." ) + + if self.Dispatcher then + self:E( "Firing Assign event " ) + self.Dispatcher:Assign( self, PlayerUnit, PlayerName ) + end + self:GetMission():__Start( 1 ) end @@ -32967,7 +34732,7 @@ function TASK:onenterSuccess( From, Event, To ) self:E( "Task Success" ) - self:MessageToGroups( "Task " .. self:GetName() .. " is successful! Good job!" ) + self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is successful! Good job!" ) self:UnAssignFromGroups() self:GetMission():__Complete( 1 ) @@ -33065,6 +34830,18 @@ function TASK:onbeforeTimeOut( From, Event, To ) return false end +do -- Dispatcher + + --- Set dispatcher of a task + -- @param #TASK self + -- @param Tasking.DetectionManager#DETECTION_MANAGER Dispatcher + -- @return #TASK + function TASK:SetDispatcher( Dispatcher ) + self.Dispatcher = Dispatcher + end + +end + do -- Reporting --- Create a summary report of the Task. @@ -33101,24 +34878,19 @@ function TASK:ReportDetails() -- Determine the status of the Task. local State = self:GetState() - -- Loop each Unit active in the Task, and find Player Names. local PlayerNames = {} - for PlayerGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do - local Player = PlayerGroup -- Wrapper.Group#GROUP - for PlayerUnitID, PlayerUnit in pairs( PlayerGroup:GetUnits() ) do - local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT - if PlayerUnit and PlayerUnit:IsAlive() then - local PlayerName = PlayerUnit:GetPlayerName() - PlayerNames[#PlayerNames+1] = PlayerName - end + local PlayerReport = REPORT:New( " - Players:" ) + for PlayerGroupID, PlayerGroupData in pairs( self:GetGroups():GetSet() ) do + local PlayerGroup = PlayerGroupData -- Wrapper.Group#GROUP + PlayerNames = PlayerGroup:GetPlayerNames() + if PlayerNames then + PlayerReport:Add( " -- Group " .. PlayerGroup:GetCallsign() .. ": " .. table.concat( PlayerNames, ", " ) ) end - local PlayerNameText = table.concat( PlayerNames, ", " ) - Report:Add( "Task " .. Name .. " - State '" .. State .. "' - Players " .. PlayerNameText ) end -- Loop each Process in the Task, and find Reporting Details. - + Report:Add( string.format( " - Task %s\n -- State '%s'\n%s", Name, State, PlayerReport:Text() ) ) return Report:Text() end @@ -33128,7 +34900,7 @@ end -- Reporting -- -- === -- --- 1) @{DetectionManager#DETECTION_MANAGER} class, extends @{Base#BASE} +-- 1) @{DetectionManager#DETECTION_MANAGER} class, extends @{Fsm#FSM} -- ==================================================================== -- The @{DetectionManager#DETECTION_MANAGER} class defines the core functions to report detected objects to groups. -- Reportings can be done in several manners, and it is up to the derived classes if DETECTION_MANAGER to model the reporting behaviour. @@ -33160,23 +34932,6 @@ end -- Reporting -- ------------------------------- -- The @{DetectionManager#DETECTION_REPORTING.New}() method creates a new DETECTION_REPORTING instance. -- --- === --- --- 3) @{#DETECTION_DISPATCHER} class, extends @{#DETECTION_MANAGER} --- ================================================================ --- The @{#DETECTION_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of FAC (groups). --- The FAC will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. --- Find a summary below describing for which situation a task type is created: --- --- * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter. --- * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter. --- * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars. --- --- Other task types will follow... --- --- 3.1) DETECTION_DISPATCHER constructor: --- -------------------------------------- --- The @{#DETECTION_DISPATCHER.New}() method creates a new DETECTION_DISPATCHER instance. -- -- === -- @@ -33191,7 +34946,7 @@ do -- DETECTION MANAGER -- @type DETECTION_MANAGER -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. - -- @extends Base#BASE + -- @extends Core.Fsm#FSM DETECTION_MANAGER = { ClassName = "DETECTION_MANAGER", SetGroup = nil, @@ -33206,17 +34961,37 @@ do -- DETECTION MANAGER function DETECTION_MANAGER:New( SetGroup, Detection ) -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) -- Functional.Detection#DETECTION_MANAGER + local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_MANAGER self.SetGroup = SetGroup self.Detection = Detection + self:SetStartState( "Stopped" ) + self:AddTransition( "Stopped", "Start", "Started" ) + self:AddTransition( "Started", "Stop", "Stopped" ) + self:AddTransition( "Started", "Report", "Started" ) + self:SetReportInterval( 30 ) self:SetReportDisplayTime( 25 ) - + + Detection:__Start( 1 ) + return self end + function DETECTION_MANAGER:onafterStart( From, Event, To ) + self:Report() + end + + function DETECTION_MANAGER:onafterReport( From, Event, To ) + + self:E( "onafterReport" ) + + self:__Report( -self._ReportInterval ) + + self:ProcessDetected( self.Detection ) + end + --- Set the reporting time interval. -- @param #DETECTION_MANAGER self -- @param #number ReportInterval The interval in seconds when a report needs to be done. @@ -33247,51 +35022,14 @@ do -- DETECTION MANAGER return self._ReportDisplayTime end - - --- Reports the detected items to the @{Set#SET_GROUP}. -- @param #DETECTION_MANAGER self -- @param Functional.Detection#DETECTION_BASE Detection -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:ReportDetected( Detection ) - self:F2() + function DETECTION_MANAGER:ProcessDetected( Detection ) + self:E() end - - --- Schedule the FAC reporting. - -- @param #DETECTION_MANAGER self - -- @param #number DelayTime The delay in seconds to wait the reporting. - -- @param #number ReportInterval The repeat interval in seconds for the reporting to happen repeatedly. - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:Schedule( DelayTime, ReportInterval ) - self:F2() - - self._ScheduleDelayTime = DelayTime - - self:SetReportInterval( ReportInterval ) - - self.FacScheduler = SCHEDULER:New(self, self._FacScheduler, { self, "DetectionManager" }, self._ScheduleDelayTime, self._ReportInterval ) - return self - end - - --- Report the detected @{Unit#UNIT}s detected within the @{Detection#DETECTION_BASE} object to the @{Set#SET_GROUP}s. - -- @param #DETECTION_MANAGER self - function DETECTION_MANAGER:_FacScheduler( SchedulerName ) - self:F2( { SchedulerName } ) - - return self:ProcessDetected( self.Detection ) - --- self.SetGroup:ForEachGroup( --- --- @param Wrapper.Group#GROUP Group --- function( Group ) --- if Group:IsAlive() then --- return self:ProcessDetected( self.Detection ) --- end --- end --- ) - --- return true - end end @@ -33376,52 +35114,108 @@ do -- DETECTION_REPORTING end -do -- DETECTION_DISPATCHER +--- **Tasking** - The TASK_A2G_DISPATCHER creates and manages player TASK_A2G tasks based on detected targets. +-- +-- === +-- +-- # 1) @{#TASK_A2G_DISPATCHER} class, extends @{#DETECTION_MANAGER} +-- +-- The @{#TASK_A2G_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of FAC (groups). +-- The FAC will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. +-- Find a summary below describing for which situation a task type is created: +-- +-- * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter. +-- * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter. +-- * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars. +-- +-- Other task types will follow... +-- +-- 3.1) TASK_A2G_DISPATCHER constructor: +-- -------------------------------------- +-- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK_A2G_DISPATCHER instance. +-- +-- === +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-03-09: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module Task_A2G_Dispatcher - --- DETECTION_DISPATCHER class. - -- @type DETECTION_DISPATCHER +do -- TASK_A2G_DISPATCHER + + --- TASK_A2G_DISPATCHER class. + -- @type TASK_A2G_DISPATCHER -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. -- @field Tasking.Mission#MISSION Mission - -- @field Wrapper.Group#GROUP CommandCenter -- @extends Tasking.DetectionManager#DETECTION_MANAGER - DETECTION_DISPATCHER = { - ClassName = "DETECTION_DISPATCHER", + TASK_A2G_DISPATCHER = { + ClassName = "TASK_A2G_DISPATCHER", Mission = nil, - CommandCenter = nil, Detection = nil, } - --- DETECTION_DISPATCHER constructor. - -- @param #DETECTION_DISPATCHER self - -- @param Set#SET_GROUP SetGroup - -- @param Functional.Detection#DETECTION_BASE Detection - -- @return #DETECTION_DISPATCHER self - function DETECTION_DISPATCHER:New( Mission, CommandCenter, SetGroup, Detection ) + --- TASK_A2G_DISPATCHER constructor. + -- @param #TASK_A2G_DISPATCHER self + -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. + -- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. + -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. + -- @return #TASK_A2G_DISPATCHER self + function TASK_A2G_DISPATCHER:New( Mission, SetGroup, Detection ) -- Inherits from DETECTION_MANAGER - local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #DETECTION_DISPATCHER + local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER self.Detection = Detection - self.CommandCenter = CommandCenter self.Mission = Mission - self:Schedule( 30 ) + self:AddTransition( "Started", "Assign", "Started" ) + + --- OnAfter Transition Handler for Event Assign. + -- @function [parent=#TASK_A2G_DISPATCHER] OnAfterAssign + -- @param #TASK_A2G_DISPATCHER self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Tasking.Task_A2G#TASK_A2G Task + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param #string PlayerName + + self:__Start( 5 ) + return self end --- Creates a SEAD task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @return Set#SET_UNIT TargetSetUnit: The target set of units. -- @return #nil If there are no targets to be set. - function DETECTION_DISPATCHER:EvaluateSEAD( DetectedArea ) - self:F( { DetectedArea.AreaID } ) + function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem ) + self:F( { DetectedItem.ItemID } ) - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone -- Determine if the set has radar targets. If it does, construct a SEAD task. local RadarCount = DetectedSet:HasSEAD() @@ -33441,19 +35235,19 @@ do -- DETECTION_DISPATCHER end --- Creates a CAS task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateCAS( DetectedArea ) - self:F( { DetectedArea.AreaID } ) + function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem ) + self:F( { DetectedItem.ItemID } ) - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone -- Determine if the set has radar targets. If it does, construct a SEAD task. local GroundUnitCount = DetectedSet:HasGroundUnits() - local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedArea ) + local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) if GroundUnitCount > 0 and FriendliesNearBy == true then @@ -33469,19 +35263,19 @@ do -- DETECTION_DISPATCHER end --- Creates a BAI task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateBAI( DetectedArea, FriendlyCoalition ) - self:F( { DetectedArea.AreaID } ) + function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition ) + self:F( { DetectedItem.ItemID } ) - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone -- Determine if the set has radar targets. If it does, construct a SEAD task. local GroundUnitCount = DetectedSet:HasGroundUnits() - local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedArea ) + local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) if GroundUnitCount > 0 and FriendliesNearBy == false then @@ -33497,16 +35291,16 @@ do -- DETECTION_DISPATCHER end --- Evaluates the removal of the Task from the Mission. - -- Can only occur when the DetectedArea is Changed AND the state of the Task is "Planned". - -- @param #DETECTION_DISPATCHER self + -- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned". + -- @param #TASK_A2G_DISPATCHER self -- @param Tasking.Mission#MISSION Mission -- @param Tasking.Task#TASK Task - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedArea ) + function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedItem ) if Task then - if Task:IsStatePlanned() and DetectedArea.Changed == true then + if Task:IsStatePlanned() and DetectedItem.Changed == true then self:E( "Removing Tasking: " .. Task:GetTaskName() ) Task = Mission:RemoveTask( Task ) end @@ -33517,140 +35311,463 @@ do -- DETECTION_DISPATCHER --- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS Detection The detection created by the @{Detection#DETECTION_AREAS} object. + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. - function DETECTION_DISPATCHER:ProcessDetected( Detection ) - self:F2() + function TASK_A2G_DISPATCHER:ProcessDetected( Detection ) + self:E() local AreaMsg = {} local TaskMsg = {} local ChangeMsg = {} local Mission = self.Mission + local ReportSEAD = REPORT:New( "- SEAD Tasks:") + local ReportCAS = REPORT:New( "- CAS Tasks:") + local ReportBAI = REPORT:New( "- BAI Tasks:") + local ReportChanges = REPORT:New( " - Changes:" ) --- First we need to the detected targets. - for DetectedAreaID, DetectedAreaData in ipairs( Detection:GetDetectedAreas() ) do + for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do - local DetectedArea = DetectedAreaData -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone - self:E( { "Targets in DetectedArea", DetectedArea.AreaID, DetectedSet:Count(), tostring( DetectedArea ) } ) + local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set -- Functional.Detection#DETECTION_BASE.DetectedSet + local DetectedZone = DetectedItem.Zone + self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) DetectedSet:Flush() - local AreaID = DetectedArea.AreaID + local ItemID = DetectedItem.ItemID -- Evaluate SEAD Tasking - local SEADTask = Mission:GetTask( "SEAD." .. AreaID ) - SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedArea ) + local SEADTask = Mission:GetTask( string.format( "SEAD.%03d", ItemID ) ) + SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedItem ) if not SEADTask then - local TargetSetUnit = self:EvaluateSEAD( DetectedArea ) -- Returns a SetUnit if there are targets to be SEADed... + local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... if TargetSetUnit then - SEADTask = Mission:AddTask( TASK_SEAD:New( Mission, self.SetGroup, "SEAD." .. AreaID, TargetSetUnit , DetectedZone ) ) + local Task = TASK_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", ItemID ), TargetSetUnit ) + Task:SetTargetZone( DetectedZone ) + Task:SetDispatcher( self ) + SEADTask = Mission:AddTask( Task ) end end if SEADTask and SEADTask:IsStatePlanned() then - self:E( "Planned" ) - --SEADTask:SetPlannedMenu() - TaskMsg[#TaskMsg+1] = " - " .. SEADTask:GetStateString() .. " SEAD " .. AreaID .. " - " .. SEADTask.TargetSetUnit:GetUnitTypesText() + ReportSEAD:Add( string.format( " - %s.%02d - %s", "SEAD", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) end -- Evaluate CAS Tasking - local CASTask = Mission:GetTask( "CAS." .. AreaID ) - CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedArea ) + local CASTask = Mission:GetTask( string.format( "CAS.%03d", ItemID ) ) + CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedItem ) if not CASTask then - local TargetSetUnit = self:EvaluateCAS( DetectedArea ) -- Returns a SetUnit if there are targets to be SEADed... + local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... if TargetSetUnit then - CASTask = Mission:AddTask( TASK_A2G:New( Mission, self.SetGroup, "CAS." .. AreaID, "CAS", TargetSetUnit , DetectedZone, DetectedArea.NearestFAC ) ) + local Task = TASK_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", ItemID ), TargetSetUnit ) + Task:SetTargetZone( DetectedZone ) + Task:SetDispatcher( self ) + CASTask = Mission:AddTask( Task ) end end if CASTask and CASTask:IsStatePlanned() then - --CASTask:SetPlannedMenu() - TaskMsg[#TaskMsg+1] = " - " .. CASTask:GetStateString() .. " CAS " .. AreaID .. " - " .. CASTask.TargetSetUnit:GetUnitTypesText() + ReportCAS:Add( string.format( " - %s.%02d - %s", "CAS", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) end -- Evaluate BAI Tasking - local BAITask = Mission:GetTask( "BAI." .. AreaID ) - BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedArea ) + local BAITask = Mission:GetTask( string.format( "BAI.%03d", ItemID ) ) + BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedItem ) if not BAITask then - local TargetSetUnit = self:EvaluateBAI( DetectedArea, self.CommandCenter:GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed... + local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed... if TargetSetUnit then - BAITask = Mission:AddTask( TASK_A2G:New( Mission, self.SetGroup, "BAI." .. AreaID, "BAI", TargetSetUnit , DetectedZone, DetectedArea.NearestFAC ) ) + local Task = TASK_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", ItemID ), TargetSetUnit ) + Task:SetTargetZone( DetectedZone ) + Task:SetDispatcher( self ) + BAITask = Mission:AddTask( Task ) end end if BAITask and BAITask:IsStatePlanned() then - --BAITask:SetPlannedMenu() - TaskMsg[#TaskMsg+1] = " - " .. BAITask:GetStateString() .. " BAI " .. AreaID .. " - " .. BAITask.TargetSetUnit:GetUnitTypesText() - end - - if #TaskMsg > 0 then - - local ThreatLevel = Detection:GetTreatLevelA2G( DetectedArea ) - - local DetectedAreaVec3 = DetectedZone:GetVec3() - local DetectedAreaPointVec3 = POINT_VEC3:New( DetectedAreaVec3.x, DetectedAreaVec3.y, DetectedAreaVec3.z ) - local DetectedAreaPointLL = DetectedAreaPointVec3:ToStringLL( 3, true ) - AreaMsg[#AreaMsg+1] = string.format( " - Area #%d - %s - Threat Level [%s] (%2d)", - DetectedAreaID, - DetectedAreaPointLL, - string.rep( "â– ", ThreatLevel ), - ThreatLevel - ) - - -- Loop through the changes ... - local ChangeText = Detection:GetChangeText( DetectedArea ) - - if ChangeText ~= "" then - ChangeMsg[#ChangeMsg+1] = string.gsub( string.gsub( ChangeText, "\n", "%1 - " ), "^.", " - %1" ) - end + ReportBAI:Add( string.format( " - %s.%02d - %s", "BAI", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) end + + -- Loop through the changes ... + local ChangeText = Detection:GetChangeText( DetectedItem ) + ReportChanges:Add( ChangeText ) + + -- OK, so the tasking has been done, now delete the changes reported for the area. - Detection:AcceptChanges( DetectedArea ) + Detection:AcceptChanges( DetectedItem ) end -- TODO set menus using the HQ coordinator Mission:GetCommandCenter():SetMenu() - if #AreaMsg > 0 then - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if not TaskGroup:GetState( TaskGroup, "Assigned" ) then - self.CommandCenter:MessageToGroup( - string.format( "HQ Reporting - Target areas for mission '%s':\nAreas:\n%s\n\nTasks:\n%s\n\nChanges:\n%s ", - self.Mission:GetName(), - table.concat( AreaMsg, "\n" ), - table.concat( TaskMsg, "\n" ), - table.concat( ChangeMsg, "\n" ) - ), self:GetReportDisplayTime(), TaskGroup - ) - end + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + if not TaskGroup:GetState( TaskGroup, "Assigned" ) then + Mission:GetCommandCenter():MessageToGroup( + string.format( "HQ Reporting - Planned tasks for mission '%s':\n%s\n", + self.Mission:GetName(), + string.format( "%s\n\n%s\n\n%s\n\n%s", ReportSEAD:Text(), ReportCAS:Text(), ReportBAI:Text(), ReportChanges:Text() + ) + ), TaskGroup + ) end end return true end -end--- This module contains the TASK_SEAD classes. +end--- **Tasking** - The TASK_A2G models tasks for players in Air to Ground engagements. -- --- 1) @{#TASK_SEAD} class, extends @{Task#TASK} --- ================================================= --- The @{#TASK_SEAD} class defines a SEAD task for a @{Set} of Target Units, located at a Target Zone, +-- ![Banner Image](..\Presentations\TASK_A2G\Dia1.JPG) +-- +-- +-- # 1) @{Task_A2G#TASK_A2G} class, extends @{Task#TASK} +-- +-- The @{#TASK_A2G} class defines Air To Ground tasks for a @{Set} of Target Units, -- based on the tasking capabilities defined in @{Task#TASK}. --- The TASK_SEAD is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses: +-- The TASK_A2G is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses: -- -- * **None**: Start of the process --- * **Planned**: The SEAD task is planned. Upon Planned, the sub-process @{Process_Fsm.Assign#ACT_ASSIGN_ACCEPT} is started to accept the task. --- * **Assigned**: The SEAD task is assigned to a @{Group#GROUP}. Upon Assigned, the sub-process @{Process_Fsm.Route#ACT_ROUTE} is started to route the active Units in the Group to the attack zone. --- * **Success**: The SEAD task is successfully completed. Upon Success, the sub-process @{Process_SEAD#PROCESS_SEAD} is started to follow-up successful SEADing of the targets assigned in the task. --- * **Failed**: The SEAD task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. +-- * **Planned**: The A2G task is planned. +-- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}. +-- * **Success**: The A2G task is successfully completed. +-- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. +-- +-- # 1.1) Set the scoring of achievements in an A2G attack. +-- +-- Scoring or penalties can be given in the following circumstances: +-- +-- * @{#TASK_A2G.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed. +-- * @{#TASK_A2G.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed. +-- * @{#TASK_A2G.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed. +-- +-- # 2) @{Task_A2G#TASK_SEAD} class, extends @{Task_A2G#TASK_A2G} +-- +-- The @{#TASK_SEAD} class defines a SEAD task for a @{Set} of Target Units. -- -- === -- --- ### Authors: FlightControl - Design and Programming +-- # 3) @{Task_A2G#TASK_CAS} class, extends @{Task_A2G#TASK_A2G} -- --- @module Task_SEAD +-- The @{#TASK_CAS} class defines a CAS task for a @{Set} of Target Units. +-- +-- === +-- +-- # 4) @{Task_A2G#TASK_BAI} class, extends @{Task_A2G#TASK_A2G} +-- +-- The @{#TASK_BAI} class defines a BAI task for a @{Set} of Target Units. +-- +-- ==== +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-03-09: Revised version. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **[WingThor]**: Concept, Advice & Testing. +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module Task_A2G +do -- TASK_A2G + + --- The TASK_A2G class + -- @type TASK_A2G + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + TASK_A2G = { + ClassName = "TASK_A2G", + } + + --- Instantiates a new TASK_A2G. + -- @param #TASK_A2G self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Set#SET_UNIT UnitSetTargets + -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. + -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. + -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. + -- @return #TASK_A2G self + function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType ) + local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) -- Tasking.Task#TASK_A2G + self:F() + + self.TargetSetUnit = TargetSetUnit + self.TaskType = TaskType + + Mission:AddTask( self ) + + local Fsm = self:GetUnitProcess() + + + Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToRendezVous", Rejected = "Reject" } ) + + Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) + Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) + Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) + + Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" ) + + Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" ) + Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" ) + + Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, self.TaskType ), { Accounted = "Success" } ) + Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" ) + Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} ) + Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} ) + Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" ) + + Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) + Fsm:AddTransition( "Accounted", "Success", "Success" ) + Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) + Fsm:AddTransition( "Failed", "Fail", "Failed" ) + + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2G#TASK_A2G Task + function Fsm:onafterRouteToRendezVous( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- Determine the first Unit from the self.RendezVousSetUnit + + if Task:GetRendezVousZone( TaskUnit ) then + self:__RouteToRendezVousZone( 0.1 ) + else + if Task:GetRendezVousPointVec2( TaskUnit ) then + self:__RouteToRendezVousPoint( 0.1 ) + else + self:__ArriveAtRendezVous( 0.1 ) + end + end + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task#TASK_A2G Task + function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- Determine the first Unit from the self.TargetSetUnit + + self:__Engage( 0.1 ) + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task#TASK_A2G Task + function Fsm:onafterEngage( TaskUnit, Task ) + self:E( { self } ) + self:__Account( 0.1 ) + self:__RouteToTarget(0.1 ) + self:__RouteToTargets( -10 ) + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2G#TASK_A2G Task + function Fsm:onafterRouteToTarget( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- Determine the first Unit from the self.TargetSetUnit + + if Task:GetTargetZone( TaskUnit ) then + self:__RouteToTargetZone( 0.1 ) + else + local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT + if TargetUnit then + local PointVec2 = TargetUnit:GetPointVec2() + self:T( { TargetPointVec2 = PointVec2, PointVec2:GetX(), PointVec2:GetAlt(), PointVec2:GetZ() } ) + Task:SetTargetPointVec2( TargetUnit:GetPointVec2(), TaskUnit ) + end + self:__RouteToTargetPoint( 0.1 ) + end + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2G#TASK_A2G Task + function Fsm:onafterRouteToTargets( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT + if TargetUnit then + Task:SetTargetPointVec2( TargetUnit:GetPointVec2(), TaskUnit ) + end + self:__RouteToTargets( -10 ) + end + + return self + + end + + --- @param #TASK_A2G self + function TASK_A2G:GetPlannedMenuText() + return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" + end + + --- @param #TASK_A2G self + -- @param Core.Point#POINT_VEC2 RendezVousPointVec2 The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map. + -- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2G:SetRendezVousPointVec2( RendezVousPointVec2, RendezVousRange, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + ActRouteRendezVous:SetPointVec2( RendezVousPointVec2 ) + ActRouteRendezVous:SetRange( RendezVousRange ) + end + + --- @param #TASK_A2G self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Point#POINT_VEC2 The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map. + -- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. + function TASK_A2G:GetRendezVousPointVec2( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + return ActRouteRendezVous:GetPointVec2(), ActRouteRendezVous:GetRange() + end + + + + --- @param #TASK_A2G self + -- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2G:SetRendezVousZone( RendezVousZone, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + ActRouteRendezVous:SetZone( RendezVousZone ) + end + + --- @param #TASK_A2G self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Zone#ZONE_BASE The Zone object where the RendezVous is located on the map. + function TASK_A2G:GetRendezVousZone( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + return ActRouteRendezVous:GetZone() + end + + --- @param #TASK_A2G self + -- @param Core.Point#POINT_VEC2 TargetPointVec2 The PointVec2 object where the Target is located on the map. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2G:SetTargetPointVec2( TargetPointVec2, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + ActRouteTarget:SetPointVec2( TargetPointVec2 ) + end + + + --- @param #TASK_A2G self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Point#POINT_VEC2 The PointVec2 object where the Target is located on the map. + function TASK_A2G:GetTargetPointVec2( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + return ActRouteTarget:GetPointVec2() + end + + + --- @param #TASK_A2G self + -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2G:SetTargetZone( TargetZone, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + ActRouteTarget:SetZone( TargetZone ) + end + + + --- @param #TASK_A2G self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. + function TASK_A2G:GetTargetZone( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + return ActRouteTarget:GetZone() + end + + --- Set a score when a target in scope of the A2G attack, has been destroyed . + -- @param #TASK_A2G self + -- @param #string Text The text to display to the player, when the target has been destroyed. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G + function TASK_A2G:SetScoreOnDestroy( Text, Score, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScoreProcess( "Engaging", "Account", "Account", Text, Score ) + + return self + end + + --- Set a score when all the targets in scope of the A2G attack, have been destroyed. + -- @param #TASK_A2G self + -- @param #string Text The text to display to the player, when all targets hav been destroyed. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G + function TASK_A2G:SetScoreOnSuccess( Text, Score, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Success", Text, Score ) + + return self + end + + --- Set a penalty when the A2G attack has failed. + -- @param #TASK_A2G self + -- @param #string Text The text to display to the player, when the A2G attack has failed. + -- @param #number Penalty The penalty in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G + function TASK_A2G:SetPenaltyOnFailed( Text, Penalty, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Failed", Text, Penalty ) + + return self + end + + +end do -- TASK_SEAD @@ -33669,130 +35786,76 @@ do -- TASK_SEAD -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. -- @param Set#SET_UNIT UnitSetTargets - -- @param Core.Zone#ZONE_BASE TargetZone + -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. + -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. + -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. -- @return #TASK_SEAD self - function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TargetZone ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, "SEAD" ) ) -- Tasking.Task_SEAD#TASK_SEAD + function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit ) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD" ) ) -- #TASK_SEAD self:F() - - self.TargetSetUnit = TargetSetUnit - self.TargetZone = TargetZone - local Fsm = self:GetUnitProcess() - - Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "Route", Rejected = "Eject" } ) - Fsm:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } ) - Fsm:AddTransition( "Rejected", "Eject", "Planned" ) - Fsm:AddTransition( "Arrived", "Update", "Updated" ) - Fsm:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "SEAD" ), { Accounted = "Success" } ) - Fsm:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) ) - Fsm:AddTransition( "Accounted", "Success", "Success" ) - Fsm:AddTransition( "Failed", "Fail", "Failed" ) - - function Fsm:onenterUpdated( TaskUnit ) - self:E( { self } ) - self:Account() - self:Smoke() - end - --- _EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventPlayerLeaveUnit, self ) --- _EVENTDISPATCHER:OnDead( self._EventDead, self ) --- _EVENTDISPATCHER:OnCrash( self._EventDead, self ) --- _EVENTDISPATCHER:OnPilotDead( self._EventDead, self ) - return self - end - - --- @param #TASK_SEAD self - function TASK_SEAD:GetPlannedMenuText() - return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" - end - -end ---- (AI) (SP) (MP) Tasking for Air to Ground Processes. --- --- 1) @{#TASK_A2G} class, extends @{Task#TASK} --- ================================================= --- The @{#TASK_A2G} class defines a CAS or BAI task of a @{Set} of Target Units, --- located at a Target Zone, based on the tasking capabilities defined in @{Task#TASK}. --- The TASK_A2G is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses: --- --- * **None**: Start of the process --- * **Planned**: The SEAD task is planned. Upon Planned, the sub-process @{Process_Fsm.Assign#ACT_ASSIGN_ACCEPT} is started to accept the task. --- * **Assigned**: The SEAD task is assigned to a @{Group#GROUP}. Upon Assigned, the sub-process @{Process_Fsm.Route#ACT_ROUTE} is started to route the active Units in the Group to the attack zone. --- * **Success**: The SEAD task is successfully completed. Upon Success, the sub-process @{Process_SEAD#PROCESS_SEAD} is started to follow-up successful SEADing of the targets assigned in the task. --- * **Failed**: The SEAD task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. --- --- === --- --- ### Authors: FlightControl - Design and Programming --- --- @module Task_A2G + end +end -do -- TASK_A2G +do -- TASK_BAI - --- The TASK_A2G class - -- @type TASK_A2G + --- The TASK_BAI class + -- @type TASK_BAI + -- @field Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - TASK_A2G = { - ClassName = "TASK_A2G", + TASK_BAI = { + ClassName = "TASK_BAI", } - --- Instantiates a new TASK_A2G. - -- @param #TASK_A2G self + --- Instantiates a new TASK_BAI. + -- @param #TASK_BAI self -- @param Tasking.Mission#MISSION Mission -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. - -- @param #string TaskType BAI or CAS -- @param Set#SET_UNIT UnitSetTargets - -- @param Core.Zone#ZONE_BASE TargetZone - -- @return #TASK_A2G self - function TASK_A2G:New( Mission, SetGroup, TaskName, TaskType, TargetSetUnit, TargetZone, FACUnit ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) + -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. + -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. + -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. + -- @return #TASK_BAI self + function TASK_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit ) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI" ) ) -- #TASK_BAI self:F() - - self.TargetSetUnit = TargetSetUnit - self.TargetZone = TargetZone - self.FACUnit = FACUnit - local A2GUnitProcess = self:GetUnitProcess() - - A2GUnitProcess:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "Attack the Area" ), { Assigned = "Route", Rejected = "Eject" } ) - A2GUnitProcess:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } ) - A2GUnitProcess:AddTransition( "Rejected", "Eject", "Planned" ) - A2GUnitProcess:AddTransition( "Arrived", "Update", "Updated" ) - A2GUnitProcess:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "Attack" ), { Accounted = "Success" } ) - A2GUnitProcess:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) ) - --Fsm:AddProcess ( "Updated", "JTAC", PROCESS_JTAC:New( self, TaskUnit, self.TargetSetUnit, self.FACUnit ) ) - A2GUnitProcess:AddTransition( "Accounted", "Success", "Success" ) - A2GUnitProcess:AddTransition( "Failed", "Fail", "Failed" ) - - function A2GUnitProcess:onenterUpdated( TaskUnit ) - self:E( { self } ) - self:Account() - self:Smoke() - end - - - - --_EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventPlayerLeaveUnit, self ) - --_EVENTDISPATCHER:OnDead( self._EventDead, self ) - --_EVENTDISPATCHER:OnCrash( self._EventDead, self ) - --_EVENTDISPATCHER:OnPilotDead( self._EventDead, self ) - return self - end + end + +end + +do -- TASK_CAS + + --- The TASK_CAS class + -- @type TASK_CAS + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + TASK_CAS = { + ClassName = "TASK_CAS", + } - --- @param #TASK_A2G self - function TASK_A2G:GetPlannedMenuText() - return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" - end - - end - - + --- Instantiates a new TASK_CAS. + -- @param #TASK_CAS self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Set#SET_UNIT UnitSetTargets + -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. + -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. + -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. + -- @return #TASK_CAS self + function TASK_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit ) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS" ) ) -- #TASK_CAS + self:F() + + return self + end +end --- The main include file for the MOOSE system. -- Test of permissions @@ -33812,6 +35875,7 @@ Include.File( "Core/Set" ) Include.File( "Core/Point" ) Include.File( "Core/Message" ) Include.File( "Core/Fsm" ) +Include.File( "Core/Radio" ) --- Wrapper Classes Include.File( "Wrapper/Object" ) @@ -33854,7 +35918,7 @@ Include.File( "Tasking/CommandCenter" ) Include.File( "Tasking/Mission" ) Include.File( "Tasking/Task" ) Include.File( "Tasking/DetectionManager" ) -Include.File( "Tasking/Task_SEAD" ) +Include.File( "Tasking/Task_A2G_Dispatcher") Include.File( "Tasking/Task_A2G" ) diff --git a/Moose Mission Setup/Moose.lua b/Moose Mission Setup/Moose.lua index fcb1e53a6..0135a9cc2 100644 --- a/Moose Mission Setup/Moose.lua +++ b/Moose Mission Setup/Moose.lua @@ -1,5 +1,5 @@ env.info( '*** MOOSE STATIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20170315_1916' ) +env.info( 'Moose Generation Timestamp: 20170328_1316' ) local base = _G Include = {} @@ -2796,9 +2796,54 @@ end -- -- === -- --- # 1) @{#BASE} class +-- The @{#BASE} class is the core root class from where every other class in moose is derived. -- --- All classes within the MOOSE framework are derived from the @{#BASE} class. +-- === +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) +-- YYYY-MM-DD: CLASS:**NewFunction( Params )** added +-- +-- Hereby the change log: +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * None. +-- +-- ### Authors: +-- +-- * **FlightControl**: Design & Programming +-- +-- @module Base + + + +local _TraceOnOff = true +local _TraceLevel = 1 +local _TraceAll = false +local _TraceClass = {} +local _TraceClassMethod = {} + +local _ClassID = 0 + +--- @type BASE +-- @field ClassName The name of the class. +-- @field ClassID The ID number of the class. +-- @field ClassNameAndID The name of the class concatenated with the ID number of the class. + +--- # 1) #BASE class +-- +-- All classes within the MOOSE framework are derived from the BASE class. -- -- BASE provides facilities for : -- @@ -2958,50 +3003,11 @@ end -- -- * @{#BASE.Inherit}: Inherits from a class. -- * @{#BASE.GetParent}: Returns the parent object from the object it is handling, or nil if there is no parent object. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) --- YYYY-MM-DD: CLASS:**NewFunction( Params )** added --- --- Hereby the change log: -- -- === -- --- # **AUTHORS and CONTRIBUTIONS** +-- @field #BASE BASE -- --- ### Contributions: --- --- * None. --- --- ### Authors: --- --- * **FlightControl**: Design & Programming --- --- @module Base - - - -local _TraceOnOff = true -local _TraceLevel = 1 -local _TraceAll = false -local _TraceClass = {} -local _TraceClassMethod = {} - -local _ClassID = 0 - ---- The BASE Class --- @type BASE --- @field ClassName The name of the class. --- @field ClassID The ID number of the class. --- @field ClassNameAndID The name of the class concatenated with the ID number of the class. BASE = { ClassName = "BASE", ClassID = 0, @@ -3048,12 +3054,14 @@ function BASE:_Destructor() --self:EventRemoveAll() end + +-- THIS IS WHY WE NEED LUA 5.2 ... function BASE:_SetDestructor() -- TODO: Okay, this is really technical... -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak... -- Therefore, I am parking this logic until I've properly discussed all this with the community. - --[[ + local proxy = newproxy(true) local proxyMeta = getmetatable(proxy) @@ -3068,7 +3076,7 @@ function BASE:_SetDestructor() -- table is about to be garbage-collected - then the __gc hook -- will be invoked and the destructor called rawset( self, '__proxy', proxy ) - --]] + end --- This is the worker method to inherit from a parent class. @@ -3084,7 +3092,7 @@ function BASE:Inherit( Child, Parent ) setmetatable( Child, Parent ) Child.__index = Child - Child:_SetDestructor() + --Child:_SetDestructor() end --self:T( 'Inherited from ' .. Parent.ClassName ) return Child @@ -3696,8 +3704,12 @@ end ---- This module contains the SCHEDULER class. +--- **Core** - SCHEDULER prepares and handles the **execution of functions over scheduled time (intervals)**. -- +-- ![Banner Image](..\Presentations\SCHEDULER\Dia1.JPG) +-- +-- === +-- -- # 1) @{Scheduler#SCHEDULER} class, extends @{Base#BASE} -- -- The @{Scheduler#SCHEDULER} class creates schedule. @@ -3845,6 +3857,13 @@ function SCHEDULER:Remove( ScheduleID ) _SCHEDULEDISPATCHER:Remove( self, ScheduleID ) end +--- Clears all pending schedules. +-- @param #SCHEDULER self +function SCHEDULER:Clear() + self:F3( ) + + _SCHEDULEDISPATCHER:Clear( self ) +end @@ -3925,7 +3944,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr -- Initialize the ObjectSchedulers array, which is a weakly coupled table. -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. - self.ObjectSchedulers = self.ObjectSchedulers or {} -- setmetatable( {}, { __mode = "v" } ) + self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) -- or {} if Scheduler.MasterObject then self.ObjectSchedulers[self.CallID] = Scheduler @@ -4042,11 +4061,15 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID ) if CallID then local Schedule = self.Schedule[Scheduler] - Schedule[CallID].ScheduleID = timer.scheduleFunction( - Schedule[CallID].CallHandler, - CallID, - timer.getTime() + Schedule[CallID].Start - ) + -- Only start when there is no ScheduleID defined! + -- This prevents to "Start" the scheduler twice with the same CallID... + if not Schedule[CallID].ScheduleID then + Schedule[CallID].ScheduleID = timer.scheduleFunction( + Schedule[CallID].CallHandler, + CallID, + timer.getTime() + Schedule[CallID].Start + ) + end else for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do self:Start( Scheduler, CallID ) -- Recursive @@ -4059,7 +4082,12 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) if CallID then local Schedule = self.Schedule[Scheduler] - timer.removeFunction( Schedule[CallID].ScheduleID ) + -- Only stop when there is a ScheduleID defined for the CallID. + -- So, when the scheduler was stopped before, do nothing. + if Schedule[CallID].ScheduleID then + timer.removeFunction( Schedule[CallID].ScheduleID ) + Schedule[CallID].ScheduleID = nil + end else for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do self:Stop( Scheduler, CallID ) -- Recursive @@ -4067,6 +4095,14 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) end end +function SCHEDULEDISPATCHER:Clear( Scheduler ) + self:F2( { Scheduler = Scheduler } ) + + for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do + self:Stop( Scheduler, CallID ) -- Recursive + end +end + --- **Core** - EVENT models DCS **event dispatching** using a **publish-subscribe** model. @@ -4355,7 +4391,7 @@ local _EVENTMETA = { }, [world.event.S_EVENT_TAKEOFF] = { Order = 1, - Event = "OnEventTakeOff", + Event = "OnEventTakeoff", Text = "S_EVENT_TAKEOFF" }, [world.event.S_EVENT_LAND] = { @@ -4496,11 +4532,11 @@ function EVENT:Init( EventID, EventClass ) -- Each event has a subtable of EventClasses, ordered by EventPriority. local EventPriority = EventClass:GetEventPriority() if not self.Events[EventID][EventPriority] then - self.Events[EventID][EventPriority] = {} + self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "k" } ) end if not self.Events[EventID][EventPriority][EventClass] then - self.Events[EventID][EventPriority][EventClass] = setmetatable( {}, { __mode = "k" } ) + self.Events[EventID][EventPriority][EventClass] = setmetatable( {}, { __mode = "v" } ) end return self.Events[EventID][EventPriority][EventClass] end @@ -4570,11 +4606,11 @@ end -- @param EventClass The instance of the class for which the event is. -- @param #function OnEventFunction -- @return #EVENT -function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, OnEventFunction ) +function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EventID ) self:F2( EventTemplate.name ) for EventUnitID, EventUnit in pairs( EventTemplate.units ) do - OnEventFunction( self, EventUnit.name, EventFunction, EventClass ) + self:OnEventForUnit( EventUnit.name, EventFunction, EventClass, EventID ) end return self end @@ -4588,9 +4624,9 @@ end function EVENT:OnEventGeneric( EventFunction, EventClass, EventID ) self:F2( { EventID } ) - local Event = self:Init( EventID, EventClass ) - Event.EventFunction = EventFunction - Event.EventClass = EventClass + local EventData = self:Init( EventID, EventClass ) + EventData.EventFunction = EventFunction + EventData.EventClass = EventClass return self end @@ -4606,13 +4642,13 @@ end function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID ) self:F2( UnitName ) - local Event = self:Init( EventID, EventClass ) - if not Event.EventUnit then - Event.EventUnit = {} + local EventData = self:Init( EventID, EventClass ) + if not EventData.EventUnit then + EventData.EventUnit = {} end - Event.EventUnit[UnitName] = {} - Event.EventUnit[UnitName].EventFunction = EventFunction - Event.EventUnit[UnitName].EventClass = EventClass + EventData.EventUnit[UnitName] = {} + EventData.EventUnit[UnitName].EventFunction = EventFunction + EventData.EventUnit[UnitName].EventClass = EventClass return self end @@ -4647,51 +4683,11 @@ do -- OnBirth function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnBirthForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Birth ) return self end - --- Set a new listener for an S_EVENT_BIRTH event, and registers the unit born. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnBirth( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_BIRTH ) - - return self - end - - --- Set a new listener for an S_EVENT_BIRTH event. - -- @param #EVENT self - -- @param #string EventDCSUnitName The id of the unit for the event to be handled. - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnBirthForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_BIRTH ) - - return self - end - - --- Stop listening to S_EVENT_BIRTH event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnBirthRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_BIRTH ) - - return self - end - - end do -- OnCrash @@ -4705,49 +4701,10 @@ do -- OnCrash function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnCrashForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Crash ) return self end - - --- Set a new listener for an S_EVENT_CRASH event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnCrash( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_CRASH ) - - return self - end - - --- Set a new listener for an S_EVENT_CRASH event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnCrashForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_CRASH ) - - return self - end - - --- Stop listening to S_EVENT_CRASH event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnCrashRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_CRASH ) - - return self - end end @@ -4762,96 +4719,13 @@ do -- OnDead function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnDeadForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Dead ) return self end - --- Set a new listener for an S_EVENT_DEAD event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnDead( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_DEAD ) - - return self - end - - - --- Set a new listener for an S_EVENT_DEAD event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnDeadForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_DEAD ) - - return self - end - - --- Stop listening to S_EVENT_DEAD event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnDeadRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_DEAD ) - - return self - end - - end -do -- OnPilotDead - - --- Set a new listener for an S_EVENT_PILOT_DEAD event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnPilotDead( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_PILOT_DEAD ) - - return self - end - - --- Set a new listener for an S_EVENT_PILOT_DEAD event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnPilotDeadForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_PILOT_DEAD ) - - return self - end - - --- Stop listening to S_EVENT_PILOT_DEAD event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnPilotDeadRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_PILOT_DEAD ) - - return self - end - -end do -- OnLand --- Create an OnLand event handler for a group @@ -4863,38 +4737,11 @@ do -- OnLand function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnLandForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Land ) return self end - --- Set a new listener for an S_EVENT_LAND event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnLandForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_LAND ) - - return self - end - - --- Stop listening to S_EVENT_LAND event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnLandRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_LAND ) - - return self - end - - end do -- OnTakeOff @@ -4907,38 +4754,11 @@ do -- OnTakeOff function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnTakeOffForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Takeoff ) return self end - --- Set a new listener for an S_EVENT_TAKEOFF event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnTakeOffForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_TAKEOFF ) - - return self - end - - --- Stop listening to S_EVENT_TAKEOFF event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnTakeOffRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_TAKEOFF ) - - return self - end - - end do -- OnEngineShutDown @@ -4952,210 +4772,11 @@ do -- OnEngineShutDown function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnEngineShutDownForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.EngineShutdown ) return self end - --- Set a new listener for an S_EVENT_ENGINE_SHUTDOWN event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnEngineShutDownForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_ENGINE_SHUTDOWN ) - - return self - end - - --- Stop listening to S_EVENT_ENGINE_SHUTDOWN event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnEngineShutDownRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_ENGINE_SHUTDOWN ) - - return self - end - -end - -do -- OnEngineStartUp - - --- Set a new listener for an S_EVENT_ENGINE_STARTUP event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnEngineStartUpForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_ENGINE_STARTUP ) - - return self - end - - --- Stop listening to S_EVENT_ENGINE_STARTUP event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnEngineStartUpRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_ENGINE_STARTUP ) - - return self - end - -end - -do -- OnShot - --- Set a new listener for an S_EVENT_SHOT event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnShot( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_SHOT ) - - return self - end - - --- Set a new listener for an S_EVENT_SHOT event for a unit. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnShotForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_SHOT ) - - return self - end - - --- Stop listening to S_EVENT_SHOT event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnShotRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_SHOT ) - - return self - end - - -end - -do -- OnHit - - --- Set a new listener for an S_EVENT_HIT event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnHit( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_HIT ) - - return self - end - - --- Set a new listener for an S_EVENT_HIT event. - -- @param #EVENT self - -- @param #string EventDCSUnitName - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnHitForUnit( EventDCSUnitName, EventFunction, EventClass ) - self:F2( EventDCSUnitName ) - - self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_HIT ) - - return self - end - - --- Stop listening to S_EVENT_HIT event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnHitRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_HIT ) - - return self - end - -end - -do -- OnPlayerEnterUnit - - --- Set a new listener for an S_EVENT_PLAYER_ENTER_UNIT event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnPlayerEnterUnit( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_PLAYER_ENTER_UNIT ) - - return self - end - - --- Stop listening to S_EVENT_PLAYER_ENTER_UNIT event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnPlayerEnterRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_PLAYER_ENTER_UNIT ) - - return self - end - -end - -do -- OnPlayerLeaveUnit - --- Set a new listener for an S_EVENT_PLAYER_LEAVE_UNIT event. - -- @param #EVENT self - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param Base#BASE EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnPlayerLeaveUnit( EventFunction, EventClass ) - self:F2() - - self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_PLAYER_LEAVE_UNIT ) - - return self - end - - --- Stop listening to S_EVENT_PLAYER_LEAVE_UNIT event. - -- @param #EVENT self - -- @param Base#BASE EventClass - -- @return #EVENT - function EVENT:OnPlayerLeaveRemove( EventClass ) - self:F2() - - self:Remove( EventClass, world.event.S_EVENT_PLAYER_LEAVE_UNIT ) - - return self - end - end @@ -5173,6 +4794,8 @@ function EVENT:onEvent( Event ) return errmsg end + self:E( _EVENTMETA[Event.id].Text, Event ) + if self and self.Events and self.Events[Event.id] then @@ -5271,6 +4894,11 @@ function EVENT:onEvent( Event ) if Event.weapon then Event.Weapon = Event.weapon Event.WeaponName = Event.Weapon:getTypeName() + Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit! + Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName() + Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition() + Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category + Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName() --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() end @@ -5278,7 +4906,9 @@ function EVENT:onEvent( Event ) local PriorityBegin = PriorityOrder == -1 and 5 or 1 local PriorityEnd = PriorityOrder == -1 and 1 or 5 - self:E( { _EVENTMETA[Event.id].Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) + if Event.IniObjectCategory ~= 3 then + self:E( { _EVENTMETA[Event.id].Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) + end for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do @@ -5299,8 +4929,10 @@ function EVENT:onEvent( Event ) -- First test if a EventFunction is Set, otherwise search for the default function if EventData.EventUnit[Event.IniDCSUnitName].EventFunction then - self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) + end + local Result, Value = xpcall( function() return EventData.EventUnit[Event.IniDCSUnitName].EventFunction( EventClass, Event ) @@ -5313,8 +4945,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end + local Result, Value = xpcall( function() return EventFunction( EventClass, Event ) @@ -5328,7 +4962,9 @@ function EVENT:onEvent( Event ) -- First test if a EventFunction is Set, otherwise search for the default function if EventData.EventUnit[Event.TgtDCSUnitName].EventFunction then - self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) + end local Result, Value = xpcall( function() @@ -5342,8 +4978,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end + local Result, Value = xpcall( function() return EventFunction( EventClass, Event ) @@ -5361,9 +4999,11 @@ function EVENT:onEvent( Event ) if EventData.EventGroup[Event.IniGroupName] then -- First test if a EventFunction is Set, otherwise search for the default function if EventData.EventGroup[Event.IniGroupName].EventFunction then - - self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) - + + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) + end + local Result, Value = xpcall( function() return EventData.EventGroup[Event.IniGroupName].EventFunction( EventClass, Event ) @@ -5376,8 +5016,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) + end + local Result, Value = xpcall( function() return EventFunction( EventClass, Event ) @@ -5389,8 +5031,10 @@ function EVENT:onEvent( Event ) if EventData.EventGroup[Event.TgtGroupName] then if EventData.EventGroup[Event.TgtGroupName].EventFunction then - self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) + end + local Result, Value = xpcall( function() return EventData.EventGroup[Event.TgtGroupName].EventFunction( EventClass, Event ) @@ -5403,7 +5047,9 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) + end local Result, Value = xpcall( function() @@ -5417,7 +5063,7 @@ function EVENT:onEvent( Event ) -- If the EventData is not bound to a specific unit, then call the EventClass EventFunction. -- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon. - if Event.IniDCSUnit and not EventData.EventUnit then + if (Event.IniDCSUnit or Event.WeaponUNIT) and not EventData.EventUnit then if EventClass == EventData.EventClass then @@ -5425,8 +5071,9 @@ function EVENT:onEvent( Event ) if EventData.EventFunction then -- There is an EventFunction defined, so call the EventFunction. - self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end local Result, Value = xpcall( function() return EventData.EventFunction( EventClass, Event ) @@ -5438,11 +5085,14 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end + local Result, Value = xpcall( function() - return EventFunction( EventClass, Event ) + local Result, Value = EventFunction( EventClass, Event ) + return Result, Value end, ErrorHandler ) end end @@ -5456,6 +5106,8 @@ function EVENT:onEvent( Event ) else self:E( { _EVENTMETA[Event.id].Text, Event } ) end + + Event = nil end --- The EVENTHANDLER structure @@ -5613,6 +5265,8 @@ do -- MENU_BASE } --- Consructor + -- @param #MENU_BASE + -- @return #MENU_BASE function MENU_BASE:New( MenuText, ParentMenu ) local MenuParentPath = {} @@ -5625,10 +5279,43 @@ do -- MENU_BASE self.MenuPath = nil self.MenuText = MenuText self.MenuParentPath = MenuParentPath + self.Menus = {} + self.MenuCount = 0 + self.MenuRemoveParent = false + self.MenuTime = timer.getTime() return self end + --- Gets a @{Menu} from a parent @{Menu} + -- @param #MENU_BASE self + -- @param #string MenuText The text of the child menu. + -- @return #MENU_BASE + function MENU_BASE:GetMenu( MenuText ) + self:F( { self.Menus, MenuText } ) + return self.Menus[MenuText] + end + + --- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}. + -- @param #MENU_BASE self + -- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}. + -- @return #MENU_BASE + function MENU_BASE:SetRemoveParent( RemoveParent ) + self:F( { RemoveParent } ) + self.MenuRemoveParent = RemoveParent + return self + end + + + --- Sets a time stamp for later prevention of menu removal. + -- @param #MENU_BASE self + -- @param MenuTime + -- @return #MENU_BASE + function MENU_BASE:SetTime( MenuTime ) + self.MenuTime = MenuTime + return self + end + end do -- MENU_COMMAND_BASE @@ -5636,7 +5323,7 @@ do -- MENU_COMMAND_BASE --- The MENU_COMMAND_BASE class -- @type MENU_COMMAND_BASE -- @field #function MenuCallHandler - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_COMMAND_BASE = { ClassName = "MENU_COMMAND_BASE", CommandMenuFunction = nil, @@ -5645,6 +5332,8 @@ do -- MENU_COMMAND_BASE } --- Constructor + -- @param #MENU_COMMAND_BASE + -- @return #MENU_COMMAND_BASE function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) @@ -5664,7 +5353,7 @@ do -- MENU_MISSION --- The MENU_MISSION class -- @type MENU_MISSION - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_MISSION = { ClassName = "MENU_MISSION" } @@ -5673,7 +5362,7 @@ do -- MENU_MISSION -- @param #MENU_MISSION self -- @param #string MenuText The text for the menu. -- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other). - -- @return #MENU_MISSION self + -- @return #MENU_MISSION function MENU_MISSION:New( MenuText, ParentMenu ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) @@ -5700,7 +5389,7 @@ do -- MENU_MISSION --- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept! -- @param #MENU_MISSION self - -- @return #MENU_MISSION self + -- @return #MENU_MISSION function MENU_MISSION:RemoveSubMenus() self:F( self.MenuPath ) @@ -5731,7 +5420,7 @@ do -- MENU_MISSION_COMMAND --- The MENU_MISSION_COMMAND class -- @type MENU_MISSION_COMMAND - -- @extends Menu#MENU_COMMAND_BASE + -- @extends Core.Menu#MENU_COMMAND_BASE MENU_MISSION_COMMAND = { ClassName = "MENU_MISSION_COMMAND" } @@ -5781,7 +5470,7 @@ do -- MENU_COALITION --- The MENU_COALITION class -- @type MENU_COALITION - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the planes within the red coalition. -- -- To test, join the planes, then look at the other radio menus (Option F10). @@ -5855,7 +5544,7 @@ do -- MENU_COALITION --- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept! -- @param #MENU_COALITION self - -- @return #MENU_COALITION self + -- @return #MENU_COALITION function MENU_COALITION:RemoveSubMenus() self:F( self.MenuPath ) @@ -5886,7 +5575,7 @@ do -- MENU_COALITION_COMMAND --- The MENU_COALITION_COMMAND class -- @type MENU_COALITION_COMMAND - -- @extends Menu#MENU_COMMAND_BASE + -- @extends Core.Menu#MENU_COMMAND_BASE MENU_COALITION_COMMAND = { ClassName = "MENU_COALITION_COMMAND" } @@ -5898,7 +5587,7 @@ do -- MENU_COALITION_COMMAND -- @param Menu#MENU_COALITION ParentMenu The parent menu. -- @param CommandMenuFunction A function that is called when the menu key is pressed. -- @param CommandMenuArgument An argument for the function. There can only be ONE argument given. So multiple arguments must be wrapped into a table. See the below example how to do this. - -- @return #MENU_COALITION_COMMAND self + -- @return #MENU_COALITION_COMMAND function MENU_COALITION_COMMAND:New( Coalition, MenuText, ParentMenu, CommandMenuFunction, ... ) local self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) @@ -5943,7 +5632,7 @@ do -- MENU_CLIENT --- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters. -- @type MENU_CLIENT - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the two clients of planes. -- -- Each client will receive a different menu structure. @@ -6084,7 +5773,7 @@ do -- MENU_CLIENT --- The MENU_CLIENT_COMMAND class -- @type MENU_CLIENT_COMMAND - -- @extends Menu#MENU_COMMAND + -- @extends Core.Menu#MENU_COMMAND MENU_CLIENT_COMMAND = { ClassName = "MENU_CLIENT_COMMAND" } @@ -6170,7 +5859,7 @@ do --- The MENU_GROUP class -- @type MENU_GROUP - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the two groups of planes. -- -- Each group will receive a different menu structure. @@ -6244,8 +5933,6 @@ do self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) MenuGroup._Menus[Path] = self - self.Menus = {} - self.MenuGroup = MenuGroup self.Path = Path self.MenuGroupID = MenuGroup:GetID() @@ -6255,8 +5942,10 @@ do self:T( { "Adding Menu ", MenuText, self.MenuParentPath } ) self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuGroupID, MenuText, self.MenuParentPath ) - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self + if self.ParentMenu and self.ParentMenu.Menus then + self.ParentMenu.Menus[MenuText] = self + self:F( { self.ParentMenu.Menus, MenuText } ) + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 end end @@ -6267,42 +5956,56 @@ do --- Removes the sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self + -- @param MenuTime -- @return #MENU_GROUP self - function MENU_GROUP:RemoveSubMenus() - self:F( self.MenuPath ) + function MENU_GROUP:RemoveSubMenus( MenuTime ) + self:F2( { self.MenuPath, MenuTime, self.MenuTime } ) - for MenuID, Menu in pairs( self.Menus ) do - Menu:Remove() + self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } ) + for MenuText, Menu in pairs( self.Menus ) do + Menu:Remove( MenuTime ) end end - + + --- Removes the main menu and sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self + -- @param MenuTime -- @return #nil - function MENU_GROUP:Remove() - self:F( { self.MenuGroupID, self.MenuPath } ) + function MENU_GROUP:Remove( MenuTime ) + self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - self:RemoveSubMenus() - - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] + self:RemoveSubMenus( MenuTime ) - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuPath] = nil + if not MenuTime or self.MenuTime ~= MenuTime then + if self.MenuGroup._Menus[self.Path] then + self = self.MenuGroup._Menus[self.Path] + + missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) + if self.ParentMenu then + self.ParentMenu.Menus[self.MenuText] = nil + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 + if self.ParentMenu.MenuCount == 0 then + if self.MenuRemoveParent == true then + self:T( "Removing Parent Menu " ) + self.ParentMenu:Remove() + end + end + end + self:T( { "Removing Group Menu:", self.MenuGroup:GetName(), self.MenuGroup._Menus[self.Path].Path } ) + self.MenuGroup._Menus[self.Path] = nil + self = nil end - self:E( self.MenuGroup._Menus[self.Path] ) - self.MenuGroup._Menus[self.Path] = nil - self = nil end + return nil end --- The MENU_GROUP_COMMAND class -- @type MENU_GROUP_COMMAND - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_GROUP_COMMAND = { ClassName = "MENU_GROUP_COMMAND" } @@ -6314,13 +6017,14 @@ do -- @param ParentMenu The parent menu. -- @param CommandMenuFunction A function that is called when the menu key is pressed. -- @param CommandMenuArgument An argument for the function. - -- @return Menu#MENU_GROUP_COMMAND self + -- @return #MENU_GROUP_COMMAND function MENU_GROUP_COMMAND:New( MenuGroup, MenuText, ParentMenu, CommandMenuFunction, ... ) MenuGroup._Menus = MenuGroup._Menus or {} local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText if MenuGroup._Menus[Path] then self = MenuGroup._Menus[Path] + self:T( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } ) else self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) MenuGroup._Menus[Path] = self @@ -6331,33 +6035,45 @@ do self.MenuText = MenuText self.ParentMenu = ParentMenu - self:T( { "Adding Command Menu ", MenuText, self.MenuParentPath } ) + self:T( { "Adding Group Command Menu:", MenuGroup:GetName(), MenuText, self.MenuParentPath } ) self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler, arg ) - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self + if self.ParentMenu and self.ParentMenu.Menus then + self.ParentMenu.Menus[MenuText] = self + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 + self:F( { ParentMenu.Menus, MenuText } ) end end - --self:F( { MenuGroup:GetName(), MenuText, ParentMenu.MenuPath } ) - return self end --- Removes a menu structure for a group. -- @param #MENU_GROUP_COMMAND self + -- @param MenuTime -- @return #nil - function MENU_GROUP_COMMAND:Remove() - self:F( { self.MenuGroupID, self.MenuPath } ) + function MENU_GROUP_COMMAND:Remove( MenuTime ) + self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] - - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - self.ParentMenu.Menus[self.MenuPath] = nil - self:E( self.MenuGroup._Menus[self.Path] ) - self.MenuGroup._Menus[self.Path] = nil - self = nil + if not MenuTime or self.MenuTime ~= MenuTime then + if self.MenuGroup._Menus[self.Path] then + self = self.MenuGroup._Menus[self.Path] + + missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) + self:T( { "Removing Group Command Menu:", self.MenuGroup:GetName(), self.MenuText, self.Path, self.MenuGroup._Menus[self.Path].Path } ) + + self.ParentMenu.Menus[self.MenuText] = nil + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 + if self.ParentMenu.MenuCount == 0 then + if self.MenuRemoveParent == true then + self:T( "Removing Parent Menu " ) + self.ParentMenu:Remove() + end + end + + self.MenuGroup._Menus[self.Path] = nil + self = nil + end end return nil @@ -6367,6 +6083,8 @@ end --- **Core** - ZONE classes define **zones** within your mission of **various forms**, with **various capabilities**. -- +-- ![Banner Image](..\Presentations\ZONE\Dia1.JPG) +-- -- === -- -- There are essentially two core functions that zones accomodate: @@ -6385,127 +6103,16 @@ end -- -- Each of these ZONE classes have a zone name, and specific parameters defining the zone type: -- --- * @{Zone#ZONE_BASE}: The ZONE_BASE class defining the base for all other zone classes. --- * @{Zone#ZONE_RADIUS}: The ZONE_RADIUS class defined by a zone name, a location and a radius. --- * @{Zone#ZONE}: The ZONE class, defined by the zone name as defined within the Mission Editor. --- * @{Zone#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Unit#UNIT} with a radius. --- * @{Zone#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. --- * @{Zone#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- * @{#ZONE_BASE}: The ZONE_BASE class defining the base for all other zone classes. +-- * @{#ZONE_RADIUS}: The ZONE_RADIUS class defined by a zone name, a location and a radius. +-- * @{#ZONE}: The ZONE class, defined by the zone name as defined within the Mission Editor. +-- * @{#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Unit#UNIT} with a radius. +-- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. +-- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- +-- === -- --- === --- --- # 1) @{Zone#ZONE_BASE} class, extends @{Base#BASE} --- --- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. --- --- ## 1.1) Each zone has a name: --- --- * @{#ZONE_BASE.GetName}(): Returns the name of the zone. --- --- ## 1.2) Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}: --- --- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a Vec2 is within the zone. --- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a Vec3 is within the zone. --- --- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones: --- --- * @{#ZONE_BASE.SetRandomizeProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% ) --- * @{#ZONE_BASE.GetRandomizeProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% ) --- * @{#ZONE_BASE.GetZoneMaybe}(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate. --- --- ## 1.4) A zone manages Vectors: --- --- * @{#ZONE_BASE.GetVec2}(): Returns the @{DCSTypes#Vec2} coordinate of the zone. --- * @{#ZONE_BASE.GetRandomVec2}(): Define a random @{DCSTypes#Vec2} within the zone. --- --- ## 1.5) A zone has a bounding square: --- --- * @{#ZONE_BASE.GetBoundingSquare}(): Get the outer most bounding square of the zone. --- --- ## 1.6) A zone can be marked: --- --- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color. --- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color. --- --- === --- --- # 2) @{Zone#ZONE_RADIUS} class, extends @{Zone#ZONE_BASE} --- --- The ZONE_RADIUS class defined by a zone name, a location and a radius. --- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties. --- --- ## 2.1) @{Zone#ZONE_RADIUS} constructor --- --- * @{#ZONE_RADIUS.New}(): Constructor. --- --- ## 2.2) Manage the radius of the zone --- --- * @{#ZONE_RADIUS.SetRadius}(): Sets the radius of the zone. --- * @{#ZONE_RADIUS.GetRadius}(): Returns the radius of the zone. --- --- ## 2.3) Manage the location of the zone --- --- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCSTypes#Vec2} of the zone. --- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCSTypes#Vec2} of the zone. --- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCSTypes#Vec3} of the zone, taking an additional height parameter. --- --- ## 2.4) Zone point randomization --- --- Various functions exist to find random points within the zone. --- --- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone. --- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Point#POINT_VEC2} object representing a random 2D point in the zone. --- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight. --- --- === --- --- # 3) @{Zone#ZONE} class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE class, defined by the zone name as defined within the Mission Editor. --- This class implements the inherited functions from {Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- # 4) @{Zone#ZONE_UNIT} class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- # 5) @{Zone#ZONE_GROUP} class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. The current leader of the group defines the center of the zone. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- # 6) @{Zone#ZONE_POLYGON_BASE} class, extends @{Zone#ZONE_BASE} --- --- The ZONE_POLYGON_BASE class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. --- --- ## 6.1) Zone point randomization --- --- Various functions exist to find random points within the zone. --- --- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone. --- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Point#POINT_VEC2} object representing a random 2D point within the zone. --- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. --- --- --- === --- --- # 7) @{Zone#ZONE_POLYGON} class, extends @{Zone#ZONE_POLYGON_BASE} --- --- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- ==== --- --- **API CHANGE HISTORY** --- ====================== +-- # **API CHANGE HISTORY** -- -- The underlying change log documents the API changes. Please read this carefully. The following notation is used: -- @@ -6547,6 +6154,43 @@ end -- @field #string ZoneName Name of the zone. -- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. -- @extends Core.Base#BASE + + +--- # 1) ZONE_BASE class, extends @{Base#BASE} +-- +-- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. +-- +-- ## 1.1) Each zone has a name: +-- +-- * @{#ZONE_BASE.GetName}(): Returns the name of the zone. +-- +-- ## 1.2) Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}: +-- +-- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a Vec2 is within the zone. +-- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a Vec3 is within the zone. +-- +-- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones: +-- +-- * @{#ZONE_BASE.SetRandomizeProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% ) +-- * @{#ZONE_BASE.GetRandomizeProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% ) +-- * @{#ZONE_BASE.GetZoneMaybe}(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate. +-- +-- ## 1.4) A zone manages Vectors: +-- +-- * @{#ZONE_BASE.GetVec2}(): Returns the @{DCSTypes#Vec2} coordinate of the zone. +-- * @{#ZONE_BASE.GetRandomVec2}(): Define a random @{DCSTypes#Vec2} within the zone. +-- +-- ## 1.5) A zone has a bounding square: +-- +-- * @{#ZONE_BASE.GetBoundingSquare}(): Get the outer most bounding square of the zone. +-- +-- ## 1.6) A zone can be marked: +-- +-- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color. +-- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color. +-- +-- === +-- @field #ZONE_BASE ZONE_BASE ZONE_BASE = { ClassName = "ZONE_BASE", ZoneName = "", @@ -6614,6 +6258,58 @@ function ZONE_BASE:GetVec2() return nil end +--- Returns a @{Point#POINT_VEC2} of the zone. +-- @param #ZONE_BASE self +-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. +-- @return Core.Point#POINT_VEC2 The PointVec2 of the zone. +function ZONE_BASE:GetPointVec2() + self:F2( self.ZoneName ) + + local Vec2 = self:GetVec2() + + local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 ) + + self:T2( { PointVec2 } ) + + return PointVec2 +end + + +--- Returns the @{DCSTypes#Vec3} of the zone. +-- @param #ZONE_BASE self +-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. +-- @return Dcs.DCSTypes#Vec3 The Vec3 of the zone. +function ZONE_BASE:GetVec3( Height ) + self:F2( self.ZoneName ) + + Height = Height or 0 + + local Vec2 = self:GetVec2() + + local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y } + + self:T2( { Vec3 } ) + + return Vec3 +end + +--- Returns a @{Point#POINT_VEC3} of the zone. +-- @param #ZONE_BASE self +-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. +-- @return Core.Point#POINT_VEC3 The PointVec3 of the zone. +function ZONE_BASE:GetPointVec3( Height ) + self:F2( self.ZoneName ) + + local Vec3 = self:GetVec3( Height ) + + local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) + + self:T2( { PointVec3 } ) + + return PointVec3 +end + + --- Define a random @{DCSTypes#Vec2} within the zone. -- @param #ZONE_BASE self -- @return Dcs.DCSTypes#Vec2 The Vec2 coordinates. @@ -6628,6 +6324,13 @@ function ZONE_BASE:GetRandomPointVec2() return nil end +--- Define a random @{Point#POINT_VEC3} within the zone. +-- @param #ZONE_BASE self +-- @return Core.Point#POINT_VEC3 The PointVec3 coordinates. +function ZONE_BASE:GetRandomPointVec3() + return nil +end + --- Get the bounding square the zone. -- @param #ZONE_BASE self -- @return #nil The bounding square. @@ -6691,6 +6394,39 @@ end -- @field Dcs.DCSTypes#Vec2 Vec2 The current location of the zone. -- @field Dcs.DCSTypes#Distance Radius The radius of the zone. -- @extends Core.Zone#ZONE_BASE + +--- # 2) @{Zone#ZONE_RADIUS} class, extends @{Zone#ZONE_BASE} +-- +-- The ZONE_RADIUS class defined by a zone name, a location and a radius. +-- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties. +-- +-- ## 2.1) @{Zone#ZONE_RADIUS} constructor +-- +-- * @{#ZONE_RADIUS.New}(): Constructor. +-- +-- ## 2.2) Manage the radius of the zone +-- +-- * @{#ZONE_RADIUS.SetRadius}(): Sets the radius of the zone. +-- * @{#ZONE_RADIUS.GetRadius}(): Returns the radius of the zone. +-- +-- ## 2.3) Manage the location of the zone +-- +-- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCSTypes#Vec2} of the zone. +-- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCSTypes#Vec2} of the zone. +-- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCSTypes#Vec3} of the zone, taking an additional height parameter. +-- +-- ## 2.4) Zone point randomization +-- +-- Various functions exist to find random points within the zone. +-- +-- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone. +-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Point#POINT_VEC2} object representing a random 2D point in the zone. +-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight. +-- +-- === +-- +-- @field #ZONE_RADIUS ZONE_RADIUS +-- ZONE_RADIUS = { ClassName="ZONE_RADIUS", } @@ -6714,8 +6450,9 @@ end --- Bounds the zone with tires. -- @param #ZONE_RADIUS self -- @param #number Points (optional) The amount of points in the circle. +-- @param #boolean UnBound If true the tyres will be destroyed. -- @return #ZONE_RADIUS self -function ZONE_RADIUS:BoundZone( Points ) +function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound ) local Point = {} local Vec2 = self:GetVec2() @@ -6731,8 +6468,10 @@ function ZONE_RADIUS:BoundZone( Points ) Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() + local CountryName = _DATABASE.COUNTRY_NAME[CountryID] + local Tire = { - ["country"] = "USA", + ["country"] = CountryName, ["category"] = "Fortifications", ["canCargo"] = false, ["shape_name"] = "H-tyre_B_WF", @@ -6744,7 +6483,10 @@ function ZONE_RADIUS:BoundZone( Points ) ["heading"] = 0, } -- end of ["group"] - coalition.addStaticObject( country.id.USA, Tire ) + local Group = coalition.addStaticObject( CountryID, Tire ) + if UnBound and UnBound == true then + Group:destroy() + end end return self @@ -6956,9 +6698,19 @@ end ---- The ZONE class, defined by the zone name as defined within the Mission Editor. The location and the radius are automatically collected from the mission settings. -- @type ZONE -- @extends Core.Zone#ZONE_RADIUS + + +--- # 3) ZONE class, extends @{Zone#ZONE_RADIUS} +-- +-- The ZONE class, defined by the zone name as defined within the Mission Editor. +-- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. +-- +-- === +-- +-- @field #ZONE ZONE +-- ZONE = { ClassName="ZONE", } @@ -6990,6 +6742,16 @@ end -- @type ZONE_UNIT -- @field Wrapper.Unit#UNIT ZoneUNIT -- @extends Core.Zone#ZONE_RADIUS + +--- # 4) #ZONE_UNIT class, extends @{Zone#ZONE_RADIUS} +-- +-- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. +-- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. +-- +-- === +-- +-- @field #ZONE_UNIT ZONE_UNIT +-- ZONE_UNIT = { ClassName="ZONE_UNIT", } @@ -7070,10 +6832,20 @@ function ZONE_UNIT:GetVec3( Height ) return Vec3 end ---- The ZONE_GROUP class defined by a zone around a @{Group}, taking the average center point of all the units within the Group, with a radius. --- @type ZONE_GROUP +--- @type ZONE_GROUP -- @field Wrapper.Group#GROUP ZoneGROUP -- @extends Core.Zone#ZONE_RADIUS + + +--- # 5) #ZONE_GROUP class, extends @{Zone#ZONE_RADIUS} +-- +-- The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. The current leader of the group defines the center of the zone. +-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. +-- +-- === +-- +-- @field #ZONE_GROUP ZONE_GROUP +-- ZONE_GROUP = { ClassName="ZONE_GROUP", } @@ -7127,12 +6899,29 @@ end --- Polygons - ---- The ZONE_POLYGON_BASE class defined by an array of @{DCSTypes#Vec2}, forming a polygon. --- @type ZONE_POLYGON_BASE +--- @type ZONE_POLYGON_BASE -- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCSTypes#Vec2}. -- @extends Core.Zone#ZONE_BASE + + +--- # 6) ZONE_POLYGON_BASE class, extends @{Zone#ZONE_BASE} +-- +-- The ZONE_POLYGON_BASE class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. +-- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. +-- +-- ## 6.1) Zone point randomization +-- +-- Various functions exist to find random points within the zone. +-- +-- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone. +-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Point#POINT_VEC2} object representing a random 2D point within the zone. +-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. +-- +-- === +-- +-- @field #ZONE_POLYGON_BASE ZONE_POLYGON_BASE +-- ZONE_POLYGON_BASE = { ClassName="ZONE_POLYGON_BASE", } @@ -7177,8 +6966,9 @@ end --- Smokes the zone boundaries in a color. -- @param #ZONE_POLYGON_BASE self +-- @param #boolean UnBound If true, the tyres will be destroyed. -- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:BoundZone( ) +function ZONE_POLYGON_BASE:BoundZone( UnBound ) local i local j @@ -7207,8 +6997,11 @@ function ZONE_POLYGON_BASE:BoundZone( ) ["name"] = string.format( "%s-Tire #%0d", self:GetName(), ((i - 1) * Segments) + Segment ), ["heading"] = 0, } -- end of ["group"] - - coalition.addStaticObject( country.id.USA, Tire ) + + local Group = coalition.addStaticObject( country.id.USA, Tire ) + if UnBound and UnBound == true then + Group:destroy() + end end j = i @@ -7362,12 +7155,19 @@ function ZONE_POLYGON_BASE:GetBoundingSquare() end - - - ---- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- @type ZONE_POLYGON +--- @type ZONE_POLYGON -- @extends Core.Zone#ZONE_POLYGON_BASE + + +--- # 7) ZONE_POLYGON class, extends @{Zone#ZONE_POLYGON_BASE} +-- +-- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. +-- +-- === +-- +-- @field #ZONE_POLYGON ZONE_POLYGON +-- ZONE_POLYGON = { ClassName="ZONE_POLYGON", } @@ -7444,6 +7244,8 @@ DATABASE = { PLAYERSJOINED = {}, CLIENTS = {}, AIRBASES = {}, + COUNTRY_ID = {}, + COUNTRY_NAME = {}, NavPoints = {}, } @@ -8151,6 +7953,9 @@ function DATABASE:_RegisterTemplates() local CountryName = string.upper(cntry_data.name) local CountryID = cntry_data.id + self.COUNTRY_ID[CountryName] = CountryID + self.COUNTRY_NAME[CountryID] = CountryName + --self.Units[coa_name][countryName] = {} --self.Units[coa_name][countryName]["countryId"] = cntry_data.id @@ -8192,225 +7997,30 @@ end ---- **Core** - SET classes define **collections** of objects to perform **bulk actions** and logically **group** objects. +--- **Core** - SET_ classes define **collections** of objects to perform **bulk actions** and logically **group** objects. +-- +-- ![Banner Image](..\Presentations\SET\Dia1.JPG) -- -- === -- --- 1) @{Set#SET_BASE} class, extends @{Base#BASE} --- ============================================== --- The @{Set#SET_BASE} class defines the core functions that define a collection of objects. --- A SET provides iterators to iterate the SET, but will **temporarily** yield the ForEach interator loop at defined **"intervals"** to the mail simulator loop. --- In this way, large loops can be done while not blocking the simulator main processing loop. --- The default **"yield interval"** is after 10 objects processed. --- The default **"time interval"** is after 0.001 seconds. +-- SET_ classes group objects of the same type into a collection, which is either: -- --- 1.1) Add or remove objects from the SET --- --------------------------------------- --- Some key core functions are @{Set#SET_BASE.Add} and @{Set#SET_BASE.Remove} to add or remove objects from the SET in your logic. --- --- 1.2) Define the SET iterator **"yield interval"** and the **"time interval"** --- ----------------------------------------------------------------------------- --- Modify the iterator intervals with the @{Set#SET_BASE.SetInteratorIntervals} method. --- You can set the **"yield interval"**, and the **"time interval"**. (See above). --- --- === --- --- 2) @{Set#SET_GROUP} class, extends @{Set#SET_BASE} --- ================================================== --- Mission designers can use the @{Set#SET_GROUP} class to build sets of groups belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Starting with certain prefix strings. --- --- 2.1) SET_GROUP construction method: --- ----------------------------------- --- Create a new SET_GROUP object with the @{#SET_GROUP.New} method: --- --- * @{#SET_GROUP.New}: Creates a new SET_GROUP object. --- --- 2.2) Add or Remove GROUP(s) from SET_GROUP: --- ------------------------------------------- --- GROUPS can be added and removed using the @{Set#SET_GROUP.AddGroupsByName} and @{Set#SET_GROUP.RemoveGroupsByName} respectively. --- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP. --- --- 2.3) SET_GROUP filter criteria: --- ------------------------------- --- You can set filter criteria to define the set of groups within the SET_GROUP. --- Filter criteria are defined by: --- --- * @{#SET_GROUP.FilterCoalitions}: Builds the SET_GROUP with the groups belonging to the coalition(s). --- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies). --- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the gruops belonging to the country(ies). --- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups starting with the same prefix string(s). +-- * Manually managed using the **:Add...()** or **:Remove...()** methods. The initial SET can be filtered with the **@{#SET_BASE.FilterOnce}()** method +-- * Dynamically updated when new objects are created or objects are destroyed using the **@{#SET_BASE.FilterStart}()** method. -- --- Once the filter criteria have been set for the SET_GROUP, you can start filtering using: +-- Various types of SET_ classes are available: -- --- * @{#SET_GROUP.FilterStart}: Starts the filtering of the groups within the SET_GROUP and add or remove GROUP objects **dynamically**. +-- * @{#SET_UNIT}: Defines a colleciton of @{Unit}s filtered by filter criteria. +-- * @{#SET_GROUP}: Defines a collection of @{Group}s filtered by filter criteria. +-- * @{#SET_CLIENT}: Defines a collection of @{Client}s filterd by filter criteria. +-- * @{#SET_AIRBASE}: Defines a collection of @{Airbase}s filtered by filter criteria. -- --- Planned filter criteria within development are (so these are not yet available): +-- These classes are derived from @{#SET_BASE}, which contains the main methods to manage SETs. -- --- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Zone#ZONE}. +-- A multitude of other methods are available in SET_ classes that allow to: -- --- 2.4) SET_GROUP iterators: --- ------------------------- --- Once the filters have been defined and the SET_GROUP has been built, you can iterate the SET_GROUP with the available iterator methods. --- The iterator methods will walk the SET_GROUP set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_GROUP: --- --- * @{#SET_GROUP.ForEachGroup}: Calls a function for each alive group it finds within the SET_GROUP. --- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupPartlyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. --- --- ==== --- --- 3) @{Set#SET_UNIT} class, extends @{Set#SET_BASE} --- =================================================== --- Mission designers can use the @{Set#SET_UNIT} class to build sets of units belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Unit types --- * Starting with certain prefix strings. --- --- 3.1) SET_UNIT construction method: --- ---------------------------------- --- Create a new SET_UNIT object with the @{#SET_UNIT.New} method: --- --- * @{#SET_UNIT.New}: Creates a new SET_UNIT object. --- --- 3.2) Add or Remove UNIT(s) from SET_UNIT: --- ----------------------------------------- --- UNITs can be added and removed using the @{Set#SET_UNIT.AddUnitsByName} and @{Set#SET_UNIT.RemoveUnitsByName} respectively. --- These methods take a single UNIT name or an array of UNIT names to be added or removed from SET_UNIT. --- --- 3.3) SET_UNIT filter criteria: --- ------------------------------ --- You can set filter criteria to define the set of units within the SET_UNIT. --- Filter criteria are defined by: --- --- * @{#SET_UNIT.FilterCoalitions}: Builds the SET_UNIT with the units belonging to the coalition(s). --- * @{#SET_UNIT.FilterCategories}: Builds the SET_UNIT with the units belonging to the category(ies). --- * @{#SET_UNIT.FilterTypes}: Builds the SET_UNIT with the units belonging to the unit type(s). --- * @{#SET_UNIT.FilterCountries}: Builds the SET_UNIT with the units belonging to the country(ies). --- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units starting with the same prefix string(s). --- --- Once the filter criteria have been set for the SET_UNIT, you can start filtering using: --- --- * @{#SET_UNIT.FilterStart}: Starts the filtering of the units within the SET_UNIT. --- --- Planned filter criteria within development are (so these are not yet available): --- --- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Zone#ZONE}. --- --- 3.4) SET_UNIT iterators: --- ------------------------ --- Once the filters have been defined and the SET_UNIT has been built, you can iterate the SET_UNIT with the available iterator methods. --- The iterator methods will walk the SET_UNIT set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_UNIT: --- --- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive unit it finds within the SET_UNIT. --- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. --- --- Planned iterators methods in development are (so these are not yet available): --- --- * @{#SET_UNIT.ForEachUnitInUnit}: Calls a function for each unit contained within the SET_UNIT. --- * @{#SET_UNIT.ForEachUnitCompletelyInZone}: Iterate and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function. --- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate and call an iterator function for each **alive** UNIT presence not in a @{Zone}, providing the UNIT and optional parameters to the called function. --- --- === --- --- 4) @{Set#SET_CLIENT} class, extends @{Set#SET_BASE} --- =================================================== --- Mission designers can use the @{Set#SET_CLIENT} class to build sets of units belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Client types --- * Starting with certain prefix strings. --- --- 4.1) SET_CLIENT construction method: --- ---------------------------------- --- Create a new SET_CLIENT object with the @{#SET_CLIENT.New} method: --- --- * @{#SET_CLIENT.New}: Creates a new SET_CLIENT object. --- --- 4.2) Add or Remove CLIENT(s) from SET_CLIENT: --- ----------------------------------------- --- CLIENTs can be added and removed using the @{Set#SET_CLIENT.AddClientsByName} and @{Set#SET_CLIENT.RemoveClientsByName} respectively. --- These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT. --- --- 4.3) SET_CLIENT filter criteria: --- ------------------------------ --- You can set filter criteria to define the set of clients within the SET_CLIENT. --- Filter criteria are defined by: --- --- * @{#SET_CLIENT.FilterCoalitions}: Builds the SET_CLIENT with the clients belonging to the coalition(s). --- * @{#SET_CLIENT.FilterCategories}: Builds the SET_CLIENT with the clients belonging to the category(ies). --- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s). --- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies). --- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients starting with the same prefix string(s). --- --- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using: --- --- * @{#SET_CLIENT.FilterStart}: Starts the filtering of the clients within the SET_CLIENT. --- --- Planned filter criteria within development are (so these are not yet available): --- --- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Zone#ZONE}. --- --- 4.4) SET_CLIENT iterators: --- ------------------------ --- Once the filters have been defined and the SET_CLIENT has been built, you can iterate the SET_CLIENT with the available iterator methods. --- The iterator methods will walk the SET_CLIENT set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_CLIENT: --- --- * @{#SET_CLIENT.ForEachClient}: Calls a function for each alive client it finds within the SET_CLIENT. --- --- ==== --- --- 5) @{Set#SET_AIRBASE} class, extends @{Set#SET_BASE} --- ==================================================== --- Mission designers can use the @{Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: --- --- * Coalitions --- --- 5.1) SET_AIRBASE construction --- ----------------------------- --- Create a new SET_AIRBASE object with the @{#SET_AIRBASE.New} method: --- --- * @{#SET_AIRBASE.New}: Creates a new SET_AIRBASE object. --- --- 5.2) Add or Remove AIRBASEs from SET_AIRBASE --- -------------------------------------------- --- AIRBASEs can be added and removed using the @{Set#SET_AIRBASE.AddAirbasesByName} and @{Set#SET_AIRBASE.RemoveAirbasesByName} respectively. --- These methods take a single AIRBASE name or an array of AIRBASE names to be added or removed from SET_AIRBASE. --- --- 5.3) SET_AIRBASE filter criteria --- -------------------------------- --- You can set filter criteria to define the set of clients within the SET_AIRBASE. --- Filter criteria are defined by: --- --- * @{#SET_AIRBASE.FilterCoalitions}: Builds the SET_AIRBASE with the airbases belonging to the coalition(s). --- --- Once the filter criteria have been set for the SET_AIRBASE, you can start filtering using: --- --- * @{#SET_AIRBASE.FilterStart}: Starts the filtering of the airbases within the SET_AIRBASE. --- --- 5.4) SET_AIRBASE iterators: --- --------------------------- --- Once the filters have been defined and the SET_AIRBASE has been built, you can iterate the SET_AIRBASE with the available iterator methods. --- The iterator methods will walk the SET_AIRBASE set, and call for each airbase within the set a function that you provide. --- The following iterator methods are currently available within the SET_AIRBASE: --- --- * @{#SET_AIRBASE.ForEachAirbase}: Calls a function for each airbase it finds within the SET_AIRBASE. --- --- ==== +-- * Validate the presence of objects in the SET. +-- * Trigger events when objects in the SET change a zone presence. -- -- ### Authors: -- @@ -8422,20 +8032,40 @@ end -- @module Set ---- SET_BASE class --- @type SET_BASE +--- @type SET_BASE -- @field #table Filter -- @field #table Set -- @field #table List -- @field Core.Scheduler#SCHEDULER CallScheduler -- @extends Core.Base#BASE + + +--- # 1) SET_BASE class, extends @{Base#BASE} +-- The @{Set#SET_BASE} class defines the core functions that define a collection of objects. +-- A SET provides iterators to iterate the SET, but will **temporarily** yield the ForEach interator loop at defined **"intervals"** to the mail simulator loop. +-- In this way, large loops can be done while not blocking the simulator main processing loop. +-- The default **"yield interval"** is after 10 objects processed. +-- The default **"time interval"** is after 0.001 seconds. +-- +-- ## 1.1) Add or remove objects from the SET +-- +-- Some key core functions are @{Set#SET_BASE.Add} and @{Set#SET_BASE.Remove} to add or remove objects from the SET in your logic. +-- +-- ## 1.2) Define the SET iterator **"yield interval"** and the **"time interval"** +-- +-- Modify the iterator intervals with the @{Set#SET_BASE.SetInteratorIntervals} method. +-- You can set the **"yield interval"**, and the **"time interval"**. (See above). +-- +-- @field #SET_BASE SET_BASE SET_BASE = { ClassName = "SET_BASE", Filter = {}, Set = {}, List = {}, + Index = {}, } + --- Creates a new SET_BASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. -- @param #SET_BASE self -- @return #SET_BASE @@ -8452,10 +8082,14 @@ function SET_BASE:New( Database ) self.YieldInterval = 10 self.TimeInterval = 0.001 + self.Set = {} + self.List = {} self.List.__index = self.List self.List = setmetatable( { Count = 0 }, self.List ) + self.Index = {} + self.CallScheduler = SCHEDULER:New( self ) self:SetEventPriority( 2 ) @@ -8507,6 +8141,8 @@ function SET_BASE:Add( ObjectName, Object ) self.Set[ObjectName] = t._ + table.insert( self.Index, ObjectName ) + end --- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using the Object Name as the index. @@ -8558,7 +8194,15 @@ function SET_BASE:Remove( ObjectName ) t._prev = nil self.List.Count = self.List.Count - 1 + for Index, Key in ipairs( self.Index ) do + if Key == ObjectName then + table.remove( self.Index, Index ) + break + end + end + self.Set[ObjectName] = nil + end end @@ -8578,12 +8222,50 @@ function SET_BASE:Get( ObjectName ) end +--- Gets the first object from the @{Set#SET_BASE} and derived classes. +-- @param #SET_BASE self +-- @return Core.Base#BASE +function SET_BASE:GetFirst() + self:F() + + local ObjectName = self.Index[1] + local FirstObject = self.Set[ObjectName] + self:T3( { FirstObject } ) + return FirstObject +end + +--- Gets the last object from the @{Set#SET_BASE} and derived classes. +-- @param #SET_BASE self +-- @return Core.Base#BASE +function SET_BASE:GetLast() + self:F() + + local ObjectName = self.Index[#self.Index] + local LastObject = self.Set[ObjectName] + self:T3( { LastObject } ) + return LastObject +end + +--- Gets a random object from the @{Set#SET_BASE} and derived classes. +-- @param #SET_BASE self +-- @return Core.Base#BASE +function SET_BASE:GetRandom() + self:F() + + local RandomItem = self.Set[self.Index[math.random(#self.Index)]] + + self:T3( { RandomItem } ) + + return RandomItem +end + + --- Retrieves the amount of objects in the @{Set#SET_BASE} and derived classes. -- @param #SET_BASE self -- @return #number Count function SET_BASE:Count() - return self.List.Count + return #self.Index or 0 end @@ -8846,7 +8528,8 @@ function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArgumen return false end - self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + --self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + Schedule() return self end @@ -8915,11 +8598,61 @@ function SET_BASE:Flush() return ObjectNames end --- SET_GROUP ---- SET_GROUP class --- @type SET_GROUP --- @extends #SET_BASE +--- @type SET_GROUP +-- @extends Core.Set#SET_BASE + +--- # 2) SET_GROUP class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the @{Set#SET_GROUP} class to build sets of groups belonging to certain: +-- +-- * Coalitions +-- * Categories +-- * Countries +-- * Starting with certain prefix strings. +-- +-- ## 2.1) SET_GROUP constructor +-- +-- Create a new SET_GROUP object with the @{#SET_GROUP.New} method: +-- +-- * @{#SET_GROUP.New}: Creates a new SET_GROUP object. +-- +-- ## 2.2) Add or Remove GROUP(s) from SET_GROUP +-- +-- GROUPS can be added and removed using the @{Set#SET_GROUP.AddGroupsByName} and @{Set#SET_GROUP.RemoveGroupsByName} respectively. +-- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP. +-- +-- ## 2.3) SET_GROUP filter criteria +-- +-- You can set filter criteria to define the set of groups within the SET_GROUP. +-- Filter criteria are defined by: +-- +-- * @{#SET_GROUP.FilterCoalitions}: Builds the SET_GROUP with the groups belonging to the coalition(s). +-- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies). +-- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the gruops belonging to the country(ies). +-- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups starting with the same prefix string(s). +-- +-- Once the filter criteria have been set for the SET_GROUP, you can start filtering using: +-- +-- * @{#SET_GROUP.FilterStart}: Starts the filtering of the groups within the SET_GROUP and add or remove GROUP objects **dynamically**. +-- +-- Planned filter criteria within development are (so these are not yet available): +-- +-- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Zone#ZONE}. +-- +-- ## 2.4) SET_GROUP iterators +-- +-- Once the filters have been defined and the SET_GROUP has been built, you can iterate the SET_GROUP with the available iterator methods. +-- The iterator methods will walk the SET_GROUP set, and call for each element within the set a function that you provide. +-- The following iterator methods are currently available within the SET_GROUP: +-- +-- * @{#SET_GROUP.ForEachGroup}: Calls a function for each alive group it finds within the SET_GROUP. +-- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- * @{#SET_GROUP.ForEachGroupPartlyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- +-- === +-- @field #SET_GROUP SET_GROUP SET_GROUP = { ClassName = "SET_GROUP", Filter = { @@ -9285,9 +9018,73 @@ function SET_GROUP:IsIncludeObject( MooseGroup ) return MooseGroupInclude end ---- SET_UNIT class --- @type SET_UNIT +--- @type SET_UNIT -- @extends Core.Set#SET_BASE + +--- # 3) SET_UNIT class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the SET_UNIT class to build sets of units belonging to certain: +-- +-- * Coalitions +-- * Categories +-- * Countries +-- * Unit types +-- * Starting with certain prefix strings. +-- +-- ## 3.1) SET_UNIT constructor +-- +-- Create a new SET_UNIT object with the @{#SET_UNIT.New} method: +-- +-- * @{#SET_UNIT.New}: Creates a new SET_UNIT object. +-- +-- ## 3.2) Add or Remove UNIT(s) from SET_UNIT +-- +-- UNITs can be added and removed using the @{Set#SET_UNIT.AddUnitsByName} and @{Set#SET_UNIT.RemoveUnitsByName} respectively. +-- These methods take a single UNIT name or an array of UNIT names to be added or removed from SET_UNIT. +-- +-- ## 3.3) SET_UNIT filter criteria +-- +-- You can set filter criteria to define the set of units within the SET_UNIT. +-- Filter criteria are defined by: +-- +-- * @{#SET_UNIT.FilterCoalitions}: Builds the SET_UNIT with the units belonging to the coalition(s). +-- * @{#SET_UNIT.FilterCategories}: Builds the SET_UNIT with the units belonging to the category(ies). +-- * @{#SET_UNIT.FilterTypes}: Builds the SET_UNIT with the units belonging to the unit type(s). +-- * @{#SET_UNIT.FilterCountries}: Builds the SET_UNIT with the units belonging to the country(ies). +-- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units starting with the same prefix string(s). +-- +-- Once the filter criteria have been set for the SET_UNIT, you can start filtering using: +-- +-- * @{#SET_UNIT.FilterStart}: Starts the filtering of the units within the SET_UNIT. +-- +-- Planned filter criteria within development are (so these are not yet available): +-- +-- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Zone#ZONE}. +-- +-- ## 3.4) SET_UNIT iterators +-- +-- Once the filters have been defined and the SET_UNIT has been built, you can iterate the SET_UNIT with the available iterator methods. +-- The iterator methods will walk the SET_UNIT set, and call for each element within the set a function that you provide. +-- The following iterator methods are currently available within the SET_UNIT: +-- +-- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive unit it finds within the SET_UNIT. +-- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. +-- +-- Planned iterators methods in development are (so these are not yet available): +-- +-- * @{#SET_UNIT.ForEachUnitInUnit}: Calls a function for each unit contained within the SET_UNIT. +-- * @{#SET_UNIT.ForEachUnitCompletelyInZone}: Iterate and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function. +-- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate and call an iterator function for each **alive** UNIT presence not in a @{Zone}, providing the UNIT and optional parameters to the called function. +-- +-- ## 3.5 ) SET_UNIT atomic methods +-- +-- Various methods exist for a SET_UNIT to perform actions or calculations and retrieve results from the SET_UNIT: +-- +-- * @{#SET_UNIT.GetTypeNames}(): Retrieve the type names of the @{Unit}s in the SET, delimited by a comma. +-- +-- === +-- @field #SET_UNIT SET_UNIT SET_UNIT = { ClassName = "SET_UNIT", Units = {}, @@ -9899,11 +9696,89 @@ function SET_UNIT:IsIncludeObject( MUnit ) end +--- Retrieve the type names of the @{Unit}s in the SET, delimited by an optional delimiter. +-- @param #SET_UNIT self +-- @param #string Delimiter (optional) The delimiter, which is default a comma. +-- @return #string The types of the @{Unit}s delimited. +function SET_UNIT:GetTypeNames( Delimiter ) + + Delimiter = Delimiter or ", " + local TypeReport = REPORT:New() + local Types = {} + + for UnitName, UnitData in pairs( self:GetSet() ) do + + local Unit = UnitData -- Wrapper.Unit#UNIT + local UnitTypeName = Unit:GetTypeName() + + if not Types[UnitTypeName] then + Types[UnitTypeName] = UnitTypeName + TypeReport:Add( UnitTypeName ) + end + end + + return TypeReport:Text( Delimiter ) +end + + --- SET_CLIENT ---- SET_CLIENT class --- @type SET_CLIENT + +--- @type SET_CLIENT -- @extends Core.Set#SET_BASE + + + +--- # 4) SET_CLIENT class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the @{Set#SET_CLIENT} class to build sets of units belonging to certain: +-- +-- * Coalitions +-- * Categories +-- * Countries +-- * Client types +-- * Starting with certain prefix strings. +-- +-- ## 4.1) SET_CLIENT constructor +-- +-- Create a new SET_CLIENT object with the @{#SET_CLIENT.New} method: +-- +-- * @{#SET_CLIENT.New}: Creates a new SET_CLIENT object. +-- +-- ## 4.2) Add or Remove CLIENT(s) from SET_CLIENT +-- +-- CLIENTs can be added and removed using the @{Set#SET_CLIENT.AddClientsByName} and @{Set#SET_CLIENT.RemoveClientsByName} respectively. +-- These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT. +-- +-- ## 4.3) SET_CLIENT filter criteria +-- +-- You can set filter criteria to define the set of clients within the SET_CLIENT. +-- Filter criteria are defined by: +-- +-- * @{#SET_CLIENT.FilterCoalitions}: Builds the SET_CLIENT with the clients belonging to the coalition(s). +-- * @{#SET_CLIENT.FilterCategories}: Builds the SET_CLIENT with the clients belonging to the category(ies). +-- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s). +-- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies). +-- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients starting with the same prefix string(s). +-- +-- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using: +-- +-- * @{#SET_CLIENT.FilterStart}: Starts the filtering of the clients within the SET_CLIENT. +-- +-- Planned filter criteria within development are (so these are not yet available): +-- +-- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Zone#ZONE}. +-- +-- ## 4.4) SET_CLIENT iterators +-- +-- Once the filters have been defined and the SET_CLIENT has been built, you can iterate the SET_CLIENT with the available iterator methods. +-- The iterator methods will walk the SET_CLIENT set, and call for each element within the set a function that you provide. +-- The following iterator methods are currently available within the SET_CLIENT: +-- +-- * @{#SET_CLIENT.ForEachClient}: Calls a function for each alive client it finds within the SET_CLIENT. +-- +-- === +-- @field #SET_CLIENT SET_CLIENT SET_CLIENT = { ClassName = "SET_CLIENT", Clients = {}, @@ -10256,11 +10131,47 @@ function SET_CLIENT:IsIncludeObject( MClient ) return MClientInclude end ---- SET_AIRBASE - ---- SET_AIRBASE class --- @type SET_AIRBASE +--- @type SET_AIRBASE -- @extends Core.Set#SET_BASE + +--- # 5) SET_AIRBASE class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the @{Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: +-- +-- * Coalitions +-- +-- ## 5.1) SET_AIRBASE constructor +-- +-- Create a new SET_AIRBASE object with the @{#SET_AIRBASE.New} method: +-- +-- * @{#SET_AIRBASE.New}: Creates a new SET_AIRBASE object. +-- +-- ## 5.2) Add or Remove AIRBASEs from SET_AIRBASE +-- +-- AIRBASEs can be added and removed using the @{Set#SET_AIRBASE.AddAirbasesByName} and @{Set#SET_AIRBASE.RemoveAirbasesByName} respectively. +-- These methods take a single AIRBASE name or an array of AIRBASE names to be added or removed from SET_AIRBASE. +-- +-- ## 5.3) SET_AIRBASE filter criteria +-- +-- You can set filter criteria to define the set of clients within the SET_AIRBASE. +-- Filter criteria are defined by: +-- +-- * @{#SET_AIRBASE.FilterCoalitions}: Builds the SET_AIRBASE with the airbases belonging to the coalition(s). +-- +-- Once the filter criteria have been set for the SET_AIRBASE, you can start filtering using: +-- +-- * @{#SET_AIRBASE.FilterStart}: Starts the filtering of the airbases within the SET_AIRBASE. +-- +-- ## 5.4) SET_AIRBASE iterators +-- +-- Once the filters have been defined and the SET_AIRBASE has been built, you can iterate the SET_AIRBASE with the available iterator methods. +-- The iterator methods will walk the SET_AIRBASE set, and call for each airbase within the set a function that you provide. +-- The following iterator methods are currently available within the SET_AIRBASE: +-- +-- * @{#SET_AIRBASE.ForEachAirbase}: Calls a function for each airbase it finds within the SET_AIRBASE. +-- +-- === +-- @field #SET_AIRBASE SET_AIRBASE SET_AIRBASE = { ClassName = "SET_AIRBASE", Airbases = {}, @@ -11694,227 +11605,14 @@ end -- I've reworked this development (taken the concept), and created a **hierarchical state machine** out of it, embedded within the DCS simulator. -- Additionally, I've added extendability and created an API that allows seamless FSM implementation. -- --- === --- --- # 1) @{#FSM} class, extends @{Base#BASE} --- --- ![Transition Rules and Transition Handlers and Event Triggers](..\Presentations\FSM\Dia3.JPG) +-- The following derived classes are available in the MOOSE framework, that implement a specialised form of a FSM: -- --- The FSM class is the base class of all FSM\_ derived classes. It implements the main functionality to define and execute Finite State Machines. --- The derived FSM\_ classes extend the Finite State Machine functionality to run a workflow process for a specific purpose or component. +-- * @{#FSM_TASK}: Models Finite State Machines for @{Task}s. +-- * @{#FSM_PROCESS}: Models Finite State Machines for @{Task} actions, which control @{Client}s. +-- * @{#FSM_CONTROLLABLE}: Models Finite State Machines for @{Controllable}s, which are @{Group}s, @{Unit}s, @{Client}s. +-- * @{#FSM_SET}: Models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here +-- for multiple objects or the position of the state machine in the process. -- --- Finite State Machines have **Transition Rules**, **Transition Handlers** and **Event Triggers**. --- --- The **Transition Rules** define the "Process Flow Boundaries", that is, --- the path that can be followed hopping from state to state upon triggered events. --- If an event is triggered, and there is no valid path found for that event, --- an error will be raised and the FSM will stop functioning. --- --- The **Transition Handlers** are special methods that can be defined by the mission designer, following a defined syntax. --- If the FSM object finds a method of such a handler, then the method will be called by the FSM, passing specific parameters. --- The method can then define its own custom logic to implement the FSM workflow, and to conduct other actions. --- --- The **Event Triggers** are methods that are defined by the FSM, which the mission designer can use to implement the workflow. --- Most of the time, these Event Triggers are used within the Transition Handler methods, so that a workflow is created running through the state machine. --- --- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation. --- The below documentation has a seperate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**. --- --- ## 1.1) FSM Linear Transitions --- --- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**. --- The Lineair transition rule evaluation will always be done from the **current state** of the FSM. --- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop. --- --- ### 1.1.1) FSM Transition Rules --- --- The FSM has transition rules that it follows and validates, as it walks the process. --- These rules define when an FSM can transition from a specific state towards an other specific state upon a triggered event. --- --- The method @{#FSM.AddTransition}() specifies a new possible Transition Rule for the FSM. --- --- The initial state can be defined using the method @{#FSM.SetStartState}(). The default start state of an FSM is "None". --- --- Find below an example of a Linear Transition Rule definition for an FSM. --- --- local Fsm3Switch = FSM:New() -- #FsmDemo --- FsmSwitch:SetStartState( "Off" ) --- FsmSwitch:AddTransition( "Off", "SwitchOn", "On" ) --- FsmSwitch:AddTransition( "Off", "SwitchMiddle", "Middle" ) --- FsmSwitch:AddTransition( "On", "SwitchOff", "Off" ) --- FsmSwitch:AddTransition( "Middle", "SwitchOff", "Off" ) --- --- The above code snippet models a 3-way switch Linear Transition: --- --- * It can be switched **On** by triggering event **SwitchOn**. --- * It can be switched to the **Middle** position, by triggering event **SwitchMiddle**. --- * It can be switched **Off** by triggering event **SwitchOff**. --- * Note that once the Switch is **On** or **Middle**, it can only be switched **Off**. --- --- ### Some additional comments: --- --- Note that Linear Transition Rules **can be declared in a few variations**: --- --- * The From states can be **a table of strings**, indicating that the transition rule will be valid **if the current state** of the FSM will be **one of the given From states**. --- * The From state can be a **"*"**, indicating that **the transition rule will always be valid**, regardless of the current state of the FSM. --- --- The below code snippet shows how the two last lines can be rewritten and consensed. --- --- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" ) --- --- ### 1.1.2) Transition Handling --- --- ![Transition Handlers](..\Presentations\FSM\Dia4.JPG) --- --- An FSM transitions in **4 moments** when an Event is being triggered and processed. --- The mission designer can define for each moment specific logic within methods implementations following a defined API syntax. --- These methods define the flow of the FSM process; because in those methods the FSM Internal Events will be triggered. --- --- * To handle **State** transition moments, create methods starting with OnLeave or OnEnter concatenated with the State name. --- * To handle **Event** transition moments, create methods starting with OnBefore or OnAfter concatenated with the Event name. --- --- **The OnLeave and OnBefore transition methods may return false, which will cancel the transition!** --- --- Transition Handler methods need to follow the above specified naming convention, but are also passed parameters from the FSM. --- These parameters are on the correct order: From, Event, To: --- --- * From = A string containing the From state. --- * Event = A string containing the Event name that was triggered. --- * To = A string containing the To state. --- --- On top, each of these methods can have a variable amount of parameters passed. See the example in section [1.1.3](#1.1.3\)-event-triggers). --- --- ### 1.1.3) Event Triggers --- --- ![Event Triggers](..\Presentations\FSM\Dia5.JPG) --- --- The FSM creates for each Event two **Event Trigger methods**. --- There are two modes how Events can be triggered, which is **synchronous** and **asynchronous**: --- --- * The method **FSM:Event()** triggers an Event that will be processed **synchronously** or **immediately**. --- * The method **FSM:__Event( __seconds__ )** triggers an Event that will be processed **asynchronously** over time, waiting __x seconds__. --- --- The destinction between these 2 Event Trigger methods are important to understand. An asynchronous call will "log" the Event Trigger to be executed at a later time. --- Processing will just continue. Synchronous Event Trigger methods are useful to change states of the FSM immediately, but may have a larger processing impact. --- --- The following example provides a little demonstration on the difference between synchronous and asynchronous Event Triggering. --- --- function FSM:OnAfterEvent( From, Event, To, Amount ) --- self:T( { Amount = Amount } ) --- end --- --- local Amount = 1 --- FSM:__Event( 5, Amount ) --- --- Amount = Amount + 1 --- FSM:Event( Text, Amount ) --- --- In this example, the **:OnAfterEvent**() Transition Handler implementation will get called when **Event** is being triggered. --- Before we go into more detail, let's look at the last 4 lines of the example. --- The last line triggers synchronously the **Event**, and passes Amount as a parameter. --- The 3rd last line of the example triggers asynchronously **Event**. --- Event will be processed after 5 seconds, and Amount is given as a parameter. --- --- The output of this little code fragment will be: --- --- * Amount = 2 --- * Amount = 2 --- --- Because ... When Event was asynchronously processed after 5 seconds, Amount was set to 2. So be careful when processing and passing values and objects in asynchronous processing! --- --- ### 1.1.4) Linear Transition Example --- --- This example is fully implemented in the MOOSE test mission on GITHUB: [FSM-100 - Transition Explanation](https://github.com/FlightControl-Master/MOOSE/blob/master/Moose%20Test%20Missions/FSM%20-%20Finite%20State%20Machine/FSM-100%20-%20Transition%20Explanation/FSM-100%20-%20Transition%20Explanation.lua) --- --- It models a unit standing still near Batumi, and flaring every 5 seconds while switching between a Green flare and a Red flare. --- The purpose of this example is not to show how exciting flaring is, but it demonstrates how a Linear Transition FSM can be build. --- Have a look at the source code. The source code is also further explained below in this section. --- --- The example creates a new FsmDemo object from class FSM. --- It will set the start state of FsmDemo to state **Green**. --- Two Linear Transition Rules are created, where upon the event **Switch**, --- the FsmDemo will transition from state **Green** to **Red** and from **Red** back to **Green**. --- --- ![Transition Example](..\Presentations\FSM\Dia6.JPG) --- --- local FsmDemo = FSM:New() -- #FsmDemo --- FsmDemo:SetStartState( "Green" ) --- FsmDemo:AddTransition( "Green", "Switch", "Red" ) --- FsmDemo:AddTransition( "Red", "Switch", "Green" ) --- --- In the above example, the FsmDemo could flare every 5 seconds a Green or a Red flare into the air. --- The next code implements this through the event handling method **OnAfterSwitch**. --- --- ![Transition Flow](..\Presentations\FSM\Dia7.JPG) --- --- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) --- self:T( { From, Event, To, FsmUnit } ) --- --- if From == "Green" then --- FsmUnit:Flare(FLARECOLOR.Green) --- else --- if From == "Red" then --- FsmUnit:Flare(FLARECOLOR.Red) --- end --- end --- self:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. --- end --- --- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the first Switch event to happen in 5 seconds. --- --- The OnAfterSwitch implements a loop. The last line of the code fragment triggers the Switch Event within 5 seconds. --- Upon the event execution (after 5 seconds), the OnAfterSwitch method is called of FsmDemo (cfr. the double point notation!!! ":"). --- The OnAfterSwitch method receives from the FSM the 3 transition parameter details ( From, Event, To ), --- and one additional parameter that was given when the event was triggered, which is in this case the Unit that is used within OnSwitchAfter. --- --- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) --- --- For debugging reasons the received parameters are traced within the DCS.log. --- --- self:T( { From, Event, To, FsmUnit } ) --- --- The method will check if the From state received is either "Green" or "Red" and will flare the respective color from the FsmUnit. --- --- if From == "Green" then --- FsmUnit:Flare(FLARECOLOR.Green) --- else --- if From == "Red" then --- FsmUnit:Flare(FLARECOLOR.Red) --- end --- end --- --- It is important that the Switch event is again triggered, otherwise, the FsmDemo would stop working after having the first Event being handled. --- --- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. --- --- The below code fragment extends the FsmDemo, demonstrating multiple **From states declared as a table**, adding a **Linear Transition Rule**. --- The new event **Stop** will cancel the Switching process. --- The transition for event Stop can be executed if the current state of the FSM is either "Red" or "Green". --- --- local FsmDemo = FSM:New() -- #FsmDemo --- FsmDemo:SetStartState( "Green" ) --- FsmDemo:AddTransition( "Green", "Switch", "Red" ) --- FsmDemo:AddTransition( "Red", "Switch", "Green" ) --- FsmDemo:AddTransition( { "Red", "Green" }, "Stop", "Stopped" ) --- --- The transition for event Stop can also be simplified, as any current state of the FSM is valid. --- --- FsmDemo:AddTransition( "*", "Stop", "Stopped" ) --- --- So... When FsmDemo:Stop() is being triggered, the state of FsmDemo will transition from Red or Green to Stopped. --- And there is no transition handling method defined for that transition, thus, no new event is being triggered causing the FsmDemo process flow to halt. --- --- ## 1.5) FSM Hierarchical Transitions --- --- Hierarchical Transitions allow to re-use readily available and implemented FSMs. --- This becomes in very useful for mission building, where mission designers build complex processes and workflows, --- combining smaller FSMs to one single FSM. --- --- The FSM can embed **Sub-FSMs** that will execute and return **multiple possible Return (End) States**. --- Depending upon **which state is returned**, the main FSM can continue the flow **triggering specific events**. --- --- The method @{#FSM.AddProcess}() adds a new Sub-FSM to the FSM. --- -- ==== -- -- # **API CHANGE HISTORY** @@ -11948,8 +11646,233 @@ end do -- FSM --- FSM class - -- @type FSM + --- @type FSM -- @extends Core.Base#BASE + + + --- # 1) FSM class, extends @{Base#BASE} + -- + -- ![Transition Rules and Transition Handlers and Event Triggers](..\Presentations\FSM\Dia3.JPG) + -- + -- The FSM class is the base class of all FSM\_ derived classes. It implements the main functionality to define and execute Finite State Machines. + -- The derived FSM\_ classes extend the Finite State Machine functionality to run a workflow process for a specific purpose or component. + -- + -- Finite State Machines have **Transition Rules**, **Transition Handlers** and **Event Triggers**. + -- + -- The **Transition Rules** define the "Process Flow Boundaries", that is, + -- the path that can be followed hopping from state to state upon triggered events. + -- If an event is triggered, and there is no valid path found for that event, + -- an error will be raised and the FSM will stop functioning. + -- + -- The **Transition Handlers** are special methods that can be defined by the mission designer, following a defined syntax. + -- If the FSM object finds a method of such a handler, then the method will be called by the FSM, passing specific parameters. + -- The method can then define its own custom logic to implement the FSM workflow, and to conduct other actions. + -- + -- The **Event Triggers** are methods that are defined by the FSM, which the mission designer can use to implement the workflow. + -- Most of the time, these Event Triggers are used within the Transition Handler methods, so that a workflow is created running through the state machine. + -- + -- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation. + -- The below documentation has a seperate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**. + -- + -- ## 1.1) FSM Linear Transitions + -- + -- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**. + -- The Lineair transition rule evaluation will always be done from the **current state** of the FSM. + -- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop. + -- + -- ### 1.1.1) FSM Transition Rules + -- + -- The FSM has transition rules that it follows and validates, as it walks the process. + -- These rules define when an FSM can transition from a specific state towards an other specific state upon a triggered event. + -- + -- The method @{#FSM.AddTransition}() specifies a new possible Transition Rule for the FSM. + -- + -- The initial state can be defined using the method @{#FSM.SetStartState}(). The default start state of an FSM is "None". + -- + -- Find below an example of a Linear Transition Rule definition for an FSM. + -- + -- local Fsm3Switch = FSM:New() -- #FsmDemo + -- FsmSwitch:SetStartState( "Off" ) + -- FsmSwitch:AddTransition( "Off", "SwitchOn", "On" ) + -- FsmSwitch:AddTransition( "Off", "SwitchMiddle", "Middle" ) + -- FsmSwitch:AddTransition( "On", "SwitchOff", "Off" ) + -- FsmSwitch:AddTransition( "Middle", "SwitchOff", "Off" ) + -- + -- The above code snippet models a 3-way switch Linear Transition: + -- + -- * It can be switched **On** by triggering event **SwitchOn**. + -- * It can be switched to the **Middle** position, by triggering event **SwitchMiddle**. + -- * It can be switched **Off** by triggering event **SwitchOff**. + -- * Note that once the Switch is **On** or **Middle**, it can only be switched **Off**. + -- + -- ### Some additional comments: + -- + -- Note that Linear Transition Rules **can be declared in a few variations**: + -- + -- * The From states can be **a table of strings**, indicating that the transition rule will be valid **if the current state** of the FSM will be **one of the given From states**. + -- * The From state can be a **"*"**, indicating that **the transition rule will always be valid**, regardless of the current state of the FSM. + -- + -- The below code snippet shows how the two last lines can be rewritten and consensed. + -- + -- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" ) + -- + -- ### 1.1.2) Transition Handling + -- + -- ![Transition Handlers](..\Presentations\FSM\Dia4.JPG) + -- + -- An FSM transitions in **4 moments** when an Event is being triggered and processed. + -- The mission designer can define for each moment specific logic within methods implementations following a defined API syntax. + -- These methods define the flow of the FSM process; because in those methods the FSM Internal Events will be triggered. + -- + -- * To handle **State** transition moments, create methods starting with OnLeave or OnEnter concatenated with the State name. + -- * To handle **Event** transition moments, create methods starting with OnBefore or OnAfter concatenated with the Event name. + -- + -- **The OnLeave and OnBefore transition methods may return false, which will cancel the transition!** + -- + -- Transition Handler methods need to follow the above specified naming convention, but are also passed parameters from the FSM. + -- These parameters are on the correct order: From, Event, To: + -- + -- * From = A string containing the From state. + -- * Event = A string containing the Event name that was triggered. + -- * To = A string containing the To state. + -- + -- On top, each of these methods can have a variable amount of parameters passed. See the example in section [1.1.3](#1.1.3\)-event-triggers). + -- + -- ### 1.1.3) Event Triggers + -- + -- ![Event Triggers](..\Presentations\FSM\Dia5.JPG) + -- + -- The FSM creates for each Event two **Event Trigger methods**. + -- There are two modes how Events can be triggered, which is **synchronous** and **asynchronous**: + -- + -- * The method **FSM:Event()** triggers an Event that will be processed **synchronously** or **immediately**. + -- * The method **FSM:__Event( __seconds__ )** triggers an Event that will be processed **asynchronously** over time, waiting __x seconds__. + -- + -- The destinction between these 2 Event Trigger methods are important to understand. An asynchronous call will "log" the Event Trigger to be executed at a later time. + -- Processing will just continue. Synchronous Event Trigger methods are useful to change states of the FSM immediately, but may have a larger processing impact. + -- + -- The following example provides a little demonstration on the difference between synchronous and asynchronous Event Triggering. + -- + -- function FSM:OnAfterEvent( From, Event, To, Amount ) + -- self:T( { Amount = Amount } ) + -- end + -- + -- local Amount = 1 + -- FSM:__Event( 5, Amount ) + -- + -- Amount = Amount + 1 + -- FSM:Event( Text, Amount ) + -- + -- In this example, the **:OnAfterEvent**() Transition Handler implementation will get called when **Event** is being triggered. + -- Before we go into more detail, let's look at the last 4 lines of the example. + -- The last line triggers synchronously the **Event**, and passes Amount as a parameter. + -- The 3rd last line of the example triggers asynchronously **Event**. + -- Event will be processed after 5 seconds, and Amount is given as a parameter. + -- + -- The output of this little code fragment will be: + -- + -- * Amount = 2 + -- * Amount = 2 + -- + -- Because ... When Event was asynchronously processed after 5 seconds, Amount was set to 2. So be careful when processing and passing values and objects in asynchronous processing! + -- + -- ### 1.1.4) Linear Transition Example + -- + -- This example is fully implemented in the MOOSE test mission on GITHUB: [FSM-100 - Transition Explanation](https://github.com/FlightControl-Master/MOOSE/blob/master/Moose%20Test%20Missions/FSM%20-%20Finite%20State%20Machine/FSM-100%20-%20Transition%20Explanation/FSM-100%20-%20Transition%20Explanation.lua) + -- + -- It models a unit standing still near Batumi, and flaring every 5 seconds while switching between a Green flare and a Red flare. + -- The purpose of this example is not to show how exciting flaring is, but it demonstrates how a Linear Transition FSM can be build. + -- Have a look at the source code. The source code is also further explained below in this section. + -- + -- The example creates a new FsmDemo object from class FSM. + -- It will set the start state of FsmDemo to state **Green**. + -- Two Linear Transition Rules are created, where upon the event **Switch**, + -- the FsmDemo will transition from state **Green** to **Red** and from **Red** back to **Green**. + -- + -- ![Transition Example](..\Presentations\FSM\Dia6.JPG) + -- + -- local FsmDemo = FSM:New() -- #FsmDemo + -- FsmDemo:SetStartState( "Green" ) + -- FsmDemo:AddTransition( "Green", "Switch", "Red" ) + -- FsmDemo:AddTransition( "Red", "Switch", "Green" ) + -- + -- In the above example, the FsmDemo could flare every 5 seconds a Green or a Red flare into the air. + -- The next code implements this through the event handling method **OnAfterSwitch**. + -- + -- ![Transition Flow](..\Presentations\FSM\Dia7.JPG) + -- + -- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) + -- self:T( { From, Event, To, FsmUnit } ) + -- + -- if From == "Green" then + -- FsmUnit:Flare(FLARECOLOR.Green) + -- else + -- if From == "Red" then + -- FsmUnit:Flare(FLARECOLOR.Red) + -- end + -- end + -- self:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. + -- end + -- + -- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the first Switch event to happen in 5 seconds. + -- + -- The OnAfterSwitch implements a loop. The last line of the code fragment triggers the Switch Event within 5 seconds. + -- Upon the event execution (after 5 seconds), the OnAfterSwitch method is called of FsmDemo (cfr. the double point notation!!! ":"). + -- The OnAfterSwitch method receives from the FSM the 3 transition parameter details ( From, Event, To ), + -- and one additional parameter that was given when the event was triggered, which is in this case the Unit that is used within OnSwitchAfter. + -- + -- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) + -- + -- For debugging reasons the received parameters are traced within the DCS.log. + -- + -- self:T( { From, Event, To, FsmUnit } ) + -- + -- The method will check if the From state received is either "Green" or "Red" and will flare the respective color from the FsmUnit. + -- + -- if From == "Green" then + -- FsmUnit:Flare(FLARECOLOR.Green) + -- else + -- if From == "Red" then + -- FsmUnit:Flare(FLARECOLOR.Red) + -- end + -- end + -- + -- It is important that the Switch event is again triggered, otherwise, the FsmDemo would stop working after having the first Event being handled. + -- + -- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. + -- + -- The below code fragment extends the FsmDemo, demonstrating multiple **From states declared as a table**, adding a **Linear Transition Rule**. + -- The new event **Stop** will cancel the Switching process. + -- The transition for event Stop can be executed if the current state of the FSM is either "Red" or "Green". + -- + -- local FsmDemo = FSM:New() -- #FsmDemo + -- FsmDemo:SetStartState( "Green" ) + -- FsmDemo:AddTransition( "Green", "Switch", "Red" ) + -- FsmDemo:AddTransition( "Red", "Switch", "Green" ) + -- FsmDemo:AddTransition( { "Red", "Green" }, "Stop", "Stopped" ) + -- + -- The transition for event Stop can also be simplified, as any current state of the FSM is valid. + -- + -- FsmDemo:AddTransition( "*", "Stop", "Stopped" ) + -- + -- So... When FsmDemo:Stop() is being triggered, the state of FsmDemo will transition from Red or Green to Stopped. + -- And there is no transition handling method defined for that transition, thus, no new event is being triggered causing the FsmDemo process flow to halt. + -- + -- ## 1.5) FSM Hierarchical Transitions + -- + -- Hierarchical Transitions allow to re-use readily available and implemented FSMs. + -- This becomes in very useful for mission building, where mission designers build complex processes and workflows, + -- combining smaller FSMs to one single FSM. + -- + -- The FSM can embed **Sub-FSMs** that will execute and return **multiple possible Return (End) States**. + -- Depending upon **which state is returned**, the main FSM can continue the flow **triggering specific events**. + -- + -- The method @{#FSM.AddProcess}() adds a new Sub-FSM to the FSM. + -- + -- === + -- + -- @field #FSM FSM + -- FSM = { ClassName = "FSM", } @@ -12068,7 +11991,6 @@ do -- FSM for ProcessID, Process in pairs( self:GetProcesses() ) do if Process.From == From and Process.Event == Event then - self:T( Process ) return Process.fsm end end @@ -12097,7 +12019,7 @@ do -- FSM -- @param #number Score is a number providing the score of the status. -- @return #FSM self function FSM:AddScore( State, ScoreText, Score ) - self:F2( { State, ScoreText, Score } ) + self:F( { State, ScoreText, Score } ) self._Scores[State] = self._Scores[State] or {} self._Scores[State].ScoreText = ScoreText @@ -12115,14 +12037,15 @@ do -- FSM -- @param #number Score is a number providing the score of the status. -- @return #FSM self function FSM:AddScoreProcess( From, Event, State, ScoreText, Score ) - self:F2( { Event, State, ScoreText, Score } ) + self:F( { From, Event, State, ScoreText, Score } ) local Process = self:GetProcess( From, Event ) - self:T( { Process = Process._Name, Scores = Process._Scores, State = State, ScoreText = ScoreText, Score = Score } ) Process._Scores[State] = Process._Scores[State] or {} Process._Scores[State].ScoreText = ScoreText Process._Scores[State].Score = Score + + self:T( Process._Scores ) return Process end @@ -12178,10 +12101,20 @@ do -- FSM function FSM:_call_handler( handler, params, EventName ) + + local ErrorHandler = function( errmsg ) + + env.info( "Error in SCHEDULER function:" .. errmsg ) + if debug ~= nil then + env.info( debug.traceback() ) + end + + return errmsg + end if self[handler] then self:T( "Calling " .. handler ) self._EventSchedules[EventName] = nil - local Value = self[handler]( self, unpack(params) ) + local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler ) return Value end end @@ -12358,10 +12291,18 @@ end do -- FSM_CONTROLLABLE - --- FSM_CONTROLLABLE class - -- @type FSM_CONTROLLABLE + --- @type FSM_CONTROLLABLE -- @field Wrapper.Controllable#CONTROLLABLE Controllable -- @extends Core.Fsm#FSM + + --- # FSM_CONTROLLABLE, extends @{#FSM} + -- + -- FSM_CONTROLLABLE class models Finite State Machines for @{Controllable}s, which are @{Group}s, @{Unit}s, @{Client}s. + -- + -- === + -- + -- @field #FSM_CONTROLLABLE FSM_CONTROLLABLE + -- FSM_CONTROLLABLE = { ClassName = "FSM_CONTROLLABLE", } @@ -12380,8 +12321,66 @@ do -- FSM_CONTROLLABLE self:SetControllable( Controllable ) end + self:AddTransition( "*", "Stop", "Stopped" ) + + --- OnBefore Transition Handler for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] OnBeforeStop + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] OnAfterStop + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] Stop + -- @param #FSM_CONTROLLABLE self + + --- Asynchronous Event Trigger for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] __Stop + -- @param #FSM_CONTROLLABLE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#FSM_CONTROLLABLE] OnLeaveStopped + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Stopped. + -- @function [parent=#FSM_CONTROLLABLE] OnEnterStopped + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + return self end + + --- OnAfter Transition Handler for Event Stop. + -- @function [parent=#FSM_CONTROLLABLE] OnAfterStop + -- @param #FSM_CONTROLLABLE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function FSM_CONTROLLABLE:OnAfterStop(Controllable,From,Event,To) + + -- Clear all pending schedules + self.CallScheduler:Clear() + end --- Sets the CONTROLLABLE object that the FSM_CONTROLLABLE governs. -- @param #FSM_CONTROLLABLE self @@ -12424,10 +12423,19 @@ end do -- FSM_PROCESS - --- FSM_PROCESS class - -- @type FSM_PROCESS + --- @type FSM_PROCESS -- @field Tasking.Task#TASK Task -- @extends Core.Fsm#FSM_CONTROLLABLE + + + --- # FSM_PROCESS, extends @{#FSM} + -- + -- FSM_PROCESS class models Finite State Machines for @{Task} actions, which control @{Client}s. + -- + -- === + -- + -- @field #FSM_PROCESS FSM_PROCESS + -- FSM_PROCESS = { ClassName = "FSM_PROCESS", } @@ -12449,12 +12457,34 @@ do -- FSM_PROCESS function FSM_PROCESS:Init( FsmProcess ) self:T( "No Initialisation" ) end + + function FSM_PROCESS:_call_handler( handler, params, EventName ) + + local ErrorHandler = function( errmsg ) + + env.info( "Error in FSM_PROCESS call handler:" .. errmsg ) + if debug ~= nil then + env.info( debug.traceback() ) + end + + return errmsg + end + + if self[handler] then + self:F3( "Calling " .. handler ) + self._EventSchedules[EventName] = nil + local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler ) + return Value + --return self[handler]( self, self.Controllable, unpack( params ) ) + end + end --- Creates a new FSM_PROCESS object based on this FSM_PROCESS. -- @param #FSM_PROCESS self -- @return #FSM_PROCESS function FSM_PROCESS:Copy( Controllable, Task ) self:T( { self:GetClassNameAndID() } ) + local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS @@ -12473,7 +12503,7 @@ do -- FSM_PROCESS -- Copy Processes for ProcessID, Process in pairs( self:GetProcesses() ) do - self:T( { Process} ) + self:E( { Process} ) local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents ) end @@ -12491,6 +12521,22 @@ do -- FSM_PROCESS return NewFsm end + + --- Removes an FSM_PROCESS object. + -- @param #FSM_PROCESS self + -- @return #FSM_PROCESS + function FSM_PROCESS:Remove() + self:T( { self:GetClassNameAndID() } ) + + -- Copy Processes + for ProcessID, Process in pairs( self:GetProcesses() ) do + self:E( { Process} ) + Process.fsm:Remove() + Process.fsm = nil + end + + return self + end --- Sets the task of the process. -- @param #FSM_PROCESS self @@ -12589,14 +12635,14 @@ end -- @param #string Event -- @param #string From -- @param #string To - function FSM_PROCESS:onstatechange( ProcessUnit, From, Event, To, Dummy ) + function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To, Dummy ) self:T( { ProcessUnit, From, Event, To, Dummy, self:IsTrace() } ) if self:IsTrace() then MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() end - self:T( self._Scores[To] ) + self:T( { Scores = self._Scores, To = To } ) -- TODO: This needs to be reworked with a callback functions allocated within Task, and set within the mission script from the Task Objects... if self._Scores[To] then @@ -12616,6 +12662,15 @@ do -- FSM_TASK -- @type FSM_TASK -- @field Tasking.Task#TASK Task -- @extends Core.Fsm#FSM + + --- # FSM_TASK, extends @{#FSM} + -- + -- FSM_TASK class models Finite State Machines for @{Task}s. + -- + -- === + -- + -- @field #FSM_TASK FSM_TASK + -- FSM_TASK = { ClassName = "FSM_TASK", } @@ -12651,6 +12706,17 @@ do -- FSM_SET -- @type FSM_SET -- @field Core.Set#SET_BASE Set -- @extends Core.Fsm#FSM + + + --- # FSM_SET, extends @{#FSM} + -- + -- FSM_SET class models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here + -- for multiple objects or the position of the state machine in the process. + -- + -- === + -- + -- @field #FSM_SET FSM_SET + -- FSM_SET = { ClassName = "FSM_SET", } @@ -12698,7 +12764,336 @@ do -- FSM_SET end -- FSM_SET ---- This module contains the OBJECT class. +--- **Core** - The RADIO class is responsible for **transmitting radio communications**. +-- +-- --- bitmap +-- +-- === +-- +-- What are radio communications in DCS ? +-- +-- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM), +-- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**. +-- +-- How to supply DCS my own Sound Files ? +-- +-- * Your sound files need to be encoded in **.ogg** or .wav, +-- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings, +-- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file), +-- * For simplicty sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission. +-- +-- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Unit#UNIT} or a @{Group#GROUP} or by any other @{Positionable#POSITIONABLE} +-- +-- * If the transmitter is a @{Unit#UNIT} or a @{Group#GROUP}, DCS will set the power of the transmission automatically, +-- * If the transmitter is any other @{Positionable#POSITIONABLE}, the transmisison can't be subtitled or looped. +-- +-- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft, +-- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below). +-- If a FC3 airacraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. +-- +-- === +-- +-- ### Authors: Hugues "Grey_Echo" Bousquet +-- +-- @module Radio + +--- # 1) RADIO class, extends @{Base#BASE} +-- +-- ## 1.1) RADIO usage +-- +-- There are 3 steps to a successful radio transmission. +-- +-- * First, you need to **"add" a @{#RADIO} object** to your @{Positionable#POSITIONABLE}. This is done using the @{Positionable#POSITIONABLE.GetRadio}() function, +-- * Then, you will **set the relevant parameters** to the transmission (see below), +-- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{Positionable#POSITIONABLE.Broadcast}() function. +-- +-- Methods to set relevant parameters for both a @{Unit#UNIT} or a @{Group#GROUP} or any other @{Positionable#POSITIONABLE} +-- +-- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"), +-- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission, +-- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission. +-- +-- Additional Methods to set relevant parameters if the transmiter is a @{Unit#UNIT} or a @{Group#GROUP} +-- +-- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped, +-- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration, +-- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call +-- +-- Additional Methods to set relevant parameters if the transmiter is any other @{Wrapper.Positionable#POSITIONABLE} +-- +-- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts +-- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call +-- +-- What is this power thing ? +-- +-- * If your transmission is sent by a @{Positionable#POSITIONABLE} other than a @{Unit#UNIT} or a @{Group#GROUP}, you can set the power of the antenna, +-- * Otherwise, DCS sets it automatically, depending on what's available on your Unit, +-- * If the player gets **too far** from the transmiter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**, +-- * This an automated DCS calculation you have no say on, +-- * For reference, a standard VOR station has a 100W antenna, a standard AA TACAN has a 120W antenna, and civilian ATC's antenna usually range between 300 and 500W, +-- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission. +-- +-- @type RADIO +-- @field Wrapper.Positionable#POSITIONABLE Positionable The transmiter +-- @field #string FileName Name of the sound file +-- @field #number Frequency Frequency of the transmission in Hz +-- @field #number Modulation Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM) +-- @field #string Subtitle Subtitle of the transmission +-- @field #number SubtitleDuration Duration of the Subtitle in seconds +-- @field #number Power Power of the antenna is Watts +-- @field #boolean Loop +-- @extends Core.Base#BASE +RADIO = { + ClassName = "RADIO", + FileName = "", + Frequency = 0, + Modulation = radio.modulation.AM, + Subtitle = "", + SubtitleDuration = 0, + Power = 100, + Loop = 0, +} + +--- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast +-- @param #RADIO self +-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. +-- @return #RADIO Radio +-- @return #nil If Positionable is invalid +-- @usage +-- -- If you want to create a RADIO, you probably should use @{Positionable#POSITIONABLE.GetRadio}() instead +function RADIO:New(Positionable) + local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO + + self:F(Positionable) + + if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid + self.Positionable = Positionable + return self + end + + self:E({"The passed positionable is invalid, no RADIO created", Positionable}) + return nil +end + +--- Check validity of the filename passed and sets RADIO.FileName +-- @param #RADIO self +-- @param #string FileName File name of the sound file (i.e. "Noise.ogg") +-- @return #RADIO self +function RADIO:SetFileName(FileName) + self:F2(FileName) + + if type(FileName) == "string" then + if FileName:find(".ogg") or FileName:find(".wav") then + if not FileName:find("l10n/DEFAULT/") then + FileName = "l10n/DEFAULT/" .. FileName + end + self.FileName = FileName + return self + end + end + + self:E({"File name invalid. Maybe something wrong with the extension ?", self.FileName}) + return self +end + +--- Check validity of the frequency passed and sets RADIO.Frequency +-- @param #RADIO self +-- @param #number Frequency in MHz (Ranges allowed for radio transmissions in DCS : 30-88 / 108-152 / 225-400MHz) +-- @return #RADIO self +function RADIO:SetFrequency(Frequency) + self:F2(Frequency) + if type(Frequency) == "number" then + -- If frequency is in range + if (Frequency >= 30 and Frequency < 88) or (Frequency >= 108 and Frequency < 152) or (Frequency >= 225 and Frequency < 400) then + self.Frequency = Frequency * 1000000 -- Conversion in Hz + -- If the RADIO is attached to a UNIT or a GROUP, we need to send the DCS Command "SetFrequency" to change the UNIT or GROUP frequency + if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then + self.Positionable:SetCommand({ + id = "SetFrequency", + params = { + frequency = self.Frequency, + modulation = self.Modulation, + } + }) + end + return self + end + end + self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", self.Frequency}) + return self +end + +--- Check validity of the frequency passed and sets RADIO.Modulation +-- @param #RADIO self +-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM +-- @return #RADIO self +function RADIO:SetModulation(Modulation) + self:F2(Modulation) + if type(Modulation) == "number" then + if Modulation == radio.modulation.AM or Modulation == radio.modulation.FM then --TODO Maybe make this future proof if ED decides to add an other modulation ? + self.Modulation = Modulation + return self + end + end + self:E({"Modulation is invalid. Use DCS's enum radio.modulation. Modulation unchanged.", self.Modulation}) + return self +end + +--- Check validity of the power passed and sets RADIO.Power +-- @param #RADIO self +-- @param #number Power in W +-- @return #RADIO self +function RADIO:SetPower(Power) + self:F2(Power) + if type(Power) == "number" then + self.Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that + return self + end + self:E({"Power is invalid. Power unchanged.", self.Power}) + return self +end + +--- Check validity of the loop passed and sets RADIO.Loop +-- @param #RADIO self +-- @param #boolean Loop +-- @return #RADIO self +-- @usage +function RADIO:SetLoop(Loop) + self:F2(Loop) + if type(Loop) == "boolean" then + self.Loop = Loop + return self + end + self:E({"Loop is invalid. Loop unchanged.", self.Loop}) + return self +end + +--- Check validity of the subtitle and the subtitleDuration passed and sets RADIO.subtitle and RADIO.subtitleDuration +-- @param #RADIO self +-- @param #string Subtitle +-- @param #number SubtitleDuration in s +-- @return #RADIO self +-- @usage +-- -- Both parameters are mandatory, since it wouldn't make much sense to change the Subtitle and not its duration +function RADIO:SetSubtitle(Subtitle, SubtitleDuration) + self:F2({Subtitle, SubtitleDuration}) + if type(Subtitle) == "string" then + self.Subtitle = Subtitle + else + self.Subtitle = "" + self:E({"Subtitle is invalid. Subtitle reset.", self.Subtitle}) + end + if type(SubtitleDuration) == "number" then + if math.floor(math.abs(SubtitleDuration)) == SubtitleDuration then + self.SubtitleDuration = SubtitleDuration + return self + end + end + self.SubtitleDuration = 0 + self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration}) +end + +--- Create a new transmission, that is to say, populate the RADIO with relevant data +-- @param #RADIO self +-- @param #string FileName +-- @param #number Frequency in MHz +-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM +-- @param #number Power in W +-- @return #RADIO self +-- @usage +-- -- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP, +-- but it will work with a UNIT or a GROUP anyway +-- -- Only the RADIO and the Filename are mandatory +function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power) + self:F({FileName, Frequency, Modulation, Power}) + + self:SetFileName(FileName) + if Frequency then self:SetFrequency(Frequency) end + if Modulation then self:SetModulation(Modulation) end + if Power then self:SetPower(Power) end + + return self +end + + +--- Create a new transmission, that is to say, populate the RADIO with relevant data +-- @param #RADIO self +-- @param #string FileName +-- @param #string Subtitle +-- @param #number SubtitleDuration in s +-- @param #number Frequency in MHz +-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM +-- @param #boolean Loop +-- @return #RADIO self +-- @usage +-- -- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP, +-- but it will work for any POSITIONABLE +-- -- Only the RADIO and the Filename are mandatory +function RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop) + self:F({FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop}) + + self:SetFileName(FileName) + if Subtitle then self:SetSubtitle(Subtitle) end + if SubtitleDuration then self:SetSubtitleDuration(SubtitleDuration) end + if Frequency then self:SetFrequency(Frequency) end + if Modulation then self:SetModulation(Modulation) end + if Loop then self:SetLoop(Loop) end + + return self +end + +--- Actually Broadcast the transmission +-- @param #RADIO self +-- @return #RADIO self +-- @usage +-- -- The Radio has to be populated with the new transmission before broadcasting. +-- -- Please use RADIO setters or either @{Radio#RADIO.NewGenericTransmission} or @{Radio#RADIO.NewUnitTransmission} +-- -- This class is in fact pretty smart, it determines the right DCS function to use depending on the type of POSITIONABLE +-- -- If the POSITIONABLE is not a UNIT or a GROUP, we use the generic (but limited) trigger.action.radioTransmission() +-- -- If the POSITIONABLE is a UNIT or a GROUP, we use the "TransmitMessage" Command +-- -- If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored. +-- -- If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration and Loop are ignored +function RADIO:Broadcast() + self:F() + -- If the POSITIONABLE is actually a UNIT or a GROUP, use the more complicated DCS command system + if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then + self:T2("Broadcasting from a UNIT or a GROUP") + self.Positionable:SetCommand({ + id = "TransmitMessage", + params = { + file = self.FileName, + duration = self.SubtitleDuration, + subtitle = self.Subtitle, + loop = self.Loop, + } + }) + else + -- If the POSITIONABLE is anything else, we revert to the general singleton function + self:T2("Broadcasting from a POSITIONABLE") + trigger.action.radioTransmission(self.FileName, self.Positionable:GetPositionVec3(), self.Modulation, false, self.Frequency, self.Power) + end + return self +end + +--- Stops a transmission +-- @param #RADIO self +-- @return #RADIO self +-- @usage +-- -- Especially usefull to stop the broadcast of looped transmissions +-- -- Only works with broadcasts from UNIT or GROUP +function RADIO:StopBroadcast() + self:F() + -- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command + if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then + self.Positionable:SetCommand({ + id = "StopTransmission", + params = {} + }) + else + self:E("This broadcast can't be stopped. It's not looped either, so please wait for the end of the sound file playback") + end + return self +end--- This module contains the OBJECT class. -- -- 1) @{Object#OBJECT} class, extends @{Base#BASE} -- =========================================================== @@ -12846,17 +13241,19 @@ function IDENTIFIABLE:New( IdentifiableName ) return self end ---- Returns if the Identifiable is alive. +--- Returns if the Identifiable is alive. +-- If the Identifiable is not alive, nil is returned. +-- If the Identifiable is alive, true is returned. -- @param #IDENTIFIABLE self -- @return #boolean true if Identifiable is alive. --- @return #nil The DCS Identifiable is not existing or alive. +-- @return #nil if the Identifiable is not existing or is not alive. function IDENTIFIABLE:IsAlive() self:F3( self.IdentifiableName ) - local DCSIdentifiable = self:GetDCSObject() + local DCSIdentifiable = self:GetDCSObject() -- Dcs.DCSObject#Object if DCSIdentifiable then - local IdentifiableIsAlive = DCSIdentifiable:isExist() + local IdentifiableIsAlive = DCSIdentifiable:isExist() return IdentifiableIsAlive end @@ -13146,8 +13543,11 @@ end --- Returns a random @{DCSTypes#Vec3} vector within a range, indicating the point in 3D of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self +-- @param #number Radius -- @return Dcs.DCSTypes#Vec3 The 3D point vector of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. +-- @usage +-- -- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP function POSITIONABLE:GetRandomVec3( Radius ) self:F2( self.PositionableName ) @@ -13155,14 +13555,20 @@ function POSITIONABLE:GetRandomVec3( Radius ) if DCSPositionable then local PositionablePointVec3 = DCSPositionable:getPosition().p - local PositionableRandomVec3 = {} - local angle = math.random() * math.pi*2; - PositionableRandomVec3.x = PositionablePointVec3.x + math.cos( angle ) * math.random() * Radius; - PositionableRandomVec3.y = PositionablePointVec3.y - PositionableRandomVec3.z = PositionablePointVec3.z + math.sin( angle ) * math.random() * Radius; - self:T3( PositionableRandomVec3 ) - return PositionableRandomVec3 + if Radius then + local PositionableRandomVec3 = {} + local angle = math.random() * math.pi*2; + PositionableRandomVec3.x = PositionablePointVec3.x + math.cos( angle ) * math.random() * Radius; + PositionableRandomVec3.y = PositionablePointVec3.y + PositionableRandomVec3.z = PositionablePointVec3.z + math.sin( angle ) * math.random() * Radius; + + self:T3( PositionableRandomVec3 ) + return PositionableRandomVec3 + else + self:E("Radius is nil, returning the PointVec3 of the POSITIONABLE", PositionablePointVec3) + return PositionablePointVec3 + end end return nil @@ -13230,6 +13636,7 @@ end --- Returns the POSITIONABLE heading in degrees. -- @param Wrapper.Positionable#POSITIONABLE self -- @return #number The POSTIONABLE heading +-- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetHeading() local DCSPositionable = self:GetDCSObject() @@ -13443,10 +13850,14 @@ function POSITIONABLE:Message( Message, Duration, Name ) return nil end - - - - +--- Create a @{Radio#RADIO}, to allow radio transmission for this POSITIONABLE. +-- Set parameters with the methods provided, then use RADIO:Broadcast() to actually broadcast the message +-- @param #POSITIONABLE self +-- @return #RADIO Radio +function POSITIONABLE:GetRadio() + self:F2(self) + return RADIO:New(self) +end --- This module contains the CONTROLLABLE class. -- -- 1) @{Controllable#CONTROLLABLE} class, extends @{Positionable#POSITIONABLE} @@ -13906,12 +14317,20 @@ function CONTROLLABLE:CommandSwitchWayPoint( FromWayPoint, ToWayPoint ) return CommandSwitchWayPoint end ---- Perform stop route command +--- Create a stop route command, which returns a string containing the command. +-- Use the result in the method @{#CONTROLLABLE.SetCommand}(). +-- A value of true will make the ground group stop, a value of false will make it continue. +-- Note that this can only work on GROUP level, although individual UNITs can be commanded, the whole GROUP will react. +-- +-- Example missions: +-- +-- * GRP-310 +-- -- @param #CONTROLLABLE self --- @param #boolean StopRoute +-- @param #boolean StopRoute true if the ground unit needs to stop, false if it needs to continue to move. -- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:CommandStopRoute( StopRoute, Index ) - self:F2( { StopRoute, Index } ) +function CONTROLLABLE:CommandStopRoute( StopRoute ) + self:F2( { StopRoute } ) local CommandStopRoute = { id = 'StopRoute', @@ -13985,47 +14404,35 @@ function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, At return DCSTask end - --- (AIR) Attack the Unit. -- @param #CONTROLLABLE self --- @param Wrapper.Unit#UNIT AttackUnit The unit. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. +-- @param Wrapper.Unit#UNIT AttackUnit The UNIT. +-- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. -- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks. --- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. +-- @param #number Altitude (optional) The altitude from where to attack. +-- @param #boolean Visible (optional) not a clue. +-- @param #number WeaponType (optional) The WeaponType. -- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskAttackUnit( AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack ) - self:F2( { self.ControllableName, AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack } ) - - -- AttackUnit = { - -- id = 'AttackUnit', - -- params = { - -- unitId = Unit.ID, - -- weaponType = number, - -- expend = enum AI.Task.WeaponExpend - -- attackQty = number, - -- direction = Azimuth, - -- attackQtyLimit = boolean, - -- controllableAttack = boolean, - -- } - -- } +function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType ) + self:F2( { self.ControllableName, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType } ) local DCSTask DCSTask = { id = 'AttackUnit', params = { - altitudeEnabled = true, unitId = AttackUnit:GetID(), - attackQtyLimit = AttackQtyLimit or false, - attackQty = AttackQty or 2, + groupAttack = GroupAttack or false, + visible = Visible or false, expend = WeaponExpend or "Auto", - altitude = 2000, - directionEnabled = true, - groupAttack = true, - --weaponType = WeaponType or 1073741822, - direction = Direction or 0, + directionEnabled = Direction and true or false, + direction = Direction, + altitudeEnabled = Altitude and true or false, + altitude = Altitude or 30, + attackQtyLimit = AttackQty and true or false, + attackQty = AttackQty, + weaponType = WeaponType } } @@ -14640,7 +15047,7 @@ function CONTROLLABLE:EnRouteTaskEngageGroup( AttackGroup, Priority, WeaponType, end ---- (AIR) Attack the Unit. +--- (AIR) Search and attack the Unit. -- @param #CONTROLLABLE self -- @param Wrapper.Unit#UNIT EngageUnit The UNIT. -- @param #number Priority (optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. @@ -15110,90 +15517,6 @@ function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation ) return nil end ---- (AIR) Return the Controllable to an @{Airbase#AIRBASE} --- A speed can be given in km/h. --- A given formation can be given. --- @param #CONTROLLABLE self --- @param Wrapper.Airbase#AIRBASE ReturnAirbase The @{Airbase#AIRBASE} to return to. --- @param #number Speed (optional) The speed. --- @return #string The route -function CONTROLLABLE:RouteReturnToAirbase( ReturnAirbase, Speed ) - self:F2( { ReturnAirbase, Speed } ) - --- Example --- [4] = --- { --- ["alt"] = 45, --- ["type"] = "Land", --- ["action"] = "Landing", --- ["alt_type"] = "BARO", --- ["formation_template"] = "", --- ["properties"] = --- { --- ["vnav"] = 1, --- ["scale"] = 0, --- ["angle"] = 0, --- ["vangle"] = 0, --- ["steer"] = 2, --- }, -- end of ["properties"] --- ["ETA"] = 527.81058817743, --- ["airdromeId"] = 12, --- ["y"] = 243127.2973737, --- ["x"] = -5406.2803440839, --- ["name"] = "DictKey_WptName_53", --- ["speed"] = 138.88888888889, --- ["ETA_locked"] = false, --- ["task"] = --- { --- ["id"] = "ComboTask", --- ["params"] = --- { --- ["tasks"] = --- { --- }, -- end of ["tasks"] --- }, -- end of ["params"] --- }, -- end of ["task"] --- ["speed_locked"] = true, --- }, -- end of [4] - - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - - local ControllablePoint = self:GetVec2() - local ControllableVelocity = self:GetMaxVelocity() - - local PointFrom = {} - PointFrom.x = ControllablePoint.x - PointFrom.y = ControllablePoint.y - PointFrom.type = "Turning Point" - PointFrom.action = "Turning Point" - PointFrom.speed = ControllableVelocity - - - local PointTo = {} - local AirbasePoint = ReturnAirbase:GetVec2() - - PointTo.x = AirbasePoint.x - PointTo.y = AirbasePoint.y - PointTo.type = "Land" - PointTo.action = "Landing" - PointTo.airdromeId = ReturnAirbase:GetID()-- Airdrome ID - self:T(PointTo.airdromeId) - --PointTo.alt = 0 - - local Points = { PointFrom, PointTo } - - self:T3( Points ) - - local Route = { points = Points, } - - return Route - end - - return nil -end -- Commands @@ -15234,6 +15557,8 @@ function CONTROLLABLE:GetTaskRoute() return routines.utils.deepCopy( _DATABASE.Templates.Controllables[self.ControllableName].Template.route.points ) end + + --- Return the route of a controllable by using the @{Database#DATABASE} class. -- @param #CONTROLLABLE self -- @param #number Begin The route point from where the copy will start. The base route point is 0. @@ -15757,11 +16082,11 @@ function CONTROLLABLE:WayPointExecute( WayPoint, WaitTime ) return self end --- Message APIs--- This module contains the GROUP class. +-- Message APIs--- **Wrapper** -- GROUP is a wrapper class for the DCS Class Group. -- --- 1) @{Group#GROUP} class, extends @{Controllable#CONTROLLABLE} --- ============================================================= --- The @{Group#GROUP} class is a wrapper class to handle the DCS Group objects: +-- === +-- +-- The @{#GROUP} class is a wrapper class to handle the DCS Group objects: -- -- * Support all DCS Group APIs. -- * Enhance with Group specific APIs not in the DCS Group API set. @@ -15770,60 +16095,8 @@ end -- -- **IMPORTANT: ONE SHOULD NEVER SANATIZE these GROUP OBJECT REFERENCES! (make the GROUP object references nil).** -- --- 1.1) GROUP reference methods --- ----------------------- --- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object. --- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the @{SPAWN} class). --- --- The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference --- using the DCS Group or the DCS GroupName. --- --- Another thing to know is that GROUP objects do not "contain" the DCS Group object. --- The GROUP methods will reference the DCS Group object by name when it is needed during API execution. --- If the DCS Group object does not exist or is nil, the GROUP methods will return nil and log an exception in the DCS.log file. --- --- The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance: --- --- * @{#GROUP.Find}(): Find a GROUP instance from the _DATABASE object using a DCS Group object. --- * @{#GROUP.FindByName}(): Find a GROUP instance from the _DATABASE object using a DCS Group name. --- --- ## 1.2) GROUP task methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} task methods section for a description of the task methods. --- --- ### 1.2.4) Obtain the mission from group templates +-- See the detailed documentation on the GROUP class. -- --- Group templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a group and assign it to another: --- --- * @{Controllable#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template. --- --- ## 1.3) GROUP Command methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} command methods section for a description of the command methods. --- --- ## 1.4) GROUP option methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} option methods section for a description of the option methods. --- --- ## 1.5) GROUP Zone validation methods --- --- The group can be validated whether it is completely, partly or not within a @{Zone}. --- Use the following Zone validation methods on the group: --- --- * @{#GROUP.IsCompletelyInZone}: Returns true if all units of the group are within a @{Zone}. --- * @{#GROUP.IsPartlyInZone}: Returns true if some units of the group are within a @{Zone}. --- * @{#GROUP.IsNotInZone}: Returns true if none of the group units of the group are within a @{Zone}. --- --- The zone can be of any @{Zone} class derived from @{Zone#ZONE_BASE}. So, these methods are polymorphic to the zones tested on. --- --- ## 1.6) GROUP AI methods --- --- A GROUP has AI methods to control the AI activation. --- --- * @{#GROUP.SetAIOnOff}(): Turns the GROUP AI On or Off. --- * @{#GROUP.SetAIOn}(): Turns the GROUP AI On. --- * @{#GROUP.SetAIOff}(): Turns the GROUP AI Off. --- -- ==== -- -- # **API CHANGE HISTORY** @@ -15835,6 +16108,8 @@ end -- -- Hereby the change log: -- +-- 2017-03-26: GROUP:**RouteRTB( RTBAirbase, Speed )** added. +-- -- 2017-03-07: GROUP:**HandleEvent( Event, EventFunction )** added. -- 2017-03-07: GROUP:**UnHandleEvent( Event )** added. -- @@ -15859,10 +16134,66 @@ end -- @module Group -- @author FlightControl ---- The GROUP class --- @type GROUP +--- @type GROUP -- @extends Wrapper.Controllable#CONTROLLABLE -- @field #string GroupName The name of the group. + +--- +-- # GROUP class, extends @{Controllable#CONTROLLABLE} +-- +-- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object. +-- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the @{SPAWN} class). +-- +-- The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference +-- using the DCS Group or the DCS GroupName. +-- +-- Another thing to know is that GROUP objects do not "contain" the DCS Group object. +-- The GROUP methods will reference the DCS Group object by name when it is needed during API execution. +-- If the DCS Group object does not exist or is nil, the GROUP methods will return nil and log an exception in the DCS.log file. +-- +-- The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance: +-- +-- * @{#GROUP.Find}(): Find a GROUP instance from the _DATABASE object using a DCS Group object. +-- * @{#GROUP.FindByName}(): Find a GROUP instance from the _DATABASE object using a DCS Group name. +-- +-- ## GROUP task methods +-- +-- A GROUP is a @{Controllable}. See the @{Controllable} task methods section for a description of the task methods. +-- +-- ### Obtain the mission from group templates +-- +-- Group templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a group and assign it to another: +-- +-- * @{Controllable#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template. +-- +-- ## GROUP Command methods +-- +-- A GROUP is a @{Controllable}. See the @{Controllable} command methods section for a description of the command methods. +-- +-- ## GROUP option methods +-- +-- A GROUP is a @{Controllable}. See the @{Controllable} option methods section for a description of the option methods. +-- +-- ## GROUP Zone validation methods +-- +-- The group can be validated whether it is completely, partly or not within a @{Zone}. +-- Use the following Zone validation methods on the group: +-- +-- * @{#GROUP.IsCompletelyInZone}: Returns true if all units of the group are within a @{Zone}. +-- * @{#GROUP.IsPartlyInZone}: Returns true if some units of the group are within a @{Zone}. +-- * @{#GROUP.IsNotInZone}: Returns true if none of the group units of the group are within a @{Zone}. +-- +-- The zone can be of any @{Zone} class derived from @{Zone#ZONE_BASE}. So, these methods are polymorphic to the zones tested on. +-- +-- ## GROUP AI methods +-- +-- A GROUP has AI methods to control the AI activation. +-- +-- * @{#GROUP.SetAIOnOff}(): Turns the GROUP AI On or Off. +-- * @{#GROUP.SetAIOn}(): Turns the GROUP AI On. +-- * @{#GROUP.SetAIOff}(): Turns the GROUP AI Off. +-- +-- @field #GROUP GROUP GROUP = { ClassName = "GROUP", } @@ -15872,7 +16203,7 @@ GROUP = { -- @param Dcs.DCSWrapper.Group#Group GroupName The DCS Group name -- @return #GROUP self function GROUP:Register( GroupName ) - local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) + self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) self:F2( GroupName ) self.GroupName = GroupName @@ -15936,19 +16267,33 @@ function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3 return nil end ---- Returns if the DCS Group is alive. --- When the group exists at run-time, this method will return true, otherwise false. +--- Returns if the Group is alive. +-- The Group must: +-- +-- * Exist at run-time. +-- * Has at least one unit. +-- +-- When the first @{Unit} of the Group is active, it will return true. +-- If the first @{Unit} of the Group is inactive, it will return false. +-- -- @param #GROUP self --- @return #boolean true if the DCS Group is alive. +-- @return #boolean true if the Group is alive and active. +-- @return #boolean false if the Group is alive but inactive. +-- @return #nil if the group does not exist anymore. function GROUP:IsAlive() self:F2( self.GroupName ) - local DCSGroup = self:GetDCSObject() + local DCSGroup = self:GetDCSObject() -- Dcs.DCSGroup#Group if DCSGroup then - local GroupIsAlive = DCSGroup:isExist() and DCSGroup:getUnit(1) ~= nil - self:T3( GroupIsAlive ) - return GroupIsAlive + if DCSGroup:isExist() then + local DCSUnit = DCSGroup:getUnit(1) -- Dcs.DCSUnit#Unit + if DCSUnit then + local GroupIsAlive = DCSUnit:isActive() + self:T3( GroupIsAlive ) + return GroupIsAlive + end + end end return nil @@ -15990,7 +16335,7 @@ function GROUP:GetCategory() return nil end ---- Returns the category name of the DCS Group. +--- Returns the category name of the #GROUP. -- @param #GROUP self -- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship function GROUP:GetCategoryName() @@ -16195,6 +16540,7 @@ function GROUP:GetVec2() end --- Returns the current Vec3 vector of the first DCS Unit in the GROUP. +-- @param #GROUP self -- @return Dcs.DCSTypes#Vec3 Current Vec3 of the first DCS Unit of the GROUP. function GROUP:GetVec3() self:F2( self.GroupName ) @@ -16204,7 +16550,65 @@ function GROUP:GetVec3() return GroupVec3 end +--- Returns a POINT_VEC2 object indicating the point in 2D of the first UNIT of the GROUP within the mission. +-- @param #GROUP self +-- @return Core.Point#POINT_VEC2 The 2D point vector of the first DCS Unit of the GROUP. +-- @return #nil The first UNIT is not existing or alive. +function GROUP:GetPointVec2() + self:F2(self.GroupName) + local FirstUnit = self:GetUnit(1) + + if FirstUnit then + local FirstUnitPointVec2 = FirstUnit:GetPointVec2() + self:T3(FirstUnitPointVec2) + return FirstUnitPointVec2 + end + + return nil +end + +--- Returns a random @{DCSTypes#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP. +-- @param #GROUP self +-- @param #number Radius +-- @return Dcs.DCSTypes#Vec3 The random 3D point vector around the first UNIT of the GROUP. +-- @return #nil The GROUP is invalid or empty +-- @usage +-- -- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP +function GROUP:GetRandomVec3(Radius) + self:F2(self.GroupName) + + local FirstUnit = self:GetUnit(1) + + if FirstUnit then + local FirstUnitRandomPointVec3 = FirstUnit:GetRandomVec3(Radius) + self:T3(FirstUnitRandomPointVec3) + return FirstUnitRandomPointVec3 + end + + return nil +end + +--- Returns the mean heading of every UNIT in the GROUP in degrees +-- @param #GROUP self +-- @return #number mean heading of the GROUP +-- @return #nil The first UNIT is not existing or alive. +function GROUP:GetHeading() + self:F2(self.GroupName) + + local GroupSize = self:GetSize() + local HeadingAccumulator = 0 + + if GroupSize then + for i = 1, GroupSize do + HeadingAccumulator = HeadingAccumulator + self:GetUnit(i):GetHeading() + end + return math.floor(HeadingAccumulator / GroupSize) + end + + return nil + +end do -- Is Zone methods @@ -16654,6 +17058,76 @@ function GROUP:InAir() return nil end +do -- Route methods + + --- (AIR) Return the Group to an @{Airbase#AIRBASE}. + -- The following things are to be taken into account: + -- + -- * The group is respawned to achieve the RTB, there may be side artefacts as a result of this. (Like weapons suddenly come back). + -- * A group consisting out of more than one unit, may rejoin formation when respawned. + -- * A speed can be given in km/h. If no speed is specified, the maximum speed of the first unit will be taken to return to base. + -- * When there is no @{Airbase} object specified, the group will return to the home base if the route of the group is pinned at take-off or at landing to a base. + -- * When there is no @{Airbase} object specified and the group route is not pinned to any airbase, it will return to the nearest airbase. + -- + -- @param #GROUP self + -- @param Wrapper.Airbase#AIRBASE RTBAirbase (optional) The @{Airbase} to return to. If blank, the controllable will return to the nearest friendly airbase. + -- @param #number Speed (optional) The Speed, if no Speed is given, the maximum Speed of the first unit is selected. + -- @return #GROUP + function GROUP:RouteRTB( RTBAirbase, Speed ) + self:F2( { RTBAirbase, Speed } ) + + local DCSGroup = self:GetDCSObject() + + if DCSGroup then + + if RTBAirbase then + + local GroupPoint = self:GetVec2() + local GroupVelocity = self:GetUnit(1):GetDesc().speedMax + + local PointFrom = {} + PointFrom.x = GroupPoint.x + PointFrom.y = GroupPoint.y + PointFrom.type = "Turning Point" + PointFrom.action = "Turning Point" + PointFrom.speed = GroupVelocity + + + local PointTo = {} + local AirbasePointVec2 = RTBAirbase:GetPointVec2() + local AirbaseAirPoint = AirbasePointVec2:RoutePointAir( + POINT_VEC3.RoutePointAltType.BARO, + "Land", + "Landing", + Speed or self:GetUnit(1):GetDesc().speedMax + ) + + AirbaseAirPoint["airdromeId"] = RTBAirbase:GetID() + AirbaseAirPoint["speed_locked"] = true, + + self:E(AirbaseAirPoint ) + + local Points = { PointFrom, AirbaseAirPoint } + + self:T3( Points ) + + local Template = self:GetTemplate() + Template.route.points = Points + self:Respawn( Template ) + + self:Route( Points ) + + self:Respawn(Template) + else + self:ClearTasks() + end + end + + return self + end + +end + function GROUP:OnReSpawn( ReSpawnFunction ) self.ReSpawnFunction = ReSpawnFunction @@ -16685,10 +17159,35 @@ do -- Event Handling end end ---- This module contains the UNIT class. + +do -- Players + + --- Get player names + -- @param #GROUP self + -- @return #table The group has players, an array of player names is returned. + -- @return #nil The group has no players + function GROUP:GetPlayerNames() + + local PlayerNames = nil + + local Units = self:GetUnits() + for UnitID, UnitData in pairs( Units ) do + local Unit = UnitData -- Wrapper.Unit#UNIT + local PlayerName = Unit:GetPlayerName() + if PlayerName and PlayerName ~= "" then + PlayerNames = PlayerNames or {} + table.insert( PlayerNames, PlayerName ) + end + end + + self:F( PlayerNames ) + return PlayerNames + end + +end--- **Wrapper** - UNIT is a wrapper class for the DCS Class Unit. +-- +-- === -- --- 1) @{#UNIT} class, extends @{Controllable#CONTROLLABLE} --- =========================================================== -- The @{#UNIT} class is a wrapper class to handle the DCS Unit objects: -- -- * Support all DCS Unit APIs. @@ -16696,9 +17195,14 @@ end -- * Handle local Unit Controller. -- * Manage the "state" of the DCS Unit. -- --- --- 1.1) UNIT reference methods --- ---------------------- +-- @module Unit + +--- @type UNIT +-- @extends Wrapper.Controllable#CONTROLLABLE + +--- +-- # UNIT class, extends @{Controllable#CONTROLLABLE} +-- -- For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _@{DATABASE} object. -- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Unit objects are spawned (using the @{SPAWN} class). -- @@ -16716,15 +17220,15 @@ end -- -- IMPORTANT: ONE SHOULD NEVER SANATIZE these UNIT OBJECT REFERENCES! (make the UNIT object references nil). -- --- 1.2) DCS UNIT APIs --- ------------------ +-- ## DCS UNIT APIs +-- -- The DCS Unit APIs are used extensively within MOOSE. The UNIT class has for each DCS Unit API a corresponding method. -- To be able to distinguish easily in your code the difference between a UNIT API call and a DCS Unit API call, -- the first letter of the method is also capitalized. So, by example, the DCS Unit method @{DCSWrapper.Unit#Unit.getName}() -- is implemented in the UNIT class as @{#UNIT.GetName}(). -- --- 1.3) Smoke, Flare Units --- ----------------------- +-- ## Smoke, Flare Units +-- -- The UNIT class provides methods to smoke or flare units easily. -- The @{#UNIT.SmokeBlue}(), @{#UNIT.SmokeGreen}(),@{#UNIT.SmokeOrange}(), @{#UNIT.SmokeRed}(), @{#UNIT.SmokeRed}() methods -- will smoke the unit in the corresponding color. Note that smoking a unit is done at the current position of the DCS Unit. @@ -16732,36 +17236,29 @@ end -- The @{#UNIT.FlareGreen}(), @{#UNIT.FlareRed}(), @{#UNIT.FlareWhite}(), @{#UNIT.FlareYellow}() -- methods will fire off a flare in the air with the corresponding color. Note that a flare is a one-off shot and its effect is of very short duration. -- --- 1.4) Location Position, Point --- ----------------------------- +-- ## Location Position, Point +-- -- The UNIT class provides methods to obtain the current point or position of the DCS Unit. -- The @{#UNIT.GetPointVec2}(), @{#UNIT.GetVec3}() will obtain the current **location** of the DCS Unit in a Vec2 (2D) or a **point** in a Vec3 (3D) vector respectively. -- If you want to obtain the complete **3D position** including ori�ntation and direction vectors, consult the @{#UNIT.GetPositionVec3}() method respectively. -- --- 1.5) Test if alive --- ------------------ +-- ## Test if alive +-- -- The @{#UNIT.IsAlive}(), @{#UNIT.IsActive}() methods determines if the DCS Unit is alive, meaning, it is existing and active. -- --- 1.6) Test for proximity --- ----------------------- +-- ## Test for proximity +-- -- The UNIT class contains methods to test the location or proximity against zones or other objects. -- --- ### 1.6.1) Zones +-- ### Zones +-- -- To test whether the Unit is within a **zone**, use the @{#UNIT.IsInZone}() or the @{#UNIT.IsNotInZone}() methods. Any zone can be tested on, but the zone must be derived from @{Zone#ZONE_BASE}. -- --- ### 1.6.2) Units +-- ### Units +-- -- Test if another DCS Unit is within a given radius of the current DCS Unit, use the @{#UNIT.OtherUnitInRadius}() method. -- --- @module Unit --- @author FlightControl - - - - - ---- The UNIT class --- @type UNIT --- @extends Wrapper.Controllable#CONTROLLABLE +-- @field #UNIT UNIT UNIT = { ClassName="UNIT", } @@ -16903,7 +17400,7 @@ function UNIT:ReSpawn( SpawnVec3, Heading ) end -- Remove obscolete units from the group structure - i = 1 + local i = 1 while i <= #SpawnGroupTemplate.units do local UnitTemplateData = SpawnGroupTemplate.units[i] @@ -16939,6 +17436,27 @@ function UNIT:IsActive() return nil end +--- Returns if the Unit is alive. +-- If the Unit is not alive, nil is returned. +-- If the Unit is alive and active, true is returned. +-- If the Unit is alive but not active, false is returned. +-- @param #UNIT self +-- @return #boolean true if Unit is alive and active. +-- @return #boolean false if Unit is alive but not active. +-- @return #nil if the Unit is not existing or is not alive. +function UNIT:IsAlive() + self:F3( self.UnitName ) + + local DCSUnit = self:GetDCSObject() -- Dcs.DCSUnit#Unit + + if DCSUnit then + local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive() + return UnitIsAlive + end + + return nil +end + --- Returns the Unit's callsign - the localized string. @@ -17209,6 +17727,31 @@ function UNIT:GetLife0() return nil end +--- Returns the category name of the #UNIT. +-- @param #UNIT self +-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship +function UNIT:GetCategoryName() + self:F3( self.UnitName ) + + local DCSUnit = self:GetDCSObject() + if DCSUnit then + local CategoryNames = { + [Unit.Category.AIRPLANE] = "Airplane", + [Unit.Category.HELICOPTER] = "Helicopter", + [Unit.Category.GROUND_UNIT] = "Ground Unit", + [Unit.Category.SHIP] = "Ship", + [Unit.Category.STRUCTURE] = "Structure", + } + local UnitCategory = DCSUnit:getDesc().category + self:T3( UnitCategory ) + + return CategoryNames[UnitCategory] + end + + return nil +end + + --- Returns the Unit's A2G threat level on a scale from 1 to 10 ... -- The following threat levels are foreseen: -- @@ -17227,14 +17770,14 @@ end function UNIT:GetThreatLevel() local Attributes = self:GetDesc().attributes - self:E( Attributes ) + self:T( Attributes ) local ThreatLevel = 0 local ThreatText = "" if self:IsGround() then - self:E( "Ground" ) + self:T( "Ground" ) local ThreatLevels = { "Unarmed", @@ -17272,7 +17815,7 @@ function UNIT:GetThreatLevel() if self:IsAir() then - self:E( "Air" ) + self:T( "Air" ) local ThreatLevels = { "Unarmed", @@ -17306,7 +17849,7 @@ function UNIT:GetThreatLevel() if self:IsShip() then - self:E( "Ship" ) + self:T( "Ship" ) --["Aircraft Carriers"] = {"Heavy armed ships",}, --["Cruisers"] = {"Heavy armed ships",}, @@ -17709,7 +18252,7 @@ CLIENT = { -- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() ) -- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() ) -- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() ) -function CLIENT:Find( DCSUnit ) +function CLIENT:Find( DCSUnit, Error ) local ClientName = DCSUnit:getName() local ClientFound = _DATABASE:FindClient( ClientName ) @@ -17718,7 +18261,9 @@ function CLIENT:Find( DCSUnit ) return ClientFound end - error( "CLIENT not found for: " .. ClientName ) + if not Error then + error( "CLIENT not found for: " .. ClientName ) + end end @@ -18165,7 +18710,7 @@ function STATIC:FindByName( StaticName, RaiseError ) self.StaticName = StaticName if StaticFound then - StaticFound:F( { StaticName } ) + StaticFound:F3( { StaticName } ) return StaticFound end @@ -18352,8 +18897,6 @@ end -- -- === -- --- # 1) @{Scoring#SCORING} class, extends @{Base#BASE} --- -- The @{#SCORING} class administers the scoring of player achievements, -- and creates a CSV file logging the scoring events and results for use at team or squadron websites. -- @@ -18404,6 +18947,8 @@ end -- Use the radio menu F10 to consult the scores while running the mission. -- Scores can be reported for your user, or an overall score can be reported of all players currently active in the mission. -- +-- # 1) @{Scoring#SCORING} class, extends @{Base#BASE} +-- -- ## 1.1) Set the destroy score or penalty scale -- -- Score scales can be set for scores granted when enemies or friendlies are destroyed. @@ -18433,8 +18978,6 @@ end -- For example, this can be done as follows: -- -- Scoring:RemoveUnitScore( UNIT:FindByName( "Unit #001" ) ) --- --- -- -- ## 1.3) Define destruction zones that will give extra scores. -- @@ -18893,6 +19436,7 @@ function SCORING:_AddPlayerFromUnit( UnitData ) local UnitCategory = UnitDesc.category local UnitCoalition = UnitData:GetCoalition() local UnitTypeName = UnitData:GetTypeName() + local UnitThreatLevel, UnitThreatType = UnitData:GetThreatLevel() self:T( { PlayerName, UnitName, UnitCategory, UnitCoalition, UnitTypeName } ) @@ -18933,6 +19477,8 @@ function SCORING:_AddPlayerFromUnit( UnitData ) self.Players[PlayerName].UnitCategory = UnitCategory self.Players[PlayerName].UnitType = UnitTypeName self.Players[PlayerName].UNIT = UnitData + self.Players[PlayerName].ThreatLevel = UnitThreatLevel + self.Players[PlayerName].ThreatType = UnitThreatType if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 then if self.Players[PlayerName].PenaltyWarning < 1 then @@ -19191,6 +19737,7 @@ function SCORING:_EventOnHit( Event ) PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT + PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() -- Only grant hit scores if there was more than one second between the last hit. if timer.getTime() - PlayerHit.TimeStamp > 1 then @@ -19229,7 +19776,7 @@ function SCORING:_EventOnHit( Event ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) end - self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else Player.Score = Player.Score + 1 PlayerHit.Score = PlayerHit.Score + 1 @@ -19262,7 +19809,7 @@ function SCORING:_EventOnHit( Event ) ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - self:ScoreCSV( InitPlayerName, "", "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) + self:ScoreCSV( InitPlayerName, "", "HIT_SCORE", 1, 0, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) end end end @@ -19270,6 +19817,89 @@ function SCORING:_EventOnHit( Event ) elseif InitPlayerName == nil then -- It is an AI hitting a player??? end + + -- It is a weapon initiated by a player, that is hitting something + -- This seems to occur only with scenery and static objects. + if Event.WeaponPlayerName ~= nil then + self:_AddPlayerFromUnit( Event.WeaponUNIT ) + if self.Players[Event.WeaponPlayerName] then -- This should normally not happen, but i'll test it anyway. + if TargetPlayerName ~= nil then -- It is a player hitting another player ... + self:_AddPlayerFromUnit( TargetUNIT ) + end + + self:T( "Hitting Scenery" ) + + -- What is he hitting? + if TargetCategory then + + -- A scenery or static got hit, score it. + -- Player contains the score data from self.Players[WeaponPlayerName] + local Player = self.Players[Event.WeaponPlayerName] + + -- Ensure there is a hit table per TargetCategory and TargetUnitName. + Player.Hit[TargetCategory] = Player.Hit[TargetCategory] or {} + Player.Hit[TargetCategory][TargetUnitName] = Player.Hit[TargetCategory][TargetUnitName] or {} + + -- PlayerHit contains the score counters and data per unit that was hit. + local PlayerHit = Player.Hit[TargetCategory][TargetUnitName] + + PlayerHit.Score = PlayerHit.Score or 0 + PlayerHit.Penalty = PlayerHit.Penalty or 0 + PlayerHit.ScoreHit = PlayerHit.ScoreHit or 0 + PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 + PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 + PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT + PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() + + -- Only grant hit scores if there was more than one second between the last hit. + if timer.getTime() - PlayerHit.TimeStamp > 1 then + PlayerHit.TimeStamp = timer.getTime() + + local Score = 0 + + if InitCoalition then -- A coalition object was hit, probably a static. + if InitCoalition == TargetCoalition then + -- TODO: Penalty according scale + Player.Penalty = Player.Penalty + 10 + PlayerHit.Penalty = PlayerHit.Penalty + 10 + PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 + + MESSAGE + :New( "Player '" .. Event.WeaponPlayerName .. "' hit a friendly target " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. + "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + else + Player.Score = Player.Score + 1 + PlayerHit.Score = PlayerHit.Score + 1 + PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 + MESSAGE + :New( "Player '" .. Event.WeaponPlayerName .. "' hit an enemy target " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. + "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + end + else -- A scenery object was hit. + MESSAGE + :New( "Player '" .. Event.WeaponPlayerName .. "' hit a scenery object.", + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + self:ScoreCSV( Event.WeaponPlayerName, "", "HIT_SCORE", 1, 0, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, "", "Scenery", TargetUnitType ) + end + end + end + end + end end --- Track DEAD or CRASH events for the scoring. @@ -19326,8 +19956,13 @@ function SCORING:_EventOnDeadOrCrash( Event ) self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) + local Destroyed = false + -- What is the player destroying? - if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? + if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 then -- Was there a hit for this unit for this player before registered??? + + local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel + local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType Player.Destroy[TargetCategory] = Player.Destroy[TargetCategory] or {} Player.Destroy[TargetCategory][TargetType] = Player.Destroy[TargetCategory][TargetType] or {} @@ -19341,8 +19976,9 @@ function SCORING:_EventOnDeadOrCrash( Event ) if TargetCoalition then if InitCoalition == TargetCoalition then - local ThreatLevelTarget, ThreatTypeTarget = TargetUnit:GetThreatLevel() - local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 + local ThreatLevelTarget = TargetThreatLevel + local ThreatTypeTarget = TargetThreatType + local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1 local ThreatPenalty = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyPenalty / 10 ) self:E( { ThreatLevel = ThreatPenalty, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) @@ -19369,11 +20005,14 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) end + + Destroyed = true self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else - - local ThreatLevelTarget, ThreatTypeTarget = TargetUnit:GetThreatLevel() - local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 + + local ThreatLevelTarget = TargetThreatLevel + local ThreatTypeTarget = TargetThreatType + local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1 local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 ) self:E( { ThreatLevel = ThreatScore, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) @@ -19400,6 +20039,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) end + Destroyed = true self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, ThreatScore, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) local UnitName = TargetUnit:GetName() @@ -19415,6 +20055,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + Destroyed = true end -- Check if there are Zones where the destruction happened. @@ -19433,6 +20074,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + Destroyed = true end end @@ -19454,10 +20096,18 @@ function SCORING:_EventOnDeadOrCrash( Event ) ) :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) + Destroyed = true self:ScoreCSV( PlayerName, "", "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) end end end + + -- Delete now the hit cache if the target was destroyed. + -- Otherwise points will be granted every time a target gets killed by the players that hit that target. + -- This is only relevant for player to player destroys. + if Destroyed then + Player.Hit[TargetCategory][TargetUnitName].TimeStamp = 0 + end end end end @@ -20009,7 +20659,9 @@ CLEANUP = { -- or -- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 ) -- CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 ) -function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self, BASE:New() ) +function CLEANUP:New( ZoneNames, TimeInterval ) + + local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP self:F( { ZoneNames, TimeInterval } ) if type( ZoneNames ) == 'table' then @@ -20021,7 +20673,7 @@ function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self, self.TimeInterval = TimeInterval end - _EVENTDISPATCHER:OnBirth( self._OnEventBirth, self ) + self:HandleEvent( EVENTS.Birth ) self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval ) @@ -20082,32 +20734,24 @@ function CLEANUP:_DestroyMissile( MissileObject ) end end -function CLEANUP:_OnEventBirth( Event ) - self:F( { Event } ) +--- @param #CLEANUP self +-- @param Core.Event#EVENTDATA EventData +function CLEANUP:_OnEventBirth( EventData ) + self:F( { EventData } ) - self.CleanUpList[Event.IniDCSUnitName] = {} - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName - - _EVENTDISPATCHER:OnEngineShutDownForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) - _EVENTDISPATCHER:OnEngineStartUpForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) - _EVENTDISPATCHER:OnHitForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) - _EVENTDISPATCHER:OnPilotDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self ) - _EVENTDISPATCHER:OnDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self ) - _EVENTDISPATCHER:OnCrashForUnit( Event.IniDCSUnitName, self._EventCrash, self ) - _EVENTDISPATCHER:OnShotForUnit( Event.IniDCSUnitName, self._EventShot, self ) - - --self:AddEvent( world.event.S_EVENT_ENGINE_SHUTDOWN, self._EventAddForCleanUp ) - --self:AddEvent( world.event.S_EVENT_ENGINE_STARTUP, self._EventAddForCleanUp ) --- self:AddEvent( world.event.S_EVENT_HIT, self._EventAddForCleanUp ) -- , self._EventHitCleanUp ) --- self:AddEvent( world.event.S_EVENT_CRASH, self._EventCrash ) -- , self._EventHitCleanUp ) --- --self:AddEvent( world.event.S_EVENT_DEAD, self._EventCrash ) --- self:AddEvent( world.event.S_EVENT_SHOT, self._EventShot ) --- --- self:EnableEvents() + self.CleanUpList[EventData.IniDCSUnitName] = {} + self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniDCSUnit + self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniDCSGroup + self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName + self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName + EventData.IniUnit:HandleEvent( EVENTS.EngineShutdown , self._EventAddForCleanUp ) + EventData.IniUnit:HandleEvent( EVENTS.EngineStartup, self._EventAddForCleanUp ) + EventData.IniUnit:HandleEvent( EVENTS.Hit, self._EventAddForCleanUp ) + EventData.IniUnit:HandleEvent( EVENTS.PilotDead, self._EventCrash ) + EventData.IniUnit:HandleEvent( EVENTS.Dead, self._EventCrash ) + EventData.IniUnit:HandleEvent( EVENTS.Crash, self._EventCrash ) + EventData.IniUnit:HandleEvent( EVENTS.Shot, self._EventShot ) end @@ -20533,6 +21177,8 @@ function SPAWN:New( SpawnTemplatePrefix ) error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) end + self:SetEventPriority( 5 ) + return self end @@ -20574,6 +21220,8 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix ) error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) end + self:SetEventPriority( 5 ) + return self end @@ -20873,16 +21521,15 @@ function SPAWN:InitArray( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) self.SpawnGroups[SpawnGroupID].Visible = true - _EVENTDISPATCHER:OnBirthForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnBirth, self ) - _EVENTDISPATCHER:OnCrashForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnDeadOrCrash, self ) - _EVENTDISPATCHER:OnDeadForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnDeadOrCrash, self ) - + self:HandleEvent( EVENTS.Birth, self._OnBirth ) + self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) if self.Repeat then - _EVENTDISPATCHER:OnTakeOffForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnTakeOff, self ) - _EVENTDISPATCHER:OnLandForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnLand, self ) + self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff ) + self:HandleEvent( EVENTS.Land, self._OnLand ) end if self.RepeatOnEngineShutDown then - _EVENTDISPATCHER:OnEngineShutDownForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnEngineShutDown, self ) + self:HandleEvent( EVENTS.EngineShutdown, self._OnEngineShutDown ) end self.SpawnGroups[SpawnGroupID].Group = _DATABASE:Spawn( self.SpawnGroups[SpawnGroupID].SpawnTemplate ) @@ -21022,18 +21669,16 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) end end - _EVENTDISPATCHER:OnBirthForTemplate( SpawnTemplate, self._OnBirth, self ) - _EVENTDISPATCHER:OnCrashForTemplate( SpawnTemplate, self._OnDeadOrCrash, self ) - _EVENTDISPATCHER:OnDeadForTemplate( SpawnTemplate, self._OnDeadOrCrash, self ) - + self:HandleEvent( EVENTS.Birth, self._OnBirth ) + self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) if self.Repeat then - _EVENTDISPATCHER:OnTakeOffForTemplate( SpawnTemplate, self._OnTakeOff, self ) - _EVENTDISPATCHER:OnLandForTemplate( SpawnTemplate, self._OnLand, self ) + self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff ) + self:HandleEvent( EVENTS.Land, self._OnLand ) end if self.RepeatOnEngineShutDown then - _EVENTDISPATCHER:OnEngineShutDownForTemplate( SpawnTemplate, self._OnEngineShutDown, self ) + self:HandleEvent( EVENTS.EngineShutdown, self._OnEngineShutDown ) end - self:T3( SpawnTemplate.name ) self.SpawnGroups[self.SpawnIndex].Group = _DATABASE:Spawn( SpawnTemplate ) @@ -21044,6 +21689,8 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) SpawnGroup:SetAIOnOff( self.AIOnOff ) end + + self:T3( SpawnTemplate.name ) -- If there is a SpawnFunction hook defined, call it. if self.SpawnFunctionHook then @@ -21415,27 +22062,6 @@ function SPAWN:GetGroupFromIndex( SpawnIndex ) end end ---- Get the group index from a DCSUnit. --- The method will search for a #-mark, and will return the index behind the #-mark of the DCSUnit. --- It will return nil of no prefix was found. --- @param #SPAWN self --- @param Dcs.DCSWrapper.Unit#Unit DCSUnit The @{DCSUnit} to be searched. --- @return #string The prefix --- @return #nil Nothing found -function SPAWN:_GetGroupIndexFromDCSUnit( DCSUnit ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, DCSUnit } ) - - local SpawnUnitName = ( DCSUnit and DCSUnit:getName() ) or nil - if SpawnUnitName then - local IndexString = string.match( SpawnUnitName, "#.*-" ):sub( 2, -2 ) - if IndexString then - local Index = tonumber( IndexString ) - return Index - end - end - - return nil -end --- Return the prefix of a SpawnUnit. -- The method will search for a #-mark, and will return the text before the #-mark. @@ -21444,49 +22070,28 @@ end -- @param Dcs.DCSWrapper.Unit#UNIT DCSUnit The @{DCSUnit} to be searched. -- @return #string The prefix -- @return #nil Nothing found -function SPAWN:_GetPrefixFromDCSUnit( DCSUnit ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, DCSUnit } ) +function SPAWN:_GetPrefixFromGroup( SpawnGroup ) + self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) - local DCSGroup = DCSUnit:getGroup() - local DCSUnitName = ( DCSGroup and DCSGroup:getName() ) or nil - if DCSUnitName then - local SpawnPrefix = string.match( DCSUnitName, ".*#" ) - if SpawnPrefix then - SpawnPrefix = SpawnPrefix:sub( 1, -2 ) - end - return SpawnPrefix - end - - return nil -end - ---- Return the group within the SpawnGroups collection with input a DCSUnit. --- @param #SPAWN self --- @param Dcs.DCSWrapper.Unit#Unit DCSUnit The @{DCSUnit} to be searched. --- @return Wrapper.Group#GROUP The Group --- @return #nil Nothing found -function SPAWN:_GetGroupFromDCSUnit( DCSUnit ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, DCSUnit } ) - - local SpawnPrefix = self:_GetPrefixFromDCSUnit( DCSUnit ) - - if self.SpawnTemplatePrefix == SpawnPrefix or ( self.SpawnAliasPrefix and self.SpawnAliasPrefix == SpawnPrefix ) then - local SpawnGroupIndex = self:_GetGroupIndexFromDCSUnit( DCSUnit ) - local SpawnGroup = self.SpawnGroups[SpawnGroupIndex].Group - self:T( SpawnGroup ) - return SpawnGroup - end - - return nil + local GroupName = SpawnGroup:GetName() + if GroupName then + local SpawnPrefix = string.match( GroupName, ".*#" ) + if SpawnPrefix then + SpawnPrefix = SpawnPrefix:sub( 1, -2 ) + end + return SpawnPrefix + end + + return nil end --- Get the index from a given group. -- The function will search the name of the group for a #, and will return the number behind the #-mark. function SPAWN:GetSpawnIndexFromGroup( SpawnGroup ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) + self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) - local IndexString = string.match( SpawnGroup:GetName(), "#.*$" ):sub( 2 ) + local IndexString = string.match( SpawnGroup:GetName(), "#(%d*)$" ):sub( 2 ) local Index = tonumber( IndexString ) self:T3( IndexString, Index ) @@ -21811,17 +22416,18 @@ end -- TODO Need to delete this... _DATABASE does this now ... --- @param #SPAWN self --- @param Core.Event#EVENTDATA Event -function SPAWN:_OnBirth( Event ) +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnBirth( EventData ) + self:F( self.SpawnTemplatePrefix ) - if timer.getTime0() < timer.getAbsTime() then - if Event.IniDCSUnit then - local EventPrefix = self:_GetPrefixFromDCSUnit( Event.IniDCSUnit ) - self:T( { "Birth Event:", EventPrefix, self.SpawnTemplatePrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self.AliveUnits = self.AliveUnits + 1 - self:T( "Alive Units: " .. self.AliveUnits ) - end + local SpawnGroup = EventData.IniGroup + + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "Birth Event:", EventPrefix, self.SpawnTemplatePrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + self.AliveUnits = self.AliveUnits + 1 + self:T( "Alive Units: " .. self.AliveUnits ) end end @@ -21831,13 +22437,15 @@ end -- @todo Need to delete this... _DATABASE does this now ... --- @param #SPAWN self --- @param Core.Event#EVENTDATA Event -function SPAWN:_OnDeadOrCrash( Event ) - self:F( self.SpawnTemplatePrefix, Event ) +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnDeadOrCrash( EventData ) + self:F( self.SpawnTemplatePrefix ) - if Event.IniDCSUnit then - local EventPrefix = self:_GetPrefixFromDCSUnit( Event.IniDCSUnit ) - self:T( { "Dead event: " .. EventPrefix, self.SpawnTemplatePrefix } ) + local SpawnGroup = EventData.IniGroup + + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "Dead event: " .. EventPrefix } ) if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then self.AliveUnits = self.AliveUnits - 1 self:T( "Alive Units: " .. self.AliveUnits ) @@ -21847,34 +22455,37 @@ end --- Will detect AIR Units taking off... When the event takes place, the spawned Group is registered as airborne... -- This is needed to ensure that Re-SPAWNing only is done for landed AIR Groups. --- @todo Need to test for AIR Groups only... -function SPAWN:_OnTakeOff( event ) - self:F( self.SpawnTemplatePrefix, event ) +-- @param #SPAWN self +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnTakeOff( EventData ) + self:F( self.SpawnTemplatePrefix ) - if event.initiator and event.initiator:getName() then - local SpawnGroup = self:_GetGroupFromDCSUnit( event.initiator ) - if SpawnGroup then - self:T( { "TakeOff event: " .. event.initiator:getName(), event } ) - self:T( "self.Landed = false" ) - self.Landed = false - end + local SpawnGroup = EventData.IniGroup + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "TakeOff event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + self:T( "self.Landed = false" ) + SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", false ) + end end end --- Will detect AIR Units landing... When the event takes place, the spawned Group is registered as landed. -- This is needed to ensure that Re-SPAWNing is only done for landed AIR Groups. --- @todo Need to test for AIR Groups only... -function SPAWN:_OnLand( event ) - self:F( self.SpawnTemplatePrefix, event ) +-- @param #SPAWN self +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnLand( EventData ) + self:F( self.SpawnTemplatePrefix ) - local SpawnUnit = event.initiator - if SpawnUnit and SpawnUnit:isExist() and Object.getCategory(SpawnUnit) == Object.Category.UNIT then - local SpawnGroup = self:_GetGroupFromDCSUnit( SpawnUnit ) - if SpawnGroup then - self:T( { "Landed event:" .. SpawnUnit:getName(), event } ) - self.Landed = true - self:T( "self.Landed = true" ) - if self.Landed and self.RepeatOnLanding then + local SpawnGroup = EventData.IniGroup + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "Land event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + -- TODO: Check if this is the last unit of the group that lands. + SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", true ) + if self.RepeatOnLanding then local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) self:T( { "Landed:", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) self:ReSpawn( SpawnGroupIndex ) @@ -21887,18 +22498,18 @@ end -- When the event takes place, and the method @{RepeatOnEngineShutDown} was called, the spawned Group will Re-SPAWN. -- But only when the Unit was registered to have landed. -- @param #SPAWN self --- @see _OnTakeOff --- @see _OnLand --- @todo Need to test for AIR Groups only... -function SPAWN:_OnEngineShutDown( event ) - self:F( self.SpawnTemplatePrefix, event ) +-- @param Core.Event#EVENTDATA EventData +function SPAWN:_OnEngineShutDown( EventData ) + self:F( self.SpawnTemplatePrefix ) - local SpawnUnit = event.initiator - if SpawnUnit and SpawnUnit:isExist() and Object.getCategory(SpawnUnit) == Object.Category.UNIT then - local SpawnGroup = self:_GetGroupFromDCSUnit( SpawnUnit ) - if SpawnGroup then - self:T( { "EngineShutDown event: " .. SpawnUnit:getName(), event } ) - if self.Landed and self.RepeatOnEngineShutDown then + local SpawnGroup = EventData.IniGroup + if SpawnGroup then + local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) + self:T( { "EngineShutdown event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + -- todo: test if on the runway + local Landed = SpawnGroup:GetState( SpawnGroup, "Spawn_Landed" ) + if Landed and self.RepeatOnEngineShutDown then local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) self:T( { "EngineShutDown: ", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) self:ReSpawn( SpawnGroupIndex ) @@ -21987,10 +22598,11 @@ end -- Performance: If in a DCSRTE there are a lot of moving GROUND units, then in a multi player mission, this WILL create lag if -- the main DCS execution core of your CPU is fully utilized. So, this class will limit the amount of simultaneous moving GROUND units -- on defined intervals (currently every minute). --- @module MOVEMENT +-- @module Movement --- the MOVEMENT class --- @type +-- @type MOVEMENT +-- @extends Core.Base#BASE MOVEMENT = { ClassName = "MOVEMENT", } @@ -22004,7 +22616,7 @@ MOVEMENT = { -- Movement_US_Platoons = MOVEMENT:New( { 'US Tank Platoon Left', 'US Tank Platoon Middle', 'US Tank Platoon Right', 'US CH-47D Troops' }, 15 ) function MOVEMENT:New( MovePrefixes, MoveMaximum ) - local self = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, BASE:New() ) -- #MOVEMENT self:F( { MovePrefixes, MoveMaximum } ) if type( MovePrefixes ) == 'table' then @@ -22017,7 +22629,7 @@ function MOVEMENT:New( MovePrefixes, MoveMaximum ) self.AliveUnits = 0 -- Contains the counter how many units are currently alive self.MoveUnits = {} -- Reflects if the Moving for this MovePrefixes is going to be scheduled or not. - _EVENTDISPATCHER:OnBirth( self.OnBirth, self ) + self:HandleEvent( EVENTS.Birth ) -- self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth ) -- @@ -22044,24 +22656,26 @@ end --- Captures the birth events when new Units were spawned. -- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. -function MOVEMENT:OnBirth( Event ) - self:F( { Event } ) +-- @param #MOVEMENT self +-- @param Core.Event#EVENTDATA self +function MOVEMENT:OnEventBirth( EventData ) + self:F( { EventData } ) if timer.getTime0() < timer.getAbsTime() then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line - if Event.IniDCSUnit then - self:T( "Birth object : " .. Event.IniDCSUnitName ) - if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then + if EventData.IniDCSUnit then + self:T( "Birth object : " .. EventData.IniDCSUnitName ) + if EventData.IniDCSGroup and EventData.IniDCSGroup:isExist() then for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do - if string.find( Event.IniDCSUnitName, MovePrefix, 1, true ) then + if string.find( EventData.IniDCSUnitName, MovePrefix, 1, true ) then self.AliveUnits = self.AliveUnits + 1 - self.MoveUnits[Event.IniDCSUnitName] = Event.IniDCSGroupName + self.MoveUnits[EventData.IniDCSUnitName] = EventData.IniDCSGroupName self:T( self.AliveUnits ) end end end end - _EVENTDISPATCHER:OnCrashForUnit( Event.IniDCSUnitName, self.OnDeadOrCrash, self ) - _EVENTDISPATCHER:OnDeadForUnit( Event.IniDCSUnitName, self.OnDeadOrCrash, self ) + + EventData.IniUnit:HandleEvent( EVENTS.DEAD, self.OnDeadOrCrash ) end end @@ -22148,25 +22762,28 @@ function SEAD:New( SEADGroupPrefixes ) else self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes end - _EVENTDISPATCHER:OnShot( self.EventShot, self ) + + self:HandleEvent( EVENTS.Shot ) return self end --- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. -- @see SEAD -function SEAD:EventShot( Event ) - self:F( { Event } ) +-- @param #SEAD +-- @param Core.Event#EVENTDATA EventData +function SEAD:OnEventShot( EventData ) + self:F( { EventData } ) - local SEADUnit = Event.IniDCSUnit - local SEADUnitName = Event.IniDCSUnitName - local SEADWeapon = Event.Weapon -- Identify the weapon fired - local SEADWeaponName = Event.WeaponName -- return weapon type + local SEADUnit = EventData.IniDCSUnit + local SEADUnitName = EventData.IniDCSUnitName + local SEADWeapon = EventData.Weapon -- Identify the weapon fired + local SEADWeaponName = EventData.WeaponName -- return weapon type -- Start of the 2nd loop self:T( "Missile Launched = " .. SEADWeaponName ) if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD local _evade = math.random (1,100) -- random number for chance of evading action - local _targetMim = Event.Weapon:getTarget() -- Identify target + local _targetMim = EventData.Weapon:getTarget() -- Identify target local _targetMimname = Unit.getName(_targetMim) local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) local _targetMimgroupName = _targetMimgroup:getName() @@ -22325,7 +22942,7 @@ end -- -- ESCORT initialization methods. -- ============================== --- The following menus are created within the RADIO MENU of an active unit hosted by a player: +-- The following menus are created within the RADIO MENU (F10) of an active unit hosted by a player: -- -- * @{#ESCORT.MenuFollowAt}: Creates a menu to make the escort follow the client. -- * @{#ESCORT.MenuHoldAtEscortPosition}: Creates a menu to hold the escort at its current position. @@ -22369,6 +22986,7 @@ end -- @Field Dcs.DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the EscortGroup. -- @field Dcs.DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the EscortGroup. -- @field Core.Menu#MENU_CLIENT EscortMenuResumeMission +-- @field Functional.Detection#DETECTION_BASE Detection ESCORT = { ClassName = "ESCORT", EscortName = nil, -- The Escort Name @@ -22417,14 +23035,22 @@ ESCORT = { -- -- Now use these 2 objects to construct the new EscortPlanes object. -- EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." ) function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) - local self = BASE:Inherit( self, BASE:New() ) + + local self = BASE:Inherit( self, BASE:New() ) -- #ESCORT self:F( { EscortClient, EscortGroup, EscortName } ) self.EscortClient = EscortClient -- Wrapper.Client#CLIENT self.EscortGroup = EscortGroup -- Wrapper.Group#GROUP self.EscortName = EscortName self.EscortBriefing = EscortBriefing - + + self.EscortSetGroup = SET_GROUP:New() + self.EscortSetGroup:AddObject( self.EscortGroup ) + self.EscortSetGroup:Flush() + self.Detection = DETECTION_UNITS:New( self.EscortSetGroup, 15000 ) + + self.EscortGroup.Detection = self.Detection + -- Set EscortGroup known at EscortClient. if not self.EscortClient._EscortGroups then self.EscortClient._EscortGroups = {} @@ -22434,7 +23060,7 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) self.EscortClient._EscortGroups[EscortGroup:GetName()] = {} self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup = self.EscortGroup self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName = self.EscortName - self.EscortClient._EscortGroups[EscortGroup:GetName()].Targets = {} + self.EscortClient._EscortGroups[EscortGroup:GetName()].Detection = self.EscortGroup.Detection end self.EscortMenu = MENU_CLIENT:New( self.EscortClient, self.EscortName ) @@ -22459,13 +23085,30 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) self.FollowDistance = 100 self.CT1 = 0 self.GT1 = 0 - self.FollowScheduler = SCHEDULER:New( self, self._FollowScheduler, {}, 1, .5, .01 ) - self.EscortMode = ESCORT.MODE.MISSION - self.FollowScheduler:Stop() + self.FollowScheduler, self.FollowSchedule = SCHEDULER:New( self, self._FollowScheduler, {}, 1, .5, .01 ) + self.FollowScheduler:Stop( self.FollowSchedule ) + + self.EscortMode = ESCORT.MODE.MISSION + + return self end +--- Set a Detection method for the EscortClient to be reported upon. +-- Detection methods are based on the derived classes from DETECTION_BASE. +-- @param #ESCORT self +-- @param Function.Detection#DETECTION_BASE Detection +function ESCORT:SetDetection( Detection ) + + self.Detection = Detection + self.EscortGroup.Detection = self.Detection + self.EscortClient._EscortGroups[self.EscortGroup:GetName()].Detection = self.EscortGroup.Detection + + Detection:__Start( 1 ) + +end + --- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to. -- This allows to visualize where the escort is flying to. -- @param #ESCORT self @@ -22523,7 +23166,7 @@ function ESCORT:MenuFollowAt( Distance ) self.EscortMenuJoinUpAndFollow = {} end - self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, { ParamSelf = self, ParamDistance = Distance } ) + self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, self, Distance ) self.EscortMode = ESCORT.MODE.FOLLOW end @@ -22581,11 +23224,10 @@ function ESCORT:MenuHoldAtEscortPosition( Height, Seconds, MenuTextFormat ) MenuText, self.EscortMenuHold, ESCORT._HoldPosition, - { ParamSelf = self, - ParamOrbitGroup = self.EscortGroup, - ParamHeight = Height, - ParamSeconds = Seconds - } + self, + self.EscortGroup, + Height, + Seconds ) end @@ -22702,9 +23344,8 @@ function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat ) MenuText, self.EscortMenuScan, ESCORT._ScanTargets, - { ParamSelf = self, - ParamScanDuration = 30 - } + self, + 30 ) end @@ -22734,11 +23375,11 @@ function ESCORT:MenuFlare( MenuTextFormat ) end if not self.EscortMenuFlare then - self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, { ParamSelf = self } ) - self.EscortMenuFlareGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Green, ParamMessage = "Released a green flare!" } ) - self.EscortMenuFlareRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Red, ParamMessage = "Released a red flare!" } ) - self.EscortMenuFlareWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.White, ParamMessage = "Released a white flare!" } ) - self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Yellow, ParamMessage = "Released a yellow flare!" } ) + self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, self ) + self.EscortMenuFlareGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Green, "Released a green flare!" ) + self.EscortMenuFlareRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Red, "Released a red flare!" ) + self.EscortMenuFlareWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.White, "Released a white flare!" ) + self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Yellow, "Released a yellow flare!" ) end return self @@ -22767,12 +23408,12 @@ function ESCORT:MenuSmoke( MenuTextFormat ) end if not self.EscortMenuSmoke then - self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, { ParamSelf = self } ) - self.EscortMenuSmokeGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Green, ParamMessage = "Releasing green smoke!" } ) - self.EscortMenuSmokeRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Red, ParamMessage = "Releasing red smoke!" } ) - self.EscortMenuSmokeWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.White, ParamMessage = "Releasing white smoke!" } ) - self.EscortMenuSmokeOrange = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Orange, ParamMessage = "Releasing orange smoke!" } ) - self.EscortMenuSmokeBlue = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Blue, ParamMessage = "Releasing blue smoke!" } ) + self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, self ) + self.EscortMenuSmokeGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Green, "Releasing green smoke!" ) + self.EscortMenuSmokeRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Red, "Releasing red smoke!" ) + self.EscortMenuSmokeWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.White, "Releasing white smoke!" ) + self.EscortMenuSmokeOrange = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Orange, "Releasing orange smoke!" ) + self.EscortMenuSmokeBlue = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Blue, "Releasing blue smoke!" ) end end @@ -22797,9 +23438,9 @@ function ESCORT:MenuReportTargets( Seconds ) end -- Report Targets - self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, { ParamSelf = self } ) - self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = true } ) - self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = false, } ) + self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, self ) + self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, true ) + self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, false ) -- Attack Targets self.EscortMenuAttackNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Attack targets", self.EscortMenu ) @@ -22836,16 +23477,16 @@ function ESCORT:MenuROE( MenuTextFormat ) -- Rules of Engagement self.EscortMenuROE = MENU_CLIENT:New( self.EscortClient, "ROE", self.EscortMenu ) if self.EscortGroup:OptionROEHoldFirePossible() then - self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEHoldFire(), ParamMessage = "Holding weapons!" } ) + self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEHoldFire(), "Holding weapons!" ) end if self.EscortGroup:OptionROEReturnFirePossible() then - self.EscortMenuROEReturnFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Return Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEReturnFire(), ParamMessage = "Returning fire!" } ) + self.EscortMenuROEReturnFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Return Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEReturnFire(), "Returning fire!" ) end if self.EscortGroup:OptionROEOpenFirePossible() then - self.EscortMenuROEOpenFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Open Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEOpenFire(), ParamMessage = "Opening fire on designated targets!!" } ) + self.EscortMenuROEOpenFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Open Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEOpenFire(), "Opening fire on designated targets!!" ) end if self.EscortGroup:OptionROEWeaponFreePossible() then - self.EscortMenuROEWeaponFree = MENU_CLIENT_COMMAND:New( self.EscortClient, "Weapon Free", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEWeaponFree(), ParamMessage = "Opening fire on targets of opportunity!" } ) + self.EscortMenuROEWeaponFree = MENU_CLIENT_COMMAND:New( self.EscortClient, "Weapon Free", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEWeaponFree(), "Opening fire on targets of opportunity!" ) end end @@ -22865,16 +23506,16 @@ function ESCORT:MenuEvasion( MenuTextFormat ) -- Reaction to Threats self.EscortMenuEvasion = MENU_CLIENT:New( self.EscortClient, "Evasion", self.EscortMenu ) if self.EscortGroup:OptionROTNoReactionPossible() then - self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTNoReaction(), ParamMessage = "Fighting until death!" } ) + self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTNoReaction(), "Fighting until death!" ) end if self.EscortGroup:OptionROTPassiveDefensePossible() then - self.EscortMenuEvasionPassiveDefense = MENU_CLIENT_COMMAND:New( self.EscortClient, "Use flares, chaff and jammers", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTPassiveDefense(), ParamMessage = "Defending using jammers, chaff and flares!" } ) + self.EscortMenuEvasionPassiveDefense = MENU_CLIENT_COMMAND:New( self.EscortClient, "Use flares, chaff and jammers", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTPassiveDefense(), "Defending using jammers, chaff and flares!" ) end if self.EscortGroup:OptionROTEvadeFirePossible() then - self.EscortMenuEvasionEvadeFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Evade enemy fire", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTEvadeFire(), ParamMessage = "Evading on enemy fire!" } ) + self.EscortMenuEvasionEvadeFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Evade enemy fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTEvadeFire(), "Evading on enemy fire!" ) end if self.EscortGroup:OptionROTVerticalPossible() then - self.EscortMenuOptionEvasionVertical = MENU_CLIENT_COMMAND:New( self.EscortClient, "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTVertical(), ParamMessage = "Evading on enemy fire with vertical manoeuvres!" } ) + self.EscortMenuOptionEvasionVertical = MENU_CLIENT_COMMAND:New( self.EscortClient, "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTVertical(), "Evading on enemy fire with vertical manoeuvres!" ) end end end @@ -22899,18 +23540,14 @@ end --- @param #MENUPARAM MenuParam -function ESCORT._HoldPosition( MenuParam ) +function ESCORT:_HoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local OrbitGroup = MenuParam.ParamOrbitGroup -- Wrapper.Group#GROUP local OrbitUnit = OrbitGroup:GetUnit(1) -- Wrapper.Unit#UNIT - local OrbitHeight = MenuParam.ParamHeight - local OrbitSeconds = MenuParam.ParamSeconds -- Not implemented yet - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) local PointFrom = {} local GroupVec3 = EscortGroup:GetUnit(1):GetVec3() @@ -22943,13 +23580,12 @@ function ESCORT._HoldPosition( MenuParam ) end --- @param #MENUPARAM MenuParam -function ESCORT._JoinUpAndFollow( MenuParam ) +function ESCORT:_JoinUpAndFollow( Distance ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - self.Distance = MenuParam.ParamDistance + self.Distance = Distance self:JoinUpAndFollow( EscortGroup, EscortClient, self.Distance ) end @@ -22962,7 +23598,7 @@ end function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) self:F( { EscortGroup, EscortClient, Distance } ) - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) EscortGroup:OptionROEHoldFire() EscortGroup:OptionROTPassiveDefense() @@ -22971,44 +23607,35 @@ function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) self.CT1 = 0 self.GT1 = 0 - self.FollowScheduler:Start() + self.FollowScheduler:Start( self.FollowSchedule ) EscortGroup:MessageToClient( "Rejoining and Following at " .. Distance .. "!", 30, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._Flare( MenuParam ) +function ESCORT:_Flare( Color, Message ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local Color = MenuParam.ParamColor - local Message = MenuParam.ParamMessage - EscortGroup:GetUnit(1):Flare( Color ) EscortGroup:MessageToClient( Message, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._Smoke( MenuParam ) +function ESCORT:_Smoke( Color, Message ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local Color = MenuParam.ParamColor - local Message = MenuParam.ParamMessage - EscortGroup:GetUnit(1):Smoke( Color ) EscortGroup:MessageToClient( Message, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ReportNearbyTargetsNow( MenuParam ) +function ESCORT:_ReportNearbyTargetsNow() - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient @@ -23016,17 +23643,16 @@ function ESCORT._ReportNearbyTargetsNow( MenuParam ) end -function ESCORT._SwitchReportNearbyTargets( MenuParam ) +function ESCORT:_SwitchReportNearbyTargets( ReportTargets ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - self.ReportTargets = MenuParam.ParamReportTargets + self.ReportTargets = ReportTargets if self.ReportTargets then if not self.ReportTargetsScheduler then - self.ReportTargetsScheduler = SCHEDULER:New( self, self._ReportTargetsScheduler, {}, 1, 30 ) + self.ReportTargetsScheduler:Schedule( self, self._ReportTargetsScheduler, {}, 1, 30 ) end else routines.removeFunction( self.ReportTargetsScheduler ) @@ -23035,40 +23661,31 @@ function ESCORT._SwitchReportNearbyTargets( MenuParam ) end --- @param #MENUPARAM MenuParam -function ESCORT._ScanTargets( MenuParam ) +function ESCORT:_ScanTargets( ScanDuration ) - local self = MenuParam.ParamSelf - local EscortGroup = self.EscortGroup + local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP local EscortClient = self.EscortClient - local ScanDuration = MenuParam.ParamScanDuration - - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroup:IsHelicopter() then - SCHEDULER:New( EscortGroup, EscortGroup.PushTask, - { EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 200, 20 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ) - }, - 1 - ) + EscortGroup:PushTask( + EscortGroup:TaskControlled( + EscortGroup:TaskOrbitCircle( 200, 20 ), + EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) + ), 1 ) elseif EscortGroup:IsAirPlane() then - SCHEDULER:New( EscortGroup, EscortGroup.PushTask, - { EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 1000, 500 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ) - }, - 1 - ) + EscortGroup:PushTask( + EscortGroup:TaskControlled( + EscortGroup:TaskOrbitCircle( 1000, 500 ), + EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) + ), 1 ) end EscortGroup:MessageToClient( "Scanning targets for " .. ScanDuration .. " seconds.", ScanDuration, EscortClient ) if self.EscortMode == ESCORT.MODE.FOLLOW then - self.FollowScheduler:Start() + self.FollowScheduler:Start( self.FollowSchedule ) end end @@ -23085,124 +23702,157 @@ function _Resume( EscortGroup ) end ---- @param #MENUPARAM MenuParam -function ESCORT._AttackTarget( MenuParam ) +--- @param #ESCORT self +-- @param #number DetectedItemID +function ESCORT:_AttackTarget( DetectedItemID ) - local self = MenuParam.ParamSelf - local EscortGroup = self.EscortGroup + local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP + self:E( EscortGroup ) local EscortClient = self.EscortClient - local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT - self.FollowScheduler:Stop() - - self:T( AttackUnit ) + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroup:IsAir() then EscortGroup:OptionROEOpenFire() EscortGroup:OptionROTPassiveDefense() EscortGroup:SetState( EscortGroup, "Escort", self ) - SCHEDULER:New( EscortGroup, - EscortGroup.PushTask, - { EscortGroup:TaskCombo( - { EscortGroup:TaskAttackUnit( AttackUnit ), - EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit, Tasks ) + if DetectedUnit:IsAlive() then + Tasks[#Tasks+1] = EscortGroup:TaskAttackUnit( DetectedUnit ) + end + end, Tasks + ) + + Tasks[#Tasks+1] = EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } ) + + EscortGroup:SetTask( + EscortGroup:TaskCombo( + Tasks + ), 1 ) + else - SCHEDULER:New( EscortGroup, - EscortGroup.PushTask, - { EscortGroup:TaskCombo( - { EscortGroup:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit, Tasks ) + if DetectedUnit:IsAlive() then + Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) + end + end, Tasks + ) + + EscortGroup:SetTask( + EscortGroup:TaskCombo( + Tasks + ), 1 ) + end EscortGroup:MessageToClient( "Engaging Designated Unit!", 10, EscortClient ) end ---- @param #MENUPARAM MenuParam -function ESCORT._AssistTarget( MenuParam ) +--- +-- @param #number DetectedItemID +function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItemID ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortGroupAttack = MenuParam.ParamEscortGroup - local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT - self.FollowScheduler:Stop() - - self:T( AttackUnit ) + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroupAttack:IsAir() then EscortGroupAttack:OptionROEOpenFire() EscortGroupAttack:OptionROTVertical() - SCHDULER:New( EscortGroupAttack, - EscortGroupAttack.PushTask, - { EscortGroupAttack:TaskCombo( - { EscortGroupAttack:TaskAttackUnit( AttackUnit ), - EscortGroupAttack:TaskOrbitCircle( 500, 350 ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit, Tasks ) + if DetectedUnit:IsAlive() then + Tasks[#Tasks+1] = EscortGroupAttack:TaskAttackUnit( DetectedUnit ) + end + end, Tasks + ) + + Tasks[#Tasks+1] = EscortGroupAttack:TaskOrbitCircle( 500, 350 ) + + EscortGroupAttack:SetTask( + EscortGroupAttack:TaskCombo( + Tasks + ), 1 ) + else - SCHEDULER:New( EscortGroupAttack, - EscortGroupAttack.PushTask, - { EscortGroupAttack:TaskCombo( - { EscortGroupAttack:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) - } - ) - }, 10 + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit, Tasks ) + if DetectedUnit:IsAlive() then + Tasks[#Tasks+1] = EscortGroupAttack:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) + end + end, Tasks + ) + + EscortGroupAttack:SetTask( + EscortGroupAttack:TaskCombo( + Tasks + ), 1 ) + end + EscortGroupAttack:MessageToClient( "Assisting with the destroying the enemy unit!", 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ROE( MenuParam ) +function ESCORT:_ROE( EscortROEFunction, EscortROEMessage ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortROEFunction = MenuParam.ParamFunction - local EscortROEMessage = MenuParam.ParamMessage - pcall( function() EscortROEFunction() end ) EscortGroup:MessageToClient( EscortROEMessage, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ROT( MenuParam ) +function ESCORT:_ROT( EscortROTFunction, EscortROTMessage ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortROTFunction = MenuParam.ParamFunction - local EscortROTMessage = MenuParam.ParamMessage - pcall( function() EscortROTFunction() end ) EscortGroup:MessageToClient( EscortROTMessage, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ResumeMission( MenuParam ) +function ESCORT:_ResumeMission( WayPoint ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local WayPoint = MenuParam.ParamWayPoint - - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) local WayPoints = EscortGroup:GetTaskRoute() self:T( WayPoint, WayPoints ) @@ -23346,176 +23996,244 @@ function ESCORT:_ReportTargetsScheduler() self:F( self.EscortGroup:GetName() ) if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then - local EscortGroupName = self.EscortGroup:GetName() - local EscortTargets = self.EscortGroup:GetDetectedTargets() - local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets + if true then - local EscortTargetMessages = "" - for EscortTargetID, EscortTarget in pairs( EscortTargets ) do - local EscortObject = EscortTarget.object - self:T( EscortObject ) - if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then + local EscortGroupName = self.EscortGroup:GetName() + + self.EscortMenuAttackNearbyTargets:RemoveSubMenus() - local EscortTargetUnit = UNIT:Find( EscortObject ) - local EscortTargetUnitName = EscortTargetUnit:GetName() - - - - -- local EscortTargetIsDetected, - -- EscortTargetIsVisible, - -- EscortTargetLastTime, - -- EscortTargetKnowType, - -- EscortTargetKnowDistance, - -- EscortTargetLastPos, - -- EscortTargetLastVelocity - -- = self.EscortGroup:IsTargetDetected( EscortObject ) - -- - -- self:T( { EscortTargetIsDetected, - -- EscortTargetIsVisible, - -- EscortTargetLastTime, - -- EscortTargetKnowType, - -- EscortTargetKnowDistance, - -- EscortTargetLastPos, - -- EscortTargetLastVelocity } ) - - - local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3() - local EscortVec3 = self.EscortGroup:GetVec3() - local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + - ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + - ( EscortTargetUnitVec3.z - EscortVec3.z )^2 - ) ^ 0.5 / 1000 - - self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } ) - - if Distance <= 15 then - - if not ClientEscortTargets[EscortTargetUnitName] then - ClientEscortTargets[EscortTargetUnitName] = {} - end - ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit - ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible - ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type - ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance - else - if ClientEscortTargets[EscortTargetUnitName] then - ClientEscortTargets[EscortTargetUnitName] = nil - end - end + if self.EscortMenuTargetAssistance then + self.EscortMenuTargetAssistance:RemoveSubMenus() end - end - self:T( { "Sorting Targets Table:", ClientEscortTargets } ) - table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end ) - self:T( { "Sorted Targets Table:", ClientEscortTargets } ) + local DetectedItems = self.Detection:GetDetectedItems() + self:E( DetectedItems ) - -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. - self.EscortMenuAttackNearbyTargets:RemoveSubMenus() + local DetectedTargets = false + + local DetectedMsgs = {} + + for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do - if self.EscortMenuTargetAssistance then - self.EscortMenuTargetAssistance:RemoveSubMenus() - end + local ClientEscortTargets = EscortGroupData.Detection - --for MenuIndex = 1, #self.EscortMenuAttackTargets do - -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } ) - -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove() - --end + for DetectedItemID, DetectedItem in ipairs( DetectedItems ) do + self:E( { DetectedItemID, DetectedItem } ) + -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. + + local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID ) - - if ClientEscortTargets then - for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do - - for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do - - if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then - - local EscortTargetMessage = "" - local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName() - local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName() - if ClientEscortTargetData.type then - EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at " - else - EscortTargetMessage = EscortTargetMessage .. "Unknown target at " - end - - local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3() - local EscortVec3 = self.EscortGroup:GetVec3() - local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + - ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + - ( EscortTargetUnitVec3.z - EscortVec3.z )^2 - ) ^ 0.5 / 1000 - - self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } ) - if ClientEscortTargetData.visible == false then - EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km" - else - EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km" - end - - if ClientEscortTargetData.visible then - EscortTargetMessage = EscortTargetMessage .. ", visual" - end - - if ClientEscortGroupName == EscortGroupName then - - MENU_CLIENT_COMMAND:New( self.EscortClient, - EscortTargetMessage, - self.EscortMenuAttackNearbyTargets, - ESCORT._AttackTarget, - { ParamSelf = self, - ParamUnit = ClientEscortTargetData.AttackUnit - } - ) - EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage - else - if self.EscortMenuTargetAssistance then - local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) - MENU_CLIENT_COMMAND:New( self.EscortClient, - EscortTargetMessage, - MenuTargetAssistance, - ESCORT._AssistTarget, - { ParamSelf = self, - ParamEscortGroup = EscortGroupData.EscortGroup, - ParamUnit = ClientEscortTargetData.AttackUnit - } - ) - end - end + if ClientEscortGroupName == EscortGroupName then + + DetectedMsgs[#DetectedMsgs+1] = DetectedItemReportSummary + + MENU_CLIENT_COMMAND:New( self.EscortClient, + DetectedItemReportSummary, + self.EscortMenuAttackNearbyTargets, + ESCORT._AttackTarget, + self, + DetectedItemID + ) else - ClientEscortTargetData = nil + if self.EscortMenuTargetAssistance then + + self:T( DetectedItemReportSummary ) + local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) + MENU_CLIENT_COMMAND:New( self.EscortClient, + DetectedItemReportSummary, + MenuTargetAssistance, + ESCORT._AssistTarget, + self, + EscortGroupData.EscortGroup, + DetectedItemID + ) + end end + + DetectedTargets = true + end end - - if EscortTargetMessages ~= "" and self.ReportTargets == true then - self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient ) + self:E( DetectedMsgs ) + if DetectedTargets then + self.EscortGroup:MessageToClient( "Detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient ) else - self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient ) + self.EscortGroup:MessageToClient( "No targets detected.", 10, self.EscortClient ) end + + return true + else +-- local EscortGroupName = self.EscortGroup:GetName() +-- local EscortTargets = self.EscortGroup:GetDetectedTargets() +-- +-- local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets +-- +-- local EscortTargetMessages = "" +-- for EscortTargetID, EscortTarget in pairs( EscortTargets ) do +-- local EscortObject = EscortTarget.object +-- self:T( EscortObject ) +-- if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then +-- +-- local EscortTargetUnit = UNIT:Find( EscortObject ) +-- local EscortTargetUnitName = EscortTargetUnit:GetName() +-- +-- +-- +-- -- local EscortTargetIsDetected, +-- -- EscortTargetIsVisible, +-- -- EscortTargetLastTime, +-- -- EscortTargetKnowType, +-- -- EscortTargetKnowDistance, +-- -- EscortTargetLastPos, +-- -- EscortTargetLastVelocity +-- -- = self.EscortGroup:IsTargetDetected( EscortObject ) +-- -- +-- -- self:T( { EscortTargetIsDetected, +-- -- EscortTargetIsVisible, +-- -- EscortTargetLastTime, +-- -- EscortTargetKnowType, +-- -- EscortTargetKnowDistance, +-- -- EscortTargetLastPos, +-- -- EscortTargetLastVelocity } ) +-- +-- +-- local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3() +-- local EscortVec3 = self.EscortGroup:GetVec3() +-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + +-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + +-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2 +-- ) ^ 0.5 / 1000 +-- +-- self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } ) +-- +-- if Distance <= 15 then +-- +-- if not ClientEscortTargets[EscortTargetUnitName] then +-- ClientEscortTargets[EscortTargetUnitName] = {} +-- end +-- ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit +-- ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible +-- ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type +-- ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance +-- else +-- if ClientEscortTargets[EscortTargetUnitName] then +-- ClientEscortTargets[EscortTargetUnitName] = nil +-- end +-- end +-- end +-- end +-- +-- self:T( { "Sorting Targets Table:", ClientEscortTargets } ) +-- table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end ) +-- self:T( { "Sorted Targets Table:", ClientEscortTargets } ) +-- +-- -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. +-- self.EscortMenuAttackNearbyTargets:RemoveSubMenus() +-- +-- if self.EscortMenuTargetAssistance then +-- self.EscortMenuTargetAssistance:RemoveSubMenus() +-- end +-- +-- --for MenuIndex = 1, #self.EscortMenuAttackTargets do +-- -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } ) +-- -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove() +-- --end +-- +-- +-- if ClientEscortTargets then +-- for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do +-- +-- for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do +-- +-- if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then +-- +-- local EscortTargetMessage = "" +-- local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName() +-- local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName() +-- if ClientEscortTargetData.type then +-- EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at " +-- else +-- EscortTargetMessage = EscortTargetMessage .. "Unknown target at " +-- end +-- +-- local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3() +-- local EscortVec3 = self.EscortGroup:GetVec3() +-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + +-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + +-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2 +-- ) ^ 0.5 / 1000 +-- +-- self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } ) +-- if ClientEscortTargetData.visible == false then +-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km" +-- else +-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km" +-- end +-- +-- if ClientEscortTargetData.visible then +-- EscortTargetMessage = EscortTargetMessage .. ", visual" +-- end +-- +-- if ClientEscortGroupName == EscortGroupName then +-- +-- MENU_CLIENT_COMMAND:New( self.EscortClient, +-- EscortTargetMessage, +-- self.EscortMenuAttackNearbyTargets, +-- ESCORT._AttackTarget, +-- { ParamSelf = self, +-- ParamUnit = ClientEscortTargetData.AttackUnit +-- } +-- ) +-- EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage +-- else +-- if self.EscortMenuTargetAssistance then +-- local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) +-- MENU_CLIENT_COMMAND:New( self.EscortClient, +-- EscortTargetMessage, +-- MenuTargetAssistance, +-- ESCORT._AssistTarget, +-- self, +-- EscortGroupData.EscortGroup, +-- ClientEscortTargetData.AttackUnit +-- ) +-- end +-- end +-- else +-- ClientEscortTargetData = nil +-- end +-- end +-- end +-- +-- if EscortTargetMessages ~= "" and self.ReportTargets == true then +-- self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient ) +-- else +-- self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient ) +-- end +-- end +-- +-- if self.EscortMenuResumeMission then +-- self.EscortMenuResumeMission:RemoveSubMenus() +-- +-- -- if self.EscortMenuResumeWayPoints then +-- -- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do +-- -- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } ) +-- -- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove() +-- -- end +-- -- end +-- +-- local TaskPoints = self:RegisterRoute() +-- for WayPointID, WayPoint in pairs( TaskPoints ) do +-- local EscortVec3 = self.EscortGroup:GetVec3() +-- local Distance = ( ( WayPoint.x - EscortVec3.x )^2 + +-- ( WayPoint.y - EscortVec3.z )^2 +-- ) ^ 0.5 / 1000 +-- MENU_CLIENT_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } ) +-- end +-- end +-- +-- return true end - - if self.EscortMenuResumeMission then - self.EscortMenuResumeMission:RemoveSubMenus() - - -- if self.EscortMenuResumeWayPoints then - -- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do - -- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } ) - -- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove() - -- end - -- end - - local TaskPoints = self:RegisterRoute() - for WayPointID, WayPoint in pairs( TaskPoints ) do - local EscortVec3 = self.EscortGroup:GetVec3() - local Distance = ( ( WayPoint.x - EscortVec3.x )^2 + - ( WayPoint.y - EscortVec3.z )^2 - ) ^ 0.5 / 1000 - MENU_CLIENT_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } ) - end - end - - return true end return false @@ -23693,7 +24411,7 @@ function MISSILETRAINER:New( Distance, Briefing ) self.Distance = Distance / 1000 - _EVENTDISPATCHER:OnShot( self._EventShot, self ) + self:HandleEvent( EVENTS.Shot ) self.DBClients = SET_CLIENT:New():FilterStart() @@ -23971,14 +24689,14 @@ end --- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. -- @param #MISSILETRAINER self --- @param Core.Event#EVENTDATA Event -function MISSILETRAINER:_EventShot( Event ) - self:F( { Event } ) +-- @param Core.Event#EVENTDATA EventData +function MISSILETRAINER:OnEventShot( EVentData ) + self:F( { EVentData } ) - local TrainerSourceDCSUnit = Event.IniDCSUnit - local TrainerSourceDCSUnitName = Event.IniDCSUnitName - local TrainerWeapon = Event.Weapon -- Identify the weapon fired - local TrainerWeaponName = Event.WeaponName -- return weapon type + local TrainerSourceDCSUnit = EVentData.IniDCSUnit + local TrainerSourceDCSUnitName = EVentData.IniDCSUnitName + local TrainerWeapon = EVentData.Weapon -- Identify the weapon fired + local TrainerWeaponName = EVentData.WeaponName -- return weapon type self:T( "Missile Launched = " .. TrainerWeaponName ) @@ -25422,991 +26140,2079 @@ end - --- This module contains the DETECTION classes. + --- **Functional** - DETECTION_ classes model the detection of enemy units by FACs or RECCEs and group them according various methods. +-- +-- ![Banner Image](..\Presentations\DETECTION\Dia1.JPG) -- -- === -- --- 1) @{Detection#DETECTION_BASE} class, extends @{Base#BASE} --- ========================================================== --- The @{Detection#DETECTION_BASE} class defines the core functions to administer detected objects. --- The @{Detection#DETECTION_BASE} class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). +-- DETECTION classes facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units). +-- DETECTION uses the in-built detection capabilities of DCS World, but adds new functionalities. -- --- 1.1) DETECTION_BASE constructor --- ------------------------------- --- Construct a new DETECTION_BASE instance using the @{Detection#DETECTION_BASE.New}() method. +-- Please watch this [youtube video](https://youtu.be/C7p81dUwP-E) that explains the detection concepts. -- --- 1.2) DETECTION_BASE initialization --- ---------------------------------- --- By default, detection will return detected objects with all the detection sensors available. --- However, you can ask how the objects were found with specific detection methods. --- If you use one of the below methods, the detection will work with the detection method specified. --- You can specify to apply multiple detection methods. --- --- Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK: --- --- * @{Detection#DETECTION_BASE.InitDetectVisual}(): Detected using Visual. --- * @{Detection#DETECTION_BASE.InitDetectOptical}(): Detected using Optical. --- * @{Detection#DETECTION_BASE.InitDetectRadar}(): Detected using Radar. --- * @{Detection#DETECTION_BASE.InitDetectIRST}(): Detected using IRST. --- * @{Detection#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. --- * @{Detection#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. --- --- 1.3) Obtain objects detected by DETECTION_BASE --- ---------------------------------------------- --- DETECTION_BASE builds @{Set}s of objects detected. These @{Set#SET_BASE}s can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSets}(). --- The method will return a list (table) of @{Set#SET_BASE} objects. --- --- === --- --- 2) @{Detection#DETECTION_AREAS} class, extends @{Detection#DETECTION_BASE} --- =============================================================================== --- The @{Detection#DETECTION_AREAS} class will detect units within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s), --- and will build a list (table) of @{Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. --- The class is group the detected units within zones given a DetectedZoneRange parameter. --- A set with multiple detected zones will be created as there are groups of units detected. --- --- 2.1) Retrieve the Detected Unit sets and Detected Zones --- ------------------------------------------------------- --- The DetectedUnitSets methods are implemented in @{Detection#DECTECTION_BASE} and the DetectedZones methods is implemented in @{Detection#DETECTION_AREAS}. --- --- Retrieve the DetectedUnitSets with the method @{Detection#DETECTION_BASE.GetDetectedSets}(). A table will be return of @{Set#SET_UNIT}s. --- To understand the amount of sets created, use the method @{Detection#DETECTION_BASE.GetDetectedSetCount}(). --- If you want to obtain a specific set from the DetectedSets, use the method @{Detection#DETECTION_BASE.GetDetectedSet}() with a given index. --- --- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Detection#DETECTION_BASE.GetDetectionZones}(). --- To understand the amount of zones created, use the method @{Detection#DETECTION_BASE.GetDetectionZoneCount}(). --- If you want to obtain a specific zone from the DetectedZones, use the method @{Detection#DETECTION_BASE.GetDetectionZone}() with a given index. --- --- 1.4) Flare or Smoke detected units --- ---------------------------------- --- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedUnits}() or @{Detection#DETECTION_AREAS.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place. --- --- 1.5) Flare or Smoke detected zones --- ---------------------------------- --- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedZones}() or @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to flare or smoke the detected zones when a new detection has taken place. --- --- === -- -- ### Contributions: -- --- * Mechanist : Concept & Testing +-- * Mechanist : Early concept of DETECTION_AREAS. -- -- ### Authors: -- --- * FlightControl : Design & Programming +-- * FlightControl : Analysis, Design, Programming, Testing -- -- @module Detection +do -- DETECTION_BASE ---- DETECTION_BASE class --- @type DETECTION_BASE --- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. --- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. --- @field #number DetectionRun --- @extends Core.Base#BASE -DETECTION_BASE = { - ClassName = "DETECTION_BASE", - DetectionSetGroup = nil, - DetectionRange = nil, - DetectedObjects = {}, - DetectionRun = 0, - DetectedObjectsIdentified = {}, -} - ---- @type DETECTION_BASE.DetectedObjects --- @list <#DETECTION_BASE.DetectedObject> - ---- @type DETECTION_BASE.DetectedObject --- @field #string Name --- @field #boolean Visible --- @field #string Type --- @field #number Distance --- @field #boolean Identified - ---- DETECTION constructor. --- @param #DETECTION_BASE self --- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @param Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @return #DETECTION_BASE self -function DETECTION_BASE:New( DetectionSetGroup, DetectionRange ) - - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) + --- # 1) DETECTION_BASE class, extends @{Fsm#FSM} + -- + -- The DETECTION_BASE class defines the core functions to administer detected objects. + -- The DETECTION_BASE class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). + -- + -- ## 1.1) DETECTION_BASE constructor + -- + -- Construct a new DETECTION_BASE instance using the @{#DETECTION_BASE.New}() method. + -- + -- ## 1.2) DETECTION_BASE initialization + -- + -- By default, detection will return detected objects with all the detection sensors available. + -- However, you can ask how the objects were found with specific detection methods. + -- If you use one of the below methods, the detection will work with the detection method specified. + -- You can specify to apply multiple detection methods. + -- + -- Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK: + -- + -- * @{#DETECTION_BASE.InitDetectVisual}(): Detected using Visual. + -- * @{#DETECTION_BASE.InitDetectOptical}(): Detected using Optical. + -- * @{#DETECTION_BASE.InitDetectRadar}(): Detected using Radar. + -- * @{#DETECTION_BASE.InitDetectIRST}(): Detected using IRST. + -- * @{#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. + -- * @{#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. + -- + -- ## 1.3) DETECTION_BASE derived classes group the detected units into a DetectedItems[] list + -- + -- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later + -- of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains + -- a SET_UNIT object that contains the detected units that belong to that group. + -- + -- Derived classes will apply different methods to group the detected units. + -- Examples are per area, per quadrant, per distance, per type. + -- See further the derived DETECTION classes on which grouping methods are currently supported. + -- + -- Various methods exist how to retrieve the grouped items from a DETECTION_BASE derived class: + -- + -- * The method @{Detection#DETECTION_BASE.GetDetectedItems}() retrieves the DetectedItems[] list. + -- * A DetectedItem from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedItem}( DetectedItemIndex ). + -- Note that this method returns a DetectedItem element from the list, that contains a Set variable and further information + -- about the DetectedItem that is set by the DETECTION_BASE derived classes, used to group the DetectedItem. + -- * A DetectedSet from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSet}( DetectedItemIndex ). + -- This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ). + -- + -- ## 1.4) Apply additional Filters to fine-tune the detected objects + -- + -- By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means. + -- That being said, the DCS World detection algorithm can sometimes be unrealistic. + -- Especially for a visual detection, DCS World is able to report within 1 second a detailed detection of a group of 20 units (including types of the units) that are 10 kilometers away, using only visual capabilities. + -- Additionally, trees and other obstacles are not accounted during the DCS World detection. + -- + -- Therefore, an additional (optional) filtering has been built into the DETECTION_BASE class, that can be set for visual detected units. + -- For electronic detection, this filtering is not applied, only for visually detected targets. + -- + -- The following additional filtering can be applied for visual filtering: + -- + -- * A probability factor per kilometer distance. + -- * A probability factor based on the alpha angle between the detected object and the unit detecting. + -- A detection from a higher altitude allows for better detection than when on the ground. + -- * Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult. + -- The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects additing a probability factor per zone. + -- + -- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. + -- Only when you experience unrealistic behaviour in your missions, these filters could be applied. + -- + -- ### 1.4.1 ) Distance visual detection probability + -- + -- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. + -- Also, the speed of accurate detection plays a role. + -- + -- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. + -- + -- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: + -- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. + -- + -- Note that based on this probability factor, not only the detection but also the **type** of the unit will be applied! + -- + -- Use the method @{Detection#DETECTION_BASE.SetDistanceProbability}() to set the probability factor upon a 10 km distance. + -- + -- ### 1.4.2 ) Alpha Angle visual detection probability + -- + -- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. + -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. + -- + -- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. + -- + -- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: + -- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% + -- + -- Use the method @{Detection#DETECTION_BASE.SetAlphaAngleProbability}() to set the probability factor if 0°. + -- + -- ### 1.4.3 ) Cloudy Zones detection probability + -- + -- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. + -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission + -- zones that reflect cloudy areas where detected units may not be so easily visually detected. + -- + -- Use the method @{Detection#DETECTION_BASE.SetZoneProbability}() to set for a defined number of zones, the probability factors. + -- + -- Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take + -- from the DETECTION_BASE to calculate the presence of the detected unit within each zone. + -- Expecially for ZONE_POLYGON, try to limit the amount of nodes of the polygon! + -- + -- Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for + -- AI not to detect so easily targets within a forrest or village rich area. + -- + -- ## 1.5 ) Accept / Reject detected units + -- + -- DETECTION_BASE can accept or reject successful detections based on the location of the detected object, + -- if it is located in range or located inside or outside of specific zones. + -- + -- ### 1.5.1 ) Detection acceptance of within range limit + -- + -- A range can be set that will limit a successful detection for a unit. + -- Use the method @{Detection#DETECTION_BASE.SetAcceptRange}() to apply a range in meters till where detected units will be accepted. + -- + -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. + -- + -- -- Build a detect object. + -- local Detection = DETECTION_BASE:New( SetGroup ) + -- + -- -- This will accept detected units if the range is below 5000 meters. + -- Detection:SetAcceptRange( 5000 ) + -- + -- -- Start the Detection. + -- Detection:Start() + -- + -- + -- ### 1.5.2 ) Detection acceptance if within zone(s). + -- + -- Specific ZONE_BASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONE_BASE object(s). + -- Use the method @{Detection#DETECTION_BASE.SetAcceptZones}() will accept detected units if they are within the specified zones. + -- + -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. + -- + -- -- Search fo the zones where units are to be accepted. + -- local ZoneAccept1 = ZONE:New( "AcceptZone1" ) + -- local ZoneAccept2 = ZONE:New( "AcceptZone2" ) + -- + -- -- Build a detect object. + -- local Detection = DETECTION_BASE:New( SetGroup ) + -- + -- -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2. + -- Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) + -- + -- -- Start the Detection. + -- Detection:Start() + -- + -- ### 1.5.3 ) Detection rejectance if within zone(s). + -- + -- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s). + -- Use the method @{Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones. + -- An example of how to use the method is shown below. + -- + -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. + -- + -- -- Search fo the zones where units are to be rejected. + -- local ZoneReject1 = ZONE:New( "RejectZone1" ) + -- local ZoneReject2 = ZONE:New( "RejectZone2" ) + -- + -- -- Build a detect object. + -- local Detection = DETECTION_BASE:New( SetGroup ) + -- + -- -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2. + -- Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) + -- + -- -- Start the Detection. + -- Detection:Start() + -- + -- ## 1.6) DETECTION_BASE is a Finite State Machine + -- + -- Various Events and State Transitions can be tailored using DETECTION_BASE. + -- + -- ### 1.6.1) DETECTION_BASE States + -- + -- * **Detecting**: The detection is running. + -- * **Stopped**: The detection is stopped. + -- + -- ### 1.6.2) DETECTION_BASE Events + -- + -- * **Start**: Start the detection process. + -- * **Detect**: Detect new units. + -- * **Detected**: New units have been detected. + -- * **Stop**: Stop the detection process. + -- + -- @type DETECTION_BASE + -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. + -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. + -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. + -- @field #number DetectionRun + -- @extends Core.Fsm#FSM + DETECTION_BASE = { + ClassName = "DETECTION_BASE", + DetectionSetGroup = nil, + DetectionRange = nil, + DetectedObjects = {}, + DetectionRun = 0, + DetectedObjectsIdentified = {}, + DetectedItems = {}, + } - self.DetectionSetGroup = DetectionSetGroup - self.DetectionRange = DetectionRange + --- @type DETECTION_BASE.DetectedObjects + -- @list <#DETECTION_BASE.DetectedObject> - self:InitDetectVisual( false ) - self:InitDetectOptical( false ) - self:InitDetectRadar( false ) - self:InitDetectRWR( false ) - self:InitDetectIRST( false ) - self:InitDetectDLINK( false ) + --- @type DETECTION_BASE.DetectedObject + -- @field #string Name + -- @field #boolean Visible + -- @field #string Type + -- @field #number Distance + -- @field #boolean Identified - return self -end - ---- Detect Visual. --- @param #DETECTION_BASE self --- @param #boolean DetectVisual --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectVisual( DetectVisual ) - - self.DetectVisual = DetectVisual -end - ---- Detect Optical. --- @param #DETECTION_BASE self --- @param #boolean DetectOptical --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectOptical( DetectOptical ) - self:F2() - - self.DetectOptical = DetectOptical -end - ---- Detect Radar. --- @param #DETECTION_BASE self --- @param #boolean DetectRadar --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectRadar( DetectRadar ) - self:F2() - - self.DetectRadar = DetectRadar -end - ---- Detect IRST. --- @param #DETECTION_BASE self --- @param #boolean DetectIRST --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectIRST( DetectIRST ) - self:F2() - - self.DetectIRST = DetectIRST -end - ---- Detect RWR. --- @param #DETECTION_BASE self --- @param #boolean DetectRWR --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectRWR( DetectRWR ) - self:F2() - - self.DetectRWR = DetectRWR -end - ---- Detect DLINK. --- @param #DETECTION_BASE self --- @param #boolean DetectDLINK --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectDLINK( DetectDLINK ) - self:F2() - - self.DetectDLINK = DetectDLINK -end - ---- Determines if a detected object has already been identified during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject --- @return #boolean true if already identified. -function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) - self:F3( DetectedObject.Name ) - - local DetectedObjectName = DetectedObject.Name - local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true - self:T3( DetectedObjectIdentified ) - return DetectedObjectIdentified -end - ---- Identifies a detected object during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject -function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) - self:F( DetectedObject.Name ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = true -end - ---- UnIdentify a detected object during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject -function DETECTION_BASE:UnIdentifyDetectedObject( DetectedObject ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = false -end - ---- UnIdentify all detected objects during detection processing. --- @param #DETECTION_BASE self -function DETECTION_BASE:UnIdentifyAllDetectedObjects() - - self.DetectedObjectsIdentified = {} -- Table will be garbage collected. -end - ---- Gets a detected object with a given name. --- @param #DETECTION_BASE self --- @param #string ObjectName --- @return #DETECTION_BASE.DetectedObject -function DETECTION_BASE:GetDetectedObject( ObjectName ) - self:F3( ObjectName ) + --- @type DETECTION_BASE.DetectedItems + -- @list <#DETECTION_BASE.DetectedItem> - if ObjectName then - local DetectedObject = self.DetectedObjects[ObjectName] + --- @type DETECTION_BASE.DetectedItem + -- @field Core.Set#SET_UNIT Set + -- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. + -- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. + -- @field #boolean Changed Documents if the detected area has changes. + -- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). + -- @field #number ItemID -- The identifier of the detected area. + -- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. + -- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - -- Only return detected objects that are alive! - local DetectedUnit = UNIT:FindByName( ObjectName ) - if DetectedUnit and DetectedUnit:IsAlive() then - if self:IsDetectedObjectIdentified( DetectedObject ) == false then - return DetectedObject + + --- DETECTION constructor. + -- @param #DETECTION_BASE self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @return #DETECTION_BASE self + function DETECTION_BASE:New( DetectionSetGroup ) + + -- Inherits from BASE + local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_BASE + + self.DetectedItemCount = 0 + self.DetectedItemMax = 0 + self.DetectedItems = {} + + self.DetectionSetGroup = DetectionSetGroup + + self.DetectionInterval = 30 + + self:InitDetectVisual( true ) + self:InitDetectOptical( false ) + self:InitDetectRadar( false ) + self:InitDetectRWR( false ) + self:InitDetectIRST( false ) + self:InitDetectDLINK( false ) + + -- Create FSM transitions. + + self:SetStartState( "Stopped" ) + self.CountryID = DetectionSetGroup:GetFirst():GetCountry() + + self:AddTransition( "Stopped", "Start", "Detecting") + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnLeaveStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnEnterStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Start. + -- @function [parent=#DETECTION_BASE] OnBeforeStart + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Start. + -- @function [parent=#DETECTION_BASE] OnAfterStart + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Start. + -- @function [parent=#DETECTION_BASE] Start + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Start. + -- @function [parent=#DETECTION_BASE] __Start + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Detecting. + -- @function [parent=#DETECTION_BASE] OnLeaveDetecting + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Detecting. + -- @function [parent=#DETECTION_BASE] OnEnterDetecting + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + self:AddTransition( "Detecting", "Detect", "Detecting" ) + self:AddTransition( "Detecting", "DetectionGroup", "Detecting" ) + + --- OnBefore Transition Handler for Event Detect. + -- @function [parent=#DETECTION_BASE] OnBeforeDetect + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Detect. + -- @function [parent=#DETECTION_BASE] OnAfterDetect + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Detect. + -- @function [parent=#DETECTION_BASE] Detect + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Detect. + -- @function [parent=#DETECTION_BASE] __Detect + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "Detecting", "Detected", "Detecting" ) + + --- OnBefore Transition Handler for Event Detected. + -- @function [parent=#DETECTION_BASE] OnBeforeDetected + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Detected. + -- @function [parent=#DETECTION_BASE] OnAfterDetected + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Detected. + -- @function [parent=#DETECTION_BASE] Detected + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Detected. + -- @function [parent=#DETECTION_BASE] __Detected + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "*", "Stop", "Stopped" ) + + --- OnBefore Transition Handler for Event Stop. + -- @function [parent=#DETECTION_BASE] OnBeforeStop + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Stop. + -- @function [parent=#DETECTION_BASE] OnAfterStop + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Stop. + -- @function [parent=#DETECTION_BASE] Stop + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Stop. + -- @function [parent=#DETECTION_BASE] __Stop + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnLeaveStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnEnterStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + return self + end + + do -- State Transition Handling + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function DETECTION_BASE:onafterStart(From,Event,To) + self:__Detect(0.1) + end + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function DETECTION_BASE:onafterDetect(From,Event,To) + self:E( {From,Event,To}) + + local DetectDelay = 0.1 + self.DetectionCount = 0 + self.DetectionRun = 0 + self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table + + self.DetectionSetGroup:Flush() + + for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do + self:E( {DetectionGroupData}) + self:__DetectionGroup( DetectDelay, DetectionGroupData ) -- Process each detection asynchronously. + self.DetectionCount = self.DetectionCount + 1 + DetectDelay = DetectDelay + 0.1 end end + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. + function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup ) + self:E( {From,Event,To}) + + self.DetectionRun = self.DetectionRun + 1 + + local HasDetectedObjects = false + + if DetectionGroup:IsAlive() then + + self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) + + local DetectionGroupName = DetectionGroup:GetName() + + local DetectedUnits = {} + + local DetectedTargets = DetectionGroup:GetDetectedTargets( + self.DetectVisual, + self.DetectOptical, + self.DetectRadar, + self.DetectIRST, + self.DetectRWR, + self.DetectDLINK + ) + + self:T( DetectedTargets ) + + for DetectionObjectID, Detection in pairs( DetectedTargets ) do + local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object + self:T2( DetectedObject ) + + if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then + + local DetectionAccepted = true + + local DetectedObjectName = DetectedObject:getName() + + local DetectedObjectVec3 = DetectedObject:getPoint() + local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z } + local DetectionGroupVec3 = DetectionGroup:GetVec3() + local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z } + + local Distance = ( ( DetectedObjectVec3.x - DetectionGroupVec3.x )^2 + + ( DetectedObjectVec3.y - DetectionGroupVec3.y )^2 + + ( DetectedObjectVec3.z - DetectionGroupVec3.z )^2 + ) ^ 0.5 / 1000 + + self:T( { "Detected Target", DetectionGroupName, DetectedObjectName, Distance } ) + + -- Calculate Acceptance + + if self.AcceptRange and Distance > self.AcceptRange then + DetectionAccepted = false + end + + if self.AcceptZones then + for AcceptZoneID, AcceptZone in pairs( self.AcceptZones ) do + local AcceptZone = AcceptZone -- Core.Zone#ZONE_BASE + if AcceptZone:IsPointVec2InZone( DetectedObjectVec2 ) == false then + DetectionAccepted = false + end + end + end + + if self.RejectZones then + for RejectZoneID, RejectZone in pairs( self.RejectZones ) do + local RejectZone = RejectZone -- Core.Zone#ZONE_BASE + if RejectZone:IsPointVec2InZone( DetectedObjectVec2 ) == true then + DetectionAccepted = false + end + end + end + + -- Calculate additional probabilities + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.DistanceProbability then + local DistanceFactor = Distance / 4 + local DistanceProbabilityReversed = ( 1 - self.DistanceProbability ) * DistanceFactor + local DistanceProbability = 1 - DistanceProbabilityReversed + DistanceProbability = DistanceProbability * 30 / 300 + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, DistanceProbability } ) + if Probability > DistanceProbability then + DetectionAccepted = false + end + end + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.AlphaAngleProbability then + local NormalVec2 = { x = DetectedObjectVec2.x - DetectionGroupVec2.x, y = DetectedObjectVec2.y - DetectionGroupVec2.y } + local AlphaAngle = math.atan2( NormalVec2.y, NormalVec2.x ) + local Sinus = math.sin( AlphaAngle ) + local AlphaAngleProbabilityReversed = ( 1 - self.AlphaAngleProbability ) * ( 1 - Sinus ) + local AlphaAngleProbability = 1 - AlphaAngleProbabilityReversed + + AlphaAngleProbability = AlphaAngleProbability * 30 / 300 + + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, AlphaAngleProbability } ) + if Probability > AlphaAngleProbability then + DetectionAccepted = false + end + + end + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.ZoneProbability then + + for ZoneDataID, ZoneData in pairs( self.ZoneProbability ) do + self:E({ZoneData}) + local ZoneObject = ZoneData[1] -- Core.Zone#ZONE_BASE + local ZoneProbability = ZoneData[2] -- #number + ZoneProbability = ZoneProbability * 30 / 300 + + if ZoneObject:IsPointVec2InZone( DetectedObjectVec2 ) == true then + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, ZoneProbability } ) + if Probability > ZoneProbability then + DetectionAccepted = false + break + end + end + end + end + + if DetectionAccepted then + + HasDetectedObjects = true + + if not self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = {} + end + self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName + self.DetectedObjects[DetectedObjectName].Visible = Detection.visible + self.DetectedObjects[DetectedObjectName].Type = Detection.type + self.DetectedObjects[DetectedObjectName].Distance = Distance + + local DetectedUnit = UNIT:FindByName( DetectedObjectName ) + + DetectedUnits[DetectedObjectName] = DetectedUnit + else + -- if beyond the DetectionRange then nullify... + if self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = nil + end + end + end + + self:T2( self.DetectedObjects ) + end + + if HasDetectedObjects then + self:__Detected( 0.1, DetectedUnits ) + end + + end + + if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then + self:__Detect( self.DetectionInterval ) + + self:T( "--> Create Detection Sets" ) + self:CreateDetectionSets() + end + + end + + + end + + do -- Initialization methods + + --- Detect Visual. + -- @param #DETECTION_BASE self + -- @param #boolean DetectVisual + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectVisual( DetectVisual ) + + self.DetectVisual = DetectVisual + end + + --- Detect Optical. + -- @param #DETECTION_BASE self + -- @param #boolean DetectOptical + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectOptical( DetectOptical ) + self:F2() + + self.DetectOptical = DetectOptical + end + + --- Detect Radar. + -- @param #DETECTION_BASE self + -- @param #boolean DetectRadar + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectRadar( DetectRadar ) + self:F2() + + self.DetectRadar = DetectRadar + end + + --- Detect IRST. + -- @param #DETECTION_BASE self + -- @param #boolean DetectIRST + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectIRST( DetectIRST ) + self:F2() + + self.DetectIRST = DetectIRST + end + + --- Detect RWR. + -- @param #DETECTION_BASE self + -- @param #boolean DetectRWR + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectRWR( DetectRWR ) + self:F2() + + self.DetectRWR = DetectRWR + end + + --- Detect DLINK. + -- @param #DETECTION_BASE self + -- @param #boolean DetectDLINK + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectDLINK( DetectDLINK ) + self:F2() + + self.DetectDLINK = DetectDLINK + end + + end + + do + + --- Set the detection interval time in seconds. + -- @param #DETECTION_BASE self + -- @param #number DetectionInterval Interval in seconds. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetDetectionInterval( DetectionInterval ) + self:F2() + + self.DetectionInterval = DetectionInterval + + return self + end + + end + + do -- Accept / Reject detected units + + --- Accept detections if within a range in meters. + -- @param #DETECTION_BASE self + -- @param #number AcceptRange Accept a detection if the unit is within the AcceptRange in meters. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAcceptRange( AcceptRange ) + self:F2() + + self.AcceptRange = AcceptRange + + return self + end + + --- Accept detections if within the specified zone(s). + -- @param #DETECTION_BASE self + -- @param AcceptZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAcceptZones( AcceptZones ) + self:F2() + + if type( AcceptZones ) == "table" then + self.AcceptZones = AcceptZones + else + self.AcceptZones = { AcceptZones } + end + + return self + end + + --- Reject detections if within the specified zone(s). + -- @param #DETECTION_BASE self + -- @param RejectZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetRejectZones( RejectZones ) + self:F2() + + if type( RejectZones ) == "table" then + self.RejectZones = RejectZones + else + self.RejectZones = { RejectZones } + end + + return self + end + + end + + do -- Probability methods + + --- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. + -- Also, the speed of accurate detection plays a role. + -- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. + -- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: + -- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. + -- @param #DETECTION_BASE self + -- @param DistanceProbability The probability factor. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetDistanceProbability( DistanceProbability ) + self:F2() + + self.DistanceProbability = DistanceProbability + + return self + end + + + --- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. + -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. + -- + -- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. + -- + -- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: + -- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% + -- @param #DETECTION_BASE self + -- @param AlphaAngleProbability The probability factor. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAlphaAngleProbability( AlphaAngleProbability ) + self:F2() + + self.AlphaAngleProbability = AlphaAngleProbability + + return self + end + + --- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. + -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission + -- zones that reflect cloudy areas where detected units may not be so easily visually detected. + -- @param #DETECTION_BASE self + -- @param ZoneArray Aray of a The ZONE_BASE object and a ZoneProbability pair.. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetZoneProbability( ZoneArray ) + self:F2() + + self.ZoneProbability = ZoneArray + + return self + end + + + end + + do -- Change processing + + --- Accepts changes from the detected item. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #DETECTION_BASE self + function DETECTION_BASE:AcceptChanges( DetectedItem ) + + DetectedItem.Changed = false + DetectedItem.Changes = {} + + return self + end + + --- Add a change to the detected zone. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @param #string ChangeCode + -- @return #DETECTION_BASE self + function DETECTION_BASE:AddChangeItem( DetectedItem, ChangeCode, ItemUnitType ) + + DetectedItem.Changed = true + local ItemID = DetectedItem.ItemID + + DetectedItem.Changes = DetectedItem.Changes or {} + DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} + DetectedItem.Changes[ChangeCode].ItemID = ItemID + DetectedItem.Changes[ChangeCode].ItemUnitType = ItemUnitType + + self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ItemUnitType } ) + + return self + end + + + --- Add a change to the detected zone. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @param #string ChangeCode + -- @param #string ChangeUnitType + -- @return #DETECTION_BASE self + function DETECTION_BASE:AddChangeUnit( DetectedItem, ChangeCode, ChangeUnitType ) + + DetectedItem.Changed = true + local ItemID = DetectedItem.ItemID + + DetectedItem.Changes = DetectedItem.Changes or {} + DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} + DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] or 0 + DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] + 1 + DetectedItem.Changes[ChangeCode].ItemID = ItemID + + self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ChangeUnitType } ) + + return self + end + + + end + + do -- Threat + + --- Returns if there are friendlies nearby the FAC units ... + -- @param #DETECTION_BASE self + -- @return #boolean trhe if there are friendlies nearby + function DETECTION_BASE:IsFriendliesNearBy( DetectedItem ) + + self:T3( DetectedItem.FriendliesNearBy ) + return DetectedItem.FriendliesNearBy or false + end + + --- Background worker function to determine if there are friendlies nearby ... + -- @param #DETECTION_BASE self + function DETECTION_BASE:ReportFriendliesNearBy( ReportGroupData ) + self:F2() + + local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = ReportGroupData.DetectedItem.Set + local DetectedUnit = DetectedSet:GetFirst() + + DetectedItem.FriendliesNearBy = false + + if DetectedUnit then + + + local SphereSearch = { + id = world.VolumeType.SPHERE, + params = { + point = DetectedUnit:GetVec3(), + radius = 6000, + } + + } + + --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit + -- @param Wrapper.Group#GROUP ReportGroup + -- @param Set#SET_GROUP ReportSetGroup + local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) + + local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = ReportGroupData.DetectedItem.Set + local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT + local ReportSetGroup = ReportGroupData.ReportSetGroup + + local EnemyCoalition = DetectedUnit:GetCoalition() + + local FoundUnitCoalition = FoundDCSUnit:getCoalition() + local FoundUnitName = FoundDCSUnit:getName() + local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() + local EnemyUnitName = DetectedUnit:GetName() + local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil + + self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) + + if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then + DetectedItem.FriendliesNearBy = true + return false + end + + return true + end + + world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) + end + end + + end + + --- Determines if a detected object has already been identified during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + -- @return #boolean true if already identified. + function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) + self:F3( DetectedObject.Name ) + + local DetectedObjectName = DetectedObject.Name + local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true + self:T3( DetectedObjectIdentified ) + return DetectedObjectIdentified + end + + --- Identifies a detected object during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) + self:F( { "Identified:", DetectedObject.Name } ) + + local DetectedObjectName = DetectedObject.Name + self.DetectedObjectsIdentified[DetectedObjectName] = true + end + + --- UnIdentify a detected object during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + function DETECTION_BASE:UnIdentifyDetectedObject( DetectedObject ) + + local DetectedObjectName = DetectedObject.Name + self.DetectedObjectsIdentified[DetectedObjectName] = false + end + + --- UnIdentify all detected objects during detection processing. + -- @param #DETECTION_BASE self + function DETECTION_BASE:UnIdentifyAllDetectedObjects() + + self.DetectedObjectsIdentified = {} -- Table will be garbage collected. + end + + --- Gets a detected object with a given name. + -- @param #DETECTION_BASE self + -- @param #string ObjectName + -- @return #DETECTION_BASE.DetectedObject + function DETECTION_BASE:GetDetectedObject( ObjectName ) + self:F( ObjectName ) + + if ObjectName then + local DetectedObject = self.DetectedObjects[ObjectName] + + -- Only return detected objects that are alive! + local DetectedUnit = UNIT:FindByName( ObjectName ) + if DetectedUnit and DetectedUnit:IsAlive() then + if self:IsDetectedObjectIdentified( DetectedObject ) == false then + return DetectedObject + end + end + end + + return nil + end + + + --- Adds a new DetectedItem to the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #string DetectedItemIndex The index of the DetectedItem. + -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:AddDetectedItem( DetectedItemIndex, Set ) + + local DetectedItem = {} + self.DetectedItemCount = self.DetectedItemCount + 1 + self.DetectedItemMax = self.DetectedItemMax + 1 + + if DetectedItemIndex then + self.DetectedItems[DetectedItemIndex] = DetectedItem + else + self.DetectedItems[self.DetectedItemCount] = DetectedItem + end + + DetectedItem.Set = Set or SET_UNIT:New() + DetectedItem.ItemID = self.DetectedItemMax + DetectedItem.Removed = false + + return DetectedItem + end + + --- Adds a new DetectedItem to the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #string DetectedItemIndex The index of the DetectedItem. + -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. + -- @param Core.Zone#ZONE_UNIT Zone (optional) The Zone to be added where the Units are located. + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:AddDetectedItemZone( DetectedItemIndex, Set, Zone ) + + local DetectedItem = self:AddDetectedItem( DetectedItemIndex, Set ) + + DetectedItem.Zone = Zone + + return DetectedItem + end + + --- Removes an existing DetectedItem from the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #number DetectedItemIndex The index or position in the DetectedItems list where the item needs to be removed. + function DETECTION_BASE:RemoveDetectedItem( DetectedItemIndex ) + + self.DetectedItemCount = self.DetectedItemCount - 1 + self.DetectedItems[DetectedItemIndex] = nil + end + + + --- Get the detected @{Set#SET_BASE}s. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE.DetectedItems + function DETECTION_BASE:GetDetectedItems() + + return self.DetectedItems + end + + --- Get the amount of SETs with detected objects. + -- @param #DETECTION_BASE self + -- @return #number Count + function DETECTION_BASE:GetDetectedItemsCount() + + local DetectedCount = self.DetectedItemCount + return DetectedCount + end + + --- Get a detected item using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:GetDetectedItem( Index ) + + local DetectedItem = self.DetectedItems[Index] + if DetectedItem then + return DetectedItem + end + + return nil + end + + --- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return Core.Set#SET_UNIT DetectedSet + function DETECTION_BASE:GetDetectedSet( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + local DetectedSetUnit = DetectedItem.Set + if DetectedSetUnit then + return DetectedSetUnit + end + + return nil + end + + do -- Zones + + --- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return Core.Zone#ZONE_UNIT DetectedZone + function DETECTION_BASE:GetDetectedZone( Index ) + + local DetectedZone = self.DetectedItems[Index].Zone + if DetectedZone then + return DetectedZone + end + + return nil + end + + end + + + --- Report summary of a detected item using a given numeric index. + -- @param #DETECTION_BASE self + -- @param Index + -- @return #string + function DETECTION_BASE:DetectedItemReportSummary( Index ) + self:F( Index ) + return nil + end + + --- Report detailed of a detectedion result. + -- @param #DETECTION_BASE self + -- @return #string + function DETECTION_BASE:DetectedReportDetailed() + self:F() + return nil + end + + --- Get the detection Groups. + -- @param #DETECTION_BASE self + -- @return Wrapper.Group#GROUP + function DETECTION_BASE:GetDetectionSetGroup() + + local DetectionSetGroup = self.DetectionSetGroup + return DetectionSetGroup + end + + --- Make a DetectionSet table. This function will be overridden in the derived clsses. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE self + function DETECTION_BASE:CreateDetectionSets() + self:F2() + + self:E( "Error, in DETECTION_BASE class..." ) + + end + + + --- Schedule the DETECTION construction. + -- @param #DETECTION_BASE self + -- @param #number DelayTime The delay in seconds to wait the reporting. + -- @param #number RepeatInterval The repeat interval in seconds for the reporting to happen repeatedly. + -- @return #DETECTION_BASE self + function DETECTION_BASE:Schedule( DelayTime, RepeatInterval ) + self:F2() + + self.ScheduleDelayTime = DelayTime + self.ScheduleRepeatInterval = RepeatInterval + + self.DetectionScheduler = SCHEDULER:New( self, self._DetectionScheduler, { self, "Detection" }, DelayTime, RepeatInterval ) + return self + end + +end + +do -- DETECTION_UNITS + + --- # 2) DETECTION_UNITS class, extends @{Detection#DETECTION_BASE} + -- + -- The DETECTION_UNITS class will detect units within the battle zone. + -- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. + -- Beware that when the amount of units detected is large, the DetectedItems list will be large also. + -- + -- @type DETECTION_UNITS + -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are detected. + -- @extends #DETECTION_BASE + DETECTION_UNITS = { + ClassName = "DETECTION_UNITS", + DetectionRange = nil, + } + + --- DETECTION_UNITS constructor. + -- @param Functional.Detection#DETECTION_UNITS self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @return Functional.Detection#DETECTION_UNITS self + function DETECTION_UNITS:New( DetectionSetGroup ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_UNITS + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self + end + + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_UNITS self + -- @param #DETECTION_UNITS.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_UNITS:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do + + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." + end + + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." + end + + end + + return table.concat( MT, "\n" ) + + end + + + --- Create the DetectedItems list from the DetectedObjects table. + -- For each DetectedItem, a one field array is created containing the Unit detected. + -- @param #DETECTION_UNITS self + -- @return #DETECTION_UNITS self + function DETECTION_UNITS:CreateDetectionSets() + self:F2( #self.DetectedObjects ) + + -- Loop the current detected items, and check if each object still exists and is detected. + + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + + local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedTypeName = DetectedItem.Type + + for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + + local DetectedObject = nil + self:E( DetectedUnit ) + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Yes, the DetectedUnit is still detected or exists. Flag as identified. + self:IdentifyDetectedObject( DetectedObject ) + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) + DetectedItemSet:Remove( DetectedUnitName ) + end + end + end + + + -- Now we need to loop through the unidentified detected units and add these... These are all new items. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do + + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + if DetectedObject then + self:T( { "Detected Unit #", DetectedUnitName } ) + + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + if DetectedUnit then + local DetectedTypeName = DetectedUnit:GetTypeName() + local DetectedItem = self:GetDetectedItem( DetectedUnitName ) + if not DetectedItem then + self:T( "Added new DetectedItem" ) + DetectedItem = self:AddDetectedItem( DetectedUnitName ) + DetectedItem.Type = DetectedUnit:GetTypeName() + DetectedItem.Name = DetectedObjectData.Name + DetectedItem.Visible = DetectedObjectData.Visible + DetectedItem.Distance = DetectedObjectData.Distance + end + + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) + end + end + end + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + --self:NearestFAC( DetectedItem ) + end + + end + + --- Report summary of a DetectedItem using a given numeric index. + -- @param #DETECTION_UNITS self + -- @param Index + -- @return #string + function DETECTION_UNITS:DetectedItemReportSummary( Index ) + self:F( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + local DetectedSet = self:GetDetectedSet( Index ) + + self:T( DetectedSet ) + if DetectedSet then + local ReportSummary = "" + local UnitDistanceText = "" + local UnitCategoryText = "" + + local DetectedItemUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT + + if DetectedItemUnit and DetectedItemUnit:IsAlive() then + self:T(DetectedItemUnit) + + local UnitCategoryName = DetectedItemUnit:GetCategoryName() or "" + local UnitCategoryType = DetectedItemUnit:GetTypeName() or "" + + if DetectedItem.Type and UnitCategoryName and UnitCategoryType then + UnitCategoryText = UnitCategoryName .. " (" .. UnitCategoryType .. ") at " + else + UnitCategoryText = "Unknown target at " + end + + if DetectedItem.Visible == false then + UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " estimated km" + else + UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " km, visual contact" + end + + local DetectedItemPointVec3 = DetectedItemUnit:GetPointVec3() + local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) + + local ThreatLevelA2G = DetectedItemUnit:GetThreatLevel( DetectedItem ) + + ReportSummary = string.format( + "%s - Threat [%s] (%2d) - %s%s", + DetectedItemPointLL, + string.rep( "â– ", ThreatLevelA2G ), + ThreatLevelA2G, + UnitCategoryText, + UnitDistanceText + ) + end + + self:T( ReportSummary ) + + return ReportSummary + end end - return nil + --- Report detailed of a detection result. + -- @param #DETECTION_UNITS self + -- @return #string + function DETECTION_UNITS:DetectedReportDetailed() + self:F() + + local Report = REPORT:New( "Detected units:" ) + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItemID ) + Report:Add( ReportSummary ) + end + + local ReportText = Report:Text() + + return ReportText + end + end ---- Get the detected @{Set#SET_BASE}s. --- @param #DETECTION_BASE self --- @return #DETECTION_BASE.DetectedSets DetectedSets -function DETECTION_BASE:GetDetectedSets() +do -- DETECTION_TYPES - local DetectionSets = self.DetectedSets - return DetectionSets -end - ---- Get the amount of SETs with detected objects. --- @param #DETECTION_BASE self --- @return #number Count -function DETECTION_BASE:GetDetectedSetCount() - - local DetectionSetCount = #self.DetectedSets - return DetectionSetCount -end - ---- Get a SET of detected objects using a given numeric index. --- @param #DETECTION_BASE self --- @param #number Index --- @return Core.Set#SET_BASE -function DETECTION_BASE:GetDetectedSet( Index ) - - local DetectionSet = self.DetectedSets[Index] - if DetectionSet then - return DetectionSet + --- # 3) DETECTION_TYPES class, extends @{Detection#DETECTION_BASE} + -- + -- The DETECTION_TYPES class will detect units within the battle zone. + -- It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. + -- Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. + -- Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also. + -- + -- @type DETECTION_TYPES + -- @extends #DETECTION_BASE + DETECTION_TYPES = { + ClassName = "DETECTION_TYPES", + DetectionRange = nil, + } + + --- DETECTION_TYPES constructor. + -- @param Functional.Detection#DETECTION_TYPES self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Recce role. + -- @return Functional.Detection#DETECTION_TYPES self + function DETECTION_TYPES:New( DetectionSetGroup ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_TYPES + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self end - return nil -end - ---- Get the detection Groups. --- @param #DETECTION_BASE self --- @return Wrapper.Group#GROUP -function DETECTION_BASE:GetDetectionSetGroup() - - local DetectionSetGroup = self.DetectionSetGroup - return DetectionSetGroup -end - ---- Make a DetectionSet table. This function will be overridden in the derived clsses. --- @param #DETECTION_BASE self --- @return #DETECTION_BASE self -function DETECTION_BASE:CreateDetectionSets() - self:F2() - - self:E( "Error, in DETECTION_BASE class..." ) - -end - - ---- Schedule the DETECTION construction. --- @param #DETECTION_BASE self --- @param #number DelayTime The delay in seconds to wait the reporting. --- @param #number RepeatInterval The repeat interval in seconds for the reporting to happen repeatedly. --- @return #DETECTION_BASE self -function DETECTION_BASE:Schedule( DelayTime, RepeatInterval ) - self:F2() - - self.ScheduleDelayTime = DelayTime - self.ScheduleRepeatInterval = RepeatInterval + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_TYPES self + -- @param #DETECTION_TYPES.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_TYPES:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do - self.DetectionScheduler = SCHEDULER:New( self, self._DetectionScheduler, { self, "Detection" }, DelayTime, RepeatInterval ) - return self -end - - ---- Form @{Set}s of detected @{Unit#UNIT}s in an array of @{Set#SET_BASE}s. --- @param #DETECTION_BASE self -function DETECTION_BASE:_DetectionScheduler( SchedulerName ) - self:F2( { SchedulerName } ) + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." + end - self.DetectionRun = self.DetectionRun + 1 - - self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table - - for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - local DetectionGroup = DetectionGroupData -- Wrapper.Group#GROUP - - if DetectionGroup:IsAlive() then - - local DetectionGroupName = DetectionGroup:GetName() + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." + end - local DetectionDetectedTargets = DetectionGroup:GetDetectedTargets( - self.DetectVisual, - self.DetectOptical, - self.DetectRadar, - self.DetectIRST, - self.DetectRWR, - self.DetectDLINK + end + + return table.concat( MT, "\n" ) + + end + + + --- Create the DetectedItems list from the DetectedObjects table. + -- For each DetectedItem, a one field array is created containing the Unit detected. + -- @param #DETECTION_TYPES self + -- @return #DETECTION_TYPES self + function DETECTION_TYPES:CreateDetectionSets() + self:F2( #self.DetectedObjects ) + + -- Loop the current detected items, and check if each object still exists and is detected. + + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + + local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedTypeName = DetectedItem.Type + + for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + + local DetectedObject = nil + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Yes, the DetectedUnit is still detected or exists. Flag as identified. + self:IdentifyDetectedObject( DetectedObject ) + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) + DetectedItemSet:Remove( DetectedUnitName ) + end + end + end + + + -- Now we need to loop through the unidentified detected units and add these... These are all new items. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do + + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + if DetectedObject then + self:T( { "Detected Unit #", DetectedUnitName } ) + + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + if DetectedUnit then + local DetectedTypeName = DetectedUnit:GetTypeName() + local DetectedItem = self:GetDetectedItem( DetectedTypeName ) + if not DetectedItem then + DetectedItem = self:AddDetectedItem( DetectedTypeName ) + DetectedItem.Type = DetectedUnit:GetTypeName() + end + + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) + end + end + end + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + --self:NearestFAC( DetectedItem ) + end + + end + + --- Report summary of a DetectedItem using a given numeric index. + -- @param #DETECTION_TYPES self + -- @param Index + -- @return #string + function DETECTION_TYPES:DetectedItemReportSummary( DetectedTypeName ) + self:F( DetectedTypeName ) + + local DetectedItem = self:GetDetectedItem( DetectedTypeName ) + local DetectedSet = self:GetDetectedSet( DetectedTypeName ) + + self:T( DetectedItem ) + if DetectedItem then + + local ThreatLevelA2G = DetectedSet:CalculateThreatLevelA2G() + local DetectedItemsCount = DetectedSet:Count() + local DetectedItemType = DetectedItem.Type + + local ReportSummary = string.format( + "Threat [%s] (%2d) - %2d of %s", + string.rep( "â– ", ThreatLevelA2G ), + ThreatLevelA2G, + DetectedItemsCount, + DetectedItemType + ) + self:T( ReportSummary ) + + return ReportSummary + end + end + + --- Report detailed of a detection result. + -- @param #DETECTION_TYPES self + -- @return #string + function DETECTION_TYPES:DetectedReportDetailed() + self:F() + + local Report = REPORT:New( "Detected types:" ) + for DetectedItemTypeName, DetectedItem in pairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItemTypeName ) + Report:Add( ReportSummary ) + end + + local ReportText = Report:Text() + + return ReportText + end + +end + + +do -- DETECTION_AREAS + + --- # 4) DETECTION_AREAS class, extends @{Detection#DETECTION_BASE} + -- + -- The DETECTION_AREAS class will detect units within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s), + -- and will build a list (table) of @{Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. + -- The class is group the detected units within zones given a DetectedZoneRange parameter. + -- A set with multiple detected zones will be created as there are groups of units detected. + -- + -- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones + -- + -- The methods to manage the DetectedItems[].Set(s) are implemented in @{Detection#DECTECTION_BASE} and + -- the methods to manage the DetectedItems[].Zone(s) is implemented in @{Detection#DETECTION_AREAS}. + -- + -- Retrieve the DetectedItems[].Set with the method @{Detection#DETECTION_BASE.GetDetectedSet}(). A @{Set#SET_UNIT} object will be returned. + -- + -- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Detection#DETECTION_BASE.GetDetectionZones}(). + -- To understand the amount of zones created, use the method @{Detection#DETECTION_BASE.GetDetectionZoneCount}(). + -- If you want to obtain a specific zone from the DetectedZones, use the method @{Detection#DETECTION_BASE.GetDetectionZone}() with a given index. + -- + -- ## 4.4) Flare or Smoke detected units + -- + -- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedUnits}() or @{Detection#DETECTION_AREAS.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place. + -- + -- ## 4.5) Flare or Smoke or Bound detected zones + -- + -- Use the methods: + -- + -- * @{Detection#DETECTION_AREAS.FlareDetectedZones}() to flare in a color + -- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to smoke in a color + -- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to bound with a tire with a white flag + -- + -- the detected zones when a new detection has taken place. + -- + -- @type DETECTION_AREAS + -- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. + -- @extends #DETECTION_BASE + DETECTION_AREAS = { + ClassName = "DETECTION_AREAS", + DetectionZoneRange = nil, + } + + + --- DETECTION_AREAS constructor. + -- @param #DETECTION_AREAS self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @return #DETECTION_AREAS + function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) + + self.DetectionZoneRange = DetectionZoneRange + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self + end + + --- Report summary of a detected item using a given numeric index. + -- @param #DETECTION_AREAS self + -- @param Index + -- @return #string + function DETECTION_AREAS:DetectedItemReportSummary( Index ) + self:F( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + if DetectedItem then + local DetectedSet = self:GetDetectedSet( Index ) + local ReportSummaryItem + + local DetectedZone = self:GetDetectedZone( Index ) + local DetectedItemPointVec3 = DetectedZone:GetPointVec3() + local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) + + local ThreatLevelA2G = self:GetTreatLevelA2G( DetectedItem ) + local DetectedItemsCount = DetectedSet:Count() + local DetectedItemsTypes = DetectedSet:GetTypeNames() + + local ReportSummary = string.format( + "%s - Threat [%s] (%2d) - %2d of %s", + DetectedItemPointLL, + string.rep( "â– ", ThreatLevelA2G ), + ThreatLevelA2G, + DetectedItemsCount, + DetectedItemsTypes ) - for DetectionDetectedTargetID, DetectionDetectedTarget in pairs( DetectionDetectedTargets ) do - local DetectionObject = DetectionDetectedTarget.object -- Dcs.DCSWrapper.Object#Object - self:T2( DetectionObject ) + return ReportSummary + end + + return nil + end + + + --- Returns if there are friendlies nearby the FAC units ... + -- @param #DETECTION_AREAS self + -- @return #boolean trhe if there are friendlies nearby + function DETECTION_AREAS:IsFriendliesNearBy( DetectedItem ) + + self:T3( DetectedItem.FriendliesNearBy ) + return DetectedItem.FriendliesNearBy or false + end + + --- Calculate the maxium A2G threat level of the DetectedItem. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedItem ) + + local MaxThreatLevelA2G = 0 + for UnitName, UnitData in pairs( DetectedItem.Set:GetSet() ) do + local ThreatUnit = UnitData -- Wrapper.Unit#UNIT + local ThreatLevelA2G = ThreatUnit:GetThreatLevel() + if ThreatLevelA2G > MaxThreatLevelA2G then + MaxThreatLevelA2G = ThreatLevelA2G + end + end + + self:T3( MaxThreatLevelA2G ) + DetectedItem.MaxThreatLevelA2G = MaxThreatLevelA2G + + end + + --- Find the nearest FAC of the DetectedItem. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return Wrapper.Unit#UNIT The nearest FAC unit + function DETECTION_AREAS:NearestFAC( DetectedItem ) + + local NearestFAC = nil + local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) + + for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do + for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do + local FACUnit = FACUnitData -- Wrapper.Unit#UNIT + if FACUnit:IsActive() then + local Vec3 = FACUnit:GetVec3() + local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) + local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) + if Distance < MinDistance then + MinDistance = Distance + NearestFAC = FACUnit + end + end + end + end + + DetectedItem.NearestFAC = NearestFAC + + end + + --- Returns the A2G threat level of the units in the DetectedItem + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #number a scale from 0 to 10. + function DETECTION_AREAS:GetTreatLevelA2G( DetectedItem ) + + self:T3( DetectedItem.MaxThreatLevelA2G ) + return DetectedItem.MaxThreatLevelA2G + end + + + + --- Smoke the detected units + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:SmokeDetectedUnits() + self:F2() + + self._SmokeDetectedUnits = true + return self + end + + --- Flare the detected units + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:FlareDetectedUnits() + self:F2() + + self._FlareDetectedUnits = true + return self + end + + --- Smoke the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:SmokeDetectedZones() + self:F2() + + self._SmokeDetectedZones = true + return self + end + + --- Flare the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:FlareDetectedZones() + self:F2() + + self._FlareDetectedZones = true + return self + end + + --- Bound the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:BoundDetectedZones() + self:F2() + + self._BoundDetectedZones = true + return self + end + + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_AREAS:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do + + if ChangeCode == "AA" then + MT[#MT+1] = "Detected new area " .. ChangeData.ItemID .. ". The center target is a " .. ChangeData.ItemUnitType .. "." + end + + if ChangeCode == "RAU" then + MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". Removed the center target." + end + + if ChangeCode == "AAU" then + MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". The new center target is a " .. ChangeData.ItemUnitType .. "." + end + + if ChangeCode == "RA" then + MT[#MT+1] = "Removed old area " .. ChangeData.ItemID .. ". No more targets in this area." + end + + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = "Detected for area " .. ChangeData.ItemID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." + end + + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = "Removed for area " .. ChangeData.ItemID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." + end + + end + + return table.concat( MT, "\n" ) + + end + + + --- Make a DetectionSet table. This function will be overridden in the derived clsses. + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:CreateDetectionSets() + self:F2() + + + self:T( "Checking Detected Items for new Detected Units ..." ) + -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. + -- Regroup when needed, split groups when needed. + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + if DetectedItem then + + self:T( { "Detected Item ID:", DetectedItemID } ) - if DetectionObject and DetectionObject:isExist() and DetectionObject.id_ < 50000000 then - - local DetectionDetectedObjectName = DetectionObject:getName() - - local DetectionDetectedObjectPositionVec3 = DetectionObject:getPoint() - local DetectionGroupVec3 = DetectionGroup:GetVec3() - - local Distance = ( ( DetectionDetectedObjectPositionVec3.x - DetectionGroupVec3.x )^2 + - ( DetectionDetectedObjectPositionVec3.y - DetectionGroupVec3.y )^2 + - ( DetectionDetectedObjectPositionVec3.z - DetectionGroupVec3.z )^2 - ) ^ 0.5 / 1000 - - self:T2( { DetectionGroupName, DetectionDetectedObjectName, Distance } ) - - if Distance <= self.DetectionRange then - - if not self.DetectedObjects[DetectionDetectedObjectName] then - self.DetectedObjects[DetectionDetectedObjectName] = {} - end - self.DetectedObjects[DetectionDetectedObjectName].Name = DetectionDetectedObjectName - self.DetectedObjects[DetectionDetectedObjectName].Visible = DetectionDetectedTarget.visible - self.DetectedObjects[DetectionDetectedObjectName].Type = DetectionDetectedTarget.type - self.DetectedObjects[DetectionDetectedObjectName].Distance = DetectionDetectedTarget.distance - else - -- if beyond the DetectionRange then nullify... - if self.DetectedObjects[DetectionDetectedObjectName] then - self.DetectedObjects[DetectionDetectedObjectName] = nil - end - end - end - end - self:T2( self.DetectedObjects ) - - -- okay, now we have a list of detected object names ... - -- Sort the table based on distance ... - table.sort( self.DetectedObjects, function( a, b ) return a.Distance < b.Distance end ) - end - end - - if self.DetectedObjects then - self:CreateDetectionSets() - end - - return true -end - - - ---- DETECTION_AREAS class --- @type DETECTION_AREAS --- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. --- @field #DETECTION_AREAS.DetectedAreas DetectedAreas A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. --- @extends Functional.Detection#DETECTION_BASE -DETECTION_AREAS = { - ClassName = "DETECTION_AREAS", - DetectedAreas = { n = 0 }, - DetectionZoneRange = nil, -} - ---- @type DETECTION_AREAS.DetectedAreas --- @list <#DETECTION_AREAS.DetectedArea> - ---- @type DETECTION_AREAS.DetectedArea --- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. --- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. --- @field #boolean Changed Documents if the detected area has changes. --- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). --- @field #number AreaID -- The identifier of the detected area. --- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. --- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - - ---- DETECTION_AREAS constructor. --- @param Functional.Detection#DETECTION_AREAS self --- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @param Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. --- @return Functional.Detection#DETECTION_AREAS self -function DETECTION_AREAS:New( DetectionSetGroup, DetectionRange, DetectionZoneRange ) - - -- Inherits from DETECTION_BASE - local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup, DetectionRange ) ) - - self.DetectionZoneRange = DetectionZoneRange - - self._SmokeDetectedUnits = false - self._FlareDetectedUnits = false - self._SmokeDetectedZones = false - self._FlareDetectedZones = false - - self:Schedule( 10, 10 ) - - return self -end - ---- Add a detected @{#DETECTION_AREAS.DetectedArea}. --- @param Core.Set#SET_UNIT Set -- The Set of Units in the detected area. --- @param Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. --- @return #DETECTION_AREAS.DetectedArea DetectedArea -function DETECTION_AREAS:AddDetectedArea( Set, Zone ) - local DetectedAreas = self:GetDetectedAreas() - DetectedAreas.n = self:GetDetectedAreaCount() + 1 - DetectedAreas[DetectedAreas.n] = {} - local DetectedArea = DetectedAreas[DetectedAreas.n] - DetectedArea.Set = Set - DetectedArea.Zone = Zone - DetectedArea.Removed = false - DetectedArea.AreaID = DetectedAreas.n - - return DetectedArea -end - ---- Remove a detected @{#DETECTION_AREAS.DetectedArea} with a given Index. --- @param #DETECTION_AREAS self --- @param #number Index The Index of the detection are to be removed. --- @return #nil -function DETECTION_AREAS:RemoveDetectedArea( Index ) - local DetectedAreas = self:GetDetectedAreas() - local DetectedAreaCount = self:GetDetectedAreaCount() - local DetectedArea = DetectedAreas[Index] - local DetectedAreaSet = DetectedArea.Set - DetectedArea[Index] = nil - return nil -end - - ---- Get the detected @{#DETECTION_AREAS.DetectedAreas}. --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS.DetectedAreas DetectedAreas -function DETECTION_AREAS:GetDetectedAreas() - - local DetectedAreas = self.DetectedAreas - return DetectedAreas -end - ---- Get the amount of @{#DETECTION_AREAS.DetectedAreas}. --- @param #DETECTION_AREAS self --- @return #number DetectedAreaCount -function DETECTION_AREAS:GetDetectedAreaCount() - - local DetectedAreaCount = self.DetectedAreas.n - return DetectedAreaCount -end - ---- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. --- @param #DETECTION_AREAS self --- @param #number Index --- @return Core.Set#SET_UNIT DetectedSet -function DETECTION_AREAS:GetDetectedSet( Index ) - - local DetectedSetUnit = self.DetectedAreas[Index].Set - if DetectedSetUnit then - return DetectedSetUnit - end - - return nil -end - ---- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. --- @param #DETECTION_AREAS self --- @param #number Index --- @return Core.Zone#ZONE_UNIT DetectedZone -function DETECTION_AREAS:GetDetectedZone( Index ) - - local DetectedZone = self.DetectedAreas[Index].Zone - if DetectedZone then - return DetectedZone - end - - return nil -end - ---- Background worker function to determine if there are friendlies nearby ... --- @param #DETECTION_AREAS self --- @param Wrapper.Unit#UNIT ReportUnit -function DETECTION_AREAS:ReportFriendliesNearBy( ReportGroupData ) - self:F2() - - local DetectedArea = ReportGroupData.DetectedArea -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = ReportGroupData.DetectedArea.Set - local DetectedZone = ReportGroupData.DetectedArea.Zone - local DetectedZoneUnit = DetectedZone.ZoneUNIT - - DetectedArea.FriendliesNearBy = false - - local SphereSearch = { - id = world.VolumeType.SPHERE, - params = { - point = DetectedZoneUnit:GetVec3(), - radius = 6000, - } - - } - - --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit - -- @param Wrapper.Group#GROUP ReportGroup - -- @param Set#SET_GROUP ReportSetGroup - local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) - - local DetectedArea = ReportGroupData.DetectedArea -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = ReportGroupData.DetectedArea.Set - local DetectedZone = ReportGroupData.DetectedArea.Zone - local DetectedZoneUnit = DetectedZone.ZoneUNIT -- Wrapper.Unit#UNIT - local ReportSetGroup = ReportGroupData.ReportSetGroup - - local EnemyCoalition = DetectedZoneUnit:GetCoalition() - - local FoundUnitCoalition = FoundDCSUnit:getCoalition() - local FoundUnitName = FoundDCSUnit:getName() - local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() - local EnemyUnitName = DetectedZoneUnit:GetName() - local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil - - self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) - - if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then - DetectedArea.FriendliesNearBy = true - return false - end - - return true - end - - world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) - -end - - - ---- Returns if there are friendlies nearby the FAC units ... --- @param #DETECTION_AREAS self --- @return #boolean trhe if there are friendlies nearby -function DETECTION_AREAS:IsFriendliesNearBy( DetectedArea ) - - self:T3( DetectedArea.FriendliesNearBy ) - return DetectedArea.FriendliesNearBy or false -end - ---- Calculate the maxium A2G threat level of the DetectedArea. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea -function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedArea ) - - local MaxThreatLevelA2G = 0 - for UnitName, UnitData in pairs( DetectedArea.Set:GetSet() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - local ThreatLevelA2G = ThreatUnit:GetThreatLevel() - if ThreatLevelA2G > MaxThreatLevelA2G then - MaxThreatLevelA2G = ThreatLevelA2G - end - end - - self:T3( MaxThreatLevelA2G ) - DetectedArea.MaxThreatLevelA2G = MaxThreatLevelA2G - -end - ---- Find the nearest FAC of the DetectedArea. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return Wrapper.Unit#UNIT The nearest FAC unit -function DETECTION_AREAS:NearestFAC( DetectedArea ) - - local NearestFAC = nil - local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) - - for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do - local FACUnit = FACUnitData -- Wrapper.Unit#UNIT - if FACUnit:IsActive() then - local Vec3 = FACUnit:GetVec3() - local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) - local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) - if Distance < MinDistance then - MinDistance = Distance - NearestFAC = FACUnit - end - end - end - end - - DetectedArea.NearestFAC = NearestFAC - -end - ---- Returns the A2G threat level of the units in the DetectedArea --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #number a scale from 0 to 10. -function DETECTION_AREAS:GetTreatLevelA2G( DetectedArea ) - - self:T3( DetectedArea.MaxThreatLevelA2G ) - return DetectedArea.MaxThreatLevelA2G -end - - - ---- Smoke the detected units --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:SmokeDetectedUnits() - self:F2() - - self._SmokeDetectedUnits = true - return self -end - ---- Flare the detected units --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:FlareDetectedUnits() - self:F2() - - self._FlareDetectedUnits = true - return self -end - ---- Smoke the detected zones --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:SmokeDetectedZones() - self:F2() - - self._SmokeDetectedZones = true - return self -end - ---- Flare the detected zones --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:FlareDetectedZones() - self:F2() - - self._FlareDetectedZones = true - return self -end - ---- Add a change to the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @param #string ChangeCode --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AddChangeArea( DetectedArea, ChangeCode, AreaUnitType ) - - DetectedArea.Changed = true - local AreaID = DetectedArea.AreaID - - DetectedArea.Changes = DetectedArea.Changes or {} - DetectedArea.Changes[ChangeCode] = DetectedArea.Changes[ChangeCode] or {} - DetectedArea.Changes[ChangeCode].AreaID = AreaID - DetectedArea.Changes[ChangeCode].AreaUnitType = AreaUnitType - - self:T( { "Change on Detection Area:", DetectedArea.AreaID, ChangeCode, AreaUnitType } ) - - return self -end - - ---- Add a change to the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @param #string ChangeCode --- @param #string ChangeUnitType --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AddChangeUnit( DetectedArea, ChangeCode, ChangeUnitType ) - - DetectedArea.Changed = true - local AreaID = DetectedArea.AreaID - - DetectedArea.Changes = DetectedArea.Changes or {} - DetectedArea.Changes[ChangeCode] = DetectedArea.Changes[ChangeCode] or {} - DetectedArea.Changes[ChangeCode][ChangeUnitType] = DetectedArea.Changes[ChangeCode][ChangeUnitType] or 0 - DetectedArea.Changes[ChangeCode][ChangeUnitType] = DetectedArea.Changes[ChangeCode][ChangeUnitType] + 1 - DetectedArea.Changes[ChangeCode].AreaID = AreaID - - self:T( { "Change on Detection Area:", DetectedArea.AreaID, ChangeCode, ChangeUnitType } ) - - return self -end - ---- Make text documenting the changes of the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #string The Changes text -function DETECTION_AREAS:GetChangeText( DetectedArea ) - self:F( DetectedArea ) - - local MT = {} - - for ChangeCode, ChangeData in pairs( DetectedArea.Changes ) do - - if ChangeCode == "AA" then - MT[#MT+1] = "Detected new area " .. ChangeData.AreaID .. ". The center target is a " .. ChangeData.AreaUnitType .. "." - end - - if ChangeCode == "RAU" then - MT[#MT+1] = "Changed area " .. ChangeData.AreaID .. ". Removed the center target." - end - - if ChangeCode == "AAU" then - MT[#MT+1] = "Changed area " .. ChangeData.AreaID .. ". The new center target is a " .. ChangeData.AreaUnitType "." - end - - if ChangeCode == "RA" then - MT[#MT+1] = "Removed old area " .. ChangeData.AreaID .. ". No more targets in this area." - end - - if ChangeCode == "AU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "AreaID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Detected for area " .. ChangeData.AreaID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - if ChangeCode == "RU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "AreaID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Removed for area " .. ChangeData.AreaID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - end - - return table.concat( MT, "\n" ) - -end - - ---- Accepts changes from the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AcceptChanges( DetectedArea ) - - DetectedArea.Changed = false - DetectedArea.Changes = {} - - return self -end - - ---- Make a DetectionSet table. This function will be overridden in the derived clsses. --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:CreateDetectionSets() - self:F2() - - -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. - -- Regroup when needed, split groups when needed. - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do - - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - if DetectedArea then - - local DetectedSet = DetectedArea.Set - - local AreaExists = false -- This flag will determine of the detected area is still existing. - - -- First test if the center unit is detected in the detection area. - self:T3( DetectedArea.Zone.ZoneUNIT.UnitName ) - local DetectedZoneObject = self:GetDetectedObject( DetectedArea.Zone.ZoneUNIT.UnitName ) - self:T3( { "Detecting Zone Object", DetectedArea.AreaID, DetectedArea.Zone, DetectedZoneObject } ) - - if DetectedZoneObject then - - --self:IdentifyDetectedObject( DetectedZoneObject ) - AreaExists = true - - - - else - -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. - -- First remove the center unit from the set. - DetectedSet:RemoveUnitsByName( DetectedArea.Zone.ZoneUNIT.UnitName ) - - self:AddChangeArea( DetectedArea, 'RAU', "Dummy" ) + local DetectedSet = DetectedItem.Set - -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) - - -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. - -- If the DetectedUnit was already identified, DetectedObject will be nil. - if DetectedObject then - self:IdentifyDetectedObject( DetectedObject ) - AreaExists = true - - -- Assign the Unit as the new center unit of the detected area. - DetectedArea.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) - - self:AddChangeArea( DetectedArea, "AAU", DetectedArea.Zone.ZoneUNIT:GetTypeName() ) - - -- We don't need to add the DetectedObject to the area set, because it is already there ... - break - end - end - end - - -- Now we've determined the center unit of the area, now we can iterate the units in the detected area. - -- Note that the position of the area may have moved due to the center unit repositioning. - -- If no center unit was identified, then the detected area does not exist anymore and should be deleted, as there are no valid units that can be the center unit. - if AreaExists then - - -- ok, we found the center unit of the area, now iterate through the detected area set and see which units are still within the center unit zone ... - -- Those units within the zone are flagged as Identified. - -- If a unit was not found in the set, remove it from the set. This may be added later to other existing or new sets. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = nil - if DetectedUnit:IsAlive() then - --self:E(DetectedUnit:GetName()) - DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) - end - if DetectedObject then - - -- Check if the DetectedUnit is within the DetectedArea.Zone - if DetectedUnit:IsInZone( DetectedArea.Zone ) then + local AreaExists = false -- This flag will determine of the detected area is still existing. - -- Yes, the DetectedUnit is within the DetectedArea.Zone, no changes, DetectedUnit can be kept within the Set. - self:IdentifyDetectedObject( DetectedObject ) - - else - -- No, the DetectedUnit is not within the DetectedArea.Zone, remove DetectedUnit from the Set. - DetectedSet:Remove( DetectedUnitName ) - self:AddChangeUnit( DetectedArea, "RU", DetectedUnit:GetTypeName() ) - end - - else - -- There was no DetectedObject, remove DetectedUnit from the Set. - self:AddChangeUnit( DetectedArea, "RU", "destroyed target" ) - DetectedSet:Remove( DetectedUnitName ) - - -- The DetectedObject has been identified, because it does not exist ... - -- self:IdentifyDetectedObject( DetectedObject ) - end - end - else - self:RemoveDetectedArea( DetectedAreaID ) - self:AddChangeArea( DetectedArea, "RA" ) - end - end - end - - -- We iterated through the existing detection areas and: - -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. - -- - We recentered the detection area to new center units where it was needed. - -- - -- Now we need to loop through the unidentified detected units and see where they belong: - -- - They can be added to a new detection area and become the new center unit. - -- - They can be added to a new detection area. - for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - - local DetectedObject = self:GetDetectedObject( DetectedUnitName ) - - if DetectedObject then - - -- We found an unidentified unit outside of any existing detection area. - local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT - - local AddedToDetectionArea = false - - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do + -- First test if the center unit is detected in the detection area. + self:T3( { "Zone Center Unit:", DetectedItem.Zone.ZoneUNIT.UnitName } ) + local DetectedZoneObject = self:GetDetectedObject( DetectedItem.Zone.ZoneUNIT.UnitName ) + self:T3( { "Detected Zone Object:", DetectedItem.Zone:GetName(), DetectedZoneObject } ) - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - if DetectedArea then - self:T( "Detection Area #" .. DetectedArea.AreaID ) - local DetectedSet = DetectedArea.Set - if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedArea.Zone ) then - self:IdentifyDetectedObject( DetectedObject ) - DetectedSet:AddUnit( DetectedUnit ) - AddedToDetectionArea = true - self:AddChangeUnit( DetectedArea, "AU", DetectedUnit:GetTypeName() ) + if DetectedZoneObject then + + --self:IdentifyDetectedObject( DetectedZoneObject ) + AreaExists = true + + + + else + -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. + -- First remove the center unit from the set. + DetectedSet:RemoveUnitsByName( DetectedItem.Zone.ZoneUNIT.UnitName ) + + self:AddChangeItem( DetectedItem, 'RAU', "Dummy" ) + + -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. + for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do + + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) + + -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. + -- If the DetectedUnit was already identified, DetectedObject will be nil. + if DetectedObject then + self:IdentifyDetectedObject( DetectedObject ) + AreaExists = true + + DetectedItem.Zone:BoundZone( 12, self.CountryID, true) + + -- Assign the Unit as the new center unit of the detected area. + DetectedItem.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) + + self:AddChangeItem( DetectedItem, "AAU", DetectedItem.Zone.ZoneUNIT:GetTypeName() ) + + -- We don't need to add the DetectedObject to the area set, because it is already there ... + break + end end end + + -- Now we've determined the center unit of the area, now we can iterate the units in the detected area. + -- Note that the position of the area may have moved due to the center unit repositioning. + -- If no center unit was identified, then the detected area does not exist anymore and should be deleted, as there are no valid units that can be the center unit. + if AreaExists then + + -- ok, we found the center unit of the area, now iterate through the detected area set and see which units are still within the center unit zone ... + -- Those units within the zone are flagged as Identified. + -- If a unit was not found in the set, remove it from the set. This may be added later to other existing or new sets. + for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do + + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + local DetectedObject = nil + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Check if the DetectedUnit is within the DetectedItem.Zone + if DetectedUnit:IsInZone( DetectedItem.Zone ) then + + -- Yes, the DetectedUnit is within the DetectedItem.Zone, no changes, DetectedUnit can be kept within the Set. + self:IdentifyDetectedObject( DetectedObject ) + + else + -- No, the DetectedUnit is not within the DetectedItem.Zone, remove DetectedUnit from the Set. + DetectedSet:Remove( DetectedUnitName ) + self:AddChangeUnit( DetectedItem, "RU", DetectedUnit:GetTypeName() ) + end + + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", "destroyed target" ) + DetectedSet:Remove( DetectedUnitName ) + + -- The DetectedObject has been identified, because it does not exist ... + -- self:IdentifyDetectedObject( DetectedObject ) + end + end + else + DetectedItem.Zone:BoundZone( 12, self.CountryID, true) + self:RemoveDetectedItem( DetectedItemID ) + self:AddChangeItem( DetectedItem, "RA" ) + end end + end - if AddedToDetectionArea == false then + -- We iterated through the existing detection areas and: + -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. + -- - We recentered the detection area to new center units where it was needed. + -- + -- Now we need to loop through the unidentified detected units and see where they belong: + -- - They can be added to a new detection area and become the new center unit. + -- - They can be added to a new detection area. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - -- New detection area - local DetectedArea = self:AddDetectedArea( - SET_UNIT:New(), - ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) - ) - --self:E( DetectedArea.Zone.ZoneUNIT.UnitName ) - DetectedArea.Set:AddUnit( DetectedUnit ) - self:AddChangeArea( DetectedArea, "AA", DetectedUnit:GetTypeName() ) - end - end - end + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + + if DetectedObject then - -- Now all the tests should have been build, now make some smoke and flares... - -- We also report here the friendlies within the detected areas. - - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do - - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone - - self:ReportFriendliesNearBy( { DetectedArea = DetectedArea, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table - self:CalculateThreatLevelA2G( DetectedArea ) -- Calculate A2G threat level - self:NearestFAC( DetectedArea ) - - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedZone.ZoneUNIT:SmokeRed() - end - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit ) - if DetectedUnit:IsAlive() then - self:T( "Detected Set #" .. DetectedArea.AreaID .. ":" .. DetectedUnit:GetName() ) - if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then - DetectedUnit:FlareGreen() - end - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedUnit:SmokeGreen() + -- We found an unidentified unit outside of any existing detection area. + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + local AddedToDetectionArea = false + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + if DetectedItem then + self:T( "Detection Area #" .. DetectedItem.ItemID ) + local DetectedSet = DetectedItem.Set + if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedItem.Zone ) then + self:IdentifyDetectedObject( DetectedObject ) + DetectedSet:AddUnit( DetectedUnit ) + AddedToDetectionArea = true + self:AddChangeUnit( DetectedItem, "AU", DetectedUnit:GetTypeName() ) + end end end + + if AddedToDetectionArea == false then + + -- New detection area + local DetectedItem = self:AddDetectedItemZone( nil, + SET_UNIT:New(), + ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) + ) + --self:E( DetectedItem.Zone.ZoneUNIT.UnitName ) + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeItem( DetectedItem, "AA", DetectedUnit:GetTypeName() ) + end end - ) - if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then - DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) end - if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then - DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) + + -- Now all the tests should have been build, now make some smoke and flares... + -- We also report here the friendlies within the detected areas. + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + self:CalculateThreatLevelA2G( DetectedItem ) -- Calculate A2G threat level + self:NearestFAC( DetectedItem ) + + if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then + DetectedZone.ZoneUNIT:SmokeRed() + end + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit ) + if DetectedUnit:IsAlive() then + self:T( "Detected Set #" .. DetectedItem.ItemID .. ":" .. DetectedUnit:GetName() ) + if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then + DetectedUnit:FlareGreen() + end + if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then + DetectedUnit:SmokeGreen() + end + end + end + ) + if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then + DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) + end + if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then + DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) + end + + if DETECTION_AREAS._BoundDetectedZones or self._BoundDetectedZones then + DetectedZone:BoundZone( 12, self.CountryID ) + end end + end - -end - - + +end --- Single-Player:**No** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** -- **AI Balancing will replace in multi player missions -- non-occupied human slots with AI groups, in order to provide an engaging simulation environment, -- even when there are hardly any players in the mission.** @@ -26713,14 +28519,73 @@ end ---- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Air** -- --- **Air Patrolling or Staging.** +--- **AI** -- **Air Patrolling or Staging.** -- -- ![Banner Image](..\Presentations\AI_PATROL\Dia1.JPG) -- -- === -- --- # 1) @{#AI_PATROL_ZONE} class, extends @{Fsm#FSM_CONTROLLABLE} +-- AI PATROL classes makes AI Controllables execute an Patrol. +-- +-- There are the following types of PATROL classes defined: +-- +-- * @{#AI_PATROL_ZONE}: Perform a PATROL in a zone. +-- +-- ==== +-- +-- # **OPEN ISSUES** +-- +-- 2017-01-17: When Spawned AI is located at an airbase, it will be routed first back to the airbase after take-off. +-- +-- 2016-01-17: +-- -- Fixed problem with AI returning to base too early and unexpected. +-- -- ReSpawning of AI will reset the AI_PATROL and derived classes. +-- -- Checked the correct workings of SCHEDULER, and it DOES work correctly. +-- +-- ==== +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-01-17: Rename of class: **AI\_PATROL\_ZONE** is the new name for the old _AI\_PATROLZONE_. +-- +-- 2017-01-15: Complete revision. AI_PATROL_ZONE is the base class for other AI_PATROL like classes. +-- +-- 2016-09-01: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review. +-- +-- ### Authors: +-- +-- * **FlightControl**: Design & Programming. +-- +-- @module AI_Patrol + +--- AI_PATROL_ZONE class +-- @type AI_PATROL_ZONE +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. +-- @field Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @field Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. +-- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @field Functional.Spawn#SPAWN CoordTest +-- @extends Core.Fsm#FSM_CONTROLLABLE + +--- # 1) @{#AI_PATROL_ZONE} class, extends @{Fsm#FSM_CONTROLLABLE} -- -- The @{#AI_PATROL_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}. -- @@ -26821,59 +28686,10 @@ end -- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB). -- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this proces in place. -- --- ==== --- --- # **OPEN ISSUES** --- --- 2017-01-17: When Spawned AI is located at an airbase, it will be routed first back to the airbase after take-off. --- --- 2016-01-17: --- -- Fixed problem with AI returning to base too early and unexpected. --- -- ReSpawning of AI will reset the AI_PATROL and derived classes. --- -- Checked the correct workings of SCHEDULER, and it DOES work correctly. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-17: Rename of class: **AI\_PATROL\_ZONE** is the new name for the old _AI\_PATROLZONE_. --- --- 2017-01-15: Complete revision. AI_PATROL_ZONE is the base class for other AI_PATROL like classes. --- --- 2016-09-01: Initial class and API. --- -- === -- --- # **AUTHORS and CONTRIBUTIONS** +-- @field #AI_PATROL_ZONE AI_PATROL_ZONE -- --- ### Contributions: --- --- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review. --- --- ### Authors: --- --- * **FlightControl**: Design & Programming. --- --- @module AI_Patrol - ---- AI_PATROL_ZONE class --- @type AI_PATROL_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @field Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @field Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. --- @field Functional.Spawn#SPAWN CoordTest --- @extends Core.Fsm#FSM_CONTROLLABLE AI_PATROL_ZONE = { ClassName = "AI_PATROL_ZONE", } @@ -27652,14 +29468,55 @@ function AI_PATROL_ZONE:OnPilotDead( EventData ) self:__PilotDead( 1, EventData ) end end ---- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Air** -- --- **Provide Close Air Support to friendly ground troops.** +--- **AI** -- **Provide Close Air Support to friendly ground troops.** -- -- ![Banner Image](..\Presentations\AI_CAS\Dia1.JPG) -- -- === +-- +-- AI CAS classes makes AI Controllables execute a Close Air Support. +-- +-- There are the following types of CAS classes defined: +-- +-- * @{#AI_CAS_ZONE}: Perform a CAS in a zone. +-- +-- === -- --- # 1) @{#AI_CAS_ZONE} class, extends @{AI_Patrol#AI_PATROL_ZONE} +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-01-15: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. +-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module AI_Cas + + +--- AI_CAS_ZONE class +-- @type AI_CAS_ZONE +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. +-- @extends AI.AI_Patrol#AI_PATROL_ZONE + +--- # 1) @{#AI_CAS_ZONE} class, extends @{AI_Patrol#AI_PATROL_ZONE} -- -- @{#AI_CAS_ZONE} derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. -- @@ -27744,42 +29601,11 @@ end -- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Unit}. -- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Unit}s assigned in the CAS task. -- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-15: Initial class and API. --- +-- -- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. --- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module AI_Cas - - ---- AI_CAS_ZONE class --- @type AI_CAS_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. --- @extends AI.AI_Patrol#AI_PATROL_ZONE +-- +-- @field #AI_CAS_ZONE AI_CAS_ZONE +-- AI_CAS_ZONE = { ClassName = "AI_CAS_ZONE", } @@ -27815,11 +29641,6 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. - -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. - -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. - -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. - -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @return #boolean Return false to cancel Transition. @@ -27830,18 +29651,15 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. - -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. - -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. - -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. - -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- Synchronous Event Trigger for Event Engage. -- @function [parent=#AI_CAS_ZONE] Engage -- @param #AI_CAS_ZONE self -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. + -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. + -- If parameter is not defined the unit / controllable will choose expend on its own discretion. + -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. @@ -27850,8 +29668,10 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #AI_CAS_ZONE self -- @param #number Delay The delay in seconds. -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. + -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. + -- If parameter is not defined the unit / controllable will choose expend on its own discretion. + -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. @@ -28062,7 +29882,7 @@ function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To ) if Detected == true then self:E( {"Target: ", DetectedUnit } ) self.DetectedUnits[DetectedUnit] = false - local AttackTask = Controllable:EnRouteTaskEngageUnit( DetectedUnit, 1, true, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil ) + local AttackTask = Controllable:TaskAttackUnit( DetectedUnit, false, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil ) self.Controllable:PushTask( AttackTask, 1 ) end end @@ -28093,8 +29913,8 @@ end -- @param #string Event The Event string. -- @param #string To The To State string. -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. +-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, @@ -28112,7 +29932,6 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, self.EngageDirection = EngageDirection if Controllable:IsAlive() then - local EngageRoute = {} @@ -28133,29 +29952,28 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, EngageRoute[#EngageRoute+1] = CurrentRoutePoint - - if self.Controllable:IsNotInZone( self.EngageZone ) then - - -- Find a random 2D point in EngageZone. - local ToEngageZoneVec2 = self.EngageZone:GetRandomVec2() - self:T2( ToEngageZoneVec2 ) - - -- Obtain a 3D @{Point} from the 2D point + altitude. - local ToEngageZonePointVec3 = POINT_VEC3:New( ToEngageZoneVec2.x, self.EngageAltitude, ToEngageZoneVec2.y ) - - -- Create a route point of type air. - local ToEngageZoneRoutePoint = ToEngageZonePointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - self.EngageSpeed, - true - ) - - EngageRoute[#EngageRoute+1] = ToEngageZoneRoutePoint + local AttackTasks = {} + for DetectedUnitID, DetectedUnit in pairs( self.DetectedUnits ) do + local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT + self:T( DetectedUnit ) + if DetectedUnit:IsAlive() then + if DetectedUnit:IsInZone( self.EngageZone ) then + self:E( {"Engaging ", DetectedUnit } ) + AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit, + true, + EngageWeaponExpend, + EngageAttackQty, + EngageDirection + ) + end + else + self.DetectedUnits[DetectedUnit] = nil + end end - + + EngageRoute[1].task = Controllable:TaskCombo( AttackTasks ) + --- Define a random point in the @{Zone}. The AI will fly to that point within the zone. --- Find a random 2D point in EngageZone. @@ -28174,45 +29992,25 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, true ) - --ToTargetPointVec3:SmokeBlue() - EngageRoute[#EngageRoute+1] = ToTargetRoutePoint + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + Controllable:WayPointInitialize( EngageRoute ) + --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... + Controllable:SetState( Controllable, "EngageZone", self ) + + Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" ) + + --- NOW ROUTE THE GROUP! + Controllable:WayPointExecute( 1 ) Controllable:OptionROEOpenFire() Controllable:OptionROTVertical() - --- local AttackTasks = {} --- --- for DetectedUnitID, DetectedUnit in pairs( self.DetectedUnits ) do --- local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT --- self:T( DetectedUnit ) --- if DetectedUnit:IsAlive() then --- if DetectedUnit:IsInZone( self.EngageZone ) then --- self:E( {"Engaging ", DetectedUnit } ) --- AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) --- end --- else --- self.DetectedUnits[DetectedUnit] = nil --- end --- end --- --- EngageRoute[1].task = Controllable:TaskCombo( AttackTasks ) - - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - self.Controllable:WayPointInitialize( EngageRoute ) - --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... - self.Controllable:SetState( self.Controllable, "EngageZone", self ) - - self.Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" ) - - --- NOW ROUTE THE GROUP! - self.Controllable:WayPointExecute( 1 ) - - self:SetDetectionInterval( 10 ) + self:SetDetectionInterval( 2 ) self:SetDetectionActivated() - self:__Target( -10 ) -- Start Targetting + self:__Target( -2 ) -- Start Targetting end end @@ -28255,13 +30053,57 @@ function AI_CAS_ZONE:OnEventDead( EventData ) end ---- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Air** -- **Execute Combat Air Patrol (CAP).** +--- **AI** - **Execute Combat Air Patrol (CAP).** -- -- ![Banner Image](..\Presentations\AI_CAP\Dia1.JPG) -- -- === +-- +-- AI CAP classes makes AI Controllables execute a Combat Air Patrol. +-- +-- There are the following types of CAP classes defined: +-- +-- * @{#AI_CAP_ZONE}: Perform a CAP in a zone. +-- +-- ==== -- --- # 1) @{#AI_CAP_ZONE} class, extends @{AI_CAP#AI_PATROL_ZONE} +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-01-15: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. +-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. +-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. +-- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing. +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module AI_Cap + + +--- @type AI_CAP_ZONE +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. +-- @extends AI.AI_Patrol#AI_PATROL_ZONE + + +--- # 1) @{#AI_CAP_ZONE} class, extends @{AI_CAP#AI_PATROL_ZONE} -- -- The @{#AI_CAP_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group} -- and automatically engage any airborne enemies that are within a certain range or within a certain zone. @@ -28339,44 +30181,11 @@ end -- An optional @{Zone} can be set, -- that will define when the AI will engage with the detected airborne enemy targets. -- Use the method @{AI_Cap#AI_CAP_ZONE.SetEngageZone}() to define that Zone. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-15: Initial class and API. --- +-- -- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. --- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. --- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. --- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module AI_Cap - - ---- AI_CAP_ZONE class --- @type AI_CAP_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. --- @extends AI.AI_Patrol#AI_PATROL_ZONE +-- +-- @field #AI_CAP_ZONE AI_CAP_ZONE +-- AI_CAP_ZONE = { ClassName = "AI_CAP_ZONE", } @@ -30014,7 +31823,7 @@ do -- ACT_ASSIGN_ACCEPT self:Message( "You are assigned to the task " .. self.Task:GetName() ) - self.Task:Assign() + self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() ) end end -- ACT_ASSIGN_ACCEPT @@ -30213,7 +32022,7 @@ do -- ACT_ROUTE -- @type ACT_ROUTE -- @field Tasking.Task#TASK TASK -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone + -- @field Core.Zone#ZONE_BASE Zone -- @extends Core.Fsm#FSM_PROCESS ACT_ROUTE = { ClassName = "ACT_ROUTE", @@ -30309,6 +32118,115 @@ do -- ACT_ROUTE end -- ACT_ROUTE +do -- ACT_ROUTE_POINT + + --- ACT_ROUTE_POINT class + -- @type ACT_ROUTE_POINT + -- @field Tasking.Task#TASK TASK + -- @extends #ACT_ROUTE + ACT_ROUTE_POINT = { + ClassName = "ACT_ROUTE_POINT", + } + + + --- Creates a new routing state machine. + -- The task will route a controllable to a PointVec2 until the controllable is within the Range. + -- @param #ACT_ROUTE_POINT self + -- @param Core.Point#POINT_VEC2 The PointVec2 to Target. + -- @param #number Range The Distance to Target. + -- @param Core.Zone#ZONE_BASE Zone + function ACT_ROUTE_POINT:New( PointVec2, Range ) + local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_POINT + + self.PointVec2 = PointVec2 + self.Range = Range or 0 + + self.DisplayInterval = 30 + self.DisplayCount = 30 + self.DisplayMessage = true + self.DisplayTime = 10 -- 10 seconds is the default + + return self + end + + function ACT_ROUTE_POINT:Init( FsmRoute ) + + self.PointVec2 = FsmRoute.PointVec2 + self.Range = FsmRoute.Range or 0 + + self.DisplayInterval = 30 + self.DisplayCount = 30 + self.DisplayMessage = true + self.DisplayTime = 10 -- 10 seconds is the default + end + + --- Set PointVec2 + -- @param #ACT_ROUTE_POINT self + -- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to route to. + function ACT_ROUTE_POINT:SetPointVec2( PointVec2 ) + self:F2( { PointVec2 } ) + self.PointVec2 = PointVec2 + end + + --- Get PointVec2 + -- @param #ACT_ROUTE_POINT self + -- @return Core.Point#POINT_VEC2 PointVec2 The PointVec2 to route to. + function ACT_ROUTE_POINT:GetPointVec2() + self:F2( { self.PointVec2 } ) + return self.PointVec2 + end + + --- Set Range around PointVec2 + -- @param #ACT_ROUTE_POINT self + -- @param #number Range The Range to consider the arrival. Default is 10000 meters. + function ACT_ROUTE_POINT:SetRange( Range ) + self:F2( { self.Range } ) + self.Range = Range or 10000 + end + + --- Get Range around PointVec2 + -- @param #ACT_ROUTE_POINT self + -- @return #number The Range to consider the arrival. Default is 10000 meters. + function ACT_ROUTE_POINT:GetRange() + return self.Range + end + + --- Method override to check if the controllable has arrived. + -- @param #ACT_ROUTE_POINT self + -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit + -- @return #boolean + function ACT_ROUTE_POINT:onfuncHasArrived( ProcessUnit ) + + if ProcessUnit:IsAlive() then + local Distance = self.PointVec2:Get2DDistance( ProcessUnit:GetPointVec2() ) + + if Distance <= self.Range then + local RouteText = "You have arrived." + self:Message( RouteText ) + return true + end + end + + return false + end + + --- Task Events + + --- StateMachine callback function + -- @param #ACT_ROUTE_POINT self + -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit + -- @param #string Event + -- @param #string From + -- @param #string To + function ACT_ROUTE_POINT:onenterReporting( ProcessUnit, From, Event, To ) + + local TaskUnitPointVec2 = ProcessUnit:GetPointVec2() + local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( self.PointVec2 ) .. " km." + self:Message( RouteText ) + end + +end -- ACT_ROUTE_POINT + do -- ACT_ROUTE_ZONE @@ -30316,7 +32234,7 @@ do -- ACT_ROUTE_ZONE -- @type ACT_ROUTE_ZONE -- @field Tasking.Task#TASK TASK -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone + -- @field Core.Zone#ZONE_BASE Zone -- @extends #ACT_ROUTE ACT_ROUTE_ZONE = { ClassName = "ACT_ROUTE_ZONE", @@ -30325,11 +32243,11 @@ do -- ACT_ROUTE_ZONE --- Creates a new routing state machine. The task will route a controllable to a ZONE until the controllable is within that ZONE. -- @param #ACT_ROUTE_ZONE self - -- @param Core.Zone#ZONE_BASE TargetZone - function ACT_ROUTE_ZONE:New( TargetZone ) + -- @param Core.Zone#ZONE_BASE Zone + function ACT_ROUTE_ZONE:New( Zone ) local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE - self.TargetZone = TargetZone + self.Zone = Zone self.DisplayInterval = 30 self.DisplayCount = 30 @@ -30341,7 +32259,7 @@ do -- ACT_ROUTE_ZONE function ACT_ROUTE_ZONE:Init( FsmRoute ) - self.TargetZone = FsmRoute.TargetZone + self.Zone = FsmRoute.Zone self.DisplayInterval = 30 self.DisplayCount = 30 @@ -30349,18 +32267,32 @@ do -- ACT_ROUTE_ZONE self.DisplayTime = 10 -- 10 seconds is the default end + --- Set Zone + -- @param #ACT_ROUTE_ZONE self + -- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to. + function ACT_ROUTE_ZONE:SetZone( Zone ) + self.Zone = Zone + end + + --- Get Zone + -- @param #ACT_ROUTE_ZONE self + -- @return Core.Zone#ZONE_BASE Zone The Zone object where to route to. + function ACT_ROUTE_ZONE:GetZone() + return self.Zone + end + --- Method override to check if the controllable has arrived. -- @param #ACT_ROUTE self -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit -- @return #boolean function ACT_ROUTE_ZONE:onfuncHasArrived( ProcessUnit ) - if ProcessUnit:IsInZone( self.TargetZone ) then + if ProcessUnit:IsInZone( self.Zone ) then local RouteText = "You have arrived within the zone." self:Message( RouteText ) end - return ProcessUnit:IsInZone( self.TargetZone ) + return ProcessUnit:IsInZone( self.Zone ) end --- Task Events @@ -30373,83 +32305,18 @@ do -- ACT_ROUTE_ZONE -- @param #string To function ACT_ROUTE_ZONE:onenterReporting( ProcessUnit, From, Event, To ) - local ZoneVec2 = self.TargetZone:GetVec2() + local ZoneVec2 = self.Zone:GetVec2() local ZonePointVec2 = POINT_VEC2:New( ZoneVec2.x, ZoneVec2.y ) local TaskUnitVec2 = ProcessUnit:GetVec2() local TaskUnitPointVec2 = POINT_VEC2:New( TaskUnitVec2.x, TaskUnitVec2.y ) - local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( ZonePointVec2 ) .. " km to target." + local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( ZonePointVec2 ) .. " km." self:Message( RouteText ) end end -- ACT_ROUTE_ZONE ---- (SP) (MP) (FSM) Account for (Detect, count and report) DCS events occuring on DCS objects (units). +--- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occuring on @{Unit}s. -- --- === --- --- # @{#ACT_ACCOUNT} FSM class, extends @{Fsm#FSM_PROCESS} --- --- ## ACT_ACCOUNT state machine: --- --- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. --- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. --- Each derived class follows exactly the same process, using the same events and following the same state transitions, --- but will have **different implementation behaviour** upon each event or state transition. --- --- ### ACT_ACCOUNT **Events**: --- --- These are the events defined in this class: --- --- * **Start**: The process is started. The process will go into the Report state. --- * **Event**: A relevant event has occured that needs to be accounted for. The process will go into the Account state. --- * **Report**: The process is reporting to the player the accounting status of the DCS events. --- * **More**: There are more DCS events that need to be accounted for. The process will go back into the Report state. --- * **NoMore**: There are no more DCS events that need to be accounted for. The process will go into the Success state. --- --- ### ACT_ACCOUNT **Event methods**: --- --- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process. --- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine: --- --- * **Immediate**: The event method has exactly the name of the event. --- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. --- --- ### ACT_ACCOUNT **States**: --- --- * **Assigned**: The player is assigned to the task. This is the initialization state for the process. --- * **Waiting**: the process is waiting for a DCS event to occur within the simulator. This state is set automatically. --- * **Report**: The process is Reporting to the players in the group of the unit. This state is set automatically every 30 seconds. --- * **Account**: The relevant DCS event has occurred, and is accounted for. --- * **Success (*)**: All DCS events were accounted for. --- * **Failed (*)**: The process has failed. --- --- (*) End states of the process. --- --- ### ACT_ACCOUNT state transition methods: --- --- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. --- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Before** the state transition. --- The state transition method needs to start with the name **OnBefore + the name of the state**. --- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, --- but then you'll need to specify your own logic using the AIControllable! --- --- * **After** the state transition. --- The state transition method needs to start with the name **OnAfter + the name of the state**. --- These state transition methods need to provide a return value, which is specified at the function description. --- --- # 1) @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Fsm.Account#ACT_ACCOUNT} --- --- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units. --- The process is given a @{Set} of units that will be tracked upon successful destruction. --- The process will end after each target has been successfully destroyed. --- Each successful dead will trigger an Account state transition that can be scored, modified or administered. --- --- --- ## ACT_ACCOUNT_DEADS constructor: --- --- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object. +-- ![Banner Image](..\Presentations\ACT_ACCOUNT\Dia1.JPG) -- -- === -- @@ -30458,7 +32325,51 @@ end -- ACT_ROUTE_ZONE do -- ACT_ACCOUNT - --- ACT_ACCOUNT class + --- # @{#ACT_ACCOUNT} FSM class, extends @{Fsm#FSM_PROCESS} + -- + -- ## ACT_ACCOUNT state machine: + -- + -- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. + -- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. + -- Each derived class follows exactly the same process, using the same events and following the same state transitions, + -- but will have **different implementation behaviour** upon each event or state transition. + -- + -- ### ACT_ACCOUNT States + -- + -- * **Asigned**: The player is assigned. + -- * **Waiting**: Waiting for an event. + -- * **Report**: Reporting. + -- * **Account**: Account for an event. + -- * **Accounted**: All events have been accounted for, end of the process. + -- * **Failed**: Failed the process. + -- + -- ### ACT_ACCOUNT Events + -- + -- * **Start**: Start the process. + -- * **Wait**: Wait for an event. + -- * **Report**: Report the status of the accounting. + -- * **Event**: An event happened, process the event. + -- * **More**: More targets. + -- * **NoMore (*)**: No more targets. + -- * **Fail (*)**: The action process has failed. + -- + -- (*) End states of the process. + -- + -- ### ACT_ACCOUNT state transition methods: + -- + -- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. + -- There are 2 moments when state transition methods will be called by the state machine: + -- + -- * **Before** the state transition. + -- The state transition method needs to start with the name **OnBefore + the name of the state**. + -- If the state transition method returns false, then the processing of the state transition will not be done! + -- If you want to change the behaviour of the AIControllable at this event, return false, + -- but then you'll need to specify your own logic using the AIControllable! + -- + -- * **After** the state transition. + -- The state transition method needs to start with the name **OnAfter + the name of the state**. + -- These state transition methods need to provide a return value, which is specified at the function description. + -- -- @type ACT_ACCOUNT -- @field Set#SET_UNIT TargetSetUnit -- @extends Core.Fsm#FSM_PROCESS @@ -30540,7 +32451,18 @@ end -- ACT_ACCOUNT do -- ACT_ACCOUNT_DEADS - --- ACT_ACCOUNT_DEADS class + --- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Fsm.Account#ACT_ACCOUNT} + -- + -- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units. + -- The process is given a @{Set} of units that will be tracked upon successful destruction. + -- The process will end after each target has been successfully destroyed. + -- Each successful dead will trigger an Account state transition that can be scored, modified or administered. + -- + -- + -- ## ACT_ACCOUNT_DEADS constructor: + -- + -- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object. + -- -- @type ACT_ACCOUNT_DEADS -- @field Set#SET_UNIT TargetSetUnit -- @extends #ACT_ACCOUNT @@ -30576,15 +32498,6 @@ do -- ACT_ACCOUNT_DEADS self.TaskName = FsmAccount.TaskName end - - - function ACT_ACCOUNT_DEADS:_Destructor() - self:E("_Destructor") - - self:EventRemoveAll() - - end - --- Process Events --- StateMachine callback function @@ -30593,7 +32506,7 @@ do -- ACT_ACCOUNT_DEADS -- @param #string Event -- @param #string From -- @param #string To - function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, From, Event, To ) + function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, Task, From, Event, To ) self:E( { ProcessUnit, From, Event, To } ) self:Message( "Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed." ) @@ -30606,18 +32519,21 @@ do -- ACT_ACCOUNT_DEADS -- @param #string Event -- @param #string From -- @param #string To - function ACT_ACCOUNT_DEADS:onenterAccount( ProcessUnit, From, Event, To, EventData ) + function ACT_ACCOUNT_DEADS:onenterAccount( ProcessUnit, Task, From, Event, To, EventData ) self:T( { ProcessUnit, EventData, From, Event, To } ) self:T({self.Controllable}) self.TargetSetUnit:Flush() + self:T( { "Before sending Message", EventData.IniUnitName, self.TargetSetUnit:FindUnit( EventData.IniUnitName ) } ) if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then + self:T( "Sending Message" ) local TaskGroup = ProcessUnit:GetGroup() - self.TargetSetUnit:RemoveUnitsByName( EventData.IniUnitName ) + self.TargetSetUnit:Remove( EventData.IniUnitName ) self:Message( "You hit a target. Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." ) end + self:T( { "After sending Message" } ) end --- StateMachine callback function @@ -30626,7 +32542,7 @@ do -- ACT_ACCOUNT_DEADS -- @param #string Event -- @param #string From -- @param #string To - function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, From, Event, To, EventData ) + function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To ) if self.TargetSetUnit:Count() > 0 then self:__More( 1 ) @@ -30643,7 +32559,7 @@ do -- ACT_ACCOUNT_DEADS self:T( { "EventDead", EventData } ) if EventData.IniDCSUnit then - self:__Event( 1, EventData ) + self:Event( EventData ) end end @@ -30758,7 +32674,7 @@ do -- ACT_ASSIST function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To ) local ProcessGroup = ProcessUnit:GetGroup() - local MissionMenu = self:GetMission():GetMissionMenu( ProcessGroup ) + local MissionMenu = self:GetMission():GetMenu( ProcessGroup ) local function MenuSmoke( MenuParam ) self:E( MenuParam ) @@ -30775,6 +32691,17 @@ do -- ACT_ASSIST self.MenuSmokeRed = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop Red smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Red } ) self.MenuSmokeWhite = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop White smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.White } ) end + + --- StateMachine callback function + -- @param #ACT_ASSIST self + -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit + -- @param #string Event + -- @param #string From + -- @param #string To + function ACT_ASSIST:onafterStop( ProcessUnit, From, Event, To ) + + self.Menu:Remove() -- When stopped, remove the menus + end end @@ -30875,7 +32802,9 @@ function REPORT:New( Title ) local self = BASE:Inherit( self, BASE:New() ) self.Report = {} - self.Report[#self.Report+1] = Title + if Title then + self.Report[#self.Report+1] = Title + end return self end @@ -30886,11 +32815,17 @@ end -- @return #REPORT function REPORT:Add( Text ) self.Report[#self.Report+1] = Text - return self.Report[#self.Report+1] + return self.Report[#self.Report] end -function REPORT:Text() - return table.concat( self.Report, "\n" ) +--- Produces the text of the report, taking into account an optional delimeter, which is \n by default. +-- @param #REPORT self +-- @param #string Delimiter (optional) A delimiter text. +-- @return #string The report text. +function REPORT:Text( Delimiter ) + Delimiter = Delimiter or "\n" + local ReportText = table.concat( self.Report, Delimiter ) or "" + return ReportText end --- The COMMANDCENTER class @@ -30923,22 +32858,23 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) self:HandleEvent( EVENTS.Birth, --- @param #COMMANDCENTER self - --- @param Core.Event#EVENTDATA EventData + -- @param Core.Event#EVENTDATA EventData function( self, EventData ) - self:E( { EventData } ) - local EventGroup = GROUP:Find( EventData.IniDCSGroup ) - if EventGroup and self:HasGroup( EventGroup ) then - local MenuReporting = MENU_GROUP:New( EventGroup, "Reporting", self.CommandCenterMenu ) - local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Summary Report", MenuReporting, self.ReportSummary, self, EventGroup ) - local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) - self:ReportSummary( EventGroup ) - end - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! - Mission:JoinUnit( PlayerUnit, PlayerGroup ) - Mission:ReportDetails() + if EventData.IniObjectCategory == 1 then + local EventGroup = GROUP:Find( EventData.IniDCSGroup ) + if EventGroup and self:HasGroup( EventGroup ) then + local MenuReporting = MENU_GROUP:New( EventGroup, "Reporting", self.CommandCenterMenu ) + local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Summary Report", MenuReporting, self.ReportSummary, self, EventGroup ) + local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) + self:ReportSummary( EventGroup ) + end + local PlayerUnit = EventData.IniUnit + for MissionID, Mission in pairs( self:GetMissions() ) do + local Mission = Mission -- Tasking.Mission#MISSION + local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! + Mission:JoinUnit( PlayerUnit, PlayerGroup ) + Mission:ReportDetails() + end end end @@ -31048,17 +32984,26 @@ function COMMANDCENTER:SetMenu() self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "Command Center (" .. self:GetName() .. ")" ) + local MenuTime = timer.getTime() for MissionID, Mission in pairs( self:GetMissions() ) do local Mission = Mission -- Tasking.Mission#MISSION - Mission:RemoveMenu() + Mission:SetMenu( MenuTime ) + end + + for MissionID, Mission in pairs( self:GetMissions() ) do + local Mission = Mission -- Tasking.Mission#MISSION + Mission:RemoveMenu( MenuTime ) end - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - Mission:SetMenu() - end end +--- Gets the commandcenter menu structure governed by the HQ command center. +-- @param #COMMANDCENTER self +-- @return Core.Menu#MENU_COALITION +function COMMANDCENTER:GetMenu() + self:F() + return self.CommandCenterMenu +end --- Checks of the COMMANDCENTER has a GROUP. -- @param #COMMANDCENTER self @@ -31079,6 +33024,14 @@ function COMMANDCENTER:HasGroup( MissionGroup ) return Has end +--- Send a CC message to the coalition of the CC. +-- @param #COMMANDCENTER self +function COMMANDCENTER:MessageToAll( Message ) + + self:GetPositionable():MessageToAll( Message, 20, self:GetName() ) + +end + --- Send a CC message to a GROUP. -- @param #COMMANDCENTER self -- @param #string Message @@ -31086,7 +33039,8 @@ end -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. function COMMANDCENTER:MessageToGroup( Message, TaskGroup, Name ) - local Prefix = Name and "@ Group (" .. Name .. "): " or '' + local Prefix = "@ Group" + Prefix = Prefix .. ( Name and " (" .. Name .. "): " or '' ) Message = Prefix .. Message self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() ) @@ -31102,6 +33056,7 @@ function COMMANDCENTER:MessageToCoalition( Message ) end + --- Report the status of all MISSIONs to a GROUP. -- Each Mission is listed, with an indication how many Tasks are still to be completed. -- @param #COMMANDCENTER self @@ -31149,22 +33104,6 @@ MISSION = { ClassName = "MISSION", Name = "", MissionStatus = "PENDING", - _Clients = {}, - TaskMenus = {}, - TaskCategoryMenus = {}, - TaskTypeMenus = {}, - _ActiveTasks = {}, - GoalFunction = nil, - MissionReportTrigger = 0, - MissionProgressTrigger = 0, - MissionReportShow = false, - MissionReportFlash = false, - MissionTimeInterval = 0, - MissionCoalition = "", - SUCCESS = 1, - FAILED = 2, - REPEAT = 3, - _GoalTasks = {} } --- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. @@ -31182,10 +33121,184 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi self:SetStartState( "Idle" ) self:AddTransition( "Idle", "Start", "Ongoing" ) + + --- OnLeave Transition Handler for State Idle. + -- @function [parent=#MISSION] OnLeaveIdle + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Idle. + -- @function [parent=#MISSION] OnEnterIdle + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnLeave Transition Handler for State Ongoing. + -- @function [parent=#MISSION] OnLeaveOngoing + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Ongoing. + -- @function [parent=#MISSION] OnEnterOngoing + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Start. + -- @function [parent=#MISSION] OnBeforeStart + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Start. + -- @function [parent=#MISSION] OnAfterStart + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Start. + -- @function [parent=#MISSION] Start + -- @param #MISSION self + + --- Asynchronous Event Trigger for Event Start. + -- @function [parent=#MISSION] __Start + -- @param #MISSION self + -- @param #number Delay The delay in seconds. + self:AddTransition( "Ongoing", "Stop", "Idle" ) + + --- OnLeave Transition Handler for State Idle. + -- @function [parent=#MISSION] OnLeaveIdle + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Idle. + -- @function [parent=#MISSION] OnEnterIdle + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Stop. + -- @function [parent=#MISSION] OnBeforeStop + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Stop. + -- @function [parent=#MISSION] OnAfterStop + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Stop. + -- @function [parent=#MISSION] Stop + -- @param #MISSION self + + --- Asynchronous Event Trigger for Event Stop. + -- @function [parent=#MISSION] __Stop + -- @param #MISSION self + -- @param #number Delay The delay in seconds. + self:AddTransition( "Ongoing", "Complete", "Completed" ) + + --- OnLeave Transition Handler for State Completed. + -- @function [parent=#MISSION] OnLeaveCompleted + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Completed. + -- @function [parent=#MISSION] OnEnterCompleted + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Complete. + -- @function [parent=#MISSION] OnBeforeComplete + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Complete. + -- @function [parent=#MISSION] OnAfterComplete + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Complete. + -- @function [parent=#MISSION] Complete + -- @param #MISSION self + + --- Asynchronous Event Trigger for Event Complete. + -- @function [parent=#MISSION] __Complete + -- @param #MISSION self + -- @param #number Delay The delay in seconds. + self:AddTransition( "*", "Fail", "Failed" ) + --- OnLeave Transition Handler for State Failed. + -- @function [parent=#MISSION] OnLeaveFailed + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnEnter Transition Handler for State Failed. + -- @function [parent=#MISSION] OnEnterFailed + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- OnBefore Transition Handler for Event Fail. + -- @function [parent=#MISSION] OnBeforeFail + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Fail. + -- @function [parent=#MISSION] OnAfterFail + -- @param #MISSION self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Fail. + -- @function [parent=#MISSION] Fail + -- @param #MISSION self + + --- Asynchronous Event Trigger for Event Fail. + -- @function [parent=#MISSION] __Fail + -- @param #MISSION self + -- @param #number Delay The delay in seconds. + self:T( { MissionName, MissionPriority, MissionBriefing, MissionCoalition } ) self.CommandCenter = CommandCenter @@ -31197,14 +33310,18 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi self.MissionCoalition = MissionCoalition self.Tasks = {} + + -- Private implementations + + return self end ---- FSM function for a MISSION +-- FSM function for a MISSION -- @param #MISSION self --- @param #string Event -- @param #string From +-- @param #string Event -- @param #string To function MISSION:onbeforeComplete( From, Event, To ) @@ -31217,10 +33334,10 @@ function MISSION:onbeforeComplete( From, Event, To ) return true -- Allow Mission completion. end ---- FSM function for a MISSION +-- FSM function for a MISSION -- @param #MISSION self --- @param #string Event -- @param #string From +-- @param #string Event -- @param #string To function MISSION:onenterCompleted( From, Event, To ) @@ -31338,23 +33455,25 @@ end --- Sets the Planned Task menu. -- @param #MISSION self -function MISSION:SetMenu() +-- @param #number MenuTime +function MISSION:SetMenu( MenuTime ) self:F() - for _, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - Task:SetMenu() + for _, TaskData in pairs( self:GetTasks() ) do + local Task = TaskData -- Tasking.Task#TASK + Task:SetMenu( MenuTime ) end end --- Removes the Planned Task menu. -- @param #MISSION self -function MISSION:RemoveMenu() +-- @param #number MenuTime +function MISSION:RemoveMenu( MenuTime ) self:F() for _, Task in pairs( self:GetTasks() ) do local Task = Task -- Tasking.Task#TASK - Task:RemoveMenu() + Task:RemoveMenu( MenuTime ) end end @@ -31366,20 +33485,6 @@ function MISSION:GetCommandCenter() return self.CommandCenter end ---- Sets the Assigned Task menu. --- @param #MISSION self --- @param Tasking.Task#TASK Task --- @param #string MenuText The menu text. --- @return #MISSION self -function MISSION:SetAssignedMenu( Task ) - - for _, Task in pairs( self.Tasks ) do - local Task = Task -- Tasking.Task#TASK - Task:RemoveMenu() - Task:SetAssignedMenu() - end - -end --- Removes a Task menu. -- @param #MISSION self @@ -31395,28 +33500,18 @@ end -- @param #MISSION self -- @param Wrapper.Group#GROUP TaskGroup -- @return Core.Menu#MENU_COALITION self -function MISSION:GetMissionMenu( TaskGroup ) +function MISSION:GetMenu( TaskGroup ) local CommandCenter = self:GetCommandCenter() - local CommandCenterMenu = CommandCenter.CommandCenterMenu + local CommandCenterMenu = CommandCenter:GetMenu() local MissionName = self:GetName() - - local TaskGroupName = TaskGroup:GetName() - local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ) + local MissionMenu = CommandCenterMenu:GetMenu( MissionName ) return MissionMenu end ---- Clears the mission menu for the coalition. --- @param #MISSION self --- @return #MISSION self -function MISSION:ClearMissionMenu() - self.MissionMenu:Remove() - self.MissionMenu = nil -end - --- Get the TASK identified by the TaskNumber from the Mission. This function is useful in GoalFunctions. -- @param #string TaskName The Name of the @{Task} within the @{Mission}. -- @return Tasking.Task#TASK The Task @@ -31487,76 +33582,44 @@ function MISSION:GetNextTaskID( Task ) return self.Tasks[TaskName].n end - - ---- old stuff - ---- Returns if a Mission has completed. --- @return bool +--- Is the @{Mission} **Completed**. +-- @param #MISSION self +-- @return #boolean function MISSION:IsCompleted() - self:F() - return self.MissionStatus == "ACCOMPLISHED" + return self:Is( "Completed" ) end ---- Set a Mission to completed. -function MISSION:Completed() - self:F() - self.MissionStatus = "ACCOMPLISHED" - self:StatusToClients() +--- Is the @{Mission} **Idle**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsIdle() + return self:Is( "Idle" ) end ---- Returns if a Mission is ongoing. --- treturn bool +--- Is the @{Mission} **Ongoing**. +-- @param #MISSION self +-- @return #boolean function MISSION:IsOngoing() - self:F() - return self.MissionStatus == "ONGOING" + return self:Is( "Ongoing" ) end ---- Set a Mission to ongoing. -function MISSION:Ongoing() - self:F() - self.MissionStatus = "ONGOING" - --self:StatusToClients() +--- Is the @{Mission} **Failed**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsFailed() + return self:Is( "Failed" ) end ---- Returns if a Mission is pending. --- treturn bool -function MISSION:IsPending() - self:F() - return self.MissionStatus == "PENDING" -end - ---- Set a Mission to pending. -function MISSION:Pending() - self:F() - self.MissionStatus = "PENDING" - self:StatusToClients() -end - ---- Returns if a Mission has failed. --- treturn bool -function MISSION:IsFailed() - self:F() - return self.MissionStatus == "FAILED" -end - ---- Set a Mission to failed. -function MISSION:Failed() - self:F() - self.MissionStatus = "FAILED" - self:StatusToClients() -end - ---- Send the status of the MISSION to all Clients. -function MISSION:StatusToClients() - self:F() - if self.MissionReportFlash then - for ClientID, Client in pairs( self._Clients ) do - Client:Message( self.MissionCoalition .. ' "' .. self.Name .. '": ' .. self.MissionStatus .. '! ( ' .. self.MissionPriority .. ' mission ) ', 10, "Mission Command: Mission Status") - end - end +--- Is the @{Mission} **Hold**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsHold() + return self:Is( "Hold" ) end +--- Validates if the Mission has a Group +-- @param #MISSION +-- @return #boolean true if the Mission has a Group. function MISSION:HasGroup( TaskGroup ) local Has = false @@ -31649,107 +33712,6 @@ function MISSION:ReportDetails() return Report:Text() end ---- Report the status of all MISSIONs to all active Clients. -function MISSION:ReportToAll() - self:F() - - local AlivePlayers = '' - for ClientID, Client in pairs( self._Clients ) do - if Client:GetDCSGroup() then - if Client:GetClientGroupDCSUnit() then - if Client:GetClientGroupDCSUnit():getLife() > 0.0 then - if AlivePlayers == '' then - AlivePlayers = ' Players: ' .. Client:GetClientGroupDCSUnit():getPlayerName() - else - AlivePlayers = AlivePlayers .. ' / ' .. Client:GetClientGroupDCSUnit():getPlayerName() - end - end - end - end - end - local Tasks = self:GetTasks() - local TaskText = "" - for TaskID, TaskData in pairs( Tasks ) do - TaskText = TaskText .. " - Task " .. TaskID .. ": " .. TaskData.Name .. ": " .. TaskData:GetGoalProgress() .. "\n" - end - MESSAGE:New( self.MissionCoalition .. ' "' .. self.Name .. '": ' .. self.MissionStatus .. ' ( ' .. self.MissionPriority .. ' mission )' .. AlivePlayers .. "\n" .. TaskText:gsub("\n$",""), 10, "Mission Command: Mission Report" ):ToAll() -end - - ---- Add a goal function to a MISSION. Goal functions are called when a @{TASK} within a mission has been completed. --- @param function GoalFunction is the function defined by the mission designer to evaluate whether a certain goal has been reached after a @{TASK} finishes within the @{MISSION}. A GoalFunction must accept 2 parameters: Mission, Client, which contains the current MISSION object and the current CLIENT object respectively. --- @usage --- PatriotActivation = { --- { "US SAM Patriot Zerti", false }, --- { "US SAM Patriot Zegduleti", false }, --- { "US SAM Patriot Gvleti", false } --- } --- --- function DeployPatriotTroopsGoal( Mission, Client ) --- --- --- -- Check if the cargo is all deployed for mission success. --- for CargoID, CargoData in pairs( Mission._Cargos ) do --- if Group.getByName( CargoData.CargoGroupName ) then --- CargoGroup = Group.getByName( CargoData.CargoGroupName ) --- if CargoGroup then --- -- Check if the cargo is ready to activate --- CurrentLandingZoneID = routines.IsUnitInZones( CargoGroup:getUnits()[1], Mission:GetTask( 2 ).LandingZones ) -- The second task is the Deploytask to measure mission success upon --- if CurrentLandingZoneID then --- if PatriotActivation[CurrentLandingZoneID][2] == false then --- -- Now check if this is a new Mission Task to be completed... --- trigger.action.setGroupAIOn( Group.getByName( PatriotActivation[CurrentLandingZoneID][1] ) ) --- PatriotActivation[CurrentLandingZoneID][2] = true --- MessageToBlue( "Mission Command: Message to all airborne units! The " .. PatriotActivation[CurrentLandingZoneID][1] .. " is armed. Our air defenses are now stronger.", 60, "BLUE/PatriotDefense" ) --- MessageToRed( "Mission Command: Our satellite systems are detecting additional NATO air defenses. To all airborne units: Take care!!!", 60, "RED/PatriotDefense" ) --- Mission:GetTask( 2 ):AddGoalCompletion( "Patriots activated", PatriotActivation[CurrentLandingZoneID][1], 1 ) -- Register Patriot activation as part of mission goal. --- end --- end --- end --- end --- end --- end --- --- local Mission = MISSIONSCHEDULER.AddMission( 'NATO Transport Troops', 'Operational', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.', 'NATO' ) --- Mission:AddGoalFunction( DeployPatriotTroopsGoal ) -function MISSION:AddGoalFunction( GoalFunction ) - self:F() - self.GoalFunction = GoalFunction -end - ---- Register a new @{CLIENT} to participate within the mission. --- @param CLIENT Client is the @{CLIENT} object. The object must have been instantiated with @{CLIENT:New}. --- @return CLIENT --- @usage --- Add a number of Client objects to the Mission. --- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*HOT-Deploy Troops 1', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*RAMP-Deploy Troops 3', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*HOT-Deploy Troops 2', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*RAMP-Deploy Troops 4', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() ) -function MISSION:AddClient( Client ) - self:F( { Client } ) - - local Valid = true - - if Valid then - self._Clients[Client.ClientName] = Client - end - - return Client -end - ---- Find a @{CLIENT} object within the @{MISSION} by its ClientName. --- @param CLIENT ClientName is a string defining the Client Group as defined within the ME. --- @return CLIENT --- @usage --- -- Seach for Client "Bomber" within the Mission. --- local BomberClient = Mission:FindClient( "Bomber" ) -function MISSION:FindClient( ClientName ) - self:F( { self._Clients[ClientName] } ) - return self._Clients[ClientName] -end - - --- Get all the TASKs from the Mission. This function is useful in GoalFunctions. -- @return {TASK,...} Structure of TASKS with the @{TASK} number as the key. -- @usage @@ -31763,333 +33725,6 @@ function MISSION:GetTasks() end ---[[ - _TransportExecuteStage: Defines the different stages of Transport unload/load execution. This table is internal and is used to control the validity of Transport load/unload timing. - - - _TransportExecuteStage.EXECUTING - - _TransportExecuteStage.SUCCESS - - _TransportExecuteStage.FAILED - ---]] -_TransportExecuteStage = { - NONE = 0, - EXECUTING = 1, - SUCCESS = 2, - FAILED = 3 -} - - ---- The MISSIONSCHEDULER is an OBJECT and is the main scheduler of ALL active MISSIONs registered within this scheduler. It's workings are considered internal and is automatically created when the Mission.lua file is included. --- @type MISSIONSCHEDULER --- @field #MISSIONSCHEDULER.MISSIONS Missions -MISSIONSCHEDULER = { - Missions = {}, - MissionCount = 0, - TimeIntervalCount = 0, - TimeIntervalShow = 150, - TimeSeconds = 14400, - TimeShow = 5 -} - ---- @type MISSIONSCHEDULER.MISSIONS --- @list <#MISSION> Mission - ---- This is the main MISSIONSCHEDULER Scheduler function. It is considered internal and is automatically created when the Mission.lua file is included. -function MISSIONSCHEDULER.Scheduler() - - - -- loop through the missions in the TransportTasks - for MissionName, MissionData in pairs( MISSIONSCHEDULER.Missions ) do - - local Mission = MissionData -- #MISSION - - if not Mission:IsCompleted() then - - -- This flag will monitor if for this mission, there are clients alive. If this flag is still false at the end of the loop, the mission status will be set to Pending (if not Failed or Completed). - local ClientsAlive = false - - for ClientID, ClientData in pairs( Mission._Clients ) do - - local Client = ClientData -- Wrapper.Client#CLIENT - - if Client:IsAlive() then - - -- There is at least one Client that is alive... So the Mission status is set to Ongoing. - ClientsAlive = true - - -- If this Client was not registered as Alive before: - -- 1. We register the Client as Alive. - -- 2. We initialize the Client Tasks and make a link to the original Mission Task. - -- 3. We initialize the Cargos. - -- 4. We flag the Mission as Ongoing. - if not Client.ClientAlive then - Client.ClientAlive = true - Client.ClientBriefingShown = false - for TaskNumber, Task in pairs( Mission._Tasks ) do - -- Note that this a deepCopy. Each client must have their own Tasks with own Stages!!! - Client._Tasks[TaskNumber] = routines.utils.deepCopy( Mission._Tasks[TaskNumber] ) - -- Each MissionTask must point to the original Mission. - Client._Tasks[TaskNumber].MissionTask = Mission._Tasks[TaskNumber] - Client._Tasks[TaskNumber].Cargos = Mission._Tasks[TaskNumber].Cargos - Client._Tasks[TaskNumber].LandingZones = Mission._Tasks[TaskNumber].LandingZones - end - - Mission:Ongoing() - end - - - -- For each Client, check for each Task the state and evolve the mission. - -- This flag will indicate if the Task of the Client is Complete. - local TaskComplete = false - - for TaskNumber, Task in pairs( Client._Tasks ) do - - if not Task.Stage then - Task:SetStage( 1 ) - end - - - local TransportTime = timer.getTime() - - if not Task:IsDone() then - - if Task:Goal() then - Task:ShowGoalProgress( Mission, Client ) - end - - --env.info( 'Scheduler: Mission = ' .. Mission.Name .. ' / Client = ' .. Client.ClientName .. ' / Task = ' .. Task.Name .. ' / Stage = ' .. Task.ActiveStage .. ' - ' .. Task.Stage.Name .. ' - ' .. Task.Stage.StageType ) - - -- Action - if Task:StageExecute() then - Task.Stage:Execute( Mission, Client, Task ) - end - - -- Wait until execution is finished - if Task.ExecuteStage == _TransportExecuteStage.EXECUTING then - Task.Stage:Executing( Mission, Client, Task ) - end - - -- Validate completion or reverse to earlier stage - if Task.Time + Task.Stage.WaitTime <= TransportTime then - Task:SetStage( Task.Stage:Validate( Mission, Client, Task ) ) - end - - if Task:IsDone() then - --env.info( 'Scheduler: Mission '.. Mission.Name .. ' Task ' .. Task.Name .. ' Stage ' .. Task.Stage.Name .. ' done. TaskComplete = ' .. string.format ( "%s", TaskComplete and "true" or "false" ) ) - TaskComplete = true -- when a task is not yet completed, a mission cannot be completed - - else - -- break only if this task is not yet done, so that future task are not yet activated. - TaskComplete = false -- when a task is not yet completed, a mission cannot be completed - --env.info( 'Scheduler: Mission "'.. Mission.Name .. '" Task "' .. Task.Name .. '" Stage "' .. Task.Stage.Name .. '" break. TaskComplete = ' .. string.format ( "%s", TaskComplete and "true" or "false" ) ) - break - end - - if TaskComplete then - - if Mission.GoalFunction ~= nil then - Mission.GoalFunction( Mission, Client ) - end - if MISSIONSCHEDULER.Scoring then - MISSIONSCHEDULER.Scoring:_AddMissionTaskScore( Client:GetClientGroupDCSUnit(), Mission.Name, 25 ) - end - --- if not Mission:IsCompleted() then --- end - end - end - end - - local MissionComplete = true - for TaskNumber, Task in pairs( Mission._Tasks ) do - if Task:Goal() then --- Task:ShowGoalProgress( Mission, Client ) - if Task:IsGoalReached() then - else - MissionComplete = false - end - else - MissionComplete = false -- If there is no goal, the mission should never be ended. The goal status will be set somewhere else. - end - end - - if MissionComplete then - Mission:Completed() - if MISSIONSCHEDULER.Scoring then - MISSIONSCHEDULER.Scoring:_AddMissionScore( Mission.Name, 100 ) - end - else - if TaskComplete then - -- Reset for new tasking of active client - Client.ClientAlive = false -- Reset the client tasks. - end - end - - - else - if Client.ClientAlive then - env.info( 'Scheduler: Client "' .. Client.ClientName .. '" is inactive.' ) - Client.ClientAlive = false - - -- This is tricky. If we sanitize Client._Tasks before sanitizing Client._Tasks[TaskNumber].MissionTask, then the original MissionTask will be sanitized, and will be lost within the garbage collector. - -- So first sanitize Client._Tasks[TaskNumber].MissionTask, after that, sanitize only the whole _Tasks structure... - --Client._Tasks[TaskNumber].MissionTask = nil - --Client._Tasks = nil - end - end - end - - -- If all Clients of this Mission are not activated, then the Mission status needs to be put back into Pending status. - -- But only if the Mission was Ongoing. In case the Mission is Completed or Failed, the Mission status may not be changed. In these cases, this will be the last run of this Mission in the Scheduler. - if ClientsAlive == false then - if Mission:IsOngoing() then - -- Mission status back to pending... - Mission:Pending() - end - end - end - - Mission:StatusToClients() - - if Mission:ReportTrigger() then - Mission:ReportToAll() - end - end - - return true -end - ---- Start the MISSIONSCHEDULER. -function MISSIONSCHEDULER.Start() - if MISSIONSCHEDULER ~= nil then - --MISSIONSCHEDULER.SchedulerId = routines.scheduleFunction( MISSIONSCHEDULER.Scheduler, { }, 0, 2 ) - MISSIONSCHEDULER.SchedulerId = SCHEDULER:New( nil, MISSIONSCHEDULER.Scheduler, { }, 0, 2 ) - end -end - ---- Stop the MISSIONSCHEDULER. -function MISSIONSCHEDULER.Stop() - if MISSIONSCHEDULER.SchedulerId then - routines.removeFunction(MISSIONSCHEDULER.SchedulerId) - MISSIONSCHEDULER.SchedulerId = nil - end -end - ---- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. --- @param Mission is the MISSION object instantiated by @{MISSION:New}. --- @return MISSION --- @usage --- -- Declare a mission. --- Mission = MISSION:New( 'Russia Transport Troops SA-6', --- 'Operational', --- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', --- 'Russia' ) --- MISSIONSCHEDULER:AddMission( Mission ) -function MISSIONSCHEDULER.AddMission( Mission ) - MISSIONSCHEDULER.Missions[Mission.Name] = Mission - MISSIONSCHEDULER.MissionCount = MISSIONSCHEDULER.MissionCount + 1 - -- Add an overall AI Client for the AI tasks... This AI Client will facilitate the Events in the background for each Task. - --MissionAdd:AddClient( CLIENT:Register( 'AI' ) ) - - return Mission -end - ---- Remove a MISSION from the MISSIONSCHEDULER. --- @param MissionName is the name of the MISSION given at declaration using @{AddMission}. --- @usage --- -- Declare a mission. --- Mission = MISSION:New( 'Russia Transport Troops SA-6', --- 'Operational', --- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', --- 'Russia' ) --- MISSIONSCHEDULER:AddMission( Mission ) --- --- -- Now remove the Mission. --- MISSIONSCHEDULER:RemoveMission( 'Russia Transport Troops SA-6' ) -function MISSIONSCHEDULER.RemoveMission( MissionName ) - MISSIONSCHEDULER.Missions[MissionName] = nil - MISSIONSCHEDULER.MissionCount = MISSIONSCHEDULER.MissionCount - 1 -end - ---- Find a MISSION within the MISSIONSCHEDULER. --- @param MissionName is the name of the MISSION given at declaration using @{AddMission}. --- @return MISSION --- @usage --- -- Declare a mission. --- Mission = MISSION:New( 'Russia Transport Troops SA-6', --- 'Operational', --- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', --- 'Russia' ) --- MISSIONSCHEDULER:AddMission( Mission ) --- --- -- Now find the Mission. --- MissionFind = MISSIONSCHEDULER:FindMission( 'Russia Transport Troops SA-6' ) -function MISSIONSCHEDULER.FindMission( MissionName ) - return MISSIONSCHEDULER.Missions[MissionName] -end - --- Internal function used by the MISSIONSCHEDULER menu. -function MISSIONSCHEDULER.ReportMissionsShow( ) - for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do - Mission.MissionReportShow = true - Mission.MissionReportFlash = false - end -end - --- Internal function used by the MISSIONSCHEDULER menu. -function MISSIONSCHEDULER.ReportMissionsFlash( TimeInterval ) - local Count = 0 - for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do - Mission.MissionReportShow = false - Mission.MissionReportFlash = true - Mission.MissionReportTrigger = timer.getTime() + Count * TimeInterval - Mission.MissionTimeInterval = MISSIONSCHEDULER.MissionCount * TimeInterval - env.info( "TimeInterval = " .. Mission.MissionTimeInterval ) - Count = Count + 1 - end -end - --- Internal function used by the MISSIONSCHEDULER menu. -function MISSIONSCHEDULER.ReportMissionsHide( Prm ) - for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do - Mission.MissionReportShow = false - Mission.MissionReportFlash = false - end -end - ---- Enables a MENU option in the communications menu under F10 to control the status of the active missions. --- This function should be called only once when starting the MISSIONSCHEDULER. -function MISSIONSCHEDULER.ReportMenu() - local ReportMenu = SUBMENU:New( 'Status' ) - local ReportMenuShow = COMMANDMENU:New( 'Show Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsShow, 0 ) - local ReportMenuFlash = COMMANDMENU:New('Flash Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsFlash, 120 ) - local ReportMenuHide = COMMANDMENU:New( 'Hide Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsHide, 0 ) -end - ---- Show the remaining mission time. -function MISSIONSCHEDULER:TimeShow() - self.TimeIntervalCount = self.TimeIntervalCount + 1 - if self.TimeIntervalCount >= self.TimeTriggerShow then - local TimeMsg = string.format("%00d", ( self.TimeSeconds / 60 ) - ( timer.getTime() / 60 )) .. ' minutes left until mission reload.' - MESSAGE:New( TimeMsg, self.TimeShow, "Mission time" ):ToAll() - self.TimeIntervalCount = 0 - end -end - -function MISSIONSCHEDULER:Time( TimeSeconds, TimeIntervalShow, TimeShow ) - - self.TimeIntervalCount = 0 - self.TimeSeconds = TimeSeconds - self.TimeIntervalShow = TimeIntervalShow - self.TimeShow = TimeShow -end - ---- Adds a mission scoring to the game. -function MISSIONSCHEDULER:Scoring( Scoring ) - - self.Scoring = Scoring -end - --- This module contains the TASK class. -- -- 1) @{#TASK} class, extends @{Base#BASE} @@ -32277,7 +33912,6 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() - Mission:AddTask( self ) return self end @@ -32285,9 +33919,13 @@ end --- Get the Task FSM Process Template -- @param #TASK self -- @return Core.Fsm#FSM_PROCESS -function TASK:GetUnitProcess() +function TASK:GetUnitProcess( TaskUnit ) - return self.FsmTemplate + if TaskUnit then + return self:GetStateMachine( TaskUnit ) + else + return self.FsmTemplate + end end --- Sets the Task FSM Process Template @@ -32320,7 +33958,7 @@ function TASK:JoinUnit( PlayerUnit, PlayerGroup ) -- If the PlayerGroup is not assigned to the Task, the menu needs to be set. In that case, the PlayerUnit will become the GroupPlayer leader. if self:IsStatePlanned() or self:IsStateReplanned() then self:SetMenuForGroup( PlayerGroup ) - self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() ) + --self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() ) end if self:IsStateAssigned() then local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) @@ -32362,10 +34000,11 @@ function TASK:AbortUnit( PlayerUnit ) self:MessageToGroups( PlayerUnit:GetPlayerName() .. " aborted Task " .. self:GetName() ) self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) if #PlayerGroup:GetUnits() == 1 then + self:UnAssignFromGroup( PlayerGroup ) PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) self:RemoveMenuForGroup( PlayerGroup ) end - self:PlayerAborted( PlayerUnit ) + self:Abort() end end end @@ -32431,7 +34070,7 @@ end ---- Assign the @{Task}to a @{Group}. +--- Assign the @{Task} to a @{Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @return #TASK @@ -32442,7 +34081,11 @@ function TASK:AssignToGroup( TaskGroup ) TaskGroup:SetState( TaskGroup, "Assigned", self ) - self:RemoveMenuForGroup( TaskGroup ) + local Mission = self:GetMission() + local MissionMenu = Mission:GetMenu( TaskGroup ) + MissionMenu:RemoveSubMenus() + + --self:RemoveMenuForGroup( TaskGroup ) self:SetAssignedMenuForGroup( TaskGroup ) local TaskUnits = TaskGroup:GetUnits() @@ -32482,6 +34125,7 @@ function TASK:AssignToUnit( TaskUnit ) self:E({"Address FsmUnit", tostring( FsmUnit ) } ) FsmUnit:SetStartState( "Planned" ) + FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow. return self @@ -32492,7 +34136,7 @@ end -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self function TASK:UnAssignFromUnit( TaskUnit ) - self:F( TaskUnit ) + self:F( TaskUnit:GetName() ) self:RemoveStateMachine( TaskUnit ) @@ -32539,28 +34183,37 @@ function TASK:SendBriefingToAssignedGroups() end ---- Assign the @{Task} from the @{Group}s. +--- UnAssign the @{Task} from the @{Group}s. -- @param #TASK self function TASK:UnAssignFromGroups() self:F2() for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do + self:UnAssignFromGroup( TaskGroup ) + end +end - TaskGroup:SetState( TaskGroup, "Assigned", nil ) +--- UnAssign the @{Task} from a @{Group}. +-- @param #TASK self +function TASK:UnAssignFromGroup( TaskGroup ) + self:F2( { TaskGroup } ) + + TaskGroup:SetState( TaskGroup, "Assigned", nil ) - self:RemoveMenuForGroup( TaskGroup ) + self:RemoveAssignedMenuForGroup( TaskGroup ) - local TaskUnits = TaskGroup:GetUnits() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - if PlayerName ~= nil or PlayerName ~= "" then - self:UnAssignFromUnit( TaskUnit ) - end + local TaskUnits = TaskGroup:GetUnits() + for UnitID, UnitData in pairs( TaskUnits ) do + local TaskUnit = UnitData -- Wrapper.Unit#UNIT + local PlayerName = TaskUnit:GetPlayerName() + if PlayerName ~= nil or PlayerName ~= "" then + self:UnAssignFromUnit( TaskUnit ) end end end + + --- Returns if the @{Task} is assigned to the Group. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup @@ -32571,10 +34224,12 @@ function TASK:IsAssignedToGroup( TaskGroup ) if self:IsStateAssigned() then if TaskGroup:GetState( TaskGroup, "Assigned" ) == self then + self:T( { "Task is assigned to:", TaskGroup:GetName() } ) return true end end + self:T( { "Task is not assigned to:", TaskGroup:GetName() } ) return false end @@ -32603,37 +34258,36 @@ end --- Set the menu options of the @{Task} to all the groups in the SetGroup. -- @param #TASK self -function TASK:SetMenu() +-- @param #number MenuTime +-- @return #TASK +function TASK:SetMenu( MenuTime ) self:F() self.SetGroup:Flush() - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if self:IsStatePlanned() or self:IsStateReplanned() then - self:SetMenuForGroup( TaskGroup ) + for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do + local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP + if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then + if self:IsStatePlanned() or self:IsStateReplanned() then + self:SetMenuForGroup( TaskGroup, MenuTime ) + end end end end ---- Remove the menu options of the @{Task} to all the groups in the SetGroup. --- @param #TASK self --- @return #TASK self -function TASK:RemoveMenu() - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - self:RemoveMenuForGroup( TaskGroup ) - end -end - --- Set the Menu for a Group -- @param #TASK self -function TASK:SetMenuForGroup( TaskGroup ) +-- @param #number MenuTime +-- @return #TASK +function TASK:SetMenuForGroup( TaskGroup, MenuTime ) - if not self:IsAssignedToGroup( TaskGroup ) then - self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName() ) + if not TaskGroup:GetState( TaskGroup, "Assigned" ) then + self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName(), MenuTime ) else - self:SetAssignedMenuForGroup( TaskGroup ) + if not self:IsAssignedToGroup( TaskGroup ) then + self:SetAssignedMenuForGroup( TaskGroup, MenuTime ) + end end end @@ -32642,16 +34296,24 @@ end -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @param #string MenuText The menu text. +-- @param #number MenuTime -- @return #TASK self -function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText ) +function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText, MenuTime ) self:E( TaskGroup:GetName() ) local Mission = self:GetMission() - local MissionMenu = Mission:GetMissionMenu( TaskGroup ) + local MissionName = Mission:GetName() + local CommandCenter = Mission:GetCommandCenter() + local CommandCenterMenu = CommandCenter:GetMenu() + + local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime ) + + + local MissionMenu = Mission:GetMenu( TaskGroup ) local TaskType = self:GetType() - local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ) + local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ):SetTime( MenuTime ) + local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ):SetTime( MenuTime ):SetRemoveParent( true ) return self end @@ -32659,32 +34321,84 @@ end --- Set the assigned menu options of the @{Task}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime -- @return #TASK self -function TASK:SetAssignedMenuForGroup( TaskGroup ) +function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) self:E( TaskGroup:GetName() ) local Mission = self:GetMission() - local MissionMenu = Mission:GetMissionMenu( TaskGroup ) + local MissionMenu = Mission:GetMenu( TaskGroup ) self:E( { MissionMenu = MissionMenu } ) - local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, { self = self, TaskGroup = TaskGroup } ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, { self = self, TaskGroup = TaskGroup } ) + local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ) + local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ) return self end +--- Remove the menu options of the @{Task} to all the groups in the SetGroup. +-- @param #TASK self +-- @param #number MenuTime +-- @return #TASK +function TASK:RemoveMenu( MenuTime ) + self:F() + + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + local TaskGroup = TaskGroup -- Wrapper.Group#GROUP + if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then + if not self:IsAssignedToGroup( TaskGroup ) then + self:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) + end + end + end +end + + --- Remove the menu option of the @{Task} for a @{Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime -- @return #TASK self -function TASK:RemoveMenuForGroup( TaskGroup ) +function TASK:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) + self:F() local Mission = self:GetMission() local MissionName = Mission:GetName() + + local MissionMenu = Mission:GetMenu( TaskGroup ) + + if MissionMenu then + local TaskType = self:GetType() + local TypeMenu = MissionMenu:GetMenu( TaskType ) + + if TypeMenu then + local TaskMenu = TypeMenu:GetMenu( self:GetTaskName() ) + if TaskMenu then + TaskMenu:Remove( MenuTime ) + end + end + end + +end - local MissionMenu = Mission:GetMissionMenu( TaskGroup ) - MissionMenu:Remove() +--- Remove the assigned menu option of the @{Task} for a @{Group}. +-- @param #TASK self +-- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime +-- @return #TASK self +function TASK:RemoveAssignedMenuForGroup( TaskGroup ) + self:F() + + local Mission = self:GetMission() + local MissionName = Mission:GetName() + + local MissionMenu = Mission:GetMenu( TaskGroup ) + + if MissionMenu then + MissionMenu:RemoveSubMenus() + end + end function TASK.MenuAssignToGroup( MenuParam ) @@ -32697,19 +34411,21 @@ function TASK.MenuAssignToGroup( MenuParam ) self:AssignToGroup( TaskGroup ) end -function TASK.MenuTaskStatus( MenuParam ) +--- Report the task status. +-- @param #TASK self +function TASK:MenuTaskStatus( TaskGroup ) - local self = MenuParam.self - local TaskGroup = MenuParam.TaskGroup + local ReportText = self:ReportDetails() - --self:AssignToGroup( TaskGroup ) + self:T( ReportText ) + self:GetMission():GetCommandCenter():MessageToGroup( ReportText, TaskGroup ) + end -function TASK.MenuTaskAbort( MenuParam ) +--- Report the task status. +-- @param #TASK self +function TASK:MenuTaskAbort( TaskGroup ) - local self = MenuParam.self - local TaskGroup = MenuParam.TaskGroup - self:Abort() end @@ -32754,15 +34470,26 @@ end --- Add a FiniteStateMachine to @{Task} with key Task@{Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit +-- @param Core.Fsm#FSM_PROCESS Fsm -- @return #TASK self function TASK:SetStateMachine( TaskUnit, Fsm ) - self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) + self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil, Fsm:GetClassNameAndID() } ) self.Fsm[TaskUnit] = Fsm return Fsm end +--- Gets the FiniteStateMachine of @{Task} with key Task@{Unit} +-- @param #TASK self +-- @param Wrapper.Unit#UNIT TaskUnit +-- @return Core.Fsm#FSM_PROCESS +function TASK:GetStateMachine( TaskUnit ) + self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) + + return self.Fsm[TaskUnit] +end + --- Remove FiniteStateMachines from @{Task} with key Task@{Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit @@ -32770,9 +34497,15 @@ end function TASK:RemoveStateMachine( TaskUnit ) self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) + self:E( self.Fsm ) + for TaskUnitT, Fsm in pairs( self.Fsm ) do + self:E( TaskUnitT ) + end + self.Fsm[TaskUnit] = nil + collectgarbage() - self:T( "Garbage Collected, Processes should be finalized now ...") + self:E( "Garbage Collected, Processes should be finalized now ...") end --- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task} @@ -32887,6 +34620,32 @@ function TASK:IsStatePlanned() return self:Is( "Planned" ) end +--- Sets a @{Task} to status **Aborted**. +-- @param #TASK self +function TASK:StateAborted() + self:SetState( self, "State", "Aborted" ) + return self +end + +--- Is the @{Task} status **Aborted**. +-- @param #TASK self +function TASK:IsStateAborted() + return self:Is( "Aborted" ) +end + +--- Sets a @{Task} to status **Cancelled**. +-- @param #TASK self +function TASK:StateCancelled() + self:SetState( self, "State", "Cancelled" ) + return self +end + +--- Is the @{Task} status **Cancelled**. +-- @param #TASK self +function TASK:IsStateCancelled() + return self:Is( "Cancelled" ) +end + --- Sets a @{Task} to status **Assigned**. -- @param #TASK self function TASK:StateAssigned() @@ -32949,11 +34708,17 @@ end -- @param #string Event -- @param #string From -- @param #string To -function TASK:onenterAssigned( From, Event, To ) +function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName ) - self:E("Task Assigned") + self:E( { "Task Assigned", self.Dispatcher } ) self:MessageToGroups( "Task " .. self:GetName() .. " has been assigned to your group." ) + + if self.Dispatcher then + self:E( "Firing Assign event " ) + self.Dispatcher:Assign( self, PlayerUnit, PlayerName ) + end + self:GetMission():__Start( 1 ) end @@ -32967,7 +34732,7 @@ function TASK:onenterSuccess( From, Event, To ) self:E( "Task Success" ) - self:MessageToGroups( "Task " .. self:GetName() .. " is successful! Good job!" ) + self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is successful! Good job!" ) self:UnAssignFromGroups() self:GetMission():__Complete( 1 ) @@ -33065,6 +34830,18 @@ function TASK:onbeforeTimeOut( From, Event, To ) return false end +do -- Dispatcher + + --- Set dispatcher of a task + -- @param #TASK self + -- @param Tasking.DetectionManager#DETECTION_MANAGER Dispatcher + -- @return #TASK + function TASK:SetDispatcher( Dispatcher ) + self.Dispatcher = Dispatcher + end + +end + do -- Reporting --- Create a summary report of the Task. @@ -33101,24 +34878,19 @@ function TASK:ReportDetails() -- Determine the status of the Task. local State = self:GetState() - -- Loop each Unit active in the Task, and find Player Names. local PlayerNames = {} - for PlayerGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do - local Player = PlayerGroup -- Wrapper.Group#GROUP - for PlayerUnitID, PlayerUnit in pairs( PlayerGroup:GetUnits() ) do - local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT - if PlayerUnit and PlayerUnit:IsAlive() then - local PlayerName = PlayerUnit:GetPlayerName() - PlayerNames[#PlayerNames+1] = PlayerName - end + local PlayerReport = REPORT:New( " - Players:" ) + for PlayerGroupID, PlayerGroupData in pairs( self:GetGroups():GetSet() ) do + local PlayerGroup = PlayerGroupData -- Wrapper.Group#GROUP + PlayerNames = PlayerGroup:GetPlayerNames() + if PlayerNames then + PlayerReport:Add( " -- Group " .. PlayerGroup:GetCallsign() .. ": " .. table.concat( PlayerNames, ", " ) ) end - local PlayerNameText = table.concat( PlayerNames, ", " ) - Report:Add( "Task " .. Name .. " - State '" .. State .. "' - Players " .. PlayerNameText ) end -- Loop each Process in the Task, and find Reporting Details. - + Report:Add( string.format( " - Task %s\n -- State '%s'\n%s", Name, State, PlayerReport:Text() ) ) return Report:Text() end @@ -33128,7 +34900,7 @@ end -- Reporting -- -- === -- --- 1) @{DetectionManager#DETECTION_MANAGER} class, extends @{Base#BASE} +-- 1) @{DetectionManager#DETECTION_MANAGER} class, extends @{Fsm#FSM} -- ==================================================================== -- The @{DetectionManager#DETECTION_MANAGER} class defines the core functions to report detected objects to groups. -- Reportings can be done in several manners, and it is up to the derived classes if DETECTION_MANAGER to model the reporting behaviour. @@ -33160,23 +34932,6 @@ end -- Reporting -- ------------------------------- -- The @{DetectionManager#DETECTION_REPORTING.New}() method creates a new DETECTION_REPORTING instance. -- --- === --- --- 3) @{#DETECTION_DISPATCHER} class, extends @{#DETECTION_MANAGER} --- ================================================================ --- The @{#DETECTION_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of FAC (groups). --- The FAC will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. --- Find a summary below describing for which situation a task type is created: --- --- * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter. --- * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter. --- * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars. --- --- Other task types will follow... --- --- 3.1) DETECTION_DISPATCHER constructor: --- -------------------------------------- --- The @{#DETECTION_DISPATCHER.New}() method creates a new DETECTION_DISPATCHER instance. -- -- === -- @@ -33191,7 +34946,7 @@ do -- DETECTION MANAGER -- @type DETECTION_MANAGER -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. - -- @extends Base#BASE + -- @extends Core.Fsm#FSM DETECTION_MANAGER = { ClassName = "DETECTION_MANAGER", SetGroup = nil, @@ -33206,17 +34961,37 @@ do -- DETECTION MANAGER function DETECTION_MANAGER:New( SetGroup, Detection ) -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) -- Functional.Detection#DETECTION_MANAGER + local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_MANAGER self.SetGroup = SetGroup self.Detection = Detection + self:SetStartState( "Stopped" ) + self:AddTransition( "Stopped", "Start", "Started" ) + self:AddTransition( "Started", "Stop", "Stopped" ) + self:AddTransition( "Started", "Report", "Started" ) + self:SetReportInterval( 30 ) self:SetReportDisplayTime( 25 ) - + + Detection:__Start( 1 ) + return self end + function DETECTION_MANAGER:onafterStart( From, Event, To ) + self:Report() + end + + function DETECTION_MANAGER:onafterReport( From, Event, To ) + + self:E( "onafterReport" ) + + self:__Report( -self._ReportInterval ) + + self:ProcessDetected( self.Detection ) + end + --- Set the reporting time interval. -- @param #DETECTION_MANAGER self -- @param #number ReportInterval The interval in seconds when a report needs to be done. @@ -33247,51 +35022,14 @@ do -- DETECTION MANAGER return self._ReportDisplayTime end - - --- Reports the detected items to the @{Set#SET_GROUP}. -- @param #DETECTION_MANAGER self -- @param Functional.Detection#DETECTION_BASE Detection -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:ReportDetected( Detection ) - self:F2() + function DETECTION_MANAGER:ProcessDetected( Detection ) + self:E() end - - --- Schedule the FAC reporting. - -- @param #DETECTION_MANAGER self - -- @param #number DelayTime The delay in seconds to wait the reporting. - -- @param #number ReportInterval The repeat interval in seconds for the reporting to happen repeatedly. - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:Schedule( DelayTime, ReportInterval ) - self:F2() - - self._ScheduleDelayTime = DelayTime - - self:SetReportInterval( ReportInterval ) - - self.FacScheduler = SCHEDULER:New(self, self._FacScheduler, { self, "DetectionManager" }, self._ScheduleDelayTime, self._ReportInterval ) - return self - end - - --- Report the detected @{Unit#UNIT}s detected within the @{Detection#DETECTION_BASE} object to the @{Set#SET_GROUP}s. - -- @param #DETECTION_MANAGER self - function DETECTION_MANAGER:_FacScheduler( SchedulerName ) - self:F2( { SchedulerName } ) - - return self:ProcessDetected( self.Detection ) - --- self.SetGroup:ForEachGroup( --- --- @param Wrapper.Group#GROUP Group --- function( Group ) --- if Group:IsAlive() then --- return self:ProcessDetected( self.Detection ) --- end --- end --- ) - --- return true - end end @@ -33376,52 +35114,108 @@ do -- DETECTION_REPORTING end -do -- DETECTION_DISPATCHER +--- **Tasking** - The TASK_A2G_DISPATCHER creates and manages player TASK_A2G tasks based on detected targets. +-- +-- === +-- +-- # 1) @{#TASK_A2G_DISPATCHER} class, extends @{#DETECTION_MANAGER} +-- +-- The @{#TASK_A2G_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of FAC (groups). +-- The FAC will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. +-- Find a summary below describing for which situation a task type is created: +-- +-- * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter. +-- * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter. +-- * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars. +-- +-- Other task types will follow... +-- +-- 3.1) TASK_A2G_DISPATCHER constructor: +-- -------------------------------------- +-- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK_A2G_DISPATCHER instance. +-- +-- === +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-03-09: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module Task_A2G_Dispatcher - --- DETECTION_DISPATCHER class. - -- @type DETECTION_DISPATCHER +do -- TASK_A2G_DISPATCHER + + --- TASK_A2G_DISPATCHER class. + -- @type TASK_A2G_DISPATCHER -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. -- @field Tasking.Mission#MISSION Mission - -- @field Wrapper.Group#GROUP CommandCenter -- @extends Tasking.DetectionManager#DETECTION_MANAGER - DETECTION_DISPATCHER = { - ClassName = "DETECTION_DISPATCHER", + TASK_A2G_DISPATCHER = { + ClassName = "TASK_A2G_DISPATCHER", Mission = nil, - CommandCenter = nil, Detection = nil, } - --- DETECTION_DISPATCHER constructor. - -- @param #DETECTION_DISPATCHER self - -- @param Set#SET_GROUP SetGroup - -- @param Functional.Detection#DETECTION_BASE Detection - -- @return #DETECTION_DISPATCHER self - function DETECTION_DISPATCHER:New( Mission, CommandCenter, SetGroup, Detection ) + --- TASK_A2G_DISPATCHER constructor. + -- @param #TASK_A2G_DISPATCHER self + -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. + -- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. + -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. + -- @return #TASK_A2G_DISPATCHER self + function TASK_A2G_DISPATCHER:New( Mission, SetGroup, Detection ) -- Inherits from DETECTION_MANAGER - local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #DETECTION_DISPATCHER + local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER self.Detection = Detection - self.CommandCenter = CommandCenter self.Mission = Mission - self:Schedule( 30 ) + self:AddTransition( "Started", "Assign", "Started" ) + + --- OnAfter Transition Handler for Event Assign. + -- @function [parent=#TASK_A2G_DISPATCHER] OnAfterAssign + -- @param #TASK_A2G_DISPATCHER self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Tasking.Task_A2G#TASK_A2G Task + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param #string PlayerName + + self:__Start( 5 ) + return self end --- Creates a SEAD task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @return Set#SET_UNIT TargetSetUnit: The target set of units. -- @return #nil If there are no targets to be set. - function DETECTION_DISPATCHER:EvaluateSEAD( DetectedArea ) - self:F( { DetectedArea.AreaID } ) + function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem ) + self:F( { DetectedItem.ItemID } ) - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone -- Determine if the set has radar targets. If it does, construct a SEAD task. local RadarCount = DetectedSet:HasSEAD() @@ -33441,19 +35235,19 @@ do -- DETECTION_DISPATCHER end --- Creates a CAS task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateCAS( DetectedArea ) - self:F( { DetectedArea.AreaID } ) + function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem ) + self:F( { DetectedItem.ItemID } ) - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone -- Determine if the set has radar targets. If it does, construct a SEAD task. local GroundUnitCount = DetectedSet:HasGroundUnits() - local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedArea ) + local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) if GroundUnitCount > 0 and FriendliesNearBy == true then @@ -33469,19 +35263,19 @@ do -- DETECTION_DISPATCHER end --- Creates a BAI task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateBAI( DetectedArea, FriendlyCoalition ) - self:F( { DetectedArea.AreaID } ) + function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition ) + self:F( { DetectedItem.ItemID } ) - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone -- Determine if the set has radar targets. If it does, construct a SEAD task. local GroundUnitCount = DetectedSet:HasGroundUnits() - local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedArea ) + local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) if GroundUnitCount > 0 and FriendliesNearBy == false then @@ -33497,16 +35291,16 @@ do -- DETECTION_DISPATCHER end --- Evaluates the removal of the Task from the Mission. - -- Can only occur when the DetectedArea is Changed AND the state of the Task is "Planned". - -- @param #DETECTION_DISPATCHER self + -- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned". + -- @param #TASK_A2G_DISPATCHER self -- @param Tasking.Mission#MISSION Mission -- @param Tasking.Task#TASK Task - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedArea ) + function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedItem ) if Task then - if Task:IsStatePlanned() and DetectedArea.Changed == true then + if Task:IsStatePlanned() and DetectedItem.Changed == true then self:E( "Removing Tasking: " .. Task:GetTaskName() ) Task = Mission:RemoveTask( Task ) end @@ -33517,140 +35311,463 @@ do -- DETECTION_DISPATCHER --- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS Detection The detection created by the @{Detection#DETECTION_AREAS} object. + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. - function DETECTION_DISPATCHER:ProcessDetected( Detection ) - self:F2() + function TASK_A2G_DISPATCHER:ProcessDetected( Detection ) + self:E() local AreaMsg = {} local TaskMsg = {} local ChangeMsg = {} local Mission = self.Mission + local ReportSEAD = REPORT:New( "- SEAD Tasks:") + local ReportCAS = REPORT:New( "- CAS Tasks:") + local ReportBAI = REPORT:New( "- BAI Tasks:") + local ReportChanges = REPORT:New( " - Changes:" ) --- First we need to the detected targets. - for DetectedAreaID, DetectedAreaData in ipairs( Detection:GetDetectedAreas() ) do + for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do - local DetectedArea = DetectedAreaData -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone - self:E( { "Targets in DetectedArea", DetectedArea.AreaID, DetectedSet:Count(), tostring( DetectedArea ) } ) + local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set -- Functional.Detection#DETECTION_BASE.DetectedSet + local DetectedZone = DetectedItem.Zone + self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) DetectedSet:Flush() - local AreaID = DetectedArea.AreaID + local ItemID = DetectedItem.ItemID -- Evaluate SEAD Tasking - local SEADTask = Mission:GetTask( "SEAD." .. AreaID ) - SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedArea ) + local SEADTask = Mission:GetTask( string.format( "SEAD.%03d", ItemID ) ) + SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedItem ) if not SEADTask then - local TargetSetUnit = self:EvaluateSEAD( DetectedArea ) -- Returns a SetUnit if there are targets to be SEADed... + local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... if TargetSetUnit then - SEADTask = Mission:AddTask( TASK_SEAD:New( Mission, self.SetGroup, "SEAD." .. AreaID, TargetSetUnit , DetectedZone ) ) + local Task = TASK_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", ItemID ), TargetSetUnit ) + Task:SetTargetZone( DetectedZone ) + Task:SetDispatcher( self ) + SEADTask = Mission:AddTask( Task ) end end if SEADTask and SEADTask:IsStatePlanned() then - self:E( "Planned" ) - --SEADTask:SetPlannedMenu() - TaskMsg[#TaskMsg+1] = " - " .. SEADTask:GetStateString() .. " SEAD " .. AreaID .. " - " .. SEADTask.TargetSetUnit:GetUnitTypesText() + ReportSEAD:Add( string.format( " - %s.%02d - %s", "SEAD", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) end -- Evaluate CAS Tasking - local CASTask = Mission:GetTask( "CAS." .. AreaID ) - CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedArea ) + local CASTask = Mission:GetTask( string.format( "CAS.%03d", ItemID ) ) + CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedItem ) if not CASTask then - local TargetSetUnit = self:EvaluateCAS( DetectedArea ) -- Returns a SetUnit if there are targets to be SEADed... + local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... if TargetSetUnit then - CASTask = Mission:AddTask( TASK_A2G:New( Mission, self.SetGroup, "CAS." .. AreaID, "CAS", TargetSetUnit , DetectedZone, DetectedArea.NearestFAC ) ) + local Task = TASK_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", ItemID ), TargetSetUnit ) + Task:SetTargetZone( DetectedZone ) + Task:SetDispatcher( self ) + CASTask = Mission:AddTask( Task ) end end if CASTask and CASTask:IsStatePlanned() then - --CASTask:SetPlannedMenu() - TaskMsg[#TaskMsg+1] = " - " .. CASTask:GetStateString() .. " CAS " .. AreaID .. " - " .. CASTask.TargetSetUnit:GetUnitTypesText() + ReportCAS:Add( string.format( " - %s.%02d - %s", "CAS", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) end -- Evaluate BAI Tasking - local BAITask = Mission:GetTask( "BAI." .. AreaID ) - BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedArea ) + local BAITask = Mission:GetTask( string.format( "BAI.%03d", ItemID ) ) + BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedItem ) if not BAITask then - local TargetSetUnit = self:EvaluateBAI( DetectedArea, self.CommandCenter:GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed... + local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed... if TargetSetUnit then - BAITask = Mission:AddTask( TASK_A2G:New( Mission, self.SetGroup, "BAI." .. AreaID, "BAI", TargetSetUnit , DetectedZone, DetectedArea.NearestFAC ) ) + local Task = TASK_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", ItemID ), TargetSetUnit ) + Task:SetTargetZone( DetectedZone ) + Task:SetDispatcher( self ) + BAITask = Mission:AddTask( Task ) end end if BAITask and BAITask:IsStatePlanned() then - --BAITask:SetPlannedMenu() - TaskMsg[#TaskMsg+1] = " - " .. BAITask:GetStateString() .. " BAI " .. AreaID .. " - " .. BAITask.TargetSetUnit:GetUnitTypesText() - end - - if #TaskMsg > 0 then - - local ThreatLevel = Detection:GetTreatLevelA2G( DetectedArea ) - - local DetectedAreaVec3 = DetectedZone:GetVec3() - local DetectedAreaPointVec3 = POINT_VEC3:New( DetectedAreaVec3.x, DetectedAreaVec3.y, DetectedAreaVec3.z ) - local DetectedAreaPointLL = DetectedAreaPointVec3:ToStringLL( 3, true ) - AreaMsg[#AreaMsg+1] = string.format( " - Area #%d - %s - Threat Level [%s] (%2d)", - DetectedAreaID, - DetectedAreaPointLL, - string.rep( "â– ", ThreatLevel ), - ThreatLevel - ) - - -- Loop through the changes ... - local ChangeText = Detection:GetChangeText( DetectedArea ) - - if ChangeText ~= "" then - ChangeMsg[#ChangeMsg+1] = string.gsub( string.gsub( ChangeText, "\n", "%1 - " ), "^.", " - %1" ) - end + ReportBAI:Add( string.format( " - %s.%02d - %s", "BAI", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) end + + -- Loop through the changes ... + local ChangeText = Detection:GetChangeText( DetectedItem ) + ReportChanges:Add( ChangeText ) + + -- OK, so the tasking has been done, now delete the changes reported for the area. - Detection:AcceptChanges( DetectedArea ) + Detection:AcceptChanges( DetectedItem ) end -- TODO set menus using the HQ coordinator Mission:GetCommandCenter():SetMenu() - if #AreaMsg > 0 then - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if not TaskGroup:GetState( TaskGroup, "Assigned" ) then - self.CommandCenter:MessageToGroup( - string.format( "HQ Reporting - Target areas for mission '%s':\nAreas:\n%s\n\nTasks:\n%s\n\nChanges:\n%s ", - self.Mission:GetName(), - table.concat( AreaMsg, "\n" ), - table.concat( TaskMsg, "\n" ), - table.concat( ChangeMsg, "\n" ) - ), self:GetReportDisplayTime(), TaskGroup - ) - end + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + if not TaskGroup:GetState( TaskGroup, "Assigned" ) then + Mission:GetCommandCenter():MessageToGroup( + string.format( "HQ Reporting - Planned tasks for mission '%s':\n%s\n", + self.Mission:GetName(), + string.format( "%s\n\n%s\n\n%s\n\n%s", ReportSEAD:Text(), ReportCAS:Text(), ReportBAI:Text(), ReportChanges:Text() + ) + ), TaskGroup + ) end end return true end -end--- This module contains the TASK_SEAD classes. +end--- **Tasking** - The TASK_A2G models tasks for players in Air to Ground engagements. -- --- 1) @{#TASK_SEAD} class, extends @{Task#TASK} --- ================================================= --- The @{#TASK_SEAD} class defines a SEAD task for a @{Set} of Target Units, located at a Target Zone, +-- ![Banner Image](..\Presentations\TASK_A2G\Dia1.JPG) +-- +-- +-- # 1) @{Task_A2G#TASK_A2G} class, extends @{Task#TASK} +-- +-- The @{#TASK_A2G} class defines Air To Ground tasks for a @{Set} of Target Units, -- based on the tasking capabilities defined in @{Task#TASK}. --- The TASK_SEAD is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses: +-- The TASK_A2G is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses: -- -- * **None**: Start of the process --- * **Planned**: The SEAD task is planned. Upon Planned, the sub-process @{Process_Fsm.Assign#ACT_ASSIGN_ACCEPT} is started to accept the task. --- * **Assigned**: The SEAD task is assigned to a @{Group#GROUP}. Upon Assigned, the sub-process @{Process_Fsm.Route#ACT_ROUTE} is started to route the active Units in the Group to the attack zone. --- * **Success**: The SEAD task is successfully completed. Upon Success, the sub-process @{Process_SEAD#PROCESS_SEAD} is started to follow-up successful SEADing of the targets assigned in the task. --- * **Failed**: The SEAD task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. +-- * **Planned**: The A2G task is planned. +-- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}. +-- * **Success**: The A2G task is successfully completed. +-- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. +-- +-- # 1.1) Set the scoring of achievements in an A2G attack. +-- +-- Scoring or penalties can be given in the following circumstances: +-- +-- * @{#TASK_A2G.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed. +-- * @{#TASK_A2G.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed. +-- * @{#TASK_A2G.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed. +-- +-- # 2) @{Task_A2G#TASK_SEAD} class, extends @{Task_A2G#TASK_A2G} +-- +-- The @{#TASK_SEAD} class defines a SEAD task for a @{Set} of Target Units. -- -- === -- --- ### Authors: FlightControl - Design and Programming +-- # 3) @{Task_A2G#TASK_CAS} class, extends @{Task_A2G#TASK_A2G} -- --- @module Task_SEAD +-- The @{#TASK_CAS} class defines a CAS task for a @{Set} of Target Units. +-- +-- === +-- +-- # 4) @{Task_A2G#TASK_BAI} class, extends @{Task_A2G#TASK_A2G} +-- +-- The @{#TASK_BAI} class defines a BAI task for a @{Set} of Target Units. +-- +-- ==== +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-03-09: Revised version. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **[WingThor]**: Concept, Advice & Testing. +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module Task_A2G +do -- TASK_A2G + + --- The TASK_A2G class + -- @type TASK_A2G + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + TASK_A2G = { + ClassName = "TASK_A2G", + } + + --- Instantiates a new TASK_A2G. + -- @param #TASK_A2G self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Set#SET_UNIT UnitSetTargets + -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. + -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. + -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. + -- @return #TASK_A2G self + function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType ) + local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) -- Tasking.Task#TASK_A2G + self:F() + + self.TargetSetUnit = TargetSetUnit + self.TaskType = TaskType + + Mission:AddTask( self ) + + local Fsm = self:GetUnitProcess() + + + Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToRendezVous", Rejected = "Reject" } ) + + Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) + Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) + Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) + + Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" ) + + Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" ) + Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" ) + + Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, self.TaskType ), { Accounted = "Success" } ) + Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" ) + Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} ) + Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} ) + Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" ) + + Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) + Fsm:AddTransition( "Accounted", "Success", "Success" ) + Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) + Fsm:AddTransition( "Failed", "Fail", "Failed" ) + + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2G#TASK_A2G Task + function Fsm:onafterRouteToRendezVous( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- Determine the first Unit from the self.RendezVousSetUnit + + if Task:GetRendezVousZone( TaskUnit ) then + self:__RouteToRendezVousZone( 0.1 ) + else + if Task:GetRendezVousPointVec2( TaskUnit ) then + self:__RouteToRendezVousPoint( 0.1 ) + else + self:__ArriveAtRendezVous( 0.1 ) + end + end + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task#TASK_A2G Task + function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- Determine the first Unit from the self.TargetSetUnit + + self:__Engage( 0.1 ) + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task#TASK_A2G Task + function Fsm:onafterEngage( TaskUnit, Task ) + self:E( { self } ) + self:__Account( 0.1 ) + self:__RouteToTarget(0.1 ) + self:__RouteToTargets( -10 ) + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2G#TASK_A2G Task + function Fsm:onafterRouteToTarget( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- Determine the first Unit from the self.TargetSetUnit + + if Task:GetTargetZone( TaskUnit ) then + self:__RouteToTargetZone( 0.1 ) + else + local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT + if TargetUnit then + local PointVec2 = TargetUnit:GetPointVec2() + self:T( { TargetPointVec2 = PointVec2, PointVec2:GetX(), PointVec2:GetAlt(), PointVec2:GetZ() } ) + Task:SetTargetPointVec2( TargetUnit:GetPointVec2(), TaskUnit ) + end + self:__RouteToTargetPoint( 0.1 ) + end + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2G#TASK_A2G Task + function Fsm:onafterRouteToTargets( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT + if TargetUnit then + Task:SetTargetPointVec2( TargetUnit:GetPointVec2(), TaskUnit ) + end + self:__RouteToTargets( -10 ) + end + + return self + + end + + --- @param #TASK_A2G self + function TASK_A2G:GetPlannedMenuText() + return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" + end + + --- @param #TASK_A2G self + -- @param Core.Point#POINT_VEC2 RendezVousPointVec2 The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map. + -- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2G:SetRendezVousPointVec2( RendezVousPointVec2, RendezVousRange, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + ActRouteRendezVous:SetPointVec2( RendezVousPointVec2 ) + ActRouteRendezVous:SetRange( RendezVousRange ) + end + + --- @param #TASK_A2G self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Point#POINT_VEC2 The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map. + -- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. + function TASK_A2G:GetRendezVousPointVec2( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + return ActRouteRendezVous:GetPointVec2(), ActRouteRendezVous:GetRange() + end + + + + --- @param #TASK_A2G self + -- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2G:SetRendezVousZone( RendezVousZone, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + ActRouteRendezVous:SetZone( RendezVousZone ) + end + + --- @param #TASK_A2G self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Zone#ZONE_BASE The Zone object where the RendezVous is located on the map. + function TASK_A2G:GetRendezVousZone( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + return ActRouteRendezVous:GetZone() + end + + --- @param #TASK_A2G self + -- @param Core.Point#POINT_VEC2 TargetPointVec2 The PointVec2 object where the Target is located on the map. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2G:SetTargetPointVec2( TargetPointVec2, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + ActRouteTarget:SetPointVec2( TargetPointVec2 ) + end + + + --- @param #TASK_A2G self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Point#POINT_VEC2 The PointVec2 object where the Target is located on the map. + function TASK_A2G:GetTargetPointVec2( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + return ActRouteTarget:GetPointVec2() + end + + + --- @param #TASK_A2G self + -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2G:SetTargetZone( TargetZone, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + ActRouteTarget:SetZone( TargetZone ) + end + + + --- @param #TASK_A2G self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. + function TASK_A2G:GetTargetZone( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + return ActRouteTarget:GetZone() + end + + --- Set a score when a target in scope of the A2G attack, has been destroyed . + -- @param #TASK_A2G self + -- @param #string Text The text to display to the player, when the target has been destroyed. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G + function TASK_A2G:SetScoreOnDestroy( Text, Score, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScoreProcess( "Engaging", "Account", "Account", Text, Score ) + + return self + end + + --- Set a score when all the targets in scope of the A2G attack, have been destroyed. + -- @param #TASK_A2G self + -- @param #string Text The text to display to the player, when all targets hav been destroyed. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G + function TASK_A2G:SetScoreOnSuccess( Text, Score, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Success", Text, Score ) + + return self + end + + --- Set a penalty when the A2G attack has failed. + -- @param #TASK_A2G self + -- @param #string Text The text to display to the player, when the A2G attack has failed. + -- @param #number Penalty The penalty in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G + function TASK_A2G:SetPenaltyOnFailed( Text, Penalty, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Failed", Text, Penalty ) + + return self + end + + +end do -- TASK_SEAD @@ -33669,130 +35786,76 @@ do -- TASK_SEAD -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. -- @param Set#SET_UNIT UnitSetTargets - -- @param Core.Zone#ZONE_BASE TargetZone + -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. + -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. + -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. -- @return #TASK_SEAD self - function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TargetZone ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, "SEAD" ) ) -- Tasking.Task_SEAD#TASK_SEAD + function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit ) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD" ) ) -- #TASK_SEAD self:F() - - self.TargetSetUnit = TargetSetUnit - self.TargetZone = TargetZone - local Fsm = self:GetUnitProcess() - - Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "Route", Rejected = "Eject" } ) - Fsm:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } ) - Fsm:AddTransition( "Rejected", "Eject", "Planned" ) - Fsm:AddTransition( "Arrived", "Update", "Updated" ) - Fsm:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "SEAD" ), { Accounted = "Success" } ) - Fsm:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) ) - Fsm:AddTransition( "Accounted", "Success", "Success" ) - Fsm:AddTransition( "Failed", "Fail", "Failed" ) - - function Fsm:onenterUpdated( TaskUnit ) - self:E( { self } ) - self:Account() - self:Smoke() - end - --- _EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventPlayerLeaveUnit, self ) --- _EVENTDISPATCHER:OnDead( self._EventDead, self ) --- _EVENTDISPATCHER:OnCrash( self._EventDead, self ) --- _EVENTDISPATCHER:OnPilotDead( self._EventDead, self ) - return self - end - - --- @param #TASK_SEAD self - function TASK_SEAD:GetPlannedMenuText() - return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" - end - -end ---- (AI) (SP) (MP) Tasking for Air to Ground Processes. --- --- 1) @{#TASK_A2G} class, extends @{Task#TASK} --- ================================================= --- The @{#TASK_A2G} class defines a CAS or BAI task of a @{Set} of Target Units, --- located at a Target Zone, based on the tasking capabilities defined in @{Task#TASK}. --- The TASK_A2G is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses: --- --- * **None**: Start of the process --- * **Planned**: The SEAD task is planned. Upon Planned, the sub-process @{Process_Fsm.Assign#ACT_ASSIGN_ACCEPT} is started to accept the task. --- * **Assigned**: The SEAD task is assigned to a @{Group#GROUP}. Upon Assigned, the sub-process @{Process_Fsm.Route#ACT_ROUTE} is started to route the active Units in the Group to the attack zone. --- * **Success**: The SEAD task is successfully completed. Upon Success, the sub-process @{Process_SEAD#PROCESS_SEAD} is started to follow-up successful SEADing of the targets assigned in the task. --- * **Failed**: The SEAD task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. --- --- === --- --- ### Authors: FlightControl - Design and Programming --- --- @module Task_A2G + end +end -do -- TASK_A2G +do -- TASK_BAI - --- The TASK_A2G class - -- @type TASK_A2G + --- The TASK_BAI class + -- @type TASK_BAI + -- @field Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - TASK_A2G = { - ClassName = "TASK_A2G", + TASK_BAI = { + ClassName = "TASK_BAI", } - --- Instantiates a new TASK_A2G. - -- @param #TASK_A2G self + --- Instantiates a new TASK_BAI. + -- @param #TASK_BAI self -- @param Tasking.Mission#MISSION Mission -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. - -- @param #string TaskType BAI or CAS -- @param Set#SET_UNIT UnitSetTargets - -- @param Core.Zone#ZONE_BASE TargetZone - -- @return #TASK_A2G self - function TASK_A2G:New( Mission, SetGroup, TaskName, TaskType, TargetSetUnit, TargetZone, FACUnit ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) + -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. + -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. + -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. + -- @return #TASK_BAI self + function TASK_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit ) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI" ) ) -- #TASK_BAI self:F() - - self.TargetSetUnit = TargetSetUnit - self.TargetZone = TargetZone - self.FACUnit = FACUnit - local A2GUnitProcess = self:GetUnitProcess() - - A2GUnitProcess:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "Attack the Area" ), { Assigned = "Route", Rejected = "Eject" } ) - A2GUnitProcess:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } ) - A2GUnitProcess:AddTransition( "Rejected", "Eject", "Planned" ) - A2GUnitProcess:AddTransition( "Arrived", "Update", "Updated" ) - A2GUnitProcess:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "Attack" ), { Accounted = "Success" } ) - A2GUnitProcess:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) ) - --Fsm:AddProcess ( "Updated", "JTAC", PROCESS_JTAC:New( self, TaskUnit, self.TargetSetUnit, self.FACUnit ) ) - A2GUnitProcess:AddTransition( "Accounted", "Success", "Success" ) - A2GUnitProcess:AddTransition( "Failed", "Fail", "Failed" ) - - function A2GUnitProcess:onenterUpdated( TaskUnit ) - self:E( { self } ) - self:Account() - self:Smoke() - end - - - - --_EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventPlayerLeaveUnit, self ) - --_EVENTDISPATCHER:OnDead( self._EventDead, self ) - --_EVENTDISPATCHER:OnCrash( self._EventDead, self ) - --_EVENTDISPATCHER:OnPilotDead( self._EventDead, self ) - return self - end + end + +end + +do -- TASK_CAS + + --- The TASK_CAS class + -- @type TASK_CAS + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + TASK_CAS = { + ClassName = "TASK_CAS", + } - --- @param #TASK_A2G self - function TASK_A2G:GetPlannedMenuText() - return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" - end - - end - - + --- Instantiates a new TASK_CAS. + -- @param #TASK_CAS self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Set#SET_UNIT UnitSetTargets + -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. + -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. + -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. + -- @return #TASK_CAS self + function TASK_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit ) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS" ) ) -- #TASK_CAS + self:F() + + return self + end +end --- The main include file for the MOOSE system. -- Test of permissions @@ -33812,6 +35875,7 @@ Include.File( "Core/Set" ) Include.File( "Core/Point" ) Include.File( "Core/Message" ) Include.File( "Core/Fsm" ) +Include.File( "Core/Radio" ) --- Wrapper Classes Include.File( "Wrapper/Object" ) @@ -33854,7 +35918,7 @@ Include.File( "Tasking/CommandCenter" ) Include.File( "Tasking/Mission" ) Include.File( "Tasking/Task" ) Include.File( "Tasking/DetectionManager" ) -Include.File( "Tasking/Task_SEAD" ) +Include.File( "Tasking/Task_A2G_Dispatcher") Include.File( "Tasking/Task_A2G" ) diff --git a/Moose Mission Setup/Moose_Create.bat b/Moose Mission Setup/Moose_Create.bat index 4e3a251d4..5f2979007 100644 --- a/Moose Mission Setup/Moose_Create.bat +++ b/Moose Mission Setup/Moose_Create.bat @@ -54,6 +54,7 @@ COPY /b Moose.lua + %1\Core\Set.lua Moose.lua COPY /b Moose.lua + %1\Core\Point.lua Moose.lua COPY /b Moose.lua + %1\Core\Message.lua Moose.lua COPY /b Moose.lua + %1\Core\Fsm.lua Moose.lua +COPY /b Moose.lua + %1\Core\Radio.lua Moose.lua rem Wrapper Classes COPY /b Moose.lua + %1\Wrapper\Object.lua Moose.lua @@ -97,7 +98,7 @@ COPY /b Moose.lua + %1\Tasking\CommandCenter.lua Moose.lua COPY /b Moose.lua + %1\Tasking\Mission.lua Moose.lua COPY /b Moose.lua + %1\Tasking\Task.lua Moose.lua COPY /b Moose.lua + %1\Tasking\DetectionManager.lua Moose.lua -COPY /b Moose.lua + %1\Tasking\Task_SEAD.lua Moose.lua +COPY /b Moose.lua + %1\Tasking\Task_A2G_Dispatcher.lua Moose.lua COPY /b Moose.lua + %1\Tasking\Task_A2G.lua Moose.lua COPY /b Moose.lua + %1\Moose.lua Moose.lua diff --git a/Moose Presentations/ACT_ACCOUNT.pptx b/Moose Presentations/ACT_ACCOUNT.pptx new file mode 100644 index 000000000..019ae69b7 Binary files /dev/null and b/Moose Presentations/ACT_ACCOUNT.pptx differ diff --git a/Moose Presentations/AI_PATROL.pptx b/Moose Presentations/AI_PATROL.pptx index bf425bd57..a7a9ff112 100644 Binary files a/Moose Presentations/AI_PATROL.pptx and b/Moose Presentations/AI_PATROL.pptx differ diff --git a/Moose Presentations/DETECTION.pptx b/Moose Presentations/DETECTION.pptx new file mode 100644 index 000000000..0f8e1f2b2 Binary files /dev/null and b/Moose Presentations/DETECTION.pptx differ diff --git a/Moose Presentations/SCHEDULER.pptx b/Moose Presentations/SCHEDULER.pptx new file mode 100644 index 000000000..6dab2d58c Binary files /dev/null and b/Moose Presentations/SCHEDULER.pptx differ diff --git a/Moose Presentations/SET.pptx b/Moose Presentations/SET.pptx new file mode 100644 index 000000000..a032db2f3 Binary files /dev/null and b/Moose Presentations/SET.pptx differ diff --git a/Moose Presentations/TASK.pptx b/Moose Presentations/TASK.pptx new file mode 100644 index 000000000..70da479bc Binary files /dev/null and b/Moose Presentations/TASK.pptx differ diff --git a/Moose Presentations/TASK_A2G.pptx b/Moose Presentations/TASK_A2G.pptx new file mode 100644 index 000000000..9254132a1 Binary files /dev/null and b/Moose Presentations/TASK_A2G.pptx differ diff --git a/Moose Presentations/TASK_DISPATCHER.pptx b/Moose Presentations/TASK_DISPATCHER.pptx new file mode 100644 index 000000000..0d9ebd4ab Binary files /dev/null and b/Moose Presentations/TASK_DISPATCHER.pptx differ diff --git a/Moose Presentations/ZONE.pptx b/Moose Presentations/ZONE.pptx new file mode 100644 index 000000000..7bf099ec4 Binary files /dev/null and b/Moose Presentations/ZONE.pptx differ diff --git a/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz b/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz index 60ea1a363..40da60e12 100644 Binary files a/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz and b/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz differ diff --git a/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz b/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz index 6dee91ca6..4893f8abf 100644 Binary files a/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz and b/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz differ diff --git a/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz b/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz index 43f3146ac..6f70aac91 100644 Binary files a/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz and b/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.lua b/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.lua index a3104d156..44b68cdb4 100644 --- a/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.lua +++ b/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.lua @@ -17,12 +17,12 @@ -- 3. If two players join the red slots, no AI plane should be spawned, and all airborne AI planes should return to the nearest home base. -- Define the SET of CLIENTs from the red coalition. This SET is filled during startup. -local RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) +RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) -- Define the SPAWN object for the red AI plane template. -- We use InitCleanUp to check every 20 seconds, if there are no planes blocked at the airbase, waithing for take-off. -- If a blocked plane exists, this red plane will be ReSpawned. -local RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) +RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) -- Start the AI_BALANCER, using the SET of red CLIENTs, and the SPAWN object as a parameter. -local RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) +RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz b/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz index ffbfefc2d..f5619d3e0 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.lua b/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.lua index 158a8f5ee..c0b085a92 100644 --- a/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.lua +++ b/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.lua @@ -17,15 +17,15 @@ -- 5. When the AI is out-of-fuel, it should report it is returning to the home base, and land at Anapa. -- Define the SET of CLIENTs from the red coalition. This SET is filled during startup. -local RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) +RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) -- Define the SPAWN object for the red AI plane template. -- We use InitCleanUp to check every 20 seconds, if there are no planes blocked at the airbase, waithing for take-off. -- If a blocked plane exists, this red plane will be ReSpawned. -local RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) +RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) -- Start the AI_BALANCER, using the SET of red CLIENTs, and the SPAWN object as a parameter. -local RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) +RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) local PatrolZones = {} diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz b/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz index d408d2460..4321844f6 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz b/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz index d4aa83919..44597cba3 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.lua b/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.lua index 222c56980..07efab0d0 100644 --- a/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.lua +++ b/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.lua @@ -22,15 +22,15 @@ -- Define the SET of CLIENTs from the red coalition. This SET is filled during startup. -local RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) +RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) -- Define the SPAWN object for the red AI plane template. -- We use InitCleanUp to check every 20 seconds, if there are no planes blocked at the airbase, waithing for take-off. -- If a blocked plane exists, this red plane will be ReSpawned. -local RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) +RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) -- Start the AI_BALANCER, using the SET of red CLIENTs, and the SPAWN object as a parameter. -local RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) +RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) function RU_AI_Balancer:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) @@ -44,3 +44,4 @@ function RU_AI_Balancer:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) Patrol:__Start( 5 ) end + diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz b/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz index f96b06726..bdc946b15 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.lua b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.lua index eab2c2e22..f0253d5e9 100644 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.lua +++ b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.lua @@ -20,26 +20,26 @@ -- 6. Ensure that you see the AI patrol in one of the two zones ... -- Define the SET of CLIENTs from the red coalition. This SET is filled during startup. -local RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) +RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) -- Define the SPAWN object for the red AI plane template. -- We use InitCleanUp to check every 20 seconds, if there are no planes blocked at the airbase, waithing for take-off. -- If a blocked plane exists, this red plane will be ReSpawned. -local RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) +RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) -- Start the AI_BALANCER, using the SET of red CLIENTs, and the SPAWN object as a parameter. -local RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) +RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) -- Create the first polygon zone ... -local PatrolZoneGroup1 = GROUP:FindByName( "PatrolZone1" ) -local PatrolZone1 = ZONE_POLYGON:New( "PatrolZone1", PatrolZoneGroup1 ) +PatrolZoneGroup1 = GROUP:FindByName( "PatrolZone1" ) +PatrolZone1 = ZONE_POLYGON:New( "PatrolZone1", PatrolZoneGroup1 ) -- Create the second polygon zone ... -local PatrolZoneGroup2 = GROUP:FindByName( "PatrolZone2" ) -local PatrolZone2 = ZONE_POLYGON:New( "PatrolZone2", PatrolZoneGroup2 ) +PatrolZoneGroup2 = GROUP:FindByName( "PatrolZone2" ) +PatrolZone2 = ZONE_POLYGON:New( "PatrolZone2", PatrolZoneGroup2 ) -- Now, create an array of these zones ... -local PatrolZoneArray = { PatrolZone1, PatrolZone2 } +PatrolZoneArray = { PatrolZone1, PatrolZone2 } function RU_AI_Balancer:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz index 40cba9afb..348d0ae84 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Config/View/Server.lua b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Config/View/Server.lua deleted file mode 100644 index c7db44162..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Config/View/Server.lua +++ /dev/null @@ -1,210 +0,0 @@ --- View scripts --- Copyright (C) 2004, Eagle Dynamics. -DisableCombatViews = false -- F5 & Ctrl-F5 -ExternalObjectsLockDistance = 10000.0 -ShowTargetInfo = false -CameraTerrainRestriction = true -hAngleRearDefault = 180 -vAngleRearDefault = -8.0 -vAngleRearMin = -90 -- -8.0 -vAngleRearMax = 90.0 - -dbg_shell = "weapons.shells.PKT_7_62_T" -- 23mm shell -dbg_shell = "weapons.nurs.WGr21" --- dbg_shell = "weapons.shells.2A64_152" -- 152mm shell -dbg_shell_v0 = -1 -- Muzzle speed m/s (-1 - speed from shall database) -dbg_shell_fire_rate = 60 ---reformatted per-unit data to be mod system friendly ---this file is no longer should be edited for adding new flyable aircraft , DCS automatically check core database (i.e. where you define your aircraft in aircraft table just define ViewSettings and SnapViews tables) - -function default_fighter_player(t) - local res = { - CameraViewAngleLimits = {20.000000,140.000000}, - CameraAngleRestriction = {false ,90.000000,0.500000}, - EyePoint = {0.05 ,0.000000 ,0.000000}, - limits_6DOF = {x = {-0.050000,0.4500000},y ={-0.300000,0.100000},z = {-0.220000,0.220000},roll = 90.000000}, - Allow360rotation = false, - CameraAngleLimits = {200,-80.000000,110.000000}, - ShoulderSize = 0.2, -- move body when azimuth value more then 90 degrees - } - if t then - for i,o in pairs(t) do - res[i] = o - end - end - return res -end - -function fulcrum() - return { - Cockpit = { - default_fighter_player({CockpitLocalPoint = {4.71,1.28,0.000000}}) - }, - Chase = { - LocalPoint = {1.220000,3.750000,0.000000}, - AnglesDefault = {180.000000,-8.000000}, - }, -- Chase - Arcade = { - LocalPoint = {-15.080000,6.350000,0.000000}, - AnglesDefault = {0.000000,-8.000000}, - }, -- Arcade - } -end - -ViewSettings = {} -ViewSettings["A-10A"] = { - Cockpit = { - [1] = default_fighter_player({CockpitLocalPoint = {4.300000,1.282000,0.000000}, - EyePoint = {0.000000,0.000000,0.000000}, - limits_6DOF = {x = {-0.050000,0.600000}, - y = {-0.300000,0.100000}, - z = {-0.250000,0.250000}, - roll = 90.000000}}), - }, -- Cockpit - Chase = { - LocalPoint = {0.600000,3.682000,0.000000}, - AnglesDefault = {180.000000,-8.000000}, - }, -- Chase - Arcade = { - LocalPoint = {-27.000000,12.000000,0.000000}, - AnglesDefault = {0.000000,-12.000000}, - }, -- Arcade -} -ViewSettings["A-10C"] = { - Cockpit = { - [1] = default_fighter_player({CockpitLocalPoint = {4.300000,1.282000,0.000000}, - EyePoint = {0.000000,0.000000,0.000000}, - limits_6DOF = {x = {-0.050000,0.600000}, - y = {-0.300000,0.100000}, - z = {-0.250000,0.250000}, - roll = 90.000000}}), - }, -- Cockpit - Chase = { - LocalPoint = {0.600000,3.682000,0.000000}, - AnglesDefault = {180.000000,-8.000000}, - }, -- Chase - Arcade = { - LocalPoint = {-27.000000,12.000000,0.000000}, - AnglesDefault = {0.000000,-12.000000}, - }, -- Arcade -} -ViewSettings["F-15C"] = { - Cockpit = { - [1] = default_fighter_player({CockpitLocalPoint = {6.210000,1.204000,0.000000}})-- player slot 1 - }, -- Cockpit - Chase = { - LocalPoint = {2.510000,3.604000,0.000000}, - AnglesDefault = {180.000000,-8.000000}, - }, -- Chase - Arcade = { - LocalPoint = {-13.790000,6.204000,0.000000}, - AnglesDefault = {0.000000,-8.000000}, - }, -- Arcade -} -ViewSettings["Ka-50"] = { - Cockpit = { - [1] = {-- player slot 1 - CockpitLocalPoint = {3.188000,0.390000,0.000000}, - CameraViewAngleLimits = {20.000000,120.000000}, - CameraAngleRestriction = {false,60.000000,0.400000}, - CameraAngleLimits = {140.000000,-65.000000,90.000000}, - EyePoint = {0.090000,0.000000,0.000000}, - limits_6DOF = {x = {-0.020000,0.350000},y ={-0.150000,0.165000},z = {-0.170000,0.170000},roll = 90.000000}, - }, - }, -- Cockpit - Chase = { - LocalPoint = {-0.512000,2.790000,0.000000}, - AnglesDefault = {180.000000,-8.000000}, - }, -- Chase - Arcade = { - LocalPoint = {-16.812000,5.390000,0.000000}, - AnglesDefault = {0.000000,-8.000000}, - }, -- Arcade -} -ViewSettings["MiG-29A"] = fulcrum() -ViewSettings["MiG-29G"] = fulcrum() -ViewSettings["MiG-29S"] = fulcrum() - -ViewSettings["P-51D"] = { - Cockpit = { - [1] = {-- player slot 1 - CockpitLocalPoint = {-1.500000,0.618000,0.000000}, - CameraViewAngleLimits = {20.000000,120.000000}, - CameraAngleRestriction = {false,90.000000,0.500000}, - CameraAngleLimits = {200,-80.000000,90.000000}, - EyePoint = {0.025000,0.100000,0.000000}, - ShoulderSize = 0.15, - Allow360rotation = false, - limits_6DOF = {x = {-0.050000,0.450000},y ={-0.200000,0.200000},z = {-0.220000,0.220000},roll = 90.000000}, - }, - }, -- Cockpit - Chase = { - LocalPoint = {0.200000,-0.652000,-0.650000}, - AnglesDefault = {0.000000,0.000000}, - }, -- Chase - Arcade = { - LocalPoint = {-21.500000,5.618000,0.000000}, - AnglesDefault = {0.000000,-8.000000}, - }, -- Arcade -} -ViewSettings["Su-25"] = { - Cockpit = { - [1] = default_fighter_player({CockpitLocalPoint = {3.352000,0.506000,0.000000}}),-- player slot 1 - }, -- Cockpit - Chase = { - LocalPoint = {-0.348000,2.906000,0.000000}, - AnglesDefault = {180.000000,-8.000000}, - }, -- Chase - Arcade = { - LocalPoint = {-16.648001,5.506000,0.000000}, - AnglesDefault = {0.000000,-8.000000}, - }, -- Arcade -} -ViewSettings["Su-25T"] = { - Cockpit = { - [1] = default_fighter_player({CockpitLocalPoint = {3.406000,0.466000,0.000000}}),-- player slot 1 - }, -- Cockpit - Chase = { - LocalPoint = {-0.294000,2.866000,0.000000}, - AnglesDefault = {180.000000,-8.000000}, - }, -- Chase - Arcade = { - LocalPoint = {-16.594000,5.466000,0.000000}, - AnglesDefault = {0.000000,-8.000000}, - }, -- Arcade -} -ViewSettings["Su-25TM"] = { - Cockpit = { - [1] = {-- player slot 1 - CockpitLocalPoint = {4.000000,1.000000,0.000000}, - CameraViewAngleLimits = {20.000000,140.000000}, - CameraAngleRestriction = {true,90.000000,0.400000}, - CameraAngleLimits = {160.000000,-70.000000,90.000000}, - EyePoint = {0.000000,0.000000,0.000000}, - limits_6DOF = {x = {-0.200000,0.200000},y ={-0.200000,0.200000},z = {-0.200000,0.200000},roll = 60.000000}, - }, - }, -- Cockpit - Chase = { - LocalPoint = {4.000000,2.000000,0.000000}, - AnglesDefault = {180.000000,-8.000000}, - }, -- Chase - Arcade = { - LocalPoint = {4.000000,2.000000,0.000000}, - AnglesDefault = {180.000000,-8.000000}, - }, -- Arcade -} -ViewSettings["Su-27"] = { - Cockpit = { - [1] = default_fighter_player({CockpitLocalPoint = {7.959000,1.419000,0.000000}})-- player slot 1 - }, -- Cockpit - Chase = { - LocalPoint = {4.259000,3.819000,0.000000}, - AnglesDefault = {180.000000,-8.000000}, - }, -- Chase - Arcade = { - LocalPoint = {-12.041000,6.419000,0.000000}, - AnglesDefault = {0.000000,-8.000000}, - }, -- Arcade -} - -ViewSettings["Su-33"] = ViewSettings["Su-27"] diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Config/View/SnapViews.lua b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Config/View/SnapViews.lua deleted file mode 100644 index 412686113..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Config/View/SnapViews.lua +++ /dev/null @@ -1,2780 +0,0 @@ -SnapViews = {} -SnapViews["A-10A"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 65.000000,--FOV - hAngle = 0.000000, - vAngle = -26.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 49.626770,--FOV - hAngle = 0.000000, - vAngle = -90.631294, - x_trans = 0.180499, - y_trans = -0.137064, - z_trans = -0.250000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 30.395041,--FOV - hAngle = 0.000000, - vAngle = -94.329208, - x_trans = 0.372718, - y_trans = -0.054055, - z_trans = 0.250000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 55.238567,--FOV - hAngle = 0.000000, - vAngle = -90.631294, - x_trans = 0.158523, - y_trans = -0.137064, - z_trans = 0.250000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 35.000000,--FOV - hAngle = 0.000000, - vAngle = -10.651850, - x_trans = 0.327622, - y_trans = -0.278207, - z_trans = -0.244799, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 34.340549,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 35.000000,--FOV - hAngle = 0.000000, - vAngle = -10.651850, - x_trans = 0.327622, - y_trans = -0.278207, - z_trans = 0.244799, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 68.628296,--FOV - hAngle = 68.292320, - vAngle = -11.477349, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 68.628296,--FOV - hAngle = 0.000000, - vAngle = 30.227919, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 68.628296,--FOV - hAngle = -67.172974, - vAngle = -11.477349, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 70.000000,--FOV - hAngle = 20.000000, - vAngle = 8.000000, - x_trans = 0.360000, - y_trans = -0.041337, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 70.000000,--FOV - hAngle = -20.000000, - vAngle = 8.000000, - x_trans = 0.360000, - y_trans = -0.041337, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 75.000000,--FOV - hAngle = 0.000000, - vAngle = -23.000000, - x_trans = 0.360000, - y_trans = -0.041337, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["A-10C"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 65.000000,--FOV - hAngle = 0.000000, - vAngle = -26.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 49.626770,--FOV - hAngle = 0.000000, - vAngle = -90.631294, - x_trans = 0.180499, - y_trans = -0.137064, - z_trans = -0.250000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 30.395041,--FOV - hAngle = 0.000000, - vAngle = -94.329208, - x_trans = 0.372718, - y_trans = -0.054055, - z_trans = 0.250000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 55.238567,--FOV - hAngle = 0.000000, - vAngle = -90.631294, - x_trans = 0.158523, - y_trans = -0.137064, - z_trans = 0.250000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 35.000000,--FOV - hAngle = 0.000000, - vAngle = -10.651850, - x_trans = 0.327622, - y_trans = -0.278207, - z_trans = -0.244799, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 34.340549,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 35.000000,--FOV - hAngle = 0.000000, - vAngle = -10.651850, - x_trans = 0.327622, - y_trans = -0.278207, - z_trans = 0.244799, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 68.628296,--FOV - hAngle = 68.292320, - vAngle = -11.477349, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 68.628296,--FOV - hAngle = 0.000000, - vAngle = 30.227919, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 68.628296,--FOV - hAngle = -67.172974, - vAngle = -11.477349, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 70.000000,--FOV - hAngle = 20.000000, - vAngle = 8.000000, - x_trans = 0.360000, - y_trans = -0.041337, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 70.000000,--FOV - hAngle = -20.000000, - vAngle = 8.000000, - x_trans = 0.360000, - y_trans = -0.041337, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 75.000000,--FOV - hAngle = 0.000000, - vAngle = -23.000000, - x_trans = 0.360000, - y_trans = -0.041337, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["Bf-109K-4"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["C-101CC"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -[2] = {-- player slot 2 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["C-101EB"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -[2] = {-- player slot 2 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["F-15C"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 70.611748,--FOV - hAngle = -1.240272, - vAngle = -33.850250, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 32.704346,--FOV - hAngle = 25.696522, - vAngle = -34.778103, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 32.704346,--FOV - hAngle = 0.000000, - vAngle = -47.845268, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 36.106045,--FOV - hAngle = -28.878576, - vAngle = -36.780628, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 88.727844,--FOV - hAngle = 128.508865, - vAngle = 13.131046, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 41.928593,--FOV - hAngle = 0.000000, - vAngle = -4.630446, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 88.727844,--FOV - hAngle = -128.508865, - vAngle = 13.131046, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 88.727844,--FOV - hAngle = 81.648369, - vAngle = -9.500000, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 88.727844,--FOV - hAngle = 0.000000, - vAngle = 34.180634, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 88.727844,--FOV - hAngle = -80.997551, - vAngle = -9.500000, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 56.032040,--FOV - hAngle = 14.803060, - vAngle = 3.332499, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 56.032040,--FOV - hAngle = -14.414484, - vAngle = 3.332499, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 88.727844,--FOV - hAngle = 0.000000, - vAngle = -9.678451, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["FW-190D9"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["Hawk"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["Ka-50"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 67.452896,--FOV - hAngle = 0.000000, - vAngle = -40.067383, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 37.846794,--FOV - hAngle = 51.644135, - vAngle = -51.870411, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 36.178646,--FOV - hAngle = -1.912186, - vAngle = -34.446247, - x_trans = 0.000000, - y_trans = -0.025421, - z_trans = 0.073226, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 73.605141,--FOV - hAngle = -90.361992, - vAngle = -44.103138, - x_trans = 0.169696, - y_trans = -0.073508, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 91.348198,--FOV - hAngle = 109.752129, - vAngle = 1.484382, - x_trans = 0.190306, - y_trans = 0.044778, - z_trans = -0.150335, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 42.512844,--FOV - hAngle = 0.000000, - vAngle = -4.478010, - x_trans = 0.154018, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 91.348198,--FOV - hAngle = -108.852020, - vAngle = 0.085984, - x_trans = 0.190306, - y_trans = 0.044778, - z_trans = 0.139404, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 89.777542,--FOV - hAngle = 16.411518, - vAngle = -27.209915, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = -0.218292, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 88.727844,--FOV - hAngle = 0.000000, - vAngle = 34.042202, - x_trans = 0.142145, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 59.208893,--FOV - hAngle = -32.128311, - vAngle = -5.720805, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 56.032040,--FOV - hAngle = 14.803060, - vAngle = 3.332499, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 56.032040,--FOV - hAngle = -14.414484, - vAngle = 3.332499, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 89.777542,--FOV - hAngle = 0.000000, - vAngle = -15.592758, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["MQ-9 Reaper"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = 0.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["MiG-29A"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 76.124840,--FOV - hAngle = -2.623254, - vAngle = -26.566959, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 34.911949,--FOV - hAngle = 24.601770, - vAngle = -32.350807, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 26.184198,--FOV - hAngle = 12.026249, - vAngle = -40.075508, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 39.454399,--FOV - hAngle = -26.664328, - vAngle = -32.355324, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 81.240005,--FOV - hAngle = 131.503998, - vAngle = 10.804660, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 44.201855,--FOV - hAngle = 0.000000, - vAngle = -2.378299, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 81.240005,--FOV - hAngle = -131.503998, - vAngle = 10.804660, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 81.240005,--FOV - hAngle = 76.013145, - vAngle = 2.248441, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 81.240005,--FOV - hAngle = 0.000000, - vAngle = 36.304676, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 81.240005,--FOV - hAngle = -74.774559, - vAngle = 2.248441, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 68.250000,--FOV - hAngle = 13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 68.250000,--FOV - hAngle = -13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 81.240005,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["MiG-29G"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 76.124840,--FOV - hAngle = -2.623254, - vAngle = -26.566959, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 34.911949,--FOV - hAngle = 24.601770, - vAngle = -32.350807, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 26.184198,--FOV - hAngle = 12.026249, - vAngle = -40.075508, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 39.454399,--FOV - hAngle = -26.664328, - vAngle = -32.355324, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 81.240005,--FOV - hAngle = 131.503998, - vAngle = 10.804660, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 44.201855,--FOV - hAngle = 0.000000, - vAngle = -2.378299, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 81.240005,--FOV - hAngle = -131.503998, - vAngle = 10.804660, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 81.240005,--FOV - hAngle = 76.013145, - vAngle = 2.248441, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 81.240005,--FOV - hAngle = 0.000000, - vAngle = 36.304676, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 81.240005,--FOV - hAngle = -74.774559, - vAngle = 2.248441, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 68.250000,--FOV - hAngle = 13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 68.250000,--FOV - hAngle = -13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 81.240005,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["MiG-29K"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 65.000000,--FOV - hAngle = 0.000000, - vAngle = -26.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 30.000000,--FOV - hAngle = 20.000000, - vAngle = -43.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 30.000000,--FOV - hAngle = 0.000000, - vAngle = -43.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 30.000000,--FOV - hAngle = -20.000000, - vAngle = -43.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 30.000000,--FOV - hAngle = 20.000000, - vAngle = -23.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 30.000000,--FOV - hAngle = 0.000000, - vAngle = -23.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 30.000000,--FOV - hAngle = -20.000000, - vAngle = -23.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 30.000000,--FOV - hAngle = 20.000000, - vAngle = 2.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 30.000000,--FOV - hAngle = 0.000000, - vAngle = 2.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 30.000000,--FOV - hAngle = -20.000000, - vAngle = 2.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 68.250000,--FOV - hAngle = 13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 68.250000,--FOV - hAngle = -13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["MiG-29S"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 76.124840,--FOV - hAngle = -2.623254, - vAngle = -26.566959, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 34.911949,--FOV - hAngle = 24.601770, - vAngle = -32.350807, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 26.184198,--FOV - hAngle = 12.026249, - vAngle = -40.075508, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 39.454399,--FOV - hAngle = -26.664328, - vAngle = -32.355324, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 81.240005,--FOV - hAngle = 131.503998, - vAngle = 10.804660, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 44.201855,--FOV - hAngle = 0.000000, - vAngle = -2.378299, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 81.240005,--FOV - hAngle = -131.503998, - vAngle = 10.804660, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 81.240005,--FOV - hAngle = 76.013145, - vAngle = 2.248441, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 81.240005,--FOV - hAngle = 0.000000, - vAngle = 36.304676, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 81.240005,--FOV - hAngle = -74.774559, - vAngle = 2.248441, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 68.250000,--FOV - hAngle = 13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 68.250000,--FOV - hAngle = -13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 81.240005,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["P-51D"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = -45.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 80.000000,--FOV - hAngle = 45.000000, - vAngle = -45.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = -75.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 80.000000,--FOV - hAngle = -45.000000, - vAngle = -45.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 91.040001,--FOV - hAngle = 157.332764, - vAngle = -28.359503, - x_trans = 0.063872, - y_trans = 0.082888, - z_trans = -0.116148, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 50.000000,--FOV - hAngle = 0.000000, - vAngle = -8.722581, - x_trans = 0.212078, - y_trans = 0.057813, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 80.000000,--FOV - hAngle = -143.000000, - vAngle = 0.000000, - x_trans = 0.350000, - y_trans = 0.059000, - z_trans = 0.100000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 80.000000,--FOV - hAngle = 45.000000, - vAngle = -5.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = 10.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 80.000000,--FOV - hAngle = -45.000000, - vAngle = -5.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = 10.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 80.000000,--FOV - hAngle = -20.000000, - vAngle = 8.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["Su-25"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 68.767799,--FOV - hAngle = 1.929517, - vAngle = -30.846605, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 29.223452,--FOV - hAngle = 37.489525, - vAngle = -38.883888, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 40.635601,--FOV - hAngle = -0.438357, - vAngle = -33.138290, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 24.797405,--FOV - hAngle = -34.382549, - vAngle = -34.808853, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 69.302101,--FOV - hAngle = 89.405373, - vAngle = 1.213156, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 29.761202,--FOV - hAngle = 0.000000, - vAngle = -6.880077, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 69.302101,--FOV - hAngle = -89.691940, - vAngle = 4.554290, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 69.302101,--FOV - hAngle = 52.113377, - vAngle = -3.970644, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 72.856201,--FOV - hAngle = 0.000000, - vAngle = 30.866713, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 69.302101,--FOV - hAngle = -50.664936, - vAngle = -3.970644, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 47.680202,--FOV - hAngle = 43.054649, - vAngle = -7.799250, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 47.680202,--FOV - hAngle = -41.743240, - vAngle = -7.799250, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 69.302101,--FOV - hAngle = 0.000000, - vAngle = -15.137112, - x_trans = 0.050000, - y_trans = 0.010000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["Su-25T"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 80.663399,--FOV - hAngle = 0.000000, - vAngle = -30.619938, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 39.764698,--FOV - hAngle = 28.661316, - vAngle = -41.406044, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 38.090847,--FOV - hAngle = -24.622110, - vAngle = -45.153934, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 36.062012,--FOV - hAngle = -20.779360, - vAngle = -23.755520, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 80.663399,--FOV - hAngle = 99.816956, - vAngle = 8.032285, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 58.718098,--FOV - hAngle = 0.000000, - vAngle = -5.000803, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 80.663399,--FOV - hAngle = -99.999687, - vAngle = 8.032285, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 80.663399,--FOV - hAngle = 58.382488, - vAngle = -6.648195, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 94.037704,--FOV - hAngle = 0.000000, - vAngle = 41.421227, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 80.663399,--FOV - hAngle = -57.531212, - vAngle = -6.648195, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 60.203396,--FOV - hAngle = 55.124939, - vAngle = -8.400513, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 60.203396,--FOV - hAngle = -52.633553, - vAngle = -8.400513, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 90.000000,--FOV - hAngle = 0.000000, - vAngle = -18.382137, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["Su-25TM"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 80.663399,--FOV - hAngle = 0.000000, - vAngle = -30.619938, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 39.764698,--FOV - hAngle = 28.661316, - vAngle = -41.406044, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 38.090847,--FOV - hAngle = -24.622110, - vAngle = -45.153934, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 33.645596,--FOV - hAngle = -36.653450, - vAngle = -23.703861, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 80.663399,--FOV - hAngle = 99.816956, - vAngle = 8.032285, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 58.718098,--FOV - hAngle = 0.000000, - vAngle = -5.000803, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 80.663399,--FOV - hAngle = -99.999687, - vAngle = 8.032285, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 80.663399,--FOV - hAngle = 58.382488, - vAngle = -6.648195, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 94.037704,--FOV - hAngle = 0.000000, - vAngle = 41.421227, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 80.663399,--FOV - hAngle = -57.531212, - vAngle = -6.648195, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 60.203396,--FOV - hAngle = 55.124939, - vAngle = -8.400513, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 60.203396,--FOV - hAngle = -52.633553, - vAngle = -8.400513, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 90.000000,--FOV - hAngle = 0.000000, - vAngle = -18.382137, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["Su-27"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 89.752197,--FOV - hAngle = 7.104492, - vAngle = 52.525635, - x_trans = 0.016405, - y_trans = 0.011140, - z_trans = -0.053543, - rollAngle = -7.110901, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 33.361835,--FOV - hAngle = 41.045925, - vAngle = -40.805656, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 30.427544,--FOV - hAngle = 0.000000, - vAngle = -41.808968, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 34.392349,--FOV - hAngle = -32.597401, - vAngle = -35.293747, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 87.468338,--FOV - hAngle = 129.012665, - vAngle = 14.547977, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 43.977936,--FOV - hAngle = 0.000000, - vAngle = -4.951577, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 87.468338,--FOV - hAngle = -129.012665, - vAngle = 14.491872, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 87.468338,--FOV - hAngle = 82.862923, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 87.468338,--FOV - hAngle = 0.000000, - vAngle = 38.979362, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 87.468338,--FOV - hAngle = -82.461266, - vAngle = -12.843998, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 68.786629,--FOV - hAngle = 15.618313, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 69.165199,--FOV - hAngle = -15.683434, - vAngle = 8.549150, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 87.468338,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.113927, - y_trans = -0.004946, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["Su-33"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 71.824692,--FOV - hAngle = 0.000000, - vAngle = -32.458889, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 33.361835,--FOV - hAngle = 41.045925, - vAngle = -40.805656, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 30.427544,--FOV - hAngle = 0.000000, - vAngle = -41.808968, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 34.392349,--FOV - hAngle = -32.597401, - vAngle = -35.293747, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 87.468338,--FOV - hAngle = 129.012665, - vAngle = 14.547977, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 43.977936,--FOV - hAngle = 0.000000, - vAngle = -4.951577, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 87.468338,--FOV - hAngle = -129.012665, - vAngle = 14.491872, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 87.468338,--FOV - hAngle = 82.862923, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 87.468338,--FOV - hAngle = 0.000000, - vAngle = 38.979362, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 87.468338,--FOV - hAngle = -82.461266, - vAngle = -12.843998, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 68.786629,--FOV - hAngle = 15.618313, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 69.165199,--FOV - hAngle = -15.683434, - vAngle = 8.549150, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 87.468338,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.113927, - y_trans = -0.004946, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["TF-51D"] = { -[1] = {-- player slot 1 - [1] = {--LWin + Num0 : Snap View 0 - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = -45.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = {--LWin + Num1 : Snap View 1 - viewAngle = 80.000000,--FOV - hAngle = 45.000000, - vAngle = -45.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = {--LWin + Num2 : Snap View 2 - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = -75.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = {--LWin + Num3 : Snap View 3 - viewAngle = 80.000000,--FOV - hAngle = -45.000000, - vAngle = -45.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = {--LWin + Num4 : Snap View 4 - viewAngle = 91.040001,--FOV - hAngle = 157.332764, - vAngle = -28.359503, - x_trans = 0.063872, - y_trans = 0.082888, - z_trans = -0.116148, - rollAngle = 0.000000, - }, - [6] = {--LWin + Num5 : Snap View 5 - viewAngle = 50.000000,--FOV - hAngle = 0.000000, - vAngle = -8.722581, - x_trans = 0.212078, - y_trans = 0.057813, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = {--LWin + Num6 : Snap View 6 - viewAngle = 80.000000,--FOV - hAngle = -143.000000, - vAngle = 0.000000, - x_trans = 0.350000, - y_trans = 0.059000, - z_trans = 0.100000, - rollAngle = 0.000000, - }, - [8] = {--LWin + Num7 : Snap View 7 - viewAngle = 80.000000,--FOV - hAngle = 45.000000, - vAngle = -5.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = {--LWin + Num8 : Snap View 8 - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = 10.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = {--LWin + Num9 : Snap View 9 - viewAngle = 80.000000,--FOV - hAngle = -45.000000, - vAngle = -5.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = 10.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 80.000000,--FOV - hAngle = -20.000000, - vAngle = 8.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Config/View/SnapViewsDefault.lua b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Config/View/SnapViewsDefault.lua deleted file mode 100644 index 754522d55..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Config/View/SnapViewsDefault.lua +++ /dev/null @@ -1,1698 +0,0 @@ ---reformatted per-unit data to be mod system friendly ---this file is no longer should be edited for adding new flyable aircraft , DCS automatically check core database for this data(i.e. where you define your aircraft in aircraft table just define ViewSettings and SnapViews tables) --- result of ingame editing is saved to Saved Games//DCS/Config/View/SnapViews.lua -SnapViews = {} -SnapViews["A-10A"] = { -[1] = {-- player slot 1 - [1] = { - viewAngle = 65.000000,--FOV - hAngle = 0.000000, - vAngle = -26.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = { - viewAngle = 49.626770,--FOV - hAngle = 0.000000, - vAngle = -90.631294, - x_trans = 0.180499, - y_trans = -0.137064, - z_trans = -0.250000, - rollAngle = 0.000000, - }, - [3] = { - viewAngle = 30.395041,--FOV - hAngle = 0.000000, - vAngle = -94.329208, - x_trans = 0.372718, - y_trans = -0.054055, - z_trans = 0.250000, - rollAngle = 0.000000, - }, - [4] = { - viewAngle = 55.238567,--FOV - hAngle = 0.000000, - vAngle = -90.631294, - x_trans = 0.158523, - y_trans = -0.137064, - z_trans = 0.250000, - rollAngle = 0.000000, - }, - [5] = { - viewAngle = 35.000000,--FOV - hAngle = 0.000000, - vAngle = -10.651850, - x_trans = 0.327622, - y_trans = -0.278207, - z_trans = -0.244799, - rollAngle = 0.000000, - }, - [6] = { - viewAngle = 34.340549,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = { - viewAngle = 35.000000,--FOV - hAngle = 0.000000, - vAngle = -10.651850, - x_trans = 0.327622, - y_trans = -0.278207, - z_trans = 0.244799, - rollAngle = 0.000000, - }, - [8] = { - viewAngle = 68.628296,--FOV - hAngle = 68.292320, - vAngle = -11.477349, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = { - viewAngle = 68.628296,--FOV - hAngle = 0.000000, - vAngle = 30.227919, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = { - viewAngle = 68.628296,--FOV - hAngle = -67.172974, - vAngle = -11.477349, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 70.000000,--FOV - hAngle = 20.000000, - vAngle = 8.000000, - x_trans = 0.360000, - y_trans = -0.041337, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 70.000000,--FOV - hAngle = -20.000000, - vAngle = 8.000000, - x_trans = 0.360000, - y_trans = -0.041337, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 75.000000,--FOV - hAngle = 0.000000, - vAngle = -23.000000, - x_trans = 0.360000, - y_trans = -0.041337, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["A-10C"] = { -[1] = {-- player slot 1 - [1] = { - viewAngle = 65.000000,--FOV - hAngle = 0.000000, - vAngle = -26.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = { - viewAngle = 49.626770,--FOV - hAngle = 0.000000, - vAngle = -90.631294, - x_trans = 0.180499, - y_trans = -0.137064, - z_trans = -0.250000, - rollAngle = 0.000000, - }, - [3] = { - viewAngle = 30.395041,--FOV - hAngle = 0.000000, - vAngle = -94.329208, - x_trans = 0.372718, - y_trans = -0.054055, - z_trans = 0.250000, - rollAngle = 0.000000, - }, - [4] = { - viewAngle = 55.238567,--FOV - hAngle = 0.000000, - vAngle = -90.631294, - x_trans = 0.158523, - y_trans = -0.137064, - z_trans = 0.250000, - rollAngle = 0.000000, - }, - [5] = { - viewAngle = 35.000000,--FOV - hAngle = 0.000000, - vAngle = -10.651850, - x_trans = 0.327622, - y_trans = -0.278207, - z_trans = -0.244799, - rollAngle = 0.000000, - }, - [6] = { - viewAngle = 34.340549,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = { - viewAngle = 35.000000,--FOV - hAngle = 0.000000, - vAngle = -10.651850, - x_trans = 0.327622, - y_trans = -0.278207, - z_trans = 0.244799, - rollAngle = 0.000000, - }, - [8] = { - viewAngle = 68.628296,--FOV - hAngle = 68.292320, - vAngle = -11.477349, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = { - viewAngle = 68.628296,--FOV - hAngle = 0.000000, - vAngle = 30.227919, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = { - viewAngle = 68.628296,--FOV - hAngle = -67.172974, - vAngle = -11.477349, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 70.000000,--FOV - hAngle = 20.000000, - vAngle = 8.000000, - x_trans = 0.360000, - y_trans = -0.041337, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 70.000000,--FOV - hAngle = -20.000000, - vAngle = 8.000000, - x_trans = 0.360000, - y_trans = -0.041337, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 75.000000,--FOV - hAngle = 0.000000, - vAngle = -23.000000, - x_trans = 0.360000, - y_trans = -0.041337, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["F-15C"] = { -[1] = {-- player slot 1 - [1] = { - viewAngle = 70.611748,--FOV - hAngle = -1.240272, - vAngle = -33.850250, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = { - viewAngle = 32.704346,--FOV - hAngle = 25.696522, - vAngle = -34.778103, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = { - viewAngle = 32.704346,--FOV - hAngle = 0.000000, - vAngle = -47.845268, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = { - viewAngle = 36.106045,--FOV - hAngle = -28.878576, - vAngle = -36.780628, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = { - viewAngle = 88.727844,--FOV - hAngle = 128.508865, - vAngle = 13.131046, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = { - viewAngle = 41.928593,--FOV - hAngle = 0.000000, - vAngle = -4.630446, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = { - viewAngle = 88.727844,--FOV - hAngle = -128.508865, - vAngle = 13.131046, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = { - viewAngle = 88.727844,--FOV - hAngle = 81.648369, - vAngle = -9.500000, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = { - viewAngle = 88.727844,--FOV - hAngle = 0.000000, - vAngle = 34.180634, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = { - viewAngle = 88.727844,--FOV - hAngle = -80.997551, - vAngle = -9.500000, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 56.032040,--FOV - hAngle = 14.803060, - vAngle = 3.332499, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 56.032040,--FOV - hAngle = -14.414484, - vAngle = 3.332499, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 88.727844,--FOV - hAngle = 0.000000, - vAngle = -9.678451, - x_trans = 0.264295, - y_trans = -0.064373, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["Ka-50"] = { -[1] = {-- player slot 1 - [1] = { - viewAngle = 67.452896,--FOV - hAngle = 0.000000, - vAngle = -40.067383, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = { - viewAngle = 37.846794,--FOV - hAngle = 51.644135, - vAngle = -51.870411, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = { - viewAngle = 36.178646,--FOV - hAngle = -1.912186, - vAngle = -34.446247, - x_trans = 0.000000, - y_trans = -0.025421, - z_trans = 0.073226, - rollAngle = 0.000000, - }, - [4] = { - viewAngle = 73.605141,--FOV - hAngle = -90.361992, - vAngle = -44.103138, - x_trans = 0.169696, - y_trans = -0.073508, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = { - viewAngle = 91.348198,--FOV - hAngle = 109.752129, - vAngle = 1.484382, - x_trans = 0.190306, - y_trans = 0.044778, - z_trans = -0.150335, - rollAngle = 0.000000, - }, - [6] = { - viewAngle = 42.512844,--FOV - hAngle = 0.000000, - vAngle = -4.478010, - x_trans = 0.154018, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = { - viewAngle = 91.348198,--FOV - hAngle = -108.852020, - vAngle = 0.085984, - x_trans = 0.190306, - y_trans = 0.044778, - z_trans = 0.139404, - rollAngle = 0.000000, - }, - [8] = { - viewAngle = 89.777542,--FOV - hAngle = 16.411518, - vAngle = -27.209915, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = -0.218292, - rollAngle = 0.000000, - }, - [9] = { - viewAngle = 88.727844,--FOV - hAngle = 0.000000, - vAngle = 34.042202, - x_trans = 0.142145, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = { - viewAngle = 59.208893,--FOV - hAngle = -32.128311, - vAngle = -5.720805, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 56.032040,--FOV - hAngle = 14.803060, - vAngle = 3.332499, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 56.032040,--FOV - hAngle = -14.414484, - vAngle = 3.332499, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 89.777542,--FOV - hAngle = 0.000000, - vAngle = -15.592758, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["MiG-29A"] = { -[1] = {-- player slot 1 - [1] = { - viewAngle = 76.124840,--FOV - hAngle = -2.623254, - vAngle = -26.566959, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = { - viewAngle = 34.911949,--FOV - hAngle = 24.601770, - vAngle = -32.350807, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = { - viewAngle = 26.184198,--FOV - hAngle = 12.026249, - vAngle = -40.075508, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = { - viewAngle = 39.454399,--FOV - hAngle = -26.664328, - vAngle = -32.355324, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = { - viewAngle = 81.240005,--FOV - hAngle = 131.503998, - vAngle = 10.804660, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = { - viewAngle = 44.201855,--FOV - hAngle = 0.000000, - vAngle = -2.378299, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = { - viewAngle = 81.240005,--FOV - hAngle = -131.503998, - vAngle = 10.804660, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = { - viewAngle = 81.240005,--FOV - hAngle = 76.013145, - vAngle = 2.248441, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = { - viewAngle = 81.240005,--FOV - hAngle = 0.000000, - vAngle = 36.304676, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = { - viewAngle = 81.240005,--FOV - hAngle = -74.774559, - vAngle = 2.248441, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 68.250000,--FOV - hAngle = 13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 68.250000,--FOV - hAngle = -13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 81.240005,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["MiG-29G"] = { -[1] = {-- player slot 1 - [1] = { - viewAngle = 76.124840,--FOV - hAngle = -2.623254, - vAngle = -26.566959, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = { - viewAngle = 34.911949,--FOV - hAngle = 24.601770, - vAngle = -32.350807, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = { - viewAngle = 26.184198,--FOV - hAngle = 12.026249, - vAngle = -40.075508, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = { - viewAngle = 39.454399,--FOV - hAngle = -26.664328, - vAngle = -32.355324, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = { - viewAngle = 81.240005,--FOV - hAngle = 131.503998, - vAngle = 10.804660, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = { - viewAngle = 44.201855,--FOV - hAngle = 0.000000, - vAngle = -2.378299, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = { - viewAngle = 81.240005,--FOV - hAngle = -131.503998, - vAngle = 10.804660, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = { - viewAngle = 81.240005,--FOV - hAngle = 76.013145, - vAngle = 2.248441, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = { - viewAngle = 81.240005,--FOV - hAngle = 0.000000, - vAngle = 36.304676, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = { - viewAngle = 81.240005,--FOV - hAngle = -74.774559, - vAngle = 2.248441, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 68.250000,--FOV - hAngle = 13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 68.250000,--FOV - hAngle = -13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 81.240005,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["MiG-29K"] = { -[1] = {-- player slot 1 - [1] = { - viewAngle = 65.000000,--FOV - hAngle = 0.000000, - vAngle = -26.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = { - viewAngle = 30.000000,--FOV - hAngle = 20.000000, - vAngle = -43.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = { - viewAngle = 30.000000,--FOV - hAngle = 0.000000, - vAngle = -43.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = { - viewAngle = 30.000000,--FOV - hAngle = -20.000000, - vAngle = -43.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = { - viewAngle = 30.000000,--FOV - hAngle = 20.000000, - vAngle = -23.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = { - viewAngle = 30.000000,--FOV - hAngle = 0.000000, - vAngle = -23.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = { - viewAngle = 30.000000,--FOV - hAngle = -20.000000, - vAngle = -23.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = { - viewAngle = 30.000000,--FOV - hAngle = 20.000000, - vAngle = 2.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = { - viewAngle = 30.000000,--FOV - hAngle = 0.000000, - vAngle = 2.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = { - viewAngle = 30.000000,--FOV - hAngle = -20.000000, - vAngle = 2.000000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 68.250000,--FOV - hAngle = 13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 68.250000,--FOV - hAngle = -13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 60.000000,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["MiG-29S"] = { -[1] = {-- player slot 1 - [1] = { - viewAngle = 76.124840,--FOV - hAngle = -2.623254, - vAngle = -26.566959, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = { - viewAngle = 34.911949,--FOV - hAngle = 24.601770, - vAngle = -32.350807, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = { - viewAngle = 26.184198,--FOV - hAngle = 12.026249, - vAngle = -40.075508, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = { - viewAngle = 39.454399,--FOV - hAngle = -26.664328, - vAngle = -32.355324, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = { - viewAngle = 81.240005,--FOV - hAngle = 131.503998, - vAngle = 10.804660, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = { - viewAngle = 44.201855,--FOV - hAngle = 0.000000, - vAngle = -2.378299, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = { - viewAngle = 81.240005,--FOV - hAngle = -131.503998, - vAngle = 10.804660, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = { - viewAngle = 81.240005,--FOV - hAngle = 76.013145, - vAngle = 2.248441, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = { - viewAngle = 81.240005,--FOV - hAngle = 0.000000, - vAngle = 36.304676, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = { - viewAngle = 81.240005,--FOV - hAngle = -74.774559, - vAngle = 2.248441, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 68.250000,--FOV - hAngle = 13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 68.250000,--FOV - hAngle = -13.070938, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 81.240005,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["P-51D"] = { -[1] = {-- player slot 1 - [1] = { - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = -45.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = { - viewAngle = 80.000000,--FOV - hAngle = 45.000000, - vAngle = -45.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = { - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = -75.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = { - viewAngle = 80.000000,--FOV - hAngle = -45.000000, - vAngle = -45.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = { - viewAngle = 91.040001,--FOV - hAngle = 157.332764, - vAngle = -28.359503, - x_trans = 0.063872, - y_trans = 0.082888, - z_trans = -0.116148, - rollAngle = 0.000000, - }, - [6] = { - viewAngle = 50.000000,--FOV - hAngle = 0.000000, - vAngle = -8.722581, - x_trans = 0.212078, - y_trans = 0.057813, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = { - viewAngle = 80.000000,--FOV - hAngle = -143.000000, - vAngle = 0.000000, - x_trans = 0.350000, - y_trans = 0.059000, - z_trans = 0.100000, - rollAngle = 0.000000, - }, - [8] = { - viewAngle = 80.000000,--FOV - hAngle = 45.000000, - vAngle = -5.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = { - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = 10.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = { - viewAngle = 80.000000,--FOV - hAngle = -45.000000, - vAngle = -5.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = 10.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 80.000000,--FOV - hAngle = -20.000000, - vAngle = 8.000000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 80.000000,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.120000, - y_trans = 0.059000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["Su-25"] = { -[1] = {-- player slot 1 - [1] = { - viewAngle = 68.767799,--FOV - hAngle = 1.929517, - vAngle = -30.846605, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = { - viewAngle = 29.223452,--FOV - hAngle = 37.489525, - vAngle = -38.883888, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = { - viewAngle = 40.635601,--FOV - hAngle = -0.438357, - vAngle = -33.138290, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = { - viewAngle = 24.797405,--FOV - hAngle = -34.382549, - vAngle = -34.808853, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = { - viewAngle = 69.302101,--FOV - hAngle = 89.405373, - vAngle = 1.213156, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = { - viewAngle = 29.761202,--FOV - hAngle = 0.000000, - vAngle = -6.880077, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = { - viewAngle = 69.302101,--FOV - hAngle = -89.691940, - vAngle = 4.554290, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = { - viewAngle = 69.302101,--FOV - hAngle = 52.113377, - vAngle = -3.970644, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = { - viewAngle = 72.856201,--FOV - hAngle = 0.000000, - vAngle = 30.866713, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = { - viewAngle = 69.302101,--FOV - hAngle = -50.664936, - vAngle = -3.970644, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 47.680202,--FOV - hAngle = 43.054649, - vAngle = -7.799250, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 47.680202,--FOV - hAngle = -41.743240, - vAngle = -7.799250, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 69.302101,--FOV - hAngle = 0.000000, - vAngle = -15.137112, - x_trans = 0.050000, - y_trans = 0.010000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["Su-25T"] = { -[1] = {-- player slot 1 - [1] = { - viewAngle = 80.663399,--FOV - hAngle = 0.000000, - vAngle = -30.619938, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = { - viewAngle = 39.764698,--FOV - hAngle = 28.661316, - vAngle = -41.406044, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = { - viewAngle = 38.090847,--FOV - hAngle = -24.622110, - vAngle = -45.153934, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = { - viewAngle = 36.062012,--FOV - hAngle = -20.779360, - vAngle = -23.755520, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = { - viewAngle = 80.663399,--FOV - hAngle = 99.816956, - vAngle = 8.032285, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = { - viewAngle = 58.718098,--FOV - hAngle = 0.000000, - vAngle = -5.000803, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = { - viewAngle = 80.663399,--FOV - hAngle = -99.999687, - vAngle = 8.032285, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = { - viewAngle = 80.663399,--FOV - hAngle = 58.382488, - vAngle = -6.648195, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = { - viewAngle = 94.037704,--FOV - hAngle = 0.000000, - vAngle = 41.421227, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = { - viewAngle = 80.663399,--FOV - hAngle = -57.531212, - vAngle = -6.648195, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 60.203396,--FOV - hAngle = 55.124939, - vAngle = -8.400513, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 60.203396,--FOV - hAngle = -52.633553, - vAngle = -8.400513, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 90.000000,--FOV - hAngle = 0.000000, - vAngle = -18.382137, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["Su-25TM"] = { -[1] = {-- player slot 1 - [1] = { - viewAngle = 80.663399,--FOV - hAngle = 0.000000, - vAngle = -30.619938, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = { - viewAngle = 39.764698,--FOV - hAngle = 28.661316, - vAngle = -41.406044, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = { - viewAngle = 38.090847,--FOV - hAngle = -24.622110, - vAngle = -45.153934, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = { - viewAngle = 33.645596,--FOV - hAngle = -36.653450, - vAngle = -23.703861, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = { - viewAngle = 80.663399,--FOV - hAngle = 99.816956, - vAngle = 8.032285, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = { - viewAngle = 58.718098,--FOV - hAngle = 0.000000, - vAngle = -5.000803, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = { - viewAngle = 80.663399,--FOV - hAngle = -99.999687, - vAngle = 8.032285, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = { - viewAngle = 80.663399,--FOV - hAngle = 58.382488, - vAngle = -6.648195, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = { - viewAngle = 94.037704,--FOV - hAngle = 0.000000, - vAngle = 41.421227, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = { - viewAngle = 80.663399,--FOV - hAngle = -57.531212, - vAngle = -6.648195, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 60.203396,--FOV - hAngle = 55.124939, - vAngle = -8.400513, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 60.203396,--FOV - hAngle = -52.633553, - vAngle = -8.400513, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 90.000000,--FOV - hAngle = 0.000000, - vAngle = -18.382137, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["Su-27"] = { -[1] = {-- player slot 1 - [1] = { - viewAngle = 71.824692,--FOV - hAngle = 0.000000, - vAngle = -32.458889, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = { - viewAngle = 33.361835,--FOV - hAngle = 41.045925, - vAngle = -40.805656, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = { - viewAngle = 30.427544,--FOV - hAngle = 0.000000, - vAngle = -41.808968, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = { - viewAngle = 34.392349,--FOV - hAngle = -32.597401, - vAngle = -35.293747, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = { - viewAngle = 87.468338,--FOV - hAngle = 129.012665, - vAngle = 14.547977, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = { - viewAngle = 43.977936,--FOV - hAngle = 0.000000, - vAngle = -4.951577, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = { - viewAngle = 87.468338,--FOV - hAngle = -129.012665, - vAngle = 14.491872, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = { - viewAngle = 87.468338,--FOV - hAngle = 82.862923, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = { - viewAngle = 87.468338,--FOV - hAngle = 0.000000, - vAngle = 38.979362, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = { - viewAngle = 87.468338,--FOV - hAngle = -82.461266, - vAngle = -12.843998, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 68.786629,--FOV - hAngle = 15.618313, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 69.165199,--FOV - hAngle = -15.683434, - vAngle = 8.549150, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 87.468338,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.113927, - y_trans = -0.004946, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} -SnapViews["Su-33"] = { -[1] = {-- player slot 1 - [1] = { - viewAngle = 71.824692,--FOV - hAngle = 0.000000, - vAngle = -32.458889, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [2] = { - viewAngle = 33.361835,--FOV - hAngle = 41.045925, - vAngle = -40.805656, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [3] = { - viewAngle = 30.427544,--FOV - hAngle = 0.000000, - vAngle = -41.808968, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [4] = { - viewAngle = 34.392349,--FOV - hAngle = -32.597401, - vAngle = -35.293747, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [5] = { - viewAngle = 87.468338,--FOV - hAngle = 129.012665, - vAngle = 14.547977, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [6] = { - viewAngle = 43.977936,--FOV - hAngle = 0.000000, - vAngle = -4.951577, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [7] = { - viewAngle = 87.468338,--FOV - hAngle = -129.012665, - vAngle = 14.491872, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [8] = { - viewAngle = 87.468338,--FOV - hAngle = 82.862923, - vAngle = -9.500000, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [9] = { - viewAngle = 87.468338,--FOV - hAngle = 0.000000, - vAngle = 38.979362, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [10] = { - viewAngle = 87.468338,--FOV - hAngle = -82.461266, - vAngle = -12.843998, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [11] = {--look at left mirror - viewAngle = 68.786629,--FOV - hAngle = 15.618313, - vAngle = 7.522498, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [12] = {--look at right mirror - viewAngle = 69.165199,--FOV - hAngle = -15.683434, - vAngle = 8.549150, - x_trans = 0.000000, - y_trans = 0.000000, - z_trans = 0.000000, - rollAngle = 0.000000, - }, - [13] = {--default view - viewAngle = 87.468338,--FOV - hAngle = 0.000000, - vAngle = -9.500000, - x_trans = 0.113927, - y_trans = -0.004946, - z_trans = 0.000000, - rollAngle = 0.000000, - }, -}, -} \ No newline at end of file diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Config/View/View.lua b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Config/View/View.lua deleted file mode 100644 index 9baf3b7df..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Config/View/View.lua +++ /dev/null @@ -1,128 +0,0 @@ --- View scripts --- Copyright (C) 2004, Eagle Dynamics. - -CockpitMouse = true --false -CockpitMouseSpeedSlow = 1.0 -CockpitMouseSpeedNormal = 10.0 -CockpitMouseSpeedFast = 20.0 -CockpitKeyboardAccelerationSlow = 5.0 -CockpitKeyboardAccelerationNormal = 30.0 -CockpitKeyboardAccelerationFast = 80.0 -CockpitKeyboardZoomAcceleration = 300.0 -DisableSnapViewsSaving = false -UseDefaultSnapViews = true -CockpitPanStepHor = 45.0 -CockpitPanStepVert = 30.0 -CockpitNyMove = true - -CockpitHAngleAccelerateTimeMax = 0.15 -CockpitVAngleAccelerateTimeMax = 0.15 -CockpitZoomAccelerateTimeMax = 0.2 - -function NaturalHeadMoving(tang, roll, omz) - local r = roll - if r > 90.0 then - r = 180.0 - r - elseif roll < -90.0 then - r = -180.0 - r - end - local hAngle = -0.25 * r - local vAngle = math.min(math.max(0.0, 0.4 * tang + 45.0 * omz), 90.0) - return hAngle, vAngle -end - -ExternalMouse = true -ExternalMouseSpeedSlow = 1.0 -ExternalMouseSpeedNormal = 5.0 -ExternalMouseSpeedFast = 20.0 -ExternalViewAngleMin = 3.0 -ExternalViewAngleMax = 170.0 -ExternalViewAngleDefault = 60.0 -ExternalKeyboardZoomAcceleration = 30.0 -ExternalKeyboardZoomAccelerateTimeMax = 1.0 -ExplosionExpoTime = 4.0 -ExternalKeyboardAccelerationSlow = 1.0 -ExternalKeyboardAccelerationNormal = 10.0 -ExternalKeyboardAccelerationFast = 30.0 -ExternalHAngleAccelerateTimeMax = 3.0 -ExternalVAngleAccelerateTimeMax = 3.0 -ExternalDistAccelerateTimeMax = 3.0 -ExternalHAngleLocalAccelerateTimeMax = 3.0 -ExternalVAngleLocalAccelerateTimeMax = 3.0 -ExternalAngleNormalDiscreteStep = 15.0/ExternalKeyboardAccelerationNormal -- When 'S' is pressed only -ChaseCameraNyMove = true -FreeCameraAngleIncrement = 3.0 -FreeCameraDistanceIncrement = 200.0 -FreeCameraLeftRightIncrement = 2.0 -FreeCameraAltitudeIncrement = 2.0 -FreeCameraScalarSpeedAcceleration = 0.1 -xMinMap = -300000 -xMaxMap = 500000 -yMinMap = -400000 -yMaxMap = 200000 -dxMap = 150000 -dyMap = 100000 - -head_roll_shaking = true -head_roll_shaking_max = 30.0 -head_roll_shaking_compensation_gain = 0.3 - --- CameraJiggle() and CameraFloat() functions make camera position --- dependent on FPS so be careful in using the Shift-J command with tracks, please. --- uncomment to use custom jiggle functions ---[[ -function CameraJiggle(t,rnd1,rnd2,rnd3) - local rotX, rotY, rotZ - rotX = 0.05 * rnd1 * math.sin(37.0 * (t - 0.0)) - rotY = 0.05 * rnd2 * math.sin(41.0 * (t - 1.0)) - rotZ = 0.05 * rnd3 * math.sin(53.0 * (t - 2.0)) - return rotX, rotY, rotZ -end - -function CameraFloat(t) - local dX, dY, dZ - dX = 0.61 * math.sin(0.7 * t) + 0.047 * math.sin(1.6 * t); - dY = 0.43 * math.sin(0.6 * t) + 0.067 * math.sin(1.7 * t); - dZ = 0.53 * math.sin(1.0 * t) + 0.083 * math.sin(1.9 * t); - return dX, dY, dZ -end ---]] ---Debug keys - -DEBUG_TEXT = 1 -DEBUG_GEOMETRY = 2 - -debug_keys = { - [DEBUG_TEXT] = 1, - [DEBUG_GEOMETRY] = 1 -} - -function onDebugCommand(command) - if command == 10000 then - if debug_keys[DEBUG_TEXT] ~= 0 or debug_keys[DEBUG_GEOMETRY] ~= 0 then - debug_keys[DEBUG_GEOMETRY] = 0 - debug_keys[DEBUG_TEXT] = 0 - else - debug_keys[DEBUG_GEOMETRY] = 1 - debug_keys[DEBUG_TEXT] = 1 - end - elseif command == 10001 then - if debug_keys[DEBUG_TEXT] ~= 0 then - debug_keys[DEBUG_TEXT] = 0 - else - debug_keys[DEBUG_TEXT] = 1 - end - elseif command == 10002 then - if debug_keys[DEBUG_GEOMETRY] ~= 0 then - debug_keys[DEBUG_GEOMETRY] = 0 - else - debug_keys[DEBUG_GEOMETRY] = 1 - end - end -end - --- gain values for TrackIR , to unify responce on diffrent types of aircraft -TrackIR_gain_x = -0.6 -TrackIR_gain_y = 0.3 -TrackIR_gain_z = -0.25 -TrackIR_gain_roll = -90 \ No newline at end of file diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Scripts/World/GPS_GNSS.lua b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Scripts/World/GPS_GNSS.lua deleted file mode 100644 index 2cb4a8e8d..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Scripts/World/GPS_GNSS.lua +++ /dev/null @@ -1,880 +0,0 @@ -SAT_SYS_GLONASS = 0 -SAT_SYS_GPS = 1 - -almanac = {} ---GPS -almanac[0] = {} -almanac[0]["System"] = SAT_SYS_GPS -almanac[0]["Number"] = 1 -almanac[0]["Orbital"] = "F" -almanac[0]["Eccentricity"] = 6.294000e-003 -almanac[0]["Time_of_Applicability"] = 5.898240e+005 -almanac[0]["Orbital_Inclination"] = 9.885676e-001 -almanac[0]["Rate_of_Right_Ascen"] = -7.862702e-009 -almanac[0]["SQRT_A"] = 5.153700e+003 -almanac[0]["Right_Ascen_at_Week"] = 8.096750e-001 -almanac[0]["Argument_of_Perigee"] = -1.777773e+000 -almanac[0]["Mean_Anom"] = -5.315745e-001 -almanac[0]["week"] = 1390 - -almanac[1] = {} -almanac[1]["System"] = SAT_SYS_GPS -almanac[1]["Number"] = 2 -almanac[1]["Orbital"] = "C" -almanac[1]["Eccentricity"] = 8.794000e-003 -almanac[1]["Time_of_Applicability"] = 5.898240e+005 -almanac[1]["Orbital_Inclination"] = 9.487811e-001 -almanac[1]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[1]["SQRT_A"] = 5.153700e+003 -almanac[1]["Right_Ascen_at_Week"] = -1.329172e+000 -almanac[1]["Argument_of_Perigee"] = 2.138637e+000 -almanac[1]["Mean_Anom"] = 7.311702e-001 -almanac[1]["week"] = 1390 - -almanac[2] = {} -almanac[2]["System"] = SAT_SYS_GPS -almanac[2]["Number"] = 3 -almanac[2]["Orbital"] = "F" -almanac[2]["Eccentricity"] = 8.424000e-003 -almanac[2]["Time_of_Applicability"] = 5.898240e+005 -almanac[2]["Orbital_Inclination"] = 9.262804e-001 -almanac[2]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[2]["SQRT_A"] = 5.153600e+003 -almanac[2]["Right_Ascen_at_Week"] = -2.341514e+000 -almanac[2]["Argument_of_Perigee"] = 6.749357e-001 -almanac[2]["Mean_Anom"] = -2.296153e-001 -almanac[2]["week"] = 1389 - -almanac[3] = {} -almanac[3]["System"] = SAT_SYS_GPS -almanac[3]["Number"] = 4 -almanac[3]["Orbital"] = "D" -almanac[3]["Eccentricity"] = 7.413000e-003 -almanac[3]["Time_of_Applicability"] = 5.898240e+005 -almanac[3]["Orbital_Inclination"] = 9.482889e-001 -almanac[3]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[3]["SQRT_A"] = 5.153600e+003 -almanac[3]["Right_Ascen_at_Week"] = -1.309589e+000 -almanac[3]["Argument_of_Perigee"] = 1.623504e-001 -almanac[3]["Mean_Anom"] = -3.022943e+000 -almanac[3]["week"] = 1390 - -almanac[4] = {} -almanac[4]["System"] = SAT_SYS_GPS -almanac[4]["Number"] = 5 -almanac[4]["Orbital"] = "B" -almanac[4]["Eccentricity"] = 7.432000e-003 -almanac[4]["Time_of_Applicability"] = 5.898240e+005 -almanac[4]["Orbital_Inclination"] = 9.387437e-001 -almanac[4]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[4]["SQRT_A"] = 5.153700e+003 -almanac[4]["Right_Ascen_at_Week"] = 2.779487e+000 -almanac[4]["Argument_of_Perigee"] = 1.099033e+000 -almanac[4]["Mean_Anom"] = 2.970984e+000 -almanac[4]["week"] = 1390 - -almanac[5] = {} -almanac[5]["System"] = SAT_SYS_GPS -almanac[5]["Number"] = 6 -almanac[5]["Orbital"] = "C" -almanac[5]["Eccentricity"] = 6.020000e-003 -almanac[5]["Time_of_Applicability"] = 5.898240e+005 -almanac[5]["Orbital_Inclination"] = 9.337591e-001 -almanac[5]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[5]["SQRT_A"] = 5.153600e+003 -almanac[5]["Right_Ascen_at_Week"] = -2.407627e+000 -almanac[5]["Argument_of_Perigee"] = -1.788263e+000 -almanac[5]["Mean_Anom"] = -2.149877e+000 -almanac[5]["week"] = 1390 - -almanac[6] = {} -almanac[6]["System"] = SAT_SYS_GPS -almanac[6]["Number"] = 7 -almanac[6]["Orbital"] = "C" -almanac[6]["Eccentricity"] = 1.052400e-002 -almanac[6]["Time_of_Applicability"] = 5.898240e+005 -almanac[6]["Orbital_Inclination"] = 9.353229e-001 -almanac[6]["Rate_of_Right_Ascen"] = -8.080868e-009 -almanac[6]["SQRT_A"] = 5.153700e+003 -almanac[6]["Right_Ascen_at_Week"] = -2.433580e+000 -almanac[6]["Argument_of_Perigee"] = -1.767301e+000 -almanac[6]["Mean_Anom"] = -3.141503e+000 -almanac[6]["week"] = 1390 - -almanac[7] = {} -almanac[7]["System"] = SAT_SYS_GPS -almanac[7]["Number"] = 8 -almanac[7]["Orbital"] = "A" -almanac[7]["Eccentricity"] = 9.822000e-003 -almanac[7]["Time_of_Applicability"] = 5.898240e+005 -almanac[7]["Orbital_Inclination"] = 9.741390e-001 -almanac[7]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[7]["SQRT_A"] = 5.153600e+003 -almanac[7]["Right_Ascen_at_Week"] = 1.857849e+000 -almanac[7]["Argument_of_Perigee"] = 2.674034e+000 -almanac[7]["Mean_Anom"] = -2.009745e+000 -almanac[7]["week"] = 1390 - -almanac[8] = {} -almanac[8]["System"] = SAT_SYS_GPS -almanac[8]["Number"] = 9 -almanac[8]["Orbital"] = "A" -almanac[8]["Eccentricity"] = 1.839300e-002 -almanac[8]["Time_of_Applicability"] = 5.898240e+005 -almanac[8]["Orbital_Inclination"] = 9.617541e-001 -almanac[8]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[8]["SQRT_A"] = 5.153600e+003 -almanac[8]["Right_Ascen_at_Week"] = 1.777005e+000 -almanac[8]["Argument_of_Perigee"] = 1.274962e+000 -almanac[8]["Mean_Anom"] = -2.349578e+000 -almanac[8]["week"] = 1390 - -almanac[9] = {} -almanac[9]["System"] = SAT_SYS_GPS -almanac[9]["Number"] = 10 -almanac[9]["Orbital"] = "E" -almanac[9]["Eccentricity"] = 7.061000e-003 -almanac[9]["Time_of_Applicability"] = 5.898240e+005 -almanac[9]["Orbital_Inclination"] = 9.728876e-001 -almanac[9]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[9]["SQRT_A"] = 5.153600e+003 -almanac[9]["Right_Ascen_at_Week"] = -2.563014e-001 -almanac[9]["Argument_of_Perigee"] = 4.377980e-001 -almanac[9]["Mean_Anom"] = 1.210716e+000 -almanac[9]["week"] = 1390 - -almanac[10] = {} -almanac[10]["System"] = SAT_SYS_GPS -almanac[10]["Number"] = 11 -almanac[10]["Orbital"] = "D" -almanac[10]["Eccentricity"] = 5.744000e-003 -almanac[10]["Time_of_Applicability"] = 5.898240e+005 -almanac[10]["Orbital_Inclination"] = 8.959309e-001 -almanac[10]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[10]["SQRT_A"] = 5.153600e+003 -almanac[10]["Right_Ascen_at_Week"] = -1.478816e+000 -almanac[10]["Argument_of_Perigee"] = 3.750011e-001 -almanac[10]["Mean_Anom"] = -1.522048e+000 -almanac[10]["week"] = 1390 - -almanac[11] = {} -almanac[11]["System"] = SAT_SYS_GPS -almanac[11]["Number"] = 13 -almanac[11]["Orbital"] = "F" -almanac[11]["Eccentricity"] = 3.088000e-003 -almanac[11]["Time_of_Applicability"] = 5.898240e+005 -almanac[11]["Orbital_Inclination"] = 9.927564e-001 -almanac[11]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[11]["SQRT_A"] = 5.153700e+003 -almanac[11]["Right_Ascen_at_Week"] = 7.956600e-001 -almanac[11]["Argument_of_Perigee"] = 1.279395e+000 -almanac[11]["Mean_Anom"] = 1.004349e+000 -almanac[11]["week"] = 1390 - -almanac[12] = {} -almanac[12]["System"] = SAT_SYS_GPS -almanac[12]["Number"] = 14 -almanac[12]["Orbital"] = "F" -almanac[12]["Eccentricity"] = 2.591000e-003 -almanac[12]["Time_of_Applicability"] = 5.898240e+005 -almanac[12]["Orbital_Inclination"] = 9.868729e-001 -almanac[12]["Rate_of_Right_Ascen"] = -7.885391e-009 -almanac[12]["SQRT_A"] = 5.153600e+003 -almanac[12]["Right_Ascen_at_Week"] = 7.819592e-001 -almanac[12]["Argument_of_Perigee"] = -2.158621e+000 -almanac[12]["Mean_Anom"] = 5.412611e-001 -almanac[12]["week"] = 1390 - -almanac[13] = {} -almanac[13]["System"] = SAT_SYS_GPS -almanac[13]["Number"] = 15 -almanac[13]["Orbital"] = "D" -almanac[13]["Eccentricity"] = 9.828000e-003 -almanac[13]["Time_of_Applicability"] = 3.194880e+005 -almanac[13]["Orbital_Inclination"] = 9.554204e-001 -almanac[13]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[13]["SQRT_A"] = 5.153600e+003 -almanac[13]["Right_Ascen_at_Week"] = -1.123869e+000 -almanac[13]["Argument_of_Perigee"] = 2.690266e+000 -almanac[13]["Mean_Anom"] = 2.220476e+000 -almanac[13]["week"] = 1389 - -almanac[14] = {} -almanac[14]["System"] = SAT_SYS_GPS -almanac[14]["Number"] = 16 -almanac[14]["Orbital"] = "B" -almanac[14]["Eccentricity"] = 3.494000e-003 -almanac[14]["Time_of_Applicability"] = 5.898240e+005 -almanac[14]["Orbital_Inclination"] = 9.629340e-001 -almanac[14]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[14]["SQRT_A"] = 5.153700e+003 -almanac[14]["Right_Ascen_at_Week"] = 2.873124e+000 -almanac[14]["Argument_of_Perigee"] = -7.819243e-001 -almanac[14]["Mean_Anom"] = 2.623629e+000 -almanac[14]["week"] = 1390 - -almanac[15] = {} -almanac[15]["System"] = SAT_SYS_GPS -almanac[15]["Number"] = 17 -almanac[15]["Orbital"] = "C" -almanac[15]["Eccentricity"] = 2.141000e-003 -almanac[15]["Time_of_Applicability"] = 5.898240e+005 -almanac[15]["Orbital_Inclination"] = 9.601170e-001 -almanac[15]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[15]["SQRT_A"] = 5.153700e+003 -almanac[15]["Right_Ascen_at_Week"] = -2.371499e+000 -almanac[15]["Argument_of_Perigee"] = 3.087694e+000 -almanac[15]["Mean_Anom"] = 1.611217e+000 -almanac[15]["week"] = 1390 - -almanac[16] = {} -almanac[16]["System"] = SAT_SYS_GPS -almanac[16]["Number"] = 18 -almanac[16]["Orbital"] = "E" -almanac[16]["Eccentricity"] = 7.636000e-003 -almanac[16]["Time_of_Applicability"] = 5.898240e+005 -almanac[16]["Orbital_Inclination"] = 9.569597e-001 -almanac[16]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[16]["SQRT_A"] = 5.153700e+003 -almanac[16]["Right_Ascen_at_Week"] = -2.359858e-001 -almanac[16]["Argument_of_Perigee"] = -2.649216e+000 -almanac[16]["Mean_Anom"] = 2.675029e+000 -almanac[16]["week"] = 1390 - -almanac[17] = {} -almanac[17]["System"] = SAT_SYS_GPS -almanac[17]["Number"] = 19 -almanac[17]["Orbital"] = "C" -almanac[17]["Eccentricity"] = 3.602000e-003 -almanac[17]["Time_of_Applicability"] = 5.898240e+005 -almanac[17]["Orbital_Inclination"] = 9.580209e-001 -almanac[17]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[17]["SQRT_A"] = 5.153600e+003 -almanac[17]["Right_Ascen_at_Week"] = -2.312385e+000 -almanac[17]["Argument_of_Perigee"] = -1.161079e+000 -almanac[17]["Mean_Anom"] = 1.310619e+000 -almanac[17]["week"] = 1390 - -almanac[18] = {} -almanac[18]["System"] = SAT_SYS_GPS -almanac[18]["Number"] = 20 -almanac[18]["Orbital"] = "E" -almanac[18]["Eccentricity"] = 2.796000e-003 -almanac[18]["Time_of_Applicability"] = 5.898240e+005 -almanac[18]["Orbital_Inclination"] = 9.564693e-001 -almanac[18]["Rate_of_Right_Ascen"] = -7.908080e-009 -almanac[18]["SQRT_A"] = 5.153600e+003 -almanac[18]["Right_Ascen_at_Week"] = -2.889565e-001 -almanac[18]["Argument_of_Perigee"] = 1.379612e+000 -almanac[18]["Mean_Anom"] = 2.461750e+000 -almanac[18]["week"] = 1390 - -almanac[19] = {} -almanac[19]["System"] = SAT_SYS_GPS -almanac[19]["Number"] = 21 -almanac[19]["Orbital"] = "D" -almanac[19]["Eccentricity"] = 1.162900e-002 -almanac[19]["Time_of_Applicability"] = 5.898240e+005 -almanac[19]["Orbital_Inclination"] = 9.418592e-001 -almanac[19]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[19]["SQRT_A"] = 5.153600e+003 -almanac[19]["Right_Ascen_at_Week"] = -1.289972e+000 -almanac[19]["Argument_of_Perigee"] = -2.923686e+000 -almanac[19]["Mean_Anom"] = -2.349194e+000 -almanac[19]["week"] = 1390 - -almanac[20] = {} -almanac[20]["System"] = SAT_SYS_GPS -almanac[20]["Number"] = 22 -almanac[20]["Orbital"] = "E" -almanac[20]["Eccentricity"] = 4.893000e-003 -almanac[20]["Time_of_Applicability"] = 5.898240e+005 -almanac[20]["Orbital_Inclination"] = 9.545093e-001 -almanac[20]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[20]["SQRT_A"] = 5.153600e+003 -almanac[20]["Right_Ascen_at_Week"] = -2.280969e-001 -almanac[20]["Argument_of_Perigee"] = -1.674502e+000 -almanac[20]["Mean_Anom"] = 1.106852e+000 -almanac[20]["week"] = 1390 - -almanac[21] = {} -almanac[21]["System"] = SAT_SYS_GPS -almanac[21]["Number"] = 23 -almanac[21]["Orbital"] = "F" -almanac[21]["Eccentricity"] = 4.822000e-003 -almanac[21]["Time_of_Applicability"] = 5.898240e+005 -almanac[21]["Orbital_Inclination"] = 9.691247e-001 -almanac[21]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[21]["SQRT_A"] = 5.153700e+003 -almanac[21]["Right_Ascen_at_Week"] = 7.667399e-001 -almanac[21]["Argument_of_Perigee"] = 2.497634e+000 -almanac[21]["Mean_Anom"] = 3.184700e-001 -almanac[21]["week"] = 1390 - -almanac[22] = {} -almanac[22]["System"] = SAT_SYS_GPS -almanac[22]["Number"] = 24 -almanac[22]["Orbital"] = "D" -almanac[22]["Eccentricity"] = 9.277000e-003 -almanac[22]["Time_of_Applicability"] = 5.898240e+005 -almanac[22]["Orbital_Inclination"] = 9.585183e-001 -almanac[22]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[22]["SQRT_A"] = 5.153900e+003 -almanac[22]["Right_Ascen_at_Week"] = -1.274840e+000 -almanac[22]["Argument_of_Perigee"] = -8.815651e-001 -almanac[22]["Mean_Anom"] = -1.695551e+000 -almanac[22]["week"] = 1390 - -almanac[23] = {} -almanac[23]["System"] = SAT_SYS_GPS -almanac[23]["Number"] = 25 -almanac[23]["Orbital"] = "A" -almanac[23]["Eccentricity"] = 1.257400e-002 -almanac[23]["Time_of_Applicability"] = 5.898240e+005 -almanac[23]["Orbital_Inclination"] = 9.551027e-001 -almanac[23]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[23]["SQRT_A"] = 5.153600e+003 -almanac[23]["Right_Ascen_at_Week"] = 1.721853e+000 -almanac[23]["Argument_of_Perigee"] = -1.329870e+000 -almanac[23]["Mean_Anom"] = -1.769623e+000 -almanac[23]["week"] = 1390 - -almanac[24] = {} -almanac[24]["System"] = SAT_SYS_GPS -almanac[24]["Number"] = 26 -almanac[24]["Orbital"] = "F" -almanac[24]["Eccentricity"] = 1.745700e-002 -almanac[24]["Time_of_Applicability"] = 5.898240e+005 -almanac[24]["Orbital_Inclination"] = 9.908749e-001 -almanac[24]["Rate_of_Right_Ascen"] = -7.840012e-009 -almanac[24]["SQRT_A"] = 5.153700e+003 -almanac[24]["Right_Ascen_at_Week"] = 7.961836e-001 -almanac[24]["Argument_of_Perigee"] = 8.161502e-001 -almanac[24]["Mean_Anom"] = -5.841961e-001 -almanac[24]["week"] = 1390 - -almanac[25] = {} -almanac[25]["System"] = SAT_SYS_GPS -almanac[25]["Number"] = 27 -almanac[25]["Orbital"] = "A" -almanac[25]["Eccentricity"] = 1.991000e-002 -almanac[25]["Time_of_Applicability"] = 5.898240e+005 -almanac[25]["Orbital_Inclination"] = 9.596563e-001 -almanac[25]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[25]["SQRT_A"] = 5.153600e+003 -almanac[25]["Right_Ascen_at_Week"] = 1.754124e+000 -almanac[25]["Argument_of_Perigee"] = -1.900854e+000 -almanac[25]["Mean_Anom"] = 3.046487e+000 -almanac[25]["week"] = 1390 - -almanac[26] = {} -almanac[26]["System"] = SAT_SYS_GPS -almanac[26]["Number"] = 28 -almanac[26]["Orbital"] = "B" -almanac[26]["Eccentricity"] = 1.162800e-002 -almanac[26]["Time_of_Applicability"] = 5.898240e+005 -almanac[26]["Orbital_Inclination"] = 9.610106e-001 -almanac[26]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[26]["SQRT_A"] = 5.153600e+003 -almanac[26]["Right_Ascen_at_Week"] = 2.882583e+000 -almanac[26]["Argument_of_Perigee"] = -2.242868e+000 -almanac[26]["Mean_Anom"] = 1.860642e+000 -almanac[26]["week"] = 1390 - -almanac[27] = {} -almanac[27]["System"] = SAT_SYS_GPS -almanac[27]["Number"] = 29 -almanac[27]["Orbital"] = "F" -almanac[27]["Eccentricity"] = 9.462000e-003 -almanac[27]["Time_of_Applicability"] = 1.474560e+005 -almanac[27]["Orbital_Inclination"] = 9.874838e-001 -almanac[27]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[27]["SQRT_A"] = 5.153700e+003 -almanac[27]["Right_Ascen_at_Week"] = 7.647503e-001 -almanac[27]["Argument_of_Perigee"] = -8.614589e-001 -almanac[27]["Mean_Anom"] = -4.488983e-001 -almanac[27]["week"] = 1390 - -almanac[28] = {} -almanac[28]["System"] = SAT_SYS_GPS -almanac[28]["Number"] = 30 -almanac[28]["Orbital"] = "B" -almanac[28]["Eccentricity"] = 9.296000e-003 -almanac[28]["Time_of_Applicability"] = 5.898240e+005 -almanac[28]["Orbital_Inclination"] = 9.452992e-001 -almanac[28]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[28]["SQRT_A"] = 5.153600e+003 -almanac[28]["Right_Ascen_at_Week"] = 2.826698e+000 -almanac[28]["Argument_of_Perigee"] = 1.306413e+000 -almanac[28]["Mean_Anom"] = 2.148725e+000 -almanac[28]["week"] = 1390 - - - - - - ---GLONASS ---1 îðáèòàëüíàÿ ïëîñêîñòü, íîìåðà 1-8 -almanac[29] = {} -almanac[29]["System"] = SAT_SYS_GLONASS -almanac[29]["Number"] = 1 -almanac[29]["Orbital"] = 1 -almanac[29]["GLONASS_Data"] = {} -almanac[29]["GLONASS_Data"]["NKU_Number"] = 796 -almanac[29]["GLONASS_Data"]["Cosmos_Number"] = 2411 -almanac[29]["Eccentricity"] = 1.184000e-003 -almanac[29]["Time_of_Applicability"] = 0.000000e+000 -almanac[29]["Orbital_Inclination"] = 1.126443e+000 -almanac[29]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[29]["SQRT_A"] = 5.050500e+003 -almanac[29]["Right_Ascen_at_Week"] = 5.979807e+000 -almanac[29]["Argument_of_Perigee"] = 2.622634e+000 -almanac[29]["Mean_Anom"] = -5.519651e+000 -almanac[29]["week"] = 1390 -almanac[29]["Commit_date"] = "06.02.2005" -almanac[29]["Life_dates"] = {} - -almanac[30] = {} -almanac[30]["System"] = SAT_SYS_GLONASS -almanac[30]["Number"] = 2 -almanac[30]["Orbital"] = 1 -almanac[30]["GLONASS_Data"] = {} -almanac[30]["GLONASS_Data"]["NKU_Number"] = 794 -almanac[30]["GLONASS_Data"]["Cosmos_Number"] = 2401 -almanac[30]["Eccentricity"] = 4.486000e-003 -almanac[30]["Time_of_Applicability"] = 0.000000e+000 -almanac[30]["Orbital_Inclination"] = 1.128459e+000 -almanac[30]["Rate_of_Right_Ascen"] = -6.759654e-009 -almanac[30]["SQRT_A"] = 5.050500e+003 -almanac[30]["Right_Ascen_at_Week"] = 5.997871e+000 -almanac[30]["Argument_of_Perigee"] = 1.709531e+000 -almanac[30]["Mean_Anom"] = -5.367633e+000 -almanac[30]["week"] = 1390 -almanac[30]["Commit_date"] = "02.02.2004" -almanac[30]["Life_dates"] = {} - -almanac[31] = {} -almanac[31]["System"] = SAT_SYS_GLONASS -almanac[31]["Number"] = 3 -almanac[31]["Orbital"] = 1 -almanac[31]["GLONASS_Data"] = {} -almanac[31]["GLONASS_Data"]["NKU_Number"] = 789 -almanac[31]["GLONASS_Data"]["Cosmos_Number"] = 2381 -almanac[31]["Eccentricity"] = 2.459000e-003 -almanac[31]["Time_of_Applicability"] = 0.000000e+000 -almanac[31]["Orbital_Inclination"] = 1.122958e+000 -almanac[31]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[31]["SQRT_A"] = 5.050500e+003 -almanac[31]["Right_Ascen_at_Week"] = 5.960713e+000 -almanac[31]["Argument_of_Perigee"] = -2.683407e+000 -almanac[31]["Mean_Anom"] = -1.791788e+000 -almanac[31]["week"] = 1390 -almanac[31]["Commit_date"] = "04.01.2002" -almanac[31]["Life_dates"] = {} - -almanac[32] = {} -almanac[32]["System"] = SAT_SYS_GLONASS -almanac[32]["Number"] = 4 -almanac[32]["Orbital"] = 1 -almanac[32]["GLONASS_Data"] = {} -almanac[32]["GLONASS_Data"]["NKU_Number"] = 795 -almanac[29]["GLONASS_Data"]["Cosmos_Number"] = 2403 -almanac[32]["Eccentricity"] = 4.054000e-003 -almanac[32]["Time_of_Applicability"] = 0.000000e+000 -almanac[32]["Orbital_Inclination"] = 1.128543e+000 -almanac[32]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[32]["SQRT_A"] = 5.050500e+003 -almanac[32]["Right_Ascen_at_Week"] = 5.998081e+000 -almanac[32]["Argument_of_Perigee"] = 1.497160e+000 -almanac[32]["Mean_Anom"] = -4.293681e-001 -almanac[32]["week"] = 1390 -almanac[32]["Commit_date"] = "29.01.2004" -almanac[32]["Life_dates"] = {} - -almanac[33] = {} -almanac[33]["System"] = SAT_SYS_GLONASS -almanac[33]["Number"] = 5 -almanac[33]["Orbital"] = 1 -almanac[33]["GLONASS_Data"] = {} -almanac[33]["GLONASS_Data"]["NKU_Number"] = 711 -almanac[33]["GLONASS_Data"]["Cosmos_Number"] = 2382 -almanac[33]["Eccentricity"] = 7.040000e-004 -almanac[33]["Time_of_Applicability"] = 0.000000e+000 -almanac[33]["Orbital_Inclination"] = 1.122886e+000 -almanac[33]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[33]["SQRT_A"] = 5.050600e+003 -almanac[33]["Right_Ascen_at_Week"] = 5.960713e+000 -almanac[33]["Argument_of_Perigee"] = 2.740933e+000 -almanac[33]["Mean_Anom"] = -2.523604e+000 -almanac[33]["week"] = 1390 -almanac[33]["Commit_date"] = "13.02.2003" -almanac[33]["Life_dates"] = {} - -almanac[34] = {} -almanac[34]["System"] = SAT_SYS_GLONASS -almanac[34]["Number"] = 6 -almanac[34]["Orbital"] = 1 -almanac[34]["GLONASS_Data"] = {} -almanac[34]["GLONASS_Data"]["NKU_Number"] = 701 -almanac[34]["GLONASS_Data"]["Cosmos_Number"] = 2404 -almanac[34]["Eccentricity"] = 4.766000e-003 -almanac[34]["Time_of_Applicability"] = 0.000000e+000 -almanac[34]["Orbital_Inclination"] = 1.128276e+000 -almanac[34]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[34]["SQRT_A"] = 5.050500e+003 -almanac[34]["Right_Ascen_at_Week"] = 5.997906e+000 -almanac[34]["Argument_of_Perigee"] = 1.802417e+000 -almanac[34]["Mean_Anom"] = -2.426512e+000 -almanac[34]["week"] = 1390 -almanac[34]["Commit_date"] = "08.12.2004" -almanac[34]["Life_dates"] = {} - -almanac[35] = {} -almanac[35]["System"] = SAT_SYS_GLONASS -almanac[35]["Number"] = 7 -almanac[35]["Orbital"] = 1 -almanac[35]["GLONASS_Data"] = {} -almanac[35]["GLONASS_Data"]["NKU_Number"] = 712 -almanac[35]["GLONASS_Data"]["Cosmos_Number"] = 2413 -almanac[35]["Eccentricity"] = 7.570000e-004 -almanac[35]["Time_of_Applicability"] = 0.000000e+000 -almanac[35]["Orbital_Inclination"] = 1.126344e+000 -almanac[35]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[35]["SQRT_A"] = 5.050500e+003 -almanac[35]["Right_Ascen_at_Week"] = 5.979388e+000 -almanac[35]["Argument_of_Perigee"] = 2.566068e+000 -almanac[35]["Mean_Anom"] = -3.921228e+000 -almanac[35]["week"] = 1390 -almanac[35]["Commit_date"] = "07.10.2005" -almanac[35]["Life_dates"] = {} - -almanac[36] = {} -almanac[36]["System"] = SAT_SYS_GLONASS -almanac[36]["GLONASS_Data"] = {} -almanac[36]["Number"] = 8 -almanac[36]["Orbital"] = 1 -almanac[36]["GLONASS_Data"] = {} -almanac[36]["GLONASS_Data"]["NKU_Number"] = 797 -almanac[36]["GLONASS_Data"]["Cosmos_Number"] = 2412 -almanac[36]["Eccentricity"] = 4.060000e-004 -almanac[36]["Time_of_Applicability"] = 0.000000e+000 -almanac[36]["Orbital_Inclination"] = 1.126564e+000 -almanac[36]["Rate_of_Right_Ascen"] = -6.785834e-009 -almanac[36]["SQRT_A"] = 5.050600e+003 -almanac[36]["Right_Ascen_at_Week"] = 5.980069e+000 -almanac[36]["Argument_of_Perigee"] = 2.673633e+000 -almanac[36]["Mean_Anom"] = -4.812026e+000 -almanac[36]["week"] = 1390 -almanac[36]["Commit_date"] = "06.02.2005" -almanac[36]["Life_dates"] = {} - ---3 îðáèòàëüíàÿ ïëîñêîñòü, íîìåðà 17-24 -almanac[37] = {} -almanac[37]["System"] = SAT_SYS_GLONASS -almanac[37]["Number"] = 17 -almanac[37]["Orbital"] = 3 -almanac[37]["GLONASS_Data"] = {} -almanac[37]["GLONASS_Data"]["NKU_Number"] = 787 -almanac[37]["GLONASS_Data"]["Cosmos_Number"] = 2375 -almanac[37]["Eccentricity"] = 5.670000e-004 -almanac[37]["Time_of_Applicability"] = 0.000000e+000 -almanac[37]["Orbital_Inclination"] = 1.126524e+000 -almanac[37]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[37]["SQRT_A"] = 5.050500e+003 -almanac[37]["Right_Ascen_at_Week"] = 3.895554e+000 -almanac[37]["Argument_of_Perigee"] = 6.085085e-001 -almanac[37]["Mean_Anom"] = -2.977407e+000 -almanac[37]["week"] = 1390 -almanac[37]["Commit_date"] = "04.11.2000" -almanac[37]["Life_dates"] = {} - - -almanac[38] = {} -almanac[38]["System"] = SAT_SYS_GLONASS -almanac[38]["Number"] = 18 -almanac[38]["Orbital"] = 3 -almanac[38]["GLONASS_Data"] = {} -almanac[38]["GLONASS_Data"]["NKU_Number"] = 783 -almanac[38]["GLONASS_Data"]["Cosmos_Number"] = 2374 -almanac[38]["Eccentricity"] = 4.520000e-003 -almanac[38]["Time_of_Applicability"] = 0.000000e+000 -almanac[38]["Orbital_Inclination"] = 1.126239e+000 -almanac[38]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[38]["SQRT_A"] = 5.050600e+003 -almanac[38]["Right_Ascen_at_Week"] = 3.894071e+000 -almanac[38]["Argument_of_Perigee"] = -2.509589e+000 -almanac[38]["Mean_Anom"] = -1.020057e+000 -almanac[38]["week"] = 1390 -almanac[38]["Commit_date"] = "05.01.2001" -almanac[38]["Life_dates"] = {} - -almanac[39] = {} -almanac[39]["System"] = SAT_SYS_GLONASS -almanac[39]["Number"] = 19 -almanac[39]["Orbital"] = 3 -almanac[39]["GLONASS_Data"] = {} -almanac[39]["GLONASS_Data"]["NKU_Number"] = 798 -almanac[39]["GLONASS_Data"]["Cosmos_Number"] = 2417 -almanac[39]["Eccentricity"] = 2.023000e-003 -almanac[39]["Time_of_Applicability"] = 0.000000e+000 -almanac[39]["Orbital_Inclination"] = 1.132205e+000 -almanac[39]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[39]["SQRT_A"] = 5.050500e+003 -almanac[39]["Right_Ascen_at_Week"] = 3.884018e+000 -almanac[39]["Argument_of_Perigee"] = 2.718313e+000 -almanac[39]["Mean_Anom"] = -3.933620e-001 -almanac[39]["week"] = 1390 -almanac[39]["Commit_date"] = "22.01.2006" -almanac[39]["Life_dates"] = {} - -almanac[40] = {} -almanac[40]["System"] = SAT_SYS_GLONASS -almanac[40]["Number"] = 20 -almanac[40]["Orbital"] = 3 -almanac[40]["GLONASS_Data"] = {} -almanac[40]["GLONASS_Data"]["NKU_Number"] = 793 -almanac[40]["GLONASS_Data"]["Cosmos_Number"] = 2396 -almanac[40]["Eccentricity"] = 1.822000e-003 -almanac[40]["Time_of_Applicability"] = 0.000000e+000 -almanac[40]["Orbital_Inclination"] = 1.129789e+000 -almanac[40]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[40]["SQRT_A"] = 5.050500e+003 -almanac[40]["Right_Ascen_at_Week"] = 3.896863e+000 -almanac[40]["Argument_of_Perigee"] = 2.723776e+000 -almanac[40]["Mean_Anom"] = -1.193647e+000 -almanac[40]["week"] = 1390 -almanac[40]["Commit_date"] = "31.01.2003" -almanac[40]["Life_dates"] = {} - -almanac[41] = {} -almanac[41]["System"] = SAT_SYS_GLONASS -almanac[41]["Number"] = 21 -almanac[41]["Orbital"] = 3 -almanac[41]["GLONASS_Data"] = {} -almanac[41]["GLONASS_Data"]["NKU_Number"] = 792 -almanac[41]["GLONASS_Data"]["Cosmos_Number"] = 2395 -almanac[41]["Eccentricity"] = 5.290000e-004 -almanac[41]["Time_of_Applicability"] = 0.000000e+000 -almanac[41]["Orbital_Inclination"] = 1.129957e+000 -almanac[41]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[41]["SQRT_A"] = 5.050500e+003 -almanac[41]["Right_Ascen_at_Week"] = 3.897806e+000 -almanac[41]["Argument_of_Perigee"] = -9.519367e-001 -almanac[41]["Mean_Anom"] = -4.578920e+000 -almanac[41]["week"] = 1390 -almanac[41]["Commit_date"] = "31.01.2003" -almanac[41]["Life_dates"] = {} - -almanac[42] = {} -almanac[42]["System"] = SAT_SYS_GLONASS -almanac[42]["Number"] = 22 -almanac[42]["Orbital"] = 3 -almanac[42]["GLONASS_Data"] = {} -almanac[42]["GLONASS_Data"]["NKU_Number"] = 791 -almanac[42]["GLONASS_Data"]["Cosmos_Number"] = 2394 -almanac[42]["Eccentricity"] = 9.200000e-005 -almanac[42]["Time_of_Applicability"] = 0.000000e+000 -almanac[42]["Orbital_Inclination"] = 1.129742e+000 -almanac[42]["Rate_of_Right_Ascen"] = -6.740456e-009 -almanac[42]["SQRT_A"] = 5.050500e+003 -almanac[42]["Right_Ascen_at_Week"] = 3.897404e+000 -almanac[42]["Argument_of_Perigee"] = 2.518211e+000 -almanac[42]["Mean_Anom"] = -2.530167e+000 -almanac[42]["week"] = 1390 -almanac[42]["Commit_date"] = "21.01.2003" -almanac[42]["Life_dates"] = {} - -almanac[43] = {} -almanac[43]["System"] = SAT_SYS_GLONASS -almanac[43]["Number"] = 23 -almanac[43]["Orbital"] = 3 -almanac[43]["GLONASS_Data"] = {} -almanac[43]["GLONASS_Data"]["NKU_Number"] = 714 -almanac[43]["GLONASS_Data"]["Cosmos_Number"] = 2419 -almanac[43]["Eccentricity"] = 8.730000e-004 -almanac[43]["Time_of_Applicability"] = 0.000000e+000 -almanac[43]["Orbital_Inclination"] = 1.132105e+000 -almanac[43]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[43]["SQRT_A"] = 5.050500e+003 -almanac[43]["Right_Ascen_at_Week"] = 3.883808e+000 -almanac[43]["Argument_of_Perigee"] = -3.039139e-001 -almanac[43]["Mean_Anom"] = -5.228304e-001 -almanac[43]["week"] = 1390 -almanac[43]["Commit_date"] = "31.08.2006" -almanac[43]["Life_dates"] = {} - -almanac[44] = {} -almanac[44]["System"] = SAT_SYS_GLONASS -almanac[44]["Number"] = 24 -almanac[44]["Orbital"] = 3 -almanac[44]["GLONASS_Data"] = {} -almanac[44]["GLONASS_Data"]["NKU_Number"] = 713 -almanac[44]["GLONASS_Data"]["Cosmos_Number"] = 2418 -almanac[44]["Eccentricity"] = 2.044000e-003 -almanac[44]["Time_of_Applicability"] = 0.000000e+000 -almanac[44]["Orbital_Inclination"] = 1.132430e+000 -almanac[44]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[44]["SQRT_A"] = 5.050500e+003 -almanac[44]["Right_Ascen_at_Week"] = 3.883983e+000 -almanac[44]["Argument_of_Perigee"] = -3.722784e-001 -almanac[44]["Mean_Anom"] = -1.240457e+000 -almanac[44]["week"] = 1390 -almanac[44]["Commit_date"] = "31.08.2006" -almanac[44]["Life_dates"] = {} - ---2 îðáèòàëüíàÿ ïëîñêîñòü, íîìåðà 9-16 -almanac[45] = {} -almanac[45]["System"] = SAT_SYS_GLONASS -almanac[45]["Number"] = 9 -almanac[45]["Orbital"] = 2 -almanac[45]["GLONASS_Data"] = {} -almanac[45]["GLONASS_Data"]["NKU_Number"] = "N/A" -almanac[45]["GLONASS_Data"]["Cosmos_Number"] = "N/A" -almanac[45]["Eccentricity"] = 1.184000e-003 -almanac[45]["Time_of_Applicability"] = 0.000000e+000 -almanac[45]["Orbital_Inclination"] = 1.126443e+000 -almanac[45]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[45]["SQRT_A"] = 5.050500e+003 -almanac[45]["Right_Ascen_at_Week"] = 1.79067e+000 -almanac[45]["Argument_of_Perigee"] = 2.88430067 -almanac[45]["Mean_Anom"] = -5.519651e+000 -almanac[45]["week"] = 1390 -almanac[45]["Commit_date"] = "N/A" -almanac[45]["Life_dates"] = {} - -almanac[46] = {} -almanac[46]["System"] = SAT_SYS_GLONASS -almanac[46]["Number"] = 10 -almanac[46]["Orbital"] = 2 -almanac[46]["GLONASS_Data"] = {} -almanac[46]["GLONASS_Data"]["NKU_Number"] = "N/A" -almanac[46]["GLONASS_Data"]["Cosmos_Number"] = "N/A" -almanac[46]["Eccentricity"] = 1.184000e-003 -almanac[46]["Time_of_Applicability"] = 0.000000e+000 -almanac[46]["Orbital_Inclination"] = 1.126443e+000 -almanac[46]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[46]["SQRT_A"] = 5.050500e+003 -almanac[46]["Right_Ascen_at_Week"] = 1.79067e+000 -almanac[46]["Argument_of_Perigee"] = 3.66930067 -almanac[46]["Mean_Anom"] = -5.519651e+000 -almanac[46]["week"] = 1390 -almanac[46]["Commit_date"] = "N/A" -almanac[46]["Life_dates"] = {} - -almanac[47] = {} -almanac[47]["System"] = SAT_SYS_GLONASS -almanac[47]["Number"] = 11 -almanac[47]["Orbital"] = 2 -almanac[47]["GLONASS_Data"] = {} -almanac[47]["GLONASS_Data"]["NKU_Number"] = "N/A" -almanac[47]["GLONASS_Data"]["Cosmos_Number"] = "N/A" -almanac[47]["Eccentricity"] = 1.184000e-003 -almanac[47]["Time_of_Applicability"] = 0.000000e+000 -almanac[47]["Orbital_Inclination"] = 1.126443e+000 -almanac[47]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[47]["SQRT_A"] = 5.050500e+003 -almanac[47]["Right_Ascen_at_Week"] = 1.79067e+000 -almanac[47]["Argument_of_Perigee"] = 4.45430067 -almanac[47]["Mean_Anom"] = -5.519651e+000 -almanac[47]["week"] = 1390 -almanac[47]["Commit_date"] = "N/A" -almanac[47]["Life_dates"] = {} - -almanac[48] = {} -almanac[48]["System"] = SAT_SYS_GLONASS -almanac[48]["Number"] = 12 -almanac[48]["Orbital"] = 2 -almanac[48]["GLONASS_Data"] = {} -almanac[48]["GLONASS_Data"]["NKU_Number"] = "N/A" -almanac[48]["GLONASS_Data"]["Cosmos_Number"] = "N/A" -almanac[48]["Eccentricity"] = 1.184000e-003 -almanac[48]["Time_of_Applicability"] = 0.000000e+000 -almanac[48]["Orbital_Inclination"] = 1.126443e+000 -almanac[48]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[48]["SQRT_A"] = 5.050500e+003 -almanac[48]["Right_Ascen_at_Week"] = 1.79067e+000 -almanac[48]["Argument_of_Perigee"] = 5.23930067 -almanac[48]["Mean_Anom"] = -5.519651e+000 -almanac[48]["week"] = 1390 -almanac[48]["Commit_date"] = "N/A" -almanac[48]["Life_dates"] = {} - -almanac[49] = {} -almanac[49]["System"] = SAT_SYS_GLONASS -almanac[49]["Number"] = 13 -almanac[49]["Orbital"] = 2 -almanac[49]["GLONASS_Data"] = {} -almanac[49]["GLONASS_Data"]["NKU_Number"] = "N/A" -almanac[49]["GLONASS_Data"]["Cosmos_Number"] = "N/A" -almanac[49]["Eccentricity"] = 1.184000e-003 -almanac[49]["Time_of_Applicability"] = 0.000000e+000 -almanac[49]["Orbital_Inclination"] = 1.126443e+000 -almanac[49]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[49]["SQRT_A"] = 5.050500e+003 -almanac[49]["Right_Ascen_at_Week"] = 1.79067e+000 -almanac[49]["Argument_of_Perigee"] = 6.02430067 -almanac[49]["Mean_Anom"] = -5.519651e+000 -almanac[49]["week"] = 1390 -almanac[49]["Commit_date"] = "N/A" -almanac[49]["Life_dates"] = {} - -almanac[50] = {} -almanac[50]["System"] = SAT_SYS_GLONASS -almanac[50]["Number"] = 14 -almanac[50]["Orbital"] = 2 -almanac[50]["GLONASS_Data"] = {} -almanac[50]["GLONASS_Data"]["NKU_Number"] = "N/A" -almanac[50]["GLONASS_Data"]["Cosmos_Number"] = "N/A" -almanac[50]["Eccentricity"] = 1.184000e-003 -almanac[50]["Time_of_Applicability"] = 0.000000e+000 -almanac[50]["Orbital_Inclination"] = 1.126443e+000 -almanac[50]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[50]["SQRT_A"] = 5.050500e+003 -almanac[50]["Right_Ascen_at_Week"] = 1.79067e+000 -almanac[50]["Argument_of_Perigee"] = 0.52930067 -almanac[50]["Mean_Anom"] = -5.519651e+000 -almanac[50]["week"] = 1390 -almanac[50]["Commit_date"] = "N/A" -almanac[50]["Life_dates"] = {} - -almanac[51] = {} -almanac[51]["System"] = SAT_SYS_GLONASS -almanac[51]["Number"] = 15 -almanac[51]["Orbital"] = 2 -almanac[51]["GLONASS_Data"] = {} -almanac[51]["GLONASS_Data"]["NKU_Number"] = "N/A" -almanac[51]["GLONASS_Data"]["Cosmos_Number"] = "N/A" -almanac[51]["Eccentricity"] = 1.184000e-003 -almanac[51]["Time_of_Applicability"] = 0.000000e+000 -almanac[51]["Orbital_Inclination"] = 1.126443e+000 -almanac[51]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[51]["SQRT_A"] = 5.050500e+003 -almanac[51]["Right_Ascen_at_Week"] = 1.79067e+000 -almanac[51]["Argument_of_Perigee"] = 1.31430067 -almanac[51]["Mean_Anom"] = -5.519651e+000 -almanac[51]["week"] = 1390 -almanac[51]["Commit_date"] = "N/A" -almanac[51]["Life_dates"] = {} - -almanac[52] = {} -almanac[52]["System"] = SAT_SYS_GLONASS -almanac[52]["Number"] = 16 -almanac[52]["Orbital"] = 2 -almanac[52]["GLONASS_Data"] = {} -almanac[52]["GLONASS_Data"]["NKU_Number"] = "N/A" -almanac[52]["GLONASS_Data"]["Cosmos_Number"] = "N/A" -almanac[52]["Eccentricity"] = 1.184000e-003 -almanac[52]["Time_of_Applicability"] = 0.000000e+000 -almanac[52]["Orbital_Inclination"] = 1.126443e+000 -almanac[52]["Rate_of_Right_Ascen"] = 0.000000e+000 -almanac[52]["SQRT_A"] = 5.050500e+003 -almanac[52]["Right_Ascen_at_Week"] = 1.79067e+000 -almanac[52]["Argument_of_Perigee"] = 2.09930067 -almanac[52]["Mean_Anom"] = -5.519651e+000 -almanac[52]["week"] = 1390 -almanac[52]["Commit_date"] = "N/A" -almanac[52]["Life_dates"] = {} - -SA_mode = false -AS_mode = false diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Scripts/World/birds.lua b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Scripts/World/birds.lua deleted file mode 100644 index 8aee1f700..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/Scripts/World/birds.lua +++ /dev/null @@ -1,27 +0,0 @@ -birds_avail = true --Birds availability. false - there is no birds -birds_maximum_hrad = 200 --Maximum altitude above ground al sea level bird could be met -birds_maximum_absolute_height = 8000 --Maximum absolute altitude bird could be met -birds_minimum_velocity = 40 --Minimum velocity bird could be met -birds_delta_time = 3.55 -birds_probability = {0.006333333*150, - 0.004166667*150, - 0.001966667*150, - 0.001090909*150, - 0.000741818*150, - 0.0006*150, - 0.000510545*150, - 0.000447273*150, - 0.000389455*150, - 0.000349091*150, - 0.000310909*150, - 0.000282545*150, - 0.000250909*150, - 0.000220364*150, - 0.000196364*150, - 0.000174545*150, - 0.000152727*150, - 0.000128727*150, - 0.000103636*150, - 7.63636E-05*150, - 0*150 -} diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/VHF_RADIO/SETTINGS.lua b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/VHF_RADIO/SETTINGS.lua deleted file mode 100644 index 92c6a42c4..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/VHF_RADIO/SETTINGS.lua +++ /dev/null @@ -1,14 +0,0 @@ -settings= -{ - ["dials"]= - { - ["channel"]=-1, - }, - ["presets"]= - { - [1]=124000000, - [2]=124000000, - [3]=131000000, - [4]=139000000, - }, -} diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/l10n/DEFAULT/AIB-005 - Patrol AI and Randomize Zones.lua b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/l10n/DEFAULT/AIB-005 - Patrol AI and Randomize Zones.lua deleted file mode 100644 index eab2c2e22..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/l10n/DEFAULT/AIB-005 - Patrol AI and Randomize Zones.lua +++ /dev/null @@ -1,51 +0,0 @@ --- Name: AIB-005 - Patrol AI and Randomize Zones --- Author: FlightControl --- Date Created: 10 Jan 2016 --- --- # Situation: --- --- For the red coalition, 2 client slots are foreseen. --- For those players that have not joined the mission, red AI is spawned. --- The red AI should start patrolling an area until fuel is empty and return to the home base. --- For each AI being spawned, ensure that they fly to a random zone defined within the mission editor. --- Right now there are two patrol zones defined, so the AI should start patrolliing in one of these zones. --- --- # Test cases: --- --- 1. If no player is logging into the red slots, 2 red AI planes should be alive. --- 2. If a player joins one red slot, one red AI plane should return to the nearest home base. --- 3. If two players join the red slots, no AI plane should be spawned, and all airborne AI planes should return to the nearest home base. --- 4. Spawned AI should take-off from the airbase, and start patrolling the area around Anapa. --- 5. When the AI is out-of-fuel, it should report it is returning to the home base, and land at Anapa. --- 6. Ensure that you see the AI patrol in one of the two zones ... - --- Define the SET of CLIENTs from the red coalition. This SET is filled during startup. -local RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) - --- Define the SPAWN object for the red AI plane template. --- We use InitCleanUp to check every 20 seconds, if there are no planes blocked at the airbase, waithing for take-off. --- If a blocked plane exists, this red plane will be ReSpawned. -local RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) - --- Start the AI_BALANCER, using the SET of red CLIENTs, and the SPAWN object as a parameter. -local RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) - --- Create the first polygon zone ... -local PatrolZoneGroup1 = GROUP:FindByName( "PatrolZone1" ) -local PatrolZone1 = ZONE_POLYGON:New( "PatrolZone1", PatrolZoneGroup1 ) - --- Create the second polygon zone ... -local PatrolZoneGroup2 = GROUP:FindByName( "PatrolZone2" ) -local PatrolZone2 = ZONE_POLYGON:New( "PatrolZone2", PatrolZoneGroup2 ) - --- Now, create an array of these zones ... -local PatrolZoneArray = { PatrolZone1, PatrolZone2 } - -function RU_AI_Balancer:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) - - local Patrol = AI_PATROL_ZONE:New( PatrolZoneArray[math.random( 1, 2 )], 3000, 6000, 400, 600 ) - Patrol:ManageFuel( 0.2, 60 ) - Patrol:SetControllable( AIGroup ) - Patrol:Start() - -end diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/l10n/DEFAULT/Moose.lua b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/l10n/DEFAULT/Moose.lua deleted file mode 100644 index 8faec6fc4..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/l10n/DEFAULT/Moose.lua +++ /dev/null @@ -1,31 +0,0 @@ -env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20170116_2116' ) - -local base = _G - -Include = {} - -Include.File = function( IncludeFile ) - if not Include.Files[ IncludeFile ] then - Include.Files[IncludeFile] = IncludeFile - env.info( "Include:" .. IncludeFile .. " from " .. Include.ProgramPath ) - local f = assert( base.loadfile( Include.ProgramPath .. IncludeFile .. ".lua" ) ) - if f == nil then - error ("Could not load MOOSE file " .. IncludeFile .. ".lua" ) - else - env.info( "Include:" .. IncludeFile .. " loaded from " .. Include.ProgramPath ) - return f() - end - end -end - -Include.ProgramPath = "Scripts/Moose/" - -env.info( "Include.ProgramPath = " .. Include.ProgramPath) - -Include.Files = {} - -Include.File( "Moose" ) - -BASE:TraceOnOff( true ) -env.info( '*** MOOSE INCLUDE END *** ' ) diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/l10n/DEFAULT/dictionary b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/l10n/DEFAULT/dictionary deleted file mode 100644 index c29347756..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/l10n/DEFAULT/dictionary +++ /dev/null @@ -1,416 +0,0 @@ -dictionary = -{ - ["DictKey_UnitName_89"] = "", - ["DictKey_UnitName_67"] = "AI US 1", - ["DictKey_UnitName_156"] = "RU Client #020", - ["DictKey_UnitName_297"] = "US Client #023", - ["DictKey_WptName_310"] = "", - ["DictKey_GroupName_125"] = "RU Client #010", - ["DictKey_UnitName_135"] = "RU Client #013", - ["DictKey_UnitName_374"] = "RU Client #004", - ["DictKey_WptName_292"] = "", - ["DictKey_WptName_48"] = "", - ["DictKey_GroupName_137"] = "RU Client #014", - ["DictKey_WptName_70"] = "", - ["DictKey_WptName_295"] = "", - ["DictKey_UnitName_25"] = "AI RU", - ["DictKey_GroupName_113"] = "RU Client #006", - ["DictKey_GroupName_284"] = "US Client #019", - ["DictKey_UnitName_312"] = "US Client #028", - ["DictKey_WptName_13"] = "", - ["DictKey_WptName_124"] = "", - ["DictKey_WptName_217"] = "", - ["DictKey_GroupName_10"] = "Client Group 2", - ["DictKey_GroupName_302"] = "US Client #025", - ["DictKey_UnitName_258"] = "US Client #010", - ["DictKey_WptName_181"] = "", - ["DictKey_WptName_109"] = "", - ["DictKey_WptName_336"] = "", - ["DictKey_GroupName_311"] = "US Client #028", - ["DictKey_GroupName_242"] = "US Client #005", - ["DictKey_UnitName_74"] = "Pilot #004", - ["DictKey_WptName_136"] = "", - ["DictKey_WptName_99"] = "", - ["DictKey_UnitName_315"] = "US Client #029", - ["DictKey_UnitName_43"] = "Client 5", - ["DictKey_GroupName_331"] = "PatrolZone2", - ["DictKey_WptName_178"] = "", - ["DictKey_GroupName_30"] = "AI RU", - ["DictKey_UnitName_249"] = "US Client #007", - ["DictKey_WptName_53"] = "", - ["DictKey_UnitName_171"] = "RU Client #025", - ["DictKey_WptName_289"] = "", - ["DictKey_GroupName_352"] = "RU Client #008", - ["DictKey_GroupName_260"] = "US Client #011", - ["DictKey_descriptionRedTask_2"] = "", - ["DictKey_WptName_55"] = "", - ["DictKey_GroupName_33"] = "AI RU", - ["DictKey_UnitName_183"] = "RU Client #029", - ["DictKey_WptName_190"] = "", - ["DictKey_UnitName_216"] = "RU Client #040", - ["DictKey_WptName_286"] = "", - ["DictKey_UnitName_153"] = "RU Client #019", - ["DictKey_WptName_75"] = "", - ["DictKey_UnitName_105"] = "RU Client #003", - ["DictKey_GroupName_382"] = "RU Client #007", - ["DictKey_UnitName_270"] = "US Client #014", - ["DictKey_WptName_47"] = "", - ["DictKey_GroupName_170"] = "RU Client #025", - ["DictKey_UnitName_46"] = "Client 7", - ["DictKey_WptName_92"] = "", - ["DictKey_UnitName_380"] = "RU Client #006", - ["DictKey_GroupName_191"] = "RU Client #032", - ["DictKey_UnitName_40"] = "Client 6", - ["DictKey_UnitName_95"] = "Pilot #002", - ["DictKey_GroupName_296"] = "US Client #023", - ["DictKey_WptName_244"] = "", - ["DictKey_UnitName_377"] = "RU Client #005", - ["DictKey_WptName_9"] = "", - ["DictKey_UnitName_11"] = "Client 2", - ["DictKey_GroupName_308"] = "US Client #027", - ["DictKey_GroupName_27"] = "AI RU", - ["DictKey_UnitName_22"] = "Client 4", - ["DictKey_WptName_62"] = "", - ["DictKey_GroupName_367"] = "RU Client #002", - ["DictKey_sortie_4"] = "", - ["DictKey_WptName_333"] = "", - ["DictKey_UnitName_356"] = "RU Client #009", - ["DictKey_GroupName_152"] = "RU Client #019", - ["DictKey_GroupName_281"] = "US Client #018", - ["DictKey_WptName_235"] = "", - ["DictKey_UnitName_362"] = "RU Client #011", - ["DictKey_WptName_375"] = "", - ["DictKey_GroupName_379"] = "RU Client #006", - ["DictKey_UnitName_267"] = "US Client #013", - ["DictKey_GroupName_173"] = "RU Client #026", - ["DictKey_GroupName_248"] = "US Client #007", - ["DictKey_WptName_369"] = "", - ["DictKey_UnitName_58"] = "Pilot #003", - ["DictKey_WptName_160"] = "", - ["DictKey_UnitName_246"] = "US Client #006", - ["DictKey_WptName_163"] = "", - ["DictKey_WptName_98"] = "", - ["DictKey_GroupName_340"] = "RU Client #004", - ["DictKey_UnitName_365"] = "RU Client #012", - ["DictKey_GroupName_221"] = "RU Client #042", - ["DictKey_UnitName_318"] = "US Client #030", - ["DictKey_GroupName_158"] = "RU Client #021", - ["DictKey_GroupName_320"] = "US Client #031", - ["DictKey_UnitName_34"] = "AI RU", - ["DictKey_WptName_208"] = "", - ["DictKey_WptName_334"] = "", - ["DictKey_WptName_54"] = "", - ["DictKey_WptName_82"] = "", - ["DictKey_UnitName_383"] = "RU Client #007", - ["DictKey_UnitName_234"] = "US Client #002", - ["DictKey_GroupName_104"] = "RU Client #003", - ["DictKey_UnitName_368"] = "RU Client #002", - ["DictKey_WptName_366"] = "", - ["DictKey_GroupName_206"] = "RU Client #037", - ["DictKey_GroupName_24"] = "AI RU", - ["DictKey_GroupName_325"] = "PatrolZone1", - ["DictKey_UnitName_225"] = "RU Client #043", - ["DictKey_WptName_328"] = "", - ["DictKey_WptName_193"] = "", - ["DictKey_UnitName_219"] = "RU Client #041", - ["DictKey_WptName_68"] = "", - ["DictKey_GroupName_122"] = "RU Client #009", - ["DictKey_GroupName_266"] = "US Client #013", - ["DictKey_UnitName_350"] = "RU Client #007", - ["DictKey_UnitName_341"] = "RU Client #004", - ["DictKey_UnitName_228"] = "RU Client #044", - ["DictKey_UnitName_204"] = "RU Client #036", - ["DictKey_WptName_49"] = "", - ["DictKey_UnitName_303"] = "US Client #025", - ["DictKey_GroupName_245"] = "US Client #006", - ["DictKey_GroupName_275"] = "US Client #016", - ["DictKey_WptName_90"] = "", - ["DictKey_UnitName_222"] = "RU Client #042", - ["DictKey_GroupName_110"] = "RU Client #005", - ["DictKey_UnitName_120"] = "RU Client #008", - ["DictKey_WptName_280"] = "", - ["DictKey_UnitName_144"] = "RU Client #016", - ["DictKey_UnitName_306"] = "US Client #026", - ["DictKey_WptName_118"] = "", - ["DictKey_WptName_307"] = "", - ["DictKey_UnitName_129"] = "RU Client #011", - ["DictKey_GroupName_59"] = "Client Group Range 1", - ["DictKey_GroupName_164"] = "RU Client #023", - ["DictKey_UnitName_252"] = "US Client #008", - ["DictKey_WptName_61"] = "", - ["DictKey_WptName_184"] = "", - ["DictKey_GroupName_361"] = "RU Client #011", - ["DictKey_WptName_80"] = "", - ["DictKey_GroupName_299"] = "US Client #024", - ["DictKey_GroupName_314"] = "US Client #029", - ["DictKey_WptName_86"] = "", - ["DictKey_GroupName_346"] = "RU Client #006", - ["DictKey_WptName_205"] = "", - ["DictKey_WptName_390"] = "", - ["DictKey_WptName_196"] = "", - ["DictKey_GroupName_263"] = "US Client #012", - ["DictKey_WptName_238"] = "", - ["DictKey_WptName_51"] = "", - ["DictKey_UnitName_150"] = "RU Client #018", - ["DictKey_WptName_76"] = "", - ["DictKey_UnitName_291"] = "US Client #021", - ["DictKey_WptName_127"] = "", - ["DictKey_GroupName_21"] = "Client Group 4", - ["DictKey_GroupName_364"] = "RU Client #012", - ["DictKey_GroupName_278"] = "US Client #017", - ["DictKey_WptName_44"] = "", - ["DictKey_WptName_335"] = "", - ["DictKey_WptName_100"] = "", - ["DictKey_GroupName_185"] = "RU Client #030", - ["DictKey_UnitName_321"] = "US Client #031", - ["DictKey_WptName_112"] = "", - ["DictKey_GroupName_230"] = "RU Client #045", - ["DictKey_UnitName_201"] = "RU Client #035", - ["DictKey_WptName_145"] = "", - ["DictKey_GroupName_128"] = "RU Client #011", - ["DictKey_descriptionBlueTask_3"] = "", - ["DictKey_WptName_241"] = "", - ["DictKey_descriptionText_1"] = "-- Name: AIB-005 - Patrol AI and Randomize Zones\ --- Author: FlightControl\ --- Date Created: 10 Jan 2016\ ---\ --- # Situation:\ ---\ --- For the red coalition, 2 client slots are foreseen.\ --- For those players that have not joined the mission, red AI is spawned.\ --- The red AI should start patrolling an area until fuel is empty and return to the home base.\ --- For each AI being spawned, ensure that they fly to a random zone defined within the mission editor.\ --- Right now there are two patrol zones defined, so the AI should start patrolliing in one of these zones.\ --- \ --- # Test cases:\ --- \ --- 1. If no player is logging into the red slots, 2 red AI planes should be alive.\ --- 2. If a player joins one red slot, one red AI plane should return to the nearest home base.\ --- 3. If two players join the red slots, no AI plane should be spawned, and all airborne AI planes should return to the nearest home base.\ --- 4. Spawned AI should take-off from the airbase, and start patrolling the area around Anapa.\ --- 5. When the AI is out-of-fuel, it should report it is returning to the home base, and land at Anapa.\ --- 6. Ensure that you see the AI patrol in one of the two zones ...\ -", - ["DictKey_GroupName_134"] = "RU Client #013", - ["DictKey_UnitName_102"] = "RU Client #002", - ["DictKey_WptName_378"] = "", - ["DictKey_GroupName_224"] = "RU Client #043", - ["DictKey_UnitName_371"] = "RU Client #003", - ["DictKey_UnitName_392"] = "RU Client #010", - ["DictKey_UnitName_108"] = "RU Client #004", - ["DictKey_UnitName_132"] = "RU Client #012", - ["DictKey_UnitName_174"] = "RU Client #026", - ["DictKey_WptName_274"] = "", - ["DictKey_GroupName_176"] = "RU Client #027", - ["DictKey_WptName_339"] = "", - ["DictKey_WptName_199"] = "", - ["DictKey_UnitName_123"] = "RU Client #009", - ["DictKey_WptName_81"] = "", - ["DictKey_UnitName_213"] = "RU Client #039", - ["DictKey_WptName_211"] = "", - ["DictKey_GroupName_227"] = "RU Client #044", - ["DictKey_WptName_77"] = "", - ["DictKey_WptName_29"] = "", - ["DictKey_WptName_166"] = "", - ["DictKey_WptName_396"] = "", - ["DictKey_UnitName_168"] = "RU Client #024", - ["DictKey_UnitName_210"] = "RU Client #038", - ["DictKey_WptName_169"] = "", - ["DictKey_GroupName_7"] = "US Client #001", - ["DictKey_WptName_384"] = "", - ["DictKey_WptName_354"] = "", - ["DictKey_GroupName_143"] = "RU Client #016", - ["DictKey_UnitName_288"] = "US Client #020", - ["DictKey_GroupName_155"] = "RU Client #020", - ["DictKey_GroupName_236"] = "US Client #003", - ["DictKey_ActionText_17"] = "", - ["DictKey_WptName_157"] = "", - ["DictKey_UnitName_114"] = "RU Client #006", - ["DictKey_WptName_298"] = "", - ["DictKey_WptName_142"] = "", - ["DictKey_GroupName_254"] = "US Client #009", - ["DictKey_WptName_91"] = "", - ["DictKey_WptName_259"] = "", - ["DictKey_WptName_23"] = "", - ["DictKey_WptName_172"] = "", - ["DictKey_WptName_214"] = "", - ["DictKey_UnitName_264"] = "US Client #012", - ["DictKey_WptName_151"] = "", - ["DictKey_UnitName_15"] = "AI US 2", - ["DictKey_GroupName_45"] = "Client Group 7", - ["DictKey_WptName_71"] = "", - ["DictKey_WptName_26"] = "", - ["DictKey_UnitName_57"] = "Pilot #002", - ["DictKey_UnitName_31"] = "AI RU", - ["DictKey_UnitName_111"] = "RU Client #005", - ["DictKey_WptName_12"] = "", - ["DictKey_GroupName_39"] = "Client Group 6", - ["DictKey_WptName_52"] = "", - ["DictKey_UnitName_159"] = "RU Client #021", - ["DictKey_WptName_38"] = "", - ["DictKey_UnitName_138"] = "RU Client #014", - ["DictKey_WptName_330"] = "", - ["DictKey_UnitName_309"] = "US Client #027", - ["DictKey_WptName_85"] = "", - ["DictKey_GroupName_290"] = "US Client #021", - ["DictKey_GroupName_373"] = "RU Client #004", - ["DictKey_UnitName_240"] = "US Client #004", - ["DictKey_GroupName_358"] = "RU Client #010", - ["DictKey_WptName_247"] = "", - ["DictKey_WptName_223"] = "", - ["DictKey_WptName_324"] = "", - ["DictKey_GroupName_18"] = "RU Client #001", - ["DictKey_WptName_148"] = "", - ["DictKey_GroupName_349"] = "RU Client #007", - ["DictKey_GroupName_36"] = "AI RU", - ["DictKey_WptName_130"] = "", - ["DictKey_WptName_381"] = "", - ["DictKey_UnitName_180"] = "RU Client #028", - ["DictKey_UnitName_117"] = "RU Client #007", - ["DictKey_WptName_329"] = "", - ["DictKey_GroupName_73"] = "Patrol Zone Blue", - ["DictKey_WptName_121"] = "", - ["DictKey_WptName_78"] = "", - ["DictKey_UnitName_147"] = "RU Client #017", - ["DictKey_UnitName_56"] = "Pilot #001", - ["DictKey_GroupName_203"] = "RU Client #036", - ["DictKey_GroupName_337"] = "RU Client #003", - ["DictKey_WptName_304"] = "", - ["DictKey_WptName_103"] = "", - ["DictKey_WptName_265"] = "", - ["DictKey_GroupName_14"] = "AI US 2", - ["DictKey_UnitName_282"] = "US Client #018", - ["DictKey_WptName_357"] = "", - ["DictKey_WptName_175"] = "", - ["DictKey_WptName_327"] = "", - ["DictKey_WptName_342"] = "", - ["DictKey_WptName_154"] = "", - ["DictKey_GroupName_391"] = "RU Client #010", - ["DictKey_UnitName_359"] = "RU Client #010", - ["DictKey_GroupName_197"] = "RU Client #034", - ["DictKey_GroupName_42"] = "Client Group 5", - ["DictKey_WptName_283"] = "", - ["DictKey_GroupName_161"] = "RU Client #022", - ["DictKey_UnitName_19"] = "RU Client #001", - ["DictKey_UnitName_237"] = "US Client #003", - ["DictKey_WptName_65"] = "", - ["DictKey_WptName_372"] = "", - ["DictKey_WptName_322"] = "", - ["DictKey_WptName_97"] = "", - ["DictKey_WptName_360"] = "", - ["DictKey_GroupName_131"] = "RU Client #012", - ["DictKey_WptName_16"] = "", - ["DictKey_GroupName_101"] = "RU Client #002", - ["DictKey_WptName_202"] = "", - ["DictKey_UnitName_198"] = "RU Client #034", - ["DictKey_WptName_253"] = "", - ["DictKey_GroupName_394"] = "RU Client #011", - ["DictKey_WptName_32"] = "", - ["DictKey_UnitName_279"] = "US Client #017", - ["DictKey_GroupName_305"] = "US Client #026", - ["DictKey_UnitName_141"] = "RU Client #015", - ["DictKey_WptName_79"] = "", - ["DictKey_UnitName_207"] = "RU Client #037", - ["DictKey_WptName_250"] = "", - ["DictKey_GroupName_116"] = "RU Client #007", - ["DictKey_GroupName_94"] = "Patrol Zone Red", - ["DictKey_GroupName_212"] = "RU Client #039", - ["DictKey_GroupName_233"] = "US Client #002", - ["DictKey_GroupName_200"] = "RU Client #035", - ["DictKey_WptName_96"] = "", - ["DictKey_WptName_50"] = "", - ["DictKey_WptName_115"] = "", - ["DictKey_WptName_226"] = "", - ["DictKey_UnitName_126"] = "RU Client #010", - ["DictKey_UnitName_243"] = "US Client #005", - ["DictKey_WptName_316"] = "", - ["DictKey_WptName_69"] = "", - ["DictKey_UnitName_347"] = "RU Client #006", - ["DictKey_GroupName_209"] = "RU Client #038", - ["DictKey_UnitName_28"] = "AI RU", - ["DictKey_GroupName_215"] = "RU Client #040", - ["DictKey_WptName_64"] = "", - ["DictKey_UnitName_192"] = "RU Client #032", - ["DictKey_UnitName_338"] = "RU Client #003", - ["DictKey_GroupName_257"] = "US Client #010", - ["DictKey_GroupName_188"] = "RU Client #031", - ["DictKey_GroupName_107"] = "RU Client #004", - ["DictKey_UnitName_255"] = "US Client #009", - ["DictKey_UnitName_231"] = "RU Client #045", - ["DictKey_UnitName_88"] = "Pilot #001", - ["DictKey_WptName_232"] = "", - ["DictKey_UnitName_195"] = "RU Client #033", - ["DictKey_GroupName_370"] = "RU Client #003", - ["DictKey_WptName_229"] = "", - ["DictKey_WptName_256"] = "", - ["DictKey_WptName_345"] = "", - ["DictKey_WptName_277"] = "", - ["DictKey_WptName_63"] = "", - ["DictKey_WptName_363"] = "", - ["DictKey_GroupName_167"] = "RU Client #024", - ["DictKey_GroupName_385"] = "RU Client #008", - ["DictKey_GroupName_376"] = "RU Client #005", - ["DictKey_UnitName_60"] = "Client Range 1", - ["DictKey_UnitName_326"] = "Pilot #001", - ["DictKey_GroupName_388"] = "RU Client #009", - ["DictKey_GroupName_251"] = "US Client #008", - ["DictKey_WptName_41"] = "", - ["DictKey_UnitName_273"] = "US Client #015", - ["DictKey_GroupName_66"] = "AI US", - ["DictKey_WptName_87"] = "", - ["DictKey_GroupName_343"] = "RU Client #005", - ["DictKey_GroupName_119"] = "RU Client #008", - ["DictKey_WptName_268"] = "", - ["DictKey_WptName_220"] = "", - ["DictKey_WptName_262"] = "", - ["DictKey_WptName_83"] = "", - ["DictKey_WptName_72"] = "", - ["DictKey_UnitName_276"] = "US Client #016", - ["DictKey_WptName_387"] = "", - ["DictKey_GroupName_272"] = "US Client #015", - ["DictKey_WptName_187"] = "", - ["DictKey_UnitName_189"] = "RU Client #031", - ["DictKey_UnitName_162"] = "RU Client #022", - ["DictKey_GroupName_146"] = "RU Client #017", - ["DictKey_GroupName_140"] = "RU Client #015", - ["DictKey_GroupName_239"] = "US Client #004", - ["DictKey_UnitName_353"] = "RU Client #008", - ["DictKey_UnitName_386"] = "RU Client #008", - ["DictKey_WptName_351"] = "", - ["DictKey_WptName_313"] = "", - ["DictKey_UnitName_93"] = "Pilot #001", - ["DictKey_GroupName_287"] = "US Client #020", - ["DictKey_UnitName_395"] = "RU Client #011", - ["DictKey_UnitName_332"] = "Pilot #002", - ["DictKey_WptName_133"] = "", - ["DictKey_UnitName_186"] = "RU Client #030", - ["DictKey_GroupName_194"] = "RU Client #033", - ["DictKey_GroupName_179"] = "RU Client #028", - ["DictKey_GroupName_269"] = "US Client #014", - ["DictKey_WptName_20"] = "", - ["DictKey_WptName_348"] = "", - ["DictKey_WptName_301"] = "", - ["DictKey_UnitName_285"] = "US Client #019", - ["DictKey_UnitName_344"] = "RU Client #005", - ["DictKey_WptName_393"] = "", - ["DictKey_WptName_84"] = "", - ["DictKey_GroupName_355"] = "RU Client #009", - ["DictKey_UnitName_177"] = "RU Client #027", - ["DictKey_UnitName_37"] = "AI RU", - ["DictKey_WptName_319"] = "", - ["DictKey_WptName_106"] = "", - ["DictKey_UnitName_294"] = "US Client #022", - ["DictKey_UnitName_165"] = "RU Client #023", - ["DictKey_WptName_35"] = "", - ["DictKey_UnitName_261"] = "US Client #011", - ["DictKey_GroupName_293"] = "US Client #022", - ["DictKey_UnitName_389"] = "RU Client #009", - ["DictKey_GroupName_182"] = "RU Client #029", - ["DictKey_UnitName_300"] = "US Client #024", - ["DictKey_GroupName_317"] = "US Client #030", - ["DictKey_WptName_271"] = "", - ["DictKey_WptName_139"] = "", - ["DictKey_GroupName_149"] = "RU Client #018", - ["DictKey_GroupName_218"] = "RU Client #041", - ["DictKey_UnitName_8"] = "US Client #001", -} -- end of dictionary diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/l10n/DEFAULT/mapResource b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/l10n/DEFAULT/mapResource deleted file mode 100644 index dc130cd07..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/l10n/DEFAULT/mapResource +++ /dev/null @@ -1,5 +0,0 @@ -mapResource = -{ - ["ResKey_Action_323"] = "Moose.lua", - ["ResKey_Action_6"] = "AIB-005 - Patrol AI and Randomize Zones.lua", -} -- end of mapResource diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/mission b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/mission deleted file mode 100644 index d243c5ada..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/mission +++ /dev/null @@ -1,5000 +0,0 @@ -mission = -{ - ["trig"] = - { - ["actions"] = - { - [1] = "a_do_script_file(getValueResourceByKey(\"ResKey_Action_323\"));", - [2] = "a_do_script(getValueDictByKey(\"DictKey_ActionText_17\"));", - [3] = "a_do_script_file(getValueResourceByKey(\"ResKey_Action_6\"));", - }, -- end of ["actions"] - ["events"] = - { - }, -- end of ["events"] - ["custom"] = - { - }, -- end of ["custom"] - ["func"] = - { - }, -- end of ["func"] - ["flag"] = - { - [1] = true, - [2] = true, - [3] = true, - }, -- end of ["flag"] - ["conditions"] = - { - [1] = "return(true)", - [2] = "return(true)", - [3] = "return(true)", - }, -- end of ["conditions"] - ["customStartup"] = - { - }, -- end of ["customStartup"] - ["funcStartup"] = - { - [1] = "if mission.trig.conditions[1]() then mission.trig.actions[1]() end", - [2] = "if mission.trig.conditions[2]() then mission.trig.actions[2]() end", - [3] = "if mission.trig.conditions[3]() then mission.trig.actions[3]() end", - }, -- end of ["funcStartup"] - }, -- end of ["trig"] - ["date"] = - { - ["Day"] = 1, - ["Year"] = 2011, - ["Month"] = 6, - }, -- end of ["date"] - ["maxDictId"] = 396, - ["result"] = - { - ["offline"] = - { - ["conditions"] = - { - }, -- end of ["conditions"] - ["actions"] = - { - }, -- end of ["actions"] - ["func"] = - { - }, -- end of ["func"] - }, -- end of ["offline"] - ["total"] = 0, - ["blue"] = - { - ["conditions"] = - { - }, -- end of ["conditions"] - ["actions"] = - { - }, -- end of ["actions"] - ["func"] = - { - }, -- end of ["func"] - }, -- end of ["blue"] - ["red"] = - { - ["conditions"] = - { - }, -- end of ["conditions"] - ["actions"] = - { - }, -- end of ["actions"] - ["func"] = - { - }, -- end of ["func"] - }, -- end of ["red"] - }, -- end of ["result"] - ["groundControl"] = - { - ["isPilotControlVehicles"] = false, - ["roles"] = - { - ["artillery_commander"] = - { - ["blue"] = 0, - ["red"] = 0, - }, -- end of ["artillery_commander"] - ["instructor"] = - { - ["blue"] = 0, - ["red"] = 0, - }, -- end of ["instructor"] - ["observer"] = - { - ["blue"] = 0, - ["red"] = 0, - }, -- end of ["observer"] - ["forward_observer"] = - { - ["blue"] = 0, - ["red"] = 0, - }, -- end of ["forward_observer"] - }, -- end of ["roles"] - }, -- end of ["groundControl"] - ["triggers"] = - { - ["zones"] = - { - }, -- end of ["zones"] - }, -- end of ["triggers"] - ["weather"] = - { - ["atmosphere_type"] = 0, - ["wind"] = - { - ["at8000"] = - { - ["speed"] = 0, - ["dir"] = 0, - }, -- end of ["at8000"] - ["atGround"] = - { - ["speed"] = 0, - ["dir"] = 0, - }, -- end of ["atGround"] - ["at2000"] = - { - ["speed"] = 0, - ["dir"] = 0, - }, -- end of ["at2000"] - }, -- end of ["wind"] - ["enable_fog"] = false, - ["season"] = - { - ["temperature"] = 20, - }, -- end of ["season"] - ["type_weather"] = 0, - ["qnh"] = 760, - ["cyclones"] = - { - }, -- end of ["cyclones"] - ["name"] = "Winter, clean sky", - ["fog"] = - { - ["thickness"] = 0, - ["visibility"] = 25, - ["density"] = 7, - }, -- end of ["fog"] - ["groundTurbulence"] = 0, - ["visibility"] = - { - ["distance"] = 80000, - }, -- end of ["visibility"] - ["clouds"] = - { - ["thickness"] = 200, - ["density"] = 0, - ["base"] = 300, - ["iprecptns"] = 0, - }, -- end of ["clouds"] - }, -- end of ["weather"] - ["theatre"] = "Caucasus", - ["needModules"] = - { - }, -- end of ["needModules"] - ["map"] = - { - ["centerY"] = 295177.98214286, - ["zoom"] = 25000, - ["centerX"] = -8598.8505859375, - }, -- end of ["map"] - ["coalitions"] = - { - ["blue"] = - { - [1] = 11, - [2] = 4, - [3] = 6, - [4] = 16, - [5] = 13, - [6] = 15, - [7] = 9, - [8] = 8, - [9] = 12, - [10] = 2, - [11] = 3, - [12] = 5, - [13] = 10, - [14] = 20, - [15] = 21, - [16] = 40, - [17] = 26, - [18] = 45, - [19] = 28, - }, -- end of ["blue"] - ["neutrals"] = - { - [1] = 7, - [2] = 17, - [3] = 22, - [4] = 23, - [5] = 25, - [6] = 29, - [7] = 30, - [8] = 31, - [9] = 32, - [10] = 33, - [11] = 35, - [12] = 36, - [13] = 39, - [14] = 41, - [15] = 42, - [16] = 44, - [17] = 46, - }, -- end of ["neutrals"] - ["red"] = - { - [1] = 0, - [2] = 1, - [3] = 18, - [4] = 19, - [5] = 37, - [6] = 24, - [7] = 27, - [8] = 43, - [9] = 47, - [10] = 34, - [11] = 38, - }, -- end of ["red"] - }, -- end of ["coalitions"] - ["descriptionText"] = "DictKey_descriptionText_1", - ["pictureFileNameR"] = - { - }, -- end of ["pictureFileNameR"] - ["descriptionBlueTask"] = "DictKey_descriptionBlueTask_3", - ["descriptionRedTask"] = "DictKey_descriptionRedTask_2", - ["pictureFileNameB"] = - { - }, -- end of ["pictureFileNameB"] - ["trigrules"] = - { - [1] = - { - ["rules"] = - { - }, -- end of ["rules"] - ["eventlist"] = "", - ["actions"] = - { - [1] = - { - ["file"] = "ResKey_Action_323", - ["predicate"] = "a_do_script_file", - ["ai_task"] = - { - [1] = "", - [2] = "", - }, -- end of ["ai_task"] - }, -- end of [1] - }, -- end of ["actions"] - ["predicate"] = "triggerStart", - ["comment"] = "Load Moose", - }, -- end of [1] - [2] = - { - ["rules"] = - { - }, -- end of ["rules"] - ["eventlist"] = "", - ["actions"] = - { - [1] = - { - ["KeyDict_text"] = "DictKey_ActionText_17", - ["text"] = "DictKey_ActionText_17", - ["predicate"] = "a_do_script", - ["ai_task"] = - { - [1] = "", - [2] = "", - }, -- end of ["ai_task"] - }, -- end of [1] - }, -- end of ["actions"] - ["predicate"] = "triggerStart", - ["comment"] = "Trace", - }, -- end of [2] - [3] = - { - ["rules"] = - { - }, -- end of ["rules"] - ["eventlist"] = "", - ["actions"] = - { - [1] = - { - ["file"] = "ResKey_Action_6", - ["predicate"] = "a_do_script_file", - ["ai_task"] = - { - [1] = "", - [2] = "", - }, -- end of ["ai_task"] - }, -- end of [1] - }, -- end of ["actions"] - ["predicate"] = "triggerStart", - ["comment"] = "Load Mission", - }, -- end of [3] - }, -- end of ["trigrules"] - ["coalition"] = - { - ["blue"] = - { - ["bullseye"] = - { - ["y"] = 617414, - ["x"] = -291014, - }, -- end of ["bullseye"] - ["nav_points"] = - { - }, -- end of ["nav_points"] - ["name"] = "blue", - ["country"] = - { - [1] = - { - ["id"] = 11, - ["name"] = "Belgium", - }, -- end of [1] - [2] = - { - ["id"] = 4, - ["name"] = "UK", - }, -- end of [2] - [3] = - { - ["id"] = 6, - ["name"] = "Germany", - }, -- end of [3] - [4] = - { - ["id"] = 16, - ["name"] = "Georgia", - }, -- end of [4] - [5] = - { - ["id"] = 13, - ["name"] = "Denmark", - }, -- end of [5] - [6] = - { - ["id"] = 15, - ["name"] = "Israel", - }, -- end of [6] - [7] = - { - ["id"] = 9, - ["name"] = "Spain", - }, -- end of [7] - [8] = - { - ["id"] = 8, - ["name"] = "Canada", - }, -- end of [8] - [9] = - { - ["id"] = 12, - ["name"] = "Norway", - }, -- end of [9] - [10] = - { - ["id"] = 2, - ["name"] = "USA", - }, -- end of [10] - [11] = - { - ["id"] = 3, - ["name"] = "Turkey", - }, -- end of [11] - [12] = - { - ["id"] = 5, - ["name"] = "France", - }, -- end of [12] - [13] = - { - ["id"] = 10, - ["name"] = "The Netherlands", - }, -- end of [13] - [14] = - { - ["id"] = 20, - ["name"] = "Italy", - }, -- end of [14] - [15] = - { - ["id"] = 21, - ["name"] = "Australia", - }, -- end of [15] - [16] = - { - ["id"] = 40, - ["name"] = "Poland", - }, -- end of [16] - [17] = - { - ["id"] = 26, - ["name"] = "Czech Republic", - }, -- end of [17] - [18] = - { - ["id"] = 45, - ["name"] = "South Korea", - }, -- end of [18] - [19] = - { - ["id"] = 28, - ["name"] = "Croatia", - }, -- end of [19] - }, -- end of ["country"] - }, -- end of ["blue"] - ["red"] = - { - ["bullseye"] = - { - ["y"] = 371700, - ["x"] = 11557, - }, -- end of ["bullseye"] - ["nav_points"] = - { - }, -- end of ["nav_points"] - ["name"] = "red", - ["country"] = - { - [1] = - { - ["helicopter"] = - { - ["group"] = - { - [1] = - { - ["lateActivation"] = true, - ["tasks"] = - { - }, -- end of ["tasks"] - ["radioSet"] = false, - ["task"] = "CAS", - ["uncontrolled"] = false, - ["route"] = - { - ["routeRelativeTOT"] = true, - ["points"] = - { - [1] = - { - ["alt"] = 500, - ["action"] = "Turning Point", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 55.555555555556, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["number"] = 1, - ["key"] = "CAS", - ["id"] = "EngageTargets", - ["enabled"] = true, - ["auto"] = true, - ["params"] = - { - ["targetTypes"] = - { - [1] = "Helicopters", - [2] = "Ground Units", - [3] = "Light armed ships", - }, -- end of ["targetTypes"] - ["priority"] = 0, - }, -- end of ["params"] - }, -- end of [1] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "Turning Point", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = 325834.45982144, - ["x"] = 7740.8543526806, - ["name"] = "DictKey_WptName_327", - ["formation_template"] = "", - ["speed_locked"] = true, - }, -- end of [1] - [2] = - { - ["alt"] = 500, - ["action"] = "Turning Point", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 55.555555555556, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "Turning Point", - ["ETA"] = 614.57680810556, - ["ETA_locked"] = false, - ["y"] = 325977.31696429, - ["x"] = 41883.711495538, - ["name"] = "DictKey_WptName_328", - ["formation_template"] = "", - ["speed_locked"] = true, - }, -- end of [2] - [3] = - { - ["alt"] = 500, - ["action"] = "Turning Point", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 55.555555555556, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "Turning Point", - ["ETA"] = 903.60812481849, - ["ETA_locked"] = false, - ["y"] = 311263.03125001, - ["x"] = 48312.282924109, - ["name"] = "DictKey_WptName_329", - ["formation_template"] = "", - ["speed_locked"] = true, - }, -- end of [3] - [4] = - { - ["alt"] = 500, - ["action"] = "Turning Point", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 55.555555555556, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "Turning Point", - ["ETA"] = 1667.3397263538, - ["ETA_locked"] = false, - ["y"] = 310977.31696429, - ["x"] = 5883.7114955378, - ["name"] = "DictKey_WptName_330", - ["formation_template"] = "", - ["speed_locked"] = true, - }, -- end of [4] - }, -- end of ["points"] - }, -- end of ["route"] - ["groupId"] = 19, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["alt"] = 500, - ["alt_type"] = "BARO", - ["livery_id"] = "Russia Standard Army", - ["skill"] = "High", - ["ropeLength"] = 15, - ["speed"] = 55.555555555556, - ["type"] = "Ka-50", - ["unitId"] = 24, - ["psi"] = -0.0041840760018655, - ["y"] = 325834.45982144, - ["x"] = 7740.8543526806, - ["name"] = "DictKey_UnitName_326", - ["payload"] = - { - ["pylons"] = - { - }, -- end of ["pylons"] - ["fuel"] = "1450", - ["flare"] = 128, - ["chaff"] = 0, - ["gun"] = 100, - }, -- end of ["payload"] - ["heading"] = 0.0041840760018655, - ["callsign"] = 105, - ["onboard_num"] = "050", - }, -- end of [1] - }, -- end of ["units"] - ["y"] = 325834.45982144, - ["x"] = 7740.8543526806, - ["name"] = "DictKey_GroupName_325", - ["communication"] = true, - ["start_time"] = 0, - ["modulation"] = 0, - ["frequency"] = 124, - }, -- end of [1] - [2] = - { - ["lateActivation"] = true, - ["tasks"] = - { - }, -- end of ["tasks"] - ["radioSet"] = false, - ["task"] = "CAS", - ["uncontrolled"] = false, - ["route"] = - { - ["routeRelativeTOT"] = true, - ["points"] = - { - [1] = - { - ["alt"] = 500, - ["action"] = "Turning Point", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 55.555555555556, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["number"] = 1, - ["key"] = "CAS", - ["id"] = "EngageTargets", - ["enabled"] = true, - ["auto"] = true, - ["params"] = - { - ["targetTypes"] = - { - [1] = "Helicopters", - [2] = "Ground Units", - [3] = "Light armed ships", - }, -- end of ["targetTypes"] - ["priority"] = 0, - }, -- end of ["params"] - }, -- end of [1] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "Turning Point", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = 323894.45982144, - ["x"] = -71460.574218749, - ["name"] = "DictKey_WptName_333", - ["formation_template"] = "", - ["speed_locked"] = true, - }, -- end of [1] - [2] = - { - ["alt"] = 500, - ["action"] = "Turning Point", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 55.555555555556, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "Turning Point", - ["ETA"] = 829.44018494895, - ["ETA_locked"] = false, - ["y"] = 321608.74553573, - ["x"] = -7603.4313616057, - ["name"] = "DictKey_WptName_334", - ["formation_template"] = "", - ["speed_locked"] = true, - }, -- end of [2] - [3] = - { - ["alt"] = 500, - ["action"] = "Turning Point", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 55.555555555556, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "Turning Point", - ["ETA"] = 581.44512129878, - ["ETA_locked"] = false, - ["y"] = 309465.88839287, - ["x"] = -8174.8599330343, - ["name"] = "DictKey_WptName_335", - ["formation_template"] = "", - ["speed_locked"] = true, - }, -- end of [3] - [4] = - { - ["alt"] = 500, - ["action"] = "Turning Point", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 55.555555555556, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "Turning Point", - ["ETA"] = 2541.8313343196, - ["ETA_locked"] = false, - ["y"] = 310894.45982144, - ["x"] = -73317.717075891, - ["name"] = "DictKey_WptName_336", - ["formation_template"] = "", - ["speed_locked"] = true, - }, -- end of [4] - }, -- end of ["points"] - }, -- end of ["route"] - ["groupId"] = 20, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["alt"] = 500, - ["alt_type"] = "BARO", - ["livery_id"] = "Russia Standard Army", - ["skill"] = "High", - ["ropeLength"] = 15, - ["speed"] = 55.555555555556, - ["type"] = "Ka-50", - ["unitId"] = 25, - ["psi"] = 0.035778908402037, - ["y"] = 323894.45982144, - ["x"] = -71460.574218749, - ["name"] = "DictKey_UnitName_332", - ["payload"] = - { - ["pylons"] = - { - }, -- end of ["pylons"] - ["fuel"] = "1450", - ["flare"] = 128, - ["chaff"] = 0, - ["gun"] = 100, - }, -- end of ["payload"] - ["heading"] = -0.035778908402037, - ["callsign"] = 105, - ["onboard_num"] = "050", - }, -- end of [1] - }, -- end of ["units"] - ["y"] = 323894.45982144, - ["x"] = -71460.574218749, - ["name"] = "DictKey_GroupName_331", - ["communication"] = true, - ["start_time"] = 0, - ["modulation"] = 0, - ["frequency"] = 124, - }, -- end of [2] - }, -- end of ["group"] - }, -- end of ["helicopter"] - ["name"] = "Russia", - ["id"] = 0, - ["plane"] = - { - ["group"] = - { - [1] = - { - ["modulation"] = 0, - ["tasks"] = - { - }, -- end of ["tasks"] - ["radioSet"] = false, - ["task"] = "CAP", - ["uncontrolled"] = false, - ["route"] = - { - ["points"] = - { - [1] = - { - ["alt"] = 88, - ["action"] = "From Parking Area", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 138.88888888889, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["enabled"] = true, - ["key"] = "CAP", - ["id"] = "EngageTargets", - ["number"] = 1, - ["auto"] = true, - ["params"] = - { - ["targetTypes"] = - { - [1] = "Air", - }, -- end of ["targetTypes"] - ["priority"] = 0, - }, -- end of ["params"] - }, -- end of [1] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "TakeOffParking", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = 293785.125, - ["x"] = -7523.8505859375, - ["name"] = "DictKey_WptName_20", - ["formation_template"] = "", - ["airdromeId"] = 15, - ["speed_locked"] = true, - }, -- end of [1] - }, -- end of ["points"] - }, -- end of ["route"] - ["groupId"] = 4, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["alt"] = 88, - ["alt_type"] = "BARO", - ["livery_id"] = "Air Force Standard", - ["skill"] = "Client", - ["parking"] = 2, - ["speed"] = 138.88888888889, - ["type"] = "Su-27", - ["unitId"] = 4, - ["psi"] = 0, - ["parking_id"] = "04", - ["x"] = -7523.8505859375, - ["name"] = "DictKey_UnitName_19", - ["payload"] = - { - ["pylons"] = - { - }, -- end of ["pylons"] - ["fuel"] = 5217, - ["flare"] = 96, - ["chaff"] = 96, - ["gun"] = 100, - }, -- end of ["payload"] - ["y"] = 293785.125, - ["heading"] = 0, - ["callsign"] = 103, - ["onboard_num"] = "013", - }, -- end of [1] - }, -- end of ["units"] - ["y"] = 293785.125, - ["x"] = -7523.8505859375, - ["name"] = "DictKey_GroupName_18", - ["communication"] = true, - ["start_time"] = 0, - ["frequency"] = 127.5, - }, -- end of [1] - [2] = - { - ["lateActivation"] = true, - ["tasks"] = - { - }, -- end of ["tasks"] - ["radioSet"] = false, - ["task"] = "CAP", - ["uncontrolled"] = false, - ["route"] = - { - ["routeRelativeTOT"] = true, - ["points"] = - { - [1] = - { - ["alt"] = 88, - ["action"] = "From Parking Area Hot", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 250, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "TakeOffParkingHot", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = 294155.125, - ["x"] = -7498.103515625, - ["name"] = "DictKey_WptName_26", - ["formation_template"] = "", - ["airdromeId"] = 15, - ["speed_locked"] = true, - }, -- end of [1] - }, -- end of ["points"] - }, -- end of ["route"] - ["groupId"] = 6, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["alt"] = 88, - ["hardpoint_racks"] = true, - ["alt_type"] = "BARO", - ["livery_id"] = "`snow` test paint scheme", - ["skill"] = "High", - ["parking"] = 91, - ["speed"] = 250, - ["type"] = "Su-30", - ["unitId"] = 6, - ["psi"] = 1.7036072745551, - ["parking_id"] = "29", - ["x"] = -7498.103515625, - ["name"] = "DictKey_UnitName_25", - ["payload"] = - { - ["pylons"] = - { - [1] = - { - ["CLSID"] = "{44EE8698-89F9-48EE-AF36-5FD31896A82F}", - }, -- end of [1] - [2] = - { - ["CLSID"] = "{FBC29BFE-3D24-4C64-B81D-941239D12249}", - }, -- end of [2] - [3] = - { - ["CLSID"] = "{88DAC840-9F75-4531-8689-B46E64E42E53}", - }, -- end of [3] - [4] = - { - ["CLSID"] = "{B79C379A-9E87-4E50-A1EE-7F7E29C2E87A}", - }, -- end of [4] - [5] = - { - ["CLSID"] = "{E8069896-8435-4B90-95C0-01A03AE6E400}", - }, -- end of [5] - [6] = - { - ["CLSID"] = "{E8069896-8435-4B90-95C0-01A03AE6E400}", - }, -- end of [6] - [7] = - { - ["CLSID"] = "{B79C379A-9E87-4E50-A1EE-7F7E29C2E87A}", - }, -- end of [7] - [8] = - { - ["CLSID"] = "{88DAC840-9F75-4531-8689-B46E64E42E53}", - }, -- end of [8] - [9] = - { - ["CLSID"] = "{FBC29BFE-3D24-4C64-B81D-941239D12249}", - }, -- end of [9] - [10] = - { - ["CLSID"] = "{44EE8698-89F9-48EE-AF36-5FD31896A82A}", - }, -- end of [10] - }, -- end of ["pylons"] - ["fuel"] = "9400", - ["flare"] = 96, - ["chaff"] = 96, - ["gun"] = 100, - }, -- end of ["payload"] - ["y"] = 294155.125, - ["heading"] = 0, - ["callsign"] = 104, - ["onboard_num"] = "013", - }, -- end of [1] - }, -- end of ["units"] - ["y"] = 294155.125, - ["x"] = -7498.103515625, - ["name"] = "DictKey_GroupName_24", - ["communication"] = true, - ["start_time"] = 0, - ["modulation"] = 0, - ["frequency"] = 251, - }, -- end of [2] - [3] = - { - ["modulation"] = 0, - ["tasks"] = - { - }, -- end of ["tasks"] - ["radioSet"] = false, - ["task"] = "CAP", - ["uncontrolled"] = false, - ["route"] = - { - ["points"] = - { - [1] = - { - ["alt"] = 88, - ["action"] = "From Parking Area", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 138.88888888889, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["enabled"] = true, - ["key"] = "CAP", - ["id"] = "EngageTargets", - ["number"] = 1, - ["auto"] = true, - ["params"] = - { - ["targetTypes"] = - { - [1] = "Air", - }, -- end of ["targetTypes"] - ["priority"] = 0, - }, -- end of ["params"] - }, -- end of [1] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "TakeOffParking", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = 294994.1875, - ["x"] = -6702.66796875, - ["name"] = "DictKey_WptName_369", - ["formation_template"] = "", - ["airdromeId"] = 15, - ["speed_locked"] = true, - }, -- end of [1] - }, -- end of ["points"] - }, -- end of ["route"] - ["groupId"] = 21, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["alt"] = 88, - ["alt_type"] = "BARO", - ["livery_id"] = "Air Force Standard", - ["skill"] = "Client", - ["parking"] = "75", - ["speed"] = 138.88888888889, - ["type"] = "Su-27", - ["unitId"] = 26, - ["psi"] = 0, - ["parking_id"] = "44", - ["x"] = -6702.66796875, - ["name"] = "DictKey_UnitName_368", - ["payload"] = - { - ["pylons"] = - { - }, -- end of ["pylons"] - ["fuel"] = 5217, - ["flare"] = 96, - ["chaff"] = 96, - ["gun"] = 100, - }, -- end of ["payload"] - ["y"] = 294994.1875, - ["heading"] = 0, - ["callsign"] = 103, - ["onboard_num"] = "013", - }, -- end of [1] - }, -- end of ["units"] - ["y"] = 294994.1875, - ["x"] = -6702.66796875, - ["name"] = "DictKey_GroupName_367", - ["communication"] = true, - ["start_time"] = 0, - ["frequency"] = 127.5, - }, -- end of [3] - [4] = - { - ["modulation"] = 0, - ["tasks"] = - { - }, -- end of ["tasks"] - ["radioSet"] = false, - ["task"] = "CAP", - ["uncontrolled"] = false, - ["route"] = - { - ["points"] = - { - [1] = - { - ["alt"] = 88, - ["action"] = "From Parking Area", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 138.88888888889, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["enabled"] = true, - ["key"] = "CAP", - ["id"] = "EngageTargets", - ["number"] = 1, - ["auto"] = true, - ["params"] = - { - ["targetTypes"] = - { - [1] = "Air", - }, -- end of ["targetTypes"] - ["priority"] = 0, - }, -- end of ["params"] - }, -- end of [1] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "TakeOffParking", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = 295139.9375, - ["x"] = -6351.4077148438, - ["name"] = "DictKey_WptName_372", - ["formation_template"] = "", - ["airdromeId"] = 15, - ["speed_locked"] = true, - }, -- end of [1] - }, -- end of ["points"] - }, -- end of ["route"] - ["groupId"] = 22, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["alt"] = 88, - ["alt_type"] = "BARO", - ["livery_id"] = "Air Force Standard", - ["skill"] = "Client", - ["parking"] = "107", - ["speed"] = 138.88888888889, - ["type"] = "Su-27", - ["unitId"] = 27, - ["psi"] = 0, - ["parking_id"] = "48", - ["x"] = -6351.4077148438, - ["name"] = "DictKey_UnitName_371", - ["payload"] = - { - ["pylons"] = - { - }, -- end of ["pylons"] - ["fuel"] = 5217, - ["flare"] = 96, - ["chaff"] = 96, - ["gun"] = 100, - }, -- end of ["payload"] - ["y"] = 295139.9375, - ["heading"] = 0, - ["callsign"] = 103, - ["onboard_num"] = "013", - }, -- end of [1] - }, -- end of ["units"] - ["y"] = 295139.9375, - ["x"] = -6351.4077148438, - ["name"] = "DictKey_GroupName_370", - ["communication"] = true, - ["start_time"] = 0, - ["frequency"] = 127.5, - }, -- end of [4] - [5] = - { - ["modulation"] = 0, - ["tasks"] = - { - }, -- end of ["tasks"] - ["radioSet"] = false, - ["task"] = "CAP", - ["uncontrolled"] = false, - ["route"] = - { - ["points"] = - { - [1] = - { - ["alt"] = 88, - ["action"] = "From Parking Area", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 138.88888888889, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["enabled"] = true, - ["key"] = "CAP", - ["id"] = "EngageTargets", - ["number"] = 1, - ["auto"] = true, - ["params"] = - { - ["targetTypes"] = - { - [1] = "Air", - }, -- end of ["targetTypes"] - ["priority"] = 0, - }, -- end of ["params"] - }, -- end of [1] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "TakeOffParking", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = 295287.09375, - ["x"] = -6172.1333007813, - ["name"] = "DictKey_WptName_375", - ["formation_template"] = "", - ["airdromeId"] = 15, - ["speed_locked"] = true, - }, -- end of [1] - }, -- end of ["points"] - }, -- end of ["route"] - ["groupId"] = 23, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["alt"] = 88, - ["alt_type"] = "BARO", - ["livery_id"] = "Air Force Standard", - ["skill"] = "Client", - ["parking"] = "62", - ["speed"] = 138.88888888889, - ["type"] = "Su-27", - ["unitId"] = 28, - ["psi"] = 0, - ["parking_id"] = "51", - ["x"] = -6172.1333007813, - ["name"] = "DictKey_UnitName_374", - ["payload"] = - { - ["pylons"] = - { - }, -- end of ["pylons"] - ["fuel"] = 5217, - ["flare"] = 96, - ["chaff"] = 96, - ["gun"] = 100, - }, -- end of ["payload"] - ["y"] = 295287.09375, - ["heading"] = 0, - ["callsign"] = 103, - ["onboard_num"] = "013", - }, -- end of [1] - }, -- end of ["units"] - ["y"] = 295287.09375, - ["x"] = -6172.1333007813, - ["name"] = "DictKey_GroupName_373", - ["communication"] = true, - ["start_time"] = 0, - ["frequency"] = 127.5, - }, -- end of [5] - [6] = - { - ["modulation"] = 0, - ["tasks"] = - { - }, -- end of ["tasks"] - ["radioSet"] = false, - ["task"] = "CAP", - ["uncontrolled"] = false, - ["route"] = - { - ["points"] = - { - [1] = - { - ["alt"] = 88, - ["action"] = "From Parking Area", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 138.88888888889, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["enabled"] = true, - ["key"] = "CAP", - ["id"] = "EngageTargets", - ["number"] = 1, - ["auto"] = true, - ["params"] = - { - ["targetTypes"] = - { - [1] = "Air", - }, -- end of ["targetTypes"] - ["priority"] = 0, - }, -- end of ["params"] - }, -- end of [1] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "TakeOffParking", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = 295051.375, - ["x"] = -6629.2661132813, - ["name"] = "DictKey_WptName_378", - ["formation_template"] = "", - ["airdromeId"] = 15, - ["speed_locked"] = true, - }, -- end of [1] - }, -- end of ["points"] - }, -- end of ["route"] - ["groupId"] = 24, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["alt"] = 88, - ["alt_type"] = "BARO", - ["livery_id"] = "Air Force Standard", - ["skill"] = "Client", - ["parking"] = "76", - ["speed"] = 138.88888888889, - ["type"] = "Su-27", - ["unitId"] = 29, - ["psi"] = 0, - ["parking_id"] = "43", - ["x"] = -6629.2661132813, - ["name"] = "DictKey_UnitName_377", - ["payload"] = - { - ["pylons"] = - { - }, -- end of ["pylons"] - ["fuel"] = 5217, - ["flare"] = 96, - ["chaff"] = 96, - ["gun"] = 100, - }, -- end of ["payload"] - ["y"] = 295051.375, - ["heading"] = 0, - ["callsign"] = 103, - ["onboard_num"] = "013", - }, -- end of [1] - }, -- end of ["units"] - ["y"] = 295051.375, - ["x"] = -6629.2661132813, - ["name"] = "DictKey_GroupName_376", - ["communication"] = true, - ["start_time"] = 0, - ["frequency"] = 127.5, - }, -- end of [6] - [7] = - { - ["modulation"] = 0, - ["tasks"] = - { - }, -- end of ["tasks"] - ["radioSet"] = false, - ["task"] = "CAP", - ["uncontrolled"] = false, - ["route"] = - { - ["points"] = - { - [1] = - { - ["alt"] = 88, - ["action"] = "From Parking Area", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 138.88888888889, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["enabled"] = true, - ["key"] = "CAP", - ["id"] = "EngageTargets", - ["number"] = 1, - ["auto"] = true, - ["params"] = - { - ["targetTypes"] = - { - [1] = "Air", - }, -- end of ["targetTypes"] - ["priority"] = 0, - }, -- end of ["params"] - }, -- end of [1] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "TakeOffParking", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = 293769.75, - ["x"] = -7511.0883789063, - ["name"] = "DictKey_WptName_381", - ["formation_template"] = "", - ["airdromeId"] = 15, - ["speed_locked"] = true, - }, -- end of [1] - }, -- end of ["points"] - }, -- end of ["route"] - ["groupId"] = 25, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["alt"] = 88, - ["alt_type"] = "BARO", - ["livery_id"] = "Air Force Standard", - ["skill"] = "Client", - ["parking"] = "6", - ["speed"] = 138.88888888889, - ["type"] = "Su-27", - ["unitId"] = 30, - ["psi"] = 0, - ["parking_id"] = "03", - ["x"] = -7511.0883789063, - ["name"] = "DictKey_UnitName_380", - ["payload"] = - { - ["pylons"] = - { - }, -- end of ["pylons"] - ["fuel"] = 5217, - ["flare"] = 96, - ["chaff"] = 96, - ["gun"] = 100, - }, -- end of ["payload"] - ["y"] = 293769.75, - ["heading"] = 0, - ["callsign"] = 103, - ["onboard_num"] = "013", - }, -- end of [1] - }, -- end of ["units"] - ["y"] = 293769.75, - ["x"] = -7511.0883789063, - ["name"] = "DictKey_GroupName_379", - ["communication"] = true, - ["start_time"] = 0, - ["frequency"] = 127.5, - }, -- end of [7] - [8] = - { - ["modulation"] = 0, - ["tasks"] = - { - }, -- end of ["tasks"] - ["radioSet"] = false, - ["task"] = "CAP", - ["uncontrolled"] = false, - ["route"] = - { - ["points"] = - { - [1] = - { - ["alt"] = 88, - ["action"] = "From Parking Area", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 138.88888888889, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["enabled"] = true, - ["key"] = "CAP", - ["id"] = "EngageTargets", - ["number"] = 1, - ["auto"] = true, - ["params"] = - { - ["targetTypes"] = - { - [1] = "Air", - }, -- end of ["targetTypes"] - ["priority"] = 0, - }, -- end of ["params"] - }, -- end of [1] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "TakeOffParking", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = 295124.0625, - ["x"] = -6280.3999023438, - ["name"] = "DictKey_WptName_384", - ["formation_template"] = "", - ["airdromeId"] = 15, - ["speed_locked"] = true, - }, -- end of [1] - }, -- end of ["points"] - }, -- end of ["route"] - ["groupId"] = 26, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["alt"] = 88, - ["alt_type"] = "BARO", - ["livery_id"] = "Air Force Standard", - ["skill"] = "Client", - ["parking"] = "108", - ["speed"] = 138.88888888889, - ["type"] = "Su-27", - ["unitId"] = 31, - ["psi"] = 0, - ["parking_id"] = "49", - ["x"] = -6280.3999023438, - ["name"] = "DictKey_UnitName_383", - ["payload"] = - { - ["pylons"] = - { - }, -- end of ["pylons"] - ["fuel"] = 5217, - ["flare"] = 96, - ["chaff"] = 96, - ["gun"] = 100, - }, -- end of ["payload"] - ["y"] = 295124.0625, - ["heading"] = 0, - ["callsign"] = 103, - ["onboard_num"] = "013", - }, -- end of [1] - }, -- end of ["units"] - ["y"] = 295124.0625, - ["x"] = -6280.3999023438, - ["name"] = "DictKey_GroupName_382", - ["communication"] = true, - ["start_time"] = 0, - ["frequency"] = 127.5, - }, -- end of [8] - [9] = - { - ["modulation"] = 0, - ["tasks"] = - { - }, -- end of ["tasks"] - ["radioSet"] = false, - ["task"] = "CAP", - ["uncontrolled"] = false, - ["route"] = - { - ["points"] = - { - [1] = - { - ["alt"] = 88, - ["action"] = "From Parking Area", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 138.88888888889, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["enabled"] = true, - ["key"] = "CAP", - ["id"] = "EngageTargets", - ["number"] = 1, - ["auto"] = true, - ["params"] = - { - ["targetTypes"] = - { - [1] = "Air", - }, -- end of ["targetTypes"] - ["priority"] = 0, - }, -- end of ["params"] - }, -- end of [1] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "TakeOffParking", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = 293890.4375, - ["x"] = -7713.9541015625, - ["name"] = "DictKey_WptName_387", - ["formation_template"] = "", - ["airdromeId"] = 15, - ["speed_locked"] = true, - }, -- end of [1] - }, -- end of ["points"] - }, -- end of ["route"] - ["groupId"] = 27, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["alt"] = 88, - ["alt_type"] = "BARO", - ["livery_id"] = "Air Force Standard", - ["skill"] = "Client", - ["parking"] = "124", - ["speed"] = 138.88888888889, - ["type"] = "Su-27", - ["unitId"] = 32, - ["psi"] = 0, - ["parking_id"] = "09", - ["x"] = -7713.9541015625, - ["name"] = "DictKey_UnitName_386", - ["payload"] = - { - ["pylons"] = - { - }, -- end of ["pylons"] - ["fuel"] = 5217, - ["flare"] = 96, - ["chaff"] = 96, - ["gun"] = 100, - }, -- end of ["payload"] - ["y"] = 293890.4375, - ["heading"] = 0, - ["callsign"] = 103, - ["onboard_num"] = "013", - }, -- end of [1] - }, -- end of ["units"] - ["y"] = 293890.4375, - ["x"] = -7713.9541015625, - ["name"] = "DictKey_GroupName_385", - ["communication"] = true, - ["start_time"] = 0, - ["frequency"] = 127.5, - }, -- end of [9] - [10] = - { - ["modulation"] = 0, - ["tasks"] = - { - }, -- end of ["tasks"] - ["radioSet"] = false, - ["task"] = "CAP", - ["uncontrolled"] = false, - ["route"] = - { - ["points"] = - { - [1] = - { - ["alt"] = 88, - ["action"] = "From Parking Area", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 138.88888888889, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["enabled"] = true, - ["key"] = "CAP", - ["id"] = "EngageTargets", - ["number"] = 1, - ["auto"] = true, - ["params"] = - { - ["targetTypes"] = - { - [1] = "Air", - }, -- end of ["targetTypes"] - ["priority"] = 0, - }, -- end of ["params"] - }, -- end of [1] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "TakeOffParking", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = 295106.59375, - ["x"] = -6693.9848632813, - ["name"] = "DictKey_WptName_390", - ["formation_template"] = "", - ["airdromeId"] = 15, - ["speed_locked"] = true, - }, -- end of [1] - }, -- end of ["points"] - }, -- end of ["route"] - ["groupId"] = 28, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["alt"] = 88, - ["alt_type"] = "BARO", - ["livery_id"] = "Air Force Standard", - ["skill"] = "Client", - ["parking"] = "77", - ["speed"] = 138.88888888889, - ["type"] = "Su-27", - ["unitId"] = 33, - ["psi"] = 0, - ["parking_id"] = "42", - ["x"] = -6693.9848632813, - ["name"] = "DictKey_UnitName_389", - ["payload"] = - { - ["pylons"] = - { - }, -- end of ["pylons"] - ["fuel"] = 5217, - ["flare"] = 96, - ["chaff"] = 96, - ["gun"] = 100, - }, -- end of ["payload"] - ["y"] = 295106.59375, - ["heading"] = 0, - ["callsign"] = 103, - ["onboard_num"] = "013", - }, -- end of [1] - }, -- end of ["units"] - ["y"] = 295106.59375, - ["x"] = -6693.9848632813, - ["name"] = "DictKey_GroupName_388", - ["communication"] = true, - ["start_time"] = 0, - ["frequency"] = 127.5, - }, -- end of [10] - [11] = - { - ["modulation"] = 0, - ["tasks"] = - { - }, -- end of ["tasks"] - ["radioSet"] = false, - ["task"] = "CAP", - ["uncontrolled"] = false, - ["route"] = - { - ["points"] = - { - [1] = - { - ["alt"] = 88, - ["action"] = "From Parking Area", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 138.88888888889, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["enabled"] = true, - ["key"] = "CAP", - ["id"] = "EngageTargets", - ["number"] = 1, - ["auto"] = true, - ["params"] = - { - ["targetTypes"] = - { - [1] = "Air", - }, -- end of ["targetTypes"] - ["priority"] = 0, - }, -- end of ["params"] - }, -- end of [1] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "TakeOffParking", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = 293815.90625, - ["x"] = -7549.3779296875, - ["name"] = "DictKey_WptName_393", - ["formation_template"] = "", - ["airdromeId"] = 15, - ["speed_locked"] = true, - }, -- end of [1] - }, -- end of ["points"] - }, -- end of ["route"] - ["groupId"] = 29, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["alt"] = 88, - ["alt_type"] = "BARO", - ["livery_id"] = "Air Force Standard", - ["skill"] = "Client", - ["parking"] = "12", - ["speed"] = 138.88888888889, - ["type"] = "Su-27", - ["unitId"] = 34, - ["psi"] = 0, - ["parking_id"] = "06", - ["x"] = -7549.3779296875, - ["name"] = "DictKey_UnitName_392", - ["payload"] = - { - ["pylons"] = - { - }, -- end of ["pylons"] - ["fuel"] = 5217, - ["flare"] = 96, - ["chaff"] = 96, - ["gun"] = 100, - }, -- end of ["payload"] - ["y"] = 293815.90625, - ["heading"] = 0, - ["callsign"] = 103, - ["onboard_num"] = "013", - }, -- end of [1] - }, -- end of ["units"] - ["y"] = 293815.90625, - ["x"] = -7549.3779296875, - ["name"] = "DictKey_GroupName_391", - ["communication"] = true, - ["start_time"] = 0, - ["frequency"] = 127.5, - }, -- end of [11] - [12] = - { - ["modulation"] = 0, - ["tasks"] = - { - }, -- end of ["tasks"] - ["radioSet"] = false, - ["task"] = "CAP", - ["uncontrolled"] = false, - ["route"] = - { - ["points"] = - { - [1] = - { - ["alt"] = 88, - ["action"] = "From Parking Area", - ["alt_type"] = "BARO", - ["properties"] = - { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - }, -- end of ["properties"] - ["speed"] = 138.88888888889, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["enabled"] = true, - ["key"] = "CAP", - ["id"] = "EngageTargets", - ["number"] = 1, - ["auto"] = true, - ["params"] = - { - ["targetTypes"] = - { - [1] = "Air", - }, -- end of ["targetTypes"] - ["priority"] = 0, - }, -- end of ["params"] - }, -- end of [1] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "TakeOffParking", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = 293831.25, - ["x"] = -7562.1723632813, - ["name"] = "DictKey_WptName_396", - ["formation_template"] = "", - ["airdromeId"] = 15, - ["speed_locked"] = true, - }, -- end of [1] - }, -- end of ["points"] - }, -- end of ["route"] - ["groupId"] = 30, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["alt"] = 88, - ["alt_type"] = "BARO", - ["livery_id"] = "Air Force Standard", - ["skill"] = "Client", - ["parking"] = "14", - ["speed"] = 138.88888888889, - ["type"] = "Su-27", - ["unitId"] = 35, - ["psi"] = 0, - ["parking_id"] = "07", - ["x"] = -7562.1723632813, - ["name"] = "DictKey_UnitName_395", - ["payload"] = - { - ["pylons"] = - { - }, -- end of ["pylons"] - ["fuel"] = 5217, - ["flare"] = 96, - ["chaff"] = 96, - ["gun"] = 100, - }, -- end of ["payload"] - ["y"] = 293831.25, - ["heading"] = 0, - ["callsign"] = 103, - ["onboard_num"] = "013", - }, -- end of [1] - }, -- end of ["units"] - ["y"] = 293831.25, - ["x"] = -7562.1723632813, - ["name"] = "DictKey_GroupName_394", - ["communication"] = true, - ["start_time"] = 0, - ["frequency"] = 127.5, - }, -- end of [12] - }, -- end of ["group"] - }, -- end of ["plane"] - }, -- end of [1] - [2] = - { - ["id"] = 1, - ["name"] = "Ukraine", - }, -- end of [2] - [3] = - { - ["id"] = 18, - ["name"] = "Abkhazia", - }, -- end of [3] - [4] = - { - ["id"] = 19, - ["name"] = "South Ossetia", - }, -- end of [4] - [5] = - { - ["id"] = 37, - ["name"] = "Kazakhstan", - }, -- end of [5] - [6] = - { - ["id"] = 24, - ["name"] = "Belarus", - }, -- end of [6] - [7] = - { - ["id"] = 27, - ["name"] = "China", - }, -- end of [7] - [8] = - { - ["id"] = 43, - ["name"] = "Serbia", - }, -- end of [8] - [9] = - { - ["id"] = 47, - ["name"] = "Syria", - }, -- end of [9] - [10] = - { - ["id"] = 34, - ["name"] = "Iran", - }, -- end of [10] - [11] = - { - ["id"] = 38, - ["name"] = "North Korea", - }, -- end of [11] - }, -- end of ["country"] - }, -- end of ["red"] - }, -- end of ["coalition"] - ["sortie"] = "DictKey_sortie_4", - ["version"] = 12, - ["goals"] = - { - }, -- end of ["goals"] - ["currentKey"] = 12213, - ["start_time"] = 43200, - ["forcedOptions"] = - { - }, -- end of ["forcedOptions"] - ["failures"] = - { - ["OIL_RADIATOR_SENSOR"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "OIL_RADIATOR_SENSOR", - ["mm"] = 0, - }, -- end of ["OIL_RADIATOR_SENSOR"] - ["TURNIND_POINTER_FAILS_NO_VACUUM"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TURNIND_POINTER_FAILS_NO_VACUUM", - ["mm"] = 0, - }, -- end of ["TURNIND_POINTER_FAILS_NO_VACUUM"] - ["helmet"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "helmet", - ["mm"] = 0, - }, -- end of ["helmet"] - ["GUN_LEFT_IN_MOUNT_LOOSE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_LEFT_IN_MOUNT_LOOSE", - ["mm"] = 0, - }, -- end of ["GUN_LEFT_IN_MOUNT_LOOSE"] - ["es_damage_MainInverter"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "es_damage_MainInverter", - ["mm"] = 0, - }, -- end of ["es_damage_MainInverter"] - ["rws"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "rws", - ["mm"] = 0, - }, -- end of ["rws"] - ["AN_ALR69V_FAILURE_SENSOR_TAIL_RIGHT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AN_ALR69V_FAILURE_SENSOR_TAIL_RIGHT", - ["mm"] = 0, - }, -- end of ["AN_ALR69V_FAILURE_SENSOR_TAIL_RIGHT"] - ["MainReductor_ShaveInOil"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "MainReductor_ShaveInOil", - ["mm"] = 0, - }, -- end of ["MainReductor_ShaveInOil"] - ["asc_y"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "asc_y", - ["mm"] = 0, - }, -- end of ["asc_y"] - ["MAIN_L_GEAR_D_LOCK"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "MAIN_L_GEAR_D_LOCK", - ["mm"] = 0, - }, -- end of ["MAIN_L_GEAR_D_LOCK"] - ["AAR_47_FAILURE_SENSOR_LEFT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AAR_47_FAILURE_SENSOR_LEFT", - ["mm"] = 0, - }, -- end of ["AAR_47_FAILURE_SENSOR_LEFT"] - ["tail_reductor_chip"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "tail_reductor_chip", - ["mm"] = 0, - }, -- end of ["tail_reductor_chip"] - ["TACAN_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TACAN_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["TACAN_FAILURE_TOTAL"] - ["OIL_RADIATOR_MOTOR"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "OIL_RADIATOR_MOTOR", - ["mm"] = 0, - }, -- end of ["OIL_RADIATOR_MOTOR"] - ["SUPERCHARGER_WIRE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "SUPERCHARGER_WIRE", - ["mm"] = 0, - }, -- end of ["SUPERCHARGER_WIRE"] - ["CADC_FAILURE_TEMPERATURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CADC_FAILURE_TEMPERATURE", - ["mm"] = 0, - }, -- end of ["CADC_FAILURE_TEMPERATURE"] - ["FUSELAGE_TANK_LEAK"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "FUSELAGE_TANK_LEAK", - ["mm"] = 0, - }, -- end of ["FUSELAGE_TANK_LEAK"] - ["AN_ALE_40V_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AN_ALE_40V_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["AN_ALE_40V_FAILURE_TOTAL"] - ["HORIZON_BAR_NOT_SETTLE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "HORIZON_BAR_NOT_SETTLE", - ["mm"] = 0, - }, -- end of ["HORIZON_BAR_NOT_SETTLE"] - ["AN_ALE_40V_FAILURE_CONTAINER_LEFT_WING"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AN_ALE_40V_FAILURE_CONTAINER_LEFT_WING", - ["mm"] = 0, - }, -- end of ["AN_ALE_40V_FAILURE_CONTAINER_LEFT_WING"] - ["OIL_DILUTION_WIRE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "OIL_DILUTION_WIRE", - ["mm"] = 0, - }, -- end of ["OIL_DILUTION_WIRE"] - ["FLEX_S_BKP_LAMP_DEFECTIVE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "FLEX_S_BKP_LAMP_DEFECTIVE", - ["mm"] = 0, - }, -- end of ["FLEX_S_BKP_LAMP_DEFECTIVE"] - ["TAIL_GEAR_FAIL_GO_DOWN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TAIL_GEAR_FAIL_GO_DOWN", - ["mm"] = 0, - }, -- end of ["TAIL_GEAR_FAIL_GO_DOWN"] - ["GUN_FAIL_RIGHT_CENTER_GUN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_FAIL_RIGHT_CENTER_GUN", - ["mm"] = 0, - }, -- end of ["GUN_FAIL_RIGHT_CENTER_GUN"] - ["LeftEngine_ShaveInOil"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "LeftEngine_ShaveInOil", - ["mm"] = 0, - }, -- end of ["LeftEngine_ShaveInOil"] - ["MAIN_R_GEAR_D_LOCK"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "MAIN_R_GEAR_D_LOCK", - ["mm"] = 0, - }, -- end of ["MAIN_R_GEAR_D_LOCK"] - ["R_GEAR_DLK_FAULT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "R_GEAR_DLK_FAULT", - ["mm"] = 0, - }, -- end of ["R_GEAR_DLK_FAULT"] - ["GMC_GYRO_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GMC_GYRO_FAILURE", - ["mm"] = 0, - }, -- end of ["GMC_GYRO_FAILURE"] - ["L_GEAR_DLK_FAULT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "L_GEAR_DLK_FAULT", - ["mm"] = 0, - }, -- end of ["L_GEAR_DLK_FAULT"] - ["K14_FIXED_LAMP_DEFECTIVE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "K14_FIXED_LAMP_DEFECTIVE", - ["mm"] = 0, - }, -- end of ["K14_FIXED_LAMP_DEFECTIVE"] - ["GUN_FAIL_LEFT_CENTER_GUN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_FAIL_LEFT_CENTER_GUN", - ["mm"] = 0, - }, -- end of ["GUN_FAIL_LEFT_CENTER_GUN"] - ["engine_droop_failure"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "engine_droop_failure", - ["mm"] = 0, - }, -- end of ["engine_droop_failure"] - ["IGNITION_TERM_CONNECT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "IGNITION_TERM_CONNECT", - ["mm"] = 0, - }, -- end of ["IGNITION_TERM_CONNECT"] - ["CADC_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CADC_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["CADC_FAILURE_TOTAL"] - ["COOLANT_POOR_CONNTECT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "COOLANT_POOR_CONNTECT", - ["mm"] = 0, - }, -- end of ["COOLANT_POOR_CONNTECT"] - ["TURNIND_POINTER_FAILS_DEFECTIVE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TURNIND_POINTER_FAILS_DEFECTIVE", - ["mm"] = 0, - }, -- end of ["TURNIND_POINTER_FAILS_DEFECTIVE"] - ["GUN_FAIL_RIGHT_OUT_GUN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_FAIL_RIGHT_OUT_GUN", - ["mm"] = 0, - }, -- end of ["GUN_FAIL_RIGHT_OUT_GUN"] - ["BOMBS_DAMAGE_LINKAGE_LEFT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_DAMAGE_LINKAGE_LEFT", - ["mm"] = 0, - }, -- end of ["BOMBS_DAMAGE_LINKAGE_LEFT"] - ["FUSELAGE_TANK_PUMP_FAULT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "FUSELAGE_TANK_PUMP_FAULT", - ["mm"] = 0, - }, -- end of ["FUSELAGE_TANK_PUMP_FAULT"] - ["hydro_main"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "hydro_main", - ["mm"] = 0, - }, -- end of ["hydro_main"] - ["CICU_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CICU_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["CICU_FAILURE_TOTAL"] - ["GUN_LEFT_OUT_MOUNT_LOOSE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_LEFT_OUT_MOUNT_LOOSE", - ["mm"] = 0, - }, -- end of ["GUN_LEFT_OUT_MOUNT_LOOSE"] - ["TAIL_GEAR_U_LOCK"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TAIL_GEAR_U_LOCK", - ["mm"] = 0, - }, -- end of ["TAIL_GEAR_U_LOCK"] - ["RADAR_ALT_TOTAL_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "RADAR_ALT_TOTAL_FAILURE", - ["mm"] = 0, - }, -- end of ["RADAR_ALT_TOTAL_FAILURE"] - ["GUN_RIGHT_CENTER_MOUNT_LOOSE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_RIGHT_CENTER_MOUNT_LOOSE", - ["mm"] = 0, - }, -- end of ["GUN_RIGHT_CENTER_MOUNT_LOOSE"] - ["TAIL_GEAR_FAIL_GO_UP"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TAIL_GEAR_FAIL_GO_UP", - ["mm"] = 0, - }, -- end of ["TAIL_GEAR_FAIL_GO_UP"] - ["asc_r"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "asc_r", - ["mm"] = 0, - }, -- end of ["asc_r"] - ["BOMBS_SOLENOID_FAULT_LEFT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_SOLENOID_FAULT_LEFT", - ["mm"] = 0, - }, -- end of ["BOMBS_SOLENOID_FAULT_LEFT"] - ["sas_yaw_left"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "sas_yaw_left", - ["mm"] = 0, - }, -- end of ["sas_yaw_left"] - ["BOMBS_ARMING_NO_VOLATAGE_LEFT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_ARMING_NO_VOLATAGE_LEFT", - ["mm"] = 0, - }, -- end of ["BOMBS_ARMING_NO_VOLATAGE_LEFT"] - ["PITOT_HEAT_ELEMENT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "PITOT_HEAT_ELEMENT", - ["mm"] = 0, - }, -- end of ["PITOT_HEAT_ELEMENT"] - ["ILS_FAILURE_ANT_LOCALIZER"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "ILS_FAILURE_ANT_LOCALIZER", - ["mm"] = 0, - }, -- end of ["ILS_FAILURE_ANT_LOCALIZER"] - ["AN_ALE_40V_FAILURE_CONTAINER_LEFT_GEAR"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AN_ALE_40V_FAILURE_CONTAINER_LEFT_GEAR", - ["mm"] = 0, - }, -- end of ["AN_ALE_40V_FAILURE_CONTAINER_LEFT_GEAR"] - ["CARBAIR_SHORT_CIRCUIT_BLB"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CARBAIR_SHORT_CIRCUIT_BLB", - ["mm"] = 0, - }, -- end of ["CARBAIR_SHORT_CIRCUIT_BLB"] - ["LEFT_TANK_PUMP_FAULT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "LEFT_TANK_PUMP_FAULT", - ["mm"] = 0, - }, -- end of ["LEFT_TANK_PUMP_FAULT"] - ["Surge_RightEngine"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "Surge_RightEngine", - ["mm"] = 0, - }, -- end of ["Surge_RightEngine"] - ["RightEngine_Fire"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["mm"] = 0, - }, -- end of ["RightEngine_Fire"] - ["GUN_FAIL_LEFT_IN_GUN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_FAIL_LEFT_IN_GUN", - ["mm"] = 0, - }, -- end of ["GUN_FAIL_LEFT_IN_GUN"] - ["CADC_FAILURE_TAS"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CADC_FAILURE_TAS", - ["mm"] = 0, - }, -- end of ["CADC_FAILURE_TAS"] - ["STARTER_SOL_SHORT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "STARTER_SOL_SHORT", - ["mm"] = 0, - }, -- end of ["STARTER_SOL_SHORT"] - ["asc_p"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "asc_p", - ["mm"] = 0, - }, -- end of ["asc_p"] - ["COOLANT_SHORT_CIRCUIT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "COOLANT_SHORT_CIRCUIT", - ["mm"] = 0, - }, -- end of ["COOLANT_SHORT_CIRCUIT"] - ["GUN_LEFT_IN_AMMUN_FAULT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_LEFT_IN_AMMUN_FAULT", - ["mm"] = 0, - }, -- end of ["GUN_LEFT_IN_AMMUN_FAULT"] - ["PUMP_RELIEF_VALVE_SCREEN_CLOGGED"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "PUMP_RELIEF_VALVE_SCREEN_CLOGGED", - ["mm"] = 0, - }, -- end of ["PUMP_RELIEF_VALVE_SCREEN_CLOGGED"] - ["abris_hardware"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "abris_hardware", - ["mm"] = 0, - }, -- end of ["abris_hardware"] - ["EEC_Failure_LeftEngine"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "EEC_Failure_LeftEngine", - ["mm"] = 0, - }, -- end of ["EEC_Failure_LeftEngine"] - ["COMPASS_POINTER_PULLS"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "COMPASS_POINTER_PULLS", - ["mm"] = 0, - }, -- end of ["COMPASS_POINTER_PULLS"] - ["GUN_RIGHT_OUT_OPEN_CIRCUIT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_RIGHT_OUT_OPEN_CIRCUIT", - ["mm"] = 0, - }, -- end of ["GUN_RIGHT_OUT_OPEN_CIRCUIT"] - ["VHF_SHORTED_CTL_BOX"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "VHF_SHORTED_CTL_BOX", - ["mm"] = 0, - }, -- end of ["VHF_SHORTED_CTL_BOX"] - ["CLOCK_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CLOCK_FAILURE", - ["mm"] = 0, - }, -- end of ["CLOCK_FAILURE"] - ["TURNIND_INCORRECT_SENS_VAC_HIGH"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TURNIND_INCORRECT_SENS_VAC_HIGH", - ["mm"] = 0, - }, -- end of ["TURNIND_INCORRECT_SENS_VAC_HIGH"] - ["OIL_RADIATOR_WIRING"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "OIL_RADIATOR_WIRING", - ["mm"] = 0, - }, -- end of ["OIL_RADIATOR_WIRING"] - ["IGNITION_NO_OUTPUT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "IGNITION_NO_OUTPUT", - ["mm"] = 0, - }, -- end of ["IGNITION_NO_OUTPUT"] - ["AAR_47_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AAR_47_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["AAR_47_FAILURE_TOTAL"] - ["PILOT_KILLED_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "PILOT_KILLED_FAILURE", - ["mm"] = 0, - }, -- end of ["PILOT_KILLED_FAILURE"] - ["GUN_LEFT_CENTER_MOUNT_LOOSE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_LEFT_CENTER_MOUNT_LOOSE", - ["mm"] = 0, - }, -- end of ["GUN_LEFT_CENTER_MOUNT_LOOSE"] - ["GUN_LEFT_OUT_AMMUN_FAULT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_LEFT_OUT_AMMUN_FAULT", - ["mm"] = 0, - }, -- end of ["GUN_LEFT_OUT_AMMUN_FAULT"] - ["COMPASS_ERRATIC_OPERATION"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "COMPASS_ERRATIC_OPERATION", - ["mm"] = 0, - }, -- end of ["COMPASS_ERRATIC_OPERATION"] - ["asc_a"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "asc_a", - ["mm"] = 0, - }, -- end of ["asc_a"] - ["AIRSPEED_INDICATOR_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AIRSPEED_INDICATOR_FAILURE", - ["mm"] = 0, - }, -- end of ["AIRSPEED_INDICATOR_FAILURE"] - ["GUN_LEFT_CENTER_BARREL_WORN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_LEFT_CENTER_BARREL_WORN", - ["mm"] = 0, - }, -- end of ["GUN_LEFT_CENTER_BARREL_WORN"] - ["abris_software"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "abris_software", - ["mm"] = 0, - }, -- end of ["abris_software"] - ["GUN_FAIL_LEFT_OUT_GUN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_FAIL_LEFT_OUT_GUN", - ["mm"] = 0, - }, -- end of ["GUN_FAIL_LEFT_OUT_GUN"] - ["SAR_1_101"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "SAR_1_101", - ["mm"] = 0, - }, -- end of ["SAR_1_101"] - ["ROCKETS_INTERVALOMETER_WIRING"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "ROCKETS_INTERVALOMETER_WIRING", - ["mm"] = 0, - }, -- end of ["ROCKETS_INTERVALOMETER_WIRING"] - ["MainReductor_LowOilPressure"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "MainReductor_LowOilPressure", - ["mm"] = 0, - }, -- end of ["MainReductor_LowOilPressure"] - ["GUN_RIGHT_IN_AMMUN_FAULT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_RIGHT_IN_AMMUN_FAULT", - ["mm"] = 0, - }, -- end of ["GUN_RIGHT_IN_AMMUN_FAULT"] - ["D2_LEFT_CYLINDER"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "D2_LEFT_CYLINDER", - ["mm"] = 0, - }, -- end of ["D2_LEFT_CYLINDER"] - ["Surge_LeftEngine"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "Surge_LeftEngine", - ["mm"] = 0, - }, -- end of ["Surge_LeftEngine"] - ["BOMBS_RUST_LEFT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_RUST_LEFT", - ["mm"] = 0, - }, -- end of ["BOMBS_RUST_LEFT"] - ["GUN_RIGHT_CENTER_BARREL_WORN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_RIGHT_CENTER_BARREL_WORN", - ["mm"] = 0, - }, -- end of ["GUN_RIGHT_CENTER_BARREL_WORN"] - ["asc"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "asc", - ["mm"] = 0, - }, -- end of ["asc"] - ["COMPASS_NO_TORQUE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "COMPASS_NO_TORQUE", - ["mm"] = 0, - }, -- end of ["COMPASS_NO_TORQUE"] - ["COOLANT_BREAK_BULB"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "COOLANT_BREAK_BULB", - ["mm"] = 0, - }, -- end of ["COOLANT_BREAK_BULB"] - ["PROP_GOVERNOR"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "PROP_GOVERNOR", - ["mm"] = 0, - }, -- end of ["PROP_GOVERNOR"] - ["MANIFOLD_SHIFT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "MANIFOLD_SHIFT", - ["mm"] = 0, - }, -- end of ["MANIFOLD_SHIFT"] - ["RIGHT_GUNNER_KILLED_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "RIGHT_GUNNER_KILLED_FAILURE", - ["mm"] = 0, - }, -- end of ["RIGHT_GUNNER_KILLED_FAILURE"] - ["UNLOAD_VALVE_NOT_UNLOAD"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "UNLOAD_VALVE_NOT_UNLOAD", - ["mm"] = 0, - }, -- end of ["UNLOAD_VALVE_NOT_UNLOAD"] - ["STARTER_BURNOUT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "STARTER_BURNOUT", - ["mm"] = 0, - }, -- end of ["STARTER_BURNOUT"] - ["UNLOAD_VALVE_NOT_LOAD"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "UNLOAD_VALVE_NOT_LOAD", - ["mm"] = 0, - }, -- end of ["UNLOAD_VALVE_NOT_LOAD"] - ["TURNIND_INCORRECT_SENS_VAC_LOW"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TURNIND_INCORRECT_SENS_VAC_LOW", - ["mm"] = 0, - }, -- end of ["TURNIND_INCORRECT_SENS_VAC_LOW"] - ["Failure_LeftEngine"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "Failure_LeftEngine", - ["mm"] = 0, - }, -- end of ["Failure_LeftEngine"] - ["GUN_RIGHT_IN_BARREL_WORN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_RIGHT_IN_BARREL_WORN", - ["mm"] = 0, - }, -- end of ["GUN_RIGHT_IN_BARREL_WORN"] - ["K14_MOV_LAMP_DEFECTIVE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "K14_MOV_LAMP_DEFECTIVE", - ["mm"] = 0, - }, -- end of ["K14_MOV_LAMP_DEFECTIVE"] - ["ILS_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "ILS_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["ILS_FAILURE_TOTAL"] - ["GUN_RIGHT_OUT_BARREL_WORN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_RIGHT_OUT_BARREL_WORN", - ["mm"] = 0, - }, -- end of ["GUN_RIGHT_OUT_BARREL_WORN"] - ["fuel_sys_transfer_pumps"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "fuel_sys_transfer_pumps", - ["mm"] = 0, - }, -- end of ["fuel_sys_transfer_pumps"] - ["PITOT_HEAT_WIRING"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "PITOT_HEAT_WIRING", - ["mm"] = 0, - }, -- end of ["PITOT_HEAT_WIRING"] - ["TURNIND_POINTER_NOT_SET_ZERO"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TURNIND_POINTER_NOT_SET_ZERO", - ["mm"] = 0, - }, -- end of ["TURNIND_POINTER_NOT_SET_ZERO"] - ["MD1_GYRO_TOTAL_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "MD1_GYRO_TOTAL_FAILURE", - ["mm"] = 0, - }, -- end of ["MD1_GYRO_TOTAL_FAILURE"] - ["VHF_FM_RADIO_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "VHF_FM_RADIO_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["VHF_FM_RADIO_FAILURE_TOTAL"] - ["RIGHT_MFCD_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "RIGHT_MFCD_FAILURE", - ["mm"] = 0, - }, -- end of ["RIGHT_MFCD_FAILURE"] - ["F2_BOTTOM_CYLINDER"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "F2_BOTTOM_CYLINDER", - ["mm"] = 0, - }, -- end of ["F2_BOTTOM_CYLINDER"] - ["LEFT_WING_TANK_LEAK"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "LEFT_WING_TANK_LEAK", - ["mm"] = 0, - }, -- end of ["LEFT_WING_TANK_LEAK"] - ["CARBAIR_BREAK_LEADS"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CARBAIR_BREAK_LEADS", - ["mm"] = 0, - }, -- end of ["CARBAIR_BREAK_LEADS"] - ["GUN_LEFT_IN_OPEN_CIRCUIT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_LEFT_IN_OPEN_CIRCUIT", - ["mm"] = 0, - }, -- end of ["GUN_LEFT_IN_OPEN_CIRCUIT"] - ["EGI_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "EGI_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["EGI_FAILURE_TOTAL"] - ["UHF_RADIO_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "UHF_RADIO_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["UHF_RADIO_FAILURE_TOTAL"] - ["GUN_RIGHT_CENTER_AMMUN_FAULT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_RIGHT_CENTER_AMMUN_FAULT", - ["mm"] = 0, - }, -- end of ["GUN_RIGHT_CENTER_AMMUN_FAULT"] - ["LEFT_GUNNER_KILLED_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "LEFT_GUNNER_KILLED_FAILURE", - ["mm"] = 0, - }, -- end of ["LEFT_GUNNER_KILLED_FAILURE"] - ["VHF_VT207_DEFECTIVE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "VHF_VT207_DEFECTIVE", - ["mm"] = 0, - }, -- end of ["VHF_VT207_DEFECTIVE"] - ["RightEngine_LowOilPressure"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "RightEngine_LowOilPressure", - ["mm"] = 0, - }, -- end of ["RightEngine_LowOilPressure"] - ["radar"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "radar", - ["mm"] = 0, - }, -- end of ["radar"] - ["RIGHT_TANK_PUMP_FAULT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "RIGHT_TANK_PUMP_FAULT", - ["mm"] = 0, - }, -- end of ["RIGHT_TANK_PUMP_FAULT"] - ["COOLANT_UNPRES"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "COOLANT_UNPRES", - ["mm"] = 0, - }, -- end of ["COOLANT_UNPRES"] - ["ARN_82_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "ARN_82_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["ARN_82_FAILURE_TOTAL"] - ["FLEX_S_NO_POWER_SUPPLY"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "FLEX_S_NO_POWER_SUPPLY", - ["mm"] = 0, - }, -- end of ["FLEX_S_NO_POWER_SUPPLY"] - ["eos"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "eos", - ["mm"] = 0, - }, -- end of ["eos"] - ["HYDRO_LOW_AIR_PRESSURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "HYDRO_LOW_AIR_PRESSURE", - ["mm"] = 0, - }, -- end of ["HYDRO_LOW_AIR_PRESSURE"] - ["K14_MOTOR_DEFECTIVE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "K14_MOTOR_DEFECTIVE", - ["mm"] = 0, - }, -- end of ["K14_MOTOR_DEFECTIVE"] - ["GENERATOR_FAULT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GENERATOR_FAULT", - ["mm"] = 0, - }, -- end of ["GENERATOR_FAULT"] - ["FUEL_PUMP_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "FUEL_PUMP_FAILURE", - ["mm"] = 0, - }, -- end of ["FUEL_PUMP_FAILURE"] - ["RADAR_ALTIMETR_LEFT_ANT_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "RADAR_ALTIMETR_LEFT_ANT_FAILURE", - ["mm"] = 0, - }, -- end of ["RADAR_ALTIMETR_LEFT_ANT_FAILURE"] - ["hydro"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "hydro", - ["mm"] = 0, - }, -- end of ["hydro"] - ["BAT_SOLENOID_DEFECTIVE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BAT_SOLENOID_DEFECTIVE", - ["mm"] = 0, - }, -- end of ["BAT_SOLENOID_DEFECTIVE"] - ["LeftEngine_Fire"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["mm"] = 0, - }, -- end of ["LeftEngine_Fire"] - ["SUPERCHARGER_LIGHT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "SUPERCHARGER_LIGHT", - ["mm"] = 0, - }, -- end of ["SUPERCHARGER_LIGHT"] - ["L_GEAR_UPL_FAULT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "L_GEAR_UPL_FAULT", - ["mm"] = 0, - }, -- end of ["L_GEAR_UPL_FAULT"] - ["fs_damage_right_cell_boost_pump"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "fs_damage_right_cell_boost_pump", - ["mm"] = 0, - }, -- end of ["fs_damage_right_cell_boost_pump"] - ["TACH_RESISTANCE_ADJ"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TACH_RESISTANCE_ADJ", - ["mm"] = 0, - }, -- end of ["TACH_RESISTANCE_ADJ"] - ["MAGNETO_1"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "MAGNETO_1", - ["mm"] = 0, - }, -- end of ["MAGNETO_1"] - ["BOMBS_NO_VOLATAGE_AT_RACK_RIGHT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_NO_VOLATAGE_AT_RACK_RIGHT", - ["mm"] = 0, - }, -- end of ["BOMBS_NO_VOLATAGE_AT_RACK_RIGHT"] - ["GUN_RIGHT_OUT_MOUNT_LOOSE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_RIGHT_OUT_MOUNT_LOOSE", - ["mm"] = 0, - }, -- end of ["GUN_RIGHT_OUT_MOUNT_LOOSE"] - ["TailReductor_ShaveInOil"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TailReductor_ShaveInOil", - ["mm"] = 0, - }, -- end of ["TailReductor_ShaveInOil"] - ["R_GEAR_UPL_FAULT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "R_GEAR_UPL_FAULT", - ["mm"] = 0, - }, -- end of ["R_GEAR_UPL_FAULT"] - ["BOMBS_TRAIN_DEFECTIVE_WIRING"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_TRAIN_DEFECTIVE_WIRING", - ["mm"] = 0, - }, -- end of ["BOMBS_TRAIN_DEFECTIVE_WIRING"] - ["autopilot"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "autopilot", - ["mm"] = 0, - }, -- end of ["autopilot"] - ["BOMBS_TRAIN_DEFECTIVE_SWITCH"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_TRAIN_DEFECTIVE_SWITCH", - ["mm"] = 0, - }, -- end of ["BOMBS_TRAIN_DEFECTIVE_SWITCH"] - ["CARBAIR_SHORT_CIRCUIT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CARBAIR_SHORT_CIRCUIT", - ["mm"] = 0, - }, -- end of ["CARBAIR_SHORT_CIRCUIT"] - ["STARTER_RELAY"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "STARTER_RELAY", - ["mm"] = 0, - }, -- end of ["STARTER_RELAY"] - ["AN_ALE_40V_FAILURE_CONTAINER_RIGHT_WING"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AN_ALE_40V_FAILURE_CONTAINER_RIGHT_WING", - ["mm"] = 0, - }, -- end of ["AN_ALE_40V_FAILURE_CONTAINER_RIGHT_WING"] - ["TACH_POOR_CONNECTION"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TACH_POOR_CONNECTION", - ["mm"] = 0, - }, -- end of ["TACH_POOR_CONNECTION"] - ["GUN_RIGHT_IN_MOUNT_LOOSE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_RIGHT_IN_MOUNT_LOOSE", - ["mm"] = 0, - }, -- end of ["GUN_RIGHT_IN_MOUNT_LOOSE"] - ["LEFT_MFCD_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "LEFT_MFCD_FAILURE", - ["mm"] = 0, - }, -- end of ["LEFT_MFCD_FAILURE"] - ["sas_yaw_right"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "sas_yaw_right", - ["mm"] = 0, - }, -- end of ["sas_yaw_right"] - ["DOORS_TVC_BROKEN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "DOORS_TVC_BROKEN", - ["mm"] = 0, - }, -- end of ["DOORS_TVC_BROKEN"] - ["SADL_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "SADL_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["SADL_FAILURE_TOTAL"] - ["fs_damage_left_cell_boost_pump"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "fs_damage_left_cell_boost_pump", - ["mm"] = 0, - }, -- end of ["fs_damage_left_cell_boost_pump"] - ["BOOST_REG"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOOST_REG", - ["mm"] = 0, - }, -- end of ["BOOST_REG"] - ["r_conv"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "r_conv", - ["mm"] = 0, - }, -- end of ["r_conv"] - ["ENGINE_JAM"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "ENGINE_JAM", - ["mm"] = 0, - }, -- end of ["ENGINE_JAM"] - ["MAGNETO_2"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "MAGNETO_2", - ["mm"] = 0, - }, -- end of ["MAGNETO_2"] - ["SAR_1_95"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "SAR_1_95", - ["mm"] = 0, - }, -- end of ["SAR_1_95"] - ["BOMBS_SOLENOID_FAULT_RIGHT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_SOLENOID_FAULT_RIGHT", - ["mm"] = 0, - }, -- end of ["BOMBS_SOLENOID_FAULT_RIGHT"] - ["CDU_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CDU_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["CDU_FAILURE_TOTAL"] - ["AN_ALR69V_FAILURE_SENSOR_NOSE_RIGHT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AN_ALR69V_FAILURE_SENSOR_NOSE_RIGHT", - ["mm"] = 0, - }, -- end of ["AN_ALR69V_FAILURE_SENSOR_NOSE_RIGHT"] - ["TAIL_GEAR_C_CABLE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TAIL_GEAR_C_CABLE", - ["mm"] = 0, - }, -- end of ["TAIL_GEAR_C_CABLE"] - ["STARTER_WIRING"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "STARTER_WIRING", - ["mm"] = 0, - }, -- end of ["STARTER_WIRING"] - ["engine_driveshaft_failure"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "engine_driveshaft_failure", - ["mm"] = 0, - }, -- end of ["engine_driveshaft_failure"] - ["PUMP_RELIEF_VALVE_LEAKS"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "PUMP_RELIEF_VALVE_LEAKS", - ["mm"] = 0, - }, -- end of ["PUMP_RELIEF_VALVE_LEAKS"] - ["HUD_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "HUD_FAILURE", - ["mm"] = 0, - }, -- end of ["HUD_FAILURE"] - ["mfd"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "mfd", - ["mm"] = 0, - }, -- end of ["mfd"] - ["CARBAIR_GND_LEAD"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CARBAIR_GND_LEAD", - ["mm"] = 0, - }, -- end of ["CARBAIR_GND_LEAD"] - ["GMC_MAGN_COMP_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GMC_MAGN_COMP_FAILURE", - ["mm"] = 0, - }, -- end of ["GMC_MAGN_COMP_FAILURE"] - ["es_damage_GeneratorLeft"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "es_damage_GeneratorLeft", - ["mm"] = 0, - }, -- end of ["es_damage_GeneratorLeft"] - ["ILS_FAILURE_ANT_GLIDESLOPE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "ILS_FAILURE_ANT_GLIDESLOPE", - ["mm"] = 0, - }, -- end of ["ILS_FAILURE_ANT_GLIDESLOPE"] - ["engine_chip"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "engine_chip", - ["mm"] = 0, - }, -- end of ["engine_chip"] - ["ARN_83_TOTAL_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "ARN_83_TOTAL_FAILURE", - ["mm"] = 0, - }, -- end of ["ARN_83_TOTAL_FAILURE"] - ["CADC_FAILURE_MACH"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CADC_FAILURE_MACH", - ["mm"] = 0, - }, -- end of ["CADC_FAILURE_MACH"] - ["ROCKETS_DEFECTIVE_WIRING"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "ROCKETS_DEFECTIVE_WIRING", - ["mm"] = 0, - }, -- end of ["ROCKETS_DEFECTIVE_WIRING"] - ["COPILOT_GYRO_TOTAL_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "COPILOT_GYRO_TOTAL_FAILURE", - ["mm"] = 0, - }, -- end of ["COPILOT_GYRO_TOTAL_FAILURE"] - ["RightEngine_ShaveInOil"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "RightEngine_ShaveInOil", - ["mm"] = 0, - }, -- end of ["RightEngine_ShaveInOil"] - ["EEC_Failure_RightEngine"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "EEC_Failure_RightEngine", - ["mm"] = 0, - }, -- end of ["EEC_Failure_RightEngine"] - ["laser_failure"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "laser_failure", - ["mm"] = 0, - }, -- end of ["laser_failure"] - ["ARN_83_ADF_DAMAGE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "ARN_83_ADF_DAMAGE", - ["mm"] = 0, - }, -- end of ["ARN_83_ADF_DAMAGE"] - ["BOMBS_NO_VOLATAGE_BOTH"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_NO_VOLATAGE_BOTH", - ["mm"] = 0, - }, -- end of ["BOMBS_NO_VOLATAGE_BOTH"] - ["es_damage_SpareInverter"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "es_damage_SpareInverter", - ["mm"] = 0, - }, -- end of ["es_damage_SpareInverter"] - ["AAR_47_FAILURE_SENSOR_BOTTOM"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AAR_47_FAILURE_SENSOR_BOTTOM", - ["mm"] = 0, - }, -- end of ["AAR_47_FAILURE_SENSOR_BOTTOM"] - ["CARBAIR_SHORT_CIRCUIT_LEADS"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CARBAIR_SHORT_CIRCUIT_LEADS", - ["mm"] = 0, - }, -- end of ["CARBAIR_SHORT_CIRCUIT_LEADS"] - ["sas_pitch_left"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "sas_pitch_left", - ["mm"] = 0, - }, -- end of ["sas_pitch_left"] - ["BATTERY_OVERHEAT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BATTERY_OVERHEAT", - ["mm"] = 0, - }, -- end of ["BATTERY_OVERHEAT"] - ["NOSE_AIRSPEED_INDICATOR_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "NOSE_AIRSPEED_INDICATOR_FAILURE", - ["mm"] = 0, - }, -- end of ["NOSE_AIRSPEED_INDICATOR_FAILURE"] - ["CADC_FAILURE_DYNAMIC"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CADC_FAILURE_DYNAMIC", - ["mm"] = 0, - }, -- end of ["CADC_FAILURE_DYNAMIC"] - ["INSUF_FUEL_PRES"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "INSUF_FUEL_PRES", - ["mm"] = 0, - }, -- end of ["INSUF_FUEL_PRES"] - ["GUN_LEFT_CENTER_OPEN_CIRCUIT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_LEFT_CENTER_OPEN_CIRCUIT", - ["mm"] = 0, - }, -- end of ["GUN_LEFT_CENTER_OPEN_CIRCUIT"] - ["AN_ALR69V_FAILURE_SENSOR_TAIL_LEFT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AN_ALR69V_FAILURE_SENSOR_TAIL_LEFT", - ["mm"] = 0, - }, -- end of ["AN_ALR69V_FAILURE_SENSOR_TAIL_LEFT"] - ["CADC_FAILURE_IAS"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CADC_FAILURE_IAS", - ["mm"] = 0, - }, -- end of ["CADC_FAILURE_IAS"] - ["GUN_LEFT_CENTER_AMMUN_FAULT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_LEFT_CENTER_AMMUN_FAULT", - ["mm"] = 0, - }, -- end of ["GUN_LEFT_CENTER_AMMUN_FAULT"] - ["GMC_TOTAL_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GMC_TOTAL_FAILURE", - ["mm"] = 0, - }, -- end of ["GMC_TOTAL_FAILURE"] - ["DEFECTIVE_MECHANISM"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "DEFECTIVE_MECHANISM", - ["mm"] = 0, - }, -- end of ["DEFECTIVE_MECHANISM"] - ["INT_HYDRO_LEAK"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "INT_HYDRO_LEAK", - ["mm"] = 0, - }, -- end of ["INT_HYDRO_LEAK"] - ["TACH_BREAK_CIRCUIT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TACH_BREAK_CIRCUIT", - ["mm"] = 0, - }, -- end of ["TACH_BREAK_CIRCUIT"] - ["hydro_main_irreversible_valve"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "hydro_main_irreversible_valve", - ["mm"] = 0, - }, -- end of ["hydro_main_irreversible_valve"] - ["COOLANT_DEFECTIVE_IND"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "COOLANT_DEFECTIVE_IND", - ["mm"] = 0, - }, -- end of ["COOLANT_DEFECTIVE_IND"] - ["hud"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "hud", - ["mm"] = 0, - }, -- end of ["hud"] - ["STARTER_SOLENOID"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "STARTER_SOLENOID", - ["mm"] = 0, - }, -- end of ["STARTER_SOLENOID"] - ["hydro_left"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "hydro_left", - ["mm"] = 0, - }, -- end of ["hydro_left"] - ["SAR_1_2_95"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "SAR_1_2_95", - ["mm"] = 0, - }, -- end of ["SAR_1_2_95"] - ["fuel_sys_left_transfer_pump"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "fuel_sys_left_transfer_pump", - ["mm"] = 0, - }, -- end of ["fuel_sys_left_transfer_pump"] - ["LeftEngine_LowOilPressure"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "LeftEngine_LowOilPressure", - ["mm"] = 0, - }, -- end of ["LeftEngine_LowOilPressure"] - ["BOMBS_ARMING_BROKEN_SOLENOID_LEFT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_ARMING_BROKEN_SOLENOID_LEFT", - ["mm"] = 0, - }, -- end of ["BOMBS_ARMING_BROKEN_SOLENOID_LEFT"] - ["FAULTY_ROCKET_LEFT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "FAULTY_ROCKET_LEFT", - ["mm"] = 0, - }, -- end of ["FAULTY_ROCKET_LEFT"] - ["l_conv"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "l_conv", - ["mm"] = 0, - }, -- end of ["l_conv"] - ["hydro_right"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "hydro_right", - ["mm"] = 0, - }, -- end of ["hydro_right"] - ["es_damage_StarterGenerator"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "es_damage_StarterGenerator", - ["mm"] = 0, - }, -- end of ["es_damage_StarterGenerator"] - ["es_damage_Battery"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "es_damage_Battery", - ["mm"] = 0, - }, -- end of ["es_damage_Battery"] - ["ILS_FAILURE_ANT_MARKER"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "ILS_FAILURE_ANT_MARKER", - ["mm"] = 0, - }, -- end of ["ILS_FAILURE_ANT_MARKER"] - ["TGP_FAILURE_LEFT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TGP_FAILURE_LEFT", - ["mm"] = 0, - }, -- end of ["TGP_FAILURE_LEFT"] - ["VHF_CRYSTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "VHF_CRYSTAL", - ["mm"] = 0, - }, -- end of ["VHF_CRYSTAL"] - ["VHF_VT_BURNED_OUT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "VHF_VT_BURNED_OUT", - ["mm"] = 0, - }, -- end of ["VHF_VT_BURNED_OUT"] - ["sas_pitch_right"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "sas_pitch_right", - ["mm"] = 0, - }, -- end of ["sas_pitch_right"] - ["BOMBS_NO_VOLATAGE_AT_RACK_LEFT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_NO_VOLATAGE_AT_RACK_LEFT", - ["mm"] = 0, - }, -- end of ["BOMBS_NO_VOLATAGE_AT_RACK_LEFT"] - ["TGP_FAILURE_RIGHT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TGP_FAILURE_RIGHT", - ["mm"] = 0, - }, -- end of ["TGP_FAILURE_RIGHT"] - ["TACAN_FAILURE_RECEIVER"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TACAN_FAILURE_RECEIVER", - ["mm"] = 0, - }, -- end of ["TACAN_FAILURE_RECEIVER"] - ["VHF_AM_RADIO_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "VHF_AM_RADIO_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["VHF_AM_RADIO_FAILURE_TOTAL"] - ["main_reductor_chip"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "main_reductor_chip", - ["mm"] = 0, - }, -- end of ["main_reductor_chip"] - ["VHF_SQUELCH_RELAY"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "VHF_SQUELCH_RELAY", - ["mm"] = 0, - }, -- end of ["VHF_SQUELCH_RELAY"] - ["COOLANT_RADIATOR_WIRING"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "COOLANT_RADIATOR_WIRING", - ["mm"] = 0, - }, -- end of ["COOLANT_RADIATOR_WIRING"] - ["RADAR_ALTIMETR_RIGHT_ANT_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "RADAR_ALTIMETR_RIGHT_ANT_FAILURE", - ["mm"] = 0, - }, -- end of ["RADAR_ALTIMETR_RIGHT_ANT_FAILURE"] - ["F2_TOP_CYLINDER"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "F2_TOP_CYLINDER", - ["mm"] = 0, - }, -- end of ["F2_TOP_CYLINDER"] - ["FLEX_S_MAIN_LAMP_DEFECTIVE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "FLEX_S_MAIN_LAMP_DEFECTIVE", - ["mm"] = 0, - }, -- end of ["FLEX_S_MAIN_LAMP_DEFECTIVE"] - ["MANIFOLD_LINE_LEAK"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "MANIFOLD_LINE_LEAK", - ["mm"] = 0, - }, -- end of ["MANIFOLD_LINE_LEAK"] - ["engine_surge_failure"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "engine_surge_failure", - ["mm"] = 0, - }, -- end of ["engine_surge_failure"] - ["CADC_FAILURE_PRESSURE_ALT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CADC_FAILURE_PRESSURE_ALT", - ["mm"] = 0, - }, -- end of ["CADC_FAILURE_PRESSURE_ALT"] - ["K14_NO_POWER_SUPPLY"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "K14_NO_POWER_SUPPLY", - ["mm"] = 0, - }, -- end of ["K14_NO_POWER_SUPPLY"] - ["PUMP_FAILS"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "PUMP_FAILS", - ["mm"] = 0, - }, -- end of ["PUMP_FAILS"] - ["RIGHT_WING_TANK_LEAK"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "RIGHT_WING_TANK_LEAK", - ["mm"] = 0, - }, -- end of ["RIGHT_WING_TANK_LEAK"] - ["BOMBS_DAMAGE_LINKAGE_RIGHT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_DAMAGE_LINKAGE_RIGHT", - ["mm"] = 0, - }, -- end of ["BOMBS_DAMAGE_LINKAGE_RIGHT"] - ["fs_damage_engine_pump"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "fs_damage_engine_pump", - ["mm"] = 0, - }, -- end of ["fs_damage_engine_pump"] - ["ROOF_AIRSPEED_INDICATOR_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "ROOF_AIRSPEED_INDICATOR_FAILURE", - ["mm"] = 0, - }, -- end of ["ROOF_AIRSPEED_INDICATOR_FAILURE"] - ["CADC_FAILURE_STATIC"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CADC_FAILURE_STATIC", - ["mm"] = 0, - }, -- end of ["CADC_FAILURE_STATIC"] - ["CADC_FAILURE_BARO_ALT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CADC_FAILURE_BARO_ALT", - ["mm"] = 0, - }, -- end of ["CADC_FAILURE_BARO_ALT"] - ["GUN_RIGHT_CENTER_OPEN_CIRCUIT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_RIGHT_CENTER_OPEN_CIRCUIT", - ["mm"] = 0, - }, -- end of ["GUN_RIGHT_CENTER_OPEN_CIRCUIT"] - ["IFFCC_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "IFFCC_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["IFFCC_FAILURE_TOTAL"] - ["GUN_FAIL_RIGHT_IN_GUN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_FAIL_RIGHT_IN_GUN", - ["mm"] = 0, - }, -- end of ["GUN_FAIL_RIGHT_IN_GUN"] - ["BOMBS_ARMING_BROKEN_WIRING_LEFT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_ARMING_BROKEN_WIRING_LEFT", - ["mm"] = 0, - }, -- end of ["BOMBS_ARMING_BROKEN_WIRING_LEFT"] - ["Failure_RightEngine"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "Failure_RightEngine", - ["mm"] = 0, - }, -- end of ["Failure_RightEngine"] - ["ecf"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["mm"] = 0, - }, -- end of ["ecf"] - ["GUN_LEFT_OUT_BARREL_WORN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_LEFT_OUT_BARREL_WORN", - ["mm"] = 0, - }, -- end of ["GUN_LEFT_OUT_BARREL_WORN"] - ["EXT_HYDRO_LEAK"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "EXT_HYDRO_LEAK", - ["mm"] = 0, - }, -- end of ["EXT_HYDRO_LEAK"] - ["BOMBS_ARMING_BROKEN_SOLENOID_RIGHT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_ARMING_BROKEN_SOLENOID_RIGHT", - ["mm"] = 0, - }, -- end of ["BOMBS_ARMING_BROKEN_SOLENOID_RIGHT"] - ["DEFECTIVE_INSTRUMENT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "DEFECTIVE_INSTRUMENT", - ["mm"] = 0, - }, -- end of ["DEFECTIVE_INSTRUMENT"] - ["AN_ALR69V_FAILURE_SENSOR_NOSE_LEFT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AN_ALR69V_FAILURE_SENSOR_NOSE_LEFT", - ["mm"] = 0, - }, -- end of ["AN_ALR69V_FAILURE_SENSOR_NOSE_LEFT"] - ["mlws"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "mlws", - ["mm"] = 0, - }, -- end of ["mlws"] - ["BOMBS_ARMING_NO_VOLATAGE_BOTH"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_ARMING_NO_VOLATAGE_BOTH", - ["mm"] = 0, - }, -- end of ["BOMBS_ARMING_NO_VOLATAGE_BOTH"] - ["BAT_SOLENOID_WIRING"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BAT_SOLENOID_WIRING", - ["mm"] = 0, - }, -- end of ["BAT_SOLENOID_WIRING"] - ["STARTER_LOSE_CON"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "STARTER_LOSE_CON", - ["mm"] = 0, - }, -- end of ["STARTER_LOSE_CON"] - ["FUEL_VALVE_LEAK"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "FUEL_VALVE_LEAK", - ["mm"] = 0, - }, -- end of ["FUEL_VALVE_LEAK"] - ["FLEX_S_NO_GUN_SIGN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "FLEX_S_NO_GUN_SIGN", - ["mm"] = 0, - }, -- end of ["FLEX_S_NO_GUN_SIGN"] - ["fuel_sys_right_transfer_pump"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "fuel_sys_right_transfer_pump", - ["mm"] = 0, - }, -- end of ["fuel_sys_right_transfer_pump"] - ["COOLANT_RADIATOR_MOTOR"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "COOLANT_RADIATOR_MOTOR", - ["mm"] = 0, - }, -- end of ["COOLANT_RADIATOR_MOTOR"] - ["CARBAIR_OPEN_CIRCUIT_BLB"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CARBAIR_OPEN_CIRCUIT_BLB", - ["mm"] = 0, - }, -- end of ["CARBAIR_OPEN_CIRCUIT_BLB"] - ["AAR_47_FAILURE_SENSOR_TAIL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AAR_47_FAILURE_SENSOR_TAIL", - ["mm"] = 0, - }, -- end of ["AAR_47_FAILURE_SENSOR_TAIL"] - ["GUN_LEFT_OUT_OPEN_CIRCUIT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_LEFT_OUT_OPEN_CIRCUIT", - ["mm"] = 0, - }, -- end of ["GUN_LEFT_OUT_OPEN_CIRCUIT"] - ["TACAN_FAILURE_TRANSMITTER"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TACAN_FAILURE_TRANSMITTER", - ["mm"] = 0, - }, -- end of ["TACAN_FAILURE_TRANSMITTER"] - ["BOMBS_ARMING_BROKEN_WIRING_RIGHT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_ARMING_BROKEN_WIRING_RIGHT", - ["mm"] = 0, - }, -- end of ["BOMBS_ARMING_BROKEN_WIRING_RIGHT"] - ["l_gen"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "l_gen", - ["mm"] = 0, - }, -- end of ["l_gen"] - ["TURNIND_POINTER_VIBRATES"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TURNIND_POINTER_VIBRATES", - ["mm"] = 0, - }, -- end of ["TURNIND_POINTER_VIBRATES"] - ["GUN_LEFT_IN_BARREL_WORN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_LEFT_IN_BARREL_WORN", - ["mm"] = 0, - }, -- end of ["GUN_LEFT_IN_BARREL_WORN"] - ["r_gen"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "r_gen", - ["mm"] = 0, - }, -- end of ["r_gen"] - ["COOLANT_RADIATOR_SENSOR"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "COOLANT_RADIATOR_SENSOR", - ["mm"] = 0, - }, -- end of ["COOLANT_RADIATOR_SENSOR"] - ["AAR_47_FAILURE_SENSOR_RIGHT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AAR_47_FAILURE_SENSOR_RIGHT", - ["mm"] = 0, - }, -- end of ["AAR_47_FAILURE_SENSOR_RIGHT"] - ["ROCKETS_INTERVALOMETER_SEQ"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "ROCKETS_INTERVALOMETER_SEQ", - ["mm"] = 0, - }, -- end of ["ROCKETS_INTERVALOMETER_SEQ"] - ["hydro_common"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "hydro_common", - ["mm"] = 0, - }, -- end of ["hydro_common"] - ["SAR_2_95"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "SAR_2_95", - ["mm"] = 0, - }, -- end of ["SAR_2_95"] - ["SAR_2_101"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "SAR_2_101", - ["mm"] = 0, - }, -- end of ["SAR_2_101"] - ["BOOSTER_COIL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOOSTER_COIL", - ["mm"] = 0, - }, -- end of ["BOOSTER_COIL"] - ["BOMBS_DAMAGE_ELINKAGE_RIGHT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_DAMAGE_ELINKAGE_RIGHT", - ["mm"] = 0, - }, -- end of ["BOMBS_DAMAGE_ELINKAGE_RIGHT"] - ["FAULTY_ROCKET_RIGHT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "FAULTY_ROCKET_RIGHT", - ["mm"] = 0, - }, -- end of ["FAULTY_ROCKET_RIGHT"] - ["GUN_RIGHT_IN_OPEN_CIRCUIT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_RIGHT_IN_OPEN_CIRCUIT", - ["mm"] = 0, - }, -- end of ["GUN_RIGHT_IN_OPEN_CIRCUIT"] - ["COMPASS_ERRATIC_INDIACATON"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "COMPASS_ERRATIC_INDIACATON", - ["mm"] = 0, - }, -- end of ["COMPASS_ERRATIC_INDIACATON"] - ["OIL_DILUTION_SOLENOID"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "OIL_DILUTION_SOLENOID", - ["mm"] = 0, - }, -- end of ["OIL_DILUTION_SOLENOID"] - ["PUMP_SEPARATOR_CLOGGED"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "PUMP_SEPARATOR_CLOGGED", - ["mm"] = 0, - }, -- end of ["PUMP_SEPARATOR_CLOGGED"] - ["es_damage_MainGenerator"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "es_damage_MainGenerator", - ["mm"] = 0, - }, -- end of ["es_damage_MainGenerator"] - ["BOMBS_RUST_RIGHT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_RUST_RIGHT", - ["mm"] = 0, - }, -- end of ["BOMBS_RUST_RIGHT"] - ["CLOGGED_FUEL_STRAINER"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "CLOGGED_FUEL_STRAINER", - ["mm"] = 0, - }, -- end of ["CLOGGED_FUEL_STRAINER"] - ["BOMBS_DAMAGE_ELINKAGE_LEFT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_DAMAGE_ELINKAGE_LEFT", - ["mm"] = 0, - }, -- end of ["BOMBS_DAMAGE_ELINKAGE_LEFT"] - ["r_engine"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "r_engine", - ["mm"] = 0, - }, -- end of ["r_engine"] - ["TACH_BREAK_IN_INDICATOR"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TACH_BREAK_IN_INDICATOR", - ["mm"] = 0, - }, -- end of ["TACH_BREAK_IN_INDICATOR"] - ["A11_CLOCK_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "A11_CLOCK_FAILURE", - ["mm"] = 0, - }, -- end of ["A11_CLOCK_FAILURE"] - ["DOORS_TV_JAMMED"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "DOORS_TV_JAMMED", - ["mm"] = 0, - }, -- end of ["DOORS_TV_JAMMED"] - ["D2_RIGHT_CYLINDER"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "D2_RIGHT_CYLINDER", - ["mm"] = 0, - }, -- end of ["D2_RIGHT_CYLINDER"] - ["ecm"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "ecm", - ["mm"] = 0, - }, -- end of ["ecm"] - ["AN_ALR69V_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AN_ALR69V_FAILURE_TOTAL", - ["mm"] = 0, - }, -- end of ["AN_ALR69V_FAILURE_TOTAL"] - ["es_damage_GeneratorRight"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "es_damage_GeneratorRight", - ["mm"] = 0, - }, -- end of ["es_damage_GeneratorRight"] - ["SUPERCHARGER_SOLENOID"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "SUPERCHARGER_SOLENOID", - ["mm"] = 0, - }, -- end of ["SUPERCHARGER_SOLENOID"] - ["GUN_RIGHT_OUT_AMMUN_FAULT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "GUN_RIGHT_OUT_AMMUN_FAULT", - ["mm"] = 0, - }, -- end of ["GUN_RIGHT_OUT_AMMUN_FAULT"] - ["l_engine"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "l_engine", - ["mm"] = 0, - }, -- end of ["l_engine"] - ["AN_ALE_40V_FAILURE_CONTAINER_RIGHT_GEAR"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "AN_ALE_40V_FAILURE_CONTAINER_RIGHT_GEAR", - ["mm"] = 0, - }, -- end of ["AN_ALE_40V_FAILURE_CONTAINER_RIGHT_GEAR"] - ["TransitionalReductor_ShaveInOil"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TransitionalReductor_ShaveInOil", - ["mm"] = 0, - }, -- end of ["TransitionalReductor_ShaveInOil"] - ["COPILOT_KILLED_FAILURE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "COPILOT_KILLED_FAILURE", - ["mm"] = 0, - }, -- end of ["COPILOT_KILLED_FAILURE"] - ["TAIL_GEAR_D_LOCK"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TAIL_GEAR_D_LOCK", - ["mm"] = 0, - }, -- end of ["TAIL_GEAR_D_LOCK"] - ["JADRO_1A_FAILURE_TOTAL"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["mm"] = 0, - }, -- end of ["JADRO_1A_FAILURE_TOTAL"] - ["BOMBS_ARMING_NO_VOLATAGE_RIGHT"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "BOMBS_ARMING_NO_VOLATAGE_RIGHT", - ["mm"] = 0, - }, -- end of ["BOMBS_ARMING_NO_VOLATAGE_RIGHT"] - ["IFFCC_FAILURE_GUN"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "IFFCC_FAILURE_GUN", - ["mm"] = 0, - }, -- end of ["IFFCC_FAILURE_GUN"] - ["APU_Fire"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["mm"] = 0, - }, -- end of ["APU_Fire"] - ["TURNIND_INCORRECT_SENS_DEFECTIVE"] = - { - ["hh"] = 0, - ["prob"] = 100, - ["enable"] = false, - ["mmint"] = 1, - ["id"] = "TURNIND_INCORRECT_SENS_DEFECTIVE", - ["mm"] = 0, - }, -- end of ["TURNIND_INCORRECT_SENS_DEFECTIVE"] - }, -- end of ["failures"] -} -- end of mission diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/options b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/options deleted file mode 100644 index bf093522c..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/options +++ /dev/null @@ -1,229 +0,0 @@ -options = -{ - ["playerName"] = "Killer", - ["miscellaneous"] = - { - ["headmove"] = true, - ["TrackIR_external_views"] = true, - ["f5_nearest_ac"] = true, - ["f11_free_camera"] = true, - ["F2_view_effects"] = 2, - ["f10_awacs"] = true, - ["Coordinate_Display"] = "Lat Long", - ["accidental_failures"] = false, - ["force_feedback_enabled"] = true, - ["synchronize_controls"] = false, - ["show_pilot_body"] = true, - }, -- end of ["miscellaneous"] - ["difficulty"] = - { - ["padlock"] = true, - ["labels"] = false, - ["easyCommunication"] = true, - ["easyRadar"] = false, - ["fuel"] = false, - ["miniHUD"] = false, - ["tips"] = true, - ["birds"] = 0, - ["optionsView"] = "optview_all", - ["permitCrash"] = true, - ["immortal"] = false, - ["cockpitStatusBarAllowed"] = false, - ["cockpitVisualRM"] = true, - ["easyFlight"] = false, - ["reports"] = true, - ["hideStick"] = false, - ["radio"] = true, - ["externalViews"] = true, - ["units"] = "metric", - ["userMarks"] = true, - ["cockpitLanguage"] = "english", - ["spectatorExternalViews"] = true, - ["userSnapView"] = true, - ["avionicsLanguage"] = "native", - ["impostors"] = "medium", - ["iconsTheme"] = "nato", - ["map"] = true, - ["weapons"] = false, - ["setGlobal"] = true, - ["geffect"] = "realistic", - }, -- end of ["difficulty"] - ["VR"] = - { - ["use_mouse"] = false, - ["enable"] = true, - ["box_mouse_cursor"] = true, - ["pixel_density"] = 1, - }, -- end of ["VR"] - ["graphics"] = - { - ["OculusRift"] = false, - ["color"] = "32", - ["LensEffects"] = 3, - ["heatBlr"] = 2, - ["scenes"] = "high", - ["water"] = 2, - ["visibRange"] = "High", - ["treesVisibility"] = 25000, - ["aspect"] = 1.25, - ["lights"] = 2, - ["HDR"] = 1, - ["MSAA"] = 2, - ["civTraffic"] = "high", - ["clutterMaxDistance"] = 1500, - ["terrainTextures"] = "max", - ["multiMonitorSetup"] = "1camera", - ["shadowTree"] = true, - ["fullScreen"] = false, - ["disableAero"] = false, - ["DOF"] = 0, - ["clouds"] = 1, - ["flatTerrainShadows"] = 1, - ["cockpitShadows"] = true, - ["height"] = 1024, - ["width"] = 1280, - ["shadows"] = 4, - ["TranspSSAA"] = false, - ["sync"] = true, - ["preloadRadius"] = 73885, - ["anisotropy"] = 2, - ["haze"] = 1, - ["textures"] = 2, - ["effects"] = 3, - }, -- end of ["graphics"] - ["plugins"] = - { - ["CA"] = - { - ["kompass_options"] = 1, - ["ground_target_info"] = true, - ["ground_aim_helper"] = true, - ["ground_platform_shake"] = true, - ["ground_automatic"] = true, - }, -- end of ["CA"] - ["M-2000C"] = - { - ["UNI_NODRIFT"] = false, - ["TDC_"] = false, - ["CPLocalList"] = "default", - ["PPA_TOTPAR"] = false, - ["UNI_ALIGNED"] = false, - }, -- end of ["M-2000C"] - ["A-10C"] = - { - ["CPLocalList"] = "default", - }, -- end of ["A-10C"] - ["FC3"] = - { - ["CPLocalList_F-15C"] = "default", - ["CPLocalList_MiG-29S"] = "default", - ["CPLocalList_MiG-29A"] = "default", - ["CPLocalList_Su-25"] = "default", - ["CPLocalList_A-10A"] = "default", - ["CPLocalList_Su-27"] = "chinese", - ["CPLocalList_MiG-29G"] = "default", - ["CPLocalList_Su-33"] = "default", - }, -- end of ["FC3"] - ["Hawk"] = - { - ["CPLocalList"] = "high", - }, -- end of ["Hawk"] - ["P-51D"] = - { - ["assistance"] = 100, - ["CPLocalList"] = "default", - ["autoRudder"] = false, - }, -- end of ["P-51D"] - ["TF-51D"] = - { - ["assistance"] = 100, - ["CPLocalList"] = "default", - ["autoRudder"] = false, - }, -- end of ["TF-51D"] - ["MiG-21Bis"] = - { - ["Engine"] = false, - ["Shake"] = 100, - ["CustomCockpit"] = false, - ["Reticle"] = false, - ["Freeze"] = false, - }, -- end of ["MiG-21Bis"] - ["F-86F"] = - { - ["landSeatAdjustF86"] = true, - ["aiHelper"] = false, - ["CPLocalList"] = "default", - ["NoseWheelSteeringSimpleBehaviourF86"] = true, - ["gunCamera"] = 0, - }, -- end of ["F-86F"] - ["Su-25T"] = - { - ["CPLocalList"] = "default", - }, -- end of ["Su-25T"] - ["Mi-8MTV2"] = - { - ["Mi8TrimmingMethod"] = 0, - ["Mi8AutopilotAdjustment"] = false, - ["gunCamera"] = 0, - ["controlHelperMi8"] = false, - ["weapTooltipsMi8"] = true, - ["Mi8RudderTrimmer"] = false, - ["CPLocalList"] = "default", - ["altMi8TrimmingMethod"] = false, - ["Mi8FOV"] = 120, - }, -- end of ["Mi-8MTV2"] - ["MiG-15bis"] = - { - ["autoLeanToAimMiG15"] = true, - ["CPLocalList"] = "chinese", - ["gunCamera"] = 0, - ["aiHelper"] = false, - }, -- end of ["MiG-15bis"] - ["FW-190D9"] = - { - ["assistance"] = 100, - ["CPLocalList"] = "default", - ["autoRudder"] = false, - }, -- end of ["FW-190D9"] - ["UH-1H"] = - { - ["UHTrackIRAiming"] = true, - ["autoPilot"] = true, - ["UHTrimmingMethod"] = 0, - ["altUHTrimmingMethod"] = false, - ["CPLocalList"] = "default", - ["weapTooltips"] = true, - ["UHRudderTrimmer"] = false, - }, -- end of ["UH-1H"] - ["Ka-50"] = - { - ["altTrimmingMethod"] = false, - ["Ka50RudderTrimmer"] = false, - ["CPLocalList"] = "english", - }, -- end of ["Ka-50"] - }, -- end of ["plugins"] - ["format"] = 1, - ["sound"] = - { - ["hear_in_helmet"] = false, - ["headphones"] = 100, - ["cockpit"] = 100, - ["GBreathEffect"] = true, - ["gui"] = 100, - ["volume"] = 100, - ["radioSpeech"] = true, - ["music"] = 100, - ["subtitles"] = true, - ["world"] = 100, - }, -- end of ["sound"] - ["views"] = - { - ["cockpit"] = - { - ["mirrors"] = false, - ["reflections"] = false, - ["avionics"] = 3, - ["russianHud"] = false, - }, -- end of ["cockpit"] - }, -- end of ["views"] -} -- end of options diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/warehouses b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/warehouses deleted file mode 100644 index 52b43d127..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones/warehouses +++ /dev/null @@ -1,807 +0,0 @@ -warehouses = -{ - ["airports"] = - { - [12] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "RED", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [12] - [13] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "BLUE", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [13] - [14] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "RED", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [14] - [15] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "RED", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [15] - [16] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "BLUE", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [16] - [17] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [17] - [18] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [18] - [19] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [19] - [20] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [20] - [21] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [21] - [22] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [22] - [23] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [23] - [24] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [24] - [25] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [25] - [26] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [26] - [27] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [27] - [28] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [28] - [29] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [29] - [30] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [30] - [31] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [31] - [32] = - { - ["gasoline"] = - { - ["InitFuel"] = 100, - }, -- end of ["gasoline"] - ["unlimitedMunitions"] = true, - ["methanol_mixture"] = - { - ["InitFuel"] = 100, - }, -- end of ["methanol_mixture"] - ["OperatingLevel_Air"] = 10, - ["diesel"] = - { - ["InitFuel"] = 100, - }, -- end of ["diesel"] - ["speed"] = 16.666666, - ["size"] = 100, - ["periodicity"] = 30, - ["suppliers"] = - { - }, -- end of ["suppliers"] - ["coalition"] = "NEUTRAL", - ["jet_fuel"] = - { - ["InitFuel"] = 100, - }, -- end of ["jet_fuel"] - ["OperatingLevel_Eqp"] = 10, - ["unlimitedFuel"] = true, - ["aircrafts"] = - { - }, -- end of ["aircrafts"] - ["weapons"] = - { - }, -- end of ["weapons"] - ["OperatingLevel_Fuel"] = 10, - ["unlimitedAircrafts"] = true, - }, -- end of [32] - }, -- end of ["airports"] - ["warehouses"] = - { - }, -- end of ["warehouses"] -} -- end of warehouses diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.lua b/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.lua index 1b604a94f..31574814d 100644 --- a/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.lua +++ b/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.lua @@ -19,19 +19,19 @@ -- 2. Play with the InitCleanUp API of the SPAWN class, extende the amount of seconds to find the optimal setting. -- Define the SET of CLIENTs from the red coalition. This SET is filled during startup. -local RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) +RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) -- Define the SPAWN object for the red AI plane template. -- We use InitCleanUp to check every 20 seconds, if there are no planes blocked at the airbase, waithing for take-off. -- If a blocked plane exists, this red plane will be ReSpawned. -local RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) +RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) -- Start the AI_BALANCER, using the SET of red CLIENTs, and the SPAWN object as a parameter. -local RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) +RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) -- Create the first polygon zone ... -local PatrolZoneGroup1 = GROUP:FindByName( "PatrolZone1" ) -local PatrolZone1 = ZONE_POLYGON:New( "PatrolZone1", PatrolZoneGroup1 ) +PatrolZoneGroup1 = GROUP:FindByName( "PatrolZone1" ) +PatrolZone1 = ZONE_POLYGON:New( "PatrolZone1", PatrolZoneGroup1 ) function RU_AI_Balancer:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz b/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz index e52064780..fc05c592b 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.lua b/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.lua index a62960186..d6cc8a8f3 100644 --- a/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.lua +++ b/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.lua @@ -26,7 +26,7 @@ -- This zone array will be used in the AI_BALANCER to randomize the patrol -- zone that each spawned group will patrol -local RedPatrolZone = {} +RedPatrolZone = {} RedPatrolZone[1] = ZONE:New( "RedPatrolZone1" ) RedPatrolZone[2] = ZONE:New( "RedPatrolZone2" ) RedPatrolZone[3] = ZONE:New( "RedPatrolZone3" ) @@ -40,7 +40,7 @@ RedPatrolZone[6] = ZONE:New( "RedPatrolZone6" ) -- with replacing any CLIENT created aircraft in the mission that a human -- player does not take. -local RU_PlanesSpawn = {} +RU_PlanesSpawn = {} RU_PlanesSpawn[1] = SPAWN:New( "RU CAP Anapa AB" ):InitCleanUp( 45 ) RU_PlanesSpawn[2] = SPAWN:New( "RU CAP Beslan AB" ):InitCleanUp( 45 ) RU_PlanesSpawn[3] = SPAWN:New( "RU CAP Gelendzhik AB" ):InitCleanUp( 45 ) @@ -62,7 +62,7 @@ RU_PlanesSpawn[11] = SPAWN:New( "RU CAP Novorossiysk AB" ):InitCleanUp( 45 ) -- The code here does not need to be changed. Only an addition in the mission editor. An example -- of this can be found on the USA side at Sochi AB. -local RU_PlanesClientSet = {} +RU_PlanesClientSet = {} RU_PlanesClientSet[1] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Anapa AB") RU_PlanesClientSet[2] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Beslan AB") RU_PlanesClientSet[3] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Gelendzhik AB") @@ -84,7 +84,7 @@ RU_PlanesClientSet[11] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Novorossiysk -- of them are included to pick randomly. -local RU_AI_Balancer = {} +RU_AI_Balancer = {} for i=1, 11 do RU_AI_Balancer[i] = AI_BALANCER:New(RU_PlanesClientSet[i], RU_PlanesSpawn[i]) @@ -113,7 +113,7 @@ end -- leader. -- Create the Blue Patrol Zone Array -local BluePatrolZone = {} +BluePatrolZone = {} BluePatrolZone[1] = ZONE:New( "BluePatrolZone1") BluePatrolZone[2] = ZONE:New( "BluePatrolZone2") BluePatrolZone[3] = ZONE:New( "BluePatrolZone3") @@ -123,7 +123,7 @@ BluePatrolZone[6] = ZONE:New( "BluePatrolZone6") --United States CAP Aircraft (these are used as templates for AI) -local US_PlanesSpawn = {} +US_PlanesSpawn = {} US_PlanesSpawn[1] = SPAWN:New( "US CAP Batumi AB" ):InitCleanUp( 45 ) US_PlanesSpawn[2] = SPAWN:New( "US CAP Gudauta AB" ):InitCleanUp( 45 ) US_PlanesSpawn[3] = SPAWN:New( "US CAP Kobuleti AB" ):InitCleanUp( 45 ) @@ -137,7 +137,7 @@ US_PlanesSpawn[10] = SPAWN:New( "US CAP Tbilisi AB" ):InitCleanUp( 45 ) --United States Client Aircraft (via AI_BALANCER, AI will replace these if no human players are in the slot) -local US_PlanesClientSet = {} +US_PlanesClientSet = {} US_PlanesClientSet[1] = SET_CLIENT:New():FilterPrefixes("US CLIENT Batumi AB") US_PlanesClientSet[2] = SET_CLIENT:New():FilterPrefixes("US CLIENT Gudauta AB") US_PlanesClientSet[3] = SET_CLIENT:New():FilterPrefixes("US CLIENT Kobuleti AB") @@ -149,7 +149,7 @@ US_PlanesClientSet[8] = SET_CLIENT:New():FilterPrefixes("US CLIENT Sukhumi AB") US_PlanesClientSet[9] = SET_CLIENT:New():FilterPrefixes("US CLIENT Vaziani AB") US_PlanesClientSet[10] = SET_CLIENT:New():FilterPrefixes("US CLIENT Tbilisi AB") -local US_AI_Balancer = {} +US_AI_Balancer = {} for i=1, 10 do US_AI_Balancer[i] = AI_BALANCER:New( US_PlanesClientSet[i], US_PlanesSpawn[i] ) @@ -160,4 +160,4 @@ for i=1, 10 do Patrol:SetControllable( AIGroup ) Patrol:Start() end -end \ No newline at end of file +end diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz b/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz index 780623ab9..26dfe66bc 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.lua b/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.lua index 64d6475c5..12abd4eb4 100644 --- a/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.lua +++ b/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.lua @@ -16,7 +16,7 @@ local CapPlane = GROUP:FindByName( "Plane" ) local PatrolZone = ZONE:New( "Patrol Zone" ) -local AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) +AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) AICapZone:SetControllable( CapPlane ) diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz index a6938e562..2e7babc53 100644 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz and b/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.lua b/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.lua index 683395146..87189c8c5 100644 --- a/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.lua +++ b/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.lua @@ -15,11 +15,11 @@ -- 3. After engage, observe that the Su-27 returns to the PatrolZone. -- 4. If you want, you can wait until the Su-27 is out of fuel and will land. -local CapPlane = GROUP:FindByName( "Plane" ) +CapPlane = GROUP:FindByName( "Plane" ) -local PatrolZone = ZONE:New( "Patrol Zone" ) +PatrolZone = ZONE:New( "Patrol Zone" ) -local AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) +AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) AICapZone:SetControllable( CapPlane ) AICapZone:SetEngageRange( 20000 ) -- Set the Engage Range to 20.000 meters. The AI won't engage when the enemy is beyond 20.000 meters. diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz index b76e7b544..529550dde 100644 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz and b/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.lua b/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.lua index 5cf3b6e74..9c6d37415 100644 --- a/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.lua +++ b/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.lua @@ -16,15 +16,15 @@ -- 4. If you want, you can wait until the Su-27 is out of fuel and will land. -local CapPlane = GROUP:FindByName( "Plane" ) +CapPlane = GROUP:FindByName( "Plane" ) -local PatrolZone = ZONE:New( "Patrol Zone" ) +PatrolZone = ZONE:New( "Patrol Zone" ) -local AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) +AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) -local EngageZoneGroup = GROUP:FindByName( "Engage Zone" ) +EngageZoneGroup = GROUP:FindByName( "Engage Zone" ) -local CapEngageZone = ZONE_POLYGON:New( "Engage Zone", EngageZoneGroup ) +CapEngageZone = ZONE_POLYGON:New( "Engage Zone", EngageZoneGroup ) AICapZone:SetControllable( CapPlane ) AICapZone:SetEngageZone( CapEngageZone ) -- Set the Engage Zone. The AI will only engage when the bogeys are within the CapEngageZone. diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz index 7a313adc9..c70916318 100644 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz and b/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.lua b/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.lua index b4f432c76..3b0ad84bc 100644 --- a/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.lua +++ b/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.lua @@ -17,15 +17,15 @@ -- 4. When it engages, it will abort the engagement after 1 minute. -local CapPlane = GROUP:FindByName( "Plane" ) +CapPlane = GROUP:FindByName( "Plane" ) -local PatrolZone = ZONE:New( "Patrol Zone" ) +PatrolZone = ZONE:New( "Patrol Zone" ) -local AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) +AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) -local EngageZoneGroup = GROUP:FindByName( "Engage Zone" ) +EngageZoneGroup = GROUP:FindByName( "Engage Zone" ) -local CapEngageZone = ZONE_POLYGON:New( "Engage Zone", EngageZoneGroup ) +CapEngageZone = ZONE_POLYGON:New( "Engage Zone", EngageZoneGroup ) AICapZone:SetControllable( CapPlane ) AICapZone:SetEngageZone( CapEngageZone ) -- Set the Engage Zone. The AI will only engage when the bogeys are within the CapEngageZone. @@ -39,4 +39,5 @@ end function AICapZone:OnAfterAbort(Controllable,From,Event,To) BASE:E("MISSION ABORTED! Returning to Patrol Zone!") MESSAGE:New("MISSION ABORTED! Returning to Patrol Zone!",30,"ALERT!") -end \ No newline at end of file +end + diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.miz index ba01dffb1..e0888120c 100644 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.miz and b/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.miz differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.lua b/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.lua index 287760dcb..16f456f04 100644 --- a/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.lua +++ b/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.lua @@ -13,13 +13,13 @@ -- 2. It should return to base when out of fuel. -- -local CapSpawn = SPAWN:New( "Plane" ):InitLimit(1,2):InitRepeatOnLanding() +CapSpawn = SPAWN:New( "Plane" ):InitLimit(1,2):InitRepeatOnLanding() -local CapGroup = CapSpawn:Spawn() +CapGroup = CapSpawn:Spawn() -local PatrolZone = ZONE:New( "Patrol Zone" ) +PatrolZone = ZONE:New( "Patrol Zone" ) -local AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) +AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) AICapZone:SetControllable( CapGroup ) diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz index 2dc6e2986..af3d81f30 100644 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz and b/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz index f275834fd..c389dfc61 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.lua b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.lua index 42f82baf7..a9a0ad737 100644 --- a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.lua +++ b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.lua @@ -24,24 +24,24 @@ -- Create a local variable (in this case called CASEngagementZone) and -- using the ZONE function find the pre-defined zone called "Engagement Zone" -- currently on the map and assign it to this variable -local CASEngagementZone = ZONE:New( "Engagement Zone" ) +CASEngagementZone = ZONE:New( "Engagement Zone" ) -- Create a local variable (in this case called CASPlane) and -- using the GROUP function find the aircraft group called "Plane" and assign to this variable -local CASPlane = GROUP:FindByName( "Plane" ) +CASPlane = GROUP:FindByName( "Plane" ) -- Create a local Variable (in this cased called PatrolZone and -- using the ZONE function find the pre-defined zone called "Patrol Zone" and assign it to this variable -local PatrolZone = ZONE:New( "Patrol Zone" ) +PatrolZone = ZONE:New( "Patrol Zone" ) -- Create and object (in this case called AICasZone) and -- using the functions AI_CAS_ZONE assign the parameters that define this object -- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -local AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) +AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) -- Create an object (in this case called Targets) and -- using the GROUP function find the group labeled "Targets" and assign it to this object -local Targets = GROUP:FindByName("Targets") +Targets = GROUP:FindByName("Targets") -- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz index 3d9094a8a..6b4b491e2 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.lua b/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.lua index 230c3d414..fad0bea38 100644 --- a/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.lua +++ b/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.lua @@ -25,24 +25,24 @@ -- Create a local variable (in this case called CASEngagementZone) and -- using the ZONE function find the pre-defined zone called "Engagement Zone" -- currently on the map and assign it to this variable -local CASEngagementZone = ZONE:New( "Engagement Zone" ) +CASEngagementZone = ZONE:New( "Engagement Zone" ) -- Create a local variable (in this case called CASPlane) and -- using the GROUP function find the aircraft group called "Plane" and assign to this variable -local CASPlane = GROUP:FindByName( "Plane" ) +CASPlane = GROUP:FindByName( "Plane" ) -- Create a local Variable (in this cased called PatrolZone and -- using the ZONE function find the pre-defined zone called "Patrol Zone" and assign it to this variable -local PatrolZone = ZONE:New( "Patrol Zone" ) +PatrolZone = ZONE:New( "Patrol Zone" ) -- Create and object (in this case called AICasZone) and -- using the functions AI_CAS_ZONE assign the parameters that define this object -- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -local AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) +AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) -- Create an object (in this case called Targets) and -- using the GROUP function find the group labeled "Targets" and assign it to this object -local Targets = GROUP:FindByName("Targets") +Targets = GROUP:FindByName("Targets") -- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz b/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz index 9a992e009..d3401bf74 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.lua b/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.lua index 42125a031..8af2ca77e 100644 --- a/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.lua +++ b/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.lua @@ -26,24 +26,24 @@ -- Create a local variable (in this case called CASEngagementZone) and -- using the ZONE function find the pre-defined zone called "Engagement Zone" -- currently on the map and assign it to this variable -local CASEngagementZone = ZONE:New( "Engagement Zone" ) +CASEngagementZone = ZONE:New( "Engagement Zone" ) -- Create a local variable (in this case called CASPlane) and -- using the GROUP function find the aircraft group called "Plane" and assign to this variable -local CASPlane = GROUP:FindByName( "Plane" ) +CASPlane = GROUP:FindByName( "Plane" ) -- Create a local Variable (in this cased called PatrolZone and -- using the ZONE function find the pre-defined zone called "Patrol Zone" and assign it to this variable -local PatrolZone = ZONE:New( "Patrol Zone" ) +PatrolZone = ZONE:New( "Patrol Zone" ) -- Create and object (in this case called AICasZone) and -- using the functions AI_CAS_ZONE assign the parameters that define this object -- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -local AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) +AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) -- Create an object (in this case called Targets) and -- using the GROUP function find the group labeled "Targets" and assign it to this object -local Targets = GROUP:FindByName("Targets") +Targets = GROUP:FindByName("Targets") -- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz b/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz index 278dfbbb9..0f412ea66 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.lua b/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.lua index 052efd4ab..295b7d11d 100644 --- a/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.lua +++ b/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.lua @@ -24,24 +24,24 @@ -- Create a local variable (in this case called CASEngagementZone) and -- using the ZONE function find the pre-defined zone called "Engagement Zone" -- currently on the map and assign it to this variable -local CASEngagementZone = ZONE:New( "Engagement Zone" ) +CASEngagementZone = ZONE:New( "Engagement Zone" ) -- Create a local variable (in this case called CASPlane) and -- using the GROUP function find the aircraft group called "Plane" and assign to this variable -local CASPlane = GROUP:FindByName( "Plane" ) +CASPlane = GROUP:FindByName( "Plane" ) -- Create a local Variable (in this cased called PatrolZone and -- using the ZONE function find the pre-defined zone called "Patrol Zone" and assign it to this variable -local PatrolZone = ZONE:New( "Patrol Zone" ) +PatrolZone = ZONE:New( "Patrol Zone" ) -- Create and object (in this case called AICasZone) and -- using the functions AI_CAS_ZONE assign the parameters that define this object -- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -local AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) +AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) -- Create an object (in this case called Targets) and -- using the GROUP function find the group labeled "Targets" and assign it to this object -local Targets = GROUP:FindByName("Targets") +Targets = GROUP:FindByName("Targets") -- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.miz b/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.miz index 9582c7911..560fb7e27 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend.lua b/Moose Test Missions/CAS - Close Air Support/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend.lua new file mode 100644 index 000000000..203eed00a --- /dev/null +++ b/Moose Test Missions/CAS - Close Air Support/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend.lua @@ -0,0 +1,78 @@ +--- +-- Name: CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend +-- Author: FlightControl +-- Date Created: 18 Mar 2017 +-- +-- # Situation: +-- +-- A group of 4 Su-25T at patrolling north of an engage zone for 1 minute. +-- After 1 minute, the command center orders the Su-25T to engage the zone and execute a CAS. +-- The planes should expend 4 weapons per run. +-- +-- # Test cases: +-- +-- 1. Observe that the Su-25T is patrolling in the patrol zone, until the engage command is given. +-- 2. The Su-25T are not detecting any target during the patrol. +-- 3. When the Su-25T is commanded to engage, the group will fly to the engage zone +-- 3.1. The approach speed to the engage zone is set to 400 km/h. +-- 3.2. The altitude to the engage zone and CAS execution is set to 500 meters. +-- 3.3. The planes should expend 4 weapons per run. +-- 4. Detection is activated and detected targets within the engage zone are assigned for CAS. +-- 5. Observe the Su-25T eliminating the targets. +-- 6. Observe the Su-25T defenses. +-- 7. When all targets within the engage zone are destroyed, the Su-25T CAS task is set to Accomplished. +-- 8. The Su-25T will return to base. + + + +-- Create a local variable (in this case called CASEngagementZone) and +-- using the ZONE function find the pre-defined zone called "Engagement Zone" +-- currently on the map and assign it to this variable +CASEngagementZone = ZONE:New( "Engagement Zone" ) + +-- Create a local variable (in this case called CASPlane) and +-- using the GROUP function find the aircraft group called "Plane" and assign to this variable +CASPlane = GROUP:FindByName( "Plane" ) + +-- Create a local Variable (in this cased called PatrolZone and +-- using the ZONE function find the pre-defined zone called "Patrol Zone" and assign it to this variable +PatrolZone = ZONE:New( "Patrol Zone" ) + +-- Create and object (in this case called AICasZone) and +-- using the functions AI_CAS_ZONE assign the parameters that define this object +-- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) +AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) + +-- Create an object (in this case called Targets) and +-- using the GROUP function find the group labeled "Targets" and assign it to this object +Targets = GROUP:FindByName("Targets") + + +-- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function +AICasZone:SetControllable( CASPlane ) + +-- Tell the group CASPlane to start the mission in 1 second. +AICasZone:__Start( 1 ) -- They should statup, and start patrolling in the PatrolZone. + +-- After 1 minute, tell the group CASPlane to engage the targets located in the engagement zone called CASEngagement Zone. (600 is 600 seconds) +AICasZone:__Engage( 60, 600, 8000, AI.Task.WeaponExpend.FOUR ) -- Engage with a speed of 600 km/h and an altitude of 8000 meters, weapn expend 4. + +-- Check every 60 seconds whether the Targets have been eliminated. +-- When the trigger completed has been fired, the Plane will go back to the Patrol Zone. +Check, CheckScheduleID = SCHEDULER:New(nil, + function() + if Targets:IsAlive() and Targets:GetSize() > 5 then + BASE:E( "Test Mission: " .. Targets:GetSize() .. " targets left to be destroyed.") + else + BASE:E( "Test Mission: The required targets are destroyed." ) + AICasZone:__Accomplish( 1 ) -- Now they should fly back to teh patrolzone and patrol. + end + end, {}, 20, 60, 0.2 ) + + +-- When the targets in the zone are destroyed, (see scheduled function), the planes will return home ... +function AICasZone:OnAfterAccomplish( Controllable, From, Event, To ) + BASE:E( "Test Mission: Sending the Su-25T back to base." ) + Check:Stop( CheckScheduleID ) + AICasZone:__RTB( 1 ) +end diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend.miz b/Moose Test Missions/CAS - Close Air Support/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend.miz new file mode 100644 index 000000000..acf7bbe90 Binary files /dev/null and b/Moose Test Missions/CAS - Close Air Support/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.lua b/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.lua index 72fb90bb8..42f76f38f 100644 --- a/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.lua +++ b/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.lua @@ -26,24 +26,24 @@ -- Create a local variable (in this case called CASEngagementZone) and -- using the ZONE function find the pre-defined zone called "Engagement Zone" -- currently on the map and assign it to this variable -local CASEngagementZone = ZONE:New( "Engagement Zone" ) +CASEngagementZone = ZONE:New( "Engagement Zone" ) -- Create a local variable (in this case called CASPlane) and -- using the GROUP function find the aircraft group called "Plane" and assign to this variable -local CASPlane = GROUP:FindByName( "Helicopter" ) +CASPlane = GROUP:FindByName( "Helicopter" ) -- Create a local Variable (in this cased called PatrolZone and -- using the ZONE function find the pre-defined zone called "Patrol Zone" and assign it to this variable -local PatrolZone = ZONE:New( "Patrol Zone" ) +PatrolZone = ZONE:New( "Patrol Zone" ) -- Create and object (in this case called AICasZone) and -- using the functions AI_CAS_ZONE assign the parameters that define this object -- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -local AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) +AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) -- Create an object (in this case called Targets) and -- using the GROUP function find the group labeled "Targets" and assign it to this object -local Targets = GROUP:FindByName("Targets") +Targets = GROUP:FindByName("Targets") -- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz b/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz index e48dbac86..18f37c504 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.lua b/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.lua index f922107fb..dbf2bcb05 100644 --- a/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.lua +++ b/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.lua @@ -25,24 +25,24 @@ -- Create a local variable (in this case called CASEngagementZone) and -- using the ZONE function find the pre-defined zone called "Engagement Zone" -- currently on the map and assign it to this variable -local CASEngagementZone = ZONE:New( "Engagement Zone" ) +CASEngagementZone = ZONE:New( "Engagement Zone" ) -- Create a local variable (in this case called CASPlane) and -- using the GROUP function find the aircraft group called "Plane" and assign to this variable -local CASPlane = GROUP:FindByName( "Helicopter" ) +CASPlane = GROUP:FindByName( "Helicopter" ) -- Create a local Variable (in this cased called PatrolZone and -- using the ZONE function find the pre-defined zone called "Patrol Zone" and assign it to this variable -local PatrolZone = ZONE:New( "Patrol Zone" ) +PatrolZone = ZONE:New( "Patrol Zone" ) -- Create and object (in this case called AICasZone) and -- using the functions AI_CAS_ZONE assign the parameters that define this object -- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -local AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) +AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) -- Create an object (in this case called Targets) and -- using the GROUP function find the group labeled "Targets" and assign it to this object -local Targets = GROUP:FindByName("Targets") +Targets = GROUP:FindByName("Targets") -- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz b/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz index ba7638ba4..d0bda8a32 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.lua b/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.lua index ae3aecf3c..05d2beba9 100644 --- a/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.lua +++ b/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.lua @@ -28,26 +28,26 @@ -- Create a local variable (in this case called CASEngagementZone) and -- using the ZONE function find the pre-defined zone called "Engagement Zone" -- currently on the map and assign it to this variable -local CASEngagementZone = ZONE:New( "Engagement Zone" ) +CASEngagementZone = ZONE:New( "Engagement Zone" ) -- Create a local variables (in this case called CASPlane and CASHelicopters) and -- using the GROUP function find the aircraft group called "Plane" and "Helicopter" and assign to these variables -local CASPlane = GROUP:FindByName( "Plane" ) -local CASHelicopter = GROUP:FindByName( "Helicopter" ) +CASPlane = GROUP:FindByName( "Plane" ) +CASHelicopter = GROUP:FindByName( "Helicopter" ) -- Create two patrol zones, one for the Planes and one for the Helicopters. -local PatrolZonePlanes = ZONE:New( "Patrol Zone Planes" ) -local PatrolZoneHelicopters = ZONE:New( "Patrol Zone Helicopters" ) +PatrolZonePlanes = ZONE:New( "Patrol Zone Planes" ) +PatrolZoneHelicopters = ZONE:New( "Patrol Zone Helicopters" ) -- Create and object (in this case called AICasZone) and -- using the functions AI_CAS_ZONE assign the parameters that define this object -- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -local AICasZonePlanes = AI_CAS_ZONE:New( PatrolZonePlanes, 400, 500, 500, 2500, CASEngagementZone ) -local AICasZoneHelicopters = AI_CAS_ZONE:New( PatrolZoneHelicopters, 100, 250, 300, 1000, CASEngagementZone ) +AICasZonePlanes = AI_CAS_ZONE:New( PatrolZonePlanes, 400, 500, 500, 2500, CASEngagementZone ) +AICasZoneHelicopters = AI_CAS_ZONE:New( PatrolZoneHelicopters, 100, 250, 300, 1000, CASEngagementZone ) -- Create an object (in this case called Targets) and -- using the GROUP function find the group labeled "Targets" and assign it to this object -local Targets = GROUP:FindByName("Targets") +Targets = GROUP:FindByName("Targets") -- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz b/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz index 01991c8b1..490b66e68 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.lua b/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.lua index b4a1e62f4..4df5cc8d0 100644 --- a/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.lua +++ b/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.lua @@ -1,8 +1,8 @@ -local CargoEngineer = UNIT:FindByName( "Engineer" ) -local InfantryCargo = AI_CARGO_UNIT:New( CargoEngineer, "Engineer", "Engineer Sven", "81", 2000, 25 ) +CargoEngineer = UNIT:FindByName( "Engineer" ) +InfantryCargo = AI_CARGO_UNIT:New( CargoEngineer, "Engineer", "Engineer Sven", "81", 2000, 25 ) -local CargoCarrier = UNIT:FindByName( "Carrier" ) +CargoCarrier = UNIT:FindByName( "Carrier" ) -- This call will make the Cargo run to the CargoCarrier. -- Upon arrival at the CargoCarrier, the Cargo will be Loaded into the Carrier. diff --git a/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz b/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz index 1e56b34c8..6d8d448e4 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.lua b/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.lua index ff8716a23..bbc6edc58 100644 --- a/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.lua +++ b/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.lua @@ -1,8 +1,8 @@ -local CargoEngineer = UNIT:FindByName( "Engineer" ) -local InfantryCargo = AI_CARGO_UNIT:New( CargoEngineer, "Engineer", "Engineer Sven", "81", 2000, 25 ) +CargoEngineer = UNIT:FindByName( "Engineer" ) +InfantryCargo = AI_CARGO_UNIT:New( CargoEngineer, "Engineer", "Engineer Sven", "81", 2000, 25 ) -local CargoCarrier = UNIT:FindByName( "Carrier" ) +CargoCarrier = UNIT:FindByName( "Carrier" ) -- This will Load immediately the Cargo into the Carrier, regardless where the Cargo is. InfantryCargo:Load( CargoCarrier ) diff --git a/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz b/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz index 57f5b0f19..11838ec94 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.lua b/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.lua index 68d74a3dc..6cf1b1f69 100644 --- a/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.lua +++ b/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.lua @@ -1,10 +1,10 @@ -local CargoEngineer = UNIT:FindByName( "Engineer" ) -local InfantryCargo = AI_CARGO_UNIT:New( CargoEngineer, "Engineer", "Engineer Sven", "81", 2000, 25 ) +CargoEngineer = UNIT:FindByName( "Engineer" ) +InfantryCargo = AI_CARGO_UNIT:New( CargoEngineer, "Engineer", "Engineer Sven", "81", 2000, 25 ) -local CargoCarrierFrom = UNIT:FindByName( "CarrierFrom" ) +CargoCarrierFrom = UNIT:FindByName( "CarrierFrom" ) -local CargoCarrierTo = UNIT:FindByName( "CarrierTo" ) +CargoCarrierTo = UNIT:FindByName( "CarrierTo" ) -- This call will make the Cargo run to the CargoCarrier. -- Upon arrival at the CargoCarrier, the Cargo will be Loaded into the Carrier. diff --git a/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz b/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz index 95d225922..ad54736e9 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz and b/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.lua b/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.lua index 355680ed9..ddc9b940a 100644 --- a/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.lua +++ b/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.lua @@ -1,13 +1,13 @@ -local CargoSet = SET_BASE:New() +CargoSet = SET_BASE:New() CargoSet:Add( "Engineer1", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer1" ), "Engineers", "Engineer", 81, 2000, 25 ) ) CargoSet:Add( "Engineer2", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer2" ), "Engineers", "Engineer", 64, 2000, 25 ) ) CargoSet:Add( "Engineer3", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer3" ), "Engineers", "Engineer", 72, 2000, 25 ) ) CargoSet:Add( "Engineer4", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer4" ), "Engineers", "Engineer", 69, 2000, 25 ) ) -local InfantryCargo = AI_CARGO_GROUPED:New( CargoSet, "Engineers", "Engineers", 2000, 25 ) +InfantryCargo = AI_CARGO_GROUPED:New( CargoSet, "Engineers", "Engineers", 2000, 25 ) -local CargoCarrier = UNIT:FindByName( "Carrier" ) +CargoCarrier = UNIT:FindByName( "Carrier" ) -- This call will make the Cargo run to the CargoCarrier. -- Upon arrival at the CargoCarrier, the Cargo will be Loaded into the Carrier. diff --git a/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz b/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz index 11f36b9a3..8733e3718 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.lua b/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.lua index 26693a557..d8f345422 100644 --- a/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.lua +++ b/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.lua @@ -1,13 +1,13 @@ -local CargoSet = SET_BASE:New() +CargoSet = SET_BASE:New() CargoSet:Add( "Engineer1", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer1" ), "Engineers", "Engineer", 81, 2000, 25 ) ) CargoSet:Add( "Engineer2", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer2" ), "Engineers", "Engineer", 64, 2000, 25 ) ) CargoSet:Add( "Engineer3", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer3" ), "Engineers", "Engineer", 72, 2000, 25 ) ) CargoSet:Add( "Engineer4", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer4" ), "Engineers", "Engineer", 69, 2000, 25 ) ) -local InfantryCargo = AI_CARGO_GROUPED:New( CargoSet, "Engineers", "Engineers", 2000, 25 ) +InfantryCargo = AI_CARGO_GROUPED:New( CargoSet, "Engineers", "Engineers", 2000, 25 ) -local CargoCarrier = UNIT:FindByName( "Carrier" ) +CargoCarrier = UNIT:FindByName( "Carrier" ) -- This will Load immediately the Cargo into the Carrier, regardless where the Cargo is. InfantryCargo:Load( CargoCarrier ) diff --git a/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz b/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz index 17676e2c4..4a238d033 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.lua b/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.lua index 4b1d85f18..49411831f 100644 --- a/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.lua +++ b/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.lua @@ -1,15 +1,15 @@ -local CargoSet = SET_BASE:New() +CargoSet = SET_BASE:New() CargoSet:Add( "Engineer1", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer1" ), "Engineers", "Engineer", 81, 2000, 25 ) ) CargoSet:Add( "Engineer2", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer2" ), "Engineers", "Engineer", 64, 2000, 25 ) ) CargoSet:Add( "Engineer3", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer3" ), "Engineers", "Engineer", 72, 2000, 25 ) ) CargoSet:Add( "Engineer4", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer4" ), "Engineers", "Engineer", 69, 2000, 25 ) ) -local InfantryCargo = AI_CARGO_GROUPED:New( CargoSet, "Engineers", "Engineers", 2000, 25 ) +InfantryCargo = AI_CARGO_GROUPED:New( CargoSet, "Engineers", "Engineers", 2000, 25 ) -local CargoCarrierFrom = UNIT:FindByName( "CarrierFrom" ) +CargoCarrierFrom = UNIT:FindByName( "CarrierFrom" ) -local CargoCarrierTo = UNIT:FindByName( "CarrierTo" ) +CargoCarrierTo = UNIT:FindByName( "CarrierTo" ) -- This call will make the Cargo run to the CargoCarrier. -- Upon arrival at the CargoCarrier, the Cargo will be Loaded into the Carrier. diff --git a/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz b/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz index e51b1b35a..748ead674 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz and b/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.lua b/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.lua index 91f6b537f..c26fa24fa 100644 --- a/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.lua +++ b/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.lua @@ -1,8 +1,8 @@ -local DeliveryUnit = UNIT:FindByName( "Delivery" ) -local Letter = AI_CARGO_PACKAGE:New( DeliveryUnit, "Letter", "Secret Orders", "0.3", 2000, 25 ) +DeliveryUnit = UNIT:FindByName( "Delivery" ) +Letter = AI_CARGO_PACKAGE:New( DeliveryUnit, "Letter", "Secret Orders", "0.3", 2000, 25 ) -local CargoCarrier = UNIT:FindByName( "Carrier" ) +CargoCarrier = UNIT:FindByName( "Carrier" ) -- This call will make the Cargo run to the CargoCarrier. -- Upon arrival at the CargoCarrier, the Cargo will be Loaded into the Carrier. diff --git a/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz b/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz index bd3ea746e..764062734 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.lua b/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.lua index a611c8ad6..716b6186c 100644 --- a/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.lua +++ b/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.lua @@ -1,8 +1,8 @@ -local CargoEngineer = UNIT:FindByName( "Engineer" ) -local InfantryCargo = AI_CARGO_UNIT:New( CargoEngineer, "Engineer", "Engineer Sven", "81", 2000, 25 ) +CargoEngineer = UNIT:FindByName( "Engineer" ) +InfantryCargo = AI_CARGO_UNIT:New( CargoEngineer, "Engineer", "Engineer Sven", "81", 2000, 25 ) -local CargoCarrier = UNIT:FindByName( "Carrier" ) +CargoCarrier = UNIT:FindByName( "Carrier" ) -- This will Load the Cargo into the Carrier, regardless where the Cargo is. InfantryCargo:Load( CargoCarrier ) diff --git a/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz b/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz index cc3066d43..478ad554d 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.lua b/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.lua index be56c16f9..7afca5c49 100644 --- a/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.lua +++ b/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.lua @@ -17,7 +17,8 @@ -- 4. The truck driving from the one group to the other, will leave the first area, and will join the second. -- 5. While driving in between the areas, it will have a separate area. -local FACSetGroup = SET_GROUP:New():FilterPrefixes( "FAC Group" ):FilterStart() +FACSetGroup = SET_GROUP:New():FilterPrefixes( "FAC Group" ):FilterStart() -local FACDetection = DETECTION_AREAS:New( FACSetGroup, 1000, 250 ):FlareDetectedZones():SmokeDetectedUnits() +FACDetection = DETECTION_AREAS:New( FACSetGroup, 150, 250 ):BoundDetectedZones():SmokeDetectedUnits() +FACDetection:__Start( 5 ) \ No newline at end of file diff --git a/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz b/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz index b4af1d6a3..c05ea2ea9 100644 Binary files a/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz and b/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.lua b/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.lua new file mode 100644 index 000000000..7fcc33cc6 --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.lua @@ -0,0 +1,56 @@ +--- +-- Name: DET-100 - Detection Probability Distance +-- Author: FlightControl +-- Date Created: 04 Feb 2017 +-- +-- # Situation: +-- +-- Demonstrates the DistanceProbability factor during the detection of units. +-- +-- Two JTAC are detecting 4 units, which are 10 km away. +-- The first JTAC has no DistanceProbability set. +-- The second JTAC has a DistanceProbability set. +-- +-- # Test cases: +-- +-- 1. Observe the reporting of both the first and second JTAC. The second should report slower the detection than the first. +-- 2. Eventually all units should be detected by both JTAC. + +RecceSetGroup1 = SET_GROUP:New():FilterPrefixes( "Recce 1" ):FilterStart() +RecceSetGroup2 = SET_GROUP:New():FilterPrefixes( "Recce 2" ):FilterStart() + +HQ = GROUP:FindByName( "HQ" ) + +CC = COMMANDCENTER:New( HQ, "HQ" ) + +RecceDetection1 = DETECTION_UNITS:New( RecceSetGroup1 ) + +RecceDetection2 = DETECTION_UNITS:New( RecceSetGroup2 ) +RecceDetection2:SetDistanceProbability( 0.2 ) -- Set a 20% probability that a vehicle can be detected at 4km distance. + +RecceDetection1:Start() +RecceDetection2:Start() + +--- OnAfter Transition Handler for Event Detect. +-- @param Functional.Detection#DETECTION_UNITS self +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function RecceDetection1:OnAfterDetect(From,Event,To) + + local DetectionReport = RecceDetection1:DetectedReportDetailed() + + HQ:MessageToAll( DetectionReport, 15, "Detection 1 - No distance Probability" ) +end + +--- OnAfter Transition Handler for Event Detect. +-- @param Functional.Detection#DETECTION_UNITS self +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function RecceDetection2:OnAfterDetect(From,Event,To) + + local DetectionReport = RecceDetection2:DetectedReportDetailed() + + HQ:MessageToAll( DetectionReport, 15, "Detection 2 - Distance Probability" ) +end diff --git a/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.miz b/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.miz new file mode 100644 index 000000000..067efcd42 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.lua b/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.lua index 35cac114a..a09bef36d 100644 --- a/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.lua +++ b/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.lua @@ -1,9 +1,9 @@ -local FACSetGroup = SET_GROUP:New():FilterPrefixes( "FAC Group" ):FilterStart() +FACSetGroup = SET_GROUP:New():FilterPrefixes( "FAC Group" ):FilterStart() -local FACDetection = DETECTION_AREAS:New( FACSetGroup, 1000, 250 ) -local SeadClientSet = SET_CLIENT:New():FilterCoalitions( "blue" ):FilterStart() -local DestroyClientSet = SET_CLIENT:New():FilterCoalitions( "blue" ):FilterStart() +FACDetection = DETECTION_AREAS:New( FACSetGroup, 1000, 250 ) +SeadClientSet = SET_CLIENT:New():FilterCoalitions( "blue" ):FilterStart() +DestroyClientSet = SET_CLIENT:New():FilterCoalitions( "blue" ):FilterStart() -local FACReporting = FAC_REPORTING:New( FACClientSet, FACDetection ) +FACReporting = FAC_REPORTING:New( FACClientSet, FACDetection ) diff --git a/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz b/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz index 5790dec96..58d3f0069 100644 Binary files a/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz and b/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-100 - Detection Probability Distance.miz b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-100 - Detection Probability Distance.miz new file mode 100644 index 000000000..262bc3681 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-100 - Detection Probability Distance.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.lua b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.lua new file mode 100644 index 000000000..6659d2c08 --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.lua @@ -0,0 +1,62 @@ +--- +-- Name: DET-120 - Detection Probability Zones +-- Author: FlightControl +-- Date Created: 04 Feb 2017 +-- +-- # Situation: +-- +-- Demonstrates the DistanceProbability factor during the detection of units. +-- +-- Two JTAC are detecting 4 units, which are 10 km away. +-- The first JTAC has no DistanceProbability set. +-- The second JTAC has a DistanceProbability set. +-- +-- # Test cases: +-- +-- 1. Observe the reporting of both the first and second JTAC. The second should report slower the detection than the first. +-- 2. Eventually all units should be detected by both JTAC. + +RecceSetGroup1 = SET_GROUP:New():FilterPrefixes( "Recce 1" ):FilterStart() +RecceSetGroup2 = SET_GROUP:New():FilterPrefixes( "Recce 2" ):FilterStart() + +HQ = GROUP:FindByName( "HQ" ) + +CC = COMMANDCENTER:New( HQ, "HQ" ) + +RecceDetection1 = DETECTION_UNITS:New( RecceSetGroup1 ) + +RecceDetection2 = DETECTION_UNITS:New( RecceSetGroup2 ) + +ForestZone = ZONE_POLYGON:New( "ForestZone", GROUP:FindByName( "ForestZone" ) ) + +RecceDetection2:SetZoneProbability( { { ForestZone, 0.1 } } ) -- Set a 10% probability that a vehicle can be detected within the forest. + + +RecceDetection1:Start() +RecceDetection2:Start() + +--- OnAfter Transition Handler for Event Detect. +-- @param Functional.Detection#DETECTION_UNITS self +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function RecceDetection1:OnAfterDetect(From,Event,To) + + local DetectionReport = self:DetectedReportDetailed() + + HQ:MessageToAll( DetectionReport, 15, "Detection 1 - No Zone Probability" ) +end + +--- OnAfter Transition Handler for Event Detect. +-- @param Functional.Detection#DETECTION_UNITS self +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function RecceDetection2:OnAfterDetect(From,Event,To) + + local DetectionReport = self:DetectedReportDetailed() + + HQ:MessageToAll( DetectionReport, 15, "Detection 2 - Forest Zone Probability" ) +end + +garbagecollect() diff --git a/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.miz b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.miz new file mode 100644 index 000000000..302fa29fc Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.lua b/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.lua new file mode 100644 index 000000000..00fd71b74 --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.lua @@ -0,0 +1,42 @@ +--- +-- Name: DET-200 - Detection UNITS +-- Author: FlightControl +-- Date Created: 13 Feb 2017 +-- +-- # Situation: +-- +-- Demonstrates the detection of units. +-- +-- A Set of Recce are detecting a large group of units, which are 5 km away. +-- Select one of the blue Recce, and press F7. Watch the reporting of the detection evolve. +-- The enemy is approaching. +-- +-- # Test cases: +-- +-- 1. Observe the detection reporting of both the Recce. +-- 2. Eventually all units should be detected by both Recce. + +RecceSetGroup = SET_GROUP:New():FilterPrefixes( "Recce" ):FilterStart() + +HQ = GROUP:FindByName( "HQ" ) + +CC = COMMANDCENTER:New( HQ, "HQ" ) + +RecceDetection = DETECTION_UNITS:New( RecceSetGroup ) + +RecceDetection:Start() + +--- OnAfter Transition Handler for Event Detect. +-- @param Functional.Detection#DETECTION_UNITS self +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function RecceDetection:OnAfterDetect(From,Event,To) + + self:E("Detect") + + local DetectionReport = RecceDetection:DetectedReportDetailed() + + CC:MessageToAll( DetectionReport, 15, "" ) +end + diff --git a/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.miz b/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.miz new file mode 100644 index 000000000..593b2bc82 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.lua b/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.lua new file mode 100644 index 000000000..e39217d20 --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.lua @@ -0,0 +1,42 @@ +--- +-- Name: DET-210 - Detection TYPES +-- Author: FlightControl +-- Date Created: 13 Feb 2017 +-- +-- # Situation: +-- +-- Demonstrates the detection of units. +-- +-- A Set of Recce are detecting a large group of units, which are 5 km away. +-- Select one of the blue Recce, and press F7. Watch the reporting of the detection evolve. +-- The enemy is approaching. +-- +-- The blue Recce will report the detected units grouped per vehicle type! +-- +-- # Test cases: +-- +-- 1. Observe the detection reporting of both the Recce. +-- 2. Eventually all units should be detected by both Recce. + +RecceSetGroup = SET_GROUP:New():FilterPrefixes( "Recce" ):FilterStart() + +HQ = GROUP:FindByName( "HQ" ) + +CC = COMMANDCENTER:New( HQ, "HQ" ) + +RecceDetection = DETECTION_TYPES:New( RecceSetGroup ) + +RecceDetection:Start() + +--- OnAfter Transition Handler for Event Detect. +-- @param Functional.Detection#DETECTION_UNITS self +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function RecceDetection:OnAfterDetect(From,Event,To) + + local DetectionReport = RecceDetection:DetectedReportDetailed() + + CC:MessageToAll( DetectionReport, 15, "" ) +end + diff --git a/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.miz b/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.miz new file mode 100644 index 000000000..01f7dfdc9 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.lua b/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.lua new file mode 100644 index 000000000..34cf755e6 --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.lua @@ -0,0 +1,58 @@ +--- +-- Name: DET-100 - Detection Probability Distance +-- Author: FlightControl +-- Date Created: 04 Feb 2017 +-- +-- # Situation: +-- +-- Demonstrates the DistanceProbability factor during the detection of units. +-- +-- Two JTAC are detecting 4 units, which are 10 km away. +-- The first JTAC has no DistanceProbability set. +-- The second JTAC has a DistanceProbability set. +-- +-- # Test cases: +-- +-- 1. Observe the reporting of both the first and second JTAC. The second should report slower the detection than the first. +-- 2. Eventually all units should be detected by both JTAC. + +RecceSetGroup1 = SET_GROUP:New():FilterPrefixes( "Recce 1" ):FilterStart() +RecceSetGroup2 = SET_GROUP:New():FilterPrefixes( "Recce 2" ):FilterStart() + +HQ = GROUP:FindByName( "HQ" ) + +CC = COMMANDCENTER:New( HQ, "HQ" ) + +RecceDetection1 = DETECTION_AREAS:New( RecceSetGroup1, 1000 ) +RecceDetection1:BoundDetectedZones() + +RecceDetection2 = DETECTION_AREAS:New( RecceSetGroup2, 1000 ) +RecceDetection2:SetDistanceProbability( 0.2 ) -- Set a 20% probability that a vehicle can be detected at 4km distance. +RecceDetection1:BoundDetectedZones() + +RecceDetection1:Start() +RecceDetection2:Start() + +--- OnAfter Transition Handler for Event Detect. +-- @param Functional.Detection#DETECTION_UNITS self +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function RecceDetection1:OnAfterDetect(From,Event,To) + + local DetectionReport = RecceDetection1:DetectedReportDetailed() + + HQ:MessageToAll( DetectionReport, 15, "Detection 1 - No distance Probability" ) +end + +--- OnAfter Transition Handler for Event Detect. +-- @param Functional.Detection#DETECTION_UNITS self +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function RecceDetection2:OnAfterDetect(From,Event,To) + + local DetectionReport = RecceDetection2:DetectedReportDetailed() + + HQ:MessageToAll( DetectionReport, 15, "Detection 2 - Distance Probability" ) +end \ No newline at end of file diff --git a/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.miz b/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.miz new file mode 100644 index 000000000..e31a2d417 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.lua b/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.lua new file mode 100644 index 000000000..cbba0418f --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.lua @@ -0,0 +1,46 @@ +--- +-- Name: DET-255 - Detection AEAS with Destroys +-- Author: FlightControl +-- Date Created: 06 Mar 2017 +-- +-- # Situation: +-- +-- A small blue vehicle with laser detection methods is detecting targets. +-- Targets are grouped within areas. A detection range and zone range is given to group the detected units. +-- This demo will group red vehicles in areas. One vehicle is diving from one group to the other. +-- After 30 seconds, one vehicle is destroyed in a zone. +-- After 60 seconds, a vehicle is destroyed that is a leader of a zone. +-- After 90 seconds, all vehicles are destroyed in a zone. +-- +-- # Test cases: +-- +-- 1. Observe the flaring of the areas formed +-- 2. Observe the smoking of the units detected +-- 3. Observe the areas being flexibly changed very detection run. +-- 4. The truck driving from the one group to the other, will leave the first area, and will join the second. +-- 5. While driving in between the areas, it will have a separate area. +-- 6. Observe the correct removal or relocation of the ZONEs. + +FACSetGroup = SET_GROUP:New():FilterPrefixes( "FAC Group" ):FilterStart() + +FACDetection = DETECTION_AREAS:New( FACSetGroup, 150, 250 ):BoundDetectedZones():SmokeDetectedUnits() + +FACDetection:__Start( 5 ) + +SCHEDULER:New( nil,function() + local Target = UNIT:FindByName( "Target #004") + Target:Destroy() + end, {}, 30 + ) + +SCHEDULER:New( nil,function() + local Target = UNIT:FindByName( "Target #006") + Target:Destroy() + end, {}, 60 + ) + +SCHEDULER:New( nil,function() + local Target = UNIT:FindByName( "Target #007") + Target:Destroy() + end, {}, 90 + ) \ No newline at end of file diff --git a/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.miz b/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.miz new file mode 100644 index 000000000..8892f6c23 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.lua b/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.lua new file mode 100644 index 000000000..41277f443 --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.lua @@ -0,0 +1,70 @@ +--- +-- Name: DET-500 - Handle Detected Event - Govern Artillery Demo +-- Author: FlightControl +-- Date Created: 13 Feb 2017 +-- +-- # Situation: +-- +-- Demonstrates the detection of units. +-- +-- A Set of Recces are detecting a large group of units, which are 5 km away. +-- Once the Recces detect the enemy, the artilley units are controlled and will fire a missile to the target. +-- +-- # Test cases: +-- +-- 1. Observe the detected reporting of the recces. +-- 2. When one Recce group detects a target, it will select an artillery unit and fire a missile. +-- 3. This will run until all Recces have eliminated the targets. + +RecceSetGroup = SET_GROUP:New():FilterCoalitions( "blue" ):FilterPrefixes( "Recce" ):FilterStart() +ArtillerySetGroup = SET_GROUP:New():FilterCoalitions( "blue" ):FilterPrefixes( "Artillery" ):FilterStart() + +HQ = GROUP:FindByName( "HQ" ) + +CC = COMMANDCENTER:New( HQ, "HQ" ) + +RecceDetection = DETECTION_UNITS:New( RecceSetGroup ) +RecceDetection:SetDetectionInterval( 5 ) + +RecceDetection:Start() + +--- OnAfter Transition Handler for Event Detect. +-- @param Functional.Detection#DETECTION_UNITS self +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function RecceDetection:OnAfterDetect(From,Event,To) + + local DetectionReport = RecceDetection:DetectedReportDetailed() + + CC:GetPositionable():MessageToAll( DetectionReport, 15, "" ) +end + +local ArtilleryTime = {} +local ArtilleryAim = 180 + +--- OnAfter Transition Handler for Event Detect. +-- @param Functional.Detection#DETECTION_UNITS self +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @param Wrapper.Unit#UNIT DetectedUnits +function RecceDetection:OnAfterDetected( From, Event, To, DetectedUnits ) + self:E( { From, Event, To, DetectedUnits } ) + + for DetectedUnitID, DetectedUnit in pairs( DetectedUnits ) do + local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT + local Artillery = ArtillerySetGroup:GetRandom() -- Wrapper.Group#GROUP + + if ArtilleryTime[Artillery] and ArtilleryTime[Artillery] <= timer.getTime() - ArtilleryAim then + ArtilleryTime[Artillery] = nil + end + + if not ArtilleryTime[Artillery] then + local Task = Artillery:TaskFireAtPoint( DetectedUnit:GetVec2(), 500, 4 ) -- Fire 2 rockets to the target point. + Artillery:SetTask( Task, 0.5 ) + ArtilleryTime[Artillery] = timer.getTime() + end + + end +end \ No newline at end of file diff --git a/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.miz b/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.miz new file mode 100644 index 000000000..678497906 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.lua b/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.lua new file mode 100644 index 000000000..cc2969de4 --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.lua @@ -0,0 +1,25 @@ +--- +-- Name: DET-900 - Detection Test with RED FACA +-- Author: FlightControl +-- Date Created: 06 Mar 2017 +-- +-- # Situation: +-- +-- A red FACA is detecting targets while airborne. +-- Targets are grouped within areas. A detection range and zone range is given to group the detected units. +-- This demo will group blue vehicles in areas. +-- Upon the detection capabilities of the red FACA, the blue vehicles will be grouped when detected. +-- All blue vehicles have ROE on hold. +-- +-- # Test cases: +-- +-- 1. Observe the tyres put around the detected areas formed +-- 2. Observe the smoking of the units detected +-- 3. Observe the areas being flexibly changed very detection run. + +FACSetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() + +FACDetection = DETECTION_AREAS:New( FACSetGroup, 2000, 250 ):BoundDetectedZones():SmokeDetectedUnits() + + +FACDetection:__Start( 5 ) diff --git a/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.miz b/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.miz new file mode 100644 index 000000000..3817f41f7 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.miz differ diff --git a/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.lua b/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.lua index 58912b1bf..054b2325c 100644 --- a/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.lua +++ b/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.lua @@ -1,9 +1,21 @@ - - - - - - +--- +-- Name: ESC-001 - Escorting Helicopters +-- Author: FlightControl +-- Date Created: 10 Mar 2017 +-- +-- # Situation: +-- +-- Your client helicopter is flying in the battle field. +-- It is escorted by an MI-28N, which you can command... +-- Use the menu options to: +-- - Make the escort follow you. +-- - Report detected targets. +-- - Attack targets +-- - Flare +-- +-- # Test cases: +-- +-- 1. When executing the commands, observe the MI-28N reactions. do local function EventAliveHelicopter( Client ) @@ -20,6 +32,8 @@ do :MenuResumeMission() :MenuROE() :MenuAssistedAttack() + + EscortHeli1:SetDetection( EscortHeliDetection ) local EscortGroupArtillery = SpawnEscortArtillery:ReSpawn(1) local EscortArtillery = ESCORT @@ -59,6 +73,12 @@ do SpawnEscortGround = SPAWN:New( "Escort Ground" ) SpawnEscortShip = SPAWN:New( "Escort Ship" ) SpawnEscortArtillery = SPAWN:New( "Ground Attack Assistance" ) + + EscortHeliSetGroup = SET_GROUP:New():FilterPrefixes("Escort Helicopter"):FilterStart() + EscortHeliDetection = DETECTION_AREAS:New( EscortHeliSetGroup, 1000, 500 ) + + EscortHeliDetection:BoundDetectedZones() + EscortHeliDetection:SetDetectionInterval( 15 ) EscortClientHeli = CLIENT:FindByName( "Lead Helicopter", "Fly around and observe the behaviour of the escort helicopter" ):Alive( EventAliveHelicopter ) EscortClientPlane = CLIENT:FindByName( "Lead Plane", "Fly around and observe the behaviour of the escort airplane. Select Navigate->Joun-Up and airplane should follow you. Change speed and directions." ) @@ -66,9 +86,4 @@ do end --- MISSION SCHEDULER STARTUP -MISSIONSCHEDULER.Start() -MISSIONSCHEDULER.ReportMenu() -MISSIONSCHEDULER.ReportMissionsHide() - env.info( "Test Mission loaded" ) diff --git a/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz b/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz index cc28f1a8d..c097c3281 100644 Binary files a/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz and b/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz b/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz index 6d127352e..87e19707b 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz and b/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.lua index 1ea3b6dba..2f84ad795 100644 --- a/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.lua +++ b/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.lua @@ -13,7 +13,7 @@ -- 2. Observe when the plane shoots the missile, a dcs.log entry is written in the logging. -- 3. Check the contents of the fields of the S_EVENT_SHOT entry. -local Plane = UNIT:FindByName( "Plane" ) +Plane = UNIT:FindByName( "Plane" ) Plane:HandleEvent( EVENTS.Shot ) diff --git a/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.miz index 2d5fc77b2..a8fefbf80 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.lua index 88781fc39..03c8435ab 100644 --- a/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.lua +++ b/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.lua @@ -13,9 +13,9 @@ -- 2. Observe when the missile hits the target, a dcs.log entry is written in the logging. -- 3. Check the contents of the fields of the S_EVENT_HIT entry. -local Plane = UNIT:FindByName( "Plane" ) +Plane = UNIT:FindByName( "Plane" ) -local Tank = UNIT:FindByName( "Tank" ) +Tank = UNIT:FindByName( "Tank" ) Plane:HandleEvent( EVENTS.Hit ) Tank:HandleEvent( EVENTS.Hit ) diff --git a/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.miz index 730bfc5b5..f607c4eef 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.lua index 21d8204c5..203d946db 100644 --- a/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.lua +++ b/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.lua @@ -13,9 +13,9 @@ -- 2. When the planes take-off, observe the message being sent. -- 3. Check the contents of the fields of the S_EVENT_TAKEOFF entry in the dcs.log file. -local PlaneAI = UNIT:FindByName( "PlaneAI" ) +PlaneAI = UNIT:FindByName( "PlaneAI" ) -local PlaneHuman = UNIT:FindByName( "PlaneHuman" ) +PlaneHuman = UNIT:FindByName( "PlaneHuman" ) PlaneAI:HandleEvent( EVENTS.Takeoff ) PlaneHuman:HandleEvent( EVENTS.Takeoff ) diff --git a/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.miz index 1ab1cfd56..1fb313b93 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.lua index 2aef2d94a..ba4e7608c 100644 --- a/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.lua +++ b/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.lua @@ -15,10 +15,10 @@ -- 3. Check the contents of the fields of the S_EVENT_LAND entry in the dcs.log file. -- Create a variable PlaneAI that holds a reference to UNIT object (created by moose at the beginning of the mission) with the name "PlaneAI". -local PlaneAI = UNIT:FindByName( "PlaneAI" ) +PlaneAI = UNIT:FindByName( "PlaneAI" ) -- Create a SPAWN object to spawn a new plane once the hold one lands. -local SpawnPlane = SPAWN:New( "SpawnPlaneAI" ) +SpawnPlane = SPAWN:New( "SpawnPlaneAI" ) -- Declare a new variable that will hold the new spawned SpawnPlaneAI local NewPlane diff --git a/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.miz index 30d9ac5fa..9b51a437e 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.lua index 37fb02005..9a6675315 100644 --- a/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.lua +++ b/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.lua @@ -15,7 +15,7 @@ -- 3. Check the contents of the fields of the S_EVENT_CRASH entry in the dcs.log file. -- Create a variable PlaneHuman that holds a reference to UNIT object (created by moose at the beginning of the mission) with the name "PlaneHuman". -local PlaneHuman = UNIT:FindByName( "PlaneHuman" ) +PlaneHuman = UNIT:FindByName( "PlaneHuman" ) -- Subscribe to the event Crash. The Crash event occurs when a plane crashes into the ground (or into something else). PlaneHuman:HandleEvent( EVENTS.Crash ) diff --git a/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.miz index 18f3591de..a3ac0a634 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.lua index b569b45c0..7afed4b7d 100644 --- a/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.lua +++ b/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.lua @@ -15,7 +15,7 @@ -- 4. The planes of GROUP "Group Plane A", should only send a message when they shoot a missile. -- 5. The planes of GROUP "Group Plane B", should NOT send a message when they shoot a missile. -local PlaneGroup = GROUP:FindByName( "Group Plane A" ) +PlaneGroup = GROUP:FindByName( "Group Plane A" ) PlaneGroup:HandleEvent( EVENTS.Shot ) diff --git a/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.miz index 16bd5254f..2fc35a4d9 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.lua index 86f789b65..765419409 100644 --- a/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.lua +++ b/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.lua @@ -15,7 +15,7 @@ -- 4. The tanks of GROUP "Group Tanks A", should only send a message when they get hit. -- 5. The tanks of GROUP "Group Tanks B", should NOT send a message when they get hit. -local TanksGroup = GROUP:FindByName( "Group Tanks A" ) +TanksGroup = GROUP:FindByName( "Group Tanks A" ) TanksGroup:HandleEvent( EVENTS.Hit ) diff --git a/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.miz index e56eefbb2..ec9b1a296 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.lua index 4d5946aba..9847ce500 100644 --- a/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.lua +++ b/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.lua @@ -13,10 +13,10 @@ -- 2. Observe when a tank receives a hit, a dcs.log entry is written in the logging. -- 3. The generic EventHandler objects should receive the hit events. -local CC = COMMANDCENTER:New( UNIT:FindByName( "HQ" ), "HQ" ) +CC = COMMANDCENTER:New( UNIT:FindByName( "HQ" ), "HQ" ) -local EventHandler1 = EVENTHANDLER:New() -local EventHandler2 = EVENTHANDLER:New() +EventHandler1 = EVENTHANDLER:New() +EventHandler2 = EVENTHANDLER:New() EventHandler1:HandleEvent( EVENTS.Hit ) EventHandler2:HandleEvent( EVENTS.Hit ) diff --git a/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz index 7eeeab10a..e7a7c6b19 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz b/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz index 8df791219..360be667c 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz and b/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz b/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz index 636a552ae..bad078054 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz and b/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.lua b/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.lua index e6e69e973..e05d83081 100644 --- a/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.lua +++ b/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.lua @@ -15,9 +15,9 @@ -- 4. Observe the tanks hitting the targets and the messages appear. -- 3. Check the contents of the fields of the S_EVENT_HIT entries. -local Plane = UNIT:FindByName( "Plane" ) +Plane = UNIT:FindByName( "Plane" ) -local UnitSet = SET_UNIT:New():FilterPrefixes( "Tank" ):FilterStart() +UnitSet = SET_UNIT:New():FilterPrefixes( "Tank" ):FilterStart() UnitSet:HandleEvent( EVENTS.Hit ) diff --git a/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.miz b/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.miz index ed45cdd89..abae38230 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.miz and b/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.miz differ diff --git a/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz b/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz index 88139c229..ad8e22fb6 100644 Binary files a/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz and b/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz b/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz index 7e21b9059..465eb6811 100644 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz and b/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.lua b/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.lua new file mode 100644 index 000000000..101ffafee --- /dev/null +++ b/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.lua @@ -0,0 +1,30 @@ +--- This test demonstrates the use(s) of the SwitchWayPoint method of the GROUP class. + +local HeliGroup = GROUP:FindByName( "Helicopter" ) + +local AttackGroup = GROUP:FindByName( "AttackGroup" ) + +local AttackUnits = AttackGroup:GetUnits() + +local Tasks = {} + +for i = 1, #AttackUnits do + + local AttackUnit = AttackGroup:GetUnit( i ) + Tasks[#Tasks+1] = HeliGroup:TaskAttackUnit( AttackUnit ) +end + +Tasks[#Tasks+1] = HeliGroup:TaskFunction( 1, 7, "_Resume", { "''" } ) + +--- @param Wrapper.Group#GROUP HeliGroup +function _Resume( HeliGroup ) + env.info( '_Resume' ) + + HeliGroup:MessageToAll( "Resuming",10,"Info") +end + +HeliGroup:PushTask( + HeliGroup:TaskCombo( + Tasks + ), 30 +) \ No newline at end of file diff --git a/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.miz b/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.miz new file mode 100644 index 000000000..7c3d455c6 Binary files /dev/null and b/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz b/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz index 394ec7b4c..68b995f09 100644 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz and b/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz b/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz index c366f3726..7c61e2010 100644 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz and b/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-310 - Command StopRoute/GRP-310 - Command StopRoute.lua b/Moose Test Missions/GRP - Group Commands/GRP-310 - Command StopRoute/GRP-310 - Command StopRoute.lua new file mode 100644 index 000000000..1f7710d18 --- /dev/null +++ b/Moose Test Missions/GRP - Group Commands/GRP-310 - Command StopRoute/GRP-310 - Command StopRoute.lua @@ -0,0 +1,39 @@ +--- +-- Name: GRP-310 - Command StopRoute +-- Author: FlightControl +-- Date Created: 25 Mar 2017 +-- +-- # Situation: +-- A ground unit is moving. +-- Using the command CommandStopMove it will stop moving after 10 seconds. +-- +-- # Test cases: +-- +-- 1. Observe the ground group stopping to move. +-- + +--- @param Wrapper.Group#GROUP GroundGroup +function StopMove( GroundGroup ) + + BASE:E("Stop") + local Command = GroundGroup:CommandStopRoute( true ) + GroundGroup:SetCommand(Command) + +end + +--- @param Wrapper.Group#GROUP GroundGroup +function StartMove( GroundGroup ) + + BASE:E("Start") + local Command = GroundGroup:CommandStopRoute( false ) + GroundGroup:SetCommand(Command) + +end + +GroundGroup = GROUP:FindByName( "Ground" ) + +Scheduler = SCHEDULER:New( nil ) +ScheduleIDStop = Scheduler:Schedule(nil, StopMove, { GroundGroup }, 10, 20 ) +ScheduleIDStart = Scheduler:Schedule(nil, StartMove, { GroundGroup }, 20, 20 ) + + diff --git a/Moose Test Missions/GRP - Group Commands/GRP-310 - Command StopRoute/GRP-310 - Command StopRoute.miz b/Moose Test Missions/GRP - Group Commands/GRP-310 - Command StopRoute/GRP-310 - Command StopRoute.miz new file mode 100644 index 000000000..9508ba428 Binary files /dev/null and b/Moose Test Missions/GRP - Group Commands/GRP-310 - Command StopRoute/GRP-310 - Command StopRoute.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-400 - RouteReturnToAirbase/GRP-400 - RouteReturnToAirbase.lua b/Moose Test Missions/GRP - Group Commands/GRP-400 - RouteReturnToAirbase/GRP-400 - RouteReturnToAirbase.lua new file mode 100644 index 000000000..47ff792f4 --- /dev/null +++ b/Moose Test Missions/GRP - Group Commands/GRP-400 - RouteReturnToAirbase/GRP-400 - RouteReturnToAirbase.lua @@ -0,0 +1,45 @@ +--- +-- Name: GRP-400 - RouteReturnToAirbase +-- Author: FlightControl +-- Date Created: 25 Mar 2017 +-- +-- # Situation: +-- Three air units are flying and are commanded to return a specific airbase. +-- +-- # Test cases: +-- +-- 1. Observe the Air1 group return to Batumi after 10 seconds. +-- 2. Observe the Air2 group returning to Kobuleti after 300 seconds. (It was planned to land at Kutaisi). +-- 3. Observe the Air3 group returning to the home (landing) airbase after 300 seconds. (It was planned to land at Kutaisi). +-- + +--- @param Wrapper.Group#GROUP AirGroup +function ReturnToBatumi( AirGroup ) + BASE:E("ReturnToBatumi") + AirGroup:RouteRTB( AIRBASE:FindByName("Batumi") ) +end + +--- @param Wrapper.Group#GROUP AirGroup +function ReturnToKobuleti( AirGroup ) + BASE:E("ReturnToKobuleti") + AirGroup:RouteRTB( AIRBASE:FindByName("Kobuleti") ) +end + +--- @param Wrapper.Group#GROUP AirGroup +function ReturnToHome( AirGroup ) + BASE:E("ReturnToHome") + AirGroup:RouteRTB() +end + +Air1Group = GROUP:FindByName( "Air1" ) +Air2Group = GROUP:FindByName( "Air2" ) +Air3Group = GROUP:FindByName( "Air3" ) + +Scheduler = SCHEDULER:New( nil ) +ScheduleIDAir1 = Scheduler:Schedule(nil, ReturnToBatumi, { Air1Group }, 10 ) +ScheduleIDAir2 = Scheduler:Schedule(nil, ReturnToKobuleti, { Air2Group }, 300 ) +ScheduleIDAir3 = Scheduler:Schedule(nil, ReturnToHome, { Air3Group }, 300 ) + + + + diff --git a/Moose Test Missions/GRP - Group Commands/GRP-400 - RouteReturnToAirbase/GRP-400 - RouteReturnToAirbase.miz b/Moose Test Missions/GRP - Group Commands/GRP-400 - RouteReturnToAirbase/GRP-400 - RouteReturnToAirbase.miz new file mode 100644 index 000000000..5ced79316 Binary files /dev/null and b/Moose Test Missions/GRP - Group Commands/GRP-400 - RouteReturnToAirbase/GRP-400 - RouteReturnToAirbase.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz b/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz index 1c050743b..2253de3c3 100644 Binary files a/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz and b/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz differ diff --git a/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz b/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz index a278af36e..092d14467 100644 Binary files a/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz and b/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz differ diff --git a/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz b/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz index 95a075d58..d9f4c11bc 100644 Binary files a/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz and b/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz differ diff --git a/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz b/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz index c32270f70..8fa0d94ae 100644 Binary files a/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz and b/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz differ diff --git a/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz b/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz index c924e6c33..9c06d94d5 100644 Binary files a/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz and b/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz differ diff --git a/Moose Test Missions/MOOSE_Header.lua b/Moose Test Missions/MOOSE_Header.lua new file mode 100644 index 000000000..daaa33103 --- /dev/null +++ b/Moose Test Missions/MOOSE_Header.lua @@ -0,0 +1,14 @@ +--- +-- Name: +-- Author: +-- Date Created: +-- +-- # Situation: +-- +-- . +-- +-- # Test cases: +-- +-- 1. +-- 2. +-- diff --git a/Moose Test Missions/MOOSE_Template.miz b/Moose Test Missions/MOOSE_Template.miz new file mode 100644 index 000000000..a3e4643e4 Binary files /dev/null and b/Moose Test Missions/MOOSE_Template.miz differ diff --git a/Moose Test Missions/MOOSE_Test_Template.miz b/Moose Test Missions/MOOSE_Test_Template.miz index 5c106af38..24b76c5ba 100644 Binary files a/Moose Test Missions/MOOSE_Test_Template.miz and b/Moose Test Missions/MOOSE_Test_Template.miz differ diff --git a/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.lua b/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.lua index 0cbc54bdc..cd25ae557 100644 --- a/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.lua +++ b/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.lua @@ -14,21 +14,21 @@ -- In this way, the Patrol1 and Patrol2 objects are fluctuating the patrol pattern from PatrolZone1 and PatrolZone2 :-) -local PatrolZoneGroup1 = GROUP:FindByName( "Patrol Zone 1" ) -local PatrolZone1 = ZONE_POLYGON:New( "Patrol Zone 1", PatrolZoneGroup1 ) +PatrolZoneGroup1 = GROUP:FindByName( "Patrol Zone 1" ) +PatrolZone1 = ZONE_POLYGON:New( "Patrol Zone 1", PatrolZoneGroup1 ) -local PatrolZoneGroup2 = GROUP:FindByName( "Patrol Zone 2" ) -local PatrolZone2 = ZONE_POLYGON:New( "Patrol Zone 2", PatrolZoneGroup2 ) +PatrolZoneGroup2 = GROUP:FindByName( "Patrol Zone 2" ) +PatrolZone2 = ZONE_POLYGON:New( "Patrol Zone 2", PatrolZoneGroup2 ) -local PatrolSpawn = SPAWN:New( "Patrol Group" ) -local PatrolGroup = PatrolSpawn:Spawn() +PatrolSpawn = SPAWN:New( "Patrol Group" ) +PatrolGroup = PatrolSpawn:Spawn() -local Patrol1 = AI_PATROL_ZONE:New( PatrolZone1, 3000, 6000, 400, 600 ) +Patrol1 = AI_PATROL_ZONE:New( PatrolZone1, 3000, 6000, 400, 600 ) Patrol1:ManageFuel( 0.2, 60 ) Patrol1:SetControllable( PatrolGroup ) Patrol1:__Start( 5 ) -local Patrol2 = AI_PATROL_ZONE:New( PatrolZone2, 600, 1000, 300, 400 ) +Patrol2 = AI_PATROL_ZONE:New( PatrolZone2, 600, 1000, 300, 400 ) Patrol2:ManageFuel( 0.2, 0 ) --- State transition function for the PROCESS\_PATROLZONE **Patrol1** object diff --git a/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz b/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz index 12f3721ad..27061e36e 100644 Binary files a/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz and b/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz differ diff --git a/Moose Test Missions/RAD - Radio/RAD-000 - Transmission from Static/RAD-000 - Transmission from Static.lua b/Moose Test Missions/RAD - Radio/RAD-000 - Transmission from Static/RAD-000 - Transmission from Static.lua new file mode 100644 index 000000000..296604a1d --- /dev/null +++ b/Moose Test Missions/RAD - Radio/RAD-000 - Transmission from Static/RAD-000 - Transmission from Static.lua @@ -0,0 +1,19 @@ +-- This test mission demonstrates the RADIO class, particularily when the transmiter is anything but a UNIT or a GROUP (a STATIC in this case) +-- The Player is in a Su25T parked on Batumi, and a Russian command center named "Russian Command Center" is placed 12km east of Batumi. + +-- Note that if you are not using an ASM aircraft (a clickable cockpit aircraft), then the frequency and the modulation is not important. +-- If you want to test the mission fully, replance the SU25T by an ASM aircraft you own and tune to the right frequency (108AM here) + +CommandCenter = STATIC:FindByName("Russian Command Center") + +-- Let's get a reference to the Command Center's RADIO +CommandCenterRadio = CommandCenter:GetRadio() + +-- Now, we'll set up the next transmission +CommandCenterRadio:SetFileName("Noise.ogg") -- We first need the file name of a sound, +CommandCenterRadio:SetFrequency(108) -- then a frequency in MHz, +CommandCenterRadio:SetModulation(radio.modulation.AM) -- a modulation (we use DCS' enumartion, this way we don't have to type numbers)... +CommandCenterRadio:SetPower(100) -- and finally a power in Watts. A "normal" ground TACAN station has a power of 120W. + +-- We have finished tinkering with our transmission, now is the time to broadcast it ! +CommandCenterRadio:Broadcast() \ No newline at end of file diff --git a/Moose Test Missions/RAD - Radio/RAD-000 - Transmission from Static/RAD-000 - Transmission from Static.miz b/Moose Test Missions/RAD - Radio/RAD-000 - Transmission from Static/RAD-000 - Transmission from Static.miz new file mode 100644 index 000000000..d9b05278b Binary files /dev/null and b/Moose Test Missions/RAD - Radio/RAD-000 - Transmission from Static/RAD-000 - Transmission from Static.miz differ diff --git a/Moose Test Missions/RAD - Radio/RAD-001 - Transmission from UNIT or GROUP/RAD-001 - Transmission from UNIT or GROUP.lua b/Moose Test Missions/RAD - Radio/RAD-001 - Transmission from UNIT or GROUP/RAD-001 - Transmission from UNIT or GROUP.lua new file mode 100644 index 000000000..53b86eccb --- /dev/null +++ b/Moose Test Missions/RAD - Radio/RAD-001 - Transmission from UNIT or GROUP/RAD-001 - Transmission from UNIT or GROUP.lua @@ -0,0 +1,25 @@ +-- This test mission demonstrates the RADIO class, particularily when the transmiter is a UNIT or a GROUP +-- The Player is in a Su25T parked on Batumi, and a Russian MiG-29 creatively named "Sergey" is placed above Kobuleti and is +-- inbound for a landing on Batumi + +-- Note that if you are not using an ASM aircraft (a clickable cockpit aircraft), then the frequency and the modulation is not important. +-- If you want to test the mission fully, replance the SU25T by an ASM aircraft you own and tune to the right frequency (108AM here) + +Sergey = UNIT:FindByName("Sergey") + +-- Let's get a reference to Sergey's RADIO +SergeyRadio = Sergey:GetRadio() + +-- Now, we'll set up the next transmission +SergeyRadio:SetFileName("Noise.ogg") -- We first need the file name of a sound, +SergeyRadio:SetFrequency(108) -- then a frequency in MHz, +SergeyRadio:SetModulation(radio.modulation.AM) -- and a modulation (we use DCS' enumartion, this way we don't have to type numbers). + +-- Since Sergey is a UNIT, we can add a subtitle (displayed on the top left) to the transmission, and loop the transmission +SergeyRadio:SetSubtitle("Hey, hear that noise ?", 5) -- The subtitle "Noise" will be displayed for 5 secs +SergeyRadio:SetLoop(false) + +-- Notice that we didn't have to imput a power ? If the broadcater is a UNIT or a GROUP, DCS automatically guesses the power to use depending on the type of UNIT or GROUP + +-- We have finished tinkering with our transmission, now is the time to broadcast it ! +SergeyRadio:Broadcast() \ No newline at end of file diff --git a/Moose Test Missions/RAD - Radio/RAD-001 - Transmission from UNIT or GROUP/RAD-001 - Transmission from UNIT or GROUP.miz b/Moose Test Missions/RAD - Radio/RAD-001 - Transmission from UNIT or GROUP/RAD-001 - Transmission from UNIT or GROUP.miz new file mode 100644 index 000000000..c86971d28 Binary files /dev/null and b/Moose Test Missions/RAD - Radio/RAD-001 - Transmission from UNIT or GROUP/RAD-001 - Transmission from UNIT or GROUP.miz differ diff --git a/Moose Test Missions/RAD - Radio/RAD-002 - Transmission Tips and Tricks/RAD-002 - Transmission Tips and Tricks.lua b/Moose Test Missions/RAD - Radio/RAD-002 - Transmission Tips and Tricks/RAD-002 - Transmission Tips and Tricks.lua new file mode 100644 index 000000000..0d7a473f1 --- /dev/null +++ b/Moose Test Missions/RAD - Radio/RAD-002 - Transmission Tips and Tricks/RAD-002 - Transmission Tips and Tricks.lua @@ -0,0 +1,91 @@ +-- This test mission demonstrates the RADIO class in a practical scenario. +-- It also focuses on how to create transmissions faster and more efficiently +-- Please Read both RAD-000 and RAD-001, as well as SCH-000 code first. + +-- Note that if you are not using an ASM aircraft (a clickable cockpit aircraft), then the frequency and the modulation is not important. +-- If you want to test the mission fully, replance the SU25T by an ASM aircraft you own and tune to the right frequency (115AM here) + +-- The Player is in a Su25T parked on Batumi, and a Russian command center named "Batumi Tower" placed near Batumi will act as Batumi's Radio Tower. +-- This mission also features the "Viktor" flight, a Russian Su25, who is inbound for landing on Batumi. +-- The goal of this script is to manage the dialog between Viktor and Batumi Tower. + +-- The (short) conversation between Viktor and Batumi Tower will happen on 115 AM +-- Time 0 : Batumi Tower "Viktor flight, this is Batumi Tower, enter left base runway one two five, report 5 kilometers final. Over." +-- Time 10 : Viktor "Report 5 kilometers final, one two five, viktor" +-- Time 145 : Viktor "Batumi Tower, Viktor is 5 kilomters final, request landing clearance. Over?" +-- Time 154 : Batumi Tower "Viktor flight, you are claer to land, runway one two five. Check gear down." +-- Time 160 : Viktor "Clear to land, One two five, Viktor" +-- Time 210 : Viktor "Viktor, touchdown" +-- Time 215 : Batumi Tower "Viktor, confirmed touchdown, taxi to parking area, Batumi Tower out." + + +BatumiRadio = STATIC:FindByName("Batumi Tower"):GetRadio() +ViktorRadio = UNIT:FindByName("Viktor"):GetRadio() + +-- Let's first explore different shortcuts to setup a transmission before broadcastiong it +------------------------------------------------------------------------------------------------------------------------------------------------------ +-- First, the long way. +BatumiRadio:SetFileName("Batumi Tower - Enter left base.ogg") +BatumiRadio:SetFrequency(115) +BatumiRadio:SetModulation(radio.modulation.AM) +BatumiRadio:SetPower(100) + +-- Every RADIO.SetXXX() function returns the radio, so we can rewrite the code above this way : +BatumiRadio:SetFileName("Batumi Tower - Enter left base.ogg"):SetFrequency(115):SetModulation(radio.modulation.AM):SetPower(100) + +-- We can also use the shortcut RADIO:NewGenericTransmission() to set multiple parameters in one function call +-- If our broadcaster was a UNIT or a GROUP, the more appropriate shortcut to use would have been NewUnitTransmission() +-- it works for both UNIT and GROUP, despite its name ! +BatumiRadio:NewGenericTransmission("Batumi Tower - Enter left base.ogg", 115, radio.modulation.AM, 100) + +-- If you already set some parameters previously, you don't have to redo it ! +-- NewGenericTransmission's paramter have to be set in order +BatumiRadio:NewGenericTransmission("Batumi Tower - Enter left base.ogg", 115) -- Modulation is still AM and power is still 100 (set previously) + +--If you want to change only the sound file, the frequency and the power for exemple, you can still use the appropriate Set function +BatumiRadio:NewGenericTransmission("Batumi Tower - Enter left base.ogg", 115):SetPower(100) + +-- We have finished tinkering with our transmission, now is the time to broadcast it ! +BatumiRadio:Broadcast() + +-- Now, if Viktor answered imedately, the two radio broadcasts would overlap. We need to delay Viktor's answer. +------------------------------------------------------------------------------------------------------------------------------------------------------ +CommunitcationScheduler = SCHEDULER:New( nil, + function() + ViktorRadio:SetFileName("Viktor - Enter left base ack.ogg"):SetFrequency(115):SetModulation(radio.modulation.AM):Broadcast() -- We don't specify a subtitle since we don't want one + end, {}, 10 -- 10s delay + ) + +-- Viktor takes 145s to be 5km final, and need to contact Batumi Tower. +------------------------------------------------------------------------------------------------------------------------------------------------------ +CommunitcationScheduler:Schedule( nil, + function() + ViktorRadio:SetFileName("Viktor - Request landing clearance.ogg"):Broadcast() --We only specify the new file name, since frequency and modulation didn't change + end, {}, 145 + ) + +-- Now that you understand everything about the RADIO class, the rest is pretty trivial +------------------------------------------------------------------------------------------------------------------------------------------------------- +CommunitcationScheduler:Schedule( nil, + function() + BatumiRadio:SetFileName("Batumi Tower - Clear to land.ogg"):Broadcast() + end, {}, 154 + ) + +CommunitcationScheduler:Schedule( nil, + function() + ViktorRadio:SetFileName("Viktor - Clear to land ack.ogg"):Broadcast() + end, {}, 160 + ) + +CommunitcationScheduler:Schedule( nil, + function() + ViktorRadio:SetFileName("Viktor - Touchdown.ogg"):Broadcast() + end, {}, 210 + ) + +CommunitcationScheduler:Schedule( nil, + function() + BatumiRadio:SetFileName("Batumi Tower - Taxi to parking.ogg"):Broadcast() + end, {}, 215 + ) \ No newline at end of file diff --git a/Moose Test Missions/RAD - Radio/RAD-002 - Transmission Tips and Tricks/RAD-002 - Transmission Tips and Tricks.miz b/Moose Test Missions/RAD - Radio/RAD-002 - Transmission Tips and Tricks/RAD-002 - Transmission Tips and Tricks.miz new file mode 100644 index 000000000..7beb833c0 Binary files /dev/null and b/Moose Test Missions/RAD - Radio/RAD-002 - Transmission Tips and Tricks/RAD-002 - Transmission Tips and Tricks.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz index fcc030cb8..c44b83b7b 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz index ef0e4f5e7..294d7e4bb 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz index 87e561815..31017e0aa 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz index 45f4f52d8..355e633b9 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz b/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz index c76856795..97165d9c9 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz and b/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz index 021db24c9..d3ae3dcc1 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz index 57e883f55..b1a844852 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz differ diff --git a/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.lua b/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.lua index 28186f64c..6c7b7b601 100644 --- a/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.lua +++ b/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.lua @@ -12,11 +12,11 @@ -- 1. Observe the scoring granted to your flight when you hit and kill targets. -local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) +HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) -local CommandCenter = COMMANDCENTER:New( HQ, "Bravo" ) +CommandCenter = COMMANDCENTER:New( HQ, "Bravo" ) -local Scoring = SCORING:New( "Shooting Range 1" ) +Scoring = SCORING:New( "Shooting Range 1" ) Scoring:SetScaleDestroyScore( 10 ) @@ -27,12 +27,12 @@ Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 ) -- Test for zone scores. -- This one is to test scoring on normal units. -local ShootingRangeZone = ZONE:New( "ScoringZone1" ) +ShootingRangeZone = ZONE:New( "ScoringZone1" ) Scoring:AddZoneScore( ShootingRangeZone, 200 ) -- This one is to test scoring on scenery. -- Note that you can only destroy scenery with heavy weapons. -local SceneryZone = ZONE:New( "ScoringZone2" ) +SceneryZone = ZONE:New( "ScoringZone2" ) Scoring:AddZoneScore( SceneryZone, 200 ) Scoring:AddStaticScore(STATIC:FindByName( "Shooting Range #010" ), 100 ) diff --git a/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz b/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz index 180ace4f8..e3003e609 100644 Binary files a/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz and b/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz differ diff --git a/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.lua b/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.lua index 704c458a4..176e709ab 100644 --- a/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.lua +++ b/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.lua @@ -12,10 +12,10 @@ -- 1. Observe the scoring granted to your flight when you hit and kill other clients. -local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) +HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) -local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) +CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) -local Scoring = SCORING:New( "Detect Demo" ) +Scoring = SCORING:New( "Detect Demo" ) diff --git a/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz b/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz index 6a4747585..cf1b30608 100644 Binary files a/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz and b/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz differ diff --git a/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.lua b/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.lua index 1a4c15294..127735351 100644 --- a/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.lua +++ b/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.lua @@ -12,57 +12,57 @@ -- 1. Observe the scoring granted to your flight when you hit and kill targets. -- Define the patrol zones -local BlueCapZone = ZONE_POLYGON:New( "BlueCapZone", GROUP:FindByName( "Blue CAP Zone Patrol" ) ) -local RedCapZone = ZONE_POLYGON:New( "RedCapZone", GROUP:FindByName( "Red CAP Zone Patrol" ) ) +BlueCapZone = ZONE_POLYGON:New( "BlueCapZone", GROUP:FindByName( "Blue CAP Zone Patrol" ) ) +RedCapZone = ZONE_POLYGON:New( "RedCapZone", GROUP:FindByName( "Red CAP Zone Patrol" ) ) -- Define the engage zones -local BlueEngageZone = ZONE_POLYGON:New( "BlueEngageZone", GROUP:FindByName( "Blue CAP Zone Engage" ) ) -local RedEngageZone = ZONE_POLYGON:New( "RedEngageZone", GROUP:FindByName( "Red CAP Zone Engage" ) ) +BlueEngageZone = ZONE_POLYGON:New( "BlueEngageZone", GROUP:FindByName( "Blue CAP Zone Engage" ) ) +RedEngageZone = ZONE_POLYGON:New( "RedEngageZone", GROUP:FindByName( "Red CAP Zone Engage" ) ) -- Define the Spawn zones for ground vehicles -local BlueSpawnGroundZone = ZONE_POLYGON:New( "BlueSpawnGroundZone", GROUP:FindByName( "Blue Spawn Zone" ) ) ---local RedSpawnGroundZone = ZONE_POLYGON:New( "RedSpawnGroundZone", GROUP:FindByName( "Red Spawn Zone" ) ) +BlueSpawnGroundZone = ZONE_POLYGON:New( "BlueSpawnGroundZone", GROUP:FindByName( "Blue Spawn Zone" ) ) +--RedSpawnGroundZone = ZONE_POLYGON:New( "RedSpawnGroundZone", GROUP:FindByName( "Red Spawn Zone" ) ) -local RedSpawnGroundZone = ZONE:New( "Red Spawn Zone" ) +RedSpawnGroundZone = ZONE:New( "Red Spawn Zone" ) -- Define the Scoring zones that define the shelters -local BlueShelterZone = ZONE_POLYGON:New( "Blue Shelters", GROUP:FindByName( "Blue Shelters" ) ) -local RedShelterZone = ZONE_POLYGON:New( "Red Shelters", GROUP:FindByName( "Red Shelters" ) ) +BlueShelterZone = ZONE_POLYGON:New( "Blue Shelters", GROUP:FindByName( "Blue Shelters" ) ) +RedShelterZone = ZONE_POLYGON:New( "Red Shelters", GROUP:FindByName( "Red Shelters" ) ) -- Define the Set of Clients that are used for the AI Balancers -local BluePlanesClientSet = SET_CLIENT:New():FilterCoalitions( "blue" ):FilterCategories( "plane" ):FilterPrefixes( "Blue Player") -local RedPlanesClientSet = SET_CLIENT:New():FilterCoalitions( "red" ):FilterCategories( "plane" ):FilterPrefixes( "Red Player") +BluePlanesClientSet = SET_CLIENT:New():FilterCoalitions( "blue" ):FilterCategories( "plane" ):FilterPrefixes( "Blue Player") +RedPlanesClientSet = SET_CLIENT:New():FilterCoalitions( "red" ):FilterCategories( "plane" ):FilterPrefixes( "Red Player") -- Define the Spawn objects for the AI planes -local BluePlanesSpawn = SPAWN:New( "BlueAICAP" ):InitCleanUp( 120 ):InitLimit( 5, 0 ) -local RedPlanesSpawn = SPAWN:New( "RedAICAP" ):InitCleanUp( 120 ):InitLimit( 5, 0 ) +BluePlanesSpawn = SPAWN:New( "BlueAICAP" ):InitCleanUp( 120 ):InitLimit( 5, 0 ) +RedPlanesSpawn = SPAWN:New( "RedAICAP" ):InitCleanUp( 120 ):InitLimit( 5, 0 ) -- Define the AI Balancers for the planes -local BlueAIB = AI_BALANCER:New( BluePlanesClientSet, BluePlanesSpawn ):InitSpawnInterval( 60, 1200 ) -local RedAIB = AI_BALANCER:New( RedPlanesClientSet, RedPlanesSpawn ):InitSpawnInterval( 60, 1200 ) +BlueAIB = AI_BALANCER:New( BluePlanesClientSet, BluePlanesSpawn ):InitSpawnInterval( 60, 1200 ) +RedAIB = AI_BALANCER:New( RedPlanesClientSet, RedPlanesSpawn ):InitSpawnInterval( 60, 1200 ) -- Define the Spawn objects for the airbase defenses -local BlueAirbaseDefense1Spawn = SPAWN:New( "Blue Airbase Defense 1" ):InitLimit( 10, 10 ):SpawnScheduled( 60, 0 ) -local BlueAirbaseDefense2Spawn = SPAWN:New( "Blue Airbase Defense 2" ):InitLimit( 2, 10 ):SpawnScheduled( 60, 0 ) -local RedAirbaseDefense1Spawn = SPAWN:New( "Red Airbase Defense 1" ):InitLimit( 10, 10 ):SpawnScheduled( 60, 0 ) -local RedAirbaseDefense2Spawn = SPAWN:New( "Red Airbase Defense 2" ):InitLimit( 2, 10 ):SpawnScheduled( 60, 0 ) +BlueAirbaseDefense1Spawn = SPAWN:New( "Blue Airbase Defense 1" ):InitLimit( 10, 10 ):SpawnScheduled( 60, 0 ) +BlueAirbaseDefense2Spawn = SPAWN:New( "Blue Airbase Defense 2" ):InitLimit( 2, 10 ):SpawnScheduled( 60, 0 ) +RedAirbaseDefense1Spawn = SPAWN:New( "Red Airbase Defense 1" ):InitLimit( 10, 10 ):SpawnScheduled( 60, 0 ) +RedAirbaseDefense2Spawn = SPAWN:New( "Red Airbase Defense 2" ):InitLimit( 2, 10 ):SpawnScheduled( 60, 0 ) -- Define the ground forces spawning engines... -- First define the template arrays. -local BlueGroundTemplates = { "Blue Ground Forces 1", "Blue Ground Forces 2", "Blue Ground Forces 3" } -local RedGroundTemplates = { "Red Ground Forces 2", "Red Ground Forces 2", "Red Ground Forces 3" } +BlueGroundTemplates = { "Blue Ground Forces 1", "Blue Ground Forces 2", "Blue Ground Forces 3" } +RedGroundTemplates = { "Red Ground Forces 2", "Red Ground Forces 2", "Red Ground Forces 3" } -- New we are using these templates to define the spawn objects for the ground forces. -- We spawn them at random places into the define zone. -local BlueGroundSpawn = SPAWN +BlueGroundSpawn = SPAWN :New( "Blue Ground Forces" ) :InitLimit( 12, 30 ) :InitRandomizeZones( { BlueSpawnGroundZone } ) :InitRandomizeTemplate( BlueGroundTemplates ) :SpawnScheduled( 60, 0.2 ) -local RedGroundSpawn = SPAWN +RedGroundSpawn = SPAWN :New( "Red Ground Forces" ) :InitLimit( 12, 30 ) :InitRandomizeTemplate( RedGroundTemplates ) @@ -71,8 +71,8 @@ local RedGroundSpawn = SPAWN -local BlueCap = {} -local RedCap = {} +BlueCap = {} +RedCap = {} -- Define the OnAfterSpawned events of the AI balancer Spawn Groups function BlueAIB:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) diff --git a/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.miz b/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.miz index 73a8cf94a..c3a3b6e7b 100644 Binary files a/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.miz and b/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.miz differ diff --git a/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.lua b/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.lua index f7ca6951a..e53f6d936 100644 --- a/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.lua +++ b/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.lua @@ -1,11 +1,11 @@ -local BlueAirbaseSet = SET_AIRBASE:New():FilterCoalitions("blue"):FilterStart() +BlueAirbaseSet = SET_AIRBASE:New():FilterCoalitions("blue"):FilterStart() -local RedAirbaseSet = SET_AIRBASE:New():FilterCoalitions("red"):FilterStart() +RedAirbaseSet = SET_AIRBASE:New():FilterCoalitions("red"):FilterStart() -local RedAirbaseHelipadSet = SET_AIRBASE:New():FilterCoalitions("red"):FilterCategories("helipad"):FilterStart() +RedAirbaseHelipadSet = SET_AIRBASE:New():FilterCoalitions("red"):FilterCategories("helipad"):FilterStart() -local BlueAirbaseShipSet = SET_AIRBASE:New():FilterCoalitions("blue"):FilterCategories("ship"):FilterStart() +BlueAirbaseShipSet = SET_AIRBASE:New():FilterCoalitions("blue"):FilterCategories("ship"):FilterStart() BlueAirbaseSet:Flush() diff --git a/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz b/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz index e59c196a9..2b8015437 100644 Binary files a/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz and b/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz differ diff --git a/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz b/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz index 57d09b219..b3bb2e970 100644 Binary files a/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz and b/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz differ diff --git a/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz b/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz index d602aab0a..fbedd028d 100644 Binary files a/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz and b/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz differ diff --git a/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz b/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz index 0d94e23e1..3657c63f6 100644 Binary files a/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz and b/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz b/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz index f0ebe022f..74ecbf75b 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz and b/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz index 016d31e66..c884e1fdd 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz and b/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz b/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz index d2d9e7c64..1cf21d951 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz and b/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz b/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz index 1f2a05b52..9405e3210 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz and b/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz b/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz index 64543f947..3d7f19ed3 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz and b/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz b/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz index 60237ed1a..b92d9ae48 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz and b/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz b/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz index 6a6d5eb08..5c2065f26 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz and b/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz index 5d7bb0c01..0b36979f5 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz and b/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz b/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz index 28a43b629..d6b0ce568 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz and b/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.miz b/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.miz index 63e2a9968..161740a5c 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.miz and b/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.miz b/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.miz index 8c8cfb1c3..7fd38af8e 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.miz and b/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names.miz b/Moose Test Missions/SPA - Spawning/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names.miz index 85d9ddf3c..cde7720ba 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names.miz and b/Moose Test Missions/SPA - Spawning/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval.lua b/Moose Test Missions/SPA - Spawning/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval.lua new file mode 100644 index 000000000..5096cddc5 --- /dev/null +++ b/Moose Test Missions/SPA - Spawning/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval.lua @@ -0,0 +1,23 @@ +-- Name: SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval +-- Author: FlightControl +-- Date Created: 18 Mar 2017 +-- +-- # Situation: +-- +-- At Gudauta spawn multiple ground vehicles, in a scheduled fashion. +-- The vehicle should respawn when killed. +-- +-- # Test cases: +-- +-- 1. Observe that the ground vehicles are spawned at the position declared within the mission editor. +-- 2. The vehicles should spawn according the scheduler parameters. + + + +-- Tests Gudauta +-- ------------- +Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ):InitLimit( 1, 0 ):SpawnScheduled( 30, .5 ) + + + + diff --git a/Moose Test Missions/SPA - Spawning/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval.miz b/Moose Test Missions/SPA - Spawning/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval.miz new file mode 100644 index 000000000..9e6718215 Binary files /dev/null and b/Moose Test Missions/SPA - Spawning/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.lua b/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.lua index 3c8c3967f..c7d62a568 100644 --- a/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.lua +++ b/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.lua @@ -3,6 +3,6 @@ -- Tests the CleanUp functionality. -- Limited spawning of groups, scheduled every 10 seconds, who are engaging into combat. Some helicopters may crash land on the ground. -- Observe when helicopters land but are not dead and are out of the danger zone, that they get removed after a while (+/- 180 seconds) and ReSpawn. -Spawn_Helicopter_Scheduled_CleanUp = SPAWN:New( "Spawn Helicopter Scheduled CleanUp" ):InitLimit( 3, 100 ):InitRandomizeRoute( 1, 1, 1000 ):CleanUp( 60 ):SpawnScheduled( 10, 0 ) +Spawn_Helicopter_Scheduled_CleanUp = SPAWN:New( "Spawn Helicopter Scheduled CleanUp" ):InitLimit( 3, 100 ):InitRandomizeRoute( 1, 1, 1000 ):InitCleanUp( 60 ):SpawnScheduled( 10, 0 ) Spawn_Vehicle_Scheduled_CleanUp = SPAWN:New( "Spawn Vehicle Scheduled CleanUp" ):InitLimit( 3, 100 ):InitRandomizeRoute( 1, 1, 1000 ):SpawnScheduled( 10, 0 ) diff --git a/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz b/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz index 2f9fac4c0..ca7dfb593 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz and b/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz index dd7ef4dec..e8d4f5309 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz and b/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.lua b/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.lua index 207bf362d..7001950cc 100644 --- a/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.lua +++ b/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.lua @@ -21,8 +21,8 @@ do -- Declare SPAWN objects - local Spawn_KA_50 = SPAWN:New("KA-50"):InitLimit( 1, 10 ) - local Spawn_A_10C = SPAWN:New("A-10C"):InitLimit( 1, 10 ) + Spawn_KA_50 = SPAWN:New("KA-50"):InitLimit( 1, 10 ) + Spawn_A_10C = SPAWN:New("A-10C"):InitLimit( 1, 10 ) -- Choose repeat functionality diff --git a/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz b/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz index c9ea6db27..dde395580 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz and b/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-120 - Repeat Spawning/SPA-120 - Repeat Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-120 - Repeat Spawning/SPA-120 - Repeat Spawning.miz new file mode 100644 index 000000000..34bd1c6fc Binary files /dev/null and b/Moose Test Missions/SPA - Spawning/SPA-120 - Repeat Spawning/SPA-120 - Repeat Spawning.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.lua b/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.lua index cfe69b7ba..2d6d11ccd 100644 --- a/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.lua +++ b/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.lua @@ -21,10 +21,10 @@ do -- Declare SPAWN objects - local Spawn_KA_50 = SPAWN:New("KA-50"):InitLimit( 2, 10 ) - local Spawn_MI_8MTV2 = SPAWN:New("MI-8MTV2"):InitLimit( 2, 10 ) - local Spawn_C_101EB = SPAWN:New("C-101EB"):InitLimit( 2, 10 ) - local Spawn_A_10C = SPAWN:New("A-10C"):InitLimit( 2, 10 ) + Spawn_KA_50 = SPAWN:New("KA-50"):InitLimit( 2, 10 ) + Spawn_MI_8MTV2 = SPAWN:New("MI-8MTV2"):InitLimit( 2, 10 ) + Spawn_C_101EB = SPAWN:New("C-101EB"):InitLimit( 2, 10 ) + Spawn_A_10C = SPAWN:New("A-10C"):InitLimit( 2, 10 ) -- Choose repeat functionality @@ -37,10 +37,10 @@ do Spawn_A_10C:InitRepeatOnEngineShutDown() -- Now SPAWN the GROUPs - Spawn_KA_50:SpawnScheduled(30,0) - Spawn_C_101EB:SpawnScheduled(30,0) - Spawn_MI_8MTV2:SpawnScheduled(30,0) - Spawn_A_10C:SpawnScheduled(30,0) + Spawn_KA_50:SpawnScheduled(180,0) + Spawn_C_101EB:SpawnScheduled(180,0) + Spawn_MI_8MTV2:SpawnScheduled(180,0) + Spawn_A_10C:SpawnScheduled(180,0) -- Now run the mission and observe the behaviour. diff --git a/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz b/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz index 88abe9a00..5987ff301 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz and b/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-122 - Air Ops - OnLand test for Scheduled Spawns/SPA-122 - Air Ops - OnLand test for Scheduled Spawns.lua b/Moose Test Missions/SPA - Spawning/SPA-122 - Air Ops - OnLand test for Scheduled Spawns/SPA-122 - Air Ops - OnLand test for Scheduled Spawns.lua new file mode 100644 index 000000000..b2e2028c1 --- /dev/null +++ b/Moose Test Missions/SPA - Spawning/SPA-122 - Air Ops - OnLand test for Scheduled Spawns/SPA-122 - Air Ops - OnLand test for Scheduled Spawns.lua @@ -0,0 +1,47 @@ +--- +-- Name: SPA-122 - Air Ops - OnLand test for Scheduled Spawns +-- Author: FlightControl +-- Date Created: 21 Mar 2017 +-- +-- # Situation: +-- +-- An airplane is spawned at a scheduled interval. +-- There is a limit on how many airplanes can be alive at the same time. +-- Upon landing, the airplane will respawn in the air. +-- +-- # Test cases: +-- +-- 1. Observe the spawning of the airplanes +-- 2. There should not be more airplanes alive than there are set by InitLimit. +-- 3. Upon landing, the planes should respawn. +-- 4. The KA-50 and the C-101EB should respawn itself directly when landed. +-- 5. the MI-8MTV2 and the A-10C should respawn itself when the air unit has parked at the ramp. + + +do + + -- Declare SPAWN objects + Spawn_KA_50 = SPAWN:New("KA-50"):InitLimit( 1, 0 ) + Spawn_MI_8MTV2 = SPAWN:New("MI-8MTV2"):InitLimit( 1, 0 ) + Spawn_C_101EB = SPAWN:New("C-101EB"):InitLimit( 1, 0 ) + Spawn_A_10C = SPAWN:New("A-10C"):InitLimit( 1, 0 ) + + -- Choose repeat functionality + + -- Repeat on landing + Spawn_KA_50:InitRepeatOnLanding() + Spawn_C_101EB:InitRepeatOnLanding() + + -- Repeat on enging shutdown (when landed on the airport) + Spawn_MI_8MTV2:InitRepeatOnEngineShutDown() + Spawn_A_10C:InitRepeatOnEngineShutDown() + + -- Now SPAWN the GROUPs + Spawn_KA_50:SpawnScheduled(30,0) + Spawn_C_101EB:SpawnScheduled(30,0) + Spawn_MI_8MTV2:SpawnScheduled(30,0) + Spawn_A_10C:SpawnScheduled(30,0) + + -- Now run the mission and observe the behaviour. + +end diff --git a/Moose Test Missions/SPA - Spawning/SPA-122 - Air Ops - OnLand test for Scheduled Spawns/SPA-122 - Air Ops - OnLand test for Scheduled Spawns.miz b/Moose Test Missions/SPA - Spawning/SPA-122 - Air Ops - OnLand test for Scheduled Spawns/SPA-122 - Air Ops - OnLand test for Scheduled Spawns.miz new file mode 100644 index 000000000..01b18b695 Binary files /dev/null and b/Moose Test Missions/SPA - Spawning/SPA-122 - Air Ops - OnLand test for Scheduled Spawns/SPA-122 - Air Ops - OnLand test for Scheduled Spawns.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.lua b/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.lua index 49d3f0951..6eb08c792 100644 --- a/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.lua +++ b/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.lua @@ -15,22 +15,22 @@ -- Create the SPAWN object looking for the group (template) "Plane". -local SpawnPlane = SPAWN:New( "Plane" ) +SpawnPlane = SPAWN:New( "Plane" ) -- Set the spawn mode to UnControlled. SpawnPlane:InitUnControlled( true ) -- Spawn the UnControlled Group -local UnControlledPlane = SpawnPlane:Spawn() +UnControlledPlane = SpawnPlane:Spawn() -- Set the spawn mode back to Controlled. SpawnPlane:InitUnControlled( false ) -local ControlledPlane = SpawnPlane:Spawn() +ControlledPlane = SpawnPlane:Spawn() -- Now, let's create a menu option at a player slot plane... -- We can only create the menu option if the player has joined the slot ... -local PlayerPlane = CLIENT:FindByName( "PlayerPlane", "Select Menu item to activate UnControlled plane" ) +PlayerPlane = CLIENT:FindByName( "PlayerPlane", "Select Menu item to activate UnControlled plane" ) PlayerPlane:Alive( function( Client, SpawnPlane ) diff --git a/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz index 4b05c7255..c44a55be7 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz and b/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz b/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz index 93e4cbacd..cc8ee53f9 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz and b/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz b/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz index 14c338f08..7837a0589 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz and b/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.lua b/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-220 - Randomize Zones.lua similarity index 91% rename from Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.lua rename to Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-220 - Randomize Zones.lua index 99388199b..c194c6cb3 100644 --- a/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.lua +++ b/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-220 - Randomize Zones.lua @@ -6,13 +6,13 @@ local Iterations = 100 local Iteration = 1 -- The PolygonGroup route defines zone 1 -local ZonePolygonGroup = GROUP:FindByName( "ZonePolygon" ) +ZonePolygonGroup = GROUP:FindByName( "ZonePolygon" ) -- The ZoneUnit defines zone 4. -local ZoneUnit = UNIT:FindByName( "ZoneUnit" ) +ZoneUnit = UNIT:FindByName( "ZoneUnit" ) -- The ZoneGroup defines zone 5 -local ZoneGroup = GROUP:FindByName( "ZoneGroup" ) +ZoneGroup = GROUP:FindByName( "ZoneGroup" ) -- This is the array that models the different zones types. -- The selection of the zones is done by taking into account the probability of the zone. diff --git a/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz b/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz index f6c18bec7..7469f6d64 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz and b/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz b/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz index 204d65f8c..e56f869b4 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz and b/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz b/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz index 07d54d5d2..cb8538f7f 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz and b/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz b/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz index 25b4bec77..be17d1af6 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz and b/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-350 - Spawn at Vec3 position RandomzePosition/SPA-350 - Spawn at Vec3 position RandomzePosition.miz b/Moose Test Missions/SPA - Spawning/SPA-350 - Spawn at Vec3 position RandomzePosition/SPA-350 - Spawn at Vec3 position RandomzePosition.miz index 6cf5ce6df..5d9b646dd 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-350 - Spawn at Vec3 position RandomzePosition/SPA-350 - Spawn at Vec3 position RandomzePosition.miz and b/Moose Test Missions/SPA - Spawning/SPA-350 - Spawn at Vec3 position RandomzePosition/SPA-350 - Spawn at Vec3 position RandomzePosition.miz differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.lua new file mode 100644 index 000000000..4753f2c73 --- /dev/null +++ b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.lua @@ -0,0 +1,46 @@ +--- +-- Name: TAD-010 - A2G Task Dispatching Destroy Test +-- Author: FlightControl +-- Date Created: 17 Mar 2017 +-- +-- # Situation: +-- +-- This tests if an accepted task successful completion does finish the processes correctly. +-- +-- # Test cases: +-- +-- +HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) + +CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + +Scoring = SCORING:New( "Detect Demo" ) + +Mission = MISSION + :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) + :AddScoring( Scoring ) + +FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() + +FACAreas = DETECTION_UNITS:New( FACSet ) + + +AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() + +TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, AttackGroups, FACAreas ) + +-- Now this is REALLY neat. I set the goal of the mission to be the destruction of Target #004. +-- This is just an example, but many more examples can follow... + +-- Every time a Task becomes Successful, it will trigger the Complete event in the Mission. +-- The mission designer NEED TO OVERRIDE the OnBeforeComplete to prevent the mission from getting into completion +-- too early! + +function Mission:OnBeforeComplete( From, Event, To ) + local Group004 = GROUP:FindByName( "Target #004" ) + if Group004 and Group004:IsAlive() == false then + Mission:GetCommandCenter():MessageToCoalition( "Mission Complete!" ) + return true + end + return false +end diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.miz new file mode 100644 index 000000000..7fbfb0299 Binary files /dev/null and b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.miz differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.lua deleted file mode 100644 index 855ed008d..000000000 --- a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.lua +++ /dev/null @@ -1,15 +0,0 @@ - -local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) - -local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) - -local Scoring = SCORING:New( "Detect Demo" ) - -local Mission = MISSION:New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ):AddScoring( Scoring ) - -local FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() -local FACDetection = DETECTION_AREAS:New( FACSet, 10000, 3000 ) - -local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() -local TaskAssign = DETECTION_DISPATCHER:New( Mission, HQ, AttackGroups, FACDetection ) - diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.miz deleted file mode 100644 index bd664e2a6..000000000 Binary files a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.miz and /dev/null differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.lua new file mode 100644 index 000000000..23f6dc52d --- /dev/null +++ b/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.lua @@ -0,0 +1,35 @@ + --- +-- Name: TAD-100 - A2G Task Dispatching DETECTION_AREAS +-- Author: FlightControl +-- Date Created: 06 Mar 2017 +-- +-- # Situation: +-- +-- This mission demonstrates the dynamic task dispatching for Air to Ground operations. +-- FACA's and FAC's are patrolling around the battle zone, while detecting targets. +-- The detection method used is the DETECTION_AREAS method, which groups detected targets into zones. +-- +-- # Test cases: +-- +-- 1. Observe the FAC(A)'s detecting targets and grouping them. +-- For test, each zone will have a circle of tyres, that are visible on the map too. +-- 2. Check that the HQ provides menus to engage on a task set by the FACs. +-- +HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) + +CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + +Scoring = SCORING:New( "Detect Demo" ) + +Mission = MISSION + :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) + :AddScoring( Scoring ) + +FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() + +FACAreas = DETECTION_AREAS:New( FACSet, 500 ) +FACAreas:BoundDetectedZones() + +AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() +TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, HQ, AttackGroups, FACAreas ) + diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.miz new file mode 100644 index 000000000..079a85e18 Binary files /dev/null and b/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.miz differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.lua new file mode 100644 index 000000000..a7479b92f --- /dev/null +++ b/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.lua @@ -0,0 +1,35 @@ + --- +-- Name: TAD-105 - A2G Task Dispatching DETECTION_AREAS +-- Author: FlightControl +-- Date Created: 12 Mar 2017 +-- +-- # Situation: +-- +-- This mission demonstrates the dynamic task dispatching for Air to Ground operations. +-- FACA's and FAC's are patrolling around the battle zone, while detecting targets. +-- The detection method used is the DETECTION_AREAS method, which groups detected targets into zones. +-- +-- # Test cases: +-- +-- 1. Observe the FAC(A)'s detecting targets and grouping them. +-- For test, each zone will have a circle of tyres, that are visible on the map too. +-- 2. Check that the HQ provides menus to engage on a task set by the FACs. +-- +HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) + +CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + +Scoring = SCORING:New( "Detect Demo" ) + +Mission = MISSION + :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) + :AddScoring( Scoring ) + +FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() + +FACAreas = DETECTION_AREAS:New( FACSet, 500 ) +FACAreas:BoundDetectedZones() + +AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() +TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, HQ, AttackGroups, FACAreas ) + diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.miz new file mode 100644 index 000000000..c78fc040c Binary files /dev/null and b/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.miz differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.lua new file mode 100644 index 000000000..37f7367e6 --- /dev/null +++ b/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.lua @@ -0,0 +1,35 @@ + --- +-- Name: TAD-100 - A2G Task Dispatching DETECTION_AREAS +-- Author: FlightControl +-- Date Created: 06 Mar 2017 +-- +-- # Situation: +-- +-- This mission demonstrates the dynamic task dispatching for Air to Ground operations. +-- FACA's and FAC's are patrolling around the battle zone, while detecting targets. +-- The detection method used is the DETECTION_AREAS method, which groups detected targets into zones. +-- +-- # Test cases: +-- +-- 1. Observe the FAC(A)'s detecting targets and grouping them. +-- For test, each zone will have a circle of tyres, that are visible on the map too. +-- 2. Check that the HQ provides menus to engage on a task set by the FACs. +-- +HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) + +CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + +Scoring = SCORING:New( "Detect Demo" ) + +Mission = MISSION + :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) + :AddScoring( Scoring ) + +FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() + +FACAreas = DETECTION_TYPES:New( FACSet ) + + +AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() +TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, HQ, AttackGroups, FACAreas ) + diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.miz new file mode 100644 index 000000000..f76492202 Binary files /dev/null and b/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.miz differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.lua new file mode 100644 index 000000000..67a481dda --- /dev/null +++ b/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.lua @@ -0,0 +1,50 @@ +--- +-- Name: TAD-120 - A2G Task Dispatching DETECTION_UNITS +-- Author: FlightControl +-- Date Created: 13 Mar 2017 +-- +-- # Situation: +-- +-- This mission demonstrates the dynamic task dispatching for Air to Ground operations. +-- FACA's and FAC's are patrolling around the battle field, while detecting targets. +-- The detection method used is the DETECTION_UNITS method, which groups detected targets per detected unit. +-- +-- # Test cases: +-- +-- 1. Observe the FAC(A)'s detecting targets and grouping them. +-- 2. Check that the HQ provides menus to engage on a task set by the FACs. +-- +HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) + +CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + +Scoring = SCORING:New( "Detect Demo" ) + +Mission = MISSION + :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) + :AddScoring( Scoring ) + +FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() + +FACAreas = DETECTION_UNITS:New( FACSet ) + + +AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() + +TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, AttackGroups, FACAreas ) + +-- Now this is REALLY neat. I set the goal of the mission to be the destruction of Target #004. +-- This is just an example, but many more examples can follow... + +-- Every time a Task becomes Successful, it will trigger the Complete event in the Mission. +-- The mission designer NEED TO OVERRIDE the OnBeforeComplete to prevent the mission from getting into completion +-- too early! + +function Mission:OnBeforeComplete( From, Event, To ) + local Group004 = GROUP:FindByName( "Target #004" ) + if Group004:IsAlive() == false then + Mission:GetCommandCenter():MessageToCoalition( "Mission Complete!" ) + return true + end + return false +end \ No newline at end of file diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.miz new file mode 100644 index 000000000..f5cdc95a4 Binary files /dev/null and b/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.miz differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-200 - A2G Task Dispatching with SCORING/TAD-200 - A2G Task Dispatching with SCORING.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-200 - A2G Task Dispatching with SCORING/TAD-200 - A2G Task Dispatching with SCORING.lua new file mode 100644 index 000000000..4df46302e --- /dev/null +++ b/Moose Test Missions/TAD - Task Dispatching/TAD-200 - A2G Task Dispatching with SCORING/TAD-200 - A2G Task Dispatching with SCORING.lua @@ -0,0 +1,64 @@ +--- +-- Name: TAD-200 - A2G Task Dispatching with SCORING +-- Author: FlightControl +-- Date Created: 19 Mar 2017 +-- +-- # Situation: +-- +-- This mission demonstrates the scoring of dynamic task dispatching for Air to Ground operations. +-- +-- # Test cases: +-- +-- 1. Observe the FAC(A)'s detecting targets and grouping them. +-- 2. Check that the HQ provides menus to engage on a task set by the FACs. +-- 3. Engage on a task and destroy a target. Check if scoring is given for that target. +-- 4. Engage all targets in the task, and check if mission success is achieved and that a scoring is given. +-- 5. Restart the mission, and crash into the ground, check if you can get penalties. +-- +local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) + +local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + +local Scoring = SCORING:New( "Detect Demo" ) + +local Mission = MISSION + :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) + :AddScoring( Scoring ) + +local FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() + +local FACAreas = DETECTION_UNITS:New( FACSet ) + + +local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() + +TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, AttackGroups, FACAreas ) + +--- @param #TaskDispatcher self +-- @param From +-- @param Event +-- @param To +-- @param Tasking.Task_A2G#TASK_A2G Task +-- @param Wrapper.Unit#UNIT TaskUnit +-- @param #string PlayerName +function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName ) + Task:SetScoreOnDestroy( "Player " .. PlayerName .. " destroyed a target", 20, TaskUnit ) + Task:SetScoreOnSuccess( "The task has been successfully completed!", 200, TaskUnit ) + Task:SetPenaltyOnFailed( "The task has failed completion!", -100, TaskUnit ) +end + +-- Now this is REALLY neat. I set the goal of the mission to be the destruction of Target #004. +-- This is just an example, but many more examples can follow... + +-- Every time a Task becomes Successful, it will trigger the Complete event in the Mission. +-- The mission designer NEED TO OVERRIDE the OnBeforeComplete to prevent the mission from getting into completion +-- too early! + +function Mission:OnBeforeComplete( From, Event, To ) + local Group004 = GROUP:FindByName( "Target #004" ) + if Group004:IsAlive() == false then + Mission:GetCommandCenter():MessageToCoalition( "Mission Complete!" ) + return true + end + return false +end \ No newline at end of file diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-200 - A2G Task Dispatching with SCORING/TAD-200 - A2G Task Dispatching with SCORING.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-200 - A2G Task Dispatching with SCORING/TAD-200 - A2G Task Dispatching with SCORING.miz new file mode 100644 index 000000000..79616d9c4 Binary files /dev/null and b/Moose Test Missions/TAD - Task Dispatching/TAD-200 - A2G Task Dispatching with SCORING/TAD-200 - A2G Task Dispatching with SCORING.miz differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-210 - A2G Task Dispatching per AREAS and SCORING/TAD-210 - A2G Task Dispatching per AREAS and SCORING.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-210 - A2G Task Dispatching per AREAS and SCORING/TAD-210 - A2G Task Dispatching per AREAS and SCORING.lua new file mode 100644 index 000000000..2a5427bcc --- /dev/null +++ b/Moose Test Missions/TAD - Task Dispatching/TAD-210 - A2G Task Dispatching per AREAS and SCORING/TAD-210 - A2G Task Dispatching per AREAS and SCORING.lua @@ -0,0 +1,65 @@ +--- +-- Name: TAD-210 - A2G Task Dispatching for AREAS and SCORING +-- Author: FlightControl +-- Date Created: 19 Mar 2017 +-- +-- # Situation: +-- +-- This mission demonstrates the scoring of dynamic task dispatching for Air to Ground operations. +-- +-- # Test cases: +-- +-- 1. Observe the FAC(A)'s detecting targets and grouping them. +-- 2. Check that the HQ provides menus to engage on a task set by the FACs. +-- 3. Engage on a task and destroy a target. Check if scoring is given for that target. +-- 4. Engage all targets in the task, and check if mission success is achieved and that a scoring is given. +-- 5. Restart the mission, and crash into the ground, check if you can get penalties. +-- +local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) + +local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + +local Scoring = SCORING:New( "Detect Demo" ) + +local Mission = MISSION + :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) + :AddScoring( Scoring ) + +local FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() + +local FACAreas = DETECTION_AREAS:New( FACSet, 400 ) +FACAreas:BoundDetectedZones() + + +local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() + +TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, AttackGroups, FACAreas ) + +--- @param #TaskDispatcher self +-- @param From +-- @param Event +-- @param To +-- @param Tasking.Task_A2G#TASK_A2G Task +-- @param Wrapper.Unit#UNIT TaskUnit +-- @param #string PlayerName +function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName ) + Task:SetScoreOnDestroy( "Player " .. PlayerName .. " destroyed a target", 20, TaskUnit ) + Task:SetScoreOnSuccess( "The task has been successfully completed!", 200, TaskUnit ) + Task:SetPenaltyOnFailed( "The task has failed completion!", -100, TaskUnit ) +end + +-- Now this is REALLY neat. I set the goal of the mission to be the destruction of Target #004. +-- This is just an example, but many more examples can follow... + +-- Every time a Task becomes Successful, it will trigger the Complete event in the Mission. +-- The mission designer NEED TO OVERRIDE the OnBeforeComplete to prevent the mission from getting into completion +-- too early! + +function Mission:OnBeforeComplete( From, Event, To ) + local Group004 = GROUP:FindByName( "Target #004" ) + if Group004:IsAlive() == false then + Mission:GetCommandCenter():MessageToCoalition( "Mission Complete!" ) + return true + end + return false +end \ No newline at end of file diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-210 - A2G Task Dispatching per AREAS and SCORING/TAD-210 - A2G Task Dispatching per AREAS and SCORING.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-210 - A2G Task Dispatching per AREAS and SCORING/TAD-210 - A2G Task Dispatching per AREAS and SCORING.miz new file mode 100644 index 000000000..ab99064fc Binary files /dev/null and b/Moose Test Missions/TAD - Task Dispatching/TAD-210 - A2G Task Dispatching per AREAS and SCORING/TAD-210 - A2G Task Dispatching per AREAS and SCORING.miz differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-220 - A2G Task Dispatching per TYPE and SCORING/TAD-220 - A2G Task Dispatching per TYPE and SCORING.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-220 - A2G Task Dispatching per TYPE and SCORING/TAD-220 - A2G Task Dispatching per TYPE and SCORING.lua new file mode 100644 index 000000000..7ebd6c1ac --- /dev/null +++ b/Moose Test Missions/TAD - Task Dispatching/TAD-220 - A2G Task Dispatching per TYPE and SCORING/TAD-220 - A2G Task Dispatching per TYPE and SCORING.lua @@ -0,0 +1,64 @@ +--- +-- Name: TAD-220 - A2G Task Dispatching per TYPE and SCORING +-- Author: FlightControl +-- Date Created: 20 Mar 2017 +-- +-- # Situation: +-- +-- This mission demonstrates the scoring of dynamic task dispatching for Air to Ground operations. +-- +-- # Test cases: +-- +-- 1. Observe the FAC(A)'s detecting targets and grouping them. +-- 2. Check that the HQ provides menus to engage on a task set by the FACs. +-- 3. Engage on a task and destroy a target. Check if scoring is given for that target. +-- 4. Engage all targets in the task, and check if mission success is achieved and that a scoring is given. +-- 5. Restart the mission, and crash into the ground, check if you can get penalties. +-- +local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) + +local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + +local Scoring = SCORING:New( "Detect Demo" ) + +local Mission = MISSION + :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) + :AddScoring( Scoring ) + +local FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() + +local DetectionTypes = DETECTION_TYPES:New( FACSet ) + +local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() + +TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, AttackGroups, DetectionTypes ) + +--- @param #TaskDispatcher self +-- @param From +-- @param Event +-- @param To +-- @param Tasking.Task_A2G#TASK_A2G Task +-- @param Wrapper.Unit#UNIT TaskUnit +-- @param #string PlayerName +function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName ) + self:E( "I am in assigned ... " ) + Task:SetScoreOnDestroy( "Player " .. PlayerName .. " destroyed a target", 50, TaskUnit ) + Task:SetScoreOnSuccess( "The task has been successfully completed!", 200, TaskUnit ) + Task:SetPenaltyOnFailed( "The task has failed completion!", 100, TaskUnit ) +end + +-- Now this is REALLY neat. I set the goal of the mission to be the destruction of Target #004. +-- This is just an example, but many more examples can follow... + +-- Every time a Task becomes Successful, it will trigger the Complete event in the Mission. +-- The mission designer NEED TO OVERRIDE the OnBeforeComplete to prevent the mission from getting into completion +-- too early! + +function Mission:OnBeforeComplete( From, Event, To ) + local Group004 = GROUP:FindByName( "Target #004" ) + if Group004:IsAlive() == false then + Mission:GetCommandCenter():MessageToCoalition( "Mission Complete!" ) + return true + end + return false +end \ No newline at end of file diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-220 - A2G Task Dispatching per TYPE and SCORING/TAD-220 - A2G Task Dispatching per TYPE and SCORING.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-220 - A2G Task Dispatching per TYPE and SCORING/TAD-220 - A2G Task Dispatching per TYPE and SCORING.miz new file mode 100644 index 000000000..dd66cc842 Binary files /dev/null and b/Moose Test Missions/TAD - Task Dispatching/TAD-220 - A2G Task Dispatching per TYPE and SCORING/TAD-220 - A2G Task Dispatching per TYPE and SCORING.miz differ diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.lua b/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.lua index f9b7e8f7f..d51287027 100644 --- a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.lua +++ b/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.lua @@ -88,7 +88,10 @@ SEADSet:Flush() -- Define the set of units that are the targets. -- Note that I use FilterOnce, which means that the set will be defined only once, -- and will not be continuously updated! -local TargetSet = SET_UNIT:New():FilterPrefixes( "US Hawk SR" ):FilterOnce() +local TargetSet = SET_UNIT:New():FilterPrefixes( "US Hawk SR" ):FilterStart() + +-- Define the RendezVous Zone where the pilot needs to RendezVous with other players before engaging. +local RendezVousZone = ZONE:New( "RendezVous Zone" ) -- Define the zone to where the pilot needs to navigate. local TargetZone = ZONE:New( "Target Zone" ) @@ -103,12 +106,29 @@ local TargetZone = ZONE:New( "Target Zone" ) -- 1. The Mission for which the Task needs to be achieved. -- 2. The set of groups of planes that pilots can join. -- 3. The name of the Task... This can be any name, and will be provided when the Pilot joins the task. --- 4. A type of the Task. When Tasks are in state Planned, then a menu can be provided that group the task based on this given type. -local SEADTask = TASK:New( - Mission, - SEADSet, - "SEAD Radars Vector 1", - "SEAD" ) -- Tasking.Task#TASK +-- 4. A set of Targets for the Task. + +-- We are going to create a couple of variations of TASK_SEAD... + +-- A TASK_SEAD that will route the player towards a Rendez-Vous point, and once arrived, route the player to the Target Zone. +local SEADTaskRendezVousPoint = TASK_SEAD:New( Mission, SEADSet, "Route to Rendez-Vous Point, then route to Target Zone", TargetSet ) -- Tasking.Task_SEAD#TASK_SEAD +SEADTaskRendezVousPoint:SetRendezVousPointVec2( RendezVousZone:GetPointVec2(), 6000 ) -- Done to test the RendezVousPointVec2 mechanism. +SEADTaskRendezVousPoint:SetTargetZone( TargetZone ) + +-- A TASK_SEAD that will route the player towards a Rendez-Vous zone, +-- and once arrived, route the player to the Target Zone. +local SEADTaskRendezVousZone = TASK_SEAD:New( Mission, SEADSet, "Route to Rendez-Vous Zone, then route to Target Zone", TargetSet ) -- Tasking.Task_SEAD#TASK_SEAD +SEADTaskRendezVousZone:SetRendezVousZone( RendezVousZone ) -- Done to test the Route to Zone mechanism. +SEADTaskRendezVousZone:SetTargetZone( TargetZone ) + +-- A TASK_SEAD that has no Rendez_vous, +-- and routes the player straight to the Target Zone. +local SEADTaskToTargetZone = TASK_SEAD:New( Mission, SEADSet, "Route to Target Zone", TargetSet ) -- Tasking.Task_SEAD#TASK_SEAD +SEADTaskToTargetZone:SetTargetZone( TargetZone ) + +-- A TASK_SEAD that has no Rendez_vous, +-- and routes the player to each Target in the Set. +local SEADTaskToTargetNoZone = TASK_SEAD:New( Mission, SEADSet, "Route to Target per Target", TargetSet ) -- Tasking.Task_SEAD#TASK_SEAD -- This is now an important part of the Task process definition. -- Each TASK contains a "Process Template". @@ -120,78 +140,36 @@ local SEADTask = TASK:New( -- The reason why this is done, is that each unit as a role within the Task, and can have different status. -- Therefore, the FsmSEAD is a TEMPLATE PROCESS of the TASK, and must be designed as a UNIT with a player is executing that PROCESS. -local SEADProcess = SEADTask:GetUnitProcess() -- #SEADProcess +local SEADProcess = SEADTaskToTargetNoZone:GetUnitProcess() -- #SEADProcess --- Adding a new sub-process to the Task Template. --- At first, the task needs to be accepted by a pilot. --- We use for this the SUB-PROCESS ACT_ASSIGN_ACCEPT. --- The method on the FsmSEAD AddProcess accepts the following parameters: --- 1. State From "Planned". When the Fsm is in state "Planned", allow the event "Accept". --- 2. Event "Accept". This event can be triggered through FsmSEAD:Accept() or FsmSEAD:__Accept( 1 ). See documentation on state machines. --- 3. The PROCESS derived class. In this case, we use the ACT_ASSIGN_ACCEPT to accept the task and provide a briefing. So, when the event "Accept" is fired, this process is executed. --- 4. A table with the "return" states of the ACT_ASSIGN_ACCEPT process. This table indicates that for a certain return state, a further event needs to be called. --- 4.1 When the return state is Assigned, fire the event in the Task FsmSEAD:Route() --- 4.2 When the return state is Rejected, fire the event in the Task FsmSEAD:Eject() --- All other AddProcess calls are working in a similar manner. - -SEADProcess:AddProcess( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "SEAD the Area" ), { Assigned = "Route", Rejected = "Eject" } ) -- FSM SUB for type SEADProcess. - -SEADProcess:AddProcess( "Assigned", "Route", ACT_ROUTE_ZONE:New( TargetZone ), { Arrived = "Update" } ) -- FSM SUB for type SEADProcess. - --- Adding a new Action... --- Actions define also the flow of the Task, but the actions will need to be programmed within your script. --- See the state machine explanation for further details. --- The AddTransition received a couple of parameters: --- 1. State From "Rejected". When the FsmSEAD is in state "Rejected", the event "Eject" can be fired. --- 2. Event "Eject". This event can be triggered synchronously through FsmSEAD:Eject() or asynchronously through FsmSEAD:__Eject(secs). --- 3. State To "Planned". After the event has been fired, the FsmSEAD will transition to Planned. - -SEADProcess:AddTransition( "Rejected", "Eject", "Planned" ) - -SEADProcess:AddTransition( "Arrived", "Update", "Updated" ) - -SEADProcess:AddProcess( "Updated", "Account", ACT_ACCOUNT_DEADS:New( TargetSet, "SEAD" ), { Accounted = "Success" } ) - -SEADProcess:AddProcess( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( TargetSet, TargetZone ) ) - -SEADProcess:AddTransition( "Accounted", "Success", "Success" ) - -SEADProcess:AddTransition( "*", "Fail", "Failed" ) - -SEADProcess:AddScoreProcess( "Updated", "Account", "Account", "destroyed a radar", 25 ) -SEADProcess:AddScoreProcess( "Updated", "Account", "Failed", "failed to destroy a radar", -10 ) +SEADProcess:AddScoreProcess( "Engaging", "Account", "Account", "destroyed a radar", 25 ) +SEADProcess:AddScoreProcess( "Engaging", "Account", "Failed", "failed to destroy a radar", -10 ) -- Now we will set the SCORING. Scoring is set using the TaskSEAD object. -- Scores can be set on the status of the Task, and on Process level. SEADProcess:AddScore( "Success", "Destroyed all target radars", 250 ) SEADProcess:AddScore( "Failed", "Failed to destroy all target radars", -100 ) -function SEADProcess:OnEnterUpdated( Controllable, From, Event, To ) - self:E( { self } ) - self:Account() - self:Smoke() -end - -- Here we handle the PlayerAborted event, which is fired when a Player leaves the unit while being assigned to the Task. -- Within the event handler, which is passed the PlayerUnit and PlayerName parameter, -- we check if the SEADTask has still AlivePlayers assigned to the Task. -- If not, the Task will Abort. -- And it will be Replanned within 30 seconds. -function SEADTask:OnEnterPlayerCrashed( PlayerUnit, PlayerName ) - if not SEADTask:HasAliveUnits() then - SEADTask:__Abort() +function SEADTaskToTargetNoZone:OnEnterPlayerCrashed( PlayerUnit, PlayerName ) + if not SEADTaskToTargetNoZone:HasAliveUnits() then + SEADTaskToTargetNoZone:__Abort() end end -local TaskSEAD2 = TASK:New( Mission, SEADSet, "SEAD Radars Vector 2", "SEAD" ) -- Tasking.Task#TASK -TaskSEAD2:SetUnitProcess( SEADTask:GetUnitProcess():Copy() ) -Mission:AddTask( TaskSEAD2 ) - -Mission:RemoveTask( SEADTask ) - -SEADTask = nil -SEADProcess = nil +--local TaskSEAD2 = TASK:New( Mission, SEADSet, "SEAD Radars Vector 2", "SEAD" ) -- Tasking.Task#TASK +--TaskSEAD2:SetUnitProcess( SEADTask:GetUnitProcess():Copy() ) +--Mission:AddTask( TaskSEAD2 ) +-- +--Mission:RemoveTask( SEADTask ) +-- +--SEADTask = nil +--SEADProcess = nil collectgarbage() diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz b/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz index 3912d4a9c..14629c7b9 100644 Binary files a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz and b/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz differ diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz b/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz index c5601b2b5..3f3a50344 100644 Binary files a/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz and b/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.lua b/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.lua index 3eb735f3b..82baa1977 100644 --- a/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.lua +++ b/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.lua @@ -15,10 +15,10 @@ -- 2. Observe the vehicle smoking a red smoke when driving through the zone. -local GroupInside = GROUP:FindByName( "Test Inside Polygon" ) -local GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) +GroupInside = GROUP:FindByName( "Test Inside Polygon" ) +GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) -local ZoneA = ZONE:New( "Zone A" ) +ZoneA = ZONE:New( "Zone A" ) ZoneA:SmokeZone( SMOKECOLOR.White, 90 ) Messager = SCHEDULER:New( nil, diff --git a/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz b/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz index 45bf7e431..175545901 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.lua b/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.lua index ed9dd3595..60f255fcd 100644 --- a/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.lua +++ b/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.lua @@ -19,9 +19,9 @@ -- 2. Observe smoking of Orange smoke in Zone 2. -- 3. Observe smoking of Red smoke in Zone 3. -local Zone1 = ZONE:New( "Zone 1" ) -local Zone2 = ZONE:New( "Zone 2" ) -local Zone3 = ZONE:New( "Zone 3" ) +Zone1 = ZONE:New( "Zone 1" ) +Zone2 = ZONE:New( "Zone 2" ) +Zone3 = ZONE:New( "Zone 3" ) Zone1:SmokeZone( SMOKECOLOR.White, 18 ) Zone2:SmokeZone( SMOKECOLOR.White, 18 ) diff --git a/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz index a732c6b44..93221c846 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.lua b/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.lua index 6e81041d6..361f2a7f9 100644 --- a/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.lua +++ b/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.lua @@ -15,10 +15,10 @@ -- 2. Observe the vehicle smoking a red smoke when driving through the zone. -local GroupInside = GROUP:FindByName( "Test Inside Polygon" ) -local GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) +GroupInside = GROUP:FindByName( "Test Inside Polygon" ) +GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) -local ZoneA = ZONE:New( "Zone A" ) +ZoneA = ZONE:New( "Zone A" ) ZoneA:BoundZone( 90 ) Messager = SCHEDULER:New( nil, diff --git a/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.miz b/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.miz index 29ac59298..325a3ce98 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.miz and b/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.lua b/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.lua index c1f996278..c59865b09 100644 --- a/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.lua +++ b/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.lua @@ -14,11 +14,11 @@ -- 1. Observe the zone perimeter smoke. -- 2. Observe the vehicle smoking a red smoke when driving through the zone. -local GroupInside = GROUP:FindByName( "Test Inside Polygon" ) -local GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) +GroupInside = GROUP:FindByName( "Test Inside Polygon" ) +GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) -local Tank = GROUP:FindByName( "Tank" ) -local ZoneA = ZONE_GROUP:New( "Zone A", Tank, 100 ) +Tank = GROUP:FindByName( "Tank" ) +ZoneA = ZONE_GROUP:New( "Zone A", Tank, 100 ) Messager = SCHEDULER:New( nil, function() diff --git a/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz b/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz index 183dd2550..46f458a43 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.lua b/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.lua index e20b5913d..38d073689 100644 --- a/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.lua +++ b/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.lua @@ -19,9 +19,9 @@ -- 2. Observe smoking of Orange smoke in Zone 2. -- 3. Observe smoking of Red smoke in Zone 3. -local Zone1 = ZONE_GROUP:New( "Zone 1", GROUP:FindByName( "Zone 1" ), 300 ) -local Zone2 = ZONE_GROUP:New( "Zone 2", GROUP:FindByName( "Zone 2" ), 300 ) -local Zone3 = ZONE_GROUP:New( "Zone 3", GROUP:FindByName( "Zone 3" ), 300 ) +Zone1 = ZONE_GROUP:New( "Zone 1", GROUP:FindByName( "Zone 1" ), 300 ) +Zone2 = ZONE_GROUP:New( "Zone 2", GROUP:FindByName( "Zone 2" ), 300 ) +Zone3 = ZONE_GROUP:New( "Zone 3", GROUP:FindByName( "Zone 3" ), 300 ) Zone1:SmokeZone( SMOKECOLOR.White, 18 ) Zone2:SmokeZone( SMOKECOLOR.White, 18 ) diff --git a/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz index 0aced7bfe..156ce8687 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.lua b/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.lua index 059af186d..e27334e35 100644 --- a/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.lua +++ b/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.lua @@ -14,11 +14,11 @@ -- 1. Observe the zone perimeter smoke. -- 2. Observe the vehicle smoking a red smoke when driving through the zone. -local GroupInside = GROUP:FindByName( "Test Inside Polygon" ) -local GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) +GroupInside = GROUP:FindByName( "Test Inside Polygon" ) +GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) -local Tank = UNIT:FindByName( "Tank" ) -local ZoneA = ZONE_UNIT:New( "Zone A", Tank, 100 ) +Tank = UNIT:FindByName( "Tank" ) +ZoneA = ZONE_UNIT:New( "Zone A", Tank, 100 ) Messager = SCHEDULER:New( nil, function() diff --git a/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz b/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz index 6d7a52222..454bf3947 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.lua b/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.lua index 95a38c69b..db53722a8 100644 --- a/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.lua +++ b/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.lua @@ -19,9 +19,9 @@ -- 2. Observe smoking of Orange smoke in Zone 2. -- 3. Observe smoking of Red smoke in Zone 3. -local Zone1 = ZONE_UNIT:New( "Zone 1", UNIT:FindByName( "Zone 1" ), 300 ) -local Zone2 = ZONE_UNIT:New( "Zone 2", UNIT:FindByName( "Zone 2" ), 300 ) -local Zone3 = ZONE_UNIT:New( "Zone 3", UNIT:FindByName( "Zone 3" ), 300 ) +Zone1 = ZONE_UNIT:New( "Zone 1", UNIT:FindByName( "Zone 1" ), 300 ) +Zone2 = ZONE_UNIT:New( "Zone 2", UNIT:FindByName( "Zone 2" ), 300 ) +Zone3 = ZONE_UNIT:New( "Zone 3", UNIT:FindByName( "Zone 3" ), 300 ) Zone1:SmokeZone( SMOKECOLOR.White, 18 ) Zone2:SmokeZone( SMOKECOLOR.White, 18 ) diff --git a/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz index cfded0ba3..79d8438ea 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.lua b/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.lua index bb887fe4b..c73a6360e 100644 --- a/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.lua +++ b/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.lua @@ -14,11 +14,11 @@ -- 1. Observe the polygon perimeter smoke. -- 2. Observe the vehicle smoking a red smoke when driving through the zone. -local GroupInside = GROUP:FindByName( "Test Inside Polygon" ) -local GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) +GroupInside = GROUP:FindByName( "Test Inside Polygon" ) +GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) -local House = STATIC:FindByName( "House" ) -local ZoneA = ZONE_RADIUS:New( "Zone A", House:GetVec2(), 300 ) +House = STATIC:FindByName( "House" ) +ZoneA = ZONE_RADIUS:New( "Zone A", House:GetVec2(), 300 ) ZoneA:SmokeZone( SMOKECOLOR.White, 90 ) Messager = SCHEDULER:New( nil, diff --git a/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz b/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz index f949bb170..6922a5501 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.lua b/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.lua index 62857df37..b3d511267 100644 --- a/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.lua +++ b/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.lua @@ -19,14 +19,14 @@ -- 2. Observe smoking of Orange smoke in Zone 2. -- 3. Observe smoking of Red smoke in Zone 3. -local Unit1 = UNIT:FindByName( "Zone 1" ) -local Unit2 = UNIT:FindByName( "Zone 2" ) -local Unit3 = UNIT:FindByName( "Zone 3" ) +Unit1 = UNIT:FindByName( "Zone 1" ) +Unit2 = UNIT:FindByName( "Zone 2" ) +Unit3 = UNIT:FindByName( "Zone 3" ) -local Zone1 = ZONE_RADIUS:New( "Zone 1", Unit1:GetVec2(), 300 ) -local Zone2 = ZONE_RADIUS:New( "Zone 2", Unit2:GetVec2(), 300 ) -local Zone3 = ZONE_RADIUS:New( "Zone 3", Unit3:GetVec2(), 300 ) +Zone1 = ZONE_RADIUS:New( "Zone 1", Unit1:GetVec2(), 300 ) +Zone2 = ZONE_RADIUS:New( "Zone 2", Unit2:GetVec2(), 300 ) +Zone3 = ZONE_RADIUS:New( "Zone 3", Unit3:GetVec2(), 300 ) Zone1:SmokeZone( SMOKECOLOR.White, 18 ) Zone2:SmokeZone( SMOKECOLOR.White, 18 ) diff --git a/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz index c71270a4d..9092b58e2 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.lua b/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.lua index 0ca6fc8c7..b5201c1f0 100644 --- a/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.lua +++ b/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.lua @@ -14,12 +14,12 @@ -- 1. Observe the polygon perimeter smoke. -- 2. Observe the vehicle smoking a red smoke when driving through the zone. -local GroupInside = GROUP:FindByName( "Test Inside Polygon" ) -local GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) +GroupInside = GROUP:FindByName( "Test Inside Polygon" ) +GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) -local GroupPolygon = GROUP:FindByName( "Polygon A" ) +GroupPolygon = GROUP:FindByName( "Polygon A" ) -local PolygonZone = ZONE_POLYGON:New( "Polygon A", GroupPolygon ) +PolygonZone = ZONE_POLYGON:New( "Polygon A", GroupPolygon ) PolygonZone:SmokeZone( SMOKECOLOR.White, 20 ) Messager = SCHEDULER:New( nil, diff --git a/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz b/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz index 9beaf5c5d..f346cd547 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.lua b/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.lua index 451aec542..545c7d9b6 100644 --- a/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.lua +++ b/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.lua @@ -19,9 +19,9 @@ -- 2. Observe smoking of Orange smoke in Zone 2. -- 3. Observe smoking of Red smoke in Zone 3. -local Zone1 = ZONE_POLYGON:New( "Zone 1", GROUP:FindByName( "Zone 1" ) ) -local Zone2 = ZONE_POLYGON:New( "Zone 2", GROUP:FindByName( "Zone 2" ) ) -local Zone3 = ZONE_POLYGON:New( "Zone 3", GROUP:FindByName( "Zone 3" ) ) +Zone1 = ZONE_POLYGON:New( "Zone 1", GROUP:FindByName( "Zone 1" ) ) +Zone2 = ZONE_POLYGON:New( "Zone 2", GROUP:FindByName( "Zone 2" ) ) +Zone3 = ZONE_POLYGON:New( "Zone 3", GROUP:FindByName( "Zone 3" ) ) Zone1:SmokeZone( SMOKECOLOR.White, 4 ) Zone2:SmokeZone( SMOKECOLOR.White, 4 ) diff --git a/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz index 221bf4ee2..9d1d25461 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.lua b/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.lua index 6fad76983..c3075153c 100644 --- a/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.lua +++ b/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.lua @@ -14,12 +14,12 @@ -- 1. Observe the polygon perimeter smoke. -- 2. Observe the vehicle smoking a red smoke when driving through the zone. -local GroupInside = GROUP:FindByName( "Test Inside Polygon" ) -local GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) +GroupInside = GROUP:FindByName( "Test Inside Polygon" ) +GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) -local GroupPolygon = GROUP:FindByName( "Polygon A" ) +GroupPolygon = GROUP:FindByName( "Polygon A" ) -local PolygonZone = ZONE_POLYGON:New( "Polygon A", GroupPolygon ) +PolygonZone = ZONE_POLYGON:New( "Polygon A", GroupPolygon ) PolygonZone:BoundZone() Messager = SCHEDULER:New( nil, diff --git a/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.miz b/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.miz index 5e365217c..8aa3dd0ba 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.miz and b/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.lua b/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.lua index 2216f6efa..2cb69852d 100644 --- a/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.lua +++ b/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.lua @@ -17,18 +17,18 @@ -- Get the GROUP wrapper objects that were created by MOOSE at mission startup, by using the GROUP:FindByName() method. -- The Group name is the parameter to be searched for. -- Note that late activated groups are also "alive" and have a corresponding GROUP object in the running mission. -local PolyZoneGroup1 = GROUP:FindByName("PolyZone1") -local PolyZoneGroup2 = GROUP:FindByName("PolyZone2") +PolyZoneGroup1 = GROUP:FindByName("PolyZone1") +PolyZoneGroup2 = GROUP:FindByName("PolyZone2") -- Create 2 Polygon objects, using the ZONE_POLYGON:New constructor. -- The first parameter gives a name to the zone, the second is the GROUP object that defines the zone form. -local PolyZone1 = ZONE_POLYGON:New( "PolyZone1", PolyZoneGroup1 ) -local PolyZone2 = ZONE_POLYGON:New( "PolyZone2", PolyZoneGroup2 ) +PolyZone1 = ZONE_POLYGON:New( "PolyZone1", PolyZoneGroup1 ) +PolyZone2 = ZONE_POLYGON:New( "PolyZone2", PolyZoneGroup2 ) -- Create a SET of Moose CLIENT wrapper objects. At mission startup, a SET of Moose client wrapper objects is created. -- Note that CLIENT objects don't necessarily need to be alive!!! -- So this set contains EVERY RED coalition client defined within the mission. -local RedClients = SET_CLIENT:New():FilterCoalitions("red"):FilterStart() +RedClients = SET_CLIENT:New():FilterCoalitions("red"):FilterStart() diff --git a/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz b/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz index 53974b022..adf919376 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz and b/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz differ diff --git a/README.md b/README.md index 93ca78473..640c22579 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ MOOSE is a **M**ission **O**bject **O**riented **S**cripting **E**nvironment, an It allows to quickly setup complex missions using pre-scripted scenarios using the available classes within the MOOSE Framework. MOOSE works with DCS world 1.5. and 2.0. -## MOOSE framework goal +## MOOSE Framework Goal The goal of MOOSE is to allow mission designers to enhance their scripting with mission orchestration objects, which can be instantiated from defined classes within the framework. This will allow to write mission scripts with minimal code embedded. Of course, the richness of the framework will determine the richness of the misson scenarios. The MOOSE is a service that is produced while being consumed ... , it will evolve further as more classes are developed for the framework, and as more users are using it. @@ -12,6 +12,21 @@ MOOSE is not a one-man show, it is a collaborative effort and meant to evolve wi Within the community, key users will start supporting, documenting, explaining and even creating new classes for the framework. It is the ambition to grow this framework as a de-facto standard for mission designers to use. -## MOOSE main site +## MOOSE Main Site [Click on this link to browse to the MOOSE main web page.](http://flightcontrol-master.github.io/MOOSE) +Documentation on the MOOSE class hierarchy, usage guides and background information can be found here for normal users, beta testers and contributors. + +## MOOSE Missions + +MOOSE comes with [demonstration missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS) that you can use to understand the mechanisms how to use the classes within MOOSE. + +## MOOSE Youtube Channel + +MOOSE has a [broadcast and training channel on YouTube](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) with various channels that you can watch. + +## MOOSE on [Slack.Com](https://flightcontrol.slack.com) + +MOOSE has a living (chat and video) community of users, beta testers and contributors. The gathering point is a service provided by slack.com. +If you want to join this community, send an email to flightcontrol_moose@outlook.com. You'll receive the invitation from slack, follow the process +and you'll be on board in no time. diff --git a/docs/Beta_Test_Guide.md b/docs/Beta_Test_Guide.md new file mode 100644 index 000000000..fa5590b83 --- /dev/null +++ b/docs/Beta_Test_Guide.md @@ -0,0 +1,149 @@ +*** UNDER CONSTRUCTION *** + +You are interrested in **testing the bleeding edge functionnalities** and features added by contributors every day, and you are **not afraid of bugs** that will inevitably crop up from time to time ? Then this page is for you ! You are going to learn : + +1. How to set up your development environment +2. How is the Moose repository organized +3. How Moose is loaded in the missions and how this can make your workflow more efficient +4. How to use tools to process your .miz files efficiently +5. How Moose's release cycle work +6. How the Issue Tracker is managed + +This might be a bit daunting at first, but the rewards in term of simplified workflow and direct access to new features is well worth the effort! If you have any problem during the setup or at any other point, don't hesitate to ask the [community](Communities) for help! + +This guide assumes that **you already setup your development environment**, like a "normal" mission designer. Which means Lua Development Tools (LDT) is installed on your computer, and you already know how to create a mission using MOOSE. If it is not the case, please follow the instructions [here](http://flightcontrol-master.github.io/MOOSE/Usage_Guide.html) before proceeding. + +# 1) Installation + +## 1.1) Get your MOOSE repository installed on your PC and linked with GITHUB + +### 1.1.1) Install GITHUB desktop + +Install [GITHUB](https://desktop.github.com) desktop. +Since the MOOSE code is evolving very rapidely between Releases, we store the MOOSE code on GitHub, and we use the GitHUb to sync it between the remote repository (the "origin") and your local MOOSE repository. That way, only one click is needed to update to the latest version of GitHub. + +### 1.1.2) Link the MOOSE repository + +Link the MOOSE repository on GITHUB to your freshly installed GITHUB desktop. +This is done by browing to the [MOOSE repository at GITHUB](https://github.com/FlightControl-Master/MOOSE), and selecting the green button **Clone or Download** -> **Open in Desktop**. +![](Installation/GitHub_Clone.jpg) + +Specify a local directory on your PC where you want to store the MOOSE repository contents. +Sync the MOOSE repository to a defined local MOOSE directory on your PC using GITHUB desktop (press the sync button). +You now have a copy of the code on computer, which you can update at any time by simply pressing the sync button. + +### 1.2) Install 7-Zip + +Install [7-Zip](http://www.7-zip.org/) if you don't already have it. It is a free and open source file archiver program. Since DCS' .miz files are simply renamed .zip files, 7-Zip is very usefull to manimulate them. We are providing the MOOSE testers and contributors with tools to batch process their .miz files, and they rely on 7-Zip. Keep the path to your 7-Zip installation handy, it will be use in the next step ! + +### 1.3) Run the Install script + +Because DCS is going to load Moose dynamically (more on that later), we need to do some (slightly) advanced stuff to finish the setup of your own development enviroment. Thankfully we wrote a program to do it automatically for you ! +![](Installation/MDES_Splash_Screen.JPG) + +Browse to your local MOOSE repository and run `Moose Development Environment Setup\MooseDevelopmentEnvironmentSetup.exe` **as an administrator** (Select the file > Right Click > Run as administrator). + +* The splash screen opens, click ok +* Enter (or browse for) the 3 paths asked and click ok. Don't worry about trailing backslashs. +* Let the program do its magic ! +* When the program finishes, it will inform you that you need to restart your computer to use the .miz files management tools. + +If you encounter a problem during this installation, please contact the [community](Communities), with the mdes.log file which was generated next to the executable file. We'll try our best to help you! + +_Wait, I'm not running a program randomly found on the internet like that. I don't even know what it does, and why does it have to be run as an administartor anyway?!_ +And you shouldn't. Here is the explanation of what this tool does (but the explanation is a bit technical, your are warned!): + +* Create a hard link between your local repository and `DCSWorld/Scripts/` +* Add 7-Zip to your PATH environment variable (this explains the restart requirement) +* Copy a precompiled version of Lua 5.1 to your `Program Files` (this explains the administrator priviledge requirement) + +The executable is made with AutoIt, its script is available near the executable. Open it as a text file if you want to know what it does. If you are still reluctant to do this, the whole process can be done manually by experienced users, get in touch with the [community](Communities)! + +# 2) MOOSE Directory Structure + +***TDOD : Update this section because of the repo changes*** + +The MOOSE framework is devided into a couple of directories: + +* Moose Development: Contains the collection of lua files that define the MOOSE classes. You can use this directory to build the dynamic luadoc documentation intellisense in your eclipse development environment. +* Moose Mission Setup: Contains the Moose.lua file to be included in your scripts when using MOOSE classes (see below the point Mission Design with Moose). +* Moose Test Missions: Contains a directory structure with Moose Test Missions and examples. In each directory, you will find a miz file and a lua file containing the main mission script. +* Moose Training: Contains the documentation of Moose generated with luadoc from the Moose source code. The presentations used during the videos in my [youtube channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg), are also to be found here. + +# 3) Static Loading vs Dynamic Loading + +## 3.1) Static Loading + +**Moose static loading** is what the "normal" mission designer uses. Simply put, there **is a tool which concatenates every .lua file** which constitutes Moose **into just one: Moose.lua**. This is the file which is loaded in Mission Editorr by the mission designer. +This process is very useful **when you are using a stable Release** of Moose which don't change often, because it is really easy to set up for the mission designer. It also allows him to **release missions** which are contained in their entirety in the .miz file. +But in a context in wich Moose changes often, static loading would require the generation of a new Moose.lua for every change and the replacement the old Moose.lua in the .miz file you are using to test the changes. Add to this cumbersome process the fact that the Mission Editor doesn't like changes to the .miz file while it is open, which means you would need to close and reopen the Mission Editor for every change, and this process becomes unworkable for both the tester and the contributor. + +## 3.2) Dynamic Loading + +Enter **Moose Dynamic loading**. In this process, the **Moose.lua** you insert in your .miz file **looks for every .lua** which constitute Moose in `DCSWorld\Scripts`, **and asks DCS to load them** during the mission startup. This way, the latest changes to Moose's .lua files in `DCSWorld\Scripts` are automatically taken into account when you restart the mission, no need to fiddle around with the .miz file or to close the mission editor! +Now, there is still a problem left : you wouldn't want to have to copy the Moose's .lua files from your local repository to `DCSWorld\Scripts` everytime you retrieve a new version of Moose. The solution to this problem is a dynamic link! It is created by the Install Scipt (see above), and, simply put, makes sure that the folder `DCSWorld\Scripts\Moose` is always in sync with your local repository. That way, **everytime you want to update to the next Moose, you simply sync your local repository** with the remote with GitHub, **and restart your mission** ! +Note that if you want to **release your missions to end users**, you will need to make it **use the static loading process**. There is a tool to automate this task, read below. + +# 4) Tools to help you manage your .miz files + +***TODO : Add a list of the .miz files tools a briefly explain their usages*** + +# 5) The release cycle + +To ensure that the next Release of Moose is as bug-free and feature rich as possible, every Moose contributor respects a release cycle. +![](Installation/MOOSE_Release_Cycle.JPG) + +If you are not familiar with Git, this might be a bit criptic, but the idea behind it is simple : + +* Most of the time, contributors write code, be it features or bug fixes, and the testers report bugs. +* When a good amount of features are added to the last release, FlightControl decides to enter in a "Feature Freeze" period. **No new features are added** during this period (but they can still be worked on), and **every bug must be eliminated**. That is the period during which bug reports are paramount! +* Now that this version of **Moose is stable**, we can release it for every mission designer, and go back to the start for the next cycle. + + +# 6) The Issue Tracker + +## 6.1) How to report a bug ? + +If you **encounter** what seem to be a **bug**, which is bound to happen sooner or later since you are testing the brand new untested features of Moose, you will want to **report it** so that it can be solved by the contributors. We use the standard GitHub [Issue Tracker](https://github.com/FlightControl-Master/MOOSE/issues). Here is the process to create a new issue : + +* First, do a quick **search on the issue tracker** to see if someone hasn't already reported your issue. If it is the case, comment on this issue that you are able to reproduce it, and add the informations listed below. +* **Create a new issue**. +* **Add the information** listed below +* Add the **lablel possible bug** +* Thank you for helping us make Moose better! + +**_Informations we need to solve the issue_** + +* **A descriptive title**, not too long if possible. For exemple, `CTD when calling SPAWN:New()` is a good title. `I found a bug in MOOSE` is not. +* Explain **what you are trying to do**, as well as **how you expect Moose to be behave**. +* Which **version of Moose** and DCS World you are using. This can be found in `dcs.log` +* Did the code you are using worked in previous version of either DCS World or Moose? Which one? +* The **code** or portion of code **that triggered the problem**. Please put **_\`\`\`lua_** one line before your code, and **_\`\`\`_** at the end, to make your code readable. +* The relevant part of **`dcs.log`**. Please put **_\`\`\`_** before and after your copy-pasted log to make it readable. + +![Example of a great bug report](Installation/GitHub_Issue_example.JPG) + +## 6.2) How to add a feature request ? + +* **A descriptive title**, not too long if possible. +* Explain in detail **what you want Moose to do** in which circonstance. +* If possible, add some pseudocode or a high level design implementation to better explain how your feature would work. +* Add the **lablel enhancment** + +## 6.3) The life and death of an issue + +**_What is going to happen next to my issue?_** + +* A contributor will set some labels and a milestone to your issue, in order to classify it. +* You might be asked to clarify some part of your issue, please answer as swiftly as possible. +* You might be asked to sync your local repository and try again, if we think we fixed your issue. If we can confirm it to be fixed the issue is closed. + +**_Wait, my issue was closed, and it never got fixed ! / Nobody is paying attention to it !_** + +This can happen for multiple reasons : + +* Your issue is a duplicate. (There is alredy a issue in our issue tracker for this) +* Nobody was able to reproduce your bug, so we think it's a problem on your end +* Your feature request asks for a feature that already exists +* We think your feature request is a really good idea, but it's a huge time commitment. We therefore postponed it to the Realease after the next one, or to a later Release. +* We are working hard and simply don't have the time. We will get around to check your issue, please be patient! diff --git a/docs/Communities.md b/docs/Communities.md index e809396d1..422300e2b 100644 --- a/docs/Communities.md +++ b/docs/Communities.md @@ -1,8 +1,8 @@ -# 3) MOOSE framework support channels +# 1) MOOSE framework support channels MOOSE is broadcasted, documented and supported through various social media channels. -## 3.1) MOOSE broadcast channels on YouTube +## 1.1) MOOSE broadcast channels on YouTube MOOSE has a [broadcast channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg/playlists) on youtube. These videos are grouped into playlists, which explain specific MOOSE capabilities, @@ -18,19 +18,19 @@ Some mandatory videos to watch are: * [MOOSE Tasking](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl3CgxN2iAViiGLTPpQ-Ajdg) * [MOOSE Task Dispatching](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl3I6ieFM-cjey-rncF1ktNI) -## 3.2) MOOSE on Eagle Dynamics forums +## 1.2) MOOSE on Eagle Dynamics forums This is a default thread at the Eagle Dynamics forums where the MOOSE framework can be discussed and supported. The thread is called [MOOSE - Mission Object Oriented Scripting Framework](https://forums.eagle.ru/showthread.php?t=138043). -## 3.3) MOOSE on GITHUB. +## 1.3) MOOSE on GITHUB. I encourage that you create also a user at GITHUB. On the MOOSE framework GITHUB site, you can register issues, feedback and comments in the issues section of the site. This allows to track this feedback and issues, in order to provide a structured support and create a milestone plan. In other words, treat this development as a project. -## 3.4) MOOSE on slack.com +## 1.4) MOOSE on slack.com Slack is a team community site. It is a great environment to discuss online the framework. Various channels are allocated in the environment to discuss specific topics. diff --git a/docs/Contribution_Guide.md b/docs/Contribution_Guide.md new file mode 100644 index 000000000..c66e20aa7 --- /dev/null +++ b/docs/Contribution_Guide.md @@ -0,0 +1,190 @@ +*** UNDER CONSTRUCTION *** + +So, you are already familiarized with Lua and you think the best way to get the new feature you want fast is to program it yourself ? Or you feel like reporting bugs isn't enough, you want to fix them yourself ? Either way, you need some more information to contribute to Moose. + +This guide assumes that **you are already familiar with Moose** and that **you already set up your development environment**. If it is not the case, please go through the [Tester Guide](Beta_Test_Guide.html) before proceeding. + +In this document, we will review + +1. GitHub/Git organisation +2. Contribution guidelines +3. Coding standards +4. The Luadoc +5. Moose release cycle +6. The Issue Tracker : labels and milestones + +# 1) GitHub/Git organisation + +You are assumed to be familar with at least the basics of GitHub (branches, commits, pull requests...). If it is not the case, please at least read [this tutorial](https://guides.github.com/introduction/flow/). If something isn't clear still, ask us, we'll be glad to explain! + +## 1.1) The Repositories + +Moose is in fact located on **three repositories** : + +* [**MOOSE**](https://github.com/FlightControl-Master/MOOSE) contains the Moose's **code**, the **documentation** and the file necessary to the **setup** +* [**MOOSE_MISSIONS**](https://github.com/FlightControl-Master/MOOSE_MISSIONS) contains the **test missions**. Be aware that the [master-release branch](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release) contains the statically loaded test missions, while the [master branch](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master) contains their dynamically loaded counterparts. will want to create a local repository for this one and to sync it. +* [**MOOSE_PRESENTATIONS**](https://github.com/FlightControl-Master/MOOSE_PRESENTATIONS) contains bits and bob related to Moose, like Moose's logos and PowerPoint files to present a specific feature of Moose. + +## 1.2) Branches + +On the [MOOSE](https://github.com/FlightControl-Master/MOOSE) repository, three branches are protected, which means that you cannot merge or commit directly to them, you need to create a pull request; + +* [master](https://github.com/FlightControl-Master/MOOSE/tree/master) stores Moose's current latest semi-stable code. +* master-release stores Mosse's last release code. There is no reason you would want to modify this branch. +* [master-backup](https://github.com/FlightControl-Master/MOOSE/tree/master-backup). We sometime backup the master into master-backup, in case the worst happen. There is no reason you would want to modify this branch. + +You are encourgaed to **create your own branch, named after you pseudonyme**, to test stuff and do some small-scale bugfixes. When you want to work on bigger features, you should **create a new branch named after the feature** you are working on. Don't forget to delete it when the feature is merged with master! + +## 1.3) Commits + +The usual [Git commit](https://chris.beams.io/posts/git-commit/) guidelines apply. Don't overthink it though, time is better spent coding or managing the Issue Tracker than writing long-form commit descriptions. + +## 1.4) Merge/Pull Requests + +When the code you are working on is finished, you will want to **resolve the merge conflicts** between your branch and master, and then **create a pull request**. Explain clearly what your code does (with a link to the relevant issue). If it meets the requirements below, it will be approved ASAP by FlightControl. + +# 2) Contribution guidelines + +We try to **follow a contribution process**, to make sure we work efficiently together. It isn't set in stone, but it gives an idea of what should be done. We wouldn't want two contributors both working on the same issue at the same time, would we ? This process is more geared towards the implementation of new features, the process for bug fixes is more flexible and several steps can be overlooked. + +1. Think about **what you are trying to achieve.** Writing some pseudocode is a great way to undersatnd the challenges you are going to face. +2. **Discuss your idea** with the community **on Slack**. Maybe there is a way to do it you didn't even think about, or maybe you could team up with someone who is already working on it! +3. Create a **high level design document**. It doesn't need to be thorough, but you need to have an idea of which class do you want to write, which class they should inherit from, which methods are needed in each class... +4. Write an **issue on GitHub** with your high level design document, apply the label "enhancement" to it, and assign it to yourself. +5. Create a **new branch** named after the feature **on MOOSE's repository** to work on (you will be given contributor access). +6. **Write your code** in this branch, following the Coding Standards. **Sync** fairly regularly **this branch with master**, so that you don't have tons of merge conflicts at the end. +7. When done, **write a/some test mission(s)** to showcase how to use your feature to the community. +8. **Generate the Luadoc**. +9. **Relsove merge conflicts** with master and **create a new pull request**. +10. **Delete your branch** if you are not going to use it again. + +# 3) Coding Standards + +To ensure a good degree of **consistency** in Moose's code, we follow the following standards : + +* The code need to be **intellisense/Luadoc compatible**. See below for more information +* The code needs to be **commented**. Remember : _“Programs must be written for people to read, and only incidentally for machines to execute.â€_ - Hal Abelson. Keep in mind that you code will be red by non-programmers and beginners. +* **Indentation** should be 2 spaces (default in LDT) +* **Class names** should be in **capital letters** (e.g. `SPAWN`) +* **Class should all inherit from `Core.Base#BASE`** +* **Methods** should start by a **capital letter** (e.g. `GetName()`) +* If your **method** is intended for **private use** only, its name should **start with an underscore** (e.g. `_GetLastIndex()`) +* **Variables** should start with a **capital leter** (e.g. `IndexString`) +* Provide a **trace** for the mission designer at the start of every method, and when it is appropriate. Trace functions are inherited from `BASE` : + * `F()`, `F2()` and `F3()` for function calls, + * `T()`, `T2()` and `T3()` for function logic, + * `E()` for errors. + +# 4) The Luadoc + +The **Luadoc system** is not only **useful for the contributor** to understand the code, but **also the mission designer**, as it used to automatically generate the [HTML documentation](http://flightcontrol-master.github.io/MOOSE/Documentation/index.html). +It thus needs to follow some strict standards. +Keep in mind the following informations : + +* Every Luadoc block needs to start with **three minus signs** (`---`). +* The generated html file will use **Markdown**. If you are not familar with it, use this [reference](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet). +* You can create **hyperlinks** by writing + * `@{Module#CLASS.Method}` for a method + * `@{Module#CLASS}` for a class + * `Module` can be ommited if the hyperlink points to the same module (e.g. `@{#CLASS`} is valid if CLASS is in the Module the link is in) +* Luadoc **types the variables**. the following types are available + * `Folder.Module#CLASS` (a Moose class) + * `Dcs.DCSTypes#Class` (a DCS class) + * `#string` + * `#number` (integer or floating point) + * `#boolean` + * `#nil` + +## 4.1) Modules + +Every new file should contain at least one module, which is documented by a comment block. You can copy paste the following model and fill in the blanks : + +```lua +--- **Name of the Module** - Short Description +-- +-- === +-- +-- Long description. Explain the use cases. +-- Remember that this section will be red by non programmers ! +-- +-- === +-- +-- ### Authors : Your name/pseudnyme +-- +-- @module Module +``` + +## 4.2) Classes + +Every class should be documented by a comment block. You can copy paste the following model and fill in the blanks : + +```lua +--- **Name of the CLASS** class, extends @{Module#CLASS} +-- +-- Long description. Explain the use cases. +-- Remember that this section will be red by non programmers ! +-- +-- @type CLASSNAME +-- @field #type AttributeName (add a @field for each attribute) +-- @extends Folder.Module#CLASS +``` + +## 4.3) Methods + +Every method should be documented by a comment block. You can copy paste the following model and fill in the blanks : + +```lua +--- Description of the method +-- @param #type (add a @param for each parameters, self included, even if it's implied) +-- @return #type (add other @return fields if the method can return different variables) +-- @usage (can be omitted if the usage is obvious) +-- -- Explain how to use this method +-- -- on multiple lines if you need it ! +``` + +# 5) Moose release cycle +To ensure that the next Release of Moose is as bug-free and feature rich as possible, every Moose contributor **respects a release cycle**. +![](Installation/MOOSE_Release_Cycle.JPG) + +The key takeways are: + +* During "normal" time, **write your features** and correct bugs **as you please**. +* During "Feature Freeze", you can still work on your features, but you are **strongly encouraged to prioritize bug fixes**, especially if it involves your code. **No pull request for new features will be accepted during feature freeze !** +* After the Release, it's back to the start for a new cycle. + +# 6) The Issue Tracker : labels and milestones + +## 6.1) Milestones + +You can see Milestone as the GitHub way to say Release. Moose repository has **three active [Milestone](https://github.com/FlightControl-Master/MOOSE/milestones) at any time** : + +* The next milestone (e.g. Moose-2.0) +* The milestone after the next one (e.g. Moose-2.1) +* The "future" milestone (Moose-Future) + +Every **bug is assigned to the next milestone**, and should be fixed before the release. **Features are assigned to any milestone depending on the importance** of it and how hard it is to implement. Typically, a feature that is currently worked on is assigned to the next milestone, an very long-term feature is assigned to Moose-Future, and any other feature is assigned to the milestone after the next one, the goal being to have a nice todo list for the contributor to pick and choose from at the end of feature freeze. If you come accross a issue that isn't assigned to a milestone, feel free to add it to the correct one ! + +## 6.2) Labels + +We heavily use **GitHub's label system** on the Issue Tracker to categorize each issue. Each **issue is assigned the relevant label(s) at birth** by a contributor, and they are then updated to reflect the current state of the issue. If you come accross an untagged issue, feel free label it ! You can consult the [full list of labels available](https://github.com/FlightControl-Master/MOOSE/labels), but **please asks the community before adding or removing a label** to the list. + +* Bugs + * question : not even a bug report + * possible bug : might be a bug, but hasn't been reproduced yet + * bug : the bug has been confirmed / reproduced + * fixed : We think the bug is fixed. but there is a good reson why we are not closing the issue just yet. Usually used in conjunction with ready for testing +* Feature requests + * enhancement : this issue is a feature request + * implemented : we implemented this enhancement, but there is a good reson why we are not closing the issue just yet. Usually used in conjunction with ready for testing +* Documentation work + * documentation +* Urgency + * urgent (fix in a few days) : This is used primarly for bugs found in the latest Release. + * not urgent (can wait) +* Work load (this is merely to help contributer choose issues conpatible with the free time they have) + * hard work + * trivial +* Will not fix + * wontfix + * duplicate : this issue already exists somewhere else + * invalid diff --git a/docs/Documentation/AI_Balancer.html b/docs/Documentation/AI_Balancer.html index 4a168b45f..2b5f48f01 100644 --- a/docs/Documentation/AI_Balancer.html +++ b/docs/Documentation/AI_Balancer.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/AI_Cap.html b/docs/Documentation/AI_Cap.html index d6441cd2c..0657d3fa0 100644 --- a/docs/Documentation/AI_Cap.html +++ b/docs/Documentation/AI_Cap.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -72,99 +73,22 @@

    Module AI_Cap

    -

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Air -- Execute Combat Air Patrol (CAP).

    +

    AI - Execute Combat Air Patrol (CAP).

    Banner Image


    -

    1) #AICAPZONE class, extends AICAP#AIPATROL_ZONE

    - -

    The #AICAPZONE class implements the core functions to patrol a Zone by an AI Controllable or Group -and automatically engage any airborne enemies that are within a certain range or within a certain zone.

    +

    AI CAP classes makes AI Controllables execute a Combat Air Patrol.

    -

    Process

    - -

    The AICAPZONE is assigned a Group and this must be done before the AICAPZONE process can be started using the Start event.

    - -

    Process

    - -

    The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. -Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.

    - -

    Process

    - -

    This cycle will continue.

    - -

    Process

    - -

    During the patrol, the AI will detect enemy targets, which are reported through the Detected event.

    - -

    Process

    - -

    When enemies are detected, the AI will automatically engage the enemy.

    - -

    Process

    - -

    Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.

    - -

    Process

    - -

    1.1) AICAPZONE constructor

    +

    There are the following types of CAP classes defined:

    -

    1.2) AICAPZONE is a FSM

    - -

    Process

    - -

    1.2.1) AICAPZONE States

    - -
      -
    • None ( Group ): The process is not started yet.
    • -
    • Patrolling ( Group ): The AI is patrolling the Patrol Zone.
    • -
    • Engaging ( Group ): The AI is engaging the bogeys.
    • -
    • Returning ( Group ): The AI is returning to Base..
    • -
    - -

    1.2.2) AICAPZONE Events

    - - - -

    1.3) Set the Range of Engagement

    - -

    Range

    - -

    An optional range can be set in meters, -that will define when the AI will engage with the detected airborne enemy targets. -The range can be beyond or smaller than the range of the Patrol Zone. -The range is applied at the position of the AI. -Use the method AICAP#AICAP_ZONE.SetEngageRange() to define that range.

    - -

    1.4) Set the Zone of Engagement

    - -

    Zone

    - -

    An optional Zone can be set, -that will define when the AI will engage with the detected airborne enemy targets. -Use the method AICap#AICAP_ZONE.SetEngageZone() to define that Zone.

    -

    API CHANGE HISTORY

    @@ -206,7 +130,10 @@ Use the method AICap#AI AI_CAP_ZONE +

    1) #AICAPZONE class, extends AICAP#AIPATROL_ZONE

    +

    The #AICAPZONE class implements the core functions to patrol a Zone by an AI Controllable or Group +and automatically engage any airborne enemies that are within a certain range or within a certain zone.

    @@ -240,12 +167,6 @@ Use the method AICap#AIAI_CAP_ZONE.Accomplished - - - - AI_CAP_ZONE.ClassName - - @@ -471,6 +392,94 @@ Use the method AICap#AI
    +

    1) #AICAPZONE class, extends AICAP#AIPATROL_ZONE

    + +

    The #AICAPZONE class implements the core functions to patrol a Zone by an AI Controllable or Group +and automatically engage any airborne enemies that are within a certain range or within a certain zone.

    + + + +

    Process

    + +

    The AICAPZONE is assigned a Group and this must be done before the AICAPZONE process can be started using the Start event.

    + +

    Process

    + +

    The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. +Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.

    + +

    Process

    + +

    This cycle will continue.

    + +

    Process

    + +

    During the patrol, the AI will detect enemy targets, which are reported through the Detected event.

    + +

    Process

    + +

    When enemies are detected, the AI will automatically engage the enemy.

    + +

    Process

    + +

    Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. +When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.

    + +

    Process

    + +

    1.1) AICAPZONE constructor

    + + + +

    1.2) AICAPZONE is a FSM

    + +

    Process

    + +

    1.2.1) AICAPZONE States

    + +
      +
    • None ( Group ): The process is not started yet.
    • +
    • Patrolling ( Group ): The AI is patrolling the Patrol Zone.
    • +
    • Engaging ( Group ): The AI is engaging the bogeys.
    • +
    • Returning ( Group ): The AI is returning to Base..
    • +
    + +

    1.2.2) AICAPZONE Events

    + + + +

    1.3) Set the Range of Engagement

    + +

    Range

    + +

    An optional range can be set in meters, +that will define when the AI will engage with the detected airborne enemy targets. +The range can be beyond or smaller than the range of the Patrol Zone. +The range is applied at the position of the AI. +Use the method AICAP#AICAP_ZONE.SetEngageRange() to define that range.

    + +

    1.4) Set the Zone of Engagement

    + +

    Zone

    + +

    An optional Zone can be set, +that will define when the AI will engage with the detected airborne enemy targets. +Use the method AICap#AICAP_ZONE.SetEngageZone() to define that Zone.

    + +
    @@ -499,10 +508,7 @@ Use the method AICap#AIType AI_Cap

    Type AI_CAP_ZONE

    - -

    AICAPZONE class

    - -

    Field(s)

    +

    Field(s)

    @@ -555,20 +561,6 @@ Use the method AICap#AI -
    -
    -
    - - #string - -AI_CAP_ZONE.ClassName - -
    -
    - - -
    diff --git a/docs/Documentation/AI_Cas.html b/docs/Documentation/AI_Cas.html index 31534e9eb..10992a925 100644 --- a/docs/Documentation/AI_Cas.html +++ b/docs/Documentation/AI_Cas.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -72,18 +73,339 @@

    Module AI_Cas

    -

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Air -- -Provide Close Air Support to friendly ground troops.

    +

    AI -- Provide Close Air Support to friendly ground troops.

    Banner Image


    +

    AI CAS classes makes AI Controllables execute a Close Air Support.

    + + + +

    There are the following types of CAS classes defined:

    + + + +
    + +

    API CHANGE HISTORY

    + +

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    + +
      +
    • Added parts are expressed in bold type face.
    • +
    • Removed parts are expressed in italic type face.
    • +
    + +

    Hereby the change log:

    + +

    2017-01-15: Initial class and API.

    + +
    + +

    AUTHORS and CONTRIBUTIONS

    + +

    Contributions:

    + +
      +
    • Quax: Concept, Advice & Testing.
    • +
    • Pikey: Concept, Advice & Testing.
    • +
    • Gunterlund: Test case revision.
    • +
    + +

    Authors:

    + +
      +
    • FlightControl: Concept, Design & Programming.
    • +
    + + +

    Global(s)

    + + + + + + + + + +
    AI_CAS_ZONE +

    1) #AICASZONE class, extends AIPatrol#AIPATROL_ZONE

    + +

    #AICASZONE derives from the AIPatrol#AIPATROL_ZONE, inheriting its methods and behaviour.

    +
    _NewEngageRoute(AIControllable) + +
    +

    Type AI_CAS_ZONE

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AI_CAS_ZONE.AIControllable +

    The Controllable patrolling.

    +
    AI_CAS_ZONE:Abort() +

    Synchronous Event Trigger for Event Abort.

    +
    AI_CAS_ZONE:Accomplish() +

    Synchronous Event Trigger for Event Accomplish.

    +
    AI_CAS_ZONE.Accomplished + +
    AI_CAS_ZONE:Destroy() +

    Synchronous Event Trigger for Event Destroy.

    +
    AI_CAS_ZONE:Engage(EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection) +

    Synchronous Event Trigger for Event Engage.

    +
    AI_CAS_ZONE.EngageAltitude + +
    AI_CAS_ZONE.EngageAttackQty + +
    AI_CAS_ZONE.EngageDirection + +
    AI_CAS_ZONE.EngageSpeed + +
    AI_CAS_ZONE.EngageWeaponExpend + +
    AI_CAS_ZONE.EngageZone + +
    AI_CAS_ZONE:Fired() +

    Synchronous Event Trigger for Event Fired.

    +
    AI_CAS_ZONE:New(PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType) +

    Creates a new AICASZONE object

    +
    AI_CAS_ZONE:OnAfterAbort(Controllable, From, Event, To) +

    OnAfter Transition Handler for Event Abort.

    +
    AI_CAS_ZONE:OnAfterAccomplish(Controllable, From, Event, To) +

    OnAfter Transition Handler for Event Accomplish.

    +
    AI_CAS_ZONE:OnAfterDestroy(Controllable, From, Event, To) +

    OnAfter Transition Handler for Event Destroy.

    +
    AI_CAS_ZONE:OnAfterEngage(Controllable, From, Event, To) +

    OnAfter Transition Handler for Event Engage.

    +
    AI_CAS_ZONE:OnAfterFired(Controllable, From, Event, To) +

    OnAfter Transition Handler for Event Fired.

    +
    AI_CAS_ZONE:OnBeforeAbort(Controllable, From, Event, To) +

    OnBefore Transition Handler for Event Abort.

    +
    AI_CAS_ZONE:OnBeforeAccomplish(Controllable, From, Event, To) +

    OnBefore Transition Handler for Event Accomplish.

    +
    AI_CAS_ZONE:OnBeforeDestroy(Controllable, From, Event, To) +

    OnBefore Transition Handler for Event Destroy.

    +
    AI_CAS_ZONE:OnBeforeEngage(Controllable, From, Event, To) +

    OnBefore Transition Handler for Event Engage.

    +
    AI_CAS_ZONE:OnBeforeFired(Controllable, From, Event, To) +

    OnBefore Transition Handler for Event Fired.

    +
    AI_CAS_ZONE:OnEnterEngaging(Controllable, From, Event, To) +

    OnEnter Transition Handler for State Engaging.

    +
    AI_CAS_ZONE:OnEventDead(EventData) + +
    AI_CAS_ZONE:OnLeaveEngaging(Controllable, From, Event, To) +

    OnLeave Transition Handler for State Engaging.

    +
    AI_CAS_ZONE:SetEngageZone(EngageZone) +

    Set the Engage Zone where the AI is performing CAS.

    +
    AI_CAS_ZONE.TargetZone +

    The Zone where the patrol needs to be executed.

    +
    AI_CAS_ZONE:__Abort(Delay) +

    Asynchronous Event Trigger for Event Abort.

    +
    AI_CAS_ZONE:__Accomplish(Delay) +

    Asynchronous Event Trigger for Event Accomplish.

    +
    AI_CAS_ZONE:__Destroy(Delay) +

    Asynchronous Event Trigger for Event Destroy.

    +
    AI_CAS_ZONE:__Engage(Delay, EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection) +

    Asynchronous Event Trigger for Event Engage.

    +
    AI_CAS_ZONE:__Fired(Delay) +

    Asynchronous Event Trigger for Event Fired.

    +
    AI_CAS_ZONE:onafterAbort(Controllable, From, Event, To) + +
    AI_CAS_ZONE:onafterAccomplish(Controllable, From, Event, To) + +
    AI_CAS_ZONE:onafterDestroy(Controllable, From, Event, To, EventData) + +
    AI_CAS_ZONE:onafterEngage(Controllable, From, Event, To, EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection) + +
    AI_CAS_ZONE:onafterStart(Controllable, From, Event, To) +

    onafter State Transition for Event Start.

    +
    AI_CAS_ZONE:onafterTarget(Controllable, From, Event, To) + +
    AI_CAS_ZONE:onbeforeEngage(Controllable, From, Event, To) + +
    + +

    Global(s)

    +
    +
    + + #AI_CAS_ZONE + +AI_CAS_ZONE + +
    +
    +

    1) #AICASZONE class, extends AIPatrol#AIPATROL_ZONE

    #AICASZONE derives from the AIPatrol#AIPATROL_ZONE, inheriting its methods and behaviour.

    - +

    The #AICASZONE class implements the core functions to provide Close Air Support in an Engage Zone by an AIR Controllable or Group. The AICASZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone.

    @@ -177,321 +499,6 @@ It can be notified to go RTB through the RTB event.


    -

    API CHANGE HISTORY

    - -

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    - -
      -
    • Added parts are expressed in bold type face.
    • -
    • Removed parts are expressed in italic type face.
    • -
    - -

    Hereby the change log:

    - -

    2017-01-15: Initial class and API.

    - -
    - -

    AUTHORS and CONTRIBUTIONS

    - -

    Contributions:

    - -
      -
    • Quax: Concept, Advice & Testing.
    • -
    • Pikey: Concept, Advice & Testing.
    • -
    • Gunterlund: Test case revision.
    • -
    - -

    Authors:

    - -
      -
    • FlightControl: Concept, Design & Programming.
    • -
    - - -

    Global(s)

    - - - - - - - - - -
    AI_CAS_ZONE - -
    _NewEngageRoute(AIControllable) - -
    -

    Type AI_CAS_ZONE

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    AI_CAS_ZONE.AIControllable -

    The Controllable patrolling.

    -
    AI_CAS_ZONE:Abort() -

    Synchronous Event Trigger for Event Abort.

    -
    AI_CAS_ZONE:Accomplish() -

    Synchronous Event Trigger for Event Accomplish.

    -
    AI_CAS_ZONE.Accomplished - -
    AI_CAS_ZONE.ClassName - -
    AI_CAS_ZONE:Destroy() -

    Synchronous Event Trigger for Event Destroy.

    -
    AI_CAS_ZONE:Engage(EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) -

    Synchronous Event Trigger for Event Engage.

    -
    AI_CAS_ZONE.EngageAltitude - -
    AI_CAS_ZONE.EngageAttackQty - -
    AI_CAS_ZONE.EngageDirection - -
    AI_CAS_ZONE.EngageSpeed - -
    AI_CAS_ZONE.EngageWeaponExpend - -
    AI_CAS_ZONE.EngageZone - -
    AI_CAS_ZONE:Fired() -

    Synchronous Event Trigger for Event Fired.

    -
    AI_CAS_ZONE:New(PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType) -

    Creates a new AICASZONE object

    -
    AI_CAS_ZONE:OnAfterAbort(Controllable, From, Event, To) -

    OnAfter Transition Handler for Event Abort.

    -
    AI_CAS_ZONE:OnAfterAccomplish(Controllable, From, Event, To) -

    OnAfter Transition Handler for Event Accomplish.

    -
    AI_CAS_ZONE:OnAfterDestroy(Controllable, From, Event, To) -

    OnAfter Transition Handler for Event Destroy.

    -
    AI_CAS_ZONE:OnAfterEngage(Controllable, From, Event, To, EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) -

    OnAfter Transition Handler for Event Engage.

    -
    AI_CAS_ZONE:OnAfterFired(Controllable, From, Event, To) -

    OnAfter Transition Handler for Event Fired.

    -
    AI_CAS_ZONE:OnBeforeAbort(Controllable, From, Event, To) -

    OnBefore Transition Handler for Event Abort.

    -
    AI_CAS_ZONE:OnBeforeAccomplish(Controllable, From, Event, To) -

    OnBefore Transition Handler for Event Accomplish.

    -
    AI_CAS_ZONE:OnBeforeDestroy(Controllable, From, Event, To) -

    OnBefore Transition Handler for Event Destroy.

    -
    AI_CAS_ZONE:OnBeforeEngage(Controllable, From, Event, To, EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) -

    OnBefore Transition Handler for Event Engage.

    -
    AI_CAS_ZONE:OnBeforeFired(Controllable, From, Event, To) -

    OnBefore Transition Handler for Event Fired.

    -
    AI_CAS_ZONE:OnEnterEngaging(Controllable, From, Event, To) -

    OnEnter Transition Handler for State Engaging.

    -
    AI_CAS_ZONE:OnEventDead(EventData) - -
    AI_CAS_ZONE:OnLeaveEngaging(Controllable, From, Event, To) -

    OnLeave Transition Handler for State Engaging.

    -
    AI_CAS_ZONE:SetEngageZone(EngageZone) -

    Set the Engage Zone where the AI is performing CAS.

    -
    AI_CAS_ZONE.TargetZone -

    The Zone where the patrol needs to be executed.

    -
    AI_CAS_ZONE:__Abort(Delay) -

    Asynchronous Event Trigger for Event Abort.

    -
    AI_CAS_ZONE:__Accomplish(Delay) -

    Asynchronous Event Trigger for Event Accomplish.

    -
    AI_CAS_ZONE:__Destroy(Delay) -

    Asynchronous Event Trigger for Event Destroy.

    -
    AI_CAS_ZONE:__Engage(Delay, EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) -

    Asynchronous Event Trigger for Event Engage.

    -
    AI_CAS_ZONE:__Fired(Delay) -

    Asynchronous Event Trigger for Event Fired.

    -
    AI_CAS_ZONE:onafterAbort(Controllable, From, Event, To) - -
    AI_CAS_ZONE:onafterAccomplish(Controllable, From, Event, To) - -
    AI_CAS_ZONE:onafterDestroy(Controllable, From, Event, To, EventData) - -
    AI_CAS_ZONE:onafterEngage(Controllable, From, Event, To, EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) - -
    AI_CAS_ZONE:onafterStart(Controllable, From, Event, To) -

    onafter State Transition for Event Start.

    -
    AI_CAS_ZONE:onafterTarget(Controllable, From, Event, To) - -
    AI_CAS_ZONE:onbeforeEngage(Controllable, From, Event, To) - -
    - -

    Global(s)

    -
    -
    - - #AI_CAS_ZONE - -AI_CAS_ZONE - -
    -
    - -
    @@ -575,20 +582,6 @@ It can be notified to go RTB through the RTB event.

    -
    -
    -
    -
    - - #string - -AI_CAS_ZONE.ClassName - -
    -
    - - -
    @@ -608,7 +601,7 @@ It can be notified to go RTB through the RTB event.

    -AI_CAS_ZONE:Engage(EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) +AI_CAS_ZONE:Engage(EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection)
    @@ -625,14 +618,16 @@ It can be notified to go RTB through the RTB event.

  • -

    Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend : -(optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.

    +

    Dcs.DCSTypes#Distance EngageAltitude : +(optional) Desired altitude to perform the unit engagement.

  • -

    Dcs.DCSTypes#Distance EngageAltitude : -(optional) Desired altitude to perform the unit engagement.

    +

    Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend : +(optional) Determines how much weapon will be released at each attack. +If parameter is not defined the unit / controllable will choose expend on its own discretion. +Use the structure DCSTypes#AI.Task.WeaponExpend to define the amount of weapons to be release at each attack.

  • @@ -932,7 +927,7 @@ The To State string.

    -AI_CAS_ZONE:OnAfterEngage(Controllable, From, Event, To, EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) +AI_CAS_ZONE:OnAfterEngage(Controllable, From, Event, To)
    @@ -964,36 +959,6 @@ The Event string.

    #string To : The To State string.

    -
  • -
  • - -

    #number EngageSpeed : -(optional) The speed the Group will hold when engaging to the target zone.

    - -
  • -
  • - -

    Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend : -(optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.

    - -
  • -
  • - -

    Dcs.DCSTypes#Distance EngageAltitude : -(optional) Desired altitude to perform the unit engagement.

    - -
  • -
  • - -

    #number EngageAttackQty : -(optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.

    - -
  • -
  • - -

    Dcs.DCSTypes#Azimuth EngageDirection : -(optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.

    -
  • @@ -1177,7 +1142,7 @@ Return false to cancel Transition.

    -AI_CAS_ZONE:OnBeforeEngage(Controllable, From, Event, To, EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) +AI_CAS_ZONE:OnBeforeEngage(Controllable, From, Event, To)
    @@ -1209,36 +1174,6 @@ The Event string.

    #string To : The To State string.

    - -
  • - -

    #number EngageSpeed : -(optional) The speed the Group will hold when engaging to the target zone.

    - -
  • -
  • - -

    Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend : -(optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.

    - -
  • -
  • - -

    Dcs.DCSTypes#Distance EngageAltitude : -(optional) Desired altitude to perform the unit engagement.

    - -
  • -
  • - -

    #number EngageAttackQty : -(optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.

    - -
  • -
  • - -

    Dcs.DCSTypes#Azimuth EngageDirection : -(optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.

    -
  • @@ -1508,7 +1443,7 @@ The delay in seconds.

    -AI_CAS_ZONE:__Engage(Delay, EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) +AI_CAS_ZONE:__Engage(Delay, EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection)
    @@ -1531,14 +1466,16 @@ The delay in seconds.

  • -

    Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend : -(optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.

    +

    Dcs.DCSTypes#Distance EngageAltitude : +(optional) Desired altitude to perform the unit engagement.

  • -

    Dcs.DCSTypes#Distance EngageAltitude : -(optional) Desired altitude to perform the unit engagement.

    +

    Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend : +(optional) Determines how much weapon will be released at each attack. +If parameter is not defined the unit / controllable will choose expend on its own discretion. +Use the structure DCSTypes#AI.Task.WeaponExpend to define the amount of weapons to be release at each attack.

  • @@ -1707,7 +1644,7 @@ The To State string.

    -AI_CAS_ZONE:onafterEngage(Controllable, From, Event, To, EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) +AI_CAS_ZONE:onafterEngage(Controllable, From, Event, To, EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection)
    @@ -1748,14 +1685,14 @@ The To State string.

  • -

    Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend : -(optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.

    +

    Dcs.DCSTypes#Distance EngageAltitude : +(optional) Desired altitude to perform the unit engagement.

  • -

    Dcs.DCSTypes#Distance EngageAltitude : -(optional) Desired altitude to perform the unit engagement.

    +

    Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend : +(optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.

  • diff --git a/docs/Documentation/AI_Patrol.html b/docs/Documentation/AI_Patrol.html index a07fbb643..20cd2b005 100644 --- a/docs/Documentation/AI_Patrol.html +++ b/docs/Documentation/AI_Patrol.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -72,128 +73,22 @@

    Module AI_Patrol

    -

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Air -- -Air Patrolling or Staging.

    +

    AI -- Air Patrolling or Staging.

    Banner Image


    -

    1) #AIPATROLZONE class, extends Fsm#FSM_CONTROLLABLE

    - -

    The #AIPATROLZONE class implements the core functions to patrol a Zone by an AI Controllable or Group.

    +

    AI PATROL classes makes AI Controllables execute an Patrol.

    -

    Process

    - -

    The AIPATROLZONE is assigned a Group and this must be done before the AIPATROLZONE process can be started using the Start event.

    - -

    Process

    - -

    The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. -Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.

    - -

    Process

    - -

    This cycle will continue.

    - -

    Process

    - -

    During the patrol, the AI will detect enemy targets, which are reported through the Detected event.

    - -

    Process

    - -

    -- Note that the enemy is not engaged! To model enemy engagement, either tailor the Detected event, or -use derived AI_ classes to model AI offensive or defensive behaviour.

    - -

    Process

    - -

    Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.

    - -

    Process

    - -

    1.1) AIPATROLZONE constructor

    +

    There are the following types of PATROL classes defined:

    -

    1.2) AIPATROLZONE is a FSM

    - -

    Process

    - -

    1.2.1) AIPATROLZONE States

    - -
      -
    • None ( Group ): The process is not started yet.
    • -
    • Patrolling ( Group ): The AI is patrolling the Patrol Zone.
    • -
    • Returning ( Group ): The AI is returning to Base.
    • -
    • Stopped ( Group ): The process is stopped.
    • -
    • Crashed ( Group ): The AI has crashed or is dead.
    • -
    - -

    1.2.2) AIPATROLZONE Events

    - -
      -
    • Start ( Group ): Start the process.
    • -
    • Stop ( Group ): Stop the process.
    • -
    • Route ( Group ): Route the AI to a new random 3D point within the Patrol Zone.
    • -
    • RTB ( Group ): Route the AI to the home base.
    • -
    • Detect ( Group ): The AI is detecting targets.
    • -
    • Detected ( Group ): The AI has detected new targets.
    • -
    • Status ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
    • -
    - -

    1.3) Set or Get the AI controllable

    - - - -

    1.4) Set the Speed and Altitude boundaries of the AI controllable

    - - - -

    1.5) Manage the detection process of the AI controllable

    - -

    The detection process of the AI controllable can be manipulated. -Detection requires an amount of CPU power, which has an impact on your mission performance. -Only put detection on when absolutely necessary, and the frequency of the detection can also be set.

    - - - -

    The detection frequency can be set with AIPATROLZONE.SetDetectionInterval( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection. -Use the method AIPATROLZONE.GetDetectedUnits() to obtain a list of the Units detected by the AI.

    - -

    The detection can be filtered to potential targets in a specific zone. -Use the method AIPATROLZONE.SetDetectionZone() to set the zone where targets need to be detected. -Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected -according the weather conditions.

    - -

    1.6) Manage the "out of fuel" in the AIPATROLZONE

    - -

    When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. -Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. -When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, -while a new AI is targetted to the AIPATROLZONE. -Once the time is finished, the old AI will return to the base. -Use the method AIPATROLZONE.ManageFuel() to have this proces in place.

    - -

    1.7) Manage "damage" behaviour of the AI in the AIPATROLZONE

    - -

    When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on. -Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB). -Use the method AIPATROLZONE.ManageDamage() to have this proces in place.

    -

    OPEN ISSUES

    @@ -247,7 +142,9 @@ Use the method AIPATROLZONE.M AI_PATROL_ZONE +

    1) #AIPATROLZONE class, extends Fsm#FSM_CONTROLLABLE

    +

    The #AIPATROLZONE class implements the core functions to patrol a Zone by an AI Controllable or Group.

    @@ -263,12 +160,6 @@ Use the method AIPATROLZONE.M AI_PATROL_ZONE.CheckStatus - - - - AI_PATROL_ZONE.ClassName - - @@ -746,6 +637,122 @@ Use the method AIPATROLZONE.M
    +

    1) #AIPATROLZONE class, extends Fsm#FSM_CONTROLLABLE

    + +

    The #AIPATROLZONE class implements the core functions to patrol a Zone by an AI Controllable or Group.

    + + + +

    Process

    + +

    The AIPATROLZONE is assigned a Group and this must be done before the AIPATROLZONE process can be started using the Start event.

    + +

    Process

    + +

    The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. +Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.

    + +

    Process

    + +

    This cycle will continue.

    + +

    Process

    + +

    During the patrol, the AI will detect enemy targets, which are reported through the Detected event.

    + +

    Process

    + +

    -- Note that the enemy is not engaged! To model enemy engagement, either tailor the Detected event, or +use derived AI_ classes to model AI offensive or defensive behaviour.

    + +

    Process

    + +

    Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. +When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.

    + +

    Process

    + +

    1.1) AIPATROLZONE constructor

    + + + +

    1.2) AIPATROLZONE is a FSM

    + +

    Process

    + +

    1.2.1) AIPATROLZONE States

    + +
      +
    • None ( Group ): The process is not started yet.
    • +
    • Patrolling ( Group ): The AI is patrolling the Patrol Zone.
    • +
    • Returning ( Group ): The AI is returning to Base.
    • +
    • Stopped ( Group ): The process is stopped.
    • +
    • Crashed ( Group ): The AI has crashed or is dead.
    • +
    + +

    1.2.2) AIPATROLZONE Events

    + +
      +
    • Start ( Group ): Start the process.
    • +
    • Stop ( Group ): Stop the process.
    • +
    • Route ( Group ): Route the AI to a new random 3D point within the Patrol Zone.
    • +
    • RTB ( Group ): Route the AI to the home base.
    • +
    • Detect ( Group ): The AI is detecting targets.
    • +
    • Detected ( Group ): The AI has detected new targets.
    • +
    • Status ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
    • +
    + +

    1.3) Set or Get the AI controllable

    + + + +

    1.4) Set the Speed and Altitude boundaries of the AI controllable

    + + + +

    1.5) Manage the detection process of the AI controllable

    + +

    The detection process of the AI controllable can be manipulated. +Detection requires an amount of CPU power, which has an impact on your mission performance. +Only put detection on when absolutely necessary, and the frequency of the detection can also be set.

    + + + +

    The detection frequency can be set with AIPATROLZONE.SetDetectionInterval( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection. +Use the method AIPATROLZONE.GetDetectedUnits() to obtain a list of the Units detected by the AI.

    + +

    The detection can be filtered to potential targets in a specific zone. +Use the method AIPATROLZONE.SetDetectionZone() to set the zone where targets need to be detected. +Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected +according the weather conditions.

    + +

    1.6) Manage the "out of fuel" in the AIPATROLZONE

    + +

    When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. +Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. +When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, +while a new AI is targetted to the AIPATROLZONE. +Once the time is finished, the old AI will return to the base. +Use the method AIPATROLZONE.ManageFuel() to have this proces in place.

    + +

    1.7) Manage "damage" behaviour of the AI in the AIPATROLZONE

    + +

    When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on. +Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB). +Use the method AIPATROLZONE.ManageDamage() to have this proces in place.

    + +
    @@ -783,20 +790,6 @@ Use the method AIPATROLZONE.M - -
    -
    -
    - - #string - -AI_PATROL_ZONE.ClassName - -
    -
    - - -
    @@ -920,6 +913,9 @@ Use the method AIPATROLZONE.M + +

    This table contains the targets detected during patrol.

    +
    diff --git a/docs/Documentation/Account.html b/docs/Documentation/Account.html index ca5687216..ad23ec3ca 100644 --- a/docs/Documentation/Account.html +++ b/docs/Documentation/Account.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -72,85 +73,11 @@

    Module Account

    -

    (SP) (MP) (FSM) Account for (Detect, count and report) DCS events occuring on DCS objects (units).

    +

    Actions - ACTACCOUNT classes account for (detect, count & report) various DCS events occuring on Units.

    -
    - -

    #ACT_ACCOUNT FSM class, extends Fsm#FSM_PROCESS

    - -

    ACT_ACCOUNT state machine:

    - -

    This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. -All derived classes from this class will start with the class name, followed by a _. See the relevant derived class descriptions below. -Each derived class follows exactly the same process, using the same events and following the same state transitions, -but will have different implementation behaviour upon each event or state transition.

    - -

    ACT_ACCOUNT Events:

    - -

    These are the events defined in this class:

    - -
      -
    • Start: The process is started. The process will go into the Report state.
    • -
    • Event: A relevant event has occured that needs to be accounted for. The process will go into the Account state.
    • -
    • Report: The process is reporting to the player the accounting status of the DCS events.
    • -
    • More: There are more DCS events that need to be accounted for. The process will go back into the Report state.
    • -
    • NoMore: There are no more DCS events that need to be accounted for. The process will go into the Success state.
    • -
    - -

    ACT_ACCOUNT Event methods:

    - -

    Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process. -There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:

    - -
      -
    • Immediate: The event method has exactly the name of the event.
    • -
    • Delayed: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
    • -
    - -

    ACT_ACCOUNT States:

    - -
      -
    • Assigned: The player is assigned to the task. This is the initialization state for the process.
    • -
    • Waiting: the process is waiting for a DCS event to occur within the simulator. This state is set automatically.
    • -
    • Report: The process is Reporting to the players in the group of the unit. This state is set automatically every 30 seconds.
    • -
    • Account: The relevant DCS event has occurred, and is accounted for.
    • -
    • Success (*): All DCS events were accounted for.
    • -
    • Failed (*): The process has failed.
    • -
    - -

    (*) End states of the process.

    - -

    ACT_ACCOUNT state transition methods:

    - -

    State transition functions can be set by the mission designer customizing or improving the behaviour of the state. -There are 2 moments when state transition methods will be called by the state machine:

    - -
      -
    • Before the state transition. - The state transition method needs to start with the name OnBefore + the name of the state. - If the state transition method returns false, then the processing of the state transition will not be done! - If you want to change the behaviour of the AIControllable at this event, return false, - but then you'll need to specify your own logic using the AIControllable!

    • -
    • After the state transition. - The state transition method needs to start with the name OnAfter + the name of the state. - These state transition methods need to provide a return value, which is specified at the function description.

    • -
    - -

    1) #ACTACCOUNTDEADS FSM class, extends Fsm.Account#ACT_ACCOUNT

    - -

    The ACTACCOUNTDEADS class accounts (detects, counts and reports) successful kills of DCS units. -The process is given a Set of units that will be tracked upon successful destruction. -The process will end after each target has been successfully destroyed. -Each successful dead will trigger an Account state transition that can be scored, modified or administered.

    - - -

    ACTACCOUNTDEADS constructor:

    - - +

    Banner Image


    @@ -279,25 +206,19 @@ Each successful dead will trigger an Account state transition that can be scored - ACT_ACCOUNT_DEADS:_Destructor() - - - - - - ACT_ACCOUNT_DEADS:onafterEvent(ProcessUnit, Event, From, To, EventData) + ACT_ACCOUNT_DEADS:onafterEvent(ProcessUnit, Event, From, To, Task)

    StateMachine callback function

    - ACT_ACCOUNT_DEADS:onenterAccount(ProcessUnit, Event, From, To, EventData) + ACT_ACCOUNT_DEADS:onenterAccount(ProcessUnit, Event, From, To, Task, EventData)

    StateMachine callback function

    - ACT_ACCOUNT_DEADS:onenterReport(ProcessUnit, Event, From, To) + ACT_ACCOUNT_DEADS:onenterReport(ProcessUnit, Event, From, To, Task)

    StateMachine callback function

    @@ -343,7 +264,58 @@ Each successful dead will trigger an Account state transition that can be scored

    Type ACT_ACCOUNT

    -

    ACT_ACCOUNT class

    +

    #ACT_ACCOUNT FSM class, extends Fsm#FSM_PROCESS

    + +

    ACT_ACCOUNT state machine:

    + +

    This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.

    + + +

    All derived classes from this class will start with the class name, followed by a _. See the relevant derived class descriptions below. +Each derived class follows exactly the same process, using the same events and following the same state transitions, +but will have different implementation behaviour upon each event or state transition.

    + +

    ACT_ACCOUNT States

    + +
      +
    • Asigned: The player is assigned.
    • +
    • Waiting: Waiting for an event.
    • +
    • Report: Reporting.
    • +
    • Account: Account for an event.
    • +
    • Accounted: All events have been accounted for, end of the process.
    • +
    • Failed: Failed the process.
    • +
    + +

    ACT_ACCOUNT Events

    + +
      +
    • Start: Start the process.
    • +
    • Wait: Wait for an event.
    • +
    • Report: Report the status of the accounting.
    • +
    • Event: An event happened, process the event.
    • +
    • More: More targets.
    • +
    • NoMore (*): No more targets.
    • +
    • Fail (*): The action process has failed.
    • +
    + +

    (*) End states of the process.

    + +

    ACT_ACCOUNT state transition methods:

    + +

    State transition functions can be set by the mission designer customizing or improving the behaviour of the state. +There are 2 moments when state transition methods will be called by the state machine:

    + +
      +
    • Before the state transition. + The state transition method needs to start with the name OnBefore + the name of the state. + If the state transition method returns false, then the processing of the state transition will not be done! + If you want to change the behaviour of the AIControllable at this event, return false, + but then you'll need to specify your own logic using the AIControllable!

    • +
    • After the state transition. + The state transition method needs to start with the name OnAfter + the name of the state. + These state transition methods need to provide a return value, which is specified at the function description.

      +
    • +

    Field(s)

    @@ -517,7 +489,22 @@ Each successful dead will trigger an Account state transition that can be scored

    Type ACT_ACCOUNT_DEADS

    -

    ACTACCOUNTDEADS class

    +

    #ACTACCOUNTDEADS FSM class, extends Fsm.Account#ACT_ACCOUNT

    + +

    The ACTACCOUNTDEADS class accounts (detects, counts and reports) successful kills of DCS units.

    + + +

    The process is given a Set of units that will be tracked upon successful destruction. +The process will end after each target has been successfully destroyed. +Each successful dead will trigger an Account state transition that can be scored, modified or administered.

    + + +

    ACTACCOUNTDEADS constructor:

    + +

    Field(s)

    @@ -683,26 +670,13 @@ Each successful dead will trigger an Account state transition that can be scored - -
    -
    -
    - - -ACT_ACCOUNT_DEADS:_Destructor() - -
    -
    - - -
    -ACT_ACCOUNT_DEADS:onafterEvent(ProcessUnit, Event, From, To, EventData) +ACT_ACCOUNT_DEADS:onafterEvent(ProcessUnit, Event, From, To, Task)
    @@ -733,7 +707,7 @@ Each successful dead will trigger an Account state transition that can be scored
  • -

    EventData :

    +

    Task :

  • @@ -743,7 +717,7 @@ Each successful dead will trigger an Account state transition that can be scored
    -ACT_ACCOUNT_DEADS:onenterAccount(ProcessUnit, Event, From, To, EventData) +ACT_ACCOUNT_DEADS:onenterAccount(ProcessUnit, Event, From, To, Task, EventData)
    @@ -774,6 +748,11 @@ Each successful dead will trigger an Account state transition that can be scored
  • +

    Task :

    + +
  • +
  • +

    EventData :

  • @@ -784,7 +763,7 @@ Each successful dead will trigger an Account state transition that can be scored
    -ACT_ACCOUNT_DEADS:onenterReport(ProcessUnit, Event, From, To) +ACT_ACCOUNT_DEADS:onenterReport(ProcessUnit, Event, From, To, Task)
    @@ -812,6 +791,11 @@ Each successful dead will trigger an Account state transition that can be scored

    #string To :

    + +
  • + +

    Task :

    +
  • diff --git a/docs/Documentation/Airbase.html b/docs/Documentation/Airbase.html index b1cb49a7f..328b41564 100644 --- a/docs/Documentation/Airbase.html +++ b/docs/Documentation/Airbase.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/AirbasePolice.html b/docs/Documentation/AirbasePolice.html index e80898903..93fdd0ae2 100644 --- a/docs/Documentation/AirbasePolice.html +++ b/docs/Documentation/AirbasePolice.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Assign.html b/docs/Documentation/Assign.html index fd741ec8a..e352ed51e 100644 --- a/docs/Documentation/Assign.html +++ b/docs/Documentation/Assign.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Base.html b/docs/Documentation/Base.html index ccdb2b19e..6a32f5c84 100644 --- a/docs/Documentation/Base.html +++ b/docs/Documentation/Base.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -81,187 +82,7 @@
    -

    1) #BASE class

    - -

    All classes within the MOOSE framework are derived from the #BASE class.

    - -

    BASE provides facilities for :

    - -
      -
    • The construction and inheritance of MOOSE classes.
    • -
    • The class naming and numbering system.
    • -
    • The class hierarchy search system.
    • -
    • The tracing of information or objects during mission execution for debuggin purposes.
    • -
    • The subscription to DCS events for event handling in MOOSE objects.
    • -
    - -

    Note: The BASE class is an abstract class and is not meant to be used directly.

    - -

    1.1) BASE constructor

    - -

    Any class derived from BASE, will use the Base#BASE.New constructor embedded in the Base#BASE.Inherit method. -See an example at the Base#BASE.New method how this is done.

    - -

    1.2) Trace information for debugging

    - -

    The BASE class contains trace methods to trace progress within a mission execution of a certain object. -These trace methods are inherited by each MOOSE class interiting BASE, soeach object created from derived class from BASE can use the tracing methods to trace its execution.

    - -

    Any type of information can be passed to these tracing methods. See the following examples:

    - -
    self:E( "Hello" )
    -
    - -

    Result in the word "Hello" in the dcs.log.

    - -
    local Array = { 1, nil, "h", { "a","b" }, "x" }
    -self:E( Array )
    -
    - -

    Results with the text [1]=1,[3]="h",[4]={[1]="a",[2]="b"},[5]="x"} in the dcs.log.

    - -
    local Object1 = "Object1"
    -local Object2 = 3
    -local Object3 = { Object 1, Object 2 }
    -self:E( { Object1, Object2, Object3 } )
    -
    - -

    Results with the text [1]={[1]="Object",[2]=3,[3]={[1]="Object",[2]=3}} in the dcs.log.

    - -
    local SpawnObject = SPAWN:New( "Plane" )
    -local GroupObject = GROUP:FindByName( "Group" )
    -self:E( { Spawn = SpawnObject, Group = GroupObject } )
    -
    - -

    Results with the text [1]={Spawn={....),Group={...}} in the dcs.log.

    - -

    Below a more detailed explanation of the different method types for tracing.

    - -

    1.2.1) Tracing methods categories

    - -

    There are basically 3 types of tracing methods available:

    - -
      -
    • BASE.F: Used to trace the entrance of a function and its given parameters. An F is indicated at column 44 in the DCS.log file.
    • -
    • BASE.T: Used to trace further logic within a function giving optional variables or parameters. A T is indicated at column 44 in the DCS.log file.
    • -
    • BASE.E: Used to always trace information giving optional variables or parameters. An E is indicated at column 44 in the DCS.log file.
    • -
    - -

    1.2.2) Tracing levels

    - -

    There are 3 tracing levels within MOOSE.
    -These tracing levels were defined to avoid bulks of tracing to be generated by lots of objects.

    - -

    As such, the F and T methods have additional variants to trace level 2 and 3 respectively:

    - -
      -
    • BASE.F2: Trace the beginning of a function and its given parameters with tracing level 2.
    • -
    • BASE.F3: Trace the beginning of a function and its given parameters with tracing level 3.
    • -
    • BASE.T2: Trace further logic within a function giving optional variables or parameters with tracing level 2.
    • -
    • BASE.T3: Trace further logic within a function giving optional variables or parameters with tracing level 3.
    • -
    - -

    1.2.3) Trace activation.

    - -

    Tracing can be activated in several ways:

    - -
      -
    • Switch tracing on or off through the BASE.TraceOnOff() method.
    • -
    • Activate all tracing through the BASE.TraceAll() method.
    • -
    • Activate only the tracing of a certain class (name) through the BASE.TraceClass() method.
    • -
    • Activate only the tracing of a certain method of a certain class through the BASE.TraceClassMethod() method.
    • -
    • Activate only the tracing of a certain level through the BASE.TraceLevel() method.
    • -
    - -

    1.2.4) Check if tracing is on.

    - -

    The method BASE.IsTrace() will validate if tracing is activated or not.

    - -

    1.3 DCS simulator Event Handling

    - -

    The BASE class provides methods to catch DCS Events. These are events that are triggered from within the DCS simulator, -and handled through lua scripting. MOOSE provides an encapsulation to handle these events more efficiently.

    - -

    1.3.1 Subscribe / Unsubscribe to DCS Events

    - -

    At first, the mission designer will need to Subscribe to a specific DCS event for the class. -So, when the DCS event occurs, the class will be notified of that event. -There are two methods which you use to subscribe to or unsubscribe from an event.

    - - - -

    1.3.2 Event Handling of DCS Events

    - -

    Once the class is subscribed to the event, an Event Handling method on the object or class needs to be written that will be called -when the DCS event occurs. The Event Handling method receives an Event#EVENTDATA structure, which contains a lot of information -about the event that occurred.

    - -

    Find below an example of the prototype how to write an event handling function for two units:

    - -
     local Tank1 = UNIT:FindByName( "Tank A" )
    - local Tank2 = UNIT:FindByName( "Tank B" )
    -
    - -- Here we subscribe to the Dead events. So, if one of these tanks dies, the Tank1 or Tank2 objects will be notified.
    - Tank1:HandleEvent( EVENTS.Dead )
    - Tank2:HandleEvent( EVENTS.Dead )
    -
    - --- This function is an Event Handling function that will be called when Tank1 is Dead.
    - -- @param Wrapper.Unit#UNIT self 
    - -- @param Core.Event#EVENTDATA EventData
    - function Tank1:OnEventDead( EventData )
    -
    -   self:SmokeGreen()
    - end
    -
    - --- This function is an Event Handling function that will be called when Tank2 is Dead.
    - -- @param Wrapper.Unit#UNIT self 
    - -- @param Core.Event#EVENTDATA EventData
    - function Tank2:OnEventDead( EventData )
    -
    -   self:SmokeBlue()
    - end
    -
    - - - -

    See the Event module for more information about event handling.

    - -

    1.4) Class identification methods

    - -

    BASE provides methods to get more information of each object:

    - -
      -
    • BASE.GetClassID(): Gets the ID (number) of the object. Each object created is assigned a number, that is incremented by one.
    • -
    • BASE.GetClassName(): Gets the name of the object, which is the name of the class the object was instantiated from.
    • -
    • BASE.GetClassNameAndID(): Gets the name and ID of the object.
    • -
    - -

    1.5) All objects derived from BASE can have "States"

    - -

    A mechanism is in place in MOOSE, that allows to let the objects administer states.
    -States are essentially properties of objects, which are identified by a Key and a Value.

    - -

    The method BASE.SetState() can be used to set a Value with a reference Key to the object.
    -To read or retrieve a state Value based on a Key, use the BASE.GetState method.

    - -

    These two methods provide a very handy way to keep state at long lasting processes. -Values can be stored within the objects, and later retrieved or changed when needed. -There is one other important thing to note, the BASE.SetState() and BASE.GetState methods -receive as the first parameter the object for which the state needs to be set. -Thus, if the state is to be set for the same object as the object for which the method is used, then provide the same -object name to the method.

    - -

    1.10) Inheritance

    - -

    The following methods are available to implement inheritance

    - -
      -
    • BASE.Inherit: Inherits from a class.
    • -
    • BASE.GetParent: Returns the parent object from the object it is handling, or nil if there is no parent object.
    • -
    +

    The #BASE class is the core root class from where every other class in moose is derived.


    @@ -301,7 +122,9 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    BASE +

    1) #BASE class

    +

    All classes within the MOOSE framework are derived from the BASE class.

    @@ -365,12 +188,6 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    BASE:EventRemoveAll()

    Remove all subscribed events

    - - - - BASE.Events - - @@ -590,12 +407,6 @@ place: Object that the unit landed on.

    BASE:SetState(Object, Key, Value)

    Set a state or property of the Object given a Key and a Value.

    - - - - BASE.States - - @@ -664,12 +475,6 @@ When Moose is loaded statically, (as one file), tracing is switched off by defau BASE:_F(Arguments, DebugInfoCurrentParam, DebugInfoFromParam)

    Trace a function call.

    - - - - BASE._Private - - @@ -713,6 +518,191 @@ When Moose is loaded statically, (as one file), tracing is switched off by defau
    +

    1) #BASE class

    + +

    All classes within the MOOSE framework are derived from the BASE class.

    + + +

    +BASE provides facilities for :

    + +
      +
    • The construction and inheritance of MOOSE classes.
    • +
    • The class naming and numbering system.
    • +
    • The class hierarchy search system.
    • +
    • The tracing of information or objects during mission execution for debuggin purposes.
    • +
    • The subscription to DCS events for event handling in MOOSE objects.
    • +
    + +

    Note: The BASE class is an abstract class and is not meant to be used directly.

    + +

    1.1) BASE constructor

    + +

    Any class derived from BASE, will use the Base#BASE.New constructor embedded in the Base#BASE.Inherit method. +See an example at the Base#BASE.New method how this is done.

    + +

    1.2) Trace information for debugging

    + +

    The BASE class contains trace methods to trace progress within a mission execution of a certain object. +These trace methods are inherited by each MOOSE class interiting BASE, soeach object created from derived class from BASE can use the tracing methods to trace its execution.

    + +

    Any type of information can be passed to these tracing methods. See the following examples:

    + +
    self:E( "Hello" )
    +
    + +

    Result in the word "Hello" in the dcs.log.

    + +
    local Array = { 1, nil, "h", { "a","b" }, "x" }
    +self:E( Array )
    +
    + +

    Results with the text [1]=1,[3]="h",[4]={[1]="a",[2]="b"},[5]="x"} in the dcs.log.

    + +
    local Object1 = "Object1"
    +local Object2 = 3
    +local Object3 = { Object 1, Object 2 }
    +self:E( { Object1, Object2, Object3 } )
    +
    + +

    Results with the text [1]={[1]="Object",[2]=3,[3]={[1]="Object",[2]=3}} in the dcs.log.

    + +
    local SpawnObject = SPAWN:New( "Plane" )
    +local GroupObject = GROUP:FindByName( "Group" )
    +self:E( { Spawn = SpawnObject, Group = GroupObject } )
    +
    + +

    Results with the text [1]={Spawn={....),Group={...}} in the dcs.log.

    + +

    Below a more detailed explanation of the different method types for tracing.

    + +

    1.2.1) Tracing methods categories

    + +

    There are basically 3 types of tracing methods available:

    + +
      +
    • BASE.F: Used to trace the entrance of a function and its given parameters. An F is indicated at column 44 in the DCS.log file.
    • +
    • BASE.T: Used to trace further logic within a function giving optional variables or parameters. A T is indicated at column 44 in the DCS.log file.
    • +
    • BASE.E: Used to always trace information giving optional variables or parameters. An E is indicated at column 44 in the DCS.log file.
    • +
    + +

    1.2.2) Tracing levels

    + +

    There are 3 tracing levels within MOOSE.
    +These tracing levels were defined to avoid bulks of tracing to be generated by lots of objects.

    + +

    As such, the F and T methods have additional variants to trace level 2 and 3 respectively:

    + +
      +
    • BASE.F2: Trace the beginning of a function and its given parameters with tracing level 2.
    • +
    • BASE.F3: Trace the beginning of a function and its given parameters with tracing level 3.
    • +
    • BASE.T2: Trace further logic within a function giving optional variables or parameters with tracing level 2.
    • +
    • BASE.T3: Trace further logic within a function giving optional variables or parameters with tracing level 3.
    • +
    + +

    1.2.3) Trace activation.

    + +

    Tracing can be activated in several ways:

    + +
      +
    • Switch tracing on or off through the BASE.TraceOnOff() method.
    • +
    • Activate all tracing through the BASE.TraceAll() method.
    • +
    • Activate only the tracing of a certain class (name) through the BASE.TraceClass() method.
    • +
    • Activate only the tracing of a certain method of a certain class through the BASE.TraceClassMethod() method.
    • +
    • Activate only the tracing of a certain level through the BASE.TraceLevel() method.
    • +
    + +

    1.2.4) Check if tracing is on.

    + +

    The method BASE.IsTrace() will validate if tracing is activated or not.

    + +

    1.3 DCS simulator Event Handling

    + +

    The BASE class provides methods to catch DCS Events. These are events that are triggered from within the DCS simulator, +and handled through lua scripting. MOOSE provides an encapsulation to handle these events more efficiently.

    + +

    1.3.1 Subscribe / Unsubscribe to DCS Events

    + +

    At first, the mission designer will need to Subscribe to a specific DCS event for the class. +So, when the DCS event occurs, the class will be notified of that event. +There are two methods which you use to subscribe to or unsubscribe from an event.

    + + + +

    1.3.2 Event Handling of DCS Events

    + +

    Once the class is subscribed to the event, an Event Handling method on the object or class needs to be written that will be called +when the DCS event occurs. The Event Handling method receives an Event#EVENTDATA structure, which contains a lot of information +about the event that occurred.

    + +

    Find below an example of the prototype how to write an event handling function for two units:

    + +
     local Tank1 = UNIT:FindByName( "Tank A" )
    + local Tank2 = UNIT:FindByName( "Tank B" )
    +
    + -- Here we subscribe to the Dead events. So, if one of these tanks dies, the Tank1 or Tank2 objects will be notified.
    + Tank1:HandleEvent( EVENTS.Dead )
    + Tank2:HandleEvent( EVENTS.Dead )
    +
    + --- This function is an Event Handling function that will be called when Tank1 is Dead.
    + -- @param Wrapper.Unit#UNIT self 
    + -- @param Core.Event#EVENTDATA EventData
    + function Tank1:OnEventDead( EventData )
    +
    +   self:SmokeGreen()
    + end
    +
    + --- This function is an Event Handling function that will be called when Tank2 is Dead.
    + -- @param Wrapper.Unit#UNIT self 
    + -- @param Core.Event#EVENTDATA EventData
    + function Tank2:OnEventDead( EventData )
    +
    +   self:SmokeBlue()
    + end
    +
    + + + +

    See the Event module for more information about event handling.

    + +

    1.4) Class identification methods

    + +

    BASE provides methods to get more information of each object:

    + +
      +
    • BASE.GetClassID(): Gets the ID (number) of the object. Each object created is assigned a number, that is incremented by one.
    • +
    • BASE.GetClassName(): Gets the name of the object, which is the name of the class the object was instantiated from.
    • +
    • BASE.GetClassNameAndID(): Gets the name and ID of the object.
    • +
    + +

    1.5) All objects derived from BASE can have "States"

    + +

    A mechanism is in place in MOOSE, that allows to let the objects administer states.
    +States are essentially properties of objects, which are identified by a Key and a Value.

    + +

    The method BASE.SetState() can be used to set a Value with a reference Key to the object.
    +To read or retrieve a state Value based on a Key, use the BASE.GetState method.

    + +

    These two methods provide a very handy way to keep state at long lasting processes. +Values can be stored within the objects, and later retrieved or changed when needed. +There is one other important thing to note, the BASE.SetState() and BASE.GetState methods +receive as the first parameter the object for which the state needs to be set. +Thus, if the state is to be set for the same object as the object for which the method is used, then provide the same +object name to the method.

    + +

    1.10) Inheritance

    + +

    The following methods are available to implement inheritance

    + +
      +
    • BASE.Inherit: Inherits from a class.
    • +
    • BASE.GetParent: Returns the parent object from the object it is handling, or nil if there is no parent object.
    • +
    + +
    @@ -734,10 +724,7 @@ When Moose is loaded statically, (as one file), tracing is switched off by defau

    Type Base

    Type BASE

    - -

    The BASE Class

    - -

    Field(s)

    +

    Field(s)

    @@ -934,20 +921,6 @@ A #table or any field.

    #BASE:

    - -
    -
    -
    - - - -BASE.Events - -
    -
    - - -
    @@ -1901,20 +1874,6 @@ The Key was not found and thus the Value could not be retrieved.

    - -
    -
    -
    - - - -BASE.States - -
    -
    - - -
    @@ -2189,20 +2148,6 @@ A #table or any field.

    - -
    -
    -
    - - - -BASE._Private - -
    -
    - - -
    @@ -2216,6 +2161,9 @@ A #table or any field.

    + +

    THIS IS WHY WE NEED LUA 5.2 ...

    +
    diff --git a/docs/Documentation/Cargo.html b/docs/Documentation/Cargo.html index 59202c294..d0b6aea75 100644 --- a/docs/Documentation/Cargo.html +++ b/docs/Documentation/Cargo.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/CleanUp.html b/docs/Documentation/CleanUp.html index 0fc27c9c6..60829d2bc 100644 --- a/docs/Documentation/CleanUp.html +++ b/docs/Documentation/CleanUp.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -101,9 +102,15 @@ - CLEANUP:New(ZoneNames, TimeInterval) + CLEANUP.CleanUpScheduler + + + + CLEANUP:New(ZoneNames, TimeInterval) + +

    Creates the main object which is handling the cleaning of the debris within the given Zone Names.

    @@ -173,7 +180,7 @@ - CLEANUP:_OnEventBirth(Event) + CLEANUP:_OnEventBirth(EventData) @@ -233,33 +240,61 @@
    - -CLEANUP:New(ZoneNames, TimeInterval) + + +CLEANUP.CleanUpScheduler
    -

    Parameters

    -
      -
    • - -

      ZoneNames :

      - -
    • -
    • - -

      TimeInterval :

      - -
    • -
    - #number + +CLEANUP:New(ZoneNames, TimeInterval) + +
    +
    + +

    Creates the main object which is handling the cleaning of the debris within the given Zone Names.

    + +

    Parameters

    +
      +
    • + +

      #table ZoneNames : +Is a table of zone names where the debris should be cleaned. Also a single string can be passed with one zone name.

      + +
    • +
    • + +

      #number TimeInterval : +The interval in seconds when the clean activity takes place. The default is 300 seconds, thus every 5 minutes.

      + +
    • +
    +

    Return value

    + +

    #CLEANUP:

    + + +

    Usage:

    +
     -- Clean these Zones.
    +CleanUpAirports = CLEANUP:New( { 'CLEAN Tbilisi', 'CLEAN Kutaisi' }, 150 )
    +or
    +CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 )
    +CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 )
    + +
    +
    +
    +
    + + CLEANUP.TimeInterval @@ -526,7 +561,7 @@ The Unit name ...

    -CLEANUP:_OnEventBirth(Event) +CLEANUP:_OnEventBirth(EventData)
    @@ -537,7 +572,7 @@ The Unit name ...

    diff --git a/docs/Documentation/Client.html b/docs/Documentation/Client.html index 799a07533..565f34b9b 100644 --- a/docs/Documentation/Client.html +++ b/docs/Documentation/Client.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -209,7 +210,7 @@ If the DCS Unit object does not exist or is nil, the CLIENT methods will return - CLIENT:Find(ClientName, ClientBriefing, DCSUnit) + CLIENT:Find(ClientName, ClientBriefing, DCSUnit, Error)

    Finds a CLIENT from the _DATABASE using the relevant DCS Unit.

    @@ -593,7 +594,7 @@ Create a function that will be called when a player joins the slot.

    -CLIENT:Find(ClientName, ClientBriefing, DCSUnit) +CLIENT:Find(ClientName, ClientBriefing, DCSUnit, Error)
    @@ -618,6 +619,11 @@ Text that describes the briefing of the mission when a Player logs into the Clie

    DCSUnit :

    + +
  • + +

    Error :

    +
  • Return value

    diff --git a/docs/Documentation/CommandCenter.html b/docs/Documentation/CommandCenter.html index 2dddeca01..3384b0fa4 100644 --- a/docs/Documentation/CommandCenter.html +++ b/docs/Documentation/CommandCenter.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -128,6 +129,12 @@ COMMANDCENTER.CommandCenterPositionable + + + + COMMANDCENTER:GetMenu() + +

    Gets the commandcenter menu structure governed by the HQ command center.

    @@ -158,6 +165,12 @@ COMMANDCENTER:HasGroup(Wrapper, MissionGroup)

    Checks of the COMMANDCENTER has a GROUP.

    + + + + COMMANDCENTER:MessageToAll(Message) + +

    Send a CC message to the coalition of the CC.

    @@ -231,9 +244,9 @@ - REPORT:Text() + REPORT:Text(Delimiter) - +

    Produces the text of the report, taking into account an optional delimeter, which is \n by default.

    @@ -366,6 +379,24 @@ +
    +
    +
    +
    + + +COMMANDCENTER:GetMenu() + +
    +
    + +

    Gets the commandcenter menu structure governed by the HQ command center.

    + +

    Return value

    + +

    Core.Menu#MENU_COALITION:

    + +
    @@ -471,6 +502,27 @@ Group#GROUP

    + +COMMANDCENTER:MessageToAll(Message) + +
    +
    + +

    Send a CC message to the coalition of the CC.

    + +

    Parameter

    +
      +
    • + +

      Message :

      + +
    • +
    +
    +
    +
    +
    + COMMANDCENTER:MessageToCoalition(Message) @@ -737,12 +789,26 @@ Group#GROUP

    -REPORT:Text() +REPORT:Text(Delimiter)
    +

    Produces the text of the report, taking into account an optional delimeter, which is \n by default.

    +

    Parameter

    +
      +
    • + +

      #string Delimiter : +(optional) A delimiter text.

      + +
    • +
    +

    Return value

    + +

    #string: +The report text.

    diff --git a/docs/Documentation/Controllable.html b/docs/Documentation/Controllable.html index 93075e7d0..22bfaf97f 100644 --- a/docs/Documentation/Controllable.html +++ b/docs/Documentation/Controllable.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -245,9 +246,9 @@ This is different from the EnRoute tasks, where the targets of the task need to - CONTROLLABLE:CommandStopRoute(StopRoute, Index) + CONTROLLABLE:CommandStopRoute(StopRoute) -

    Perform stop route command

    +

    Create a stop route command, which returns a string containing the command.

    @@ -301,7 +302,7 @@ This is different from the EnRoute tasks, where the targets of the task need to CONTROLLABLE:EnRouteTaskEngageUnit(EngageUnit, Priority, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack) -

    (AIR) Attack the Unit.

    +

    (AIR) Search and attack the Unit.

    @@ -482,13 +483,6 @@ This is different from the EnRoute tasks, where the targets of the task need to CONTROLLABLE:Route(GoPoints)

    Make the controllable to follow a given route.

    - - - - CONTROLLABLE:RouteReturnToAirbase(ReturnAirbase, Speed) - -

    (AIR) Return the Controllable to an Airbase#AIRBASE -A speed can be given in km/h.

    @@ -528,7 +522,7 @@ A speed can be given in km/h.

    - CONTROLLABLE:TaskAttackUnit(AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack) + CONTROLLABLE:TaskAttackUnit(AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType)

    (AIR) Attack the Unit.

    @@ -793,23 +787,31 @@ A speed can be given in km/h.

    -CONTROLLABLE:CommandStopRoute(StopRoute, Index) +CONTROLLABLE:CommandStopRoute(StopRoute)
    -

    Perform stop route command

    +

    Create a stop route command, which returns a string containing the command.

    -

    Parameters

    + +

    Use the result in the method CONTROLLABLE.SetCommand(). +A value of true will make the ground group stop, a value of false will make it continue. +Note that this can only work on GROUP level, although individual UNITs can be commanded, the whole GROUP will react.

    + +

    Example missions:

    + +
      +
    • GRP-310 +
    • +
    + +

    Parameter

    • -

      #boolean StopRoute :

      - -
    • -
    • - -

      Index :

      +

      #boolean StopRoute : +true if the ground unit needs to stop, false if it needs to continue to move.

    @@ -1094,7 +1096,7 @@ The DCS task structure.

    -

    (AIR) Attack the Unit.

    +

    (AIR) Search and attack the Unit.

    Parameters

    diff --git a/docs/Documentation/Database.html b/docs/Documentation/Database.html index be41b649d..0309494e8 100644 --- a/docs/Documentation/Database.html +++ b/docs/Documentation/Database.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -169,6 +170,18 @@ The following iterator methods are currently available within the DATABASE:

    DATABASE.CLIENTS + + + + DATABASE.COUNTRY_ID + + + + + + DATABASE.COUNTRY_NAME + + @@ -628,6 +641,34 @@ The following iterator methods are currently available within the DATABASE:

    +
    +
    +
    +
    + + + +DATABASE.COUNTRY_ID + +
    +
    + + + +
    +
    +
    +
    + + + +DATABASE.COUNTRY_NAME + +
    +
    + + +
    diff --git a/docs/Documentation/Detection.html b/docs/Documentation/Detection.html index 4c3e89eab..7002da0ba 100644 --- a/docs/Documentation/Detection.html +++ b/docs/Documentation/Detection.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -72,79 +73,33 @@

    Module Detection

    -

    This module contains the DETECTION classes.

    +

    Functional - DETECTION_ classes model the detection of enemy units by FACs or RECCEs and group them according various methods.

    -

    -

    1) Detection#DETECTION_BASE class, extends Base#BASE

    -

    The Detection#DETECTION_BASE class defines the core functions to administer detected objects. - The Detection#DETECTION_BASE class will detect objects within the battle zone for a list of Groups detecting targets following (a) detection method(s).

    - -

    1.1) DETECTION_BASE constructor

    -

    Construct a new DETECTION_BASE instance using the Detection#DETECTION_BASE.New() method.

    - -

    1.2) DETECTION_BASE initialization

    -

    By default, detection will return detected objects with all the detection sensors available. - However, you can ask how the objects were found with specific detection methods. - If you use one of the below methods, the detection will work with the detection method specified. - You can specify to apply multiple detection methods.

    - -

    Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK:

    - - - -

    1.3) Obtain objects detected by DETECTION_BASE

    -

    DETECTION_BASE builds Sets of objects detected. These Set#SET_BASEs can be retrieved using the method Detection#DETECTION_BASE.GetDetectedSets(). - The method will return a list (table) of Set#SET_BASE objects.

    +

    Banner Image


    -

    2) Detection#DETECTION_AREAS class, extends Detection#DETECTION_BASE

    -

    The Detection#DETECTION_AREAS class will detect units within the battle zone for a list of Groups detecting targets following (a) detection method(s), - and will build a list (table) of Set#SET_UNITs containing the Unit#UNITs detected. - The class is group the detected units within zones given a DetectedZoneRange parameter. - A set with multiple detected zones will be created as there are groups of units detected.

    +

    DETECTION classes facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units). +DETECTION uses the in-built detection capabilities of DCS World, but adds new functionalities.

    -

    2.1) Retrieve the Detected Unit sets and Detected Zones

    -

    The DetectedUnitSets methods are implemented in Detection#DECTECTION_BASE and the DetectedZones methods is implemented in Detection#DETECTION_AREAS.

    +

    Please watch this youtube video that explains the detection concepts.

    -

    Retrieve the DetectedUnitSets with the method Detection#DETECTION_BASE.GetDetectedSets(). A table will be return of Set#SET_UNITs. - To understand the amount of sets created, use the method Detection#DETECTION_BASE.GetDetectedSetCount(). - If you want to obtain a specific set from the DetectedSets, use the method Detection#DETECTION_BASE.GetDetectedSet() with a given index.

    -

    Retrieve the formed Zones as a result of the grouping the detected units within the DetectionZoneRange, use the method Detection#DETECTION_BASE.GetDetectionZones(). - To understand the amount of zones created, use the method Detection#DETECTION_BASE.GetDetectionZoneCount(). - If you want to obtain a specific zone from the DetectedZones, use the method Detection#DETECTION_BASE.GetDetectionZone() with a given index.

    - -

    1.4) Flare or Smoke detected units

    -

    Use the methods Detection#DETECTION_AREAS.FlareDetectedUnits() or Detection#DETECTION_AREAS.SmokeDetectedUnits() to flare or smoke the detected units when a new detection has taken place.

    - -

    1.5) Flare or Smoke detected zones

    -

    Use the methods Detection#DETECTION_AREAS.FlareDetectedZones() or Detection#DETECTION_AREAS.SmokeDetectedZones() to flare or smoke the detected zones when a new detection has taken place.

    - -
    - -

    ### Contributions:

    +

    Contributions:

      -
    • Mechanist : Concept & Testing
    • +
    • Mechanist : Early concept of DETECTION_AREAS.
    -

    ### Authors:

    +

    Authors:

      -
    • FlightControl : Design & Programming -
    • +
    • FlightControl : Analysis, Design, Programming, Testing
    +

    Global(s)

    @@ -157,39 +112,33 @@ + + + + + + + +
    DETECTION_BASE +
    DETECTION_TYPES + +
    DETECTION_UNITS +

    Type DETECTION_AREAS

    - + - + - - - - - - - - - - - - @@ -205,7 +154,13 @@ - + + + + + @@ -229,69 +184,33 @@ - + - + - - - - - - - - - - - - - - - - - + - + - + - - - - - - - - @@ -304,6 +223,12 @@ + + + + @@ -328,58 +253,6 @@ - -
    DETECTION_AREAS:AcceptChanges(DetectedArea)DETECTION_AREAS:BoundDetectedZones() -

    Accepts changes from the detected zone.

    +

    Bound the detected zones

    DETECTION_AREAS:AddChangeArea(DetectedArea, ChangeCode, AreaUnitType)DETECTION_AREAS:CalculateThreatLevelA2G(DetectedItem) -

    Add a change to the detected zone.

    -
    DETECTION_AREAS:AddChangeUnit(DetectedArea, ChangeCode, ChangeUnitType) -

    Add a change to the detected zone.

    -
    DETECTION_AREAS.AddDetectedArea(Set, Zone, self) -

    Add a detected DETECTION_AREAS.DetectedArea.

    -
    DETECTION_AREAS:CalculateThreatLevelA2G(DetectedArea) -

    Calculate the maxium A2G threat level of the DetectedArea.

    +

    Calculate the maxium A2G threat level of the DetectedItem.

    DETECTION_AREAS.DetectedAreasDETECTION_AREAS:DetectedItemReportSummary(Index) +

    Report summary of a detected item using a given numeric index.

    +
    DETECTION_AREAS.DetectedItems

    A list of areas containing the set of Units, Zones, the center Unit within the zone, and ID of each area that was detected within a DetectionZoneRange.

    DETECTION_AREAS:GetChangeText(DetectedArea)DETECTION_AREAS:GetChangeText(DetectedItem)

    Make text documenting the changes of the detected zone.

    DETECTION_AREAS:GetDetectedAreaCount()DETECTION_AREAS:GetTreatLevelA2G(DetectedItem) -

    Get the amount of DETECTION_AREAS.DetectedAreas.

    +

    Returns the A2G threat level of the units in the DetectedItem

    DETECTION_AREAS:GetDetectedAreas() -

    Get the detected DETECTION_AREAS.DetectedAreas.

    -
    DETECTION_AREAS:GetDetectedSet(Index) -

    Get the Set#SET_UNIT of a detecttion area using a given numeric index.

    -
    DETECTION_AREAS:GetDetectedZone(Index) -

    Get the Zone#ZONE_UNIT of a detection area using a given numeric index.

    -
    DETECTION_AREAS:GetTreatLevelA2G(DetectedArea) -

    Returns the A2G threat level of the units in the DetectedArea

    -
    DETECTION_AREAS:IsFriendliesNearBy(DetectedArea)DETECTION_AREAS:IsFriendliesNearBy(DetectedItem)

    Returns if there are friendlies nearby the FAC units ...

    DETECTION_AREAS:NearestFAC(DetectedArea)DETECTION_AREAS:NearestFAC(DetectedItem) -

    Find the nearest FAC of the DetectedArea.

    +

    Find the nearest FAC of the DetectedItem.

    DETECTION_AREAS:New(DetectionSetGroup, DetectionRange, DetectionZoneRange)DETECTION_AREAS:New(DetectionSetGroup, DetectionZoneRange)

    DETECTION_AREAS constructor.

    -
    DETECTION_AREAS:RemoveDetectedArea(Index) -

    Remove a detected DETECTION_AREAS.DetectedArea with a given Index.

    -
    DETECTION_AREAS:ReportFriendliesNearBy(ReportUnit, ReportGroupData) -

    Background worker function to determine if there are friendlies nearby ...

    DETECTION_AREAS:SmokeDetectedZones()

    Smoke the detected zones

    +
    DETECTION_AREAS._BoundDetectedZones +
    DETECTION_AREAS._SmokeDetectedZones -
    - -

    Type DETECTION_AREAS.DetectedArea

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    DETECTION_AREAS.DetectedArea.AreaID -

    -- The identifier of the detected area.

    -
    DETECTION_AREAS.DetectedArea.Changed -

    Documents if the detected area has changes.

    -
    DETECTION_AREAS.DetectedArea.Changes -

    A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes).

    -
    DETECTION_AREAS.DetectedArea.FriendliesNearBy -

    Indicates if there are friendlies within the detected area.

    -
    DETECTION_AREAS.DetectedArea.MaxThreatLevelA2G - -
    DETECTION_AREAS.DetectedArea.NearestFAC -

    The nearest FAC near the Area.

    -
    DETECTION_AREAS.DetectedArea.Set -

    -- The Set of Units in the detected area.

    -
    DETECTION_AREAS.DetectedArea.Zone -

    -- The Zone of the detected area.

    @@ -387,15 +260,75 @@

    Type DETECTION_BASE

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -432,6 +365,36 @@ + + + + + + + + + + + + + + + + + + + + @@ -444,6 +407,24 @@ + + + + + + + + + + + + @@ -468,6 +449,30 @@ + + + + + + + + + + + + + + + + @@ -479,19 +484,13 @@ - + - - - - @@ -549,9 +548,105 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -570,6 +665,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -585,9 +734,103 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DETECTION_BASE:AcceptChanges(DetectedItem) +

    Accepts changes from the detected item.

    +
    DETECTION_BASE.AcceptRange + +
    DETECTION_BASE.AcceptZones + +
    DETECTION_BASE:AddChangeItem(DetectedItem, ChangeCode, ItemUnitType) +

    Add a change to the detected zone.

    +
    DETECTION_BASE:AddChangeUnit(DetectedItem, ChangeCode, ChangeUnitType) +

    Add a change to the detected zone.

    +
    DETECTION_BASE:AddDetectedItem(DetectedItemIndex, Set) +

    Adds a new DetectedItem to the DetectedItems list.

    +
    DETECTION_BASE:AddDetectedItemZone(DetectedItemIndex, Set, Zone) +

    Adds a new DetectedItem to the DetectedItems list.

    +
    DETECTION_BASE.AlphaAngleProbability + +
    DETECTION_BASE.ClassName +
    DETECTION_BASE.CountryID +
    DETECTION_BASE:CreateDetectionSets()

    Make a DetectionSet table.

    +
    DETECTION_BASE:Detect() +

    Synchronous Event Trigger for Event Detect.

    DETECTION_BASE.DetectVisual +
    DETECTION_BASE:Detected() +

    Synchronous Event Trigger for Event Detected.

    +
    DETECTION_BASE.DetectedItemCount + +
    DETECTION_BASE.DetectedItemMax + +
    DETECTION_BASE:DetectedItemReportSummary(Index) +

    Report summary of a detected item using a given numeric index.

    +
    DETECTION_BASE.DetectedItems +
    DETECTION_BASE.DetectedObjectsIdentified

    Map of the DetectedObjects identified.

    +
    DETECTION_BASE:DetectedReportDetailed() +

    Report detailed of a detectedion result.

    +
    DETECTION_BASE.DetectionCount + +
    DETECTION_BASE.DetectionInterval +
    DETECTION_BASE.DetectionSetGroup

    The Set of GROUPs in the Forward Air Controller role.

    +
    DETECTION_BASE.DistanceProbability + +
    DETECTION_BASE:GetDetectedItem(Index) +

    Get a detected item using a given numeric index.

    +
    DETECTION_BASE:GetDetectedItems() +

    Get the detected Set#SET_BASEs.

    +
    DETECTION_BASE:GetDetectedItemsCount() +

    Get the amount of SETs with detected objects.

    DETECTION_BASE:GetDetectedSet(Index) -

    Get a SET of detected objects using a given numeric index.

    +

    Get the Set#SET_UNIT of a detecttion area using a given numeric index.

    DETECTION_BASE:GetDetectedSetCount()DETECTION_BASE:GetDetectedZone(Index) -

    Get the amount of SETs with detected objects.

    -
    DETECTION_BASE:GetDetectedSets() -

    Get the detected Set#SET_BASEs.

    +

    Get the Zone#ZONE_UNIT of a detection area using a given numeric index.

    DETECTION_BASE:New(DetectionSetGroup, DetectionRange)DETECTION_BASE:IsFriendliesNearBy(DetectedItem) +

    Returns if there are friendlies nearby the FAC units ...

    +
    DETECTION_BASE:New(DetectionSetGroup)

    DETECTION constructor.

    +
    DETECTION_BASE:OnAfterDetect(From, Event, To) +

    OnAfter Transition Handler for Event Detect.

    +
    DETECTION_BASE:OnAfterDetected(From, Event, To) +

    OnAfter Transition Handler for Event Detected.

    +
    DETECTION_BASE:OnAfterStart(From, Event, To) +

    OnAfter Transition Handler for Event Start.

    +
    DETECTION_BASE:OnAfterStop(From, Event, To) +

    OnAfter Transition Handler for Event Stop.

    +
    DETECTION_BASE:OnBeforeDetect(From, Event, To) +

    OnBefore Transition Handler for Event Detect.

    +
    DETECTION_BASE:OnBeforeDetected(From, Event, To) +

    OnBefore Transition Handler for Event Detected.

    +
    DETECTION_BASE:OnBeforeStart(From, Event, To) +

    OnBefore Transition Handler for Event Start.

    +
    DETECTION_BASE:OnBeforeStop(From, Event, To) +

    OnBefore Transition Handler for Event Stop.

    +
    DETECTION_BASE:OnEnterDetecting(From, Event, To) +

    OnEnter Transition Handler for State Detecting.

    +
    DETECTION_BASE:OnEnterStopped(From, Event, To) +

    OnEnter Transition Handler for State Stopped.

    +
    DETECTION_BASE:OnLeaveDetecting(From, Event, To) +

    OnLeave Transition Handler for State Detecting.

    +
    DETECTION_BASE:OnLeaveStopped(From, Event, To) +

    OnLeave Transition Handler for State Stopped.

    +
    DETECTION_BASE.RejectZones + +
    DETECTION_BASE:RemoveDetectedItem(DetectedItemIndex) +

    Removes an existing DetectedItem from the DetectedItems list.

    +
    DETECTION_BASE:ReportFriendliesNearBy(ReportGroupData) +

    Background worker function to determine if there are friendlies nearby ...

    DETECTION_BASE.ScheduleRepeatInterval +
    DETECTION_BASE:SetAcceptRange(AcceptRange) +

    Accept detections if within a range in meters.

    +
    DETECTION_BASE:SetAcceptZones(AcceptZones) +

    Accept detections if within the specified zone(s).

    +
    DETECTION_BASE:SetAlphaAngleProbability(AlphaAngleProbability) +

    Upon a visual detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly.

    +
    DETECTION_BASE:SetDetectionInterval(DetectionInterval) +

    Set the detection interval time in seconds.

    +
    DETECTION_BASE:SetDistanceProbability(DistanceProbability) +

    Upon a visual detection, the further away a detected object is, the less likely it is to be detected properly.

    +
    DETECTION_BASE:SetRejectZones(RejectZones) +

    Reject detections if within the specified zone(s).

    +
    DETECTION_BASE:SetZoneProbability(ZoneArray) +

    Upon a visual detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully.

    +
    DETECTION_BASE:Start() +

    Synchronous Event Trigger for Event Start.

    +
    DETECTION_BASE:Stop() +

    Synchronous Event Trigger for Event Stop.

    DETECTION_BASE:_DetectionScheduler(SchedulerName)DETECTION_BASE.ZoneProbability -

    Form Sets of detected Unit#UNITs in an array of Set#SET_BASEs.

    + +
    DETECTION_BASE:__Detect(Delay) +

    Asynchronous Event Trigger for Event Detect.

    +
    DETECTION_BASE:__Detected(Delay) +

    Asynchronous Event Trigger for Event Detected.

    +
    DETECTION_BASE:__Start(Delay) +

    Asynchronous Event Trigger for Event Start.

    +
    DETECTION_BASE:__Stop(Delay) +

    Asynchronous Event Trigger for Event Stop.

    +
    DETECTION_BASE:onafterDetect(From, Event, To) + +
    DETECTION_BASE:onafterDetectionGroup(From, Event, To, DetectionGroup) + +
    DETECTION_BASE:onafterStart(From, Event, To) + +
    + +

    Type DETECTION_BASE.DetectedItem

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DETECTION_BASE.DetectedItem.Changed +

    Documents if the detected area has changes.

    +
    DETECTION_BASE.DetectedItem.Changes +

    A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes).

    +
    DETECTION_BASE.DetectedItem.FriendliesNearBy +

    Indicates if there are friendlies within the detected area.

    +
    DETECTION_BASE.DetectedItem.ItemID +

    -- The identifier of the detected area.

    +
    DETECTION_BASE.DetectedItem.MaxThreatLevelA2G + +
    DETECTION_BASE.DetectedItem.NearestFAC +

    The nearest FAC near the Area.

    +
    DETECTION_BASE.DetectedItem.Set +

    -- The Set of Units in the detected area.

    +
    DETECTION_BASE.DetectedItem.Zone +

    -- The Zone of the detected area.

    @@ -622,6 +865,158 @@ DETECTION_BASE.DetectedObject.Visible + + + + +

    Type DETECTION_TYPES

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DETECTION_TYPES.ClassName + +
    DETECTION_TYPES:CreateDetectionSets() +

    Create the DetectedItems list from the DetectedObjects table.

    +
    DETECTION_TYPES:DetectedItemReportSummary(Index, DetectedTypeName) +

    Report summary of a DetectedItem using a given numeric index.

    +
    DETECTION_TYPES:DetectedReportDetailed() +

    Report detailed of a detection result.

    +
    DETECTION_TYPES.DetectionRange + +
    DETECTION_TYPES:GetChangeText(DetectedItem) +

    Make text documenting the changes of the detected zone.

    +
    DETECTION_TYPES:New(DetectionSetGroup) +

    DETECTION_TYPES constructor.

    +
    DETECTION_TYPES._BoundDetectedZones + +
    DETECTION_TYPES._FlareDetectedUnits + +
    DETECTION_TYPES._FlareDetectedZones + +
    DETECTION_TYPES._SmokeDetectedUnits + +
    DETECTION_TYPES._SmokeDetectedZones + +
    + +

    Type DETECTION_UNITS

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DETECTION_UNITS.ClassName + +
    DETECTION_UNITS:CreateDetectionSets() +

    Create the DetectedItems list from the DetectedObjects table.

    +
    DETECTION_UNITS:DetectedItemReportSummary(Index) +

    Report summary of a DetectedItem using a given numeric index.

    +
    DETECTION_UNITS:DetectedReportDetailed() +

    Report detailed of a detection result.

    +
    DETECTION_UNITS.DetectionRange +

    The range till which targets are detected.

    +
    DETECTION_UNITS:GetChangeText(DetectedItem) +

    Make text documenting the changes of the detected zone.

    +
    DETECTION_UNITS:New(DetectionSetGroup) +

    DETECTION_UNITS constructor.

    +
    DETECTION_UNITS._BoundDetectedZones + +
    DETECTION_UNITS._FlareDetectedUnits + +
    DETECTION_UNITS._FlareDetectedZones + +
    DETECTION_UNITS._SmokeDetectedUnits + +
    DETECTION_UNITS._SmokeDetectedZones +
    @@ -653,34 +1048,89 @@ + +
    +
    +
    + + #DETECTION_TYPES + +DETECTION_TYPES + +
    +
    + + + +
    +
    +
    +
    + + #DETECTION_UNITS + +DETECTION_UNITS + +
    +
    + + +

    Type Detection

    Type DETECTION_AREAS

    -

    DETECTION_AREAS class

    +

    # 4) DETECTION_AREAS class, extends Detection#DETECTION_BASE

    + +

    The DETECTION_AREAS class will detect units within the battle zone for a list of Groups detecting targets following (a) detection method(s), + and will build a list (table) of Set#SET_UNITs containing the Unit#UNITs detected.

    + + +

    The class is group the detected units within zones given a DetectedZoneRange parameter. + A set with multiple detected zones will be created as there are groups of units detected.

    + +

    ## 4.1) Retrieve the Detected Unit Sets and Detected Zones

    + +

    The methods to manage the DetectedItems[].Set(s) are implemented in Detection#DECTECTION_BASE and + the methods to manage the DetectedItems[].Zone(s) is implemented in Detection#DETECTION_AREAS.

    + +

    Retrieve the DetectedItems[].Set with the method Detection#DETECTION_BASE.GetDetectedSet(). A Set#SET_UNIT object will be returned.

    + +

    Retrieve the formed Zones as a result of the grouping the detected units within the DetectionZoneRange, use the method Detection#DETECTION_BASE.GetDetectionZones(). + To understand the amount of zones created, use the method Detection#DETECTION_BASE.GetDetectionZoneCount(). + If you want to obtain a specific zone from the DetectedZones, use the method Detection#DETECTION_BASE.GetDetectionZone() with a given index.

    + +

    ## 4.4) Flare or Smoke detected units

    + +

    Use the methods Detection#DETECTION_AREAS.FlareDetectedUnits() or Detection#DETECTION_AREAS.SmokeDetectedUnits() to flare or smoke the detected units when a new detection has taken place.

    + +

    ## 4.5) Flare or Smoke or Bound detected zones

    + +

    Use the methods:

    + + + +

    the detected zones when a new detection has taken place. +

    Field(s)

    - -DETECTION_AREAS:AcceptChanges(DetectedArea) + +DETECTION_AREAS:BoundDetectedZones()
    -

    Accepts changes from the detected zone.

    +

    Bound the detected zones

    -

    Parameter

    -

    Return value

    #DETECTION_AREAS: @@ -691,129 +1141,19 @@ self

    - -DETECTION_AREAS:AddChangeArea(DetectedArea, ChangeCode, AreaUnitType) - -
    -
    - -

    Add a change to the detected zone.

    - -

    Parameters

    - -

    Return value

    - -

    #DETECTION_AREAS: -self

    - -
    -
    -
    -
    - - -DETECTION_AREAS:AddChangeUnit(DetectedArea, ChangeCode, ChangeUnitType) - -
    -
    - -

    Add a change to the detected zone.

    - -

    Parameters

    - -

    Return value

    - -

    #DETECTION_AREAS: -self

    - -
    -
    -
    -
    - - -DETECTION_AREAS.AddDetectedArea(Set, Zone, self) - -
    -
    - -

    Add a detected DETECTION_AREAS.DetectedArea.

    - -

    Parameters

    - -

    Return value

    - -

    #DETECTION_AREAS.DetectedArea: -DetectedArea

    - -
    -
    -
    -
    - -DETECTION_AREAS:CalculateThreatLevelA2G(DetectedArea) +DETECTION_AREAS:CalculateThreatLevelA2G(DetectedItem)
    -

    Calculate the maxium A2G threat level of the DetectedArea.

    +

    Calculate the maxium A2G threat level of the DetectedItem.

    Parameter

    @@ -857,9 +1197,35 @@ self

    - #DETECTION_AREAS.DetectedAreas - -DETECTION_AREAS.DetectedAreas + +DETECTION_AREAS:DetectedItemReportSummary(Index) + +
    +
    + +

    Report summary of a detected item using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + #DETECTION_BASE.DetectedItems + +DETECTION_AREAS.DetectedItems
    @@ -922,7 +1288,7 @@ self

    -DETECTION_AREAS:GetChangeText(DetectedArea) +DETECTION_AREAS:GetChangeText(DetectedItem)
    @@ -933,7 +1299,7 @@ self

    @@ -947,107 +1313,19 @@ The Changes text

    - -DETECTION_AREAS:GetDetectedAreaCount() - -
    -
    - -

    Get the amount of DETECTION_AREAS.DetectedAreas.

    - -

    Return value

    - -

    #number: -DetectedAreaCount

    - -
    -
    -
    -
    - - -DETECTION_AREAS:GetDetectedAreas() - -
    -
    - -

    Get the detected DETECTION_AREAS.DetectedAreas.

    - -

    Return value

    - -

    #DETECTION_AREAS.DetectedAreas: -DetectedAreas

    - -
    -
    -
    -
    - - -DETECTION_AREAS:GetDetectedSet(Index) - -
    -
    - -

    Get the Set#SET_UNIT of a detecttion area using a given numeric index.

    - -

    Parameter

    -
      -
    • - -

      #number Index :

      - -
    • -
    -

    Return value

    - -

    Core.Set#SET_UNIT: -DetectedSet

    - -
    -
    -
    -
    - - -DETECTION_AREAS:GetDetectedZone(Index) - -
    -
    - -

    Get the Zone#ZONE_UNIT of a detection area using a given numeric index.

    - -

    Parameter

    -
      -
    • - -

      #number Index :

      - -
    • -
    -

    Return value

    - -

    Core.Zone#ZONE_UNIT: -DetectedZone

    - -
    -
    -
    -
    - -DETECTION_AREAS:GetTreatLevelA2G(DetectedArea) +DETECTION_AREAS:GetTreatLevelA2G(DetectedItem)
    -

    Returns the A2G threat level of the units in the DetectedArea

    +

    Returns the A2G threat level of the units in the DetectedItem

    Parameter

    @@ -1062,7 +1340,7 @@ a scale from 0 to 10.

    -DETECTION_AREAS:IsFriendliesNearBy(DetectedArea) +DETECTION_AREAS:IsFriendliesNearBy(DetectedItem)
    @@ -1073,7 +1351,7 @@ a scale from 0 to 10.

    • -

      DetectedArea :

      +

      DetectedItem :

    @@ -1088,18 +1366,18 @@ trhe if there are friendlies nearby

    -DETECTION_AREAS:NearestFAC(DetectedArea) +DETECTION_AREAS:NearestFAC(DetectedItem)
    -

    Find the nearest FAC of the DetectedArea.

    +

    Find the nearest FAC of the DetectedItem.

    Parameter

    @@ -1114,7 +1392,7 @@ The nearest FAC unit

    -DETECTION_AREAS:New(DetectionSetGroup, DetectionRange, DetectionZoneRange) +DETECTION_AREAS:New(DetectionSetGroup, DetectionZoneRange)
    @@ -1131,12 +1409,6 @@ The Set of GROUPs in the Forward Air Controller role.

  • -

    Dcs.DCSTypes#Distance DetectionRange : -The range till which targets are accepted to be detected.

    - -
  • -
  • -

    Dcs.DCSTypes#Distance DetectionZoneRange : The range till which targets are grouped upon the first detected target.

    @@ -1144,34 +1416,7 @@ The range till which targets are grouped upon the first detected target.

    Return value

    -

    Functional.Detection#DETECTION_AREAS: -self

    - -
  • -
    -
    -
    - - -DETECTION_AREAS:RemoveDetectedArea(Index) - -
    -
    - -

    Remove a detected DETECTION_AREAS.DetectedArea with a given Index.

    - -

    Parameter

    -
      -
    • - -

      #number Index : -The Index of the detection are to be removed.

      - -
    • -
    -

    Return value

    - -

    #nil:

    +

    #DETECTION_AREAS:

    @@ -1179,32 +1424,6 @@ The Index of the detection are to be removed.

    - -DETECTION_AREAS:ReportFriendliesNearBy(ReportUnit, ReportGroupData) - -
    -
    - -

    Background worker function to determine if there are friendlies nearby ...

    - -

    Parameters

    - -
    -
    -
    -
    - DETECTION_AREAS:SmokeDetectedUnits() @@ -1236,6 +1455,20 @@ self

    #DETECTION_AREAS: self

    +
    +
    +
    +
    + + #boolean + +DETECTION_AREAS._BoundDetectedZones + +
    +
    + + +
    @@ -1295,61 +1528,233 @@ self

    -

    Type DETECTION_AREAS.DetectedArea

    -

    Field(s)

    +

    Type DETECTION_BASE

    + +

    1) DETECTION_BASE class, extends Fsm#FSM

    + +

    The DETECTION_BASE class defines the core functions to administer detected objects.

    + + +

    The DETECTION_BASE class will detect objects within the battle zone for a list of Groups detecting targets following (a) detection method(s).

    + +

    1.1) DETECTION_BASE constructor

    + +

    Construct a new DETECTION_BASE instance using the DETECTION_BASE.New() method.

    + +

    1.2) DETECTION_BASE initialization

    + +

    By default, detection will return detected objects with all the detection sensors available. +However, you can ask how the objects were found with specific detection methods. +If you use one of the below methods, the detection will work with the detection method specified. +You can specify to apply multiple detection methods.

    + +

    Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK:

    + + + +

    1.3) DETECTION_BASE derived classes group the detected units into a DetectedItems[] list

    + +

    DETECTIONBASE derived classes build a list called DetectedItems[], which is essentially a first later +of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains +a SETUNIT object that contains the detected units that belong to that group.

    + +

    Derived classes will apply different methods to group the detected units. +Examples are per area, per quadrant, per distance, per type. +See further the derived DETECTION classes on which grouping methods are currently supported.

    + +

    Various methods exist how to retrieve the grouped items from a DETECTION_BASE derived class:

    + +
      +
    • The method Detection#DETECTION_BASE.GetDetectedItems() retrieves the DetectedItems[] list.
    • +
    • A DetectedItem from the DetectedItems[] list can be retrieved using the method Detection#DETECTION_BASE.GetDetectedItem( DetectedItemIndex ). + Note that this method returns a DetectedItem element from the list, that contains a Set variable and further information + about the DetectedItem that is set by the DETECTION_BASE derived classes, used to group the DetectedItem.
    • +
    • A DetectedSet from the DetectedItems[] list can be retrieved using the method Detection#DETECTION_BASE.GetDetectedSet( DetectedItemIndex ). + This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ).
    • +
    + +

    1.4) Apply additional Filters to fine-tune the detected objects

    + +

    By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means. +That being said, the DCS World detection algorithm can sometimes be unrealistic. +Especially for a visual detection, DCS World is able to report within 1 second a detailed detection of a group of 20 units (including types of the units) that are 10 kilometers away, using only visual capabilities. +Additionally, trees and other obstacles are not accounted during the DCS World detection.

    + +

    Therefore, an additional (optional) filtering has been built into the DETECTION_BASE class, that can be set for visual detected units. +For electronic detection, this filtering is not applied, only for visually detected targets.

    + +

    The following additional filtering can be applied for visual filtering:

    + +
      +
    • A probability factor per kilometer distance.
    • +
    • A probability factor based on the alpha angle between the detected object and the unit detecting. + A detection from a higher altitude allows for better detection than when on the ground.
    • +
    • Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult. + The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects additing a probability factor per zone.
    • +
    + +

    I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. +Only when you experience unrealistic behaviour in your missions, these filters could be applied.

    + +

    1.4.1 ) Distance visual detection probability

    + +

    Upon a visual detection, the further away a detected object is, the less likely it is to be detected properly. +Also, the speed of accurate detection plays a role.

    + +

    A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance.

    + +

    For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: +1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%.

    + +

    Note that based on this probability factor, not only the detection but also the type of the unit will be applied!

    + +

    Use the method Detection#DETECTION_BASE.SetDistanceProbability() to set the probability factor upon a 10 km distance.

    + +

    1.4.2 ) Alpha Angle visual detection probability

    + +

    Upon a visual detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. +A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct.

    + +

    A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle.

    + +

    For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: +0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100%

    + +

    Use the method Detection#DETECTION_BASE.SetAlphaAngleProbability() to set the probability factor if 0°.

    + +

    1.4.3 ) Cloudy Zones detection probability

    + +

    Upon a visual detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. +The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission +zones that reflect cloudy areas where detected units may not be so easily visually detected.

    + +

    Use the method Detection#DETECTION_BASE.SetZoneProbability() to set for a defined number of zones, the probability factors.

    + +

    Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take +from the DETECTIONBASE to calculate the presence of the detected unit within each zone. +Expecially for ZONEPOLYGON, try to limit the amount of nodes of the polygon!

    + +

    Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for +AI not to detect so easily targets within a forrest or village rich area.

    + +

    1.5 ) Accept / Reject detected units

    + +

    DETECTION_BASE can accept or reject successful detections based on the location of the detected object, +if it is located in range or located inside or outside of specific zones.

    + +

    1.5.1 ) Detection acceptance of within range limit

    + +

    A range can be set that will limit a successful detection for a unit. +Use the method Detection#DETECTION_BASE.SetAcceptRange() to apply a range in meters till where detected units will be accepted.

    + +
     local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers.
    +
    + -- Build a detect object.
    + local Detection = DETECTION_BASE:New( SetGroup )
    +
    + -- This will accept detected units if the range is below 5000 meters.
    + Detection:SetAcceptRange( 5000 ) 
    +
    + -- Start the Detection.
    + Detection:Start()
    +
    + + +

    1.5.2 ) Detection acceptance if within zone(s).

    + +

    Specific ZONEBASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONEBASE object(s). +Use the method Detection#DETECTION_BASE.SetAcceptZones() will accept detected units if they are within the specified zones.

    + +
     local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers.
    +
    + -- Search fo the zones where units are to be accepted.
    + local ZoneAccept1 = ZONE:New( "AcceptZone1" )
    + local ZoneAccept2 = ZONE:New( "AcceptZone2" )
    +
    + -- Build a detect object.
    + local Detection = DETECTION_BASE:New( SetGroup )
    +
    + -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2.
    + Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) 
    +
    + -- Start the Detection.
    + Detection:Start()
    +
    + +

    1.5.3 ) Detection rejectance if within zone(s).

    + +

    Specific ZONEBASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONEBASE object(s). +Use the method Detection#DETECTION_BASE.SetRejectZones() will reject detected units if they are within the specified zones. +An example of how to use the method is shown below.

    + +
     local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers.
    +
    + -- Search fo the zones where units are to be rejected.
    + local ZoneReject1 = ZONE:New( "RejectZone1" )
    + local ZoneReject2 = ZONE:New( "RejectZone2" )
    +
    + -- Build a detect object.
    + local Detection = DETECTION_BASE:New( SetGroup )
    +
    + -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2.
    + Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) 
    +
    + -- Start the Detection.
    + Detection:Start()
    +
    + +

    1.6) DETECTION_BASE is a Finite State Machine

    + +

    Various Events and State Transitions can be tailored using DETECTION_BASE.

    + +

    1.6.1) DETECTION_BASE States

    + +
      +
    • Detecting: The detection is running.
    • +
    • Stopped: The detection is stopped.
    • +
    + +

    1.6.2) DETECTION_BASE Events

    + +
      +
    • Start: Start the detection process.
    • +
    • Detect: Detect new units.
    • +
    • Detected: New units have been detected.
    • +
    • Stop: Stop the detection process.
    • +
    + + +

    Field(s)

    - #number - -DETECTION_AREAS.DetectedArea.AreaID + +DETECTION_BASE:AcceptChanges(DetectedItem)
    -

    -- The identifier of the detected area.

    +

    Accepts changes from the detected item.

    -
    -
    -
    -
    +

    Parameter

    +
    -
    - -

    Documents if the detected area has changes.

    - -
    -
    -
    -
    - - #table - -DETECTION_AREAS.DetectedArea.Changes - -
    -
    - -

    A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes).

    - -
    -
    -
    -
    - - #boolean - -DETECTION_AREAS.DetectedArea.FriendliesNearBy - -
    -
    - -

    Indicates if there are friendlies within the detected area.

    + + +

    Return value

    + +

    #DETECTION_BASE: +self

    @@ -1357,8 +1762,8 @@ self

    - -DETECTION_AREAS.DetectedArea.MaxThreatLevelA2G + +DETECTION_BASE.AcceptRange
    @@ -1370,53 +1775,181 @@ self

    - Wrapper.Unit#UNIT - -DETECTION_AREAS.DetectedArea.NearestFAC + + +DETECTION_BASE.AcceptZones
    -

    The nearest FAC near the Area.

    +
    - Core.Set#SET_UNIT - -DETECTION_AREAS.DetectedArea.Set + +DETECTION_BASE:AddChangeItem(DetectedItem, ChangeCode, ItemUnitType)
    -

    -- The Set of Units in the detected area.

    +

    Add a change to the detected zone.

    -
    -
    -
    -
    - - Core.Zone#ZONE_UNIT - -DETECTION_AREAS.DetectedArea.Zone - -
    -
    - -

    -- The Zone of the detected area.

    - -
    -
    - -

    Type DETECTION_AREAS.DetectedAreas

    - -

    Type DETECTION_BASE

    +

    Parameters

    +
      +
    • -

      DETECTION_BASE class

      +

      #DETECTION_BASE.DetectedItem DetectedItem :

      -

      Field(s)

      +
    • +
    • + +

      #string ChangeCode :

      + +
    • +
    • + +

      ItemUnitType :

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:AddChangeUnit(DetectedItem, ChangeCode, ChangeUnitType) + +
    +
    + +

    Add a change to the detected zone.

    + +

    Parameters

    + +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:AddDetectedItem(DetectedItemIndex, Set) + +
    +
    + +

    Adds a new DetectedItem to the DetectedItems list.

    + + +

    The DetectedItem is a table and contains a SET_UNIT in the field Set.

    + +

    Parameters

    +
      +
    • + +

      #string DetectedItemIndex : +The index of the DetectedItem.

      + +
    • +
    • + +

      Core.Set#SET_UNIT Set : +(optional) The Set of Units to be added.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE.DetectedItem:

    + + +
    +
    +
    +
    + + +DETECTION_BASE:AddDetectedItemZone(DetectedItemIndex, Set, Zone) + +
    +
    + +

    Adds a new DetectedItem to the DetectedItems list.

    + + +

    The DetectedItem is a table and contains a SET_UNIT in the field Set.

    + +

    Parameters

    +
      +
    • + +

      #string DetectedItemIndex : +The index of the DetectedItem.

      + +
    • +
    • + +

      Core.Set#SET_UNIT Set : +(optional) The Set of Units to be added.

      + +
    • +
    • + +

      Core.Zone#ZONE_UNIT Zone : +(optional) The Zone to be added where the Units are located.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE.DetectedItem:

    + + +
    +
    +
    +
    + + + +DETECTION_BASE.AlphaAngleProbability + +
    +
    + + + +
    +
    @@ -1429,6 +1962,20 @@ self

    + +
    +
    +
    + + + +DETECTION_BASE.CountryID + +
    +
    + + +
    @@ -1450,6 +1997,19 @@ self

    #DETECTION_BASE: self

    + +
    +
    +
    + + +DETECTION_BASE:Detect() + +
    +
    + +

    Synchronous Event Trigger for Event Detect.

    +
    @@ -1534,6 +2094,87 @@ self

    + +
    +
    +
    + + +DETECTION_BASE:Detected() + +
    +
    + +

    Synchronous Event Trigger for Event Detected.

    + +
    +
    +
    +
    + + #number + +DETECTION_BASE.DetectedItemCount + +
    +
    + + + +
    +
    +
    +
    + + #number + +DETECTION_BASE.DetectedItemMax + +
    +
    + + + +
    +
    +
    +
    + + +DETECTION_BASE:DetectedItemReportSummary(Index) + +
    +
    + +

    Report summary of a detected item using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + + +DETECTION_BASE.DetectedItems + +
    +
    + + +
    @@ -1562,6 +2203,52 @@ self

    Map of the DetectedObjects identified.

    + +
    +
    +
    + + +DETECTION_BASE:DetectedReportDetailed() + +
    +
    + +

    Report detailed of a detectedion result.

    + +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + #number + +DETECTION_BASE.DetectionCount + +
    +
    + + + +
    +
    +
    +
    + + #number + +DETECTION_BASE.DetectionInterval + +
    +
    + + +
    @@ -1618,6 +2305,82 @@ self

    The Set of GROUPs in the Forward Air Controller role.

    + +
    +
    +
    + + + +DETECTION_BASE.DistanceProbability + +
    +
    + + + +
    +
    +
    +
    + + +DETECTION_BASE:GetDetectedItem(Index) + +
    +
    + +

    Get a detected item using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      #number Index :

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE.DetectedItem:

    + + +
    +
    +
    +
    + + +DETECTION_BASE:GetDetectedItems() + +
    +
    + +

    Get the detected Set#SET_BASEs.

    + +

    Return value

    + +

    #DETECTION_BASE.DetectedItems:

    + + +
    +
    +
    +
    + + +DETECTION_BASE:GetDetectedItemsCount() + +
    +
    + +

    Get the amount of SETs with detected objects.

    + +

    Return value

    + +

    #number: +Count

    +
    @@ -1655,7 +2418,7 @@ self

    -

    Get a SET of detected objects using a given numeric index.

    +

    Get the Set#SET_UNIT of a detecttion area using a given numeric index.

    Parameter

      @@ -1667,44 +2430,34 @@ self

    Return value

    -

    Core.Set#SET_BASE:

    - +

    Core.Set#SET_UNIT: +DetectedSet

    - -DETECTION_BASE:GetDetectedSetCount() + +DETECTION_BASE:GetDetectedZone(Index)
    -

    Get the amount of SETs with detected objects.

    +

    Get the Zone#ZONE_UNIT of a detection area using a given numeric index.

    +

    Parameter

    +
      +
    • + +

      #number Index :

      + +
    • +

    Return value

    -

    #number: -Count

    - -
    -
    -
    -
    - - -DETECTION_BASE:GetDetectedSets() - -
    -
    - -

    Get the detected Set#SET_BASEs.

    - -

    Return value

    - -

    #DETECTION_BASE.DetectedSets: -DetectedSets

    +

    Core.Zone#ZONE_UNIT: +DetectedZone

    @@ -1932,27 +2685,47 @@ true if already identified.

    + +DETECTION_BASE:IsFriendliesNearBy(DetectedItem) + +
    +
    + +

    Returns if there are friendlies nearby the FAC units ...

    + +

    Parameter

    +
      +
    • + +

      DetectedItem :

      + +
    • +
    +

    Return value

    + +

    #boolean: +trhe if there are friendlies nearby

    + +
    +
    +
    +
    + -DETECTION_BASE:New(DetectionSetGroup, DetectionRange) +DETECTION_BASE:New(DetectionSetGroup)

    DETECTION constructor.

    -

    Parameters

    +

    Parameter

    Return value

    @@ -1965,6 +2738,504 @@ self

    + +DETECTION_BASE:OnAfterDetect(From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Detect.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:OnAfterDetected(From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Detected.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:OnAfterStart(From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Start.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:OnAfterStop(From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Stop.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:OnBeforeDetect(From, Event, To) + +
    +
    + +

    OnBefore Transition Handler for Event Detect.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +DETECTION_BASE:OnBeforeDetected(From, Event, To) + +
    +
    + +

    OnBefore Transition Handler for Event Detected.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +DETECTION_BASE:OnBeforeStart(From, Event, To) + +
    +
    + +

    OnBefore Transition Handler for Event Start.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +DETECTION_BASE:OnBeforeStop(From, Event, To) + +
    +
    + +

    OnBefore Transition Handler for Event Stop.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +DETECTION_BASE:OnEnterDetecting(From, Event, To) + +
    +
    + +

    OnEnter Transition Handler for State Detecting.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:OnEnterStopped(From, Event, To) + +
    +
    + +

    OnEnter Transition Handler for State Stopped.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:OnLeaveDetecting(From, Event, To) + +
    +
    + +

    OnLeave Transition Handler for State Detecting.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +DETECTION_BASE:OnLeaveStopped(From, Event, To) + +
    +
    + +

    OnLeave Transition Handler for State Stopped.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + + +DETECTION_BASE.RejectZones + +
    +
    + + + +
    +
    +
    +
    + + +DETECTION_BASE:RemoveDetectedItem(DetectedItemIndex) + +
    +
    + +

    Removes an existing DetectedItem from the DetectedItems list.

    + + +

    The DetectedItem is a table and contains a SET_UNIT in the field Set.

    + +

    Parameter

    +
      +
    • + +

      #number DetectedItemIndex : +The index or position in the DetectedItems list where the item needs to be removed.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:ReportFriendliesNearBy(ReportGroupData) + +
    +
    + +

    Background worker function to determine if there are friendlies nearby ...

    + +

    Parameter

    +
      +
    • + +

      ReportGroupData :

      + +
    • +
    +
    +
    +
    +
    + DETECTION_BASE:Schedule(DelayTime, RepeatInterval) @@ -2021,6 +3292,239 @@ self

    +
    +
    +
    +
    + + +DETECTION_BASE:SetAcceptRange(AcceptRange) + +
    +
    + +

    Accept detections if within a range in meters.

    + +

    Parameter

    +
      +
    • + +

      #number AcceptRange : +Accept a detection if the unit is within the AcceptRange in meters.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:SetAcceptZones(AcceptZones) + +
    +
    + +

    Accept detections if within the specified zone(s).

    + +

    Parameter

    +
      +
    • + +

      AcceptZones : +Can be a list or ZONEBASE objects, or a single ZONEBASE object.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:SetAlphaAngleProbability(AlphaAngleProbability) + +
    +
    + +

    Upon a visual detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly.

    + + +

    A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct.

    + +

    A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle.

    + +

    For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: +0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100%

    + +

    Parameter

    +
      +
    • + +

      AlphaAngleProbability : +The probability factor.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:SetDetectionInterval(DetectionInterval) + +
    +
    + +

    Set the detection interval time in seconds.

    + +

    Parameter

    +
      +
    • + +

      #number DetectionInterval : +Interval in seconds.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:SetDistanceProbability(DistanceProbability) + +
    +
    + +

    Upon a visual detection, the further away a detected object is, the less likely it is to be detected properly.

    + + +

    Also, the speed of accurate detection plays a role. +A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. +For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: +1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%.

    + +

    Parameter

    +
      +
    • + +

      DistanceProbability : +The probability factor.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:SetRejectZones(RejectZones) + +
    +
    + +

    Reject detections if within the specified zone(s).

    + +

    Parameter

    +
      +
    • + +

      RejectZones : +Can be a list or ZONEBASE objects, or a single ZONEBASE object.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:SetZoneProbability(ZoneArray) + +
    +
    + +

    Upon a visual detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully.

    + + +

    The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission +zones that reflect cloudy areas where detected units may not be so easily visually detected.

    + +

    Parameter

    +
      +
    • + +

      ZoneArray : +Aray of a The ZONE_BASE object and a ZoneProbability pair..

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:Start() + +
    +
    + +

    Synchronous Event Trigger for Event Start.

    + +
    +
    +
    +
    + + +DETECTION_BASE:Stop() + +
    +
    + +

    Synchronous Event Trigger for Event Stop.

    +
    @@ -2060,25 +3564,331 @@ self

    - -DETECTION_BASE:_DetectionScheduler(SchedulerName) + + +DETECTION_BASE.ZoneProbability
    -

    Form Sets of detected Unit#UNITs in an array of Set#SET_BASEs.

    + + +
    +
    +
    +
    + + +DETECTION_BASE:__Detect(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Detect.

    Parameter

    • -

      SchedulerName :

      +

      #number Delay : +The delay in seconds.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:__Detected(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Detected.

    + +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:__Start(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Start.

    + +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:__Stop(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Stop.

    + +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:onafterDetect(From, Event, To) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:onafterDetectionGroup(From, Event, To, DetectionGroup) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    • + +

      Wrapper.Group#GROUP DetectionGroup : +The Group detecting.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:onafterStart(From, Event, To) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

    +

    Type DETECTION_BASE.DetectedItem

    +

    Field(s)

    +
    +
    + + #boolean + +DETECTION_BASE.DetectedItem.Changed + +
    +
    + +

    Documents if the detected area has changes.

    + +
    +
    +
    +
    + + #table + +DETECTION_BASE.DetectedItem.Changes + +
    +
    + +

    A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes).

    + +
    +
    +
    +
    + + #boolean + +DETECTION_BASE.DetectedItem.FriendliesNearBy + +
    +
    + +

    Indicates if there are friendlies within the detected area.

    + +
    +
    +
    +
    + + #number + +DETECTION_BASE.DetectedItem.ItemID + +
    +
    + +

    -- The identifier of the detected area.

    + +
    +
    +
    +
    + + + +DETECTION_BASE.DetectedItem.MaxThreatLevelA2G + +
    +
    + + + +
    +
    +
    +
    + + Wrapper.Unit#UNIT + +DETECTION_BASE.DetectedItem.NearestFAC + +
    +
    + +

    The nearest FAC near the Area.

    + +
    +
    +
    +
    + + Core.Set#SET_UNIT + +DETECTION_BASE.DetectedItem.Set + +
    +
    + +

    -- The Set of Units in the detected area.

    + +
    +
    +
    +
    + + Core.Zone#ZONE_UNIT + +DETECTION_BASE.DetectedItem.Zone + +
    +
    + +

    -- The Zone of the detected area.

    + +
    +
    + +

    Type DETECTION_BASE.DetectedItems

    +

    Type DETECTION_BASE.DetectedObject

    Field(s)

    @@ -2154,7 +3964,472 @@ self

    Type DETECTION_BASE.DetectedObjects

    -

    Type DETECTION_BASE.DetectedSets

    +

    Type DETECTION_TYPES

    + +

    3) DETECTION_TYPES class, extends Detection#DETECTION_BASE

    + +

    The DETECTION_TYPES class will detect units within the battle zone.

    + + +

    It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. +Each DetectedItem will contain a field Set, which contains a Set#SET_UNIT containing ONE UNIT object reference. +Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also.

    + + +

    Field(s)

    +
    +
    + + #string + +DETECTION_TYPES.ClassName + +
    +
    + + + +
    +
    +
    +
    + + +DETECTION_TYPES:CreateDetectionSets() + +
    +
    + +

    Create the DetectedItems list from the DetectedObjects table.

    + + +

    For each DetectedItem, a one field array is created containing the Unit detected.

    + +

    Return value

    + +

    #DETECTION_TYPES: +self

    + +
    +
    +
    +
    + + +DETECTION_TYPES:DetectedItemReportSummary(Index, DetectedTypeName) + +
    +
    + +

    Report summary of a DetectedItem using a given numeric index.

    + +

    Parameters

    +
      +
    • + +

      Index :

      + +
    • +
    • + +

      DetectedTypeName :

      + +
    • +
    +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + +DETECTION_TYPES:DetectedReportDetailed() + +
    +
    + +

    Report detailed of a detection result.

    + +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + +DETECTION_TYPES.DetectionRange + +
    +
    + + + +
    +
    +
    +
    + + +DETECTION_TYPES:GetChangeText(DetectedItem) + +
    +
    + +

    Make text documenting the changes of the detected zone.

    + +

    Parameter

    + +

    Return value

    + +

    #string: +The Changes text

    + +
    +
    +
    +
    + + +DETECTION_TYPES:New(DetectionSetGroup) + +
    +
    + +

    DETECTION_TYPES constructor.

    + +

    Parameter

    + +

    Return value

    + +

    Functional.Detection#DETECTION_TYPES: +self

    + +
    +
    +
    +
    + + #boolean + +DETECTION_TYPES._BoundDetectedZones + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_TYPES._FlareDetectedUnits + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_TYPES._FlareDetectedZones + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_TYPES._SmokeDetectedUnits + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_TYPES._SmokeDetectedZones + +
    +
    + + + +
    +
    + +

    Type DETECTION_TYPES.DetectedItem

    + +

    Type DETECTION_UNITS

    + +

    2) DETECTION_UNITS class, extends Detection#DETECTION_BASE

    + +

    The DETECTION_UNITS class will detect units within the battle zone.

    + + +

    It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a Set#SET_UNIT containing ONE UNIT object reference. +Beware that when the amount of units detected is large, the DetectedItems list will be large also.

    + + +

    Field(s)

    +
    +
    + + #string + +DETECTION_UNITS.ClassName + +
    +
    + + + +
    +
    +
    +
    + + +DETECTION_UNITS:CreateDetectionSets() + +
    +
    + +

    Create the DetectedItems list from the DetectedObjects table.

    + + +

    For each DetectedItem, a one field array is created containing the Unit detected.

    + +

    Return value

    + +

    #DETECTION_UNITS: +self

    + +
    +
    +
    +
    + + +DETECTION_UNITS:DetectedItemReportSummary(Index) + +
    +
    + +

    Report summary of a DetectedItem using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + +DETECTION_UNITS:DetectedReportDetailed() + +
    +
    + +

    Report detailed of a detection result.

    + +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + Dcs.DCSTypes#Distance + +DETECTION_UNITS.DetectionRange + +
    +
    + +

    The range till which targets are detected.

    + +
    +
    +
    +
    + + +DETECTION_UNITS:GetChangeText(DetectedItem) + +
    +
    + +

    Make text documenting the changes of the detected zone.

    + +

    Parameter

    + +

    Return value

    + +

    #string: +The Changes text

    + +
    +
    +
    +
    + + +DETECTION_UNITS:New(DetectionSetGroup) + +
    +
    + +

    DETECTION_UNITS constructor.

    + +

    Parameter

    +
      +
    • + +

      Core.Set#SET_GROUP DetectionSetGroup : +The Set of GROUPs in the Forward Air Controller role.

      + +
    • +
    +

    Return value

    + +

    Functional.Detection#DETECTION_UNITS: +self

    + +
    +
    +
    +
    + + #boolean + +DETECTION_UNITS._BoundDetectedZones + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_UNITS._FlareDetectedUnits + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_UNITS._FlareDetectedZones + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_UNITS._SmokeDetectedUnits + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_UNITS._SmokeDetectedZones + +
    +
    + + + +
    +
    + +

    Type DETECTION_UNITS.DetectedItem

    diff --git a/docs/Documentation/DetectionManager.html b/docs/Documentation/DetectionManager.html index 0c48152fb..e31707159 100644 --- a/docs/Documentation/DetectionManager.html +++ b/docs/Documentation/DetectionManager.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -78,7 +79,7 @@
    -

    1) DetectionManager#DETECTION_MANAGER class, extends Base#BASE

    +

    1) DetectionManager#DETECTION_MANAGER class, extends Fsm#FSM

    The DetectionManager#DETECTION_MANAGER class defines the core functions to report detected objects to groups. Reportings can be done in several manners, and it is up to the derived classes if DETECTION_MANAGER to model the reporting behaviour.

    @@ -105,23 +106,6 @@ If an ad-hoc report is requested, use the method DetectionManager#DETECTION_REPORTING.New() method creates a new DETECTION_REPORTING instance.

    -
    - -

    3) #DETECTION_DISPATCHER class, extends #DETECTION_MANAGER

    -

    The #DETECTION_DISPATCHER class implements the dynamic dispatching of tasks upon groups of detected units determined a Set of FAC (groups). -The FAC will detect units, will group them, and will dispatch Tasks to groups. Depending on the type of target detected, different tasks will be dispatched. -Find a summary below describing for which situation a task type is created:

    - -
      -
    • CAS Task: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter.
    • -
    • BAI Task: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter.
    • -
    • SEAD Task: Is created when there are enemy ground units wihtin range of the FAC, with air search radars.
    • -
    - -

    Other task types will follow...

    - -

    3.1) DETECTION_DISPATCHER constructor:

    -

    The DETECTION_DISPATCHER.New() method creates a new DETECTION_DISPATCHER instance.


    @@ -131,12 +115,6 @@ Find a summary below describing for which situation a task type is created:

    Global(s)

    - - - -
    DETECTION_DISPATCHER - -
    DETECTION_MANAGER @@ -150,76 +128,6 @@ Find a summary below describing for which situation a task type is created:

    -

    Type DETECTION_DISPATCHER

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    DETECTION_DISPATCHER.ClassName - -
    DETECTION_DISPATCHER.CommandCenter - -
    DETECTION_DISPATCHER.Detection -

    The DETECTION_BASE object that is used to report the detected objects.

    -
    DETECTION_DISPATCHER:EvaluateBAI(DetectedArea, FriendlyCoalition) -

    Creates a BAI task when there are targets for it.

    -
    DETECTION_DISPATCHER:EvaluateCAS(DetectedArea) -

    Creates a CAS task when there are targets for it.

    -
    DETECTION_DISPATCHER:EvaluateRemoveTask(Mission, Task, DetectedArea) -

    Evaluates the removal of the Task from the Mission.

    -
    DETECTION_DISPATCHER:EvaluateSEAD(DetectedArea) -

    Creates a SEAD task when there are targets for it.

    -
    DETECTION_DISPATCHER.Mission - -
    DETECTION_DISPATCHER:New(SetGroup, Detection, Mission, CommandCenter) -

    DETECTION_DISPATCHER constructor.

    -
    DETECTION_DISPATCHER:ProcessDetected(Detection) -

    Assigns tasks in relation to the detected items to the Set#SET_GROUP.

    -
    DETECTION_DISPATCHER.SetGroup -

    The groups to which the FAC will report to.

    -
    -

    Type DETECTION_MANAGER

    @@ -232,12 +140,6 @@ Find a summary below describing for which situation a task type is created:

    - - - - @@ -253,15 +155,9 @@ Find a summary below describing for which situation a task type is created:

    - + - - - - @@ -280,12 +176,6 @@ Find a summary below describing for which situation a task type is created:

    - - - - @@ -301,7 +191,13 @@ Find a summary below describing for which situation a task type is created:

    - + + + + + @@ -352,20 +248,6 @@ Find a summary below describing for which situation a task type is created:

    - #DETECTION_DISPATCHER - -DETECTION_DISPATCHER - -
    -
    - - - -
    -
    -
    -
    - #DETECTION_MANAGER DETECTION_MANAGER @@ -393,282 +275,6 @@ Find a summary below describing for which situation a task type is created:

    Type DetectionManager

    -

    Type DETECTION_DISPATCHER

    - -

    DETECTION_DISPATCHER class.

    - -

    Field(s)

    -
    -
    - - #string - -DETECTION_DISPATCHER.ClassName - -
    -
    - - - -
    -
    -
    -
    - - Wrapper.Group#GROUP - -DETECTION_DISPATCHER.CommandCenter - -
    -
    - - - -
    -
    -
    -
    - - Functional.Detection#DETECTION_BASE - -DETECTION_DISPATCHER.Detection - -
    -
    - -

    The DETECTION_BASE object that is used to report the detected objects.

    - -
    -
    -
    -
    - - -DETECTION_DISPATCHER:EvaluateBAI(DetectedArea, FriendlyCoalition) - -
    -
    - -

    Creates a BAI task when there are targets for it.

    - -

    Parameters

    - -

    Return value

    - -

    Tasking.Task#TASK:

    - - -
    -
    -
    -
    - - -DETECTION_DISPATCHER:EvaluateCAS(DetectedArea) - -
    -
    - -

    Creates a CAS task when there are targets for it.

    - -

    Parameter

    - -

    Return value

    - -

    Tasking.Task#TASK:

    - - -
    -
    -
    -
    - - -DETECTION_DISPATCHER:EvaluateRemoveTask(Mission, Task, DetectedArea) - -
    -
    - -

    Evaluates the removal of the Task from the Mission.

    - - -

    Can only occur when the DetectedArea is Changed AND the state of the Task is "Planned".

    - -

    Parameters

    - -

    Return value

    - -

    Tasking.Task#TASK:

    - - -
    -
    -
    -
    - - -DETECTION_DISPATCHER:EvaluateSEAD(DetectedArea) - -
    -
    - -

    Creates a SEAD task when there are targets for it.

    - -

    Parameter

    - -

    Return values

    -
      -
    1. - -

      Set#SET_UNIT: -TargetSetUnit: The target set of units.

      - -
    2. -
    3. - -

      #nil: -If there are no targets to be set.

      - -
    4. -
    -
    -
    -
    -
    - - Tasking.Mission#MISSION - -DETECTION_DISPATCHER.Mission - -
    -
    - - - -
    -
    -
    -
    - - -DETECTION_DISPATCHER:New(SetGroup, Detection, Mission, CommandCenter) - -
    -
    - -

    DETECTION_DISPATCHER constructor.

    - -

    Parameters

    - -

    Return value

    - -

    #DETECTION_DISPATCHER: -self

    - -
    -
    -
    -
    - - -DETECTION_DISPATCHER:ProcessDetected(Detection) - -
    -
    - -

    Assigns tasks in relation to the detected items to the Set#SET_GROUP.

    - -

    Parameter

    - -

    Return value

    - -

    #boolean: -Return true if you want the task assigning to continue... false will cancel the loop.

    - -
    -
    -
    -
    - - Set#SET_GROUP - -DETECTION_DISPATCHER.SetGroup - -
    -
    - -

    The groups to which the FAC will report to.

    - -
    -
    -

    Type DETECTION_MANAGER

    DETECTION_MANAGER class.

    @@ -700,20 +306,6 @@ Return true if you want the task assigning to continue... false will cancel the

    The DETECTION_BASE object that is used to report the detected objects.

    - - -
    -
    - - - -DETECTION_MANAGER.FacScheduler - -
    -
    - - -
    @@ -768,8 +360,8 @@ self

    - -DETECTION_MANAGER:ReportDetected(Detection) + +DETECTION_MANAGER:ProcessDetected(Detection)
    @@ -789,39 +381,6 @@ self

    #DETECTION_MANAGER: self

    -
    -
    -
    -
    - - -DETECTION_MANAGER:Schedule(DelayTime, ReportInterval) - -
    -
    - -

    Schedule the FAC reporting.

    - -

    Parameters

    -
      -
    • - -

      #number DelayTime : -The delay in seconds to wait the reporting.

      - -
    • -
    • - -

      #number ReportInterval : -The repeat interval in seconds for the reporting to happen repeatedly.

      - -
    • -
    -

    Return value

    - -

    #DETECTION_MANAGER: -self

    -
    @@ -890,27 +449,6 @@ The interval in seconds when a report needs to be done.

    #DETECTION_MANAGER: self

    - -
    -
    -
    - - -DETECTION_MANAGER:_FacScheduler(SchedulerName) - -
    -
    - -

    Report the detected Unit#UNITs detected within the Detection#DETECTION_BASE object to the Set#SET_GROUPs.

    - -

    Parameter

    -
      -
    • - -

      SchedulerName :

      - -
    • -
    @@ -944,15 +482,63 @@ self

    - - -DETECTION_MANAGER._ScheduleDelayTime + +DETECTION_MANAGER:onafterReport(From, Event, To)
    +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_MANAGER:onafterStart(From, Event, To) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    diff --git a/docs/Documentation/Escort.html b/docs/Documentation/Escort.html index e2da40c9e..d2890d3e0 100644 --- a/docs/Documentation/Escort.html +++ b/docs/Documentation/Escort.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -161,7 +162,7 @@ Note that this is really fantastic, as you now have the dynamic of taking contro

    ESCORT initialization methods.

    -

    The following menus are created within the RADIO MENU of an active unit hosted by a player:

    +

    The following menus are created within the RADIO MENU (F10) of an active unit hosted by a player:

    • ESCORT.MenuFollowAt: Creates a menu to make the escort follow the client.
    • @@ -208,9 +209,27 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the

      Type ESCORT

    DETECTION_MANAGER.Detection

    The DETECTION_BASE object that is used to report the detected objects.

    -
    DETECTION_MANAGER.FacScheduler -
    DETECTION_MANAGER:ReportDetected(Detection)DETECTION_MANAGER:ProcessDetected(Detection)

    Reports the detected items to the Set#SET_GROUP.

    -
    DETECTION_MANAGER:Schedule(DelayTime, ReportInterval) -

    Schedule the FAC reporting.

    DETECTION_MANAGER:SetReportInterval(ReportInterval)

    Set the reporting time interval.

    -
    DETECTION_MANAGER:_FacScheduler(SchedulerName) -

    Report the detected Unit#UNITs detected within the Detection#DETECTION_BASE object to the Set#SET_GROUPs.

    DETECTION_MANAGER._ScheduleDelayTimeDETECTION_MANAGER:onafterReport(From, Event, To) + +
    DETECTION_MANAGER:onafterStart(From, Event, To)
    + + + + + + + + + + + + @@ -223,6 +242,12 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + + + + @@ -445,6 +470,12 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + + + + @@ -457,6 +488,12 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + + + + @@ -577,6 +614,12 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + + + + @@ -604,19 +647,19 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the - + - + - + @@ -628,31 +671,31 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the - + - + - + - + - + @@ -664,25 +707,25 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the - + - + - + - + @@ -708,6 +751,12 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the

    Type MENUPARAM

    ESCORT.CT1 + +
    ESCORT.ClassName +
    ESCORT.Detection + +
    ESCORT.EscortBriefing +
    ESCORT.EscortGroup +
    ESCORT.EscortMenu +
    ESCORT.EscortName +
    ESCORT.EscortSetGroup +
    ESCORT.FollowScheduler

    The instance of the SCHEDULER class.

    +
    ESCORT.GT1 +
    ESCORT.ReportTargetsScheduler +
    ESCORT:SetDetection(Detection) +

    Set a Detection method for the EscortClient to be reported upon.

    ESCORT._AssistTarget(MenuParam)ESCORT._AssistTarget(DetectedItemID, self, EscortGroupAttack)
    ESCORT._AttackTarget(MenuParam)ESCORT:_AttackTarget(DetectedItemID)
    ESCORT._Flare(MenuParam)ESCORT._Flare(MenuParam, self, Color, Message)
    ESCORT._HoldPosition(MenuParam)ESCORT._HoldPosition(MenuParam, self, OrbitGroup, OrbitHeight, OrbitSeconds)
    ESCORT._JoinUpAndFollow(MenuParam)ESCORT._JoinUpAndFollow(MenuParam, self, Distance)
    ESCORT._ROE(MenuParam)ESCORT._ROE(MenuParam, self, EscortROEFunction, EscortROEMessage)
    ESCORT._ROT(MenuParam)ESCORT._ROT(MenuParam, self, EscortROTFunction, EscortROTMessage)
    ESCORT._ReportNearbyTargetsNow(MenuParam)ESCORT._ReportNearbyTargetsNow(MenuParam, self)
    ESCORT._ResumeMission(MenuParam)ESCORT._ResumeMission(MenuParam, self, WayPoint)
    ESCORT._ScanTargets(MenuParam)ESCORT._ScanTargets(MenuParam, self, ScanDuration)
    ESCORT._Smoke(MenuParam)ESCORT._Smoke(MenuParam, self, Color, Message)
    ESCORT._SwitchReportNearbyTargets(MenuParam)ESCORT:_SwitchReportNearbyTargets(ReportTargets)
    + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -443,7 +366,7 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - + @@ -458,120 +381,18 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -780,18 +601,48 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    + + + + + + + + + + + + + + + + + + + + @@ -1140,38 +991,6 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - - -
    -
    - - -EVENT:OnBirth(EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTBIRTH event, and registers the unit born.

    - -

    Parameters

    -
      -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass :

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - -
    @@ -1215,102 +1034,6 @@ The self instance of the class for which the event is.

    #EVENT:

    - -
    -
    -
    - - -EVENT:OnBirthForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTBIRTH event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName : -The id of the unit for the event to be handled.

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass :

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnBirthRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTBIRTH event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnCrash(EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTCRASH event.

    - -

    Parameters

    -
      -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass :

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - -
    @@ -1354,102 +1077,6 @@ The self instance of the class for which the event is.

    #EVENT:

    - -
    -
    -
    - - -EVENT:OnCrashForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTCRASH event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnCrashRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTCRASH event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnDead(EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTDEAD event.

    - -

    Parameters

    -
      -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass :

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - -
    @@ -1493,70 +1120,6 @@ The self instance of the class for which the event is.

    #EVENT:

    - -
    -
    -
    - - -EVENT:OnDeadForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTDEAD event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnDeadRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTDEAD event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - -
    @@ -1595,134 +1158,6 @@ The self instance of the class for which the event is.

    #EVENT:

    - -
    -
    -
    - - -EVENT:OnEngineShutDownForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTENGINE_SHUTDOWN event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnEngineShutDownRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTENGINE_SHUTDOWN event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnEngineStartUpForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTENGINE_STARTUP event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnEngineStartUpRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTENGINE_STARTUP event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - -
    @@ -1773,7 +1208,7 @@ The self instance of the class for which the event is.

    -EVENT:OnEventForTemplate(EventTemplate, EventFunction, EventClass, OnEventFunction) +EVENT:OnEventForTemplate(EventTemplate, EventFunction, EventClass, OnEventFunction, EventID)
    @@ -1803,6 +1238,11 @@ The instance of the class for which the event is.

    #function OnEventFunction :

    + +
  • + +

    EventID :

    +
  • Return value

    @@ -1892,103 +1332,6 @@ The self instance of the class for which the event is captured. When the event h

    #EVENT:

    -
    -
    -
    -
    - - -EVENT:OnHit(EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTHIT event.

    - -

    Parameters

    -
      -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnHitForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTHIT event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnHitRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTHIT event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - -
    @@ -2025,367 +1368,6 @@ The self instance of the class for which the event is.

    - -EVENT:OnLandForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTLAND event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnLandRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTLAND event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnPilotDead(EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTPILOT_DEAD event.

    - -

    Parameters

    -
      -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass :

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnPilotDeadForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTPILOT_DEAD event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnPilotDeadRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTPILOT_DEAD event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnPlayerEnterRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTPLAYERENTERUNIT event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnPlayerEnterUnit(EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTPLAYERENTERUNIT event.

    - -

    Parameters

    -
      -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnPlayerLeaveRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTPLAYERLEAVEUNIT event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnPlayerLeaveUnit(EventFunction, EventClass) - -
    -
    - - - -

    Parameters

    -
      -
    • - -

      EventFunction :

      - -
    • -
    • - -

      EventClass :

      - -
    • -
    -
    -
    -
    -
    - - -EVENT:OnShot(EventFunction, EventClass) - -
    -
    - - - -

    Parameters

    -
      -
    • - -

      EventFunction :

      - -
    • -
    • - -

      EventClass :

      - -
    • -
    -
    -
    -
    -
    - - -EVENT:OnShotForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTSHOT event for a unit.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnShotRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTSHOT event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - EVENT:OnTakeOffForTemplate(EventTemplate, EventFunction, EventClass) @@ -2417,70 +1399,6 @@ The self instance of the class for which the event is.

    - -EVENT:OnTakeOffForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTTAKEOFF event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnTakeOffRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTTAKEOFF event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - EVENT:Remove(EventClass, EventID) @@ -3042,6 +1960,32 @@ Note that at the beginning of each field description, there is an indication whi + +
    +
    +
    + + +EVENTDATA.WeaponCategory + +
    +
    + + + +
    +
    +
    +
    + + +EVENTDATA.WeaponCoalition + +
    +
    + + +
    @@ -3055,6 +1999,19 @@ Note that at the beginning of each field description, there is an indication whi + +
    +
    +
    + + +EVENTDATA.WeaponPlayerName + +
    +
    + + +
    @@ -3068,6 +2025,36 @@ Note that at the beginning of each field description, there is an indication whi + +
    +
    +
    + + +EVENTDATA.WeaponTypeName + +
    +
    + + + +
    +
    +
    +
    + + + +EVENTDATA.WeaponUNIT + +
    +
    + + + + +

    Sometimes, the weapon is a player unit!

    +
    diff --git a/docs/Documentation/Fsm.html b/docs/Documentation/Fsm.html index 1518eb4af..40679fc47 100644 --- a/docs/Documentation/Fsm.html +++ b/docs/Documentation/Fsm.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -126,13 +127,593 @@ The state machine can be found on #FSM_TASK: Models Finite State Machines for Tasks. +
  • #FSM_PROCESS: Models Finite State Machines for Task actions, which control Clients.
  • +
  • #FSM_CONTROLLABLE: Models Finite State Machines for Controllables, which are Groups, Units, Clients.
  • +
  • #FSM_SET: Models Finite State Machines for Sets. Note that these FSMs control multiple objects!!! So State concerns here + for multiple objects or the position of the state machine in the process.
  • + +
    -

    1) #FSM class, extends Base#BASE

    +

    API CHANGE HISTORY

    + +

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    + +
      +
    • Added parts are expressed in bold type face.
    • +
    • Removed parts are expressed in italic type face.
    • +
    + +

    YYYY-MM-DD: CLASS:NewFunction( Params ) replaces CLASS:OldFunction( Params ) +YYYY-MM-DD: CLASS:NewFunction( Params ) added

    + +

    Hereby the change log:

    + +
      +
    • 2016-12-18: Released.
    • +
    + +
    + +

    AUTHORS and CONTRIBUTIONS

    + +

    Contributions:

    + +
      +
    • Pikey: Review of documentation & advice for improvements.
    • +
    + +

    Authors:

    + + + + +

    Global(s)

    +
    MENUPARAM.Distance + +
    MENUPARAM.ParamDistance @@ -781,6 +830,20 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the
    + #number + +ESCORT.CT1 + +
    +
    + + + +
    +
    +
    +
    + #string ESCORT.ClassName @@ -790,6 +853,34 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + +
    +
    +
    + + Functional.Detection#DETECTION_BASE + +ESCORT.Detection + +
    +
    + + + +
    +
    +
    +
    + + + +ESCORT.EscortBriefing + +
    +
    + + +
    @@ -818,6 +909,20 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + +
    +
    +
    + + + +ESCORT.EscortMenu + +
    +
    + + +
    @@ -1352,6 +1457,20 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + +
    +
    +
    + + + +ESCORT.EscortSetGroup + +
    +
    + + +
    @@ -1380,6 +1499,20 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the

    The instance of the SCHEDULER class.

    + +
    +
    +
    + + #number + +ESCORT.GT1 + +
    +
    + + +
    @@ -1923,6 +2056,30 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + +
    +
    +
    + + +ESCORT:SetDetection(Detection) + +
    +
    + +

    Set a Detection method for the EscortClient to be reported upon.

    + + +

    Detection methods are based on the derived classes from DETECTION_BASE.

    + +

    Parameter

    +
    @@ -1995,18 +2152,28 @@ If true, then the direction vector will be smoked.

    -ESCORT._AssistTarget(MenuParam) +ESCORT._AssistTarget(DetectedItemID, self, EscortGroupAttack)
    -

    Parameter

    +

    Parameters

    • -

      #MENUPARAM MenuParam :

      +

      #number DetectedItemID :

      + +
    • +
    • + +

      self :

      + +
    • +
    • + +

      EscortGroupAttack :

    @@ -2016,7 +2183,7 @@ If true, then the direction vector will be smoked.

    -ESCORT._AttackTarget(MenuParam) +ESCORT:_AttackTarget(DetectedItemID)
    @@ -2027,7 +2194,7 @@ If true, then the direction vector will be smoked.

    • -

      #MENUPARAM MenuParam :

      +

      #number DetectedItemID :

    @@ -2037,19 +2204,34 @@ If true, then the direction vector will be smoked.

    -ESCORT._Flare(MenuParam) +ESCORT._Flare(MenuParam, self, Color, Message)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      Color :

      + +
    • +
    • + +

      Message :

      +
    @@ -2071,19 +2253,39 @@ If true, then the direction vector will be smoked.

    -ESCORT._HoldPosition(MenuParam) +ESCORT._HoldPosition(MenuParam, self, OrbitGroup, OrbitHeight, OrbitSeconds)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      OrbitGroup :

      + +
    • +
    • + +

      OrbitHeight :

      + +
    • +
    • + +

      OrbitSeconds :

      +
    @@ -2092,19 +2294,29 @@ If true, then the direction vector will be smoked.

    -ESCORT._JoinUpAndFollow(MenuParam) +ESCORT._JoinUpAndFollow(MenuParam, self, Distance)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      Distance :

      +
    @@ -2113,19 +2325,34 @@ If true, then the direction vector will be smoked.

    -ESCORT._ROE(MenuParam) +ESCORT._ROE(MenuParam, self, EscortROEFunction, EscortROEMessage)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      EscortROEFunction :

      + +
    • +
    • + +

      EscortROEMessage :

      +
    @@ -2134,19 +2361,34 @@ If true, then the direction vector will be smoked.

    -ESCORT._ROT(MenuParam) +ESCORT._ROT(MenuParam, self, EscortROTFunction, EscortROTMessage)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      EscortROTFunction :

      + +
    • +
    • + +

      EscortROTMessage :

      +
    @@ -2155,19 +2397,24 @@ If true, then the direction vector will be smoked.

    -ESCORT._ReportNearbyTargetsNow(MenuParam) +ESCORT._ReportNearbyTargetsNow(MenuParam, self)
    -

    Parameter

    +

    Parameters

    @@ -2189,19 +2436,29 @@ If true, then the direction vector will be smoked.

    -ESCORT._ResumeMission(MenuParam) +ESCORT._ResumeMission(MenuParam, self, WayPoint)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      WayPoint :

      +
    @@ -2210,19 +2467,29 @@ If true, then the direction vector will be smoked.

    -ESCORT._ScanTargets(MenuParam) +ESCORT._ScanTargets(MenuParam, self, ScanDuration)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      ScanDuration :

      +
    @@ -2231,19 +2498,34 @@ If true, then the direction vector will be smoked.

    -ESCORT._Smoke(MenuParam) +ESCORT._Smoke(MenuParam, self, Color, Message)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      Color :

      + +
    • +
    • + +

      Message :

      +
    @@ -2252,7 +2534,7 @@ If true, then the direction vector will be smoked.

    -ESCORT._SwitchReportNearbyTargets(MenuParam) +ESCORT:_SwitchReportNearbyTargets(ReportTargets)
    @@ -2263,7 +2545,7 @@ If true, then the direction vector will be smoked.

    • -

      MenuParam :

      +

      ReportTargets :

    @@ -2312,6 +2594,20 @@ If true, then the direction vector will be smoked.

    + + +MENUPARAM.Distance + +
    +
    + + + +
    +
    +
    +
    + #Distance MENUPARAM.ParamDistance diff --git a/docs/Documentation/Event.html b/docs/Documentation/Event.html index d4bdd4c3d..da27fb360 100644 --- a/docs/Documentation/Event.html +++ b/docs/Documentation/Event.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -332,108 +333,30 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    EVENT:New() -
    EVENT:OnBirth(EventFunction, EventClass) -

    Set a new listener for an SEVENTBIRTH event, and registers the unit born.

    EVENT:OnBirthForTemplate(EventGroup, EventFunction, EventClass, EventTemplate)

    Create an OnBirth event handler for a group

    -
    EVENT:OnBirthForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTBIRTH event.

    -
    EVENT:OnBirthRemove(EventClass) -

    Stop listening to SEVENTBIRTH event.

    -
    EVENT:OnCrash(EventFunction, EventClass) -

    Set a new listener for an SEVENTCRASH event.

    EVENT:OnCrashForTemplate(EventGroup, EventFunction, EventClass, EventTemplate)

    Create an OnCrash event handler for a group

    -
    EVENT:OnCrashForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTCRASH event.

    -
    EVENT:OnCrashRemove(EventClass) -

    Stop listening to SEVENTCRASH event.

    -
    EVENT:OnDead(EventFunction, EventClass) -

    Set a new listener for an SEVENTDEAD event.

    EVENT:OnDeadForTemplate(EventGroup, EventFunction, EventClass, EventTemplate)

    Create an OnDead event handler for a group

    -
    EVENT:OnDeadForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTDEAD event.

    -
    EVENT:OnDeadRemove(EventClass) -

    Stop listening to SEVENTDEAD event.

    EVENT:OnEngineShutDownForTemplate(EventTemplate, EventFunction, EventClass)

    Create an OnDead event handler for a group

    -
    EVENT:OnEngineShutDownForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTENGINE_SHUTDOWN event.

    -
    EVENT:OnEngineShutDownRemove(EventClass) -

    Stop listening to SEVENTENGINE_SHUTDOWN event.

    -
    EVENT:OnEngineStartUpForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTENGINE_STARTUP event.

    -
    EVENT:OnEngineStartUpRemove(EventClass) -

    Stop listening to SEVENTENGINE_STARTUP event.

    EVENT:OnEventForTemplate(EventTemplate, EventFunction, EventClass, OnEventFunction)EVENT:OnEventForTemplate(EventTemplate, EventFunction, EventClass, OnEventFunction, EventID)

    Create an OnDead event handler for a group

    EVENT:OnEventGeneric(EventFunction, EventClass, EventID)

    Set a new listener for an SEVENTX event independent from a unit or a weapon.

    -
    EVENT:OnHit(EventFunction, EventClass) -

    Set a new listener for an SEVENTHIT event.

    -
    EVENT:OnHitForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTHIT event.

    -
    EVENT:OnHitRemove(EventClass) -

    Stop listening to SEVENTHIT event.

    EVENT:OnLandForTemplate(EventTemplate, EventFunction, EventClass) -
    EVENT:OnLandForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTLAND event.

    -
    EVENT:OnLandRemove(EventClass) -

    Stop listening to SEVENTLAND event.

    -
    EVENT:OnPilotDead(EventFunction, EventClass) -

    Set a new listener for an SEVENTPILOT_DEAD event.

    -
    EVENT:OnPilotDeadForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTPILOT_DEAD event.

    -
    EVENT:OnPilotDeadRemove(EventClass) -

    Stop listening to SEVENTPILOT_DEAD event.

    -
    EVENT:OnPlayerEnterRemove(EventClass) -

    Stop listening to SEVENTPLAYERENTERUNIT event.

    -
    EVENT:OnPlayerEnterUnit(EventFunction, EventClass) -

    Set a new listener for an SEVENTPLAYERENTERUNIT event.

    -
    EVENT:OnPlayerLeaveRemove(EventClass) -

    Stop listening to SEVENTPLAYERLEAVEUNIT event.

    -
    EVENT:OnPlayerLeaveUnit(EventFunction, EventClass) - -
    EVENT:OnShot(EventFunction, EventClass) - -
    EVENT:OnShotForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTSHOT event for a unit.

    -
    EVENT:OnShotRemove(EventClass) -

    Stop listening to SEVENTSHOT event.

    EVENT:OnTakeOffForTemplate(EventTemplate, EventFunction, EventClass) -
    EVENT:OnTakeOffForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTTAKEOFF event.

    -
    EVENT:OnTakeOffRemove(EventClass) -

    Stop listening to SEVENTTAKEOFF event.

    EVENTDATA.Weapon +
    EVENTDATA.WeaponCategory + +
    EVENTDATA.WeaponCoalition +
    EVENTDATA.WeaponName +
    EVENTDATA.WeaponPlayerName +
    EVENTDATA.WeaponTgtDCSUnit +
    EVENTDATA.WeaponTypeName + +
    EVENTDATA.WeaponUNIT +
    + + + + + + + + + + + + + + + + + + + + +
    FSM +

    1) FSM class, extends Base#BASE

    Transition Rules and Transition Handlers and Event Triggers

    -

    The FSM class is the base class of all FSM_ derived classes. It implements the main functionality to define and execute Finite State Machines. +

    The FSM class is the base class of all FSM_ derived classes.

    +
    FSM_CONTROLLABLE +

    FSM_CONTROLLABLE, extends #FSM

    + +

    FSM_CONTROLLABLE class models Finite State Machines for Controllables, which are Groups, Units, Clients.

    +
    FSM_PROCESS +

    FSM_PROCESS, extends #FSM

    + +

    FSM_PROCESS class models Finite State Machines for Task actions, which control Clients.

    +
    FSM_SET +

    FSM_SET, extends #FSM

    + +

    FSM_SET class models Finite State Machines for Sets.

    +
    FSM_TASK +

    FSM_TASK, extends #FSM

    + +

    FSM_TASK class models Finite State Machines for Tasks.

    +
    +

    Type FSM

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FSM:AddEndState(State) +

    Adds an End state.

    +
    FSM:AddProcess(From, Event, Process, ReturnEvents) +

    Set the default Process template with key ProcessName providing the ProcessClass and the process object when it is assigned to a Controllable by the task.

    +
    FSM:AddScore(State, ScoreText, Score) +

    Adds a score for the FSM to be achieved.

    +
    FSM:AddScoreProcess(From, Event, State, ScoreText, Score) +

    Adds a score for the FSM_PROCESS to be achieved.

    +
    FSM:AddTransition(From, Event, To) +

    Add a new transition rule to the FSM.

    +
    FSM.CallScheduler + +
    FSM.Events + +
    FSM:GetEndStates() +

    Returns the End states.

    +
    FSM:GetProcess(From, Event) + +
    FSM:GetProcesses() +

    Returns a table of the SubFSM rules defined within the FSM.

    +
    FSM:GetScores() +

    Returns a table with the scores defined.

    +
    FSM:GetStartState() +

    Returns the start state of the FSM.

    +
    FSM:GetState() + +
    FSM:GetSubs() +

    Returns a table with the Subs defined.

    +
    FSM:GetTransitions() +

    Returns a table of the transition rules defined within the FSM.

    +
    FSM:Is(State) + +
    FSM:LoadCallBacks(CallBackTable) + +
    FSM:New(FsmT) +

    Creates a new FSM object.

    +
    FSM.Scores + +
    FSM:SetStartState(State) +

    Sets the start state of the FSM.

    +
    FSM._EndStates + +
    FSM._EventSchedules + +
    FSM._Processes + +
    FSM._Scores + +
    FSM._StartState + +
    FSM._Transitions + +
    FSM:_add_to_map(Map, Event) + +
    FSM:_call_handler(handler, params, EventName) + +
    FSM:_create_transition(EventName) + +
    FSM:_delayed_transition(EventName) + +
    FSM:_eventmap(Events, EventStructure) + +
    FSM:_gosub(ParentFrom, ParentEvent) + +
    FSM:_handler(EventName, ...) + +
    FSM:_isendstate(Current) + +
    FSM:_submap(subs, sub, name) + +
    FSM:can(e) + +
    FSM:cannot(e) + +
    FSM.current + +
    FSM.endstates + +
    FSM:is(state) + +
    FSM.options + +
    FSM.subs + +
    + +

    Type FSM_CONTROLLABLE

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FSM_CONTROLLABLE.Controllable + +
    FSM_CONTROLLABLE:GetControllable() +

    Gets the CONTROLLABLE object that the FSM_CONTROLLABLE governs.

    +
    FSM_CONTROLLABLE:New(FSMT, Controllable) +

    Creates a new FSM_CONTROLLABLE object.

    +
    FSM_CONTROLLABLE:OnAfterStop(Controllable, From, Event, To) +

    OnAfter Transition Handler for Event Stop.

    +
    FSM_CONTROLLABLE:OnBeforeStop(Controllable, From, Event, To) +

    OnBefore Transition Handler for Event Stop.

    +
    FSM_CONTROLLABLE:OnEnterStopped(Controllable, From, Event, To) +

    OnEnter Transition Handler for State Stopped.

    +
    FSM_CONTROLLABLE:OnLeaveStopped(Controllable, From, Event, To) +

    OnLeave Transition Handler for State Stopped.

    +
    FSM_CONTROLLABLE:SetControllable(FSMControllable) +

    Sets the CONTROLLABLE object that the FSM_CONTROLLABLE governs.

    +
    FSM_CONTROLLABLE:Stop() +

    Synchronous Event Trigger for Event Stop.

    +
    FSM_CONTROLLABLE:__Stop(Delay) +

    Asynchronous Event Trigger for Event Stop.

    +
    FSM_CONTROLLABLE:_call_handler(handler, params, EventName) + +
    + +

    Type FSM_PROCESS

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FSM_PROCESS:Assign(Task, ProcessUnit) +

    Assign the process to a Unit and activate the process.

    +
    FSM_PROCESS:Copy(Controllable, Task) +

    Creates a new FSMPROCESS object based on this FSMPROCESS.

    +
    FSM_PROCESS:GetCommandCenter() +

    Gets the mission of the process.

    +
    FSM_PROCESS:GetMission() +

    Gets the mission of the process.

    +
    FSM_PROCESS:GetTask() +

    Gets the task of the process.

    +
    FSM_PROCESS:Init(FsmProcess) + +
    FSM_PROCESS:Message(Message) +

    Send a message of the Task to the Group of the Unit.

    +
    FSM_PROCESS:New(Controllable, Task) +

    Creates a new FSM_PROCESS object.

    +
    FSM_PROCESS:Remove() +

    Removes an FSM_PROCESS object.

    +
    FSM_PROCESS:SetTask(Task) +

    Sets the task of the process.

    +
    FSM_PROCESS.Task + +
    FSM_PROCESS:_call_handler(handler, params, EventName) + +
    FSM_PROCESS:onenterAssigned(ProcessUnit) + +
    FSM_PROCESS:onenterFailed(ProcessUnit) + +
    FSM_PROCESS:onenterSuccess(ProcessUnit) + +
    FSM_PROCESS:onstatechange(ProcessUnit, Event, From, To, Task, Dummy) +

    StateMachine callback function for a FSM_PROCESS

    +
    + +

    Type FSM_SET

    + + + + + + + + + + + + + + + + + +
    FSM_SET:Get() +

    Gets the SETBASE object that the FSMSET governs.

    +
    FSM_SET:New(FSMT, Set_SET_BASE, FSMSet) +

    Creates a new FSM_SET object.

    +
    FSM_SET.Set + +
    FSM_SET:_call_handler(handler, params, EventName) + +
    + +

    Type FSM_TASK

    + + + + + + + + + + + + + +
    FSM_TASK:New(FSMT, Task, TaskUnit) +

    Creates a new FSM_TASK object.

    +
    FSM_TASK.Task + +
    FSM_TASK:_call_handler(handler, params, EventName) + +
    + +

    Global(s)

    +
    +
    + + #FSM + +FSM + +
    +
    + +

    1) FSM class, extends Base#BASE

    + +

    Transition Rules and Transition Handlers and Event Triggers

    + +

    The FSM class is the base class of all FSM_ derived classes.

    + + +

    It implements the main functionality to define and execute Finite State Machines. The derived FSM_ classes extend the Finite State Machine functionality to run a workflow process for a specific purpose or component.

    Finite State Machines have Transition Rules, Transition Handlers and Event Triggers.

    @@ -372,544 +953,6 @@ Depending upon which state is returned, the main FSM can contin
    -

    API CHANGE HISTORY

    - -

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    - -
      -
    • Added parts are expressed in bold type face.
    • -
    • Removed parts are expressed in italic type face.
    • -
    - -

    YYYY-MM-DD: CLASS:NewFunction( Params ) replaces CLASS:OldFunction( Params ) -YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - -

    Hereby the change log:

    - -
      -
    • 2016-12-18: Released.
    • -
    - -
    - -

    AUTHORS and CONTRIBUTIONS

    - -

    Contributions:

    - -
      -
    • Pikey: Review of documentation & advice for improvements.
    • -
    - -

    Authors:

    - - - - -

    Global(s)

    - - - - - - - - - - - - - - - - - - - - - -
    FSM - -
    FSM_CONTROLLABLE - -
    FSM_PROCESS - -
    FSM_SET - -
    FSM_TASK - -
    -

    Type FSM

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    FSM:AddEndState(State) -

    Adds an End state.

    -
    FSM:AddProcess(From, Event, Process, ReturnEvents) -

    Set the default Process template with key ProcessName providing the ProcessClass and the process object when it is assigned to a Controllable by the task.

    -
    FSM:AddScore(State, ScoreText, Score) -

    Adds a score for the FSM to be achieved.

    -
    FSM:AddScoreProcess(From, Event, State, ScoreText, Score) -

    Adds a score for the FSM_PROCESS to be achieved.

    -
    FSM:AddTransition(From, Event, To) -

    Add a new transition rule to the FSM.

    -
    FSM.CallScheduler - -
    FSM.ClassName - -
    FSM.Events - -
    FSM:GetEndStates() -

    Returns the End states.

    -
    FSM:GetProcess(From, Event) - -
    FSM:GetProcesses() -

    Returns a table of the SubFSM rules defined within the FSM.

    -
    FSM:GetScores() -

    Returns a table with the scores defined.

    -
    FSM:GetStartState() -

    Returns the start state of the FSM.

    -
    FSM:GetState() - -
    FSM:GetSubs() -

    Returns a table with the Subs defined.

    -
    FSM:GetTransitions() -

    Returns a table of the transition rules defined within the FSM.

    -
    FSM:Is(State) - -
    FSM:LoadCallBacks(CallBackTable) - -
    FSM:New(FsmT) -

    Creates a new FSM object.

    -
    FSM.Scores - -
    FSM:SetStartState(State) -

    Sets the start state of the FSM.

    -
    FSM._EndStates - -
    FSM._EventSchedules - -
    FSM._Processes - -
    FSM._Scores - -
    FSM._StartState - -
    FSM._Transitions - -
    FSM:_add_to_map(Map, Event) - -
    FSM:_call_handler(handler, params, EventName) - -
    FSM:_create_transition(EventName) - -
    FSM:_delayed_transition(EventName) - -
    FSM:_eventmap(Events, EventStructure) - -
    FSM:_gosub(ParentFrom, ParentEvent) - -
    FSM:_handler(EventName, ...) - -
    FSM:_isendstate(Current) - -
    FSM:_submap(subs, sub, name) - -
    FSM:can(e) - -
    FSM:cannot(e) - -
    FSM.current - -
    FSM.endstates - -
    FSM:is(state) - -
    FSM.options - -
    FSM.subs - -
    - -

    Type FSM_CONTROLLABLE

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    FSM_CONTROLLABLE.ClassName - -
    FSM_CONTROLLABLE.Controllable - -
    FSM_CONTROLLABLE:GetControllable() -

    Gets the CONTROLLABLE object that the FSM_CONTROLLABLE governs.

    -
    FSM_CONTROLLABLE:New(FSMT, Controllable) -

    Creates a new FSM_CONTROLLABLE object.

    -
    FSM_CONTROLLABLE:SetControllable(FSMControllable) -

    Sets the CONTROLLABLE object that the FSM_CONTROLLABLE governs.

    -
    FSM_CONTROLLABLE:_call_handler(handler, params, EventName) - -
    - -

    Type FSM_PROCESS

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    FSM_PROCESS:Assign(Task, ProcessUnit) -

    Assign the process to a Unit and activate the process.

    -
    FSM_PROCESS.ClassName - -
    FSM_PROCESS:Copy(Controllable, Task) -

    Creates a new FSMPROCESS object based on this FSMPROCESS.

    -
    FSM_PROCESS:GetCommandCenter() -

    Gets the mission of the process.

    -
    FSM_PROCESS:GetMission() -

    Gets the mission of the process.

    -
    FSM_PROCESS:GetTask() -

    Gets the task of the process.

    -
    FSM_PROCESS:Init(FsmProcess) - -
    FSM_PROCESS:Message(Message) -

    Send a message of the Task to the Group of the Unit.

    -
    FSM_PROCESS:New(Controllable, Task) -

    Creates a new FSM_PROCESS object.

    -
    FSM_PROCESS:SetTask(Task) -

    Sets the task of the process.

    -
    FSM_PROCESS.Task - -
    FSM_PROCESS:onenterAssigned(ProcessUnit) - -
    FSM_PROCESS:onenterFailed(ProcessUnit) - -
    FSM_PROCESS:onenterSuccess(ProcessUnit) - -
    FSM_PROCESS:onstatechange(ProcessUnit, Event, From, To, Dummy) -

    StateMachine callback function for a FSM_PROCESS

    -
    - -

    Type FSM_SET

    - - - - - - - - - - - - - - - - - - - - - -
    FSM_SET.ClassName - -
    FSM_SET:Get() -

    Gets the SETBASE object that the FSMSET governs.

    -
    FSM_SET:New(FSMT, Set_SET_BASE, FSMSet) -

    Creates a new FSM_SET object.

    -
    FSM_SET.Set - -
    FSM_SET:_call_handler(handler, params, EventName) - -
    - -

    Type FSM_TASK

    - - - - - - - - - - - - - - - - - -
    FSM_TASK.ClassName - -
    FSM_TASK:New(FSMT, Task, TaskUnit) -

    Creates a new FSM_TASK object.

    -
    FSM_TASK.Task - -
    FSM_TASK:_call_handler(handler, params, EventName) - -
    - -

    Global(s)

    -
    -
    - - #FSM - -FSM - -
    -
    - -
    @@ -923,6 +966,13 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    +

    FSM_CONTROLLABLE, extends #FSM

    + +

    FSM_CONTROLLABLE class models Finite State Machines for Controllables, which are Groups, Units, Clients.

    + + + +
    @@ -937,6 +987,13 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    +

    FSM_PROCESS, extends #FSM

    + +

    FSM_PROCESS class models Finite State Machines for Task actions, which control Clients.

    + + + +
    @@ -951,6 +1008,15 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    +

    FSM_SET, extends #FSM

    + +

    FSM_SET class models Finite State Machines for Sets.

    + + +

    Note that these FSMs control multiple objects!!! So State concerns here +for multiple objects or the position of the state machine in the process.

    + +
    @@ -965,6 +1031,13 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    +

    FSM_TASK, extends #FSM

    + +

    FSM_TASK class models Finite State Machines for Tasks.

    + + + +
    @@ -972,10 +1045,7 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    Type Fsm

    Type FSM

    - -

    FSM class

    - -

    Field(s)

    +

    Field(s)

    @@ -1181,20 +1251,6 @@ The To state.

    - -
    -
    -
    - - #string - -FSM.ClassName - -
    -
    - - -
    @@ -1879,24 +1935,7 @@ A string defining the start state.

    Type FSM_CONTROLLABLE

    - -

    FSM_CONTROLLABLE class

    - -

    Field(s)

    -
    -
    - - #string - -FSM_CONTROLLABLE.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    @@ -1960,6 +1999,176 @@ Finite State Machine Table

    #FSM_CONTROLLABLE:

    + +
    +
    +
    + + +FSM_CONTROLLABLE:OnAfterStop(Controllable, From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Stop.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

      + +
    • +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +FSM_CONTROLLABLE:OnBeforeStop(Controllable, From, Event, To) + +
    +
    + +

    OnBefore Transition Handler for Event Stop.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

      + +
    • +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +FSM_CONTROLLABLE:OnEnterStopped(Controllable, From, Event, To) + +
    +
    + +

    OnEnter Transition Handler for State Stopped.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

      + +
    • +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +FSM_CONTROLLABLE:OnLeaveStopped(Controllable, From, Event, To) + +
    +
    + +

    OnLeave Transition Handler for State Stopped.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

      + +
    • +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    +
    @@ -1991,6 +2200,41 @@ Finite State Machine Table

    + +FSM_CONTROLLABLE:Stop() + +
    +
    + +

    Synchronous Event Trigger for Event Stop.

    + +
    +
    +
    +
    + + +FSM_CONTROLLABLE:__Stop(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Stop.

    + +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    +
    +
    +
    +
    + FSM_CONTROLLABLE:_call_handler(handler, params, EventName) @@ -2021,10 +2265,7 @@ Finite State Machine Table

    Type FSM_PROCESS

    - -

    FSM_PROCESS class

    - -

    Field(s)

    +

    Field(s)

    @@ -2054,20 +2295,6 @@ Finite State Machine Table

    #FSM_PROCESS: self

    - -
    -
    -
    - - #string - -FSM_PROCESS.ClassName - -
    -
    - - -
    @@ -2226,6 +2453,24 @@ self

    #FSM_PROCESS:

    + +
    +
    +
    + + +FSM_PROCESS:Remove() + +
    +
    + +

    Removes an FSM_PROCESS object.

    + +

    Return value

    + +

    #FSM_PROCESS:

    + +
    @@ -2266,6 +2511,37 @@ self

    + +
    +
    +
    + + +FSM_PROCESS:_call_handler(handler, params, EventName) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      handler :

      + +
    • +
    • + +

      params :

      + +
    • +
    • + +

      EventName :

      + +
    • +
    @@ -2335,7 +2611,7 @@ self

    -FSM_PROCESS:onstatechange(ProcessUnit, Event, From, To, Dummy) +FSM_PROCESS:onstatechange(ProcessUnit, Event, From, To, Task, Dummy)
    @@ -2366,6 +2642,11 @@ self

  • +

    Task :

    + +
  • +
  • +

    Dummy :

  • @@ -2381,20 +2662,6 @@ self

    - #string - -FSM_SET.ClassName - -
    -
    - - - -
    -
    -
    -
    - FSM_SET:Get() @@ -2502,20 +2769,6 @@ FSMSet (optional) The Set object that the FSM_SET governs.

    - #string - -FSM_TASK.ClassName - -
    -
    - - - -
    -
    -
    -
    - FSM_TASK:New(FSMT, Task, TaskUnit) diff --git a/docs/Documentation/Group.html b/docs/Documentation/Group.html index eb3d67814..7d74f1dbd 100644 --- a/docs/Documentation/Group.html +++ b/docs/Documentation/Group.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -72,12 +73,13 @@

    Module Group

    -

    This module contains the GROUP class.

    +

    Wrapper -- GROUP is a wrapper class for the DCS Class Group.

    -

    1) Group#GROUP class, extends Controllable#CONTROLLABLE

    -

    The Group#GROUP class is a wrapper class to handle the DCS Group objects:

    +
    + +

    The #GROUP class is a wrapper class to handle the DCS Group objects:

    • Support all DCS Group APIs.
    • @@ -88,66 +90,7 @@

      IMPORTANT: ONE SHOULD NEVER SANATIZE these GROUP OBJECT REFERENCES! (make the GROUP object references nil).

      -

      1.1) GROUP reference methods

      -

      For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _DATABASE object. -This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the SPAWN class).

      - -

      The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference -using the DCS Group or the DCS GroupName.

      - -

      Another thing to know is that GROUP objects do not "contain" the DCS Group object. -The GROUP methods will reference the DCS Group object by name when it is needed during API execution. -If the DCS Group object does not exist or is nil, the GROUP methods will return nil and log an exception in the DCS.log file.

      - -

      The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance:

      - -
        -
      • GROUP.Find(): Find a GROUP instance from the _DATABASE object using a DCS Group object.
      • -
      • GROUP.FindByName(): Find a GROUP instance from the _DATABASE object using a DCS Group name.
      • -
      - -

      1.2) GROUP task methods

      - -

      A GROUP is a Controllable. See the Controllable task methods section for a description of the task methods.

      - -

      1.2.4) Obtain the mission from group templates

      - -

      Group templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a group and assign it to another:

      - - - -

      1.3) GROUP Command methods

      - -

      A GROUP is a Controllable. See the Controllable command methods section for a description of the command methods.

      - -

      1.4) GROUP option methods

      - -

      A GROUP is a Controllable. See the Controllable option methods section for a description of the option methods.

      - -

      1.5) GROUP Zone validation methods

      - -

      The group can be validated whether it is completely, partly or not within a Zone. -Use the following Zone validation methods on the group:

      - - - -

      The zone can be of any Zone class derived from Zone#ZONE_BASE. So, these methods are polymorphic to the zones tested on.

      - -

      1.6) GROUP AI methods

      - -

      A GROUP has AI methods to control the AI activation.

      - - +

      See the detailed documentation on the GROUP class.


      @@ -162,6 +105,8 @@ Use the following Zone validation methods on the group:

      Hereby the change log:

      +

      2017-03-26: GROUP:RouteRTB( RTBAirbase, Speed ) added.

      +

      2017-03-07: GROUP:HandleEvent( Event, EventFunction ) added.
      2017-03-07: GROUP:UnHandleEvent( Event ) added.

      @@ -193,7 +138,9 @@ Use the following Zone validation methods on the group:

      GROUP +

      GROUP class, extends Controllable#CONTROLLABLE

      +

      For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _DATABASE object.

      @@ -215,12 +162,6 @@ Use the following Zone validation methods on the group:

      GROUP:CalculateThreatLevelA2G()

      Calculate the maxium A2G threat level of the Group.

      - - - - GROUP.ClassName - - @@ -262,7 +203,7 @@ Use the following Zone validation methods on the group:

      GROUP:GetCategoryName() -

      Returns the category name of the DCS Group.

      +

      Returns the category name of the #GROUP.

      @@ -319,6 +260,12 @@ Use the following Zone validation methods on the group:

      GROUP:GetMinHeight()

      Returns the current minimum height of the group.

      + + + + GROUP:GetPlayerNames() + +

      Get player names

      @@ -408,7 +355,7 @@ Use the following Zone validation methods on the group:

      GROUP:IsAlive() -

      Returns if the DCS Group is alive.

      +

      Returns if the Group is alive.

      @@ -463,6 +410,12 @@ Use the following Zone validation methods on the group:

      GROUP:Respawn(Template)

      Respawn the GROUP using a (tweaked) template of the Group.

      + + + + GROUP:RouteRTB(RTBAirbase, Speed) + +

      (AIR) Return the Group to an Airbase#AIRBASE.

      @@ -520,6 +473,69 @@ Use the following Zone validation methods on the group:

    +

    GROUP class, extends Controllable#CONTROLLABLE

    + +

    For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _DATABASE object.

    + + +

    This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the SPAWN class).

    + +

    The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference +using the DCS Group or the DCS GroupName.

    + +

    Another thing to know is that GROUP objects do not "contain" the DCS Group object. +The GROUP methods will reference the DCS Group object by name when it is needed during API execution. +If the DCS Group object does not exist or is nil, the GROUP methods will return nil and log an exception in the DCS.log file.

    + +

    The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance:

    + +
      +
    • GROUP.Find(): Find a GROUP instance from the _DATABASE object using a DCS Group object.
    • +
    • GROUP.FindByName(): Find a GROUP instance from the _DATABASE object using a DCS Group name.
    • +
    + +

    GROUP task methods

    + +

    A GROUP is a Controllable. See the Controllable task methods section for a description of the task methods.

    + +

    Obtain the mission from group templates

    + +

    Group templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a group and assign it to another:

    + + + +

    GROUP Command methods

    + +

    A GROUP is a Controllable. See the Controllable command methods section for a description of the command methods.

    + +

    GROUP option methods

    + +

    A GROUP is a Controllable. See the Controllable option methods section for a description of the option methods.

    + +

    GROUP Zone validation methods

    + +

    The group can be validated whether it is completely, partly or not within a Zone. +Use the following Zone validation methods on the group:

    + + + +

    The zone can be of any Zone class derived from Zone#ZONE_BASE. So, these methods are polymorphic to the zones tested on.

    + +

    GROUP AI methods

    + +

    A GROUP has AI methods to control the AI activation.

    + +
    @@ -527,10 +543,7 @@ Use the following Zone validation methods on the group:

    Type Group

    Type GROUP

    - -

    The GROUP class

    - -

    Field(s)

    +

    Field(s)

    @@ -576,20 +589,6 @@ All units on the ground result.

    Calculate the maxium A2G threat level of the Group.

    -
    -
    -
    -
    - - #string - -GROUP.ClassName - -
    -
    - - -
    @@ -748,7 +747,7 @@ The category ID

    -

    Returns the category name of the DCS Group.

    +

    Returns the category name of the #GROUP.

    Return value

    @@ -958,6 +957,34 @@ Minimum height found.

    + +GROUP:GetPlayerNames() + +
    +
    + +

    Get player names

    + +

    Return values

    +
      +
    1. + +

      #table: +The group has players, an array of player names is returned.

      + +
    2. +
    3. + +

      #nil: +The group has no players

      + +
    4. +
    +
    +
    +
    +
    + GROUP:GetPositionVec3() @@ -1264,16 +1291,41 @@ true if DCS Group contains AirPlanes.

    -

    Returns if the DCS Group is alive.

    +

    Returns if the Group is alive.

    -

    When the group exists at run-time, this method will return true, otherwise false.

    +

    The Group must:

    -

    Return value

    +
      +
    • Exist at run-time.
    • +
    • Has at least one unit.
    • +
    + +

    When the first Unit of the Group is active, it will return true. +If the first Unit of the Group is inactive, it will return false.

    + + +

    Return values

    +
      +
    1. #boolean: -true if the DCS Group is alive.

      +true if the Group is alive and active.

      +
    2. +
    3. + +

      #boolean: +false if the Group is alive but inactive.

      + +
    4. +
    5. + +

      #nil: +if the group does not exist anymore.

      + +
    6. +
    @@ -1506,6 +1558,51 @@ The template of the Group retrieved with GROUP:GetTemplate()

    + +GROUP:RouteRTB(RTBAirbase, Speed) + +
    +
    + +

    (AIR) Return the Group to an Airbase#AIRBASE.

    + + +

    The following things are to be taken into account:

    + +
      +
    • The group is respawned to achieve the RTB, there may be side artefacts as a result of this. (Like weapons suddenly come back).
    • +
    • A group consisting out of more than one unit, may rejoin formation when respawned.
    • +
    • A speed can be given in km/h. If no speed is specified, the maximum speed of the first unit will be taken to return to base.
    • +
    • When there is no Airbase object specified, the group will return to the home base if the route of the group is pinned at take-off or at landing to a base.
    • +
    • When there is no Airbase object specified and the group route is not pinned to any airbase, it will return to the nearest airbase.
    • +
    + + +

    Parameters

    +
      +
    • + +

      Wrapper.Airbase#AIRBASE RTBAirbase : +(optional) The Airbase to return to. If blank, the controllable will return to the nearest friendly airbase.

      + +
    • +
    • + +

      #number Speed : +(optional) The Speed, if no Speed is given, the maximum Speed of the first unit is selected.

      + +
    • +
    +

    Return value

    + +

    #GROUP:

    + + +
    +
    +
    +
    + GROUP:SetAIOff() diff --git a/docs/Documentation/Identifiable.html b/docs/Documentation/Identifiable.html index 33086e9bf..26302176c 100644 --- a/docs/Documentation/Identifiable.html +++ b/docs/Documentation/Identifiable.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -473,6 +474,10 @@ The DCS Identifiable is not existing or alive.

    Returns if the Identifiable is alive.

    + +

    If the Identifiable is not alive, nil is returned.
    +If the Identifiable is alive, true is returned.

    +

    Return values

    1. @@ -484,7 +489,7 @@ true if Identifiable is alive.

    2. #nil: -The DCS Identifiable is not existing or alive.

      +if the Identifiable is not existing or is not alive.

    diff --git a/docs/Documentation/MOVEMENT.html b/docs/Documentation/MOVEMENT.html index aed446e5c..2ec03bdbd 100644 --- a/docs/Documentation/MOVEMENT.html +++ b/docs/Documentation/MOVEMENT.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -70,7 +71,7 @@
    -

    Module MOVEMENT

    +

    Module Movement

    Limit the simultaneous movement of Groups within a running Mission.

    @@ -89,30 +90,60 @@ on defined intervals (currently every minute).

    -

    Type MOVEMENT

    - +

    Type MOVEMENT

    +
    + + + + + + + + + + + + + + + + + + + + - - - - + + + + @@ -150,8 +181,26 @@ on defined intervals (currently every minute).

    -

    Type MOVEMENT

    -

    Field(s)

    +

    Type Movement

    + +

    Type MOVEMENT

    + +

    the MOVEMENT class

    + +

    Field(s)

    +
    +
    + + +MOVEMENT.AliveUnits + +
    +
    + + + +
    +
    @@ -164,6 +213,71 @@ on defined intervals (currently every minute).

    + +
    +
    +
    + + #number + +MOVEMENT.MoveCount + +
    +
    + + + + +

    The internal counter of the amount of Moveing the has happened since MoveStart.

    + +
    +
    +
    +
    + + + +MOVEMENT.MoveMaximum + +
    +
    + + + + +

    Contains the Maximum amount of units that are allowed to move...

    + +
    +
    +
    +
    + + + +MOVEMENT.MovePrefixes + +
    +
    + + + +
    +
    +
    +
    + + + +MOVEMENT.MoveUnits + +
    +
    + + + + +

    Reflects if the Moving for this MovePrefixes is going to be scheduled or not.

    +
    @@ -195,13 +309,13 @@ on defined intervals (currently every minute).

    - -MOVEMENT:OnBirth(Event) + +MOVEMENT:OnDeadOrCrash(Event)
    -

    Captures the birth events when new Units were spawned.

    +

    Captures the Dead or Crash events when Units crash or are destroyed.

    Parameter

    + + + + @@ -269,6 +276,12 @@ Using this object reference, you can then remove ALL the menus and submenus unde + + + + @@ -278,9 +291,27 @@ Using this object reference, you can then remove ALL the menus and submenus unde - + + + + + + + + + + + + +
    MOVEMENT.AliveUnits + +
    MOVEMENT.ClassName +
    MOVEMENT.MoveCount + +
    MOVEMENT.MoveMaximum + +
    MOVEMENT.MovePrefixes + +
    MOVEMENT.MoveUnits +
    MOVEMENT:New(MovePrefixes, MoveMaximum) -
    MOVEMENT:OnBirth(Event) -

    Captures the birth events when new Units were spawned.

    MOVEMENT:OnDeadOrCrash(Event)

    Captures the Dead or Crash events when Units crash or are destroyed.

    +
    MOVEMENT:OnEventBirth(self, EventData) +

    Captures the birth events when new Units were spawned.

    MENU_BASE.ClassName +
    MENU_BASE:GetMenu(MenuText) +

    Gets a Menu from a parent Menu

    MENU_BASE.MenuPath +
    MENU_BASE.MenuRemoveParent +
    MENU_BASE:New(MenuText, ParentMenu)MENU_BASE.MenuTime + +
    MENU_BASE.New(#, self, MenuText, ParentMenu)

    Consructor

    +
    MENU_BASE:SetRemoveParent(RemoveParent) +

    Sets a Menu to remove automatically the parent menu when the menu removed is the last child menu of that parent Menu.

    +
    MENU_BASE:SetTime(MenuTime) +

    Sets a time stamp for later prevention of menu removal.

    @@ -412,7 +443,7 @@ Using this object reference, you can then remove ALL the menus and submenus unde - MENU_COMMAND_BASE:New(MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments) + MENU_COMMAND_BASE.New(#, self, MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments)

    Constructor

    @@ -449,12 +480,6 @@ Using this object reference, you can then remove ALL the menus and submenus unde MENU_GROUP.MenuText - - - - MENU_GROUP.Menus - - @@ -476,13 +501,13 @@ Using this object reference, you can then remove ALL the menus and submenus unde - MENU_GROUP:Remove() + MENU_GROUP:Remove(MenuTime)

    Removes the main menu and sub menus recursively of this MENU_GROUP.

    - MENU_GROUP:RemoveSubMenus() + MENU_GROUP:RemoveSubMenus(MenuTime)

    Removes the sub menus recursively of this MENU_GROUP.

    @@ -540,7 +565,7 @@ Using this object reference, you can then remove ALL the menus and submenus unde - MENU_GROUP_COMMAND:Remove() + MENU_GROUP_COMMAND:Remove(MenuTime)

    Removes a menu structure for a group.

    @@ -757,6 +782,33 @@ Using this object reference, you can then remove ALL the menus and submenus unde + +
    +
    +
    + + +MENU_BASE:GetMenu(MenuText) + +
    +
    + +

    Gets a Menu from a parent Menu

    + +

    Parameter

    +
      +
    • + +

      #string MenuText : +The text of the child menu.

      + +
    • +
    +

    Return value

    + +

    #MENU_BASE:

    + +
    @@ -783,6 +835,20 @@ Using this object reference, you can then remove ALL the menus and submenus unde + +
    +
    +
    + + + +MENU_BASE.MenuRemoveParent + +
    +
    + + +
    @@ -797,13 +863,27 @@ Using this object reference, you can then remove ALL the menus and submenus unde + +
    +
    +
    + + + +MENU_BASE.MenuTime + +
    +
    + + +
    -MENU_BASE:New(MenuText, ParentMenu) +MENU_BASE.New(#, self, MenuText, ParentMenu)
    @@ -814,6 +894,17 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    • +

      # : +ENU_BASE

      + +
    • +
    • + +

      self :

      + +
    • +
    • +

      MenuText :

    • @@ -823,6 +914,64 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    +

    Return value

    + +

    #MENU_BASE:

    + + +
    +
    +
    +
    + + +MENU_BASE:SetRemoveParent(RemoveParent) + +
    +
    + +

    Sets a Menu to remove automatically the parent menu when the menu removed is the last child menu of that parent Menu.

    + +

    Parameter

    +
      +
    • + +

      #boolean RemoveParent : +If true, the parent menu is automatically removed when this menu is the last child menu of that parent Menu.

      + +
    • +
    +

    Return value

    + +

    #MENU_BASE:

    + + +
    +
    +
    +
    + + +MENU_BASE:SetTime(MenuTime) + +
    +
    + +

    Sets a time stamp for later prevention of menu removal.

    + +

    Parameter

    +
      +
    • + +

      MenuTime :

      + +
    • +
    +

    Return value

    + +

    #MENU_BASE:

    + +
    @@ -1200,8 +1349,8 @@ self

    Return value

    -

    #MENU_COALITION: -self

    +

    #MENU_COALITION:

    + @@ -1279,8 +1428,8 @@ An argument for the function. There can only be ONE argument given. So multiple

    Return value

    -

    #MENUCOALITIONCOMMAND: -self

    +

    #MENUCOALITIONCOMMAND:

    + @@ -1366,7 +1515,7 @@ self

    -MENU_COMMAND_BASE:New(MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments) +MENU_COMMAND_BASE.New(#, self, MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments)
    @@ -1377,6 +1526,17 @@ self

    • +

      # : +ENUCOMMANDBASE

      + +
    • +
    • + +

      self :

      + +
    • +
    • +

      MenuText :

    • @@ -1396,6 +1556,11 @@ self

    +

    Return value

    + +

    #MENUCOMMANDBASE:

    + +
    @@ -1523,20 +1688,6 @@ self

    - - -
    -
    - - - -MENU_GROUP.Menus - -
    -
    - - -
    @@ -1613,13 +1764,21 @@ self

    -MENU_GROUP:Remove() +MENU_GROUP:Remove(MenuTime)

    Removes the main menu and sub menus recursively of this MENU_GROUP.

    +

    Parameter

    +
      +
    • + +

      MenuTime :

      + +
    • +

    Return value

    #nil:

    @@ -1631,13 +1790,21 @@ self

    -MENU_GROUP:RemoveSubMenus() +MENU_GROUP:RemoveSubMenus(MenuTime)

    Removes the sub menus recursively of this MENU_GROUP.

    +

    Parameter

    +
      +
    • + +

      MenuTime :

      + +
    • +

    Return value

    #MENU_GROUP: @@ -1772,8 +1939,8 @@ An argument for the function.

    Return value

    -

    Menu#MENUGROUPCOMMAND: -self

    +

    #MENUGROUPCOMMAND:

    +
    @@ -1809,13 +1976,21 @@ self

    -MENU_GROUP_COMMAND:Remove() +MENU_GROUP_COMMAND:Remove(MenuTime)

    Removes a menu structure for a group.

    +

    Parameter

    +
      +
    • + +

      MenuTime :

      + +
    • +

    Return value

    #nil:

    @@ -1874,8 +2049,8 @@ The parent menu. This parameter can be ignored if you want the menu to be locate

    Return value

    -

    #MENU_MISSION: -self

    +

    #MENU_MISSION:

    +
    @@ -1913,8 +2088,8 @@ self

    Return value

    -

    #MENU_MISSION: -self

    +

    #MENU_MISSION:

    + diff --git a/docs/Documentation/Message.html b/docs/Documentation/Message.html index 2da7809e7..db044781f 100644 --- a/docs/Documentation/Message.html +++ b/docs/Documentation/Message.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/MissileTrainer.html b/docs/Documentation/MissileTrainer.html index b51e024ab..028f28dff 100644 --- a/docs/Documentation/MissileTrainer.html +++ b/docs/Documentation/MissileTrainer.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -297,6 +298,12 @@ Together with the 476 virtual team, we tested the MISSILETRAINE MISSILETRAINER:New(Distance, Briefing)

    Creates the main object which is handling missile tracking.

    + + + + MISSILETRAINER:OnEventShot(EventData, EVentData) + +

    Detects if an SA site was shot with an anti radiation missile.

    @@ -339,12 +346,6 @@ Together with the 476 virtual team, we tested the MISSILETRAINE MISSILETRAINER._Alive(Client, self) - - - - MISSILETRAINER:_EventShot(Event) - -

    Detects if an SA site was shot with an anti radiation missile.

    @@ -821,6 +822,35 @@ The distance in meters when a tracked missile needs to be destroyed when close t
    + +MISSILETRAINER:OnEventShot(EventData, EVentData) + +
    +
    + +

    Detects if an SA site was shot with an anti radiation missile.

    + + +

    In this case, take evasive actions based on the skill level set within the ME.

    + +

    Parameters

    + +
    +
    +
    +
    + MISSILETRAINER.TrackingFrequency @@ -954,30 +984,6 @@ The distance in meters when a tracked missile needs to be destroyed when close t
    - -MISSILETRAINER:_EventShot(Event) - -
    -
    - -

    Detects if an SA site was shot with an anti radiation missile.

    - - -

    In this case, take evasive actions based on the skill level set within the ME.

    - -

    Parameter

    - -
    -
    -
    -
    - MISSILETRAINER._MenuMessages(MenuParameters) diff --git a/docs/Documentation/Mission.html b/docs/Documentation/Mission.html index 1ad2c406d..bb21a844f 100644 --- a/docs/Documentation/Mission.html +++ b/docs/Documentation/Mission.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -84,18 +85,6 @@ A CLIENT needs to be registered within the MISSION - - - - MISSIONSCHEDULER - - - - - - _TransportExecuteStage - - @@ -105,18 +94,6 @@ A CLIENT needs to be registered within the MISSION:AbortUnit(PlayerUnit)

    Aborts a PlayerUnit from the Mission.

    - - - - MISSION.AddClient(CLIENT, self, Client) - -

    Register a new CLIENT to participate within the mission.

    - - - - MISSION.AddGoalFunction(function, self, GoalFunction) - -

    Add a goal function to a MISSION.

    @@ -138,15 +115,9 @@ A CLIENT needs to be registered within the MISSION:ClearMissionMenu() + MISSION:Complete() -

    Clears the mission menu for the coalition.

    - - - - MISSION:Completed() - -

    Set a Mission to completed.

    +

    Synchronous Event Trigger for Event Complete.

    @@ -156,21 +127,9 @@ A CLIENT needs to be registered within the MISSION.FAILED + MISSION:Fail() - - - - - MISSION:Failed() - -

    Set a Mission to failed.

    - - - - MISSION.FindClient(CLIENT, self, ClientName) - -

    Find a CLIENT object within the MISSION by its ClientName.

    +

    Synchronous Event Trigger for Event Fail.

    @@ -186,7 +145,7 @@ A CLIENT needs to be registered within the MISSION:GetMissionMenu(TaskGroup) + MISSION:GetMenu(TaskGroup)

    Gets the mission menu for the coalition.

    @@ -222,39 +181,39 @@ A CLIENT needs to be registered within the MISSION.GoalFunction + MISSION.HasGroup(#, self, TaskGroup) - - - - - MISSION:HasGroup(TaskGroup) - - +

    Validates if the Mission has a Group

    MISSION:IsCompleted() -

    Returns if a Mission has completed.

    +

    Is the Mission Completed.

    MISSION:IsFailed() -

    Returns if a Mission has failed.

    +

    Is the Mission Failed.

    + + + + MISSION:IsHold() + +

    Is the Mission Hold.

    + + + + MISSION:IsIdle() + +

    Is the Mission Idle.

    MISSION:IsOngoing() -

    Returns if a Mission is ongoing.

    - - - - MISSION:IsPending() - -

    Returns if a Mission is pending.

    +

    Is the Mission Ongoing.

    @@ -267,54 +226,18 @@ A CLIENT needs to be registered within the MISSION.MissionBriefing - - - - MISSION.MissionCoalition - - MISSION.MissionMenu - - - - MISSION.MissionProgressTrigger - - - - - - MISSION.MissionReportFlash - - - - - - MISSION.MissionReportShow - - - - - - MISSION.MissionReportTrigger - - MISSION.MissionStatus - - - - MISSION.MissionTimeInterval - - @@ -330,25 +253,103 @@ A CLIENT needs to be registered within the MISSION:Ongoing() + MISSION:OnAfterComplete(From, Event, To) -

    Set a Mission to ongoing.

    +

    OnAfter Transition Handler for Event Complete.

    - MISSION:Pending() + MISSION:OnAfterFail(From, Event, To) -

    Set a Mission to pending.

    +

    OnAfter Transition Handler for Event Fail.

    - MISSION.REPEAT + MISSION:OnAfterStart(From, Event, To) - +

    OnAfter Transition Handler for Event Start.

    - MISSION:RemoveMenu() + MISSION:OnAfterStop(From, Event, To) + +

    OnAfter Transition Handler for Event Stop.

    + + + + MISSION:OnBeforeComplete(From, Event, To) + +

    OnBefore Transition Handler for Event Complete.

    + + + + MISSION:OnBeforeFail(From, Event, To) + +

    OnBefore Transition Handler for Event Fail.

    + + + + MISSION:OnBeforeStart(From, Event, To) + +

    OnBefore Transition Handler for Event Start.

    + + + + MISSION:OnBeforeStop(From, Event, To) + +

    OnBefore Transition Handler for Event Stop.

    + + + + MISSION:OnEnterCompleted(From, Event, To) + +

    OnEnter Transition Handler for State Completed.

    + + + + MISSION:OnEnterFailed(From, Event, To) + +

    OnEnter Transition Handler for State Failed.

    + + + + MISSION:OnEnterIdle(From, Event, To) + +

    OnEnter Transition Handler for State Idle.

    + + + + MISSION:OnEnterOngoing(From, Event, To) + +

    OnEnter Transition Handler for State Ongoing.

    + + + + MISSION:OnLeaveCompleted(From, Event, To) + +

    OnLeave Transition Handler for State Completed.

    + + + + MISSION:OnLeaveFailed(From, Event, To) + +

    OnLeave Transition Handler for State Failed.

    + + + + MISSION:OnLeaveIdle(From, Event, To) + +

    OnLeave Transition Handler for State Idle.

    + + + + MISSION:OnLeaveOngoing(From, Event, To) + +

    OnLeave Transition Handler for State Ongoing.

    + + + + MISSION:RemoveMenu(MenuTime)

    Removes the Planned Task menu.

    @@ -381,18 +382,6 @@ A CLIENT needs to be registered within the MISSION:ReportSummary()

    Create a summary report of the Mission (one line).

    - - - - MISSION:ReportToAll() - -

    Report the status of all MISSIONs to all active Clients.

    - - - - MISSION.SUCCESS - - @@ -402,45 +391,21 @@ A CLIENT needs to be registered within the MISSION:SetAssignedMenu(Task, MenuText) - -

    Sets the Assigned Task menu.

    - - - - MISSION:SetMenu() + MISSION:SetMenu(MenuTime)

    Sets the Planned Task menu.

    - MISSION:StatusToClients() + MISSION:Start() -

    Send the status of the MISSION to all Clients.

    +

    Synchronous Event Trigger for Event Start.

    - MISSION.TaskCategoryMenus + MISSION:Stop() - - - - - MISSION.TaskMenus - - - - - - MISSION.TaskTypeMenus - - - - - - MISSION._ActiveTasks - - +

    Synchronous Event Trigger for Event Stop.

    @@ -450,137 +415,37 @@ A CLIENT needs to be registered within the MISSION._GoalTasks + MISSION:__Complete(Delay) + +

    Asynchronous Event Trigger for Event Complete.

    + + + + MISSION:__Fail(Delay) + +

    Asynchronous Event Trigger for Event Fail.

    + + + + MISSION:__Start(Delay) + +

    Asynchronous Event Trigger for Event Start.

    + + + + MISSION:__Stop(Delay) + +

    Asynchronous Event Trigger for Event Stop.

    + + + + MISSION:onbeforeComplete(From, Event, To) - MISSION:onbeforeComplete(Event, From, To) - -

    FSM function for a MISSION

    - - - - MISSION:onenterCompleted(Event, From, To) - -

    FSM function for a MISSION

    - - - - -

    Type MISSIONSCHEDULER

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -600,45 +465,6 @@ A CLIENT needs to be registered within the -
    - - #MISSIONSCHEDULER - -MISSIONSCHEDULER - -
    -
    - - - -
    - -
    -
    - - - -_TransportExecuteStage - -
    -
    - - - - -

    _TransportExecuteStage: Defines the different stages of Transport unload/load execution. This table is internal and is used to control the validity of Transport load/unload timing.

    - -
      -
    • _TransportExecuteStage.EXECUTING
    • -
    • _TransportExecuteStage.SUCCESS
    • -
    • _TransportExecuteStage.FAILED
    • -
    - -

    --

    -

    Type Mission

    @@ -683,120 +509,6 @@ true if Unit is part of a Task in the Mission.

    - -MISSION.AddClient(CLIENT, self, Client) - -
    -
    - -

    Register a new CLIENT to participate within the mission.

    - -

    Parameters

    -
      -
    • - -

      CLIENT : -Client is the CLIENT object. The object must have been instantiated with CLIENT.

      - -
    • -
    • - -

      self :

      - -
    • -
    • - -

      Client :

      - -
    • -
    -

    Return value

    - - -

    CLIENT

    - -

    Usage:

    -
    Add a number of Client objects to the Mission.
    -	Mission:AddClient( CLIENT:FindByName( 'US UH-1H*HOT-Deploy Troops 1', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() )
    -	Mission:AddClient( CLIENT:FindByName( 'US UH-1H*RAMP-Deploy Troops 3', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() )
    -	Mission:AddClient( CLIENT:FindByName( 'US UH-1H*HOT-Deploy Troops 2', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() )
    -	Mission:AddClient( CLIENT:FindByName( 'US UH-1H*RAMP-Deploy Troops 4', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() )
    - -
    -
    -
    -
    - - -MISSION.AddGoalFunction(function, self, GoalFunction) - -
    -
    - -

    Add a goal function to a MISSION.

    - - -

    Goal functions are called when a TASK within a mission has been completed.

    - -

    Parameters

    -
      -
    • - -

      function : -GoalFunction is the function defined by the mission designer to evaluate whether a certain goal has been reached after a TASK finishes within the MISSION. A GoalFunction must accept 2 parameters: Mission, Client, which contains the current MISSION object and the current CLIENT object respectively.

      - -
    • -
    • - -

      self :

      - -
    • -
    • - -

      GoalFunction :

      - -
    • -
    -

    Usage:

    -
     PatriotActivation = { 
    -		{ "US SAM Patriot Zerti", false },
    -		{ "US SAM Patriot Zegduleti", false },
    -		{ "US SAM Patriot Gvleti", false }
    -	}
    -
    -	function DeployPatriotTroopsGoal( Mission, Client )
    -
    -
    -		-- Check if the cargo is all deployed for mission success.
    -		for CargoID, CargoData in pairs( Mission._Cargos ) do
    -			if Group.getByName( CargoData.CargoGroupName ) then
    -				CargoGroup = Group.getByName( CargoData.CargoGroupName )
    -				if CargoGroup then
    -					-- Check if the cargo is ready to activate
    -					CurrentLandingZoneID = routines.IsUnitInZones( CargoGroup:getUnits()[1], Mission:GetTask( 2 ).LandingZones ) -- The second task is the Deploytask to measure mission success upon
    -					if CurrentLandingZoneID then
    -						if PatriotActivation[CurrentLandingZoneID][2] == false then
    -							-- Now check if this is a new Mission Task to be completed...
    -							trigger.action.setGroupAIOn( Group.getByName( PatriotActivation[CurrentLandingZoneID][1] ) )
    -							PatriotActivation[CurrentLandingZoneID][2] = true
    -							MessageToBlue( "Mission Command: Message to all airborne units! The " .. PatriotActivation[CurrentLandingZoneID][1] .. " is armed. Our air defenses are now stronger.", 60, "BLUE/PatriotDefense" )
    -							MessageToRed( "Mission Command: Our satellite systems are detecting additional NATO air defenses. To all airborne units: Take care!!!", 60, "RED/PatriotDefense" )
    -							Mission:GetTask( 2 ):AddGoalCompletion( "Patriots activated", PatriotActivation[CurrentLandingZoneID][1], 1 ) -- Register Patriot activation as part of mission goal.
    -						end
    -					end
    -				end
    -			end
    -		end
    -	end
    -
    -	local Mission = MISSIONSCHEDULER.AddMission( 'NATO Transport Troops', 'Operational', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.', 'NATO' )
    -	Mission:AddGoalFunction( DeployPatriotTroopsGoal )
    - -
    -
    -
    -
    - MISSION:AddScoring(Scoring) @@ -868,31 +580,13 @@ The task added.

    - -MISSION:ClearMissionMenu() + +MISSION:Complete()
    -

    Clears the mission menu for the coalition.

    - -

    Return value

    - -

    #MISSION: -self

    - -
    -
    -
    -
    - - -MISSION:Completed() - -
    -
    - -

    Set a Mission to completed.

    +

    Synchronous Event Trigger for Event Complete.

    @@ -931,68 +625,13 @@ true if Unit is part of a Task in the Mission.

    - #number - -MISSION.FAILED + +MISSION:Fail()
    - - -
    -
    -
    -
    - - -MISSION:Failed() - -
    -
    - -

    Set a Mission to failed.

    - -
    -
    -
    -
    - - -MISSION.FindClient(CLIENT, self, ClientName) - -
    -
    - -

    Find a CLIENT object within the MISSION by its ClientName.

    - -

    Parameters

    -
      -
    • - -

      CLIENT : -ClientName is a string defining the Client Group as defined within the ME.

      - -
    • -
    • - -

      self :

      - -
    • -
    • - -

      ClientName :

      - -
    • -
    -

    Return value

    - - -

    CLIENT

    - -

    Usage:

    -
    -- Seach for Client "Bomber" within the Mission.
    -local BomberClient = Mission:FindClient( "Bomber" )
    +

    Synchronous Event Trigger for Event Fail.

    @@ -1035,8 +674,8 @@ local BomberClient = Mission:FindClient( "Bomber" )
    - -MISSION:GetMissionMenu(TaskGroup) + +MISSION:GetMenu(TaskGroup)
    @@ -1190,40 +829,43 @@ Returns nil if no task was found.

    Tasks = Mission:GetTasks() env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" ) -
    -
    -
    -
    - - -MISSION.GoalFunction - -
    -
    - - -
    -MISSION:HasGroup(TaskGroup) +MISSION.HasGroup(#, self, TaskGroup)
    +

    Validates if the Mission has a Group

    - -

    Parameter

    +

    Parameters

    • +

      # : +ISSION

      + +
    • +
    • + +

      self :

      + +
    • +
    • +

      TaskGroup :

    +

    Return value

    + +

    #boolean: +true if the Mission has a Group.

    +
    @@ -1235,12 +877,12 @@ env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" )
    -

    Returns if a Mission has completed.

    +

    Is the Mission Completed.

    Return value

    +

    #boolean:

    -

    bool

    @@ -1253,10 +895,48 @@ env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" )
    -

    Returns if a Mission has failed.

    +

    Is the Mission Failed.

    +

    Return value

    + +

    #boolean:

    + + +
    + +
    +
    + + +MISSION:IsHold() + +
    +
    -

    treturn bool

    +

    Is the Mission Hold.

    + +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +MISSION:IsIdle() + +
    +
    + +

    Is the Mission Idle.

    + +

    Return value

    + +

    #boolean:

    +
    @@ -1269,26 +949,12 @@ env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" )
    -

    Returns if a Mission is ongoing.

    +

    Is the Mission Ongoing.

    - -

    treturn bool

    +

    Return value

    + +

    #boolean:

    -
    - -
    -
    - - -MISSION:IsPending() - -
    -
    - -

    Returns if a Mission is pending.

    - - -

    treturn bool

    @@ -1342,20 +1008,6 @@ true if Unit is part of a Task in the Mission.

    - - -
    -
    - - #string - -MISSION.MissionCoalition - -
    -
    - - -
    @@ -1370,62 +1022,6 @@ true if Unit is part of a Task in the Mission.

    - -
    -
    -
    - - #number - -MISSION.MissionProgressTrigger - -
    -
    - - - -
    -
    -
    -
    - - #boolean - -MISSION.MissionReportFlash - -
    -
    - - - -
    -
    -
    -
    - - #boolean - -MISSION.MissionReportShow - -
    -
    - - - -
    -
    -
    -
    - - #number - -MISSION.MissionReportTrigger - -
    -
    - - -
    @@ -1440,20 +1036,6 @@ true if Unit is part of a Task in the Mission.

    - -
    -
    -
    - - #number - -MISSION.MissionTimeInterval - -
    -
    - - -
    @@ -1526,40 +1108,584 @@ self

    - -MISSION:Ongoing() + +MISSION:OnAfterComplete(From, Event, To)
    -

    Set a Mission to ongoing.

    +

    OnAfter Transition Handler for Event Complete.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnAfterFail(From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Fail.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnAfterStart(From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Start.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnAfterStop(From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Stop.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnBeforeComplete(From, Event, To) + +
    +
    + +

    OnBefore Transition Handler for Event Complete.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    - -MISSION:Pending() + +MISSION:OnBeforeFail(From, Event, To)
    -

    Set a Mission to pending.

    +

    OnBefore Transition Handler for Event Fail.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    - #number - -MISSION.REPEAT + +MISSION:OnBeforeStart(From, Event, To)
    +

    OnBefore Transition Handler for Event Start.

    +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +MISSION:OnBeforeStop(From, Event, To) + +
    +
    + +

    OnBefore Transition Handler for Event Stop.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +MISSION:OnEnterCompleted(From, Event, To) + +
    +
    + +

    OnEnter Transition Handler for State Completed.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnEnterFailed(From, Event, To) + +
    +
    + +

    OnEnter Transition Handler for State Failed.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnEnterIdle(From, Event, To) + +
    +
    + +

    OnEnter Transition Handler for State Idle.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnEnterOngoing(From, Event, To) + +
    +
    + +

    OnEnter Transition Handler for State Ongoing.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnLeaveCompleted(From, Event, To) + +
    +
    + +

    OnLeave Transition Handler for State Completed.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +MISSION:OnLeaveFailed(From, Event, To) + +
    +
    + +

    OnLeave Transition Handler for State Failed.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +MISSION:OnLeaveIdle(From, Event, To) + +
    +
    + +

    OnLeave Transition Handler for State Idle.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +MISSION:OnLeaveOngoing(From, Event, To) + +
    +
    + +

    OnLeave Transition Handler for State Ongoing.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    @@ -1567,13 +1693,21 @@ self

    -MISSION:RemoveMenu() +MISSION:RemoveMenu(MenuTime)

    Removes the Planned Task menu.

    +

    Parameter

    +
      +
    • + +

      #number MenuTime :

      + +
    • +
    @@ -1685,33 +1819,6 @@ self

    #string:

    - -
    -
    -
    - - -MISSION:ReportToAll() - -
    -
    - -

    Report the status of all MISSIONs to all active Clients.

    - -
    -
    -
    -
    - - #number - -MISSION.SUCCESS - -
    -
    - - -
    @@ -1726,119 +1833,52 @@ self

    - -
    -
    -
    - - -MISSION:SetAssignedMenu(Task, MenuText) - -
    -
    - -

    Sets the Assigned Task menu.

    - -

    Parameters

    - -

    Return value

    - -

    #MISSION: -self

    -
    -MISSION:SetMenu() +MISSION:SetMenu(MenuTime)

    Sets the Planned Task menu.

    +

    Parameter

    +
      +
    • + +

      #number MenuTime :

      + +
    • +
    - -MISSION:StatusToClients() + +MISSION:Start()
    -

    Send the status of the MISSION to all Clients.

    +

    Synchronous Event Trigger for Event Start.

    - - -MISSION.TaskCategoryMenus + +MISSION:Stop()
    - - -
    -
    -
    -
    - - - -MISSION.TaskMenus - -
    -
    - - - -
    -
    -
    -
    - - - -MISSION.TaskTypeMenus - -
    -
    - - - -
    -
    -
    -
    - - - -MISSION._ActiveTasks - -
    -
    - - +

    Synchronous Event Trigger for Event Stop.

    @@ -1859,43 +1899,124 @@ self

    - - -MISSION._GoalTasks + +MISSION:__Complete(Delay)
    +

    Asynchronous Event Trigger for Event Complete.

    +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      +
    • +
    +
    +
    +
    +
    + + +MISSION:__Fail(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Fail.

    + +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:__Start(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Start.

    + +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:__Stop(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Stop.

    + +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    -MISSION:onbeforeComplete(Event, From, To) +MISSION:onbeforeComplete(From, Event, To)
    -

    FSM function for a MISSION

    + + + +

    FSM function for a MISSION + @param #MISSION self + @param #string From + @param #string Event + @param #string To

    Parameters

    • -

      #string Event :

      +

      From :

    • -

      #string From :

      +

      Event :

    • -

      #string To :

      +

      To :

    @@ -1905,28 +2026,35 @@ self

    -MISSION:onenterCompleted(Event, From, To) +MISSION:onenterCompleted(From, Event, To)
    -

    FSM function for a MISSION

    + + + +

    FSM function for a MISSION + @param #MISSION self + @param #string From + @param #string Event + @param #string To

    Parameters

    • -

      #string Event :

      +

      From :

    • -

      #string From :

      +

      Event :

    • -

      #string To :

      +

      To :

    @@ -1935,402 +2063,6 @@ self

    Type MISSION.Clients

    -

    Type MISSIONSCHEDULER

    - -

    The MISSIONSCHEDULER is an OBJECT and is the main scheduler of ALL active MISSIONs registered within this scheduler.

    - - -

    It's workings are considered internal and is automatically created when the Mission.lua file is included.

    - -

    Field(s)

    -
    -
    - - -MISSIONSCHEDULER.AddMission(Mission) - -
    -
    - -

    This is the main MISSION declaration method.

    - - -

    Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc.

    - -

    Parameter

    -
      -
    • - -

      Mission : -is the MISSION object instantiated by MISSION.

      - -
    • -
    -

    Return value

    - - -

    MISSION

    - -

    Usage:

    -
    
    --- Declare a mission.
    -Mission = MISSION:New( 'Russia Transport Troops SA-6', 
    -                       'Operational', 
    -                       'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 
    -                       'Russia' )
    -MISSIONSCHEDULER:AddMission( Mission )
    - -
    -
    -
    -
    - - -MISSIONSCHEDULER.FindMission(MissionName) - -
    -
    - -

    Find a MISSION within the MISSIONSCHEDULER.

    - -

    Parameter

    -
      -
    • - -

      MissionName : -is the name of the MISSION given at declaration using AddMission.

      - -
    • -
    -

    Return value

    - - -

    MISSION

    - -

    Usage:

    -
    -- Declare a mission.
    -Mission = MISSION:New( 'Russia Transport Troops SA-6', 
    -                       'Operational', 
    -                       'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 
    -                       'Russia' )
    -MISSIONSCHEDULER:AddMission( Mission )
    -
    --- Now find the Mission.
    -MissionFind = MISSIONSCHEDULER:FindMission( 'Russia Transport Troops SA-6' )
    - -
    -
    -
    -
    - - #number - -MISSIONSCHEDULER.MissionCount - -
    -
    - - - -
    -
    -
    -
    - - #MISSIONSCHEDULER.MISSIONS - -MISSIONSCHEDULER.Missions - -
    -
    - - - -
    -
    -
    -
    - - -MISSIONSCHEDULER.RemoveMission(MissionName) - -
    -
    - -

    Remove a MISSION from the MISSIONSCHEDULER.

    - -

    Parameter

    -
      -
    • - -

      MissionName : -is the name of the MISSION given at declaration using AddMission.

      - -
    • -
    -

    Usage:

    -
    -- Declare a mission.
    -Mission = MISSION:New( 'Russia Transport Troops SA-6', 
    -                       'Operational', 
    -                       'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 
    -                       'Russia' )
    -MISSIONSCHEDULER:AddMission( Mission )
    -
    --- Now remove the Mission.
    -MISSIONSCHEDULER:RemoveMission( 'Russia Transport Troops SA-6' )
    - -
    -
    -
    -
    - - -MISSIONSCHEDULER.ReportMenu() - -
    -
    - -

    Enables a MENU option in the communications menu under F10 to control the status of the active missions.

    - - -

    This function should be called only once when starting the MISSIONSCHEDULER.

    - -
    -
    -
    -
    - - -MISSIONSCHEDULER.ReportMissionsFlash(TimeInterval) - -
    -
    - - - - -

    Internal function used by the MISSIONSCHEDULER menu.

    - -

    Parameter

    -
      -
    • - -

      TimeInterval :

      - -
    • -
    -
    -
    -
    -
    - - -MISSIONSCHEDULER.ReportMissionsHide(Prm) - -
    -
    - - - - -

    Internal function used by the MISSIONSCHEDULER menu.

    - -

    Parameter

    -
      -
    • - -

      Prm :

      - -
    • -
    -
    -
    -
    -
    - - -MISSIONSCHEDULER.ReportMissionsShow() - -
    -
    - - - - -

    Internal function used by the MISSIONSCHEDULER menu.

    - -
    -
    -
    -
    - - -MISSIONSCHEDULER.Scheduler() - -
    -
    - -

    This is the main MISSIONSCHEDULER Scheduler function.

    - - -

    It is considered internal and is automatically created when the Mission.lua file is included.

    - -
    -
    -
    -
    - - - -MISSIONSCHEDULER.SchedulerId - -
    -
    - - - - -

    MISSIONSCHEDULER.SchedulerId = routines.scheduleFunction( MISSIONSCHEDULER.Scheduler, { }, 0, 2 )

    - -
    -
    -
    -
    - - -MISSIONSCHEDULER:Scoring(Scoring) - -
    -
    - -

    Adds a mission scoring to the game.

    - -

    Parameter

    -
      -
    • - -

      Scoring :

      - -
    • -
    -
    -
    -
    -
    - - -MISSIONSCHEDULER.Start() - -
    -
    - -

    Start the MISSIONSCHEDULER.

    - -
    -
    -
    -
    - - -MISSIONSCHEDULER.Stop() - -
    -
    - -

    Stop the MISSIONSCHEDULER.

    - -
    -
    -
    -
    - - -MISSIONSCHEDULER:Time(TimeSeconds, TimeIntervalShow, TimeShow) - -
    -
    - - - -

    Parameters

    -
      -
    • - -

      TimeSeconds :

      - -
    • -
    • - -

      TimeIntervalShow :

      - -
    • -
    • - -

      TimeShow :

      - -
    • -
    -
    -
    -
    -
    - - #number - -MISSIONSCHEDULER.TimeIntervalCount - -
    -
    - - - -
    -
    -
    -
    - - #number - -MISSIONSCHEDULER.TimeIntervalShow - -
    -
    - - - -
    -
    -
    -
    - - #number - -MISSIONSCHEDULER.TimeSeconds - -
    -
    - - - -
    -
    -
    -
    - - #number - -MISSIONSCHEDULER.TimeShow - -
    -
    - - - -
    -
    - -

    Type MISSIONSCHEDULER.MISSIONS

    -

    Type SCORING

    diff --git a/docs/Documentation/Object.html b/docs/Documentation/Object.html index 6be6e7aa6..cb8608218 100644 --- a/docs/Documentation/Object.html +++ b/docs/Documentation/Object.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Point.html b/docs/Documentation/Point.html index b5a28e4a9..2f03970b6 100644 --- a/docs/Documentation/Point.html +++ b/docs/Documentation/Point.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -1346,7 +1347,6 @@ The new calculated POINT_VEC2.

    - POINT_VEC2.z diff --git a/docs/Documentation/Positionable.html b/docs/Documentation/Positionable.html index 0aeaabf15..6ac05da3e 100644 --- a/docs/Documentation/Positionable.html +++ b/docs/Documentation/Positionable.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -164,6 +165,12 @@
    + + + + @@ -491,6 +498,27 @@ The POSITIONABLE is not existing or alive.

    + +POSITIONABLE:GetRadio() + +
    +
    + +

    Create a Radio#RADIO, to allow radio transmission for this POSITIONABLE.

    + + +

    Set parameters with the methods provided, then use RADIO:Broadcast() to actually broadcast the message

    + +

    Return value

    + +

    #RADIO: +Radio

    + +
    +
    +
    +
    + POSITIONABLE:GetRandomVec3(Radius) @@ -1014,6 +1042,8 @@ self

    +

    Type RADIO

    + diff --git a/docs/Documentation/Process_JTAC.html b/docs/Documentation/Process_JTAC.html index 16ebd156c..92c909e8f 100644 --- a/docs/Documentation/Process_JTAC.html +++ b/docs/Documentation/Process_JTAC.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Process_Pickup.html b/docs/Documentation/Process_Pickup.html index c39875791..a031bed16 100644 --- a/docs/Documentation/Process_Pickup.html +++ b/docs/Documentation/Process_Pickup.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Radio.html b/docs/Documentation/Radio.html new file mode 100644 index 000000000..3b47dc1f1 --- /dev/null +++ b/docs/Documentation/Radio.html @@ -0,0 +1,821 @@ + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +

    Module Radio

    + +

    Core - The RADIO class is responsible for transmitting radio communications.

    + + + +

    --- bitmap

    + +
    + +

    What are radio communications in DCS ?

    + +
      +
    • Radio transmissions consist of sound files that are broadcasted on a specific frequency (e.g. 115MHz) and modulation (e.g. AM),
    • +
    • They can be subtitled for a specific duration, the power in Watts of the transmiter's antenna can be set, and the transmission can be looped.
    • +
    + +

    How to supply DCS my own Sound Files ?

    + +
      +
    • Your sound files need to be encoded in .ogg or .wav,
    • +
    • Your sound files should be as tiny as possible. It is suggested you encode in .ogg with low bitrate and sampling settings,
    • +
    • They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file),
    • +
    • For simplicty sake, you can let DCS' Mission Editor add the file itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission.
    • +
    + +

    Due to weird DCS quirks, radio communications behave differently if sent by a Unit#UNIT or a Group#GROUP or by any other Positionable#POSITIONABLE

    + + + +

    Note that obviously, the frequency and the modulation of the transmission are important only if the players are piloting an Advanced System Modelling enabled aircraft, +like the A10C or the Mirage 2000C. They will hear the transmission if they are tuned on the right frequency and modulation (and if they are close enough - more on that below). +If a FC3 airacraft is used, it will hear every communication, whatever the frequency and the modulation is set to.

    + +
    + +

    Authors: Hugues "Grey_Echo" Bousquet

    + + +

    Global(s)

    +
    MISSIONSCHEDULER.AddMission(Mission) -

    This is the main MISSION declaration method.

    -
    MISSIONSCHEDULER.FindMission(MissionName) -

    Find a MISSION within the MISSIONSCHEDULER.

    -
    MISSIONSCHEDULER.MissionCount - -
    MISSIONSCHEDULER.Missions - -
    MISSIONSCHEDULER.RemoveMission(MissionName) -

    Remove a MISSION from the MISSIONSCHEDULER.

    -
    MISSIONSCHEDULER.ReportMenu() -

    Enables a MENU option in the communications menu under F10 to control the status of the active missions.

    -
    MISSIONSCHEDULER.ReportMissionsFlash(TimeInterval) - -
    MISSIONSCHEDULER.ReportMissionsHide(Prm) - -
    MISSIONSCHEDULER.ReportMissionsShow() - -
    MISSIONSCHEDULER.Scheduler() -

    This is the main MISSIONSCHEDULER Scheduler function.

    -
    MISSIONSCHEDULER.SchedulerId - -
    MISSIONSCHEDULER:Scoring(Scoring) -

    Adds a mission scoring to the game.

    -
    MISSIONSCHEDULER.Start() -

    Start the MISSIONSCHEDULER.

    -
    MISSIONSCHEDULER.Stop() -

    Stop the MISSIONSCHEDULER.

    -
    MISSIONSCHEDULER:Time(TimeSeconds, TimeIntervalShow, TimeShow) - -
    MISSIONSCHEDULER.TimeIntervalCount - -
    MISSIONSCHEDULER.TimeIntervalShow - -
    MISSIONSCHEDULER.TimeSeconds - -
    MISSIONSCHEDULER.TimeShowMISSION:onenterCompleted(From, Event, To) POSITIONABLE:GetPositionVec3()

    Returns the DCSTypes#Position3 position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission.

    +
    POSITIONABLE:GetRadio() +

    Create a Radio#RADIO, to allow radio transmission for this POSITIONABLE.

    + + + + +
    RADIO + +
    +

    Type RADIO

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    RADIO:Broadcast() +

    Actually Broadcast the transmission

    +
    RADIO.ClassName + +
    RADIO.FileName +

    Name of the sound file

    +
    RADIO.Frequency +

    Frequency of the transmission in Hz

    +
    RADIO.Loop + +
    RADIO.Modulation +

    Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM)

    +
    RADIO:New(Positionable) +

    Create a new RADIO Object.

    +
    RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power) +

    Create a new transmission, that is to say, populate the RADIO with relevant data

    +
    RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop) +

    Create a new transmission, that is to say, populate the RADIO with relevant data

    +
    RADIO.Positionable +

    The transmiter

    +
    RADIO.Power +

    Power of the antenna is Watts

    +
    RADIO:SetFileName(FileName) +

    Check validity of the filename passed and sets RADIO.FileName

    +
    RADIO:SetFrequency(Frequency) +

    Check validity of the frequency passed and sets RADIO.Frequency

    +
    RADIO:SetLoop(Loop) +

    Check validity of the loop passed and sets RADIO.Loop

    +
    RADIO:SetModulation(Modulation) +

    Check validity of the frequency passed and sets RADIO.Modulation

    +
    RADIO:SetPower(Power) +

    Check validity of the power passed and sets RADIO.Power

    +
    RADIO:SetSubtitle(Subtitle, SubtitleDuration) +

    Check validity of the subtitle and the subtitleDuration passed and sets RADIO.subtitle and RADIO.subtitleDuration

    +
    RADIO:StopBroadcast() +

    Stops a transmission

    +
    RADIO.Subtitle +

    Subtitle of the transmission

    +
    RADIO.SubtitleDuration +

    Duration of the Subtitle in seconds

    +
    + +

    Global(s)

    +
    +
    + + #RADIO + +RADIO + +
    +
    + + + +
    +
    +

    Type Radio

    + +

    Type RADIO

    + +

    1) RADIO class, extends Base#BASE

    + +

    1.1) RADIO usage

    + +

    There are 3 steps to a successful radio transmission.

    + + + + + +

    Methods to set relevant parameters for both a Unit#UNIT or a Group#GROUP or any other Positionable#POSITIONABLE

    + + + +

    Additional Methods to set relevant parameters if the transmiter is a Unit#UNIT or a Group#GROUP

    + + + +

    Additional Methods to set relevant parameters if the transmiter is any other Wrapper.Positionable#POSITIONABLE

    + + + +

    What is this power thing ?

    + +
      +
    • If your transmission is sent by a Positionable#POSITIONABLE other than a Unit#UNIT or a Group#GROUP, you can set the power of the antenna,
    • +
    • Otherwise, DCS sets it automatically, depending on what's available on your Unit,
    • +
    • If the player gets too far from the transmiter, or if the antenna is too weak, the transmission will fade and become noisyer,
    • +
    • This an automated DCS calculation you have no say on,
    • +
    • For reference, a standard VOR station has a 100W antenna, a standard AA TACAN has a 120W antenna, and civilian ATC's antenna usually range between 300 and 500W,
    • +
    • Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission. +
    • +
    + +

    Field(s)

    +
    +
    + + +RADIO:Broadcast() + +
    +
    + +

    Actually Broadcast the transmission

    + +

    Return value

    + +

    #RADIO: +self

    + +

    Usage:

    +
    -- The Radio has to be populated with the new transmission before broadcasting.
    +-- Please use RADIO setters or either @{Radio#RADIO.NewGenericTransmission} or @{Radio#RADIO.NewUnitTransmission}
    +-- This class is in fact pretty smart, it determines the right DCS function to use depending on the type of POSITIONABLE
    +-- If the POSITIONABLE is not a UNIT or a GROUP, we use the generic (but limited) trigger.action.radioTransmission()
    +-- If the POSITIONABLE is a UNIT or a GROUP, we use the "TransmitMessage" Command
    +-- If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored.
    +-- If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration and Loop are ignored
    + +
    +
    +
    +
    + + #string + +RADIO.ClassName + +
    +
    + + + +
    +
    +
    +
    + + #string + +RADIO.FileName + +
    +
    + +

    Name of the sound file

    + +
    +
    +
    +
    + + #number + +RADIO.Frequency + +
    +
    + +

    Frequency of the transmission in Hz

    + +
    +
    +
    +
    + + #boolean + +RADIO.Loop + +
    +
    + + + +
    +
    +
    +
    + + #number + +RADIO.Modulation + +
    +
    + +

    Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM)

    + +
    +
    +
    +
    + + +RADIO:New(Positionable) + +
    +
    + +

    Create a new RADIO Object.

    + + +

    This doesn't broadcast a transmission, though, use RADIO.Broadcast to actually broadcast

    + +

    Parameter

    + +

    Return values

    +
      +
    1. + +

      #RADIO: +Radio

      + +
    2. +
    3. + +

      #nil: +If Positionable is invalid

      + +
    4. +
    +

    Usage:

    +
    -- If you want to create a RADIO, you probably should use @{Positionable#POSITIONABLE.GetRadio}() instead
    + +
    +
    +
    +
    + + +RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power) + +
    +
    + +

    Create a new transmission, that is to say, populate the RADIO with relevant data

    + +

    Parameters

    +
      +
    • + +

      #string FileName :

      + +
    • +
    • + +

      #number Frequency : +in MHz

      + +
    • +
    • + +

      #number Modulation : +either radio.modulation.AM or radio.modulation.FM

      + +
    • +
    • + +

      #number Power : +in W

      + +
    • +
    +

    Return value

    + +

    #RADIO: +self

    + +

    Usage:

    +
    -- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP,
    +but it will work with a UNIT or a GROUP anyway
    +-- Only the RADIO and the Filename are mandatory
    + +
    +
    +
    +
    + + +RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop) + +
    +
    + +

    Create a new transmission, that is to say, populate the RADIO with relevant data

    + +

    Parameters

    +
      +
    • + +

      #string FileName :

      + +
    • +
    • + +

      #string Subtitle :

      + +
    • +
    • + +

      #number SubtitleDuration : +in s

      + +
    • +
    • + +

      #number Frequency : +in MHz

      + +
    • +
    • + +

      #number Modulation : +either radio.modulation.AM or radio.modulation.FM

      + +
    • +
    • + +

      #boolean Loop :

      + +
    • +
    +

    Return value

    + +

    #RADIO: +self

    + +

    Usage:

    +
    -- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP,
    +but it will work for any POSITIONABLE
    +-- Only the RADIO and the Filename are mandatory
    + +
    +
    +
    +
    + + Wrapper.Positionable#POSITIONABLE + +RADIO.Positionable + +
    +
    + +

    The transmiter

    + +
    +
    +
    +
    + + #number + +RADIO.Power + +
    +
    + +

    Power of the antenna is Watts

    + +
    +
    +
    +
    + + +RADIO:SetFileName(FileName) + +
    +
    + +

    Check validity of the filename passed and sets RADIO.FileName

    + +

    Parameter

    +
      +
    • + +

      #string FileName : +File name of the sound file (i.e. "Noise.ogg")

      + +
    • +
    +

    Return value

    + +

    #RADIO: +self

    + +
    +
    +
    +
    + + +RADIO:SetFrequency(Frequency) + +
    +
    + +

    Check validity of the frequency passed and sets RADIO.Frequency

    + +

    Parameter

    +
      +
    • + +

      #number Frequency : +in MHz (Ranges allowed for radio transmissions in DCS : 30-88 / 108-152 / 225-400MHz)

      + +
    • +
    +

    Return value

    + +

    #RADIO: +self

    + +
    +
    +
    +
    + + +RADIO:SetLoop(Loop) + +
    +
    + +

    Check validity of the loop passed and sets RADIO.Loop

    + +

    Parameter

    +
      +
    • + +

      #boolean Loop :

      + +
    • +
    +

    Return value

    + +

    #RADIO: +self

    + +

    Usage:

    +
    + +
    +
    +
    +
    + + +RADIO:SetModulation(Modulation) + +
    +
    + +

    Check validity of the frequency passed and sets RADIO.Modulation

    + +

    Parameter

    +
      +
    • + +

      #number Modulation : +either radio.modulation.AM or radio.modulation.FM

      + +
    • +
    +

    Return value

    + +

    #RADIO: +self

    + +
    +
    +
    +
    + + +RADIO:SetPower(Power) + +
    +
    + +

    Check validity of the power passed and sets RADIO.Power

    + +

    Parameter

    +
      +
    • + +

      #number Power : +in W

      + +
    • +
    +

    Return value

    + +

    #RADIO: +self

    + +
    +
    +
    +
    + + +RADIO:SetSubtitle(Subtitle, SubtitleDuration) + +
    +
    + +

    Check validity of the subtitle and the subtitleDuration passed and sets RADIO.subtitle and RADIO.subtitleDuration

    + +

    Parameters

    +
      +
    • + +

      #string Subtitle :

      + +
    • +
    • + +

      #number SubtitleDuration : +in s

      + +
    • +
    +

    Return value

    + +

    #RADIO: +self

    + +

    Usage:

    +
    -- Both parameters are mandatory, since it wouldn't make much sense to change the Subtitle and not its duration
    + +
    +
    +
    +
    + + +RADIO:StopBroadcast() + +
    +
    + +

    Stops a transmission

    + +

    Return value

    + +

    #RADIO: +self

    + +

    Usage:

    +
    -- Especially usefull to stop the broadcast of looped transmissions
    +-- Only works with broadcasts from UNIT or GROUP
    + +
    +
    +
    +
    + + #string + +RADIO.Subtitle + +
    +
    + +

    Subtitle of the transmission

    + +
    +
    +
    +
    + + #number + +RADIO.SubtitleDuration + +
    +
    + +

    Duration of the Subtitle in seconds

    + +
    +
    + + + + + + diff --git a/docs/Documentation/Route.html b/docs/Documentation/Route.html index 3425dc182..8d2ec52ea 100644 --- a/docs/Documentation/Route.html +++ b/docs/Documentation/Route.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -164,6 +165,12 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil ACT_ROUTE + + + + ACT_ROUTE_POINT + + @@ -206,7 +213,7 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil - ACT_ROUTE.TargetZone + ACT_ROUTE.Zone @@ -227,6 +234,106 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil ACT_ROUTE:onfuncHasArrived(ProcessUnit)

    Check if the controllable has arrived.

    + + + + +

    Type ACT_ROUTE_POINT

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ACT_ROUTE_POINT.ClassName + +
    ACT_ROUTE_POINT.DisplayCount + +
    ACT_ROUTE_POINT.DisplayInterval + +
    ACT_ROUTE_POINT.DisplayMessage + +
    ACT_ROUTE_POINT.DisplayTime + +
    ACT_ROUTE_POINT:GetPointVec2() +

    Get PointVec2

    +
    ACT_ROUTE_POINT:GetRange() +

    Get Range around PointVec2

    +
    ACT_ROUTE_POINT:Init(FsmRoute) + +
    ACT_ROUTE_POINT:New(The, Range, Zone, PointVec2) +

    Creates a new routing state machine.

    +
    ACT_ROUTE_POINT.PointVec2 + +
    ACT_ROUTE_POINT.Range + +
    ACT_ROUTE_POINT:SetPointVec2(PointVec2) +

    Set PointVec2

    +
    ACT_ROUTE_POINT:SetRange(Range) +

    Set Range around PointVec2

    +
    ACT_ROUTE_POINT.TASK + +
    ACT_ROUTE_POINT:onenterReporting(ProcessUnit, Event, From, To) +

    StateMachine callback function

    +
    ACT_ROUTE_POINT:onfuncHasArrived(ProcessUnit) +

    Method override to check if the controllable has arrived.

    @@ -261,6 +368,12 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil ACT_ROUTE_ZONE.DisplayTime + + + + ACT_ROUTE_ZONE:GetZone() + +

    Get Zone

    @@ -270,7 +383,7 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil - ACT_ROUTE_ZONE:New(TargetZone) + ACT_ROUTE_ZONE:New(Zone)

    Creates a new routing state machine.

    @@ -279,6 +392,12 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil ACT_ROUTE_ZONE.ProcessUnit + + + + ACT_ROUTE_ZONE:SetZone(Zone) + +

    Set Zone

    @@ -288,7 +407,7 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil - ACT_ROUTE_ZONE.TargetZone + ACT_ROUTE_ZONE.Zone @@ -320,6 +439,20 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil + +
    +
    +
    + + #ACT_ROUTE_POINT + +ACT_ROUTE_POINT + +
    +
    + + +
    @@ -424,8 +557,8 @@ self

    Core.Zone#ZONE_BASE - -ACT_ROUTE.TargetZone + +ACT_ROUTE.Zone
    @@ -530,6 +663,330 @@ self

    #boolean:

    +
    +
    + +

    Type ACT_ROUTE_POINT

    + +

    ACTROUTEPOINT class

    + +

    Field(s)

    +
    +
    + + #string + +ACT_ROUTE_POINT.ClassName + +
    +
    + + + +
    +
    +
    +
    + + #number + +ACT_ROUTE_POINT.DisplayCount + +
    +
    + + + +
    +
    +
    +
    + + #number + +ACT_ROUTE_POINT.DisplayInterval + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +ACT_ROUTE_POINT.DisplayMessage + +
    +
    + + + +
    +
    +
    +
    + + #number + +ACT_ROUTE_POINT.DisplayTime + +
    +
    + + + + +

    10 seconds is the default

    + +
    +
    +
    +
    + + +ACT_ROUTE_POINT:GetPointVec2() + +
    +
    + +

    Get PointVec2

    + +

    Return value

    + +

    Core.Point#POINT_VEC2: +PointVec2 The PointVec2 to route to.

    + +
    +
    +
    +
    + + +ACT_ROUTE_POINT:GetRange() + +
    +
    + +

    Get Range around PointVec2

    + +

    Return value

    + +

    #number: +The Range to consider the arrival. Default is 10000 meters.

    + +
    +
    +
    +
    + + +ACT_ROUTE_POINT:Init(FsmRoute) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      FsmRoute :

      + +
    • +
    +
    +
    +
    +
    + + +ACT_ROUTE_POINT:New(The, Range, Zone, PointVec2) + +
    +
    + +

    Creates a new routing state machine.

    + + +

    The task will route a controllable to a PointVec2 until the controllable is within the Range.

    + +

    Parameters

    + +
    +
    +
    +
    + + + +ACT_ROUTE_POINT.PointVec2 + +
    +
    + + + +
    +
    +
    +
    + + +ACT_ROUTE_POINT.Range + +
    +
    + + + +
    +
    +
    +
    + + +ACT_ROUTE_POINT:SetPointVec2(PointVec2) + +
    +
    + +

    Set PointVec2

    + +

    Parameter

    + +
    +
    +
    +
    + + +ACT_ROUTE_POINT:SetRange(Range) + +
    +
    + +

    Set Range around PointVec2

    + +

    Parameter

    +
      +
    • + +

      #number Range : +The Range to consider the arrival. Default is 10000 meters.

      + +
    • +
    +
    +
    +
    +
    + + Tasking.Task#TASK + +ACT_ROUTE_POINT.TASK + +
    +
    + + + +
    +
    +
    +
    + + +ACT_ROUTE_POINT:onenterReporting(ProcessUnit, Event, From, To) + +
    +
    + +

    StateMachine callback function

    + +

    Parameters

    + +
    +
    +
    +
    + + +ACT_ROUTE_POINT:onfuncHasArrived(ProcessUnit) + +
    +
    + +

    Method override to check if the controllable has arrived.

    + +

    Parameter

    + +

    Return value

    + +

    #boolean:

    + +
    @@ -614,6 +1071,24 @@ self

    + +ACT_ROUTE_ZONE:GetZone() + +
    +
    + +

    Get Zone

    + +

    Return value

    + +

    Core.Zone#ZONE_BASE: +Zone The Zone object where to route to.

    + +
    +
    +
    +
    + ACT_ROUTE_ZONE:Init(FsmRoute) @@ -636,7 +1111,7 @@ self

    -ACT_ROUTE_ZONE:New(TargetZone) +ACT_ROUTE_ZONE:New(Zone)
    @@ -650,7 +1125,7 @@ self

    @@ -668,6 +1143,28 @@ self

    +
    +
    +
    +
    + + +ACT_ROUTE_ZONE:SetZone(Zone) + +
    +
    + +

    Set Zone

    + +

    Parameter

    +
    @@ -688,8 +1185,8 @@ self

    Core.Zone#ZONE_BASE - -ACT_ROUTE_ZONE.TargetZone + +ACT_ROUTE_ZONE.Zone
    diff --git a/docs/Documentation/Scenery.html b/docs/Documentation/Scenery.html index f7258b5b4..84704e530 100644 --- a/docs/Documentation/Scenery.html +++ b/docs/Documentation/Scenery.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/ScheduleDispatcher.html b/docs/Documentation/ScheduleDispatcher.html index df99aa154..887ea6292 100644 --- a/docs/Documentation/ScheduleDispatcher.html +++ b/docs/Documentation/ScheduleDispatcher.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -139,6 +140,12 @@ The Schedule() method returns the CallID that is the reference ID for each plann SCHEDULEDISPATCHER.ClassName + + + + SCHEDULEDISPATCHER:Clear(Scheduler) + + @@ -288,6 +295,27 @@ Nothing of this code should be modified without testing it thoroughly.

    +
    +
    +
    +
    + + +SCHEDULEDISPATCHER:Clear(Scheduler) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      Scheduler :

      + +
    • +
    @@ -315,7 +343,7 @@ Nothing of this code should be modified without testing it thoroughly.

    -

    setmetatable( {}, { __mode = "v" } )

    +

    or {}

    diff --git a/docs/Documentation/Scheduler.html b/docs/Documentation/Scheduler.html index eb654041b..b8e42d2bb 100644 --- a/docs/Documentation/Scheduler.html +++ b/docs/Documentation/Scheduler.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -72,10 +73,14 @@

    Module Scheduler

    -

    This module contains the SCHEDULER class.

    +

    Core - SCHEDULER prepares and handles the execution of functions over scheduled time (intervals).

    +

    Banner Image

    + +
    +

    1) Scheduler#SCHEDULER class, extends Base#BASE

    The Scheduler#SCHEDULER class creates schedule.

    @@ -142,6 +147,12 @@ SCHEDULER.ClassName + + + + SCHEDULER:Clear() + +

    Clears all pending schedules.

    @@ -228,6 +239,19 @@ + +
    +
    +
    + + +SCHEDULER:Clear() + +
    +
    + +

    Clears all pending schedules.

    +
    diff --git a/docs/Documentation/Scoring.html b/docs/Documentation/Scoring.html index 0ffe32d75..0d635d4ef 100644 --- a/docs/Documentation/Scoring.html +++ b/docs/Documentation/Scoring.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -79,8 +80,6 @@ and create a CSV file logging the scoring events for use at team or squadron web
    -

    1) Scoring#SCORING class, extends Base#BASE

    -

    The #SCORING class administers the scoring of player achievements, and creates a CSV file logging the scoring events and results for use at team or squadron websites.

    @@ -135,6 +134,8 @@ These CSV files can be used to:

    Use the radio menu F10 to consult the scores while running the mission. Scores can be reported for your user, or an overall score can be reported of all players currently active in the mission.

    +

    1) Scoring#SCORING class, extends Base#BASE

    +

    1.1) Set the destroy score or penalty scale

    Score scales can be set for scores granted when enemies or friendlies are destroyed. @@ -168,8 +169,6 @@ For example, this can be done as follows:

     Scoring:RemoveUnitScore( UNIT:FindByName( "Unit #001" ) )
     
    - -

    1.3) Define destruction zones that will give extra scores.

    Define zones of destruction. Any object destroyed within the zone of the given category will give extra points. diff --git a/docs/Documentation/Sead.html b/docs/Documentation/Sead.html index be4e3bf31..12a877b80 100644 --- a/docs/Documentation/Sead.html +++ b/docs/Documentation/Sead.html @@ -39,16 +39,17 @@

  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -89,18 +90,18 @@ SEAD.ClassName - - - - SEAD:EventShot(Event) - -

    Detects if an SA site was shot with an anti radiation missile.

    SEAD.New(table, self, SEADGroupPrefixes)

    Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.

    + + + + SEAD.OnEventShot(#, EventData, self) + +

    Detects if an SA site was shot with an anti radiation missile.

    @@ -151,30 +152,6 @@ - -
    -
    -
    - - -SEAD:EventShot(Event) - -
    -
    - -

    Detects if an SA site was shot with an anti radiation missile.

    - - -

    In this case, take evasive actions based on the skill level set within the ME.

    - -

    Parameter

    -
      -
    • - -

      Event :

      - -
    • -
    @@ -221,6 +198,41 @@ string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Grou -- Defends the Russian SA installations from SEAD attacks. SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
    + + +
    +
    + + +SEAD.OnEventShot(#, EventData, self) + +
    +
    + +

    Detects if an SA site was shot with an anti radiation missile.

    + + +

    In this case, take evasive actions based on the skill level set within the ME.

    + +

    Parameters

    +
    diff --git a/docs/Documentation/Set.html b/docs/Documentation/Set.html index 8c6884967..4c542f663 100644 --- a/docs/Documentation/Set.html +++ b/docs/Documentation/Set.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -72,253 +73,39 @@

    Module Set

    -

    Core - SET classes define collections of objects to perform bulk actions and logically group objects.

    +

    Core - SET_ classes define collections of objects to perform bulk actions and logically group objects.

    -
    - -

    1) Set#SET_BASE class, extends Base#BASE

    -

    The Set#SET_BASE class defines the core functions that define a collection of objects. -A SET provides iterators to iterate the SET, but will temporarily yield the ForEach interator loop at defined "intervals" to the mail simulator loop. -In this way, large loops can be done while not blocking the simulator main processing loop. -The default "yield interval" is after 10 objects processed. -The default "time interval" is after 0.001 seconds.

    - -

    1.1) Add or remove objects from the SET

    -

    Some key core functions are Set#SET_BASE.Add and Set#SET_BASE.Remove to add or remove objects from the SET in your logic.

    - -

    1.2) Define the SET iterator "yield interval" and the "time interval"

    -

    Modify the iterator intervals with the Set#SET_BASE.SetInteratorIntervals method. -You can set the "yield interval", and the "time interval". (See above).

    +

    Banner Image


    -

    2) Set#SET_GROUP class, extends Set#SET_BASE

    -

    Mission designers can use the Set#SET_GROUP class to build sets of groups belonging to certain:

    +

    SET_ classes group objects of the same type into a collection, which is either:

      -
    • Coalitions
    • -
    • Categories
    • -
    • Countries
    • -
    • Starting with certain prefix strings.
    • +
    • Manually managed using the :Add...() or :Remove...() methods. The initial SET can be filtered with the **SET_BASE.FilterOnce()** method
    • +
    • Dynamically updated when new objects are created or objects are destroyed using the **SET_BASE.FilterStart()** method.
    -

    2.1) SET_GROUP construction method:

    -

    Create a new SET_GROUP object with the SET_GROUP.New method:

    +

    Various types of SET_ classes are available:

    -

    2.2) Add or Remove GROUP(s) from SET_GROUP:

    -

    GROUPS can be added and removed using the Set#SET_GROUP.AddGroupsByName and Set#SET_GROUP.RemoveGroupsByName respectively. -These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP.

    +

    These classes are derived from #SET_BASE, which contains the main methods to manage SETs.

    -

    2.3) SET_GROUP filter criteria:

    -

    You can set filter criteria to define the set of groups within the SET_GROUP. -Filter criteria are defined by:

    +

    A multitude of other methods are available in SET_ classes that allow to:

      -
    • SET_GROUP.FilterCoalitions: Builds the SET_GROUP with the groups belonging to the coalition(s).
    • -
    • SET_GROUP.FilterCategories: Builds the SET_GROUP with the groups belonging to the category(ies).
    • -
    • SET_GROUP.FilterCountries: Builds the SET_GROUP with the gruops belonging to the country(ies).
    • -
    • SET_GROUP.FilterPrefixes: Builds the SET_GROUP with the groups starting with the same prefix string(s).
    • +
    • Validate the presence of objects in the SET.
    • +
    • Trigger events when objects in the SET change a zone presence.
    -

    Once the filter criteria have been set for the SET_GROUP, you can start filtering using:

    - -
      -
    • SET_GROUP.FilterStart: Starts the filtering of the groups within the SET_GROUP and add or remove GROUP objects dynamically.
    • -
    - -

    Planned filter criteria within development are (so these are not yet available):

    - - - -

    2.4) SET_GROUP iterators:

    -

    Once the filters have been defined and the SETGROUP has been built, you can iterate the SETGROUP with the available iterator methods. -The iterator methods will walk the SETGROUP set, and call for each element within the set a function that you provide. -The following iterator methods are currently available within the SETGROUP:

    - -
      -
    • SET_GROUP.ForEachGroup: Calls a function for each alive group it finds within the SET_GROUP.
    • -
    • SET_GROUP.ForEachGroupCompletelyInZone: Iterate the SET_GROUP and call an iterator function for each alive GROUP presence completely in a Zone, providing the GROUP and optional parameters to the called function.
    • -
    • SET_GROUP.ForEachGroupPartlyInZone: Iterate the SET_GROUP and call an iterator function for each alive GROUP presence partly in a Zone, providing the GROUP and optional parameters to the called function.
    • -
    • SET_GROUP.ForEachGroupNotInZone: Iterate the SET_GROUP and call an iterator function for each alive GROUP presence not in a Zone, providing the GROUP and optional parameters to the called function.
    • -
    - -
    - -

    3) Set#SET_UNIT class, extends Set#SET_BASE

    -

    Mission designers can use the Set#SET_UNIT class to build sets of units belonging to certain:

    - -
      -
    • Coalitions
    • -
    • Categories
    • -
    • Countries
    • -
    • Unit types
    • -
    • Starting with certain prefix strings.
    • -
    - -

    3.1) SET_UNIT construction method:

    -

    Create a new SET_UNIT object with the SET_UNIT.New method:

    - - - -

    3.2) Add or Remove UNIT(s) from SET_UNIT:

    -

    UNITs can be added and removed using the Set#SET_UNIT.AddUnitsByName and Set#SET_UNIT.RemoveUnitsByName respectively. -These methods take a single UNIT name or an array of UNIT names to be added or removed from SET_UNIT.

    - -

    3.3) SET_UNIT filter criteria:

    -

    You can set filter criteria to define the set of units within the SET_UNIT. -Filter criteria are defined by:

    - - - -

    Once the filter criteria have been set for the SET_UNIT, you can start filtering using:

    - - - -

    Planned filter criteria within development are (so these are not yet available):

    - - - -

    3.4) SET_UNIT iterators:

    -

    Once the filters have been defined and the SETUNIT has been built, you can iterate the SETUNIT with the available iterator methods. -The iterator methods will walk the SETUNIT set, and call for each element within the set a function that you provide. -The following iterator methods are currently available within the SETUNIT:

    - -
      -
    • SET_UNIT.ForEachUnit: Calls a function for each alive unit it finds within the SET_UNIT.
    • -
    • SET_GROUP.ForEachGroupCompletelyInZone: Iterate the SET_GROUP and call an iterator function for each alive GROUP presence completely in a Zone, providing the GROUP and optional parameters to the called function.
    • -
    • SET_GROUP.ForEachGroupNotInZone: Iterate the SET_GROUP and call an iterator function for each alive GROUP presence not in a Zone, providing the GROUP and optional parameters to the called function.
    • -
    - -

    Planned iterators methods in development are (so these are not yet available):

    - - - -
    - -

    4) Set#SET_CLIENT class, extends Set#SET_BASE

    -

    Mission designers can use the Set#SET_CLIENT class to build sets of units belonging to certain:

    - -
      -
    • Coalitions
    • -
    • Categories
    • -
    • Countries
    • -
    • Client types
    • -
    • Starting with certain prefix strings.
    • -
    - -

    4.1) SET_CLIENT construction method:

    -

    Create a new SET_CLIENT object with the SET_CLIENT.New method:

    - - - -

    4.2) Add or Remove CLIENT(s) from SET_CLIENT:

    -

    CLIENTs can be added and removed using the Set#SET_CLIENT.AddClientsByName and Set#SET_CLIENT.RemoveClientsByName respectively. -These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT.

    - -

    4.3) SET_CLIENT filter criteria:

    -

    You can set filter criteria to define the set of clients within the SET_CLIENT. -Filter criteria are defined by:

    - - - -

    Once the filter criteria have been set for the SET_CLIENT, you can start filtering using:

    - - - -

    Planned filter criteria within development are (so these are not yet available):

    - - - -

    4.4) SET_CLIENT iterators:

    -

    Once the filters have been defined and the SETCLIENT has been built, you can iterate the SETCLIENT with the available iterator methods. -The iterator methods will walk the SETCLIENT set, and call for each element within the set a function that you provide. -The following iterator methods are currently available within the SETCLIENT:

    - - - -
    - -

    5) Set#SET_AIRBASE class, extends Set#SET_BASE

    -

    Mission designers can use the Set#SET_AIRBASE class to build sets of airbases optionally belonging to certain:

    - -
      -
    • Coalitions
    • -
    - -

    5.1) SET_AIRBASE construction

    -

    Create a new SET_AIRBASE object with the SET_AIRBASE.New method:

    - - - -

    5.2) Add or Remove AIRBASEs from SET_AIRBASE

    -

    AIRBASEs can be added and removed using the Set#SET_AIRBASE.AddAirbasesByName and Set#SET_AIRBASE.RemoveAirbasesByName respectively. -These methods take a single AIRBASE name or an array of AIRBASE names to be added or removed from SET_AIRBASE.

    - -

    5.3) SET_AIRBASE filter criteria

    -

    You can set filter criteria to define the set of clients within the SET_AIRBASE. -Filter criteria are defined by:

    - - - -

    Once the filter criteria have been set for the SET_AIRBASE, you can start filtering using:

    - - - -

    5.4) SET_AIRBASE iterators:

    -

    Once the filters have been defined and the SETAIRBASE has been built, you can iterate the SETAIRBASE with the available iterator methods. -The iterator methods will walk the SETAIRBASE set, and call for each airbase within the set a function that you provide. -The following iterator methods are currently available within the SETAIRBASE:

    - - - -
    -

    Authors:

    @@ -1097,6 +904,23 @@ The following iterator methods are currently available within the SETAIRBAS
    +

    1) SET_BASE class, extends Base#BASE

    +

    The Set#SET_BASE class defines the core functions that define a collection of objects.

    + + +

    A SET provides iterators to iterate the SET, but will temporarily yield the ForEach interator loop at defined "intervals" to the mail simulator loop. +In this way, large loops can be done while not blocking the simulator main processing loop. +The default "yield interval" is after 10 objects processed. +The default "time interval" is after 0.001 seconds.

    + +

    1.1) Add or remove objects from the SET

    + +

    Some key core functions are Set#SET_BASE.Add and Set#SET_BASE.Remove to add or remove objects from the SET in your logic.

    + +

    1.2) Define the SET iterator "yield interval" and the "time interval"

    + +

    Modify the iterator intervals with the Set#SET_BASE.SetInteratorIntervals method. +You can set the "yield interval", and the "time interval". (See above).

    @@ -1111,7 +935,69 @@ The following iterator methods are currently available within the SETAIRBAS
    +

    4) SET_CLIENT class, extends Set#SET_BASE

    +

    Mission designers can use the Set#SET_CLIENT class to build sets of units belonging to certain:

    + +
      +
    • Coalitions
    • +
    • Categories
    • +
    • Countries
    • +
    • Client types
    • +
    • Starting with certain prefix strings.
    • +
    + + +

    +

    4.1) SET_CLIENT constructor

    + +

    Create a new SET_CLIENT object with the SET_CLIENT.New method:

    + + + +

    4.2) Add or Remove CLIENT(s) from SET_CLIENT

    + +

    CLIENTs can be added and removed using the Set#SET_CLIENT.AddClientsByName and Set#SET_CLIENT.RemoveClientsByName respectively. +These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT.

    + +

    4.3) SET_CLIENT filter criteria

    + +

    You can set filter criteria to define the set of clients within the SET_CLIENT. +Filter criteria are defined by:

    + + + +

    Once the filter criteria have been set for the SET_CLIENT, you can start filtering using:

    + + + +

    Planned filter criteria within development are (so these are not yet available):

    + + + +

    4.4) SET_CLIENT iterators

    + +

    Once the filters have been defined and the SETCLIENT has been built, you can iterate the SETCLIENT with the available iterator methods. +The iterator methods will walk the SETCLIENT set, and call for each element within the set a function that you provide. +The following iterator methods are currently available within the SETCLIENT:

    + + + +
    @@ -1125,7 +1011,70 @@ The following iterator methods are currently available within the SETAIRBAS
    +

    2) SET_GROUP class, extends Set#SET_BASE

    +

    Mission designers can use the Set#SET_GROUP class to build sets of groups belonging to certain:

    + +
      +
    • Coalitions
    • +
    • Categories
    • +
    • Countries
    • +
    • Starting with certain prefix strings.
    • +
    + + +

    +

    2.1) SET_GROUP constructor

    + +

    Create a new SET_GROUP object with the SET_GROUP.New method:

    + + + +

    2.2) Add or Remove GROUP(s) from SET_GROUP

    + +

    GROUPS can be added and removed using the Set#SET_GROUP.AddGroupsByName and Set#SET_GROUP.RemoveGroupsByName respectively. +These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP.

    + +

    2.3) SET_GROUP filter criteria

    + +

    You can set filter criteria to define the set of groups within the SET_GROUP. +Filter criteria are defined by:

    + + + +

    Once the filter criteria have been set for the SET_GROUP, you can start filtering using:

    + +
      +
    • SET_GROUP.FilterStart: Starts the filtering of the groups within the SET_GROUP and add or remove GROUP objects dynamically.
    • +
    + +

    Planned filter criteria within development are (so these are not yet available):

    + + + +

    2.4) SET_GROUP iterators

    + +

    Once the filters have been defined and the SETGROUP has been built, you can iterate the SETGROUP with the available iterator methods. +The iterator methods will walk the SETGROUP set, and call for each element within the set a function that you provide. +The following iterator methods are currently available within the SETGROUP:

    + +
      +
    • SET_GROUP.ForEachGroup: Calls a function for each alive group it finds within the SET_GROUP.
    • +
    • SET_GROUP.ForEachGroupCompletelyInZone: Iterate the SET_GROUP and call an iterator function for each alive GROUP presence completely in a Zone, providing the GROUP and optional parameters to the called function.
    • +
    • SET_GROUP.ForEachGroupPartlyInZone: Iterate the SET_GROUP and call an iterator function for each alive GROUP presence partly in a Zone, providing the GROUP and optional parameters to the called function.
    • +
    • SET_GROUP.ForEachGroupNotInZone: Iterate the SET_GROUP and call an iterator function for each alive GROUP presence not in a Zone, providing the GROUP and optional parameters to the called function.
    • +
    + +
    @@ -1139,17 +1088,94 @@ The following iterator methods are currently available within the SETAIRBAS
    +

    3) SET_UNIT class, extends Set#SET_BASE

    +

    Mission designers can use the SET_UNIT class to build sets of units belonging to certain:

    + +
      +
    • Coalitions
    • +
    • Categories
    • +
    • Countries
    • +
    • Unit types
    • +
    • Starting with certain prefix strings.
    • +
    + + +

    +

    3.1) SET_UNIT constructor

    + +

    Create a new SET_UNIT object with the SET_UNIT.New method:

    + + + +

    3.2) Add or Remove UNIT(s) from SET_UNIT

    + +

    UNITs can be added and removed using the Set#SET_UNIT.AddUnitsByName and Set#SET_UNIT.RemoveUnitsByName respectively. +These methods take a single UNIT name or an array of UNIT names to be added or removed from SET_UNIT.

    + +

    3.3) SET_UNIT filter criteria

    + +

    You can set filter criteria to define the set of units within the SET_UNIT. +Filter criteria are defined by:

    + + + +

    Once the filter criteria have been set for the SET_UNIT, you can start filtering using:

    + + + +

    Planned filter criteria within development are (so these are not yet available):

    + + + +

    3.4) SET_UNIT iterators

    + +

    Once the filters have been defined and the SETUNIT has been built, you can iterate the SETUNIT with the available iterator methods. +The iterator methods will walk the SETUNIT set, and call for each element within the set a function that you provide. +The following iterator methods are currently available within the SETUNIT:

    + +
      +
    • SET_UNIT.ForEachUnit: Calls a function for each alive unit it finds within the SET_UNIT.
    • +
    • SET_GROUP.ForEachGroupCompletelyInZone: Iterate the SET_GROUP and call an iterator function for each alive GROUP presence completely in a Zone, providing the GROUP and optional parameters to the called function.
    • +
    • SET_GROUP.ForEachGroupNotInZone: Iterate the SET_GROUP and call an iterator function for each alive GROUP presence not in a Zone, providing the GROUP and optional parameters to the called function.
    • +
    + +

    Planned iterators methods in development are (so these are not yet available):

    + + + +

    3.5 ) SET_UNIT atomic methods

    + +

    Various methods exist for a SETUNIT to perform actions or calculations and retrieve results from the SETUNIT:

    + + + +

    Type Set

    Type SET_AIRBASE

    - -

    SET_AIRBASE class

    - -

    Field(s)

    +

    Field(s)

    @@ -1214,48 +1240,6 @@ The AIRBASE

    - -
    -
    -
    - - - -SET_AIRBASE.Airbases - -
    -
    - - - -
    -
    -
    -
    - - #string - -SET_AIRBASE.ClassName - -
    -
    - - - -
    -
    -
    -
    - - - -SET_AIRBASE.Filter - -
    -
    - - -
    @@ -1316,20 +1300,6 @@ Can take the following values: "red", "blue", "neutral".

    #SET_AIRBASE: self

    - -
    -
    -
    - - - -SET_AIRBASE.FilterMeta - -
    -
    - - -
    @@ -1551,10 +1521,7 @@ A single name or an array of AIRBASE names.

    Type SET_BASE

    - -

    SET_BASE class

    - -

    Field(s)

    +

    Field(s)

    @@ -1624,20 +1591,6 @@ The added BASE Object.

    - -
    -
    -
    - - #string - -SET_BASE.ClassName - -
    -
    - - -
    @@ -1844,6 +1797,60 @@ self

    Core.Base#BASE:

    + +
    +
    +
    + + +SET_BASE:GetFirst() + +
    +
    + +

    Gets the first object from the Set#SET_BASE and derived classes.

    + +

    Return value

    + +

    Core.Base#BASE:

    + + +
    +
    +
    +
    + + +SET_BASE:GetLast() + +
    +
    + +

    Gets the last object from the Set#SET_BASE and derived classes.

    + +

    Return value

    + +

    Core.Base#BASE:

    + + +
    +
    +
    +
    + + +SET_BASE:GetRandom() + +
    +
    + +

    Gets a random object from the Set#SET_BASE and derived classes.

    + +

    Return value

    + +

    Core.Base#BASE:

    + +
    @@ -2186,10 +2193,7 @@ The Object found.

    Type SET_CLIENT

    - -

    SET_CLIENT class

    - -

    Field(s)

    +

    Field(s)

    @@ -2254,48 +2258,6 @@ The CLIENT

    - -
    -
    -
    - - #string - -SET_CLIENT.ClassName - -
    -
    - - - -
    -
    -
    -
    - - - -SET_CLIENT.Clients - -
    -
    - - - -
    -
    -
    -
    - - - -SET_CLIENT.Filter - -
    -
    - - -
    @@ -2386,20 +2348,6 @@ Can take those country strings known within DCS world.

    #SET_CLIENT: self

    - -
    -
    -
    - - - -SET_CLIENT.FilterMeta - -
    -
    - - -
    @@ -2730,10 +2678,7 @@ A single name or an array of CLIENT names.

    Type SET_GROUP

    - -

    SET_GROUP class

    - -

    Field(s)

    +

    Field(s)

    @@ -2798,34 +2743,6 @@ The GROUP

    - -
    -
    -
    - - #string - -SET_GROUP.ClassName - -
    -
    - - - -
    -
    -
    -
    - - - -SET_GROUP.Filter - -
    -
    - - -
    @@ -2916,20 +2833,6 @@ Can take those country strings known within DCS world.

    #SET_GROUP: self

    - -
    -
    -
    - - - -SET_GROUP.FilterMeta - -
    -
    - - -
    @@ -3268,10 +3171,7 @@ A single name or an array of GROUP names.

    Type SET_UNIT

    - -

    SET_UNIT class

    - -

    Field(s)

    +

    Field(s)

    @@ -3376,34 +3276,6 @@ self

    Calculate the maxium A2G threat level of the SET_UNIT.

    - -
    -
    -
    - - #string - -SET_UNIT.ClassName - -
    -
    - - - -
    -
    -
    -
    - - - -SET_UNIT.Filter - -
    -
    - - -
    @@ -3542,20 +3414,6 @@ self

    #SET_UNIT: self

    - -
    -
    -
    - - - -SET_UNIT.FilterMeta - -
    -
    - - -
    @@ -3812,6 +3670,33 @@ self

    + +SET_UNIT:GetTypeNames(Delimiter) + +
    +
    + +

    Retrieve the type names of the Units in the SET, delimited by an optional delimiter.

    + +

    Parameter

    +
      +
    • + +

      #string Delimiter : +(optional) The delimiter, which is default a comma.

      + +
    • +
    +

    Return value

    + +

    #string: +The types of the Units delimited.

    + +
    +
    +
    +
    + SET_UNIT:GetUnitThreatLevels() @@ -4024,20 +3909,6 @@ A single name or an array of UNIT names.

    self

    - -
    -
    -
    - - - -SET_UNIT.Units - -
    -
    - - -
    diff --git a/docs/Documentation/Smoke.html b/docs/Documentation/Smoke.html index 0089621d3..f91a5703f 100644 --- a/docs/Documentation/Smoke.html +++ b/docs/Documentation/Smoke.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -231,6 +232,12 @@ At random intervals, a new target is smoked.

    ACT_ASSIST:onafterStart(ProcessUnit, Event, From, To)

    StateMachine callback function

    + + + + ACT_ASSIST:onafterStop(ProcessUnit, Event, From, To) + +

    StateMachine callback function

    @@ -467,6 +474,42 @@ At random intervals, a new target is smoked.

    +

    StateMachine callback function

    + +

    Parameters

    + +
    +
    +
    +
    + + +ACT_ASSIST:onafterStop(ProcessUnit, Event, From, To) + +
    +
    +

    StateMachine callback function

    Parameters

    diff --git a/docs/Documentation/Spawn.html b/docs/Documentation/Spawn.html index bf5889313..3c2379643 100644 --- a/docs/Documentation/Spawn.html +++ b/docs/Documentation/Spawn.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -769,18 +770,6 @@ and any spaces before and after the resulting name are removed.

    SPAWN:_GetGroupCountryID(SpawnPrefix)

    Gets the CountryID of the Group with the given SpawnPrefix

    - - - - SPAWN:_GetGroupFromDCSUnit(DCSUnit) - -

    Return the group within the SpawnGroups collection with input a DCSUnit.

    - - - - SPAWN:_GetGroupIndexFromDCSUnit(DCSUnit) - -

    Get the group index from a DCSUnit.

    @@ -790,7 +779,7 @@ and any spaces before and after the resulting name are removed.

    - SPAWN:_GetPrefixFromDCSUnit(DCSUnit) + SPAWN:_GetPrefixFromGroup(DCSUnit, SpawnGroup)

    Return the prefix of a SpawnUnit.

    @@ -814,31 +803,31 @@ and any spaces before and after the resulting name are removed.

    - SPAWN:_OnBirth(Event) + SPAWN:_OnBirth(EventData) - SPAWN:_OnDeadOrCrash(Event) + SPAWN:_OnDeadOrCrash(EventData) - SPAWN:_OnEngineShutDown(event) + SPAWN:_OnEngineShutDown(EventData)

    Will detect AIR Units shutting down their engines ...

    - SPAWN:_OnLand(event) + SPAWN:_OnLand(EventData)

    Will detect AIR Units landing...

    - SPAWN:_OnTakeOff(event) + SPAWN:_OnTakeOff(EventData)

    Will detect AIR Units taking off...

    @@ -2337,9 +2326,6 @@ when nothing was spawned.

    - -

    By default, no InitLimit

    -
    @@ -2375,7 +2361,7 @@ when nothing was spawned.

    - #number + SPAWN.SpawnMaxGroups @@ -2392,7 +2378,7 @@ when nothing was spawned.

    - #number + SPAWN.SpawnMaxUnitsAlive @@ -2865,84 +2851,6 @@ The group that was spawned. You can use this group for further actions.

    - -SPAWN:_GetGroupFromDCSUnit(DCSUnit) - -
    -
    - -

    Return the group within the SpawnGroups collection with input a DCSUnit.

    - -

    Parameter

    - -

    Return values

    -
      -
    1. - -

      Wrapper.Group#GROUP: -The Group

      - -
    2. -
    3. - -

      #nil: -Nothing found

      - -
    4. -
    -
    -
    -
    -
    - - -SPAWN:_GetGroupIndexFromDCSUnit(DCSUnit) - -
    -
    - -

    Get the group index from a DCSUnit.

    - - -

    The method will search for a #-mark, and will return the index behind the #-mark of the DCSUnit. -It will return nil of no prefix was found.

    - -

    Parameter

    - -

    Return values

    -
      -
    1. - -

      #string: -The prefix

      - -
    2. -
    3. - -

      #nil: -Nothing found

      - -
    4. -
    -
    -
    -
    -
    - SPAWN:_GetLastIndex() @@ -2956,8 +2864,8 @@ Nothing found

    - -SPAWN:_GetPrefixFromDCSUnit(DCSUnit) + +SPAWN:_GetPrefixFromGroup(DCSUnit, SpawnGroup)
    @@ -2968,13 +2876,18 @@ Nothing found

    The method will search for a #-mark, and will return the text before the #-mark. It will return nil of no prefix was found.

    -

    Parameter

    +

    Parameters

    Return values

    @@ -3072,7 +2985,7 @@ Nothing found

    -SPAWN:_OnBirth(Event) +SPAWN:_OnBirth(EventData)
    @@ -3083,7 +2996,7 @@ Nothing found

    @@ -3093,7 +3006,7 @@ Nothing found

    -SPAWN:_OnDeadOrCrash(Event) +SPAWN:_OnDeadOrCrash(EventData)
    @@ -3104,7 +3017,7 @@ Nothing found

    @@ -3114,7 +3027,7 @@ Nothing found

    -SPAWN:_OnEngineShutDown(event) +SPAWN:_OnEngineShutDown(EventData)
    @@ -3129,7 +3042,7 @@ But only when the Unit was registered to have landed.

    @@ -3139,7 +3052,7 @@ But only when the Unit was registered to have landed.

    -SPAWN:_OnLand(event) +SPAWN:_OnLand(EventData)
    @@ -3154,7 +3067,7 @@ This is needed to ensure that Re-SPAWNing is only done for landed AIR Groups.

  • -

    event :

    +

    Core.Event#EVENTDATA EventData :

  • @@ -3164,7 +3077,7 @@ This is needed to ensure that Re-SPAWNing is only done for landed AIR Groups.

    -SPAWN:_OnTakeOff(event) +SPAWN:_OnTakeOff(EventData)
    @@ -3179,7 +3092,7 @@ This is needed to ensure that Re-SPAWNing only is done for landed AIR Groups.

  • -

    event :

    +

    Core.Event#EVENTDATA EventData :

  • diff --git a/docs/Documentation/Static.html b/docs/Documentation/Static.html index cdcb75c70..ff176e054 100644 --- a/docs/Documentation/Static.html +++ b/docs/Documentation/Static.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Task.html b/docs/Documentation/Task.html index 9befab2b3..f2a503a07 100644 --- a/docs/Documentation/Task.html +++ b/docs/Documentation/Task.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -152,7 +153,7 @@ Use the method TASK.AddScore() to add scores whe TASK:AssignToGroup(TaskGroup) -

    Assign the Taskto a Group.

    +

    Assign the Task to a Group.

    @@ -183,6 +184,12 @@ Use the method TASK.AddScore() to add scores whe TASK:CrashUnit(PlayerUnit)

    A PlayerUnit crashed in a Task.

    + + + + TASK.Dispatcher + + @@ -237,6 +244,12 @@ Use the method TASK.AddScore() to add scores whe TASK:GetScoring()

    Gets the Scoring of the task

    + + + + TASK:GetStateMachine(TaskUnit) + +

    Gets the FiniteStateMachine of Task with key TaskUnit

    @@ -264,7 +277,7 @@ Use the method TASK.AddScore() to add scores whe - TASK:GetUnitProcess() + TASK:GetUnitProcess(TaskUnit)

    Get the Task FSM Process Template

    @@ -291,12 +304,24 @@ Use the method TASK.AddScore() to add scores whe TASK:IsAssignedToGroup(TaskGroup)

    Returns if the Task is assigned to the Group.

    + + + + TASK:IsStateAborted() + +

    Is the Task status Aborted.

    TASK:IsStateAssigned()

    Is the Task status Assigned.

    + + + + TASK:IsStateCancelled() + +

    Is the Task status Cancelled.

    @@ -348,15 +373,15 @@ Use the method TASK.AddScore() to add scores whe - TASK.MenuTaskAbort(MenuParam) + TASK:MenuTaskAbort(TaskGroup) - +

    Report the task status.

    - TASK.MenuTaskStatus(MenuParam) + TASK:MenuTaskStatus(TaskGroup) - +

    Report the task status.

    @@ -414,13 +439,19 @@ Use the method TASK.AddScore() to add scores whe - TASK:RemoveMenu() + TASK:RemoveAssignedMenuForGroup(TaskGroup, MenuTime) + +

    Remove the assigned menu option of the Task for a Group.

    + + + + TASK:RemoveMenu(MenuTime)

    Remove the menu options of the Task to all the groups in the SetGroup.

    - TASK:RemoveMenuForGroup(TaskGroup) + TASK:RemovePlannedMenuForGroup(TaskGroup, MenuTime)

    Remove the menu option of the Task for a Group.

    @@ -462,7 +493,7 @@ Use the method TASK.AddScore() to add scores whe - TASK:SetAssignedMenuForGroup(TaskGroup) + TASK:SetAssignedMenuForGroup(TaskGroup, MenuTime)

    Set the assigned menu options of the Task.

    @@ -471,6 +502,12 @@ Use the method TASK.AddScore() to add scores whe TASK:SetBriefing(TaskBriefing)

    Sets a Task briefing.

    + + + + TASK:SetDispatcher(Dispatcher) + +

    Set dispatcher of a task

    @@ -486,13 +523,13 @@ Use the method TASK.AddScore() to add scores whe - TASK:SetMenu() + TASK:SetMenu(MenuTime)

    Set the menu options of the Task to all the groups in the SetGroup.

    - TASK:SetMenuForGroup(TaskGroup) + TASK:SetMenuForGroup(MenuTime, TaskGroup)

    Set the Menu for a Group

    @@ -504,7 +541,7 @@ Use the method TASK.AddScore() to add scores whe - TASK:SetPlannedMenuForGroup(TaskGroup, MenuText) + TASK:SetPlannedMenuForGroup(TaskGroup, MenuText, MenuTime)

    Set the planned menu option of the Task.

    @@ -531,12 +568,24 @@ Use the method TASK.AddScore() to add scores whe TASK:SetUnitProcess(Core, FsmTemplate)

    Sets the Task FSM Process Template

    + + + + TASK:StateAborted() + +

    Sets a Task to status Aborted.

    TASK:StateAssigned()

    Sets a Task to status Assigned.

    + + + + TASK:StateCancelled() + +

    Sets a Task to status Cancelled.

    @@ -609,12 +658,18 @@ Use the method TASK.AddScore() to add scores whe TASK.TimeOut + + + + TASK:UnAssignFromGroup(TaskGroup) + +

    UnAssign the Task from a Group.

    TASK:UnAssignFromGroups() -

    Assign the Task from the Groups.

    +

    UnAssign the Task from the Groups.

    @@ -672,7 +727,7 @@ Use the method TASK.AddScore() to add scores whe - TASK:onenterAssigned(Event, From, To) + TASK:onenterAssigned(Event, From, To, PlayerUnit, PlayerName)

    FSM function for a TASK

    @@ -781,7 +836,7 @@ true if Unit is part of the Task.

    -

    Assign the Taskto a Group.

    +

    Assign the Task to a Group.

    Parameter

      @@ -898,6 +953,20 @@ The CLIENT or UNIT of the Player aborting the Task.

      #boolean: true if Unit is part of the Task.

      +
    +
    +
    +
    + + + +TASK.Dispatcher + +
    +
    + + +
    @@ -1072,6 +1141,32 @@ The Task Name

    Functional.Scoring#SCORING: Scoring

    + +
    +
    +
    + + +TASK:GetStateMachine(TaskUnit) + +
    +
    + +

    Gets the FiniteStateMachine of Task with key TaskUnit

    + +

    Parameter

    + +

    Return value

    + +

    Core.Fsm#FSM_PROCESS:

    + +
    @@ -1145,13 +1240,21 @@ TaskType

    -TASK:GetUnitProcess() +TASK:GetUnitProcess(TaskUnit)

    Get the Task FSM Process Template

    +

    Parameter

    +
      +
    • + +

      TaskUnit :

      + +
    • +

    Return value

    Core.Fsm#FSM_PROCESS:

    @@ -1253,6 +1356,19 @@ self

    #boolean:

    +
    +
    +
    +
    + + +TASK:IsStateAborted() + +
    +
    + +

    Is the Task status Aborted.

    +
    @@ -1271,6 +1387,19 @@ self

    + +TASK:IsStateCancelled() + +
    +
    + +

    Is the Task status Cancelled.

    + +
    +
    +
    +
    + TASK:IsStateFailed() @@ -1410,18 +1539,18 @@ true if Unit is part of the Task.

    -TASK.MenuTaskAbort(MenuParam) +TASK:MenuTaskAbort(TaskGroup)
    - +

    Report the task status.

    Parameter

    • -

      MenuParam :

      +

      TaskGroup :

    @@ -1431,18 +1560,18 @@ true if Unit is part of the Task.

    -TASK.MenuTaskStatus(MenuParam) +TASK:MenuTaskStatus(TaskGroup)
    - +

    Report the task status.

    Parameter

    • -

      MenuParam :

      +

      TaskGroup :

    @@ -1659,14 +1788,27 @@ The name of the Player.

    - -TASK:RemoveMenu() + +TASK:RemoveAssignedMenuForGroup(TaskGroup, MenuTime)
    -

    Remove the menu options of the Task to all the groups in the SetGroup.

    +

    Remove the assigned menu option of the Task for a Group.

    +

    Parameters

    +

    Return value

    #TASK: @@ -1677,20 +1819,51 @@ self

    - -TASK:RemoveMenuForGroup(TaskGroup) + +TASK:RemoveMenu(MenuTime) + +
    +
    + +

    Remove the menu options of the Task to all the groups in the SetGroup.

    + +

    Parameter

    +
      +
    • + +

      #number MenuTime :

      + +
    • +
    +

    Return value

    + +

    #TASK:

    + + +
    +
    +
    +
    + + +TASK:RemovePlannedMenuForGroup(TaskGroup, MenuTime)

    Remove the menu option of the Task for a Group.

    -

    Parameter

    +

    Parameters

    Return value

    @@ -1815,19 +1988,24 @@ self

    -TASK:SetAssignedMenuForGroup(TaskGroup) +TASK:SetAssignedMenuForGroup(TaskGroup, MenuTime)

    Set the assigned menu options of the Task.

    -

    Parameter

    +

    Parameters

    Return value

    @@ -1861,6 +2039,32 @@ self

    #TASK: self

    +
    +
    +
    +
    + + +TASK:SetDispatcher(Dispatcher) + +
    +
    + +

    Set dispatcher of a task

    + +

    Parameter

    + +

    Return value

    + +

    #TASK:

    + +
    @@ -1902,34 +2106,57 @@ self

    -TASK:SetMenu() +TASK:SetMenu(MenuTime)

    Set the menu options of the Task to all the groups in the SetGroup.

    +

    Parameter

    +
      +
    • + +

      #number MenuTime :

      + +
    • +
    +

    Return value

    + +

    #TASK:

    + +
    -TASK:SetMenuForGroup(TaskGroup) +TASK:SetMenuForGroup(MenuTime, TaskGroup)

    Set the Menu for a Group

    -

    Parameter

    +

    Parameters

    • +

      #number MenuTime :

      + +
    • +
    • +

      TaskGroup :

    +

    Return value

    + +

    #TASK:

    + +
    @@ -1957,7 +2184,7 @@ self

    -TASK:SetPlannedMenuForGroup(TaskGroup, MenuText) +TASK:SetPlannedMenuForGroup(TaskGroup, MenuText, MenuTime)
    @@ -1976,6 +2203,11 @@ self

    #string MenuText : The menu text.

    + +
  • + +

    #number MenuTime :

    +
  • Return value

    @@ -2005,7 +2237,7 @@ self

  • -

    Fsm :

    +

    Core.Fsm#FSM_PROCESS Fsm :

  • @@ -2097,6 +2329,19 @@ Fsm#FSM_PROCESS

    + +TASK:StateAborted() + +
    +
    + +

    Sets a Task to status Aborted.

    + +
    +
    +
    +
    + TASK:StateAssigned() @@ -2110,6 +2355,19 @@ Fsm#FSM_PROCESS

    + +TASK:StateCancelled() + +
    +
    + +

    Sets a Task to status Cancelled.

    + +
    +
    +
    +
    + TASK:StateFailed() @@ -2270,6 +2528,27 @@ Fsm#FSM_PROCESS

    +
    +
    +
    +
    + + +TASK:UnAssignFromGroup(TaskGroup) + +
    +
    + +

    UnAssign the Task from a Group.

    + +

    Parameter

    +
      +
    • + +

      TaskGroup :

      + +
    • +
    @@ -2281,7 +2560,7 @@ Fsm#FSM_PROCESS

    -

    Assign the Task from the Groups.

    +

    UnAssign the Task from the Groups.

    @@ -2488,7 +2767,7 @@ self

    -TASK:onenterAssigned(Event, From, To) +TASK:onenterAssigned(Event, From, To, PlayerUnit, PlayerName)
    @@ -2511,6 +2790,16 @@ self

    #string To :

    + +
  • + +

    PlayerUnit :

    + +
  • +
  • + +

    PlayerName :

    +
  • diff --git a/docs/Documentation/Task_A2G.html b/docs/Documentation/Task_A2G.html index 97aa39bb4..46c91e395 100644 --- a/docs/Documentation/Task_A2G.html +++ b/docs/Documentation/Task_A2G.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -72,27 +73,84 @@

    Module Task_A2G

    -

    (AI) (SP) (MP) Tasking for Air to Ground Processes.

    +

    Tasking - The TASK_A2G models tasks for players in Air to Ground engagements.

    -

    1) #TASK_A2G class, extends Task#TASK

    -

    The #TASK_A2G class defines a CAS or BAI task of a Set of Target Units, -located at a Target Zone, based on the tasking capabilities defined in Task#TASK. +

    Banner Image

    + + +

    1) TaskA2G#TASKA2G class, extends Task#TASK

    + +

    The #TASK_A2G class defines Air To Ground tasks for a Set of Target Units, +based on the tasking capabilities defined in Task#TASK. The TASK_A2G is implemented using a Statemachine#FSM_TASK, and has the following statuses:

    • None: Start of the process
    • -
    • Planned: The SEAD task is planned. Upon Planned, the sub-process ProcessFsm.Assign#ACTASSIGN_ACCEPT is started to accept the task.
    • -
    • Assigned: The SEAD task is assigned to a Group#GROUP. Upon Assigned, the sub-process ProcessFsm.Route#ACTROUTE is started to route the active Units in the Group to the attack zone.
    • -
    • Success: The SEAD task is successfully completed. Upon Success, the sub-process ProcessSEAD#PROCESSSEAD is started to follow-up successful SEADing of the targets assigned in the task.
    • -
    • Failed: The SEAD task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
    • +
    • Planned: The A2G task is planned.
    • +
    • Assigned: The A2G task is assigned to a Group#GROUP.
    • +
    • Success: The A2G task is successfully completed.
    • +
    • Failed: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
    +

    1.1) Set the scoring of achievements in an A2G attack.

    + +

    Scoring or penalties can be given in the following circumstances:

    + + + +

    2) TaskA2G#TASKSEAD class, extends TaskA2G#TASKA2G

    + +

    The #TASK_SEAD class defines a SEAD task for a Set of Target Units.

    +
    -

    Authors: FlightControl - Design and Programming

    +

    3) TaskA2G#TASKCAS class, extends TaskA2G#TASKA2G

    +

    The #TASK_CAS class defines a CAS task for a Set of Target Units.

    + +
    + +

    4) TaskA2G#TASKBAI class, extends TaskA2G#TASKA2G

    + +

    The #TASK_BAI class defines a BAI task for a Set of Target Units.

    + +
    + +

    API CHANGE HISTORY

    + +

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    + +
      +
    • Added parts are expressed in bold type face.
    • +
    • Removed parts are expressed in italic type face.
    • +
    + +

    Hereby the change log:

    + +

    2017-03-09: Revised version.

    + +
    + +

    AUTHORS and CONTRIBUTIONS

    + +

    Contributions:

    + +
      +
    • [WingThor]: Concept, Advice & Testing.
    • +
    + +

    Authors:

    + +
      +
    • FlightControl: Concept, Design & Programming. +
    • +

    Global(s)

    @@ -100,6 +158,24 @@ The TASK_A2G is implemented using a Stat + + + + + + + + + + + +
    TASK_A2G +
    TASK_BAI + +
    TASK_CAS + +
    TASK_SEAD +
    @@ -118,9 +194,147 @@ The TASK_A2G is implemented using a Stat - TASK_A2G:New(Mission, SetGroup, TaskName, TaskType, UnitSetTargets, TargetZone, TargetSetUnit, FACUnit) + TASK_A2G:GetRendezVousPointVec2(TaskUnit) + + + + + + TASK_A2G:GetRendezVousZone(TaskUnit) + + + + + + TASK_A2G:GetTargetPointVec2(TaskUnit) + + + + + + TASK_A2G:GetTargetZone(TaskUnit) + + + + + + TASK_A2G:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit, TaskType)

    Instantiates a new TASK_A2G.

    + + + + TASK_A2G:SetPenaltyOnFailed(Text, Penalty, TaskUnit) + +

    Set a penalty when the A2G attack has failed.

    + + + + TASK_A2G:SetRendezVousPointVec2(RendezVousPointVec2, RendezVousRange, TaskUnit) + + + + + + TASK_A2G:SetRendezVousZone(RendezVousZone, TaskUnit) + + + + + + TASK_A2G:SetScoreOnDestroy(Text, Score, TaskUnit) + +

    Set a score when a target in scope of the A2G attack, has been destroyed .

    + + + + TASK_A2G:SetScoreOnSuccess(Text, Score, TaskUnit) + +

    Set a score when all the targets in scope of the A2G attack, have been destroyed.

    + + + + TASK_A2G:SetTargetPointVec2(TargetPointVec2, TaskUnit) + + + + + + TASK_A2G:SetTargetZone(TargetZone, TaskUnit) + + + + + + TASK_A2G.TargetSetUnit + + + + + + +

    Type TASK_BAI

    + + + + + + + + + + + + + +
    TASK_BAI.ClassName + +
    TASK_BAI:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit) +

    Instantiates a new TASK_BAI.

    +
    TASK_BAI.TargetSetUnit + +
    + +

    Type TASK_CAS

    + + + + + + + + + + + + + +
    TASK_CAS.ClassName + +
    TASK_CAS:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit) +

    Instantiates a new TASK_CAS.

    +
    TASK_CAS.TargetSetUnit + +
    + +

    Type TASK_SEAD

    + + + + + + + + + + + +
    TASK_SEAD.ClassName + +
    TASK_SEAD:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit) +

    Instantiates a new TASK_SEAD.

    +
    TASK_SEAD.TargetSetUnit +
    @@ -138,10 +352,54 @@ The TASK_A2G is implemented using a Stat + +
    +
    +
    + + #TASK_BAI + +TASK_BAI + +
    +
    + + + +
    +
    +
    +
    + + #TASK_CAS + +TASK_CAS + +
    +
    + + + +
    +
    +
    +
    + + #TASK_SEAD + +TASK_SEAD + +
    +
    + + +

    Type Task_A2G

    +

    Type FSM_PROCESS

    +

    Type TASK_A2G

    The TASK_A2G class

    @@ -172,13 +430,127 @@ The TASK_A2G is implemented using a Stat + + +
    +
    + + +TASK_A2G:GetRendezVousPointVec2(TaskUnit) + +
    +
    + + + +

    Parameter

    + +

    Return values

    +
      +
    1. + +

      Core.Point#POINT_VEC2: +The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map.

      + +
    2. +
    3. + +

      #number: +The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.

      + +
    4. +
    +
    +
    +
    +
    + + +TASK_A2G:GetRendezVousZone(TaskUnit) + +
    +
    + + + +

    Parameter

    + +

    Return value

    + +

    Core.Zone#ZONE_BASE: +The Zone object where the RendezVous is located on the map.

    + +
    +
    +
    +
    + + +TASK_A2G:GetTargetPointVec2(TaskUnit) + +
    +
    + + + +

    Parameter

    + +

    Return value

    + +

    Core.Point#POINT_VEC2: +The PointVec2 object where the Target is located on the map.

    + +
    +
    +
    +
    + + +TASK_A2G:GetTargetZone(TaskUnit) + +
    +
    + + + +

    Parameter

    + +

    Return value

    + +

    Core.Zone#ZONE_BASE: +The Zone object where the Target is located on the map.

    +
    -TASK_A2G:New(Mission, SetGroup, TaskName, TaskType, UnitSetTargets, TargetZone, TargetSetUnit, FACUnit) +TASK_A2G:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit, TaskType)
    @@ -206,18 +578,20 @@ The name of the Task.

  • -

    #string TaskType : -BAI or CAS

    - -
  • -
  • -

    Set#SET_UNIT UnitSetTargets :

  • -

    Core.Zone#ZONE_BASE TargetZone :

    +

    #number TargetDistance : +The distance to Target when the Player is considered to have "arrived" at the engagement range.

    + +
  • +
  • + +

    Core.Zone#ZONE_BASE TargetZone : +The target zone, if known. +If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be.

  • @@ -227,7 +601,7 @@ BAI or CAS

  • -

    FACUnit :

    +

    TaskType :

  • @@ -236,6 +610,533 @@ BAI or CAS

    #TASK_A2G: self

    +
    +
    +
    +
    + + +TASK_A2G:SetPenaltyOnFailed(Text, Penalty, TaskUnit) + +
    +
    + +

    Set a penalty when the A2G attack has failed.

    + +

    Parameters

    +
      +
    • + +

      #string Text : +The text to display to the player, when the A2G attack has failed.

      + +
    • +
    • + +

      #number Penalty : +The penalty in points.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK_A2G:

    + + +
    +
    +
    +
    + + +TASK_A2G:SetRendezVousPointVec2(RendezVousPointVec2, RendezVousRange, TaskUnit) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      Core.Point#POINT_VEC2 RendezVousPointVec2 : +The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map.

      + +
    • +
    • + +

      #number RendezVousRange : +The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +
    +
    +
    +
    + + +TASK_A2G:SetRendezVousZone(RendezVousZone, TaskUnit) + +
    +
    + + + +

    Parameters

    + +
    +
    +
    +
    + + +TASK_A2G:SetScoreOnDestroy(Text, Score, TaskUnit) + +
    +
    + +

    Set a score when a target in scope of the A2G attack, has been destroyed .

    + +

    Parameters

    +
      +
    • + +

      #string Text : +The text to display to the player, when the target has been destroyed.

      + +
    • +
    • + +

      #number Score : +The score in points.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK_A2G:

    + + +
    +
    +
    +
    + + +TASK_A2G:SetScoreOnSuccess(Text, Score, TaskUnit) + +
    +
    + +

    Set a score when all the targets in scope of the A2G attack, have been destroyed.

    + +

    Parameters

    +
      +
    • + +

      #string Text : +The text to display to the player, when all targets hav been destroyed.

      + +
    • +
    • + +

      #number Score : +The score in points.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK_A2G:

    + + +
    +
    +
    +
    + + +TASK_A2G:SetTargetPointVec2(TargetPointVec2, TaskUnit) + +
    +
    + + + +

    Parameters

    + +
    +
    +
    +
    + + +TASK_A2G:SetTargetZone(TargetZone, TaskUnit) + +
    +
    + + + +

    Parameters

    + +
    +
    +
    +
    + + Set#SET_UNIT + +TASK_A2G.TargetSetUnit + +
    +
    + + + +
    +
    + +

    Type TASK_BAI

    + +

    The TASK_BAI class

    + +

    Field(s)

    +
    +
    + + #string + +TASK_BAI.ClassName + +
    +
    + + + +
    +
    +
    +
    + + +TASK_BAI:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit) + +
    +
    + +

    Instantiates a new TASK_BAI.

    + +

    Parameters

    +
      +
    • + +

      Tasking.Mission#MISSION Mission :

      + +
    • +
    • + +

      Set#SET_GROUP SetGroup : +The set of groups for which the Task can be assigned.

      + +
    • +
    • + +

      #string TaskName : +The name of the Task.

      + +
    • +
    • + +

      Set#SET_UNIT UnitSetTargets :

      + +
    • +
    • + +

      #number TargetDistance : +The distance to Target when the Player is considered to have "arrived" at the engagement range.

      + +
    • +
    • + +

      Core.Zone#ZONE_BASE TargetZone : +The target zone, if known. +If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be.

      + +
    • +
    • + +

      TargetSetUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK_BAI: +self

    + +
    +
    +
    +
    + + Set#SET_UNIT + +TASK_BAI.TargetSetUnit + +
    +
    + + + +
    +
    + +

    Type TASK_CAS

    + +

    The TASK_CAS class

    + +

    Field(s)

    +
    +
    + + #string + +TASK_CAS.ClassName + +
    +
    + + + +
    +
    +
    +
    + + +TASK_CAS:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit) + +
    +
    + +

    Instantiates a new TASK_CAS.

    + +

    Parameters

    +
      +
    • + +

      Tasking.Mission#MISSION Mission :

      + +
    • +
    • + +

      Set#SET_GROUP SetGroup : +The set of groups for which the Task can be assigned.

      + +
    • +
    • + +

      #string TaskName : +The name of the Task.

      + +
    • +
    • + +

      Set#SET_UNIT UnitSetTargets :

      + +
    • +
    • + +

      #number TargetDistance : +The distance to Target when the Player is considered to have "arrived" at the engagement range.

      + +
    • +
    • + +

      Core.Zone#ZONE_BASE TargetZone : +The target zone, if known. +If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be.

      + +
    • +
    • + +

      TargetSetUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK_CAS: +self

    + +
    +
    +
    +
    + + Set#SET_UNIT + +TASK_CAS.TargetSetUnit + +
    +
    + + + +
    +
    + +

    Type TASK_SEAD

    + +

    The TASK_SEAD class

    + +

    Field(s)

    +
    +
    + + #string + +TASK_SEAD.ClassName + +
    +
    + + + +
    +
    +
    +
    + + +TASK_SEAD:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit) + +
    +
    + +

    Instantiates a new TASK_SEAD.

    + +

    Parameters

    +
      +
    • + +

      Tasking.Mission#MISSION Mission :

      + +
    • +
    • + +

      Set#SET_GROUP SetGroup : +The set of groups for which the Task can be assigned.

      + +
    • +
    • + +

      #string TaskName : +The name of the Task.

      + +
    • +
    • + +

      Set#SET_UNIT UnitSetTargets :

      + +
    • +
    • + +

      #number TargetDistance : +The distance to Target when the Player is considered to have "arrived" at the engagement range.

      + +
    • +
    • + +

      Core.Zone#ZONE_BASE TargetZone : +The target zone, if known. +If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be.

      + +
    • +
    • + +

      TargetSetUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK_SEAD: +self

    + +
    +
    +
    +
    + + Set#SET_UNIT + +TASK_SEAD.TargetSetUnit + +
    +
    + + +
    diff --git a/docs/Documentation/Task_A2G_Dispatcher.html b/docs/Documentation/Task_A2G_Dispatcher.html new file mode 100644 index 000000000..d3f800748 --- /dev/null +++ b/docs/Documentation/Task_A2G_Dispatcher.html @@ -0,0 +1,536 @@ + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +

    Module Task_A2G_Dispatcher

    + +

    Tasking - The TASKA2GDISPATCHER creates and manages player TASK_A2G tasks based on detected targets.

    + + + +
    + +

    1) #TASKA2GDISPATCHER class, extends #DETECTION_MANAGER

    + +

    The #TASKA2GDISPATCHER class implements the dynamic dispatching of tasks upon groups of detected units determined a Set of FAC (groups). +The FAC will detect units, will group them, and will dispatch Tasks to groups. Depending on the type of target detected, different tasks will be dispatched. +Find a summary below describing for which situation a task type is created:

    + +
      +
    • CAS Task: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter.
    • +
    • BAI Task: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter.
    • +
    • SEAD Task: Is created when there are enemy ground units wihtin range of the FAC, with air search radars.
    • +
    + +

    Other task types will follow...

    + +

    3.1) TASKA2GDISPATCHER constructor:

    +

    The TASKA2GDISPATCHER.New() method creates a new TASKA2GDISPATCHER instance.

    + +
    + +

    API CHANGE HISTORY

    + +

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    + +
      +
    • Added parts are expressed in bold type face.
    • +
    • Removed parts are expressed in italic type face.
    • +
    + +

    Hereby the change log:

    + +

    2017-03-09: Initial class and API.

    + +
    + +

    AUTHORS and CONTRIBUTIONS

    + +

    Contributions:

    + +

    Authors:

    + +
      +
    • FlightControl: Concept, Design & Programming.
    • +
    + + +

    Global(s)

    + + + + + +
    TASK_A2G_DISPATCHER + +
    +

    Type TASK_A2G_DISPATCHER

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TASK_A2G_DISPATCHER.ClassName + +
    TASK_A2G_DISPATCHER.Detection +

    The DETECTION_BASE object that is used to report the detected objects.

    +
    TASK_A2G_DISPATCHER:EvaluateBAI(DetectedItem, FriendlyCoalition) +

    Creates a BAI task when there are targets for it.

    +
    TASK_A2G_DISPATCHER:EvaluateCAS(DetectedItem) +

    Creates a CAS task when there are targets for it.

    +
    TASK_A2G_DISPATCHER:EvaluateRemoveTask(Mission, Task, DetectedItem) +

    Evaluates the removal of the Task from the Mission.

    +
    TASK_A2G_DISPATCHER:EvaluateSEAD(DetectedItem) +

    Creates a SEAD task when there are targets for it.

    +
    TASK_A2G_DISPATCHER.Mission + +
    TASK_A2G_DISPATCHER:New(Mission, SetGroup, Detection) +

    TASKA2GDISPATCHER constructor.

    +
    TASK_A2G_DISPATCHER:OnAfterAssign(From, Event, To, Task, TaskUnit, PlayerName) +

    OnAfter Transition Handler for Event Assign.

    +
    TASK_A2G_DISPATCHER:ProcessDetected(Detection) +

    Assigns tasks in relation to the detected items to the Set#SET_GROUP.

    +
    TASK_A2G_DISPATCHER.SetGroup +

    The groups to which the FAC will report to.

    +
    + +

    Global(s)

    +
    +
    + + #TASK_A2G_DISPATCHER + +TASK_A2G_DISPATCHER + +
    +
    + + + +
    +
    +

    Type Task_A2G_Dispatcher

    + +

    Type TASK_A2G_DISPATCHER

    + +

    TASKA2GDISPATCHER class.

    + +

    Field(s)

    +
    +
    + + #string + +TASK_A2G_DISPATCHER.ClassName + +
    +
    + + + +
    +
    +
    +
    + + Functional.Detection#DETECTION_BASE + +TASK_A2G_DISPATCHER.Detection + +
    +
    + +

    The DETECTION_BASE object that is used to report the detected objects.

    + +
    +
    +
    +
    + + +TASK_A2G_DISPATCHER:EvaluateBAI(DetectedItem, FriendlyCoalition) + +
    +
    + +

    Creates a BAI task when there are targets for it.

    + +

    Parameters

    + +

    Return value

    + +

    Tasking.Task#TASK:

    + + +
    +
    +
    +
    + + +TASK_A2G_DISPATCHER:EvaluateCAS(DetectedItem) + +
    +
    + +

    Creates a CAS task when there are targets for it.

    + +

    Parameter

    + +

    Return value

    + +

    Tasking.Task#TASK:

    + + +
    +
    +
    +
    + + +TASK_A2G_DISPATCHER:EvaluateRemoveTask(Mission, Task, DetectedItem) + +
    +
    + +

    Evaluates the removal of the Task from the Mission.

    + + +

    Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".

    + +

    Parameters

    + +

    Return value

    + +

    Tasking.Task#TASK:

    + + +
    +
    +
    +
    + + +TASK_A2G_DISPATCHER:EvaluateSEAD(DetectedItem) + +
    +
    + +

    Creates a SEAD task when there are targets for it.

    + +

    Parameter

    + +

    Return values

    +
      +
    1. + +

      Set#SET_UNIT: +TargetSetUnit: The target set of units.

      + +
    2. +
    3. + +

      #nil: +If there are no targets to be set.

      + +
    4. +
    +
    +
    +
    +
    + + Tasking.Mission#MISSION + +TASK_A2G_DISPATCHER.Mission + +
    +
    + + + +
    +
    +
    +
    + + +TASK_A2G_DISPATCHER:New(Mission, SetGroup, Detection) + +
    +
    + +

    TASKA2GDISPATCHER constructor.

    + +

    Parameters

    + +

    Return value

    + +

    #TASKA2GDISPATCHER: +self

    + +
    +
    +
    +
    + + +TASK_A2G_DISPATCHER:OnAfterAssign(From, Event, To, Task, TaskUnit, PlayerName) + +
    +
    + +

    OnAfter Transition Handler for Event Assign.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    • + +

      Tasking.TaskA2G#TASKA2G Task :

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    • + +

      #string PlayerName :

      + +
    • +
    +
    +
    +
    +
    + + +TASK_A2G_DISPATCHER:ProcessDetected(Detection) + +
    +
    + +

    Assigns tasks in relation to the detected items to the Set#SET_GROUP.

    + +

    Parameter

    + +

    Return value

    + +

    #boolean: +Return true if you want the task assigning to continue... false will cancel the loop.

    + +
    +
    +
    +
    + + Set#SET_GROUP + +TASK_A2G_DISPATCHER.SetGroup + +
    +
    + +

    The groups to which the FAC will report to.

    + +
    +
    + +
    + +
    + + diff --git a/docs/Documentation/Task_PICKUP.html b/docs/Documentation/Task_PICKUP.html index fd8241229..fe83f60db 100644 --- a/docs/Documentation/Task_PICKUP.html +++ b/docs/Documentation/Task_PICKUP.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Task_SEAD.html b/docs/Documentation/Task_SEAD.html index e443fdc31..a998527ee 100644 --- a/docs/Documentation/Task_SEAD.html +++ b/docs/Documentation/Task_SEAD.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • diff --git a/docs/Documentation/Unit.html b/docs/Documentation/Unit.html index 39cf4d1d7..097aa6cf3 100644 --- a/docs/Documentation/Unit.html +++ b/docs/Documentation/Unit.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -72,97 +73,36 @@

    Module Unit

    -

    This module contains the UNIT class.

    +

    Wrapper - UNIT is a wrapper class for the DCS Class Unit.

    -

    1) #UNIT class, extends Controllable#CONTROLLABLE

    +
    +

    The #UNIT class is a wrapper class to handle the DCS Unit objects:

    • Support all DCS Unit APIs.
    • Enhance with Unit specific APIs not in the DCS Unit API set.
    • Handle local Unit Controller.
    • -
    • Manage the "state" of the DCS Unit.
    • +
    • Manage the "state" of the DCS Unit. +
    - -

    1.1) UNIT reference methods

    -

    For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _DATABASE object. -This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Unit objects are spawned (using the SPAWN class).

    - -

    The UNIT class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference -using the DCS Unit or the DCS UnitName.

    - -

    Another thing to know is that UNIT objects do not "contain" the DCS Unit object. -The UNIT methods will reference the DCS Unit object by name when it is needed during API execution. -If the DCS Unit object does not exist or is nil, the UNIT methods will return nil and log an exception in the DCS.log file.

    - -

    The UNIT class provides the following functions to retrieve quickly the relevant UNIT instance:

    - -
      -
    • UNIT.Find(): Find a UNIT instance from the _DATABASE object using a DCS Unit object.
    • -
    • UNIT.FindByName(): Find a UNIT instance from the _DATABASE object using a DCS Unit name.
    • -
    - -

    IMPORTANT: ONE SHOULD NEVER SANATIZE these UNIT OBJECT REFERENCES! (make the UNIT object references nil).

    - -

    1.2) DCS UNIT APIs

    -

    The DCS Unit APIs are used extensively within MOOSE. The UNIT class has for each DCS Unit API a corresponding method. -To be able to distinguish easily in your code the difference between a UNIT API call and a DCS Unit API call, -the first letter of the method is also capitalized. So, by example, the DCS Unit method DCSWrapper.Unit#Unit.getName() -is implemented in the UNIT class as UNIT.GetName().

    - -

    1.3) Smoke, Flare Units

    -

    The UNIT class provides methods to smoke or flare units easily. -The UNIT.SmokeBlue(), UNIT.SmokeGreen(),UNIT.SmokeOrange(), UNIT.SmokeRed(), UNIT.SmokeRed() methods -will smoke the unit in the corresponding color. Note that smoking a unit is done at the current position of the DCS Unit. -When the DCS Unit moves for whatever reason, the smoking will still continue! -The UNIT.FlareGreen(), UNIT.FlareRed(), UNIT.FlareWhite(), UNIT.FlareYellow() -methods will fire off a flare in the air with the corresponding color. Note that a flare is a one-off shot and its effect is of very short duration.

    - -

    1.4) Location Position, Point

    -

    The UNIT class provides methods to obtain the current point or position of the DCS Unit. -The UNIT.GetPointVec2(), UNIT.GetVec3() will obtain the current location of the DCS Unit in a Vec2 (2D) or a point in a Vec3 (3D) vector respectively. -If you want to obtain the complete 3D position including ori�ntation and direction vectors, consult the UNIT.GetPositionVec3() method respectively.

    - -

    1.5) Test if alive

    -

    The UNIT.IsAlive(), UNIT.IsActive() methods determines if the DCS Unit is alive, meaning, it is existing and active.

    - -

    1.6) Test for proximity

    -

    The UNIT class contains methods to test the location or proximity against zones or other objects.

    - -

    1.6.1) Zones

    -

    To test whether the Unit is within a zone, use the UNIT.IsInZone() or the UNIT.IsNotInZone() methods. Any zone can be tested on, but the zone must be derived from Zone#ZONE_BASE.

    - -

    1.6.2) Units

    -

    Test if another DCS Unit is within a given radius of the current DCS Unit, use the UNIT.OtherUnitInRadius() method.

    - -

    Global(s)

    - - - -
    UNIT +

    UNIT class, extends Controllable#CONTROLLABLE

    -
    i - +

    For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _DATABASE object.

    Type UNIT

    - - - - + + + + @@ -326,6 +272,12 @@ If you want to obtain the complete 3D position including ori� + + + + @@ -465,48 +417,74 @@ If you want to obtain the complete 3D position including ori�
    +

    UNIT class, extends Controllable#CONTROLLABLE

    - -
    - -
    -
    - - #number - -i - -
    -
    - - +

    For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _DATABASE object.

    -

    Remove obscolete units from the group structure

    +

    This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Unit objects are spawned (using the SPAWN class).

    + +

    The UNIT class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference +using the DCS Unit or the DCS UnitName.

    + +

    Another thing to know is that UNIT objects do not "contain" the DCS Unit object. +The UNIT methods will reference the DCS Unit object by name when it is needed during API execution. +If the DCS Unit object does not exist or is nil, the UNIT methods will return nil and log an exception in the DCS.log file.

    + +

    The UNIT class provides the following functions to retrieve quickly the relevant UNIT instance:

    + +
      +
    • UNIT.Find(): Find a UNIT instance from the _DATABASE object using a DCS Unit object.
    • +
    • UNIT.FindByName(): Find a UNIT instance from the _DATABASE object using a DCS Unit name.
    • +
    + +

    IMPORTANT: ONE SHOULD NEVER SANATIZE these UNIT OBJECT REFERENCES! (make the UNIT object references nil).

    + +

    DCS UNIT APIs

    + +

    The DCS Unit APIs are used extensively within MOOSE. The UNIT class has for each DCS Unit API a corresponding method. +To be able to distinguish easily in your code the difference between a UNIT API call and a DCS Unit API call, +the first letter of the method is also capitalized. So, by example, the DCS Unit method DCSWrapper.Unit#Unit.getName() +is implemented in the UNIT class as UNIT.GetName().

    + +

    Smoke, Flare Units

    + +

    The UNIT class provides methods to smoke or flare units easily. +The UNIT.SmokeBlue(), UNIT.SmokeGreen(),UNIT.SmokeOrange(), UNIT.SmokeRed(), UNIT.SmokeRed() methods +will smoke the unit in the corresponding color. Note that smoking a unit is done at the current position of the DCS Unit. +When the DCS Unit moves for whatever reason, the smoking will still continue! +The UNIT.FlareGreen(), UNIT.FlareRed(), UNIT.FlareWhite(), UNIT.FlareYellow() +methods will fire off a flare in the air with the corresponding color. Note that a flare is a one-off shot and its effect is of very short duration.

    + +

    Location Position, Point

    + +

    The UNIT class provides methods to obtain the current point or position of the DCS Unit. +The UNIT.GetPointVec2(), UNIT.GetVec3() will obtain the current location of the DCS Unit in a Vec2 (2D) or a point in a Vec3 (3D) vector respectively. +If you want to obtain the complete 3D position including ori�ntation and direction vectors, consult the UNIT.GetPositionVec3() method respectively.

    + +

    Test if alive

    + +

    The UNIT.IsAlive(), UNIT.IsActive() methods determines if the DCS Unit is alive, meaning, it is existing and active.

    + +

    Test for proximity

    + +

    The UNIT class contains methods to test the location or proximity against zones or other objects.

    + +

    Zones

    + +

    To test whether the Unit is within a zone, use the UNIT.IsInZone() or the UNIT.IsNotInZone() methods. Any zone can be tested on, but the zone must be derived from Zone#ZONE_BASE.

    + +

    Units

    + +

    Test if another DCS Unit is within a given radius of the current DCS Unit, use the UNIT.OtherUnitInRadius() method.

    +

    Type Unit

    Type UNIT

    - -

    The UNIT class

    - -

    Field(s)

    -
    -
    - - #string - -UNIT.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    @@ -693,6 +671,24 @@ The DCS Unit is not existing or alive.

    + +UNIT:GetCategoryName() + +
    +
    + +

    Returns the category name of the #UNIT.

    + +

    Return value

    + +

    #string: +Category name = Helicopter, Airplane, Ground Unit, Ship

    + +
    +
    +
    +
    + UNIT:GetDCSObject() @@ -1215,6 +1211,45 @@ Air category evaluation result.

    + +UNIT:IsAlive() + +
    +
    + +

    Returns if the Unit is alive.

    + + +

    If the Unit is not alive, nil is returned.
    +If the Unit is alive and active, true is returned.
    +If the Unit is alive but not active, false is returned.

    + +

    Return values

    +
      +
    1. + +

      #boolean: +true if Unit is alive and active.

      + +
    2. +
    3. + +

      #boolean: +false if Unit is alive but not active.

      + +
    4. +
    5. + +

      #nil: +if the Unit is not existing or is not alive.

      + +
    6. +
    +
    +
    +
    +
    + UNIT:IsFriendly(FriendlyCoalition) diff --git a/docs/Documentation/Utils.html b/docs/Documentation/Utils.html index 33c940e11..59585ad25 100644 --- a/docs/Documentation/Utils.html +++ b/docs/Documentation/Utils.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Zone.html b/docs/Documentation/Zone.html index 9c92cf3da..86c4fee62 100644 --- a/docs/Documentation/Zone.html +++ b/docs/Documentation/Zone.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -76,6 +77,8 @@ +

    Banner Image

    +

    There are essentially two core functions that zones accomodate:

    @@ -99,148 +102,16 @@

    Each of these ZONE classes have a zone name, and specific parameters defining the zone type:

      -
    • Zone#ZONE_BASE: The ZONE_BASE class defining the base for all other zone classes.
    • -
    • Zone#ZONE_RADIUS: The ZONE_RADIUS class defined by a zone name, a location and a radius.
    • -
    • Zone#ZONE: The ZONE class, defined by the zone name as defined within the Mission Editor.
    • -
    • Zone#ZONE_UNIT: The ZONE_UNIT class defines by a zone around a Unit#UNIT with a radius.
    • -
    • Zone#ZONE_GROUP: The ZONE_GROUP class defines by a zone around a Group#GROUP with a radius.
    • -
    • Zone#ZONE_POLYGON: The ZONE_POLYGON class defines by a sequence of Group#GROUP waypoints within the Mission Editor, forming a polygon.
    • +
    • #ZONE_BASE: The ZONE_BASE class defining the base for all other zone classes.
    • +
    • #ZONE_RADIUS: The ZONE_RADIUS class defined by a zone name, a location and a radius.
    • +
    • #ZONE: The ZONE class, defined by the zone name as defined within the Mission Editor.
    • +
    • #ZONE_UNIT: The ZONE_UNIT class defines by a zone around a Unit#UNIT with a radius.
    • +
    • #ZONE_GROUP: The ZONE_GROUP class defines by a zone around a Group#GROUP with a radius.
    • +
    • #ZONE_POLYGON: The ZONE_POLYGON class defines by a sequence of Group#GROUP waypoints within the Mission Editor, forming a polygon.

    -

    1) Zone#ZONE_BASE class, extends Base#BASE

    - -

    This class is an abstract BASE class for derived classes, and is not meant to be instantiated.

    - -

    1.1) Each zone has a name:

    - - - -

    1.2) Each zone implements two polymorphic functions defined in Zone#ZONE_BASE:

    - - - -

    1.3) A zone has a probability factor that can be set to randomize a selection between zones:

    - -
      -
    • ZONE_BASE.SetRandomizeProbability(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% )
    • -
    • ZONE_BASE.GetRandomizeProbability(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% )
    • -
    • ZONE_BASE.GetZoneMaybe(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate.
    • -
    - -

    1.4) A zone manages Vectors:

    - - - -

    1.5) A zone has a bounding square:

    - - - -

    1.6) A zone can be marked:

    - - - -
    - -

    2) Zone#ZONE_RADIUS class, extends Zone#ZONE_BASE

    - -

    The ZONERADIUS class defined by a zone name, a location and a radius. -This class implements the inherited functions from Core.Zone#ZONEBASE taking into account the own zone format and properties.

    - -

    2.1) Zone#ZONE_RADIUS constructor

    - - - -

    2.2) Manage the radius of the zone

    - - - -

    2.3) Manage the location of the zone

    - - - -

    2.4) Zone point randomization

    - -

    Various functions exist to find random points within the zone.

    - - - -
    - -

    3) Zone#ZONE class, extends Zone#ZONE_RADIUS

    - -

    The ZONE class, defined by the zone name as defined within the Mission Editor. -This class implements the inherited functions from {Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties.

    - -
    - -

    4) Zone#ZONE_UNIT class, extends Zone#ZONE_RADIUS

    - -

    The ZONE_UNIT class defined by a zone around a Unit#UNIT with a radius. -This class implements the inherited functions from Zone#ZONE_RADIUS taking into account the own zone format and properties.

    - -
    - -

    5) Zone#ZONE_GROUP class, extends Zone#ZONE_RADIUS

    - -

    The ZONE_GROUP class defines by a zone around a Group#GROUP with a radius. The current leader of the group defines the center of the zone. -This class implements the inherited functions from Zone#ZONE_RADIUS taking into account the own zone format and properties.

    - -
    - -

    6) Zone#ZONEPOLYGONBASE class, extends Zone#ZONE_BASE

    - -

    The ZONEPOLYGONBASE class defined by a sequence of Group#GROUP waypoints within the Mission Editor, forming a polygon. -This class implements the inherited functions from Zone#ZONE_RADIUS taking into account the own zone format and properties. -This class is an abstract BASE class for derived classes, and is not meant to be instantiated.

    - -

    6.1) Zone point randomization

    - -

    Various functions exist to find random points within the zone.

    - - - - -
    - -

    7) Zone#ZONE_POLYGON class, extends Zone#ZONEPOLYGONBASE

    - -

    The ZONE_POLYGON class defined by a sequence of Group#GROUP waypoints within the Mission Editor, forming a polygon. -This class implements the inherited functions from Zone#ZONE_RADIUS taking into account the own zone format and properties.

    - -
    -

    API CHANGE HISTORY

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    @@ -283,55 +154,63 @@ This class implements the inherited functions from ZONE
    UNIT.ClassName - -
    UNIT:Find(DCSUnit)

    Finds a UNIT from the _DATABASE using a DCSUnit object.

    @@ -214,6 +154,12 @@ If you want to obtain the complete 3D position including ori�
    UNIT:GetCallsign()

    Returns the Unit's callsign - the localized string.

    +
    UNIT:GetCategoryName() +

    Returns the category name of the #UNIT.

    UNIT:IsAir()

    Returns if the unit is of an air category.

    +
    UNIT:IsAlive() +

    Returns if the Unit is alive.

    +

    3) ZONE class, extends Zone#ZONE_RADIUS

    +

    The ZONE class, defined by the zone name as defined within the Mission Editor.

    ZONE_BASE +

    1) ZONE_BASE class, extends Base#BASE

    +

    This class is an abstract BASE class for derived classes, and is not meant to be instantiated.

    ZONE_GROUP +

    5) #ZONE_GROUP class, extends Zone#ZONE_RADIUS

    +

    The ZONE_GROUP class defines by a zone around a Group#GROUP with a radius.

    ZONE_POLYGON +

    7) ZONE_POLYGON class, extends Zone#ZONEPOLYGONBASE

    +

    The ZONE_POLYGON class defined by a sequence of Group#GROUP waypoints within the Mission Editor, forming a polygon.

    ZONE_POLYGON_BASE +

    6) ZONEPOLYGONBASE class, extends Zone#ZONE_BASE

    +

    The ZONEPOLYGONBASE class defined by a sequence of Group#GROUP waypoints within the Mission Editor, forming a polygon.

    ZONE_RADIUS +

    2) Zone#ZONE_RADIUS class, extends Zone#ZONE_BASE

    +

    The ZONE_RADIUS class defined by a zone name, a location and a radius.

    ZONE_UNIT +

    4) #ZONE_UNIT class, extends Zone#ZONE_RADIUS

    +

    The ZONE_UNIT class defined by a zone around a Unit#UNIT with a radius.

    Type ZONE

    - - - - - - - - @@ -363,12 +236,30 @@ This class implements the inherited functions from ZONE_BASE:GetName() + + + + + + + + + + + + @@ -381,6 +272,12 @@ This class implements the inherited functions from ZONE_BASE:GetVec2() + + + + @@ -470,12 +367,6 @@ This class implements the inherited functions from Type ZONE_GROUP
    ZONE.ClassName - -
    ZONE:New(ZoneName)

    Constructor of ZONE, taking the zone name.

    @@ -345,12 +224,6 @@ This class implements the inherited functions from ZONE_BASE:BoundZone()

    Bound the zone boundaries with a tires.

    -
    ZONE_BASE.ClassName -

    Returns the name of the zone.

    +
    ZONE_BASE:GetPointVec2(Height) +

    Returns a Point#POINT_VEC2 of the zone.

    +
    ZONE_BASE:GetPointVec3(Height) +

    Returns a Point#POINT_VEC3 of the zone.

    ZONE_BASE:GetRandomPointVec2()

    Define a random Point#POINT_VEC2 within the zone.

    +
    ZONE_BASE:GetRandomPointVec3() +

    Define a random Point#POINT_VEC3 within the zone.

    Returns the DCSTypes#Vec2 coordinate of the zone.

    +
    ZONE_BASE:GetVec3(Height) +

    Returns the DCSTypes#Vec3 of the zone.

    - - - -
    ZONE_GROUP.ClassName - -
    ZONE_GROUP:GetRandomVec2()

    Returns a random location within the zone of the Group.

    @@ -504,12 +395,6 @@ This class implements the inherited functions from Type ZONE_POLYGON - - - -
    ZONE_POLYGON.ClassName - -
    ZONE_POLYGON:New(ZoneName, ZoneGroup)

    Constructor to create a ZONE_POLYGON instance, taking the zone name and the name of the Group#GROUP defined within the Mission Editor.

    @@ -520,15 +405,9 @@ This class implements the inherited functions from Type ZONE_POLYGON_BASE - + - - - - @@ -590,15 +469,9 @@ This class implements the inherited functions from Type ZONE_RADIUS
    ZONE_POLYGON_BASE:BoundZone()ZONE_POLYGON_BASE:BoundZone(UnBound)

    Smokes the zone boundaries in a color.

    -
    ZONE_POLYGON_BASE.ClassName -
    - + - - - - @@ -696,12 +569,6 @@ This class implements the inherited functions from Type ZONE_UNIT
    ZONE_RADIUS:BoundZone(Points)ZONE_RADIUS:BoundZone(Points, UnBound, CountryID)

    Bounds the zone with tires.

    -
    ZONE_RADIUS.ClassName -
    - - - - @@ -214,7 +206,7 @@ and automatically engage any airborne enemies that are within a certain range or @@ -245,19 +237,13 @@ are design patterns allowing efficient (long-lasting) processes and workflows. - - - - @@ -282,6 +268,12 @@ are design patterns allowing efficient (long-lasting) processes and workflows.Mission + + + + @@ -312,6 +304,12 @@ are design patterns allowing efficient (long-lasting) processes and workflows.Process_Pickup + + + + @@ -335,7 +333,7 @@ are design patterns allowing efficient (long-lasting) processes and workflows. @@ -348,8 +346,6 @@ and create a CSV file logging the scoring events for use at team or squadron web
    -

    1) Scoring#SCORING class, extends Base#BASE

    -

    The #SCORING class administers the scoring of player achievements, and creates a CSV file logging the scoring events and results for use at team or squadron websites.

    @@ -363,7 +359,7 @@ and creates a CSV file logging the scoring events and results for use at team or @@ -402,25 +398,25 @@ and creates a CSV file logging the scoring events and results for use at team or + + + + - - - - diff --git a/docs/Documentation/routines.html b/docs/Documentation/routines.html index 3c5b6891b..c2949d324 100644 --- a/docs/Documentation/routines.html +++ b/docs/Documentation/routines.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Installation.md b/docs/Installation.md deleted file mode 100644 index 61fb78ce9..000000000 --- a/docs/Installation.md +++ /dev/null @@ -1,81 +0,0 @@ -# 2) Installation of the MOOSE Environment. - -This document describes how to install your MOOSE development environment, enhancing the mission design experience for your missions in DCS World. - -## 2.1) Install Eclipse LDT, a lua advanced editor - -Install [Eclipse LDT](https://eclipse.org/ldt) on your Windows 64 bit system. -This is a free lua editor based on the Eclipse ecosystem. -The advantage of LDT is that it greatly enhances your lua development environment with intellisense, better search capabilities etc. -You may have to install [java](https://www.java.com/en/download) first. -Ensure you install the **64-bit versions** of both Eclipse LDT and java! - -## 2.2) Get your MOOSE repository installed on your PC and linked with GITHUB - -### 2.2.1) Install GITHUB desktop - -Install [GITHUB](https://desktop.github.com) desktop. -We use GITHUB desktop to sync the moose repository to your system. - -### 2.2.2) Link the MOOSE repository - -Link the MOOSE repository on GITHUB to your freshly installed GITHUB desktop. -Do this by browing to the MOOSE repository at GITHUB, and select the green button **Clone or Download** -> **Open in Desktop**. -Specify a local directory on your PC where you want to store the MOOSE repository contents. -Sync the MOOSE repository to a defined local MOOSE directory on your PC using GITHUB desktop (press the sync button). - -### 2.2.3) Sync the Dcs folder in the MOOSE repository - -On your local MOOSE directory, execute the batch file [DCS_Folder_Sync.bat](https://github.com/FlightControl-Master/MOOSE/blob/master/DCS_Folder_Sync.bat). -This will sync the dcs folder in the MOOSE repository from the submodule DCS API. -The Dcs folder is what we call a GITHUB submodule, which needs to be synced separately. -You will be notified when you need to re-sync the Dcs folder through GITHUB channels. - -** As a result, you have installed the MOOSE repository on your PC, and it is fully synced. ** - -## 2.3) Configuration of the Eclipse LDT to work with MOOSE and activate your intellisense etc. - -The section explains how to setup your Eclipse LDT environment, link it with the MOOSE respository. -This will enable you to start developing mission scripts in lua, which will be fully intellisense enabled!!! - -### 2.3.1) Create a new **Workspace** in LDT. - -The LDT editor has a concept of "workspaces", this contains all your settings of your editing environment, like views, menu options etc. -I suggest you create a workspace at your user id, the default location when you first start LDT. - -1. Open Eclipse LDT. -2. Select the workspace to be stored at your user id. - -### 2.3.2) Create a new **Project** in LDT. - -Here we will create a new project called "Moose_Framework" in your LDT environment. -The project details are already defined within the MOOSE framework repository, which is installed on your local MOOSE directory on your PC. -We will link into that directory and load the Project properties. - -1. Select from the Menu: File -> New -> Lua Project. - -![LDT_New_Project](Installation/LDT_New_Project.JPG) - -2. A "New Project" dialog box is shown. - -![LDT_Project](Installation/LDT_Project.JPG) - -3. Type the Project Name: **Moose_Framework**. -4. In the sub-box "Project Contents", select the option Create Project at existing location (from existing source). -5. Browse to the local MOOSE directory (press on the Browse button) and select the root directory of your local MO.OSE directory on your PC. Press OK. -6. You're back at the "New Project" dialog box. Press the **Next** button below the dialog box. -__(All the other settings are by default ok)__. -7. You should see now a dialog box with the following properties. -Note that the Moose Development/Moose directory is flagged as the **Source Directory*. (It is listed totally on top.) -This is important because it will search in the files in this directory and sub directories for lua documentator enabled lua files. -This will enable the intellisense of the MOOSE repository! - -![LDT Finish](Installation/LDT_Moose_Framework_Finish.JPG) - -8. Press the **Finish** button. - -As a result, when you browse to the Script Explorer, you'll see the following: - -![LDT_Script_Explorer](Installation/LDT_Script_Explorer.JPG) - -**Congratulations! You have now setup your Moose_Framework project LDT environment!** diff --git a/docs/Installation/DCS_Triggers_Empty.JPG b/docs/Installation/DCS_Triggers_Empty.JPG new file mode 100644 index 000000000..2daab421e Binary files /dev/null and b/docs/Installation/DCS_Triggers_Empty.JPG differ diff --git a/docs/Installation/DCS_Triggers_Load_Mission_Add.JPG b/docs/Installation/DCS_Triggers_Load_Mission_Add.JPG new file mode 100644 index 000000000..68335a57b Binary files /dev/null and b/docs/Installation/DCS_Triggers_Load_Mission_Add.JPG differ diff --git a/docs/Installation/DCS_Triggers_Load_Mission_File_Added.JPG b/docs/Installation/DCS_Triggers_Load_Mission_File_Added.JPG new file mode 100644 index 000000000..7faa21e48 Binary files /dev/null and b/docs/Installation/DCS_Triggers_Load_Mission_File_Added.JPG differ diff --git a/docs/Installation/DCS_Triggers_Load_Mission_File_Select.JPG b/docs/Installation/DCS_Triggers_Load_Mission_File_Select.JPG new file mode 100644 index 000000000..34febf8c2 Binary files /dev/null and b/docs/Installation/DCS_Triggers_Load_Mission_File_Select.JPG differ diff --git a/docs/Installation/DCS_Triggers_Load_Moose_Add.JPG b/docs/Installation/DCS_Triggers_Load_Moose_Add.JPG new file mode 100644 index 000000000..b12c4d94d Binary files /dev/null and b/docs/Installation/DCS_Triggers_Load_Moose_Add.JPG differ diff --git a/docs/Installation/DCS_Triggers_Load_Moose_File_Added.JPG b/docs/Installation/DCS_Triggers_Load_Moose_File_Added.JPG new file mode 100644 index 000000000..56ee0e742 Binary files /dev/null and b/docs/Installation/DCS_Triggers_Load_Moose_File_Added.JPG differ diff --git a/docs/Installation/DCS_Triggers_Load_Moose_Select_File.JPG b/docs/Installation/DCS_Triggers_Load_Moose_Select_File.JPG new file mode 100644 index 000000000..21a8f1f21 Binary files /dev/null and b/docs/Installation/DCS_Triggers_Load_Moose_Select_File.JPG differ diff --git a/docs/Installation/GitHub_Clone.JPG b/docs/Installation/GitHub_Clone.JPG new file mode 100644 index 000000000..7ea187890 Binary files /dev/null and b/docs/Installation/GitHub_Clone.JPG differ diff --git a/docs/Installation/GitHub_Issue_example.JPG b/docs/Installation/GitHub_Issue_example.JPG new file mode 100644 index 000000000..4107ca45a Binary files /dev/null and b/docs/Installation/GitHub_Issue_example.JPG differ diff --git a/docs/Installation/LDT_Add_Folder.JPG b/docs/Installation/LDT_Add_Folder.JPG new file mode 100644 index 000000000..919f4821e Binary files /dev/null and b/docs/Installation/LDT_Add_Folder.JPG differ diff --git a/docs/Installation/LDT_Delete_Src.JPG b/docs/Installation/LDT_Delete_Src.JPG new file mode 100644 index 000000000..8093f335f Binary files /dev/null and b/docs/Installation/LDT_Delete_Src.JPG differ diff --git a/docs/Installation/LDT_Mission_Folder_Name.JPG b/docs/Installation/LDT_Mission_Folder_Name.JPG new file mode 100644 index 000000000..5d7f7d1b4 Binary files /dev/null and b/docs/Installation/LDT_Mission_Folder_Name.JPG differ diff --git a/docs/Installation/LDT_Mission_Lua_File_Name.JPG b/docs/Installation/LDT_Mission_Lua_File_Name.JPG new file mode 100644 index 000000000..a18694315 Binary files /dev/null and b/docs/Installation/LDT_Mission_Lua_File_Name.JPG differ diff --git a/docs/Installation/LDT_New_Lua_File.JPG b/docs/Installation/LDT_New_Lua_File.JPG new file mode 100644 index 000000000..376ca4453 Binary files /dev/null and b/docs/Installation/LDT_New_Lua_File.JPG differ diff --git a/docs/Installation/LDT_New_Project_Projects.JPG b/docs/Installation/LDT_New_Project_Projects.JPG new file mode 100644 index 000000000..6598fc253 Binary files /dev/null and b/docs/Installation/LDT_New_Project_Projects.JPG differ diff --git a/docs/Installation/LDT_Project_My_Missions.JPG b/docs/Installation/LDT_Project_My_Missions.JPG new file mode 100644 index 000000000..72613f549 Binary files /dev/null and b/docs/Installation/LDT_Project_My_Missions.JPG differ diff --git a/docs/Installation/LDT_Select_Moose_Framework.JPG b/docs/Installation/LDT_Select_Moose_Framework.JPG new file mode 100644 index 000000000..8252e05c9 Binary files /dev/null and b/docs/Installation/LDT_Select_Moose_Framework.JPG differ diff --git a/docs/Installation/MDES_Splash_Screen.JPG b/docs/Installation/MDES_Splash_Screen.JPG new file mode 100644 index 000000000..2084e4768 Binary files /dev/null and b/docs/Installation/MDES_Splash_Screen.JPG differ diff --git a/docs/Installation/MOOSE_Release_Cycle.JPG b/docs/Installation/MOOSE_Release_Cycle.JPG new file mode 100644 index 000000000..29e41a618 Binary files /dev/null and b/docs/Installation/MOOSE_Release_Cycle.JPG differ diff --git a/docs/Presentations/ACT_ACCOUNT/Dia1.JPG b/docs/Presentations/ACT_ACCOUNT/Dia1.JPG new file mode 100644 index 000000000..dbb082a48 Binary files /dev/null and b/docs/Presentations/ACT_ACCOUNT/Dia1.JPG differ diff --git a/docs/Presentations/DETECTION/Dia1.JPG b/docs/Presentations/DETECTION/Dia1.JPG new file mode 100644 index 000000000..568646fa4 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia1.JPG differ diff --git a/docs/Presentations/DETECTION/Dia10.JPG b/docs/Presentations/DETECTION/Dia10.JPG new file mode 100644 index 000000000..1d6cab399 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia10.JPG differ diff --git a/docs/Presentations/DETECTION/Dia11.JPG b/docs/Presentations/DETECTION/Dia11.JPG new file mode 100644 index 000000000..0ce4a6d8a Binary files /dev/null and b/docs/Presentations/DETECTION/Dia11.JPG differ diff --git a/docs/Presentations/DETECTION/Dia12.JPG b/docs/Presentations/DETECTION/Dia12.JPG new file mode 100644 index 000000000..befe2f3a3 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia12.JPG differ diff --git a/docs/Presentations/DETECTION/Dia13.JPG b/docs/Presentations/DETECTION/Dia13.JPG new file mode 100644 index 000000000..2b2e3182c Binary files /dev/null and b/docs/Presentations/DETECTION/Dia13.JPG differ diff --git a/docs/Presentations/DETECTION/Dia14.JPG b/docs/Presentations/DETECTION/Dia14.JPG new file mode 100644 index 000000000..ca35da8f9 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia14.JPG differ diff --git a/docs/Presentations/DETECTION/Dia15.JPG b/docs/Presentations/DETECTION/Dia15.JPG new file mode 100644 index 000000000..746860f9d Binary files /dev/null and b/docs/Presentations/DETECTION/Dia15.JPG differ diff --git a/docs/Presentations/DETECTION/Dia16.JPG b/docs/Presentations/DETECTION/Dia16.JPG new file mode 100644 index 000000000..957c00307 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia16.JPG differ diff --git a/docs/Presentations/DETECTION/Dia17.JPG b/docs/Presentations/DETECTION/Dia17.JPG new file mode 100644 index 000000000..a95e866c2 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia17.JPG differ diff --git a/docs/Presentations/DETECTION/Dia18.JPG b/docs/Presentations/DETECTION/Dia18.JPG new file mode 100644 index 000000000..98b861148 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia18.JPG differ diff --git a/docs/Presentations/DETECTION/Dia19.JPG b/docs/Presentations/DETECTION/Dia19.JPG new file mode 100644 index 000000000..02a90e680 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia19.JPG differ diff --git a/docs/Presentations/DETECTION/Dia2.JPG b/docs/Presentations/DETECTION/Dia2.JPG new file mode 100644 index 000000000..a3dcfd13b Binary files /dev/null and b/docs/Presentations/DETECTION/Dia2.JPG differ diff --git a/docs/Presentations/DETECTION/Dia20.JPG b/docs/Presentations/DETECTION/Dia20.JPG new file mode 100644 index 000000000..8268dd239 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia20.JPG differ diff --git a/docs/Presentations/DETECTION/Dia21.JPG b/docs/Presentations/DETECTION/Dia21.JPG new file mode 100644 index 000000000..0b299e044 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia21.JPG differ diff --git a/docs/Presentations/DETECTION/Dia22.JPG b/docs/Presentations/DETECTION/Dia22.JPG new file mode 100644 index 000000000..460fd1a19 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia22.JPG differ diff --git a/docs/Presentations/DETECTION/Dia23.JPG b/docs/Presentations/DETECTION/Dia23.JPG new file mode 100644 index 000000000..da9dc5a0d Binary files /dev/null and b/docs/Presentations/DETECTION/Dia23.JPG differ diff --git a/docs/Presentations/DETECTION/Dia24.JPG b/docs/Presentations/DETECTION/Dia24.JPG new file mode 100644 index 000000000..a521d0c09 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia24.JPG differ diff --git a/docs/Presentations/DETECTION/Dia25.JPG b/docs/Presentations/DETECTION/Dia25.JPG new file mode 100644 index 000000000..0206d8f96 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia25.JPG differ diff --git a/docs/Presentations/DETECTION/Dia26.JPG b/docs/Presentations/DETECTION/Dia26.JPG new file mode 100644 index 000000000..2d7109c90 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia26.JPG differ diff --git a/docs/Presentations/DETECTION/Dia3.JPG b/docs/Presentations/DETECTION/Dia3.JPG new file mode 100644 index 000000000..1f2c58924 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia3.JPG differ diff --git a/docs/Presentations/DETECTION/Dia4.JPG b/docs/Presentations/DETECTION/Dia4.JPG new file mode 100644 index 000000000..b9450da5c Binary files /dev/null and b/docs/Presentations/DETECTION/Dia4.JPG differ diff --git a/docs/Presentations/DETECTION/Dia5.JPG b/docs/Presentations/DETECTION/Dia5.JPG new file mode 100644 index 000000000..0df32d026 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia5.JPG differ diff --git a/docs/Presentations/DETECTION/Dia6.JPG b/docs/Presentations/DETECTION/Dia6.JPG new file mode 100644 index 000000000..6707c5928 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia6.JPG differ diff --git a/docs/Presentations/DETECTION/Dia7.JPG b/docs/Presentations/DETECTION/Dia7.JPG new file mode 100644 index 000000000..f3e509fe3 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia7.JPG differ diff --git a/docs/Presentations/DETECTION/Dia8.JPG b/docs/Presentations/DETECTION/Dia8.JPG new file mode 100644 index 000000000..388e6419d Binary files /dev/null and b/docs/Presentations/DETECTION/Dia8.JPG differ diff --git a/docs/Presentations/DETECTION/Dia9.JPG b/docs/Presentations/DETECTION/Dia9.JPG new file mode 100644 index 000000000..9b65f8733 Binary files /dev/null and b/docs/Presentations/DETECTION/Dia9.JPG differ diff --git a/docs/Presentations/SCHEDULER/Dia1.JPG b/docs/Presentations/SCHEDULER/Dia1.JPG new file mode 100644 index 000000000..81cfd2575 Binary files /dev/null and b/docs/Presentations/SCHEDULER/Dia1.JPG differ diff --git a/docs/Presentations/SET/Dia1.JPG b/docs/Presentations/SET/Dia1.JPG new file mode 100644 index 000000000..dd975a856 Binary files /dev/null and b/docs/Presentations/SET/Dia1.JPG differ diff --git a/docs/Presentations/TASK_A2G/Dia1.JPG b/docs/Presentations/TASK_A2G/Dia1.JPG new file mode 100644 index 000000000..08c642ea5 Binary files /dev/null and b/docs/Presentations/TASK_A2G/Dia1.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia1.JPG b/docs/Presentations/TASK_DISPATCHER/Dia1.JPG new file mode 100644 index 000000000..2501578e9 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia1.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia10.JPG b/docs/Presentations/TASK_DISPATCHER/Dia10.JPG new file mode 100644 index 000000000..55f545f95 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia10.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia11.JPG b/docs/Presentations/TASK_DISPATCHER/Dia11.JPG new file mode 100644 index 000000000..37f538608 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia11.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia12.JPG b/docs/Presentations/TASK_DISPATCHER/Dia12.JPG new file mode 100644 index 000000000..d69f65f24 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia12.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia13.JPG b/docs/Presentations/TASK_DISPATCHER/Dia13.JPG new file mode 100644 index 000000000..4c4e25a5a Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia13.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia14.JPG b/docs/Presentations/TASK_DISPATCHER/Dia14.JPG new file mode 100644 index 000000000..99f45e145 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia14.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia15.JPG b/docs/Presentations/TASK_DISPATCHER/Dia15.JPG new file mode 100644 index 000000000..09a00e4eb Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia15.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia16.JPG b/docs/Presentations/TASK_DISPATCHER/Dia16.JPG new file mode 100644 index 000000000..c74c69956 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia16.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia17.JPG b/docs/Presentations/TASK_DISPATCHER/Dia17.JPG new file mode 100644 index 000000000..ab23e55da Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia17.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia18.JPG b/docs/Presentations/TASK_DISPATCHER/Dia18.JPG new file mode 100644 index 000000000..9ad40904a Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia18.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia2.JPG b/docs/Presentations/TASK_DISPATCHER/Dia2.JPG new file mode 100644 index 000000000..edfc790e5 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia2.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia3.JPG b/docs/Presentations/TASK_DISPATCHER/Dia3.JPG new file mode 100644 index 000000000..2e129761a Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia3.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia4.JPG b/docs/Presentations/TASK_DISPATCHER/Dia4.JPG new file mode 100644 index 000000000..6adebc4f2 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia4.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia5.JPG b/docs/Presentations/TASK_DISPATCHER/Dia5.JPG new file mode 100644 index 000000000..057a68877 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia5.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia6.JPG b/docs/Presentations/TASK_DISPATCHER/Dia6.JPG new file mode 100644 index 000000000..9387e30e8 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia6.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia7.JPG b/docs/Presentations/TASK_DISPATCHER/Dia7.JPG new file mode 100644 index 000000000..66adb011f Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia7.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia8.JPG b/docs/Presentations/TASK_DISPATCHER/Dia8.JPG new file mode 100644 index 000000000..cf0b93e30 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia8.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia9.JPG b/docs/Presentations/TASK_DISPATCHER/Dia9.JPG new file mode 100644 index 000000000..49335ef21 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia9.JPG differ diff --git a/docs/Presentations/ZONE/Dia1.JPG b/docs/Presentations/ZONE/Dia1.JPG new file mode 100644 index 000000000..ca4dd2b3f Binary files /dev/null and b/docs/Presentations/ZONE/Dia1.JPG differ diff --git a/docs/README.md b/docs/README.md index eacd0bba7..56c645be6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,22 +6,7 @@ MOOSE works with DCS world 1.5. and 2.0. ![MOOSE Banner](Presentations\MOOSE\Dia1.JPG) -MOOSE is an Object Oriented framework and defines **Classes**, which are components that combine **Methods** and **Variables**/**Properties** as one encapsulated structure (table). -Mission Designers can **instantiate objects** from these MOOSE classes, which declares a variable that contains a reference to the instantiated object from the MOOSE Class. -So, you can use that variable to use its methods and properties to build your logic. Note that in OO lua, a methods are addressed using the ":" notation, -because a "self" variable is passed that contains the object reference to that function! - -An example of what this means is shortly explained using the SPAWN class of MOOSE, which you can use to spawn new groups into your running mission. -The SPAWN class simplifies the process of spawning, and it has many methods that you can use to create variations how you want your spawn object to spawn new groups. - -```lua -local SpawnObject = SPAWN:New( "GroupName" ) -- This creates a new SpawnObject from the SPAWN class, using the :New method constructor to instantiate a new SPAWN object searching for the GroupName as the late activated group defined within your Mission Editor. --- Nothing is spawned yet..., so let's use now the SpawnObject to spawn a new GROUP. - -local SpawnGroup = SpawnObject:Spawn() -- Here we use the :Spawn() method of the SPAWN class. This method creates a new group from the GroupName template as defined within the Mission Editor. -``` - -## 1.1) MOOSE framework goal +## MOOSE framework goal The goal of MOOSE is to allow mission designers to enhance their scripting with mission orchestration objects, which can be instantiated from defined classes within the framework. This will allow to write mission scripts with minimal code embedded. Of course, the richness of the framework will determine the richness of the misson scenarios. The MOOSE is a service that is produced while being consumed ... , it will evolve further as more classes are developed for the framework, and as more users are using it. @@ -31,91 +16,61 @@ MOOSE is NOT meant to be a one-man show, it is meant to evolve within a growing Within the community, key users will start supporting, documenting, explaining and even creating new classes for the framework. It is the ambition to grow this framework as a de-facto standard for mission designers to use. -## 1.2) MOOSE framework at GITHUB -You can find the source of [MOOSE framework on GITHUB](https://github.com/FlightControl-Master/MOOSE/). -It is free for download and usage, but it is under license of the GNU 3.0 open source license policy. -The MOOSE development uses the GITHUB service to enforce and control a structured development within a growing community. -GITHUB enforces a structured approval process, release and change management, and a communicative distribution and deployment. -The MOOSE framework development is considered an open source project, where contributors are welcome to contribute on the development. -Some key users have already started with this process. Contact me if you're interested to contribute. -## 1.3) Eclipse LDT -MOOSE utilizes the Eclipse Lua Development Tools. As a result, the MOOSE framework is documented using the luadocumentor standard. -Every class, method and variable is documented within the source, and mission designers can write mission script lua code that is **intellisense**(-ed) ... -What that means is that while you are coding your mission, your object and variables (derived from MOOSE classes) will list the methods and properties of that class interactively while coding ... +# 2) MOOSE usage -![Intellisense](Usage/Intellisense.JPG) +The delivery of MOOSE follows a structured release process. Over time, new features are added that can be used in your mission. -## 1.4) LUA training +### The latest release of MOOSE can be downloaded **[here](https://github.com/FlightControl-Master/MOOSE/releases)**. -In order to efficiently use the MOOSE framework, it is highly recommended that you learn a couple of basic principles of lua. -I recommend you learn the basic lua principles following this [lua tutorial](https://www.tutorialspoint.com/lua). -We're not asking you to become an expert in lua, leave that to the experts, but, you'll need to have some basic lua coding -knowledge to "understand" the code, and also, to understand the syntax. +There are 3 different ways how you can use MOOSE, each with a different engagement and complexity level: -**Therefore, I suggest you walk through this [lua quick guide](https://www.tutorialspoint.com/lua/lua_quick_guide.htm)**. -Ignore the lua environment setup. DCS comes with a pre-defined lua environment. -# 2) Installation -There is a complete installation guide as part of this documentation. +## 2.1) Use MOOSE as a Mission Designer -[Click here for the installation guide of the MOOSE framework](Installation.html). +Refer to the detailed **[Usage Guide](Usage_Guide.html)** for more information. -# 3) Support Channels + + +## 2.2) Beta test MOOSE + +Beta testers of MOOSE are requested to install additional software. + +As a return or as a reward, testers get: + + * Newly developed features planned for the next MOOSE release can be tested and incorporated in your missions early. + * You can evaluate and contribute to the stability of the next release. + * Your mission creation workflow becomes very flexible. New features are dynamically added to you missions. + +Please read the detailed **[Beta Test Guide](Beta_Test_Guide.html)** for more information. + + + +## 2.3) Contribute on the MOOSE development + +Those people who have experience in lua development or are excited to contribute to the MOOSE project are welcome. + +Please consult the **[Contribution Guide](Contribution_Guide.html)** for more information. + + + +# 3) MOOSE Support Channels MOOSE is broadcasted, documented and supported through various social media channels. -[Click here for the communities guide of the MOOSE framework](Communities.html). +Click here for the **[communities guide](Communities.html)** of the MOOSE framework. + + -# 4) Demonstration Missions - -The framework comes with [Test Missions](https://github.com/FlightControl-Master/MOOSE/tree/master/Moose%20Test%20Missions), -that you can try out and helps you to code. These test missions provide examples of defined use cases how the MOOSE -framework can be utilized. Each test mission is located in a separate directory, which contains at least one .lua file and .miz file. -The .lua file contains the mission script file that shows how the use case was implemented. -You can copy/paste code the code snippets from this .lua file into your missions, as it will accellerate your mission developments. -You will learn, see, and understand how the different MOOSE classes need to be applied, and how you can create -more complex mission scenarios by combining these MOOSE classes into a complex but powerful mission engine. - -These exact test missions are also demonstrated at the demo videos in the YouTube channel. - -# 5) MOOSE Directory Structure - -The MOOSE framework is devided into a couple of directories: - -* Moose Development: Contains the collection of lua files that define the MOOSE classes. You can use this directory to build the dynamic luadoc documentation intellisense in your eclipse development environment. -* Moose Mission Setup: Contains the Moose.lua file to be included in your scripts when using MOOSE classes (see below the point Mission Design with Moose). -* Moose Test Missions: Contains a directory structure with Moose Test Missions and examples. In each directory, you will find a miz file and a lua file containing the main mission script. -* Moose Training: Contains the documentation of Moose generated with luadoc from the Moose source code. The presentations used during the videos in my [youtube channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg), are also to be found here. - -# 6) Mission Design with MOOSE - -In order to create or design a mission using MOOSE, you'll have to include a file named **Moose.lua**: - -1. Create a new mission in the DCS World Mission Editor. -2. In the mission editor, create a new trigger. -3. Name the trigger Moose Load and let it execute only at MISSION START. -4. Add an action DO SCRIPT FILE (without a condition, so the middle column must be empty). -5. In the action, browse to the **[Moose.lua](https://github.com/FlightControl-Master/MOOSE/tree/master/Moose%20Mission%20Setup)** file in the **Moose Mission Setup** directory, and include this file within your mission. -6. Make sure that the "Moose Load" trigger is completely at the top of your mission. - -Voila, MOOSE is now included in your mission. During the execution of this mission, all MOOSE classes will be loaded, and all MOOSE initializations will be exectuted before any other mission action is executed. - -IMPORTANT NOTE: When a new version of MOOSE is released, you'll have to UPDATE the Moose.lua file in EACH OF YOUR MISSION. -This can be a tedious task, and for this purpose, a tool has been developed that will update the Moose.lua files automatically within your missions. -Refer to the tool at [Moose Mission Setup\Moose Mission Update](https://github.com/FlightControl-Master/MOOSE/tree/master/Moose%20Mission%20Setup/Moose%20Mission%20Update) directory for further information included in the [READ.ME]() file. - -# 7) MOOSE Framework +# 4) MOOSE Framework The following classes are currently embedded within MOOSE framework and can be included within your mission scripts: -![MOOSE framework](Presentations\MOOSE\Dia2.JPG) - MOOSE is an Object Oriented framework and defines **Classes**, which are components that combine **Methods** and **Variables**/**Properties** as one encapsulated structure (table). -Mission Designers can what we call **instantiate objects** from these MOOSE classes. +Mission Designers can what we call **Instantiate Objects** from these MOOSE classes. An example of what this means is shortly explained using the SPAWN class of MOOSE, which you can use to spawn new groups into your running mission. The SPAWN class simplifies the process of spawning, and it has many methods that you can use to create variations how you want your spawn object to spawn new groups. @@ -133,7 +88,24 @@ So is also the SPAWN class derived from the BASE class. The BASE class provides As a normal MOOSE user, you won't implement any code using inheritance but just know that the inheritance structure is omni present in the intellisense and documentation. You'll need to browse to the right MOOSE Class within the inheritance tree structure to identify which methods are properties are defined for which class. -## 7.1) MOOSE Core Classes +![MOOSE framework](Presentations\MOOSE\Dia2.JPG) + + + +## 4.1) MOOSE Demonstration Missions + +The framework comes with demonstration missions which can be downloaded [here](https://github.com/FlightControl-Master/MOOSE_PRESENTATIONS/releases), that you can try out and helps you to code. +These missions provide examples of defined use cases how the MOOSE framework can be utilized. Each test mission is located in a separate directory, which contains at least one .lua file and .miz file. +The .lua file contains the mission script file that shows how the use case was implemented. +You can copy/paste code the code snippets from this .lua file into your missions, as it will accellerate your mission developments. +You will learn, see, and understand how the different MOOSE classes need to be applied, and how you can create +more complex mission scenarios by combining these MOOSE classes into a complex but powerful mission engine. + +Some of these exact test missions are also demonstrated in a video format on the [YouTube channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg). + + + +## 4.2) MOOSE Core Classes These classes define the base building blocks of the MOOSE framework. These classes are heavily used within the MOOSE framework. @@ -143,11 +115,13 @@ These classes define the base building blocks of the MOOSE framework. These clas * [EVENT](Documentation/Event.html): Provides the Event Dispatcher base class to handle DCS Events, being fired upon registered events within the DCS simulator. Note that EVENT is used by BASE, exposing OnEvent() methods to catch these DCS events. +* [SCHEDULER](Documentation/Scheduler.html): This class implements a timer scheduler that will call at optional specified intervals repeatedly or just one time a scheduled function. + * [FSM](Documentation/Fsm.html): The main FSM class can be used to build a Finite State Machine. The derived FSM_ classes provide Finite State Machine building capability for CONTROLLABLEs, ACT_ (Task Actions) classes, TASKs and SETs. * [MENU](Documentation/Menu.html): Set Menu options (F10) for All Players, Coalitions, Groups, Clients. MENU also manages the recursive removal of menus, which is a big asset! -* [SETS](Documentation/Set.html): Create SETs of MOOSE objects. SETs can be created for GROUPs, UNITs, AIRBASEs, ... +* [SET](Documentation/Set.html): Create SETs of MOOSE objects. SETs can be created for GROUPs, UNITs, AIRBASEs, ... The SET can be filtered with defined filter criteria. Iterators are available that iterate through the GROUPSET, calling a function for each object within the SET. @@ -158,10 +132,9 @@ The POINT_VEC3 class manages the 3D simulation space, while the POINT_VEC2 class * [ZONES](Documentation/Zone.html): A set of zone classes that provide the functionality to validate the presence of GROUPS, UNITS, CLIENTS, STATICS within a certain ZONE. The zones can take various forms and can be movable. -* [SCHEDULER](Documentation/Scheduler.html): This class implements a timer scheduler that will call at optional specified intervals repeatedly or just one time a scheduled function. -## 7.2) MOOSE Wrapper Classes +## 4.3) MOOSE Wrapper Classes MOOSE Wrapper Classes provide an object oriented hierarchical mechanism to manage the DCS objects within the simulator. Wrapper classes provide another easier mechanism to control Groups, Units, Statics, Airbases and other objects. @@ -193,7 +166,8 @@ The CLIENT class derives from the UNIT class, thus contains the complete UNIT AP The STATIC class derives from the POSITIONABLE class, thus contains also the position API set. -## 7.3) MOOSE Functional Classes + +## 4.4) MOOSE Functional Classes MOOSE Functional Classes provide various functions that are useful in mission design. @@ -207,14 +181,16 @@ MOOSE Functional Classes provide various functions that are useful in mission de * [SCORING](Documentation/Scoring.html): Administer the scoring of player achievements, and create a CSV file logging the scoring events for use at team or squadron websites. -## 7.4) MOOSE AI Controlling Classes + + +## 4.5) MOOSE AI Controlling Classes MOOSE AI Controlling Classes provide mechanisms to control AI over long lasting processes. These AI Controlling Classes are based on FSM (Finite State Machine) Classes, and provided an encapsulated way to make AI behave or execute an activity. * [AI_BALANCER](Documentation/AI_Balancer.html): Compensate in a multi player mission the abscence of players with dynamically spawned AI air units. When players join CLIENTS, the AI will either be destroyed, or will fly back to the home or nearest friendly airbase. -* [AI_PATROLZONE](Documentation/AI_PatrolZone.html): Make an alive AI Group patrol a zone derived from the ZONE_BASE class. Manage out-of-fuel events and set altitude and speed ranges for the patrol. +* [AI_PATROL_ZONE](Documentation/AI_Patrol_Zone.html): Make an alive AI Group patrol a zone derived from the ZONE_BASE class. Manage out-of-fuel events and set altitude and speed ranges for the patrol. * [AI_CAP](Documentation/AI_Cap.html): Make an alive AI Group perform Combat Air Patrol in a dynamic process. @@ -222,7 +198,9 @@ These AI Controlling Classes are based on FSM (Finite State Machine) Classes, an * [AI_CARGO](Documentation/AI_Cargo.html): Make AI behave as cargo. Various CARGO types exist. -## 7.5) MOOSE Human Tasking Classes + + +## 4.6) MOOSE Human Tasking Classes MOOSE Tasking Classes provide a comprehensive Mission Orchestration System. Through COMMANDCENTERs, multiple logical MISSIONs can be orchestrated for coalitions. @@ -237,9 +215,13 @@ Each TASK has a TASK ACTION flow, which is the flow that a player (hosted by a U * [TASK_SEAD](Documentation/Task_SEAD.html): Models a SEAD Task, where a Player is routed towards an attack zone, and various SEADing targets need to be eliminated. -* [TASK_A2G](Documentation/Task_A2G.html): Models a A2G Task, where a Player is routed towards an attack zone, and various A2G targets need to be eliminated. +* [TASK_BAI](Documentation/Task_A2G.html): Models a CAP Task, where a Player is routed towards an attack zone without enemies nearby, and various ground targets need to be eliminated. -## 7.6) MOOSE Action Classes +* [TASK_CAS](Documentation/Task_A2G.html): Models a CAS Task, where a Player is routed towards an attack zone with enemies nearby, and various ground targets need to be eliminated. + + + +## 4.7) MOOSE Action Classes MOOSE Action Classes are task action sub-flows, that can be used and combined, to quickly define a comprehensive end-to-end task action flow. For example, for the SEAD Task, the task action flow combines the actions ASSIGN, ROUTE, ACCOUNT and ASSIST task action sub-flows. @@ -253,7 +235,10 @@ For example, for the SEAD Task, the task action flow combines the actions ASSIGN * [ACT_ASSIST](Documentation/Assist.html): Mechanisms to assist players executing a task. For example, acquire targets through smoking them. -# 8) Credits + + + +# 5) Credits Note that most of the framework is based on code i've written myself, but some code of it is also based on code that i've seen as great scripting code and ideas, @@ -263,12 +248,10 @@ Therefore a list of credits to all who have or are contributing (this list will You'll notice that within this framework, there are functions used from mist. I've taken the liberty to copy those atomic mist functions that are very nice and useful, and used those. -**Grimes** -Without the effort of Grimes with MIST and his continuous documentation of the DCS API, the development of MOOSE would not have been possible. -MOOSE is complementary to [MIST](https://github.com/mrSkortch/MissionScriptingTools/releases), so if you use MIST in parallel with MOOSE objects, this should work. -The documentation of the [DCS API] is the work of Grimes. Please consult his documentation in case of any question concerning the DCS API level. - +**Greyecho** +Contributed the devevelopment of the new release system, and the development of the RADIO and BEACON functionalities. +We'll see more appearing from him. **Prof_hilactic** SEAD Defenses. I've taken the script, and reworded it to fit within MOOSE. The script within MOOSE is hardly recognizable anymore from the original. Find here the posts: http://forums.eagle.ru/showpost.php?...59&postcount=1 @@ -288,6 +271,11 @@ Worked together with Gabor to create the concept of the DETECTION and FAC classe **Shadoh** Interacted on the eagle dynamics forum to build the FOLLOW class to build large WWII airplane formations. +**Grimes** +Without the effort of Grimes with MIST and his continuous documentation of the DCS API, the development of MOOSE would not have been possible. +MOOSE is complementary to [MIST](https://github.com/mrSkortch/MissionScriptingTools/releases), so if you use MIST in parallel with MOOSE objects, this should work. +The documentation of the [DCS API] is the work of Grimes. Please consult his documentation in case of any question concerning the DCS API level. + For the rest I also would like to thank the numerous feedback and help and assistance of the moose community at SLACK.COM. Note that there is a vast amount of other scripts out there. I may contact you personally to ask for your contribution / permission if i can use your idea or script to tweak it to the framework. @@ -298,4 +286,5 @@ Trust I've spent hours and hours investigating, trying and writing and documenti Hope you think the idea is great and useful. Thank you! -FC \ No newline at end of file +FC + diff --git a/docs/Usage_Guide.md b/docs/Usage_Guide.md new file mode 100644 index 000000000..90afd885b --- /dev/null +++ b/docs/Usage_Guide.md @@ -0,0 +1,286 @@ +# 1) MOOSE Usage Guide + +Using the MOOSE framework is very easy, and this document provides you with a detailed explanation how to install +and use MOOSE within your missions. + +## 1.1) MOOSE framework at GitHub + +You can find the source of [MOOSE framework on GITHUB](https://github.com/FlightControl-Master/MOOSE/). +It is free for download and usage, since it is released under the GNU 3.0 open source license policy. +Although the MOOSE contributors and tester are using the GitHub service to enforces a structured approval process, release and change management, and a communicative distribution and deployment, you, as a mission designer, don't need to mess with it. Still, if you are interrested intesting the latest features of MOOSE of in adding your own, you can read the [relevant](http://flightcontrol-master.github.io/MOOSE/Beta_Test_Guide.html) [guides](http://flightcontrol-master.github.io/MOOSE/Contribution_Guide.html) and/or contact FlightControl +The MOOSE framework development is an open source project, and as such, contributors are welcome and encouraged to contribute on the development.Some key users have already started with this process. + +## 1.2) Eclipse LDT + +MOOSE utilizes the Eclipse Lua Development Tools. As a result, the MOOSE framework is documented using the luadocumentor standard. +Every class, method and variable is documented within the source, and mission designers can write mission script lua code that is **intellisense**(-ed) ... +What that means is that while you are coding your mission, your object and variables (derived from MOOSE classes) will list the methods and properties of that class interactively while coding ... + +![Intellisense](Usage/Intellisense.JPG) + +## 1.3) LUA training + +In order to efficiently use the MOOSE framework, it is highly recommended that you learn a couple of basic principles of lua. +I recommend you learn the basic lua principles following this [lua tutorial](https://www.tutorialspoint.com/lua). +We're not asking you to become an expert in lua, leave that to the experts, but, you'll need to have some basic lua coding +knowledge to "understand" the code, and also, to understand the syntax. + +**Therefore, I suggest you walk through this [lua quick guide](https://www.tutorialspoint.com/lua/lua_quick_guide.htm)**. +Ignore the lua environment setup. DCS comes with a pre-defined lua environment. + +# 2) MOOSE Installation Guide + +## 2.1) Download the latest release of MOOSE + +The delivery of MOOSE follows a structured release process. Over time, new features are added that can be used in your mission. + +## The latest release of MOOSE can be downloaded **[here](https://github.com/FlightControl-Master/MOOSE/releases)**. + +**Unzip the files into a directory of your choice, but keep the folder structure intact**. + + +## 2.2) Download and install the Eclipse Lua Development Tools (LDT), which is an advanced lua editor. + +1. If you don't have JAVA yet, you may have to install [java](https://www.java.com/en/download) first. +2. Download and Install [Eclipse LDT](https://eclipse.org/ldt) on your Windows 64 bit system. + +TNow you should have a working LDT on your system. +Don't skip this step, LDT is a game-changer. Don't believe us ? Well we challenge you to test and tell us what you think ! Once you tried coding with intellisense, you won't go back ! + +## 2.3) Configure your LDT for the usage of MOOSE. + +You need to configure your Eclipse LDT environment and link it with the MOOSE respository. +This will enable you to **start developing mission scripts** in lua, which will be **fully intellisense enabled**!!! + +Please follow the steps outlined! + +### 2.3.1) Create a new **Workspace** in LDT. + +The LDT editor has a concept of **Workspaces**, which contains all your settings of your editing environment, +like views, menu options etc, and your code... Nothing to pay attention to really, but you need to set it up! +When you open EclipseLDT for the first time, it will ask you where to put your *workspace area*... + +1. Open Eclipse LDT. +2. Select the default workspace that LDT suggests. + +### 2.3.2) Create a new **Project** in LDT. + +Here, we will create a **New Project** called **Moose_Framework** in your LDT environment. +The project details are already defined within the MOOSE framework repository, +which is unzipped on your local MOOSE directory on your PC. +We will link into that directory and automatically load the Project properties. + +1. Select from the Menu: **File** -> **New** -> **Lua Project**. + +![LDT_New_Project](Installation/LDT_New_Project.JPG) + +2. A **New Project** dialog box is shown. + +![LDT_Project](Installation/LDT_Project.JPG) + +3. Type the Project Name: **Moose_Framework**. +4. In the sub-box "Project Contents", select the option **Create Project at existing location** (from existing source). +5. **Browse** to the local MOOSE directory (press on the Browse button) and select the root directory of your local MO.OSE directory on your PC. Press OK. +6. You're back at the "New Project" dialog box. Press the **Next** button below the dialog box. +__(All the other settings are by default ok)__. +7. You should see now a dialog box with the following properties. +Note that the Moose Development/Moose directory is flagged as the **Source Directory*. (It is listed totally on top.) +This is important because it will search in the files in this directory and sub directories for lua documentator enabled lua files. +This will enable the intellisense of the MOOSE repository! + +![LDT Finish](Installation/LDT_Moose_Framework_Finish.JPG) + +8. Press the **Finish** button. + +As a result, when you browse to the Script Explorer, you'll see the following: + +![LDT_Script_Explorer](Installation/LDT_Script_Explorer.JPG) + +**Congratulations! You have now setup your Moose_Framework project LDT environment!** + +# 2.4) Your first mission + +## 2.4.1) Setup your **Mission Project** in LDT + +In order to design your own missions, it is recommended you create a separate directory on your PC +which contains your mission files. Your mission will be designed consisting possibly +out of a couple of components, which are: + + * (Mandatory) An include of the Moose.lua file (see 2.3.2). + * (Mandatory) An include of your lua mission script file(s) (also with a .lua extension). + * (Optionally) Sound files (.ogg) and pictures (.jpg) which are added into your mission. + +Using the menu system of the DCS World Mission Editor, you need to include files in your mission (.miz) file. +However, once included, maintaining these files is a tedious task, +having to replace each time manually these files when they change +(due to a new release or a change in your mission script). + +Therefore, **the recommendation is that your create for each mission a separate folder**. +The MOOSE test mission folder structure is a good example how this could be organized. +The LDT has been customized and provides a tool to **automatically** maintain your existing .miz files. + +### 2.4.1.1. Select from the Menu: **File** -> **New** -> **Lua Project**. + +![LDT_New_Project](Installation/LDT_New_Project.JPG) + +### 2.4.1.2. A **New Project** dialog box is shown. + +![LDT_Project](Installation/LDT_Project.JPG) + +### 2.4.1.3. Type your Project Name: (In my example it is **DCS_Caucasus_Missions**. + + +Note the indicated options in yellow: + + * Note that you can select the option **No execution environment**. + * Deselect the option **Create default project template ready to run**. + +![LDT_Project](Installation/LDT_Project_My_Missions.JPG) + +### 2.4.1.4. Press **Next >** + +### 2.4.1.5. Click the **Projects** tab at the top of the window. + +![LDT_Project](Installation/LDT_New_Project_Projects.JPG) + +### 2.4.1.6. Press the **Add...** button. + +### 2.4.1.7. A new windows will be displayed: **Required Project Selection**. + +This is an important step. This will _link_ your project to the Moose_Framework project and will activate **intellisense**. + +![LDT_Project](Installation/LDT_Select_Moose_Framework.JPG) + +### 2.4.1.8. After the selection, press the **OK** button. + +### 2.4.1.9. Watch your newly created project in the Script Explorer of LDT. + +You can delete the possibly created SRC directory. You won't need it at all. + +![LDT_Project](Installation/LDT_Delete_Src.JPG) + +### 2.4.1.10. Within your newly created Missions Project, right click and select **New -> Folder**. + +As explained above, each of your missions will be stored in a separate folder. Please follow the explanation how to do that. + +![LDT_Project](Installation/LDT_Add_Folder.JPG) + +### 2.4.1.11. Type the **Folder Name**. + +This can be any descriptive text explaining the title of your mission. + +![LDT_Project](Installation/LDT_Mission_Folder_Name.JPG) + +### 2.4.1.12. In your newly created **Mission Folder**, right click and select **New -> Lua File**. + +This will create your **mission script file**, +the file that contains all the lua code using the Moose framework using your mission. + +### 2.4.1.13. Type the **Lua Mission Script Name**. + +![LDT_Project](Installation/LDT_Mission_Lua_File_Name.JPG) + + +## 2.4.2) Create your first Mission file + +In the root of the MOOSE package, a file named **Moose.lua** can be found. +In order to create or design a mission using the MOOSE framework, +you'll have to include this **Moose.lua** file into your missions: + + 1. Create a new mission in the DCS World Mission Editor. + 2. In the mission editor, create a new trigger. + 3. Name the trigger Moose Load and let it execute only at MISSION START. + 4. Add an action DO SCRIPT FILE (without a condition, so the middle column must be empty). + 5. In the action, browse to the **[Moose.lua](https://github.com/FlightControl-Master/MOOSE/tree/master/Moose%20Mission%20Setup)** file in the **Moose Mission Setup** directory, and include this file within your mission. + 6. Make sure that the "Moose Load" trigger is completely at the top of your mission. + +Voila, MOOSE is now included in your mission. During the execution of this mission, all MOOSE classes will be loaded, and all MOOSE initializations will be exectuted before any other mission action is executed. + +Find below a detailed explanation of the actions to follow: + +### 2.4.2.1. Open the Mission Editor in DCS, select an empty mission, and click the triggers button. + +![LDT_Project](Installation/DCS_Triggers_Empty.JPG) + +### 2.4.2.2. Add a new trigger, that will load the Moose.lua file. + +Check the cyan colored circles: + + * This trigger is loaded at MISSION START. + * It is the first trigger in your mission. + * It contains a DO SCRIPT FILE action. + * No additional conditions! + +![LDT_Project](Installation/DCS_Triggers_Load_Moose_Add.JPG) + +### 2.4.2.3. Select the Moose.lua loader from the **Moose Mission Setup** folder in the Moose_Framework pack. + +Additional notes: + + * If you've setup a folder link into Saved Games/DCS/Missions/Moose Mission Setup, then you can directly select this folder from **My Missions**. + * See point ... + +Press the **OK** button. + +![LDT_Project](Installation/DCS_Triggers_Load_Moose_Select_File.JPG) + +### 2.4.2.4. Check that the Moose.lua file has been correctly added to your Mission. + +![LDT_Project](Installation/DCS_Triggers_Load_Moose_File_Added.JPG) + +### 2.4.2.5. Add a new trigger, that will load your mission .lua file. + +Check the cyan colored circles: + + * This trigger is loaded at MISSION START. + * It is the second trigger in your mission. + * It contains a DO SCRIPT FILE action. + * No additional conditions! + +![LDT_Project](Installation/DCS_Triggers_Load_Mission_Add.JPG) + +### 2.4.2.6. Select the mission .lua file from your **missions** folder you just created or already have. + +Additional notes: + + * If you've setup a folder link into Saved Games/DCS/Missions/Moose Mission Setup, then you can directly select this folder from **My Missions**. + * See point ... + +Press the **OK** button. + +![LDT_Project](Installation/DCS_Triggers_Load_Mission_File_Select.JPG) + +### 2.4.2.7. Check that your mission .lua script file has been correctly added to your mission. + +![LDT_Project](Installation/DCS_Triggers_Load_Mission_File_Added.JPG) + + +## 2.4.3) Maintain your .miz files + +IMPORTANT NOTE: When a new version of MOOSE is released, you'll have to UPDATE the Moose.lua file in EACH OF YOUR MISSION. +This can be a tedious task, and for this purpose, a tool has been developed that will update the Moose.lua files automatically within your missions. + +### 2.4.3.1. Select the **Update SELECTED Mission** from the External Tools in LDT. + +This will activate a script that will automatically re-insert your mission .lua file into your mission. + +![LDT_Project](Installation/DCS_Triggers_Load_Mission_File_Added.JPG) + +## 2.4.4) Create folder links into your "My Missions" folder in Saved Games/DCS/Missions. + +***TODO : Detail how hard links work, explain how they help the wworkflow*** + +This trick will save you a lot of time. You need to install the tool ... to create easily new links. + +Select from the following possible links that can be created to save you time while browing through the different folders to include script files: + +### 2.4.4.1. Create a link to your **Moose Mission Setup** folder ... + +### 2.4.4.2. Create a link to your **missions** folder ... + +# 4) Demonstration Missions + +The framework comes with demonstration missions which can be downloaded [here](https://github.com/FlightControl-Master/MOOSE_PRESENTATIONS/releases), that you can try out and helps you to code. +These missions provide examples of defined use cases how the MOOSE framework can be utilized. Each test mission is located in a separate directory, which contains at least one .lua file and .miz file. The .lua file contains the mission script file that shows how the use case was implemented. You can copy/paste code the code snippets from this .lua file into your missions, as it will accellerate your mission developments. You will learn, see, and understand how the different MOOSE classes need to be applied, and how you can create more complex mission scenarios by combining these MOOSE classes into a complex but powerful mission engine. +Some of these exact test missions are also demonstrated in a video format on the [YouTube channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg). +
    ZONE_UNIT.ClassName - -
    ZONE_UNIT:GetRandomVec2()

    Returns a random location within the zone.

    @@ -750,6 +617,14 @@ This class implements the inherited functions from Zone#ZONE_RADIUS + +

    The ZONE class, defined by the zone name as defined within the Mission Editor.

    + + +

    This class implements the inherited functions from #ZONE_RADIUS taking into account the own zone format and properties.

    + +
    @@ -764,7 +639,54 @@ This class implements the inherited functions from Base#BASE +

    This class is an abstract BASE class for derived classes, and is not meant to be instantiated.

    + + + +

    1.1) Each zone has a name:

    + + + +

    1.2) Each zone implements two polymorphic functions defined in Zone#ZONE_BASE:

    + + + +

    1.3) A zone has a probability factor that can be set to randomize a selection between zones:

    + +
      +
    • ZONE_BASE.SetRandomizeProbability(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% )
    • +
    • ZONE_BASE.GetRandomizeProbability(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% )
    • +
    • ZONE_BASE.GetZoneMaybe(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate.
    • +
    + +

    1.4) A zone manages Vectors:

    + + + +

    1.5) A zone has a bounding square:

    + + + +

    1.6) A zone can be marked:

    + + + +
    @@ -778,6 +700,15 @@ This class implements the inherited functions from Zone#ZONE_RADIUS + +

    The ZONE_GROUP class defines by a zone around a Group#GROUP with a radius.

    + + +

    The current leader of the group defines the center of the zone. +This class implements the inherited functions from Zone#ZONE_RADIUS taking into account the own zone format and properties.

    + +
    @@ -792,6 +723,14 @@ This class implements the inherited functions from Zone#ZONEPOLYGONBASE + +

    The ZONE_POLYGON class defined by a sequence of Group#GROUP waypoints within the Mission Editor, forming a polygon.

    + + +

    This class implements the inherited functions from Zone#ZONE_RADIUS taking into account the own zone format and properties.

    + +
    @@ -806,6 +745,25 @@ This class implements the inherited functions from Zone#ZONE_BASE + +

    The ZONEPOLYGONBASE class defined by a sequence of Group#GROUP waypoints within the Mission Editor, forming a polygon.

    + + +

    This class implements the inherited functions from Zone#ZONE_RADIUS taking into account the own zone format and properties. +This class is an abstract BASE class for derived classes, and is not meant to be instantiated.

    + +

    6.1) Zone point randomization

    + +

    Various functions exist to find random points within the zone.

    + + + +
    @@ -820,6 +778,45 @@ This class implements the inherited functions from Zone#ZONE_RADIUS class, extends Zone#ZONE_BASE + +

    The ZONE_RADIUS class defined by a zone name, a location and a radius.

    + + +

    This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties.

    + +

    2.1) Zone#ZONE_RADIUS constructor

    + + + +

    2.2) Manage the radius of the zone

    + + + +

    2.3) Manage the location of the zone

    + + + +

    2.4) Zone point randomization

    + +

    Various functions exist to find random points within the zone.

    + + + +
    @@ -834,6 +831,14 @@ This class implements the inherited functions from Zone#ZONE_RADIUS + +

    The ZONE_UNIT class defined by a zone around a Unit#UNIT with a radius.

    + + +

    This class implements the inherited functions from #ZONE_RADIUS taking into account the own zone format and properties.

    + +
    @@ -841,27 +846,7 @@ This class implements the inherited functions from Type Zone

    Type ZONE

    - -

    The ZONE class, defined by the zone name as defined within the Mission Editor.

    - - -

    The location and the radius are automatically collected from the mission settings.

    - -

    Field(s)

    -
    -
    - - #string - -ZONE.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    @@ -906,20 +891,6 @@ The name of the zone as defined within the mission editor.

    Bound the zone boundaries with a tires.

    - -
    -
    -
    - - #string - -ZONE_BASE.ClassName - -
    -
    - - -
    @@ -961,6 +932,60 @@ The name of the zone.

    + +ZONE_BASE:GetPointVec2(Height) + +
    +
    + +

    Returns a Point#POINT_VEC2 of the zone.

    + +

    Parameter

    +
      +
    • + +

      Dcs.DCSTypes#Distance Height : +The height to add to the land height where the center of the zone is located.

      + +
    • +
    +

    Return value

    + +

    Core.Point#POINT_VEC2: +The PointVec2 of the zone.

    + +
    +
    +
    +
    + + +ZONE_BASE:GetPointVec3(Height) + +
    +
    + +

    Returns a Point#POINT_VEC3 of the zone.

    + +

    Parameter

    +
      +
    • + +

      Dcs.DCSTypes#Distance Height : +The height to add to the land height where the center of the zone is located.

      + +
    • +
    +

    Return value

    + +

    Core.Point#POINT_VEC3: +The PointVec3 of the zone.

    + +
    +
    +
    +
    + ZONE_BASE:GetRandomPointVec2() @@ -979,6 +1004,24 @@ The PointVec2 coordinates.

    + +ZONE_BASE:GetRandomPointVec3() + +
    +
    + +

    Define a random Point#POINT_VEC3 within the zone.

    + +

    Return value

    + +

    Core.Point#POINT_VEC3: +The PointVec3 coordinates.

    + +
    +
    +
    +
    + ZONE_BASE:GetRandomVec2() @@ -1010,6 +1053,33 @@ The Vec2 coordinates.

    #nil:

    + +
    +
    +
    + + +ZONE_BASE:GetVec3(Height) + +
    +
    + +

    Returns the DCSTypes#Vec3 of the zone.

    + +

    Parameter

    +
      +
    • + +

      Dcs.DCSTypes#Distance Height : +The height to add to the land height where the center of the zone is located.

      + +
    • +
    +

    Return value

    + +

    Dcs.DCSTypes#Vec3: +The Vec3 of the zone.

    +
    @@ -1275,24 +1345,7 @@ The smoke color.

    Type ZONE_GROUP

    - -

    The ZONE_GROUP class defined by a zone around a Group, taking the average center point of all the units within the Group, with a radius.

    - -

    Field(s)

    -
    -
    - - #string - -ZONE_GROUP.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    @@ -1384,24 +1437,7 @@ self

    Type ZONE_POLYGON

    - -

    The ZONE_POLYGON class defined by a sequence of Group#GROUP waypoints within the Mission Editor, forming a polygon.

    - -

    Field(s)

    -
    -
    - - #string - -ZONE_POLYGON.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    @@ -1440,40 +1476,32 @@ self

    Type ZONE_POLYGON_BASE

    - -

    The ZONEPOLYGONBASE class defined by an array of DCSTypes#Vec2, forming a polygon.

    - -

    Field(s)

    +

    Field(s)

    -ZONE_POLYGON_BASE:BoundZone() +ZONE_POLYGON_BASE:BoundZone(UnBound)

    Smokes the zone boundaries in a color.

    +

    Parameter

    +
      +
    • + +

      #boolean UnBound : +If true, the tyres will be destroyed.

      + +
    • +

    Return value

    #ZONEPOLYGONBASE: self

    -
    -
    -
    -
    - - #string - -ZONE_POLYGON_BASE.ClassName - -
    -
    - - -
    @@ -1690,20 +1718,31 @@ self

    -ZONE_RADIUS:BoundZone(Points) +ZONE_RADIUS:BoundZone(Points, UnBound, CountryID)

    Bounds the zone with tires.

    -

    Parameter

    +

    Parameters

    • #number Points : (optional) The amount of points in the circle.

      +
    • +
    • + +

      #boolean UnBound : +If true the tyres will be destroyed.

      + +
    • +
    • + +

      CountryID :

      +

    Return value

    @@ -1711,20 +1750,6 @@ self

    #ZONE_RADIUS: self

    -
    -
    -
    -
    - - #string - -ZONE_RADIUS.ClassName - -
    -
    - - -
    @@ -2145,20 +2170,6 @@ self

    - #string - -ZONE_UNIT.ClassName - -
    -
    - - - -
    -
    -
    -
    - ZONE_UNIT:GetRandomVec2() diff --git a/docs/Documentation/index.html b/docs/Documentation/index.html index 124efee34..2c422f83e 100644 --- a/docs/Documentation/index.html +++ b/docs/Documentation/index.html @@ -39,16 +39,17 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • Process_JTAC
  • Process_Pickup
  • +
  • Radio
  • Route
  • Scenery
  • ScheduleDispatcher
  • @@ -61,8 +62,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -92,52 +93,43 @@ CLIENTS in a SET_CLIENT collection, which are not occupied by human players.

    AI_Cap -

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Air -- Execute Combat Air Patrol (CAP).

    +

    AI - Execute Combat Air Patrol (CAP).

    Banner Image


    -

    1) #AICAPZONE class, extends AICAP#AIPATROL_ZONE

    - -

    The #AICAPZONE class implements the core functions to patrol a Zone by an AI Controllable or Group -and automatically engage any airborne enemies that are within a certain range or within a certain zone.

    +

    AI CAP classes makes AI Controllables execute a Combat Air Patrol.

    AI_Cas -

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Air -- -Provide Close Air Support to friendly ground troops.

    +

    AI -- Provide Close Air Support to friendly ground troops.

    Banner Image


    -

    1) #AICASZONE class, extends AIPatrol#AIPATROL_ZONE

    - -

    #AICASZONE derives from the AIPatrol#AIPATROL_ZONE, inheriting its methods and behaviour.

    +

    AI CAS classes makes AI Controllables execute a Close Air Support.

    AI_Patrol -

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Air -- -Air Patrolling or Staging.

    +

    AI -- Air Patrolling or Staging.

    Banner Image


    -

    1) #AIPATROLZONE class, extends Fsm#FSM_CONTROLLABLE

    - -

    The #AIPATROLZONE class implements the core functions to patrol a Zone by an AI Controllable or Group.

    +

    AI PATROL classes makes AI Controllables execute an Patrol.

    Account -

    (SP) (MP) (FSM) Account for (Detect, count and report) DCS events occuring on DCS objects (units).

    +

    Actions - ACTACCOUNT classes account for (detect, count & report) various DCS events occuring on Units.

    Detection -

    This module contains the DETECTION classes.

    +

    Functional - DETECTION_ classes model the detection of enemy units by FACs or RECCEs and group them according various methods.

    Group -

    This module contains the GROUP class.

    +

    Wrapper -- GROUP is a wrapper class for the DCS Class Group.

    Identifiable

    This module contains the IDENTIFIABLE class.

    -
    MOVEMENT -

    Limit the simultaneous movement of Groups within a running Mission.

    A MISSION is the main owner of a Mission orchestration within MOOSE .

    +
    Movement +

    Limit the simultaneous movement of Groups within a running Mission.

    +
    Radio +

    Core - The RADIO class is responsible for transmitting radio communications.

    Scheduler -

    This module contains the SCHEDULER class.

    +

    Core - SCHEDULER prepares and handles the execution of functions over scheduled time (intervals).

    Set -

    Core - SET classes define collections of objects to perform bulk actions and logically group objects.

    +

    Core - SET_ classes define collections of objects to perform bulk actions and logically group objects.

    Task_A2G -

    (AI) (SP) (MP) Tasking for Air to Ground Processes.

    +

    Tasking - The TASK_A2G models tasks for players in Air to Ground engagements.

    +
    Task_A2G_Dispatcher +

    Tasking - The TASKA2GDISPATCHER creates and manages player TASK_A2G tasks based on detected targets.

    Task_PICKUP

    This module contains the TASK_PICKUP classes.

    -
    Task_SEAD -

    This module contains the TASK_SEAD classes.

    Unit -

    This module contains the UNIT class.

    +

    Wrapper - UNIT is a wrapper class for the DCS Class Unit.