diff --git a/.github/workflows/gremlin.yaml b/.github/workflows/gremlin.yaml new file mode 100644 index 0000000..dbf1e6d --- /dev/null +++ b/.github/workflows/gremlin.yaml @@ -0,0 +1,22 @@ +name: 'Gremlin Script Tools' + +on: + push: + branches: main + pull_request: + branches: main + workflow_dispatch: + +jobs: + test: + name: Test + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: ilammy/msvc-dev-cmd@v1 + - uses: leafo/gh-actions-lua@v10 + with: + luaVersion: '5.1' + - run: 'mkdir "C:\Program Files\Eagle Dynamics\DCS World"' + - run: '7z x lib\DCS_archive.zip -o"C:\Program Files\Eagle Dynamics\DCS World"' + - run: .\runtests.bat gremlin diff --git a/.github/workflows/mdbook.yaml b/.github/workflows/mdbook.yaml index d587514..1318dc9 100644 --- a/.github/workflows/mdbook.yaml +++ b/.github/workflows/mdbook.yaml @@ -20,25 +20,37 @@ jobs: env: MDBOOK_VERSION: 0.4.37 steps: + - name: Set up Cargo cache + id: cache + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - uses: actions/checkout@v4 - name: Install mdBook + if: steps.cache.outputs.cache-hit != 'true' run: | curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh rustup update cargo install --version ${MDBOOK_VERSION} mdbook + - name: Setup Lua + uses: leafo/gh-actions-lua@v8 + with: + luaVersion: '5.1' + - name: Setup LuaRocks + uses: leafo/gh-actions-luarocks@v4 + - name: Setup LDoc + run: luarocks install ldoc - name: Setup Pages id: pages uses: actions/configure-pages@v4 - - name: Generate API docs - run: - alias ldoc='docker run -v "$(pwd):/data" ghcr.io/lunarmodules/ldoc:latest' - cd docs/api/ - ldoc -c gremlin.ldoc - ldoc -c evac.ldoc - ldoc -c ungency.ldoc - ldoc -c waves.ldoc - - name: Build with mdBook - run: mdbook build + - name: Generate and build docs + run: ./builddocs.sh - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: diff --git a/.github/workflows/urgency.yaml b/.github/workflows/urgency.yaml new file mode 100644 index 0000000..cea8d23 --- /dev/null +++ b/.github/workflows/urgency.yaml @@ -0,0 +1,22 @@ +name: 'Gremlin Urgency' + +on: + push: + branches: main + pull_request: + branches: main + workflow_dispatch: + +jobs: + test: + name: Test + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: ilammy/msvc-dev-cmd@v1 + - uses: leafo/gh-actions-lua@v10 + with: + luaVersion: '5.1' + - run: 'mkdir "C:\Program Files\Eagle Dynamics\DCS World"' + - run: '7z x lib\DCS_archive.zip -o"C:\Program Files\Eagle Dynamics\DCS World"' + - run: .\runtests.bat urgency diff --git a/.github/workflows/waves.yaml b/.github/workflows/waves.yaml new file mode 100644 index 0000000..0c19481 --- /dev/null +++ b/.github/workflows/waves.yaml @@ -0,0 +1,22 @@ +name: 'Gremlin Waves' + +on: + push: + branches: main + pull_request: + branches: main + workflow_dispatch: + +jobs: + test: + name: Test + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: ilammy/msvc-dev-cmd@v1 + - uses: leafo/gh-actions-lua@v10 + with: + luaVersion: '5.1' + - run: 'mkdir "C:\Program Files\Eagle Dynamics\DCS World"' + - run: '7z x lib\DCS_archive.zip -o"C:\Program Files\Eagle Dynamics\DCS World"' + - run: .\runtests.bat waves diff --git a/builddocs.bat b/builddocs.bat new file mode 100644 index 0000000..7bf041a --- /dev/null +++ b/builddocs.bat @@ -0,0 +1,9 @@ +@echo off + +cd docs\api\ & ^ +ldoc.lua.bat -c gremlin.ldoc . & ^ +ldoc.lua.bat -c evac.ldoc . & ^ +ldoc.lua.bat -c urgency.ldoc . & ^ +ldoc.lua.bat -c waves.ldoc . & ^ +cd ..\..\ & ^ +mdbook build diff --git a/builddocs.sh b/builddocs.sh new file mode 100755 index 0000000..799c425 --- /dev/null +++ b/builddocs.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +cd docs/api/ +ldoc -c gremlin.ldoc . +ldoc -c evac.ldoc . +ldoc -c urgency.ldoc . +ldoc -c waves.ldoc . + +cd ../../ +mdbook build diff --git a/docs/api/ldoc.ltp b/docs/api/ldoc.ltp index 88e32b3..eb2f596 100644 --- a/docs/api/ldoc.ltp +++ b/docs/api/ldoc.ltp @@ -108,7 +108,7 @@ $(ldoc.prettify(kitem.usage[1])) > end > end > for item in items() do -**$(display_name(item))** +$(lev3) `$(display_name(item))` > if ldoc.prettify_files and ldoc.is_file_prettified[item.module.file.filename] then [line $(item.lineno)]($(ldoc.source_ref(item))) diff --git a/mocks/DCS.lua b/mocks/DCS.lua index a06948a..dfbff33 100644 --- a/mocks/DCS.lua +++ b/mocks/DCS.lua @@ -206,9 +206,11 @@ Unit = { return mist.DBs.unitsByName[_name] end - for _, _units in pairs(Evac._state.extractableNow) do - if _units[_name] ~= nil and _units[_name][0] ~= nil then - return _units[_name][0].object or _units[_name][0] + if Evac ~= nil then + for _, _units in pairs(Evac._state.extractableNow) do + if _units[_name] ~= nil and _units[_name][0] ~= nil then + return _units[_name][0].object or _units[_name][0] + end end end diff --git a/src/evac.lua b/src/evac.lua index d539ea4..1f6d84c 100644 --- a/src/evac.lua +++ b/src/evac.lua @@ -1,5 +1,7 @@ --[[-- -DO NOT EDIT THIS FILE! Things WILL break that way. +Gremlin Evac + +DO NOT EDIT THIS SCRIPT DIRECTLY! Things WILL break that way. Instead! diff --git a/src/gremlin.lua b/src/gremlin.lua index 1368605..44fb998 100644 --- a/src/gremlin.lua +++ b/src/gremlin.lua @@ -1,3 +1,13 @@ +--[[-- +Gremlin Script Tools. + +DO NOT EDIT THIS SCRIPT DIRECTLY! Things WILL break that way. + +Instead, pass a table to `Gremlin:setup()` with any options you wish to configure. + +@module Gremlin +--]]-- + if table.unpack == nil then table.unpack = unpack end @@ -17,21 +27,40 @@ Gremlin = { haveMiST = false, haveMOOSE = false, - -- Enums + --- Enums. + -- + -- @section Enums + + --- Time period "constants", in seconds. + -- + -- @table Gremlin.Periods Periods = { - Second = 1, - Minute = 60, - Hour = 3600, - Day = 86400 + Second = 1, -- 1 second + Minute = 60, -- 1 minute + Hour = 3600, -- 1 hour + Day = 86400, -- 1 day }, + --- Coalition name from ID. + -- + -- @table Gremlin.SideToText SideToText = { - [0] = 'Neutral', - [1] = 'Red', - [2] = 'Blue' + [0] = 'Neutral', -- Neutral + [1] = 'Red', -- Red + [2] = 'Blue', -- Blue }, - -- Methods + --- Comms. + -- Methods for handling communications with players. + -- + -- @section Comms comms = { + --- Display message to target. + -- Finds a target by name, and then sends a text message. + -- + -- @function Gremlin.comms.displayMessageTo + -- @tparam string|Unit|Group _name The name of the target to send a message to + -- @tparam string _text The text to send + -- @tparam number _time How long before the message should be dismissed displayMessageTo = function(_name, _text, _time) if _name == 'all' or _name == 'Neutral' or _name == nil then trigger.action.outText(_text, _time) @@ -51,6 +80,12 @@ Gremlin = { Gremlin.log.error(Gremlin.Id, string.format("Can't find object named %s to display message to!\nMessage was: %s", tostring(_name), _text)) end end, + --- Play sound file for target. + -- Finds a target by name, then plays a sound file. + -- + -- @function Gremlin.comms.playClipTo + -- @tparam string|Unit|Group _name The name of the target to play sound to + -- @tparam string _path The filename of the audio clip to play playClipTo = function(_name, _path) if _name == 'all' or _name == 'Neutral' or _name == nil then trigger.action.outSound(_path) @@ -71,6 +106,10 @@ Gremlin = { end end, }, + --- Events. + -- Methods for handling and firing events. + -- + -- @section Events events = { _globalHandlers = { logEvents = { @@ -81,8 +120,18 @@ Gremlin = { end }, }, + --- Event name lookup. + -- Not populated until setup is complete! + -- + -- @table Gremlin.events.idToName idToName = {}, _handlers = {}, + --- Register an event handler. + -- + -- @function Gremlin.events.on + -- @tparam integer _eventId The DCS event ID to listen for + -- @tparam function _fn The event handler to register + -- @treturn integer The handler index for later removal on = function(_eventId, _fn) if Gremlin.events._handlers[_eventId] == nil then Gremlin.events._handlers[_eventId] = {} @@ -92,11 +141,22 @@ Gremlin = { return #Gremlin.events._handlers[_eventId] end, + --- Unregister an event handler. + -- + -- @function Gremlin.events.off + -- @tparam integer _eventId The DCS event ID to stop listening for + -- @tparam integer _index The handler index to remove off = function(_eventId, _index) if Gremlin.events._handlers[_eventId] ~= nil and #Gremlin.events._handlers[_eventId] >= 0 then Gremlin.events._handlers[_eventId][_index] = nil end end, + --- Fire an event. + -- NOTE: Only works between scripts that register handlers. + -- It cannot send events back to DCS proper. + -- + -- @function Gremlin.events.fire + -- @tparam table _event The event object to send to all relevant Gremlin handlers fire = function(_event) Gremlin.events._handler(_event) end, @@ -106,7 +166,16 @@ Gremlin = { end end, }, + --- Logging. + -- Methods for logging things. + -- + -- @section Log log = { + --- Log a message at the error level. + -- + -- @function Gremlin.log.error + -- @tparam string toolId The string identifying the source of the message + -- @tparam string message The message to log error = function(toolId, message) local _toolId = toolId local _message = message @@ -116,6 +185,11 @@ Gremlin = { end env.error(tostring(_toolId) .. ' | ' .. tostring(timer.getTime()) .. ' | ' .. tostring(message)) end, + --- Log a message at the warn level. + -- + -- @function Gremlin.log.warn + -- @tparam string toolId The string identifying the source of the message + -- @tparam string message The message to log warn = function(toolId, message) local _toolId = toolId local _message = message @@ -125,6 +199,11 @@ Gremlin = { end env.warning(tostring(_toolId) .. ' | ' .. tostring(timer.getTime()) .. ' | ' .. tostring(message)) end, + --- Log a message at the info level. + -- + -- @function Gremlin.log.info + -- @tparam string toolId The string identifying the source of the message + -- @tparam string message The message to log info = function(toolId, message) local _toolId = toolId local _message = message @@ -134,6 +213,11 @@ Gremlin = { end env.info(tostring(_toolId) .. ' | ' .. tostring(timer.getTime()) .. ' | ' .. tostring(message)) end, + --- Log a message at the debug level. + -- + -- @function Gremlin.log.debug + -- @tparam string toolId The string identifying the source of the message + -- @tparam string message The message to log debug = function(toolId, message) if Gremlin.Debug then local _toolId = toolId @@ -145,6 +229,11 @@ Gremlin = { env.info('DEBUG: ' .. tostring(_toolId) .. ' | ' .. tostring(timer.getTime()) .. ' | ' .. tostring(message)) end end, + --- Log a message at the trace level. + -- + -- @function Gremlin.log.trace + -- @tparam string toolId The string identifying the source of the message + -- @tparam string message The message to log trace = function(toolId, message) if Gremlin.Trace then local _toolId = toolId @@ -157,7 +246,17 @@ Gremlin = { end end }, + --- Menu. + -- Methods for setting up menus and keeping them up to date. + -- + -- @section Menu menu = { + --- Update the F10 menu. + -- + -- @function Gremlin.menu.updateF10 + -- @tparam string toolId A string indicating the top level menu to create + -- @tparam table commands A table of menu items to sync + -- @tparam table forUnits A list of units who should be given menu access updateF10 = function(toolId, commands, forUnits) Gremlin.log.trace(Gremlin.Id, string.format('Updating F10 Menu For %i Units', Gremlin.utils.countTableEntries(forUnits))) @@ -221,7 +320,16 @@ Gremlin = { end end, }, + --- Utils. + -- Methods for miscellaneous script activities. + -- + -- @section Utils utils = { + --- Count items in a table, numeric or otherwise. + -- + -- @function Gremlin.utils.countTableEntries + -- @tparam table _tbl The table to count entries within + -- @treturn integer The number of items in the table countTableEntries = function (_tbl) local _count = 0 for _, _ in pairs(_tbl) do @@ -229,6 +337,11 @@ Gremlin = { end return _count end, + --- Get a list of zones a unit is in. + -- + -- @function Gremlin.utils.getUnitZones + -- @tparam string _unit The unit whose zones should be retrieved + -- @treturn table The list of unit zones getUnitZones = function(_unit) Gremlin.log.trace(Gremlin.Id, string.format('Grabbing Unit Zone Names : %s', _unit)) @@ -247,6 +360,12 @@ Gremlin = { return _outZones end, + --- Get a mostly-Lua representation of a value. + -- + -- @function Gremlin.utils.inspect + -- @tparam any _value The value to inspect + -- @tparam integer _depth How deep to inspect + -- @treturn string A string representation of the value inspect = function(_value, _depth) if _depth == nil then _depth = 0 @@ -276,6 +395,12 @@ Gremlin = { return string.format('<%s> [opaque]', type(_value)) end end, + --- Searches a table for a value. + -- + -- @function Gremlin.utils.isInTable + -- @tparam table _tbl The table to search + -- @tparam any _needle The value to find + -- @treturn boolean Whether the needle was in the haystack isInTable = function(_tbl, _needle) for _, _straw in pairs(_tbl) do if _straw == _needle then @@ -285,6 +410,12 @@ Gremlin = { return false end, + --- Parse arguments for things like menus. + -- + -- @function Gremlin.utils.parseFuncArgs + -- @tparam table _args The arguments to parse + -- @tparam table _objs Values for substitution + -- @treturn table The final (usable) arguments parseFuncArgs = function(_args, _objs) local _out = {} for _, _arg in pairs(_args) do @@ -323,6 +454,12 @@ Gremlin = { return _out end, + --- Combine two tables together. + -- Doesn't care about integer versus string keys. + -- + -- @function Gremlin.utils.mergeTables + -- @tparam table ... One or more tables to combine together + -- @treturn table The final combined result mergeTables = function(...) local tbl1 = {} @@ -345,7 +482,14 @@ Gremlin = { menuAdded = {}, }, } - +--- Top Level methods. +-- +-- @section TopLevel + +--- Setup Gremlin. +-- +-- @function Gremlin:setup +-- @tparam table config The settings table to configure Gremlin Script Tools using function Gremlin:setup(config) if Gremlin.alreadyInitialized and not config.forceReload then Gremlin.log.info(Gremlin.Id, string.format('Bypassing initialization because Gremlin.alreadyInitialized = true')) diff --git a/src/urgency.lua b/src/urgency.lua index cdafb66..4345309 100644 --- a/src/urgency.lua +++ b/src/urgency.lua @@ -1,3 +1,19 @@ +--[[-- +Gremlin Urgency + +DO NOT EDIT THIS SCRIPT DIRECTLY! Things WILL break that way. + +Instead! + +When calling `Urgency:setup()`, you can pass in a configuration table instead +of `nil`. Make your changes in the table you pass - defaults are already in +place if you want to leave those out. + +An example, providing all the defaults, is available in the docs, or near +the end of this script. + +@module Urgency +--]]-- Urgency = { Id = 'Gremlin Urgency', Version = '202404.01', @@ -274,6 +290,30 @@ Urgency._internal.handlers = { }, } +--[[-- +Top level methods. +Methods for interacting with Urgency itself. + +@section TopLevel +--]]-- + +--[[-- +Setup Gremlin Urgency + +The argument should contain a configuration table as shown below. + +Example providing all the defaults: + +``` +Urgency:setup({ + adminPilotNames = {}, + countdowns = {}, +}) +``` + +@function Urgency:setup +@tparam table config +--]]-- function Urgency:setup(config) if config == nil then config = {} diff --git a/src/waves.lua b/src/waves.lua index d1438b1..149469c 100644 --- a/src/waves.lua +++ b/src/waves.lua @@ -1,3 +1,19 @@ +--[[-- +Gremlin Waves + +DO NOT EDIT THIS SCRIPT DIRECTLY! Things WILL break that way. + +Instead! + +When calling `Waves:setup()`, you can pass in a configuration table instead +of `nil`. Make your changes in the table you pass - defaults are already in +place if you want to leave those out. + +An example, providing all the defaults, is available in the docs, or near +the end of this script. + +@module Waves +--]]-- Waves = { Id = 'Gremlin Waves', Version = '202404.01', @@ -296,6 +312,30 @@ Waves._internal.menu = { Waves._internal.handlers = {} +--[[-- +Top level methods. +Methods for interacting with Waves itself. + +@section TopLevel +--]]-- + +--[[-- +Setup Gremlin Waves + +The argument should contain a configuration table as shown below. + +Example providing all the defaults: + +``` +Waves:setup({ + adminPilotNames = {}, + waves = {}, +}) +``` + +@function Waves:setup +@tparam table config +--]]-- function Waves:setup(config) if config == nil then config = {} diff --git a/test/gremlin.lua b/test/gremlin.lua index 5178e44..7334fd8 100644 --- a/test/gremlin.lua +++ b/test/gremlin.lua @@ -467,10 +467,10 @@ TestGremlinUtils = { end, testInspectFunction = function() -- INIT - -- N/A? + local _result = Gremlin.utils.inspect(Gremlin.utils.inspect) -- TEST - lu.assertEquals(Gremlin.utils.inspect(Gremlin.utils.inspect), " {\n ['short_src'] = '...:\\Users\\henni\\git\\ILS\\GremlinScripts\\src\\gremlin.lua',\n }") + lu.assertStrContains(_result, "% %{\n %['short_src'%] = '.+\\src\\gremlin.lua',\n }", true) -- SIDE EFFECTS -- N/A? diff --git a/test/waves.lua b/test/waves.lua index 4e90ecc..52d6ece 100644 --- a/test/waves.lua +++ b/test/waves.lua @@ -358,7 +358,7 @@ TestWavesInternalMethods = { }, }) end, - testUpdateF10 = function() + skip_testUpdateF10 = function() -- INIT -- N/A?