From 5e35d482164b8b81dc3734c6bd015c0892a68aae Mon Sep 17 00:00:00 2001 From: Matt Wagner Date: Thu, 30 Sep 2021 09:01:29 -0700 Subject: [PATCH] Adding comlink package and subcommand handler utility --- src/aliases/comlink-info/aliases.json | 7 + src/scripts/autoresearch/autoresearch.lua | 55 ++++--- src/scripts/comlink-info/comlink-info.lua | 133 +++++++++++++++++ src/scripts/comlink-info/scripts.json | 5 + src/scripts/info-panel/info-panel.lua | 12 -- src/scripts/setup/scripts.json | 3 + src/scripts/setup/setup.lua | 21 +-- src/scripts/setup/util.lua | 139 ++++++++++++++++++ .../comlink-info/inv-comlink-info.lua | 11 ++ src/triggers/comlink-info/store-comlink.lua | 5 + src/triggers/comlink-info/triggers.json | 50 +++++++ 11 files changed, 379 insertions(+), 62 deletions(-) create mode 100644 src/aliases/comlink-info/aliases.json create mode 100644 src/scripts/comlink-info/comlink-info.lua create mode 100644 src/scripts/comlink-info/scripts.json create mode 100644 src/scripts/setup/util.lua create mode 100644 src/triggers/comlink-info/inv-comlink-info.lua create mode 100644 src/triggers/comlink-info/store-comlink.lua create mode 100644 src/triggers/comlink-info/triggers.json diff --git a/src/aliases/comlink-info/aliases.json b/src/aliases/comlink-info/aliases.json new file mode 100644 index 0000000..35abd4b --- /dev/null +++ b/src/aliases/comlink-info/aliases.json @@ -0,0 +1,7 @@ +[ + { + "name": "comlinks", + "regex": "^comlinks( ?.*)$", + "script": "lotj.comlinkInfo.command(matches[2])" + } +] \ No newline at end of file diff --git a/src/scripts/autoresearch/autoresearch.lua b/src/scripts/autoresearch/autoresearch.lua index f7520f8..4cbe40f 100644 --- a/src/scripts/autoresearch/autoresearch.lua +++ b/src/scripts/autoresearch/autoresearch.lua @@ -1,3 +1,5 @@ +-- Based on AutoResearch script by @ZakattackLOTJ + lotj = lotj or {} lotj.autoResearch = lotj.autoResearch or {} @@ -8,10 +10,9 @@ function lotj.autoResearch.log(text, precedingNewline) cecho("[LOTJ AutoResearch] "..text.."\n") end -function lotj.autoResearch.command(args) - argList = splitargs(args) - - if #argList == 1 and argList[1] == "start" then +local subcommands = {{ + args = {"start"}, + action = function() lotj.autoResearch.enabled = true lotj.autoResearch.researchList = {} lotj.autoResearch.log("Research list cleared.") @@ -19,8 +20,11 @@ function lotj.autoResearch.command(args) enableTrigger("autoresearch.grabSkills") lotj.autoResearch.log("Retrieving research list...") send("practice", false) - - elseif #argList == 1 and argList[1] == "next" then + end, + helpText = "Get the practice list and automatically begin researching anything eligible for it. Must be in a library." +},{ + args = {"next"}, + action = function() if #(lotj.autoResearch.researchList or {}) == 0 then lotj.autoResearch.log("Research list empty.") lotj.autoResearch.enabled = false @@ -29,8 +33,11 @@ function lotj.autoResearch.command(args) table.remove(lotj.autoResearch.researchList, 1) expandAlias("autoresearch continue", false) - - elseif #argList == 1 and argList[1] == "continue" then + end, + helpText = "Resume researching the first skill in the current autoresearch list." +},{ + args = {"continue"}, + action = function() if #(lotj.autoResearch.researchList or {}) == 0 then lotj.autoResearch.log("Research list empty.") lotj.autoResearch.enabled = false @@ -40,30 +47,18 @@ function lotj.autoResearch.command(args) local current = lotj.autoResearch.initialCount - #lotj.autoResearch.researchList + 1 lotj.autoResearch.log(current.."/"..lotj.autoResearch.initialCount..": Researching "..lotj.autoResearch.researchList[1].."...") send("research "..lotj.autoResearch.researchList[1], false) - - elseif #argList == 1 and argList[1] == "stop" then + end, + helpText = "Skip to the next skill in the autoresearch list and begin researching it." +},{ + args = {"stop"}, + action = function() lotj.autoResearch.enabled = false lotj.autoResearch.researchList = {} lotj.autoResearch.log("Research list cleared.") + end, + helpText = "Clear the autoresearch list and disable triggers for continuing automatically." +}} - else - lotj.autoResearch.log("AutoResearch Command List\n") - cecho([[ -autoresearch start - -Get the practice list and automatically begin researching anything eligible for it. Must be in a library. - -autoresearch continue - -Resume researching the first skill in the current autoresearch list. - -autoresearch next - -Skip to the next skill in the autoresearch list and begin researching it. - -autoresearch stop - -Clear the autoresearch list and disable triggers for continuing automatically. -]]) - end +function lotj.autoResearch.command(args) + processCommand("autoresearch", subcommands, args) end \ No newline at end of file diff --git a/src/scripts/comlink-info/comlink-info.lua b/src/scripts/comlink-info/comlink-info.lua new file mode 100644 index 0000000..7c95a21 --- /dev/null +++ b/src/scripts/comlink-info/comlink-info.lua @@ -0,0 +1,133 @@ +-- Comlink info script by Johnson +-- Translated to Mudlet by Kbug + +lotj = lotj or {} +lotj.comlinkInfo = lotj.comlinkInfo or {} + +function lotj.comlinkInfo.setup() + lotj.comlinkInfo.loadForChar() + lotj.setup.registerEventHandler("gmcp.Char.Info", lotj.comlinkInfo.loadForChar) +end + +function lotj.comlinkInfo.log(text, precedingNewline) + if precedingNewline then + echo("\n") + end + cecho("[LOTJ Comlinks] "..text.."\n") +end + +function lotj.comlinkInfo.loadForChar() + local charName = gmcpVarByPath("Char.Info.name") + if charName and io.exists(getMudletHomeDir() .. "/comlinkdata_" .. charName .. ".lua") then + table.load(getMudletHomeDir() .. "/comlinkdata_" .. charName .. ".lua", lotj.comlinkInfo.comlinks) + if lotj.comlinkInfo.comlinks then + local comlinkCount = 0 + for _, _ in pairs(lotj.comlinkInfo.comlinks) do + comlinkCount = comlinkCount+1 + end + lotj.comlinkInfo.log("Loaded data for "..comlinkCount.." comlinks.") + end + end + + if not lotj.comlinkInfo.comlinks then + lotj.comlinkInfo.comlinks = {} + end +end + +function lotj.comlinkInfo.saveForChar() + local charName = gmcpVarByPath("Char.Info.name") + if charName then + table.save(getMudletHomeDir() .. "/comlinkdata_" .. charName .. ".lua", lotj.comlinkInfo.comlinks) + else + lotj.comlinkInfo.log("No character name detected for saving comlink data.") + end +end + +function lotj.comlinkInfo.cleanupComlinkName(line) + line = line:gsub("%(Humming%)", '') + line = string.trim(line:gsub("%(%d+%)", '')) + line = line:gsub('"', '') + line = line:gsub("'", '') + return line +end + +function lotj.comlinkInfo.registerComlink(comlinkName, channel, encryption) + local name = lotj.comlinkInfo.cleanupComlinkName(comlinkName) + local comlink = { channel = 0, encryption = 0 } + comlink = lotj.comlinkInfo.comlinks[name] or comlink -- load an existing comlink or make a new one + if channel ~= nil then -- got a channel + if comlink.channel ~= channel then -- new channel is different than existing channel + comlink.channel = tonumber(channel) or 0 + end + end + if encryption ~= nil then -- got an encryption + if comlink.encryption ~= encryption then -- new encryption is different than existing + comlink.encryption = tonumber(encryption) or 0 + end + end + lotj.comlinkInfo.comlinks[name] = comlink -- rip out quotation marks + + lotj.comlinkInfo.saveForChar() +end + +local subcommands = {{ + args = {"reset"}, + action = function() + lotj.comlinkInfo.comlinks = {} + lotj.comlinkInfo.log("Stored comlink data erased.") + lotj.comlinkInfo.saveForChar() + end, + helpText = "Deletes the stored comlink list." +},{ + args = {"delete", "comlink:string"}, + action = function(name) + for i, v in pairs(lotj.comlinkInfo.comlinks) do + -- search by comlink keyword or note keyword + if string.find(i:lower(), name:lower(), 0, true) or (v.note and (string.find(v.note:lower(), name:lower(), 0, true))) then + lotj.comlinkInfo.log("Comlink '" .. i .. "' removed from stored comlinks.") + lotj.comlinkInfo.comlinks[i] = nil + lotj.comlinkInfo.saveForChar() + return + end + end + lotj.comlinkInfo.log("Comlink '" .. name .. "' not found in comlinks.") + end, + helpText = "Deletes the first comlink matching the specified keyword from the comlink list.\n".. + "Notice: Will match either the comlink description or the note attached to that comlink." +},{ + args = {"notes"}, + action = function() + lotj.comlinkInfo.log("Listing all stored comlinks with notes:") + for i, v in pairs(lotj.comlinkInfo.comlinks) do + if v.note then + cecho(" " .. i .. " -> (Note:" .. v.note .. ")\n") + end + end + end, + helpText = "Prints a list of every comlink that has a note." +},{ + args = {"note", "comlink:string", "note:string?"}, + action = function(com, note) + for i, v in pairs(lotj.comlinkInfo.comlinks) do + if string.find(i:lower(), com:lower(), 0, true) or (v.note and (string.find(v.note:lower(), com:lower(), 0, true))) then -- search by comlink keyword or note keyword + if note == "" or note == nil then + lotj.comlinkInfo.log("Removed '"..v.note.."' as note from comlink '"..i.."'.") + v.note = nil + else + v.note = note + lotj.comlinkInfo.log("Added '"..v.note.."' as note to comlink '"..i.."'.") + end + lotj.comlinkInfo.saveForChar() + return + end + end + lotj.comlinkInfo.log("Comlink '"..com.."' note found in comlinks.") + end, + helpText = "Adds a note to the first comlink matching the specified keyword.\n".. + "Notice: Will match either the comlink description or the note attached to that comlink.\n".. + "Use quotes around multi-word notes, as in: comlink note newbtech \"Secret channel for Anakin\"" +}} + +function lotj.comlinkInfo.command(args) + processCommand("comlinks", subcommands, args) +end diff --git a/src/scripts/comlink-info/scripts.json b/src/scripts/comlink-info/scripts.json new file mode 100644 index 0000000..94edf13 --- /dev/null +++ b/src/scripts/comlink-info/scripts.json @@ -0,0 +1,5 @@ +[ + { + "name": "comlink-info" + } +] diff --git a/src/scripts/info-panel/info-panel.lua b/src/scripts/info-panel/info-panel.lua index e22e95a..03c5a6a 100644 --- a/src/scripts/info-panel/info-panel.lua +++ b/src/scripts/info-panel/info-panel.lua @@ -52,18 +52,6 @@ local function styleGaugeText(gauge, fontSize) gauge:setFontSize(fontSize) end -local function gmcpVarByPath(varPath) - local temp = gmcp - for varStep in varPath:gmatch("([^\\.]+)") do - if temp and temp[varStep] then - temp = temp[varStep] - else - return nil - end - end - return temp -end - -- Wires up GMCP subscriptions for a gauge. -- statName is the short version of the stat name to show after the value (mv, hp, etc) local function wireGaugeUpdate(gauge, valueVarName, maxVarName, statName, eventName) diff --git a/src/scripts/setup/scripts.json b/src/scripts/setup/scripts.json index ad46a83..c33abc0 100644 --- a/src/scripts/setup/scripts.json +++ b/src/scripts/setup/scripts.json @@ -1,5 +1,8 @@ [ { "name": "setup" + }, + { + "name": "util" } ] diff --git a/src/scripts/setup/setup.lua b/src/scripts/setup/setup.lua index f210d16..e05455c 100644 --- a/src/scripts/setup/setup.lua +++ b/src/scripts/setup/setup.lua @@ -24,6 +24,7 @@ local function setup() lotj.infoPanel.setup() lotj.mapper.setup() lotj.systemMap.setup() + lotj.comlinkInfo.setup() -- Then set our UI default view lotj.layout.selectTab(lotj.layout.upperRightTabData, "map") @@ -73,23 +74,3 @@ lotj.setup.registerEventHandler("sysProtocolEnabled", function(_, protocol) sendGMCP("Core.Supports.Set", "[\"Ship 1\"]") end end) - -function splitargs(args) - local retval = {} - local spat, epat, buf, quoted = [=[^(['"])]=], [=[(['"])$]=] - for str in args:gmatch("%S+") do - local squoted = str:match(spat) - local equoted = str:match(epat) - local escaped = str:match([=[(\*)['"]$]=]) - if squoted and not quoted and not equoted then - buf, quoted = str, squoted - elseif buf and equoted == quoted and #escaped % 2 == 0 then - str, buf, quoted = buf .. ' ' .. str, nil, nil - elseif buf then - buf = buf .. ' ' .. str - end - if not buf then table.insert(retval, (str:gsub(spat,""):gsub(epat,""))) end - end - if buf then error("Missing matching quote for "..buf) end - return retval -end diff --git a/src/scripts/setup/util.lua b/src/scripts/setup/util.lua new file mode 100644 index 0000000..85412f8 --- /dev/null +++ b/src/scripts/setup/util.lua @@ -0,0 +1,139 @@ +function splitargs(args) + local retval = {} + local spat, epat, buf, quoted = [=[^(['"])]=], [=[(['"])$]=] + for str in args:gmatch("%S+") do + local squoted = str:match(spat) + local equoted = str:match(epat) + local escaped = str:match([=[(\*)['"]$]=]) + if squoted and not quoted and not equoted then + buf, quoted = str, squoted + elseif buf and equoted == quoted and #escaped % 2 == 0 then + str, buf, quoted = buf .. ' ' .. str, nil, nil + elseif buf then + buf = buf .. ' ' .. str + end + if not buf then table.insert(retval, (str:gsub(spat,""):gsub(epat,""))) end + end + if buf then error("Missing matching quote for "..buf) end + return retval +end + +function gmcpVarByPath(varPath) + local temp = gmcp + for varStep in varPath:gmatch("([^\\.]+)") do + if temp and temp[varStep] then + temp = temp[varStep] + else + return nil + end + end + return temp +end + +-- Handles matching argument text to a list of subcommands. +-- Each subcommand should be an object with the following properties: +-- args: List of elements to match input against, each matching: +-- a string to match exactly for the argument in this position +-- a token indicating a type of variable argument in this position, one of: +-- argName:string - any string +-- argName:string? - any string, optional +-- argName:number - any number +-- argName:number? - any number, optional +-- all optional arguments must appear after all non-optional arguments. +-- action: A function, taking each variable argument as an argument. +-- helpText: Description of what this subcommand does. +-- +-- If +-- For example: +-- local subcommands = {{ +-- "args": {"list"}, +-- "action": function() ...do stuff... end, +-- "helpText": "List the things." +-- },{ +-- "args": {"show", ""}, +-- "action": function(thingName) ...do stuff... end, +-- "helpText": "Find a given thing by name and show it" +-- }} +-- +-- This would match against the second subcommand, effectively calling your function +-- with "testName" as the argument. +-- processCommand("shopkeeper", subcommands, "show testName") +-- +-- Calling this with "help" or any nonmatching argument will print out the list of +-- subcommands and descriptions, using the commandName argument as the overall command +-- name for documentation. +function processCommand(commandName, subcommands, input) + inputArgs = splitargs(input) + + for _, subcommand in pairs(subcommands) do + local match = true + local matchArgs = {} + local foundOptArg = false + + -- Go through each subcommand arg to look for non-matches. Innocent until proven guilty. + for i, arg in ipairs(subcommand.args) do + local startIdx, _, argName, varType, argOpt = arg:find("([^:]+):?([^?]*)([?]?)") + if not startIdx then + error("Invalid subcommand argument specifier: "..arg) + elseif foundOptArg and argOpt ~= "?" then + error("Found non-optional argument ("..arg..") after optional argument.") + end + + if varType == "" then + -- Exact match for subcommand name + if inputArgs[i] ~= argName then + match = false + end + else + -- Variable. Check type and optional state + local rawValue = inputArgs[i] + + if varType == "number" then + rawValue = tonumber(rawValue) + if not rawValue then + match = false + end + end + + -- Check that optional arguments are given values, or no match + if argOpt == "?" then + foundOptArg = true + elseif not rawValue or rawValue == "" then + match = false + end + + table.insert(matchArgs, rawValue) + end + end + + -- Got too many arguments, fail + if #inputArgs > #subcommand.args then + match = false + end + + if match then + subcommand.action(unpack(matchArgs)) + return + end + end + + if not (#inputArgs == 1 and inputArgs[1] == "help") then + cecho("Invalid syntax.\n\n") + end + cecho("Available options for "..commandName.." command:") + for _, subcommand in pairs(subcommands) do + cecho("\n\n"..commandName) + for _, arg in ipairs(subcommand.args) do + local _, _, argName, varType, argOpt = arg:find("([^:]+):?([^?]*)([?]?)") + if varType == "" then + cecho(" "..argName) + elseif argOpt == "?" then + cecho(" ["..argName.."]") + else + cecho(" <"..argName..">") + end + end + cecho("\n\n"..subcommand.helpText) + end + echo("\n\n") +end \ No newline at end of file diff --git a/src/triggers/comlink-info/inv-comlink-info.lua b/src/triggers/comlink-info/inv-comlink-info.lua new file mode 100644 index 0000000..a1d6fc6 --- /dev/null +++ b/src/triggers/comlink-info/inv-comlink-info.lua @@ -0,0 +1,11 @@ +if not lotj.comlinkInfo.comlinks then + return +end + +local line = lotj.comlinkInfo.cleanupComlinkName(matches[1]) +local comlink = lotj.comlinkInfo.comlinks[line] +if comlink ~= nil then -- found a comlink with stored data + cecho(" -> (Chan:" .. comlink.channel .. " Enc:" .. comlink.encryption .. "" .. ((comlink.note and " Note:") or "") .. "" .. ((comlink.note and comlink.note) or "") .. ")") +end + +setTriggerStayOpen("inventory-comlinks", 1) diff --git a/src/triggers/comlink-info/store-comlink.lua b/src/triggers/comlink-info/store-comlink.lua new file mode 100644 index 0000000..e993777 --- /dev/null +++ b/src/triggers/comlink-info/store-comlink.lua @@ -0,0 +1,5 @@ +lotj.comlinkInfo.registerComlink(matches.comlink, matches.channel, matches.encryption) +echo("\n") +lotj.comlinkInfo.log("Stored comlink: "..matches.comlink) +lotj.comlinkInfo.log("Stored channel: "..matches.channel) +lotj.comlinkInfo.log("Stored encryption: "..matches.encryption) diff --git a/src/triggers/comlink-info/triggers.json b/src/triggers/comlink-info/triggers.json new file mode 100644 index 0000000..a69cf93 --- /dev/null +++ b/src/triggers/comlink-info/triggers.json @@ -0,0 +1,50 @@ +[ + { + "name": "store-comlink", + "patterns": [ + { + "pattern": "^You tune (?.*) to channel (?.*)\\.$", + "type": "regex" + }, + { + "pattern": "^You set (?P.*) to encryption code (?P.*)\\.$", + "type": "regex" + }, + { + "pattern": "^(Current comlink now\\:)?(?P.*) Frequency\\:(?P.*) Encryption\\:(?P.*)$", + "type": "regex" + } + ] + }, + { + "name": "inventory-comlinks", + "fireLength": 1, + "patterns": [ + { + "pattern": "^You are carrying\\:$", + "type": "regex" + } + ], + "children": [ + { + "name": "inv-comlink-info", + "patterns": [ + { + "pattern": "^ (.*)$", + "type": "regex" + } + ] + } + ] + }, + { + "name": "equipment-comlink", + "patterns": [ + { + "pattern": "^\\<(.*)\\> +(?P.*) \\(Channel\\: (?P.*?)\\)( \\(Code\\: (?P.*?)\\))?$", + "type": "regex" + } + ], + "script": "lotj.comlinkInfo.registerComlink(matches.comlink, matches.channel, matches.encryption)" + } +] \ No newline at end of file