Terraria Wiki

Miss the old Hydra Skin? Try out our Hydralize gadget! Visit the preferences page while logged in and turn on the gadget.

READ MORE

Terraria Wiki
Advertisement
Terraria Wiki
Lua logo.svg Documentation The documentation below is transcluded from Module:Exclusive/doc. (edit | history)

This module is the core of the dynamic platform exclusivity system.

The basic function to invoke from templates is getInfo, which queries the exclusivity information database and stores the result to a set of dplvars. These are named in the format ex_<platform>, e.g. ex_d for Desktop, and they are either empty or set to y. The function does not produce any output.

The two other functions, eicons and simpleEicons, return the HTML code for the icons denoting platform exclusivity. The first function is intended to be used only by the {{eicons}} template, the second function can be used from any other module that includes platform exclusivity icons (such as Module:Item).

Important note: This module relies entirely on the database Module:Exclusive/data. The database is not updated automatically and instead requires periodic manual updates. Please see its documentation for instructions.


---Holds the tables with the l10n information for the different languages, taken from the l10n submodule.
local l10n_data = mw.loadData('Module:Exclusive/l10n')

---Database with exclusivity info.
local exclusive_info = mw.loadData('Module:Exclusive/data')

local bit32 = require('bit32')
local trim = mw.text.trim

