Module:Coin expr
Jump to navigation
Jump to search
This module provides the functionality of the {{sell expr}} and {{buy expr}} templates.
Usage
Wikitext
The module can be called from wikitext with the functions listed below.
go
{{#invoke:Coin expr| go | sell = y/yes/1/true }}
Performs the calculation. By default, the function uses the items' buy prices. If sell is true, then it uses the items' sell values (by dividing the buy prices by 5).
This function is generally only intended to be called from the templates mentioned above. The parameters passed in the transclusions of those templates (e.g. {{sell expr|raw=yes|True Excalibur|+|True Night's Edge}}) are recognized and handled by this function. See the template documentation for details about the parameters.
--------------------------------------------------------------------------------
--
-- =============================================================================
--
-- Module:Coin expr
--
-- Calculation of expressions that involve dynamic item values
--
-- =============================================================================
--
-- Code annotations:
-- This module is documented according to LuaCATS (Lua Comment and Type System).
-- LuaCATS comments are prefixed with three dashes (---) and use Markdown syntax.
-- For a full list of annotations, see the following link:
-- https://luals.github.io/wiki/annotations/
--
--------------------------------------------------------------------------------
local trim = mw.text.trim
local iteminfo = require('Module:Iteminfo')
local itemNameData = require('Module:ItemNames').getData
---A cached version of the current frame, the interface to the parser.
local currentFrame
---Holds the arguments from the template call.
---@type table<string, string>
local inputArgs
---Whether the module should compute sell values of items, instead of buy prices.
local isSellExpr = false
---Return a trimmed version of the value of the template parameter with the specified `key`.
---Return `nil` if the parameter is unset.
---@param key string|integer
---@return string?
local function getArg(key)
local value = inputArgs[key]
if not value then
return nil
end
value = trim(value)
return value
end
---Conversion table for the `getBoolArg` function.
---@type table<string, boolean>
local stringToBoolean = {
['y'] = true, ['yes'] = true, ['1'] = true, ['on'] = true, ['true'] = true,
['n'] = false, ['no'] = false, ['0'] = false, ['off'] = false, ['false'] = false,
}
---Convert the value of the template parameter with the specified `key` to a Boolean.
---Return `default` or `nil` if the value is not a recognized Boolean string.
---@param key string|integer
---@param default boolean?
---@return boolean?
local function getBoolArg(key, default)
local value = getArg(key)
if stringToBoolean[value] ~= nil then
return stringToBoolean[value]
end
return default
end
---List of strings about erroneous parameters.
---@type string[]
local argErrors = {}
---Mark a parameter as erroneous and add an error string about it.
---@param argValue string Erroneous value of the parameter
---@param argIndex integer Index of the parameter
local function argError(argValue, argIndex)
argErrors[#argErrors + 1] = 'Parameter ' .. argIndex .. ' is not a valid item name or ID (Value: <code>' .. argValue .. '</code>)! '
end
---Combine all the error strings that were collected previously via `argError()`
---and format them with `{{error}}`.
---@return string
local function errorOutput()
local templateName = (isSellExpr and 'sell' or 'buy') .. ' expr'
local templateLink = currentFrame:expandTemplate{ title = 'tl', args = {templateName} }
local errorStr = 'Error in ' .. templateLink .. ': ' .. table.concat(argErrors)
return currentFrame:expandTemplate{ title = 'error', args = {errorStr} }
end
---Convert the input parameter to an item ID. Recognizes valid item ID strings
---and valid English item names. If the `itemNameOrId` is neither, then return
---`nil`.
---@param itemNameOrId string Input parameter value
---@return number?
local function convertToItemId(itemNameOrId)
-- check: is `itemNameOrId` an item ID string?
if (
-- is it a number?
type(tonumber(itemNameOrId)) == 'number'
-- does it not start with '+' nor '-'?
-- (`tonumber` accepts '+' and '-' at the start of the string, so we
-- need to explicitly exclude that)
and string.byte(itemNameOrId) ~= 43 and string.byte(itemNameOrId) ~= 45
-- is it in the range of valid item IDs?
and iteminfo.info.IDs.isValidWithUnused(tonumber(itemNameOrId)))
then
-- check succeeded
return tonumber(itemNameOrId)
end
-- check: is `itemNameOrId` an English item name string?
local itemId = itemNameData('itemIdFromName', itemNameOrId)
if itemId and itemId ~= '' then
-- check succeeded
return tonumber(itemId) -- itemIdFromName returns the ID as a string
end
-- both checks failed, we don't recognize `itemNameOrId` and cannot convert
-- it to an item ID
return nil
end
---Return the coin value of an item as a string for the expression string.
---@param itemNameOrId string Input parameter value
---@param argIndex integer Parameter index
---@return string coinvalue
local function coinvalueOfItem(itemNameOrId, argIndex)
if itemNameOrId == '' then
return ''
end
local itemid = convertToItemId(itemNameOrId)
if itemid then
local coinvalue = iteminfo.getItemStat(itemid, 'value')
if isSellExpr then
coinvalueFloored = math.floor(coinvalue / 5)
-- do not round to below 1
-- though if the value was already 0 before rounding, then keep that
if coinvalue > 0 and coinvalueFloored == 0 then
coinvalueFloored = 1
end
coinvalue = coinvalueFloored
end
return tostring(coinvalue)
end
-- the parameter is not a recognized item ID or English item name, so add an
-- error string about this
argError(itemNameOrId, argIndex)
return ''
end
--------------------------------------------------------------------------------
---Main return object
local p = {}
---@param frame table Interface to the parser (`mw.frame`)
p.go = function(frame)
currentFrame = frame -- global frame cache
inputArgs = currentFrame:getParent().args -- global input args cache
local sellArg = currentFrame.args['sell']
if stringToBoolean[sellArg] ~= nil then
isSellExpr = stringToBoolean[sellArg]
end
local exprStr = ''
-- iterate over all parameters and assemble the expression string
-- (e.g. {'Terra Blade', '+ 5*', 'Torch'} => '200000 + 5* 10')
local argIndex = 0
while true do
argIndex = argIndex + 1
local arg = getArg(argIndex)
if arg == nil then
-- we reached the end of the parameters
break
end
-- the parameters 1, 3, 5, ... are item names or IDs; the parameters
-- 2, 4, 6, ... are expression syntax
if argIndex % 2 == 0 then
-- the current parameter is just expression syntax, so simply append
-- it to the expression
exprStr = exprStr .. arg
else
-- the current parameter is an item name or ID, so append its coin
-- value to the expression
exprStr = exprStr .. coinvalueOfItem(arg, argIndex)
end
end
if #argErrors > 0 then
return errorOutput()
end
-- parse and compute the expression (e.g. '200000 + 5* 10' => '200050')
local success, resultStr = pcall(mw.ext.ParserFunctions.expr, exprStr)
if getBoolArg('raw') then
assert(success, 'input parameters contain invalid #expr: syntax')
return resultStr
else
if not success then
resultStr = '0'
end
-- format the result with `{{coin}}`
local round = getArg('round') or 999
return currentFrame:expandTemplate{ title = 'coin', args = {resultStr, round=round} }
end
end
return p