Modul:Coin expr

Aus Terraria Wiki
Zur Navigation springen Zur Suche springen
Siehe auch die englische Modulseite: Module:Coin expr. Sie enthält möglicherweise umfassendere oder aktuellere Informationen.
Important.svg
Diese Seite ist nicht oder unvollständig übersetzt.
Hilf mit, indem du den Text übersetzt und anschließend diesen Hinweis entfernst. Dies entfernt diese Seite auch aus der Liste von Seiten mit unzureichender Übersetzung.

Provides the functionality for {{sell expr}} and {{buy expr}}. Only parameters from the template transclusion are considered (instead of from the #invoke call), with the exception of sell, which, if set, causes the module to divide all coin values gathered via {{iteminfo}} by 5.


--------------------------------------------------------------------------------
--
-- =============================================================================
--
-- 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
            coinvalue = math.floor(coinvalue / 5)
        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 resultStr = currentFrame:callParserFunction('#expr', exprStr)
    if getBoolArg('raw') then
        return resultStr
    else
        -- format the result with `{{coin}}`
        local round = getArg('round') or 999
        return currentFrame:expandTemplate{ title = 'coin', args = {resultStr, round=round} }
	end
end

return p