---Default content language of the wiki (i.e. `$wgLanguageCode`, not the value of the
---`uselang` URL parameter, and not the user's language preference setting).
local contentLanguage = mw.getContentLanguage()

---Holds the arguments from the template call.
local args_table

---The current language. Determines which l10n table to use.
local lang

---Return the l10n string associated with the `key`.
---@param key string
---@return string
local function l10n(key)
	if l10n_data[lang] then
		return l10n_data[lang][key] or l10n_data['en'][key]
	else
		return l10n_data['en'][key]
	end
end

---Return a trimmed version of the value of the template parameter with the specified `key`.
---Return `nil` if the parameter is empty or unset.
---@param key string|number
---@return string|nil
local function getArg(key)
	local value = trim(args_table[key] or '')
	return (value ~= '') and value or nil
end

---Convert a string of parameters in a `@param1:value^@param2:value^` format to a table.
---@param paramstr string
---@return table
local function parse(paramstr)
	local args = {}
	for s in string.gmatch(paramstr, '%b@^') do
		local k,v = string.match(s, '^@(.-):(.*)^$')
		args[k] = v
	end
	return args
end

---Split the `str` on each `div` in it and return the result as a table.
---This is much much faster then `mw.text.split`.
---Credit: http://richard.warburton.it.
---@param div string
---@param str string
---@return table|boolean
local function explode(div,str)
	if (div=='') then return false end
	local pos,arr = 0,{}
	-- for each divider found
	for st,sp in function() return string.find(str,div,pos,true) end do
		arr[#arr + 1] = string.sub(str,pos,st-1) -- Attach chars left of current divider
		pos = sp + 1 -- Jump past current divider
	end
	arr[#arr + 1] = string.sub(str,pos) -- Attach chars right of last divider
	return arr
end

---Return the integer defined in the database for the specified `page`.
---Perform some standardization on the `page` for that first.
---@param page string
---@return number
local function readFromDb(page)
	-- standardize pagename: remove section parts ('x#section' -> 'x') and replace underscores with spaces
	page = contentLanguage:ucfirst(string.gsub(string.gsub(page or '', '#.*', ''), '_', ' '))
	return exclusive_info[page] or 0
end

---Override the exclusivity information in `info`
---with the content of `dcom3j`.
---@param info number
---@param dcom3j string Expected format: `<d>:<c>:<o>:<m>:<3>:<j>`, with each platform either a Boolean string ("y", "0", etc.) or an empty string
---@return number
local function override(info, dcom3j)
	for k, v in pairs(explode(':', dcom3j)) do
		if v ~= '' then
			if v == '1' or v == 'y' or v == 'yes' then
				info = bit32.replace(info, 1, k-1)
			elseif v == '0' or v == 'n' or v == 'no' then
				info = bit32.replace(info, 0, k-1)
			end
		end
	end
	return info

	-- Example to demonstrate the behavior of this function:
	-- Goal: Change "dcom" to "dco3".
	-- info = 15 ("dcom"), dcom3j = ":::no:yes:"
	-- decimal 15 = binary 001111
	-- The first "no" is at index 3 in the dcom3j string, so
	-- replace the corresponding bit with a 0: 001111 -> 000111.
	-- The "yes" is at index 4, so replace the bit at index 4
	-- with a 1: 000111 -> 010111.
	-- The result is info = 23 ("dco3").
end

---Main function to retrieve exclusivity information.
---@param page string The entity to get the info about.
---@param invert boolean Whether to invert the exclusivity info.
---@param pagenot string The entity whose exclusivity info to subtract from the main one's.
---@param dcom3j string Manual exclusivity info to override the fetched one with.
---@return number info An integer that holds the exclusivity information.
local function getInfo(page, invert, pagenot, dcom3j)
	local info = 0

	-- A piece of exclusivity information is a set of Boolean values, one
	-- for each platform. This is represented as bits of the `info` integer.
	-- Each platform (Desktop, Console, Old-gen console, Mobile, 3DS,
	-- and Japanese console – "dcom3j") is assigned one bit, in this order.
	-- This means that, for instance, an `info` value of 1 would represent
	-- Desktop-only exclusivity ("d"):
	-- decimal  1 = binary 000001
	--                     j3mocd  -> "d"
	-- Similarly, an `info` value of 20 would represent Old-gen and 3DS exclusivity ("o3"):
	-- decimal 20 = binary 010100
	--                     j3mocd  -> "o3"
	-- See Module:Exclusive/data for a quick overview of all values.

	-- This system allows using bitwise operations (https://en.wikipedia.org/wiki/Bitwise_operation)
	-- instead of the formerly used string processing, resulting in much lower script execution times.


	-- get info about page
	if page then
		info = readFromDb(page)
		if invert then
			-- invert dcom3 and set j=0 (always force-off Japanese console when inverting)
			info = bit32.band(bit32.bnot(info), 31)
		end
		if pagenot then
			-- exclude some versions, depending on pagenot
			local info_not = readFromDb(pagenot)
			info = bit32.band(info, bit32.bnot(info_not))
		end

		-- The "invert" and "pagenot" functionalities above utilize
		-- bit masking (https://en.wikipedia.org/wiki/Mask_(computing)).
		-- The following example demonstrates the operations:
		-- 1. assume info=11 ("dcm", binary 001011)
		-- 2. invert:
		--    2a. not(001011) = 110100 ("o3j", the inverse of "dcm")
		--    2b. and(110100, 011111) = 010100 ("o3", forced-off "j")
		-- 3. assume info_not=4 ("o", binary 000100)
		--    3a. not(000100) = 111011
		--    3b. and(010100, 111011) = 010000 ("3")
		-- An initial exclusivity info of "dcm" was inverted to "o3",
		-- then "o" was subtracted from it, resulting in the final
		-- exclusivity information of "3".
	end

	-- override if needed
	if dcom3j == nil or dcom3j == ':::::' then
		return info
	else
		return override(info, dcom3j)
	end
end

---Return an HTML span tag whose `class` attribute is set
---according to the exclusivity `info`.
---@param info number
---@param _small boolean Whether to add the "s" class, for small icons
---@return string
local function eicons(info, _small)
	local class = 'eico' .. (_small and ' s' or '')

	if bit32.btest(info, 32) then
		-- Japanese console is set, so simply display that
		-- ("j" is always alone or not set at all – "dcj", for instance, doesn't exist)
		class = class .. ' j'
		local hovertext = l10n('text_j')
		return mw.text.tag('span', {class=class}, mw.text.tag('span', {title=hovertext}, ''))
	end

	-- for each platform of dcom3, add the class and load the hovertext if the platform if set
	-- (e.g. for info=25 ("dm3"), append "i0 i3 i4" to the class and load the
	-- "text_0", "text_3", and "text_4" l10n strings)
	local v = {}
	for i = 0, 4 do
		if bit32.btest(info, 2^i) then
			class = class .. ' i' .. i
			v[#v+1] = l10n('text_' .. i)
		end
	end
	local hovertext = mw.text.listToText(v, l10n('list_separator'), l10n('list_conjunction'))
	hovertext = hovertext .. contentLanguage:convertPlural(#v, l10n('version_plural_forms'))
	return mw.text.tag('span', {class=class}, mw.text.tag('span', {title=hovertext}, ''))
end

---Check if the `infoToCheck` integer is a valid number for output.
---It is considered invalid if it represents an empty exclusivity ("")
---or a "full" exclusivity, i.e. all platforms being set ("dcom3" or "dcom3j").
---@param infoToCheck number
---@return boolean
local function infoIsInvalid(infoToCheck)
	return infoToCheck == 0 or infoToCheck == 31 or infoToCheck == 63
end

-----------------------------------------------------------------
-- main return object
return {

-- for templates; get all exclusive info and set it in dplvars.
-- parameters: $1 = pagename
getInfo = function(frame)
	args_table = frame.args -- cache

	local info = getInfo(getArg(1), getArg('invert'), getArg('pagenot'))
	if infoIsInvalid(info) then
		frame:callParserFunction{ name = '#dplvar:set', args = {
			'ex_d', '',
			'ex_c', '',
			'ex_o', '',
			'ex_m', '',
			'ex_3', '',
			'ex_j', '',
			'ex_cached', 'y'
		} }
	else
		frame:callParserFunction{ name = '#dplvar:set', args = {
			'ex_d', bit32.btest(info, 2^0) and 'y' or '',
			'ex_c', bit32.btest(info, 2^1) and 'y' or '',
			'ex_o', bit32.btest(info, 2^2) and 'y' or '',
			'ex_m', bit32.btest(info, 2^3) and 'y' or '',
			'ex_3', bit32.btest(info, 2^4) and 'y' or '',
			'ex_j', bit32.btest(info, 2^5) and 'y' or '',
			'ex_cached', 'y'
		} }
	end
end,

-- for {{eicons}}
eicons = function(frame)
	args_table = parse(frame.args[1])-- cache

	local info = getInfo(getArg('page'), getArg('invert'), getArg('pagenot'), getArg('dcom3j'))
	if infoIsInvalid(info) then
		return frame:expandTemplate{ title = 'error', args = { l10n('eicons_error_text'), l10n('eicons_error_cate'), from = 'Eicons' } }
	end
	lang = getArg('lang') -- set lang for l10n
	return eicons(info, getArg('small'))
end,

-- simplified version of the eicons function above, for other modules such as Module:Item
simpleEicons = function(page, language, small)
	local info = getInfo(page)
	if infoIsInvalid(info) then
		return ''
	end
	lang = language -- set lang for l10n
	return eicons(info, small)
end,

}
Advertisement