From f62a95d31d3b3774e24bf1c83eb6d89a7158e60f Mon Sep 17 00:00:00 2001 From: Damian Monogue <3660+demonnic@users.noreply.github.com> Date: Sat, 8 Aug 2020 15:46:22 -0400 Subject: [PATCH] Self Updating Gauges (#5) * Add self updating gauges, add some getValueAt updates back to archon * Bump version * Adjusting docs, adding some additional debugc echos * Include usage information * Update README --- README.md | 9 +- src/resources/archon.lua | 3 +- src/resources/mdkversion.txt | 2 +- src/resources/sug.lua | 201 +++++++++++++++++++++++++++++ src/scripts/MDKExample/Example.lua | 2 +- 5 files changed, 211 insertions(+), 6 deletions(-) create mode 100644 src/resources/sug.lua diff --git a/README.md b/README.md index f1cd9ec..e99970d 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,12 @@ These files contain the modules in the MDK. You only need to include those files * ftext.lua * basic fText. Documentation at https://github.com/demonnic/fText/wiki +* sortbox.lua + * SortBox, an alternative to H/VBox which can be either, and also provides options for sorting its contents. Overview at https://github.com/demonnic/MDK/wiki/SortBox + +* sug.lua + * Self Updating Gauges, will watch a set of variables and update itself on a timer based on what values those variables hold. Documentation at https://demonnic.github.io/mdk/current/classes/SUG.html + * textformatter.lua * TextFormatter, a reusable fText object. Must have ftext.lua in the same directory. Documentation at https://github.com/demonnic/fText/wiki/TextFormatter @@ -36,9 +42,6 @@ These files contain the modules in the MDK. You only need to include those files * timergauge.lua * TimerGauge, an extension of Geyser.Gauge which serves as an animated countdown timer. Overview at https://github.com/demonnic/MDK/wiki/TimerGauge -* sortbox.lua - * SortBox, an alternative to H/VBox which can be either, and also provides options for sorting its contents. Overview at https://github.com/demonnic/MDK/wiki/SortBox - * luaunit.lua * LuaUnit, for writing unit tests for your packages. Modified slightly for use with Mudlet. Documentation at https://github.com/bluebird75/luaunit diff --git a/src/resources/archon.lua b/src/resources/archon.lua index 69d1136..6f1bc8a 100644 --- a/src/resources/archon.lua +++ b/src/resources/archon.lua @@ -134,7 +134,7 @@ function Archon.verify_entry(entry) end local function digForValue(dataFrom, tableTo) - if table.size(tableTo) == 0 then + if dataFrom == nil or table.size(tableTo) == 0 then return dataFrom else local newData = dataFrom[tableTo[1]] @@ -144,6 +144,7 @@ local function digForValue(dataFrom, tableTo) end function Archon.getValueAt(accessString) + if accessString == "" then return nil end local tempTable = accessString:split("%.") local accessTable = {} for i,v in ipairs(tempTable) do diff --git a/src/resources/mdkversion.txt b/src/resources/mdkversion.txt index b0f3d96..66c4c22 100644 --- a/src/resources/mdkversion.txt +++ b/src/resources/mdkversion.txt @@ -1 +1 @@ -1.0.8 +1.0.9 diff --git a/src/resources/sug.lua b/src/resources/sug.lua new file mode 100644 index 0000000..ad1dc88 --- /dev/null +++ b/src/resources/sug.lua @@ -0,0 +1,201 @@ +--- Self Updating Gauge, extends Geyser.Gauge +--@classmod SUG +--@author Damian Monogue +--@copyright 2020 Damian Monogue +--@license MIT, see LICENSE.lua + +local SUG = { + name = "SelfUpdatingGaugeClass", + active = true, + updateTime = 333, + currentVariable = "", + maxVariable = "", + defaultCurrent = 50, + defaultMax = 100, + textTemplate = " |c/|m |p%", + strict = true, +} + +--- Creates a new Self Updating Gauge. +--@tparam table cons table of options which control the Gauge's behaviour. In addition to all valid contraints for Geyser.Gauge, SUG adds: +--
+-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +--
namedescriptiondefault
activeboolean, if true starts the timer updatingtrue
updateTimeHow often should the gauge autoupdate? Milliseconds333
currentVariableWhat variable will hold the 'current' value of the gauge? Pass the name as a string, IE "currentHP" or "gmcp.Char.Vitals.hp"""
maxVariableWhat variable will hold the 'current' value of the gauge? Pass the name as a string, IE "maxHP" or "gmcp.Char.Vitals.maxhp"""
textTemplateTemplate to use for the text on the gauge. "|c" replaced with current value, "|m" replaced with max value, "|p" replaced with the % full the gauge should be" |c/|m |p%"
defaultCurrentWhat value to use if the currentVariable points to nil or something which cannot be made a number?50
defaultMaxWhat value to use if the maxVariable points to nil or something which cannot be made a number?100
+--@param parent The Geyser container for this gauge +--@usage local SUG = require("MDK-1.sug") --the following will watch "gmcp.Char.Vitals.hp" and "gmcp.Char.Vitals.maxhp" and update itself every 333 milliseconds +-- myGauge = SUG:new({ +-- name = "myGauge", +-- currentVariable = "gmcp.Char.Vitals.hp", --if this is nil, it will use the defaultCurrent of 50 +-- maxVariable = "gmcp.Char.Vitals.maxhp", --if this is nil, it will use the defaultMax of 100. +-- height = 50, +-- }) +function SUG:new(cons, container) + local funcName = "SUG:new(cons, container)" + cons = cons or {} + local consType = type(cons) + assert(consType == "table", string.format("%s: cons as table expected, got %s", funcName, consType)) + local me = SUG.parent:new(cons, container) + setmetatable(me, self) + self.__index = self + -- apply any styling requested + if me.cssFront then + if not me.cssBack then + me.cssBack = me.cssFront .. "background-color: black;" + end + me:setStyleSheet(me.cssFront, me.cssBack, me.cssText) + end + if me.active then me:start() end + return me +end + +-- internal function, recursively digs for a value within subtables if possible +local function digForValue(dataFrom, tableTo) + if digForValue == nil or table.size(tableTo) == 0 then + return dataFrom + else + local newData = dataFrom[tableTo[1]] + table.remove(tableTo, 1) + return digForValue(newData, tableTo) + end +end + +-- Internal function, used to turn a string variable name into a value +local function getValueAt(accessString) + if accessString == "" then return nil end + local tempTable = accessString:split("%.") + local accessTable = {} + for i,v in ipairs(tempTable) do + if tonumber(v) then + accessTable[i] = tonumber(v) + else + accessTable[i] = v + end + end + return digForValue(_G, accessTable) +end + +--- Set the name of the variable the Self Updating Gauge watches for the 'current' value of the gauge +--@tparam string variableName The name of the variable to get the current value for the gauge. For instance "currentHP", "gmcp.Char.Vitals.hp" etc +function SUG:setCurrentVariable(variableName) + local nameType = type(variableName) + local funcName = "SUG:setCurrentVariable(variableName)" + assert(nameType == "string", string.format("%s: variableName as string expected, got: %s",funcName, nameType)) + local val = getValueAt(variableName) + local valType = type(tonumber(val)) + assert(valType == "number", string.format("%s: variableName must point to a variable which is a number or coercable into one. %s points to a %s", funcName, variableName, type(val))) + self.currentVariable = variableName + self:update() +end + +--- Set the name of the variable the Self Updating Gauge watches for the 'max' value of the gauge +--@tparam string variableName The name of the variable to get the max value for the gauge. For instance "maxHP", "gmcp.Char.Vitals.maxhp" etc. Set to "" to only check the current value +function SUG:setMaxVariable(variableName) + if variableName == "" then + self.maxVariable = variableName + self:update() + return + end + local nameType = type(variableName) + local funcName = "SUG:setMaxVariable(variableName)" + assert(nameType == "string", string.format("%s: variableName as string expected, got: %s",funcName, nameType)) + local val = getValueAt(variableName) + local valType = type(tonumber(val)) + assert(valType == "number", string.format("%s: variableName must point to a variable which is a number or coercable into one. %s points to a %s", funcName, variableName, type(val))) + self.maxVariable = variableName + self:update() +end + +--- Set the template for the Self Updating Gauge to set the text with. "|c" is replaced by the current value, "|m" is replaced by the max value, and "|p" is replaced by the percentage current/max +--@tparam string template The template to use for the text on the gauge. If the max value is 200 and current is 68, then |c will be replace by 68, |m replaced by 200, and |p replaced by 34. +function SUG:setTextTemplate(template) + local templateType = type(template) + local funcName = "SUG:setTextTemplate(template)" + assert(templateType == "string", string.format("%s: template as string expected, got %s", funcName, templateType)) + self.textTemplate = template + self:update() +end + +--- Stops the Self Updating Gauge from updating +function SUG:stop() + self.active = false + if self.timer then + killTimer(self.timer) + self.timer = nil + end +end + +--- Starts the Self Updating Gauge updating. If it is already updating, it will restart it. +function SUG:start() + SUG:stop() + self.active = true + self.timer = tempTimer(self.updateTime / 1000, function() self:update() end, true) +end + +--- Reads the values from currentVariable and maxVariable, and updates the gauge's value and text. +function SUG:update() + local current = getValueAt(self.currentVariable) + local max = getValueAt(self.maxVariable) + current = tonumber(current) + max = tonumber(max) + if current == nil then + current = self.defaultCurrent + debugc(string.format("Self Updating Gauge named %s is trying to update with an invalid current value. Using the defaultCurrent instead. currentVariable: '%s' maxVariable: '%s'", self.name, self.currentVariable, self.maxVariable)) + end + if max == nil then + max = self.defaultMax + if self.maxVariable ~= "" then + debugc(string.format("Self Updating Gauge named %s is trying to update with an invalid max value. Using the defaultCurrent instead. currentVariable: '%s' maxVariable: '%s'", self.name, self.currentVariable, self.maxVariable)) + end + end + local text = self.textTemplate + local percent = math.floor((current / max * 100) + 0.5) + text = text:gsub("|c", current) + text = text:gsub("|m", max) + text = text:gsub("|p", percent) + self:setValue(current, max, text) +end + +SUG.parent = Geyser.Gauge +setmetatable(SUG, Geyser.Gauge) + +return SUG diff --git a/src/scripts/MDKExample/Example.lua b/src/scripts/MDKExample/Example.lua index 61ca6df..1b3c197 100644 --- a/src/scripts/MDKExample/Example.lua +++ b/src/scripts/MDKExample/Example.lua @@ -1,4 +1,4 @@ -MDKExample = MDKExample or { version = "1.0.8" } +MDKExample = MDKExample or { version = "1.0.9" } function MDKExample.exampleFText() local fText = require("@PKGNAME@.ftext") cecho(fText.fText("Testing!", {width = 40, formatType = 'c', textColor = '', capColor = '', cap = '[TEST]'}))