Module:Currency

From EarthMC
Jump to navigation Jump to search

Documentation for this module may be created at Module:Currency/doc

-- <pre>
local p = {}

local loader = require('Module:Loader')
local getArgs = require('Module:Arguments').getArgs

local string, table, yesno, colorMdl = loader.require('String', 'Table', 'Yesno', 'Color')
local currencyData, bazaarData = loader.loadData('Currency/Data', 'Bazaar/Data')

local formatNum, shorten, toNumber = string._formatNum, string._formatShortNum, string._toNumber

local lang = mw.language.getContentLanguage()

----------------------------------------------------------------------------------------------------
-- Template:Currency access point
--
--
----------------------------------------------------------------------------------------------------
function p.currency(frame)
	local args = getArgs(frame)
	
	local s = tostring(args[1])
	if s and s:match('</font>') then return s end
	s = s and s:gsub('%s*([Ss]ky[Bb]lock)?', '')
	local m = args[2]
	local useShort = yesno(args['short'] or args['s'] , false)
	local useApproximate = yesno(args['approximate'] or args['approx'] or args['a'], false)
	local useImage = yesno(args['image'] or args['i'] or args['si'] or args['show_image'], false)
	local noLink = yesno(args['nolink'] or args['nl'], false)
	local imageInFront = yesno(args['image_in_front'] or args['iif'], false)
	local alt = args['alt']
	
	return p._currencyTemplate(s, m, useShort, useApproximate, useImage, noLink, imageInFront, alt)
end
function p._currencyTemplate(s, m, useShort, useApproximate, useImage, noLink, imageInFront, alt)
	local curr
	if s:find('[Gg]ems?') then
		s = s:gsub('[Gg]ems?', '')
		curr = 'gems'
	elseif s:find('[Bb]its?') then
		s = s:gsub('[Bb]its?', '')
		curr = 'bits'
	elseif s:find('[Pp]elts?') then
		s = s:gsub('[Pp]elts?', '')
		curr = 'pelts'
	elseif s:find('[Tt]okens?') then
		s = s:gsub('[Tt]okens?', '')
		curr = 'tokens'
	elseif s:find('[Bb]ingo [Pp]oints?') then
		s = s:gsub('[Bb]ingo [Pp]oints?', '')
		curr = 'bingoPoints'
	elseif s:find('[Nn]orth [Ss]tars?') then
		s = s:gsub('[Nn]orth [Ss]tars?', '')
		curr = 'northStars'
	elseif s:find('[Cc]opper') then
		s = s:gsub('[Cc]opper', '')
		curr = 'copper'
	elseif s:find('[Mm]otes?') then
		s = s:gsub('[Mm]otes?', '')
		curr = 'motes'
	else
		s = s:gsub('[Cc]oins?', '')
		curr = 'coins'
	end
	return p._currency(s, m, {
		useShort = useShort, 
		useApproximate = useApproximate, 
		useImage = useImage, 
		noLink = noLink, 
		imageInFront = imageInFront, 
		alt = alt, 
		currency = curr,
	})
end

local function getImg(val, t)
	if type(t) ~= 'table' then return {t} end
	if type(t[1]) ~= 'table' then return {t[1]} end
	for i, v in ipairs(t) do
		if (not v.lim or v.lim == 'inf') -- no upper limit, or upper limit is inf
			or (i == table.length(t)) -- reached last element in table
			or (val < v.lim)
		then
			return v
		end
	end
end

-- Module access point
function p._currency(...)
	local s, m, options, useShort, useApproximate, useImage, noLink, imageInFront, alt, currency
	-- Support numbered arguments
	if type(({...})[3]) ~= 'table' then
		s, m, useShort, useApproximate, useImage, noLink, imageInFront, alt, currency = ...
	else
		s, m, options = ...
		useShort, useApproximate, useImage, noLink, imageInFront, alt, currency =
			options.useShort,
			options.useApproximate,
			options.useImage,
			options.noLink,
			options.imageInFront,
			options.alt,
			options.currency
	end
	
	local a, b = tostring(s):match('^%s*(%-?%d+)%s*%-%s*(%-?%d+)%s*$')
	if not not a and not not b then
		s = a
		m = b
	end
	
	-- Default values
	if not currency then currency = 'coins' end
	useShort = useShort or false
	useApproximate = useApproximate or false
	currency = currencyData[currency]
	
	-- If first value is scientific notation
	if s and string.find(tostring(s), '10%^%d*') then
		--if not useShort, return scientific notation. if useShort, convert scientific notation to number
		if not useShort then
			s = s:gsub('%s+?%*%s+?', ' × ')
			s = s:gsub('10%^(%d*)', '10<sup>%1</sup>')
			return table.concat({
				colorMdl._colorTemplates(currency.color, s),
				'[[', currency.destPage, '|', colorMdl._colorTemplates(currency.color, currency.name), ']]'
			})
		else
			if s:find('%*') then
				local number = s:match('(%d*)%s?*%s+?10%^%d*')
				local exponent = s:match('%d*%s?*%s+?10%^(%d*)')
				s = tonumber(number) * math.pow(10, tonumber(exponent))
			else
				local toShort = s:match('10%^(%d*)')
				s = math.pow(10, tonumber(toShort))
			end
		end
	-- If first value isn't a valid number, just return the invalid number as a fallthrough
	elseif s and toNumber(s) == nil then
		return s
	-- If first value is null, just return colored currency text
	elseif s == nil then
		return table.concat({
			'[[', currency.destPage, '|', colorMdl._colorTemplates(currency.color, alt and alt or currency.pluralName or (currency.name .. 's')), ']]'
		})
	end
	
	-- Convert params to numbers - nil if not a number
	s = toNumber(s)
	m = toNumber(m)
	local sAsNumber = s
	local mAsNumber = m
	
	image = ''
	if (useImage or imageInFront) and currency.images then
		local img_ = getImg(s, currency.images)
		
		image = table.concat{
			'[[File:', img_[1], '.png|', 
			img_.size or 24, 'px', 
			'|link=', (noLink and '' or currency.destPage), ']]'
		}
		image = image .. '&thinsp;'
	end
	
	local tooltip = nil
	if useShort then
		if m then
			tooltip = formatNum(s) .. '–' .. formatNum(m)
		elseif s then
			tooltip = formatNum(s)
		end
		s = shorten(s)
		if m then
			m = shorten(m)
		end
	else
		s = formatNum(s)
		if m then
			m = formatNum(m)
		end
	end
	
	local g = mw.html.create('span')
		:attr('title', tooltip)
		:css('font-variant-numeric', 'tabular-nums')
	if useApproximate then
		g:wikitext('&#8776;')
	end
	if m then
		t = s .. '–' .. m .. '&nbsp;'
		g:wikitext(t)
	elseif s then
		t = s .. '&nbsp;'
		g:wikitext(t)
	end
	
	return mw.html.create('span')
		:css('color', currency.color)
		:wikitext(
			(imageInFront and image or ''),
			colorMdl._colorTemplates(currency.color, tostring(g)),
			(imageInFront and '' or image),
			'[[', currency.destPage, '|', colorMdl._colorTemplates(currency.color, alt and alt or lang:plural(mAsNumber and mAsNumber or sAsNumber, currency.name, currency.pluralName or (currency.name .. 's'))), ']]'
		)
end

-- Used in Module:Inventory_slot
function p._newCurrencySlot(s, m, noLink)
	local currency
	-- only parse if starts with number
	if type(s) ~= 'string' or not s:find('^%s*%d') then return end
	if s:find('[Gg]ems?%s*$') then
		s = s:gsub('[Gg]ems?%s*$', '')
		currency = 'gems'
	elseif s:find('[Bb]its?%s*$') then
		s = s:gsub('[Bb]its?%s*$', '')
		currency = 'bits'
	elseif s:find('[Pp]elts?%s*$') then
		s = s:gsub('[Pp]elts?%s*$', '')
		currency = 'pelts'
	elseif s:find('[Tt]okens?%s*$') then
		s = s:gsub('[Tt]okens?%s*$', '')
		currency = 'tokens'
	elseif s:find('[Bb]ingo [Pp]oints?%s*$') then
		s = s:gsub('[Bb]ingo [Pp]ointselts?%s*$', '')
		currency = 'bingoPoints'
	elseif s:find('[Nn]orth [Ss]tars?%s*$') then
		s = s:gsub('[Nn]orth [Ss]tars?%s*$', '')
		curr = 'northStars'
	elseif s:find('[Cc]opper%s*$') then
		s = s:gsub('[Cc]opper%s*$', '')
		curr = 'copper'
	elseif s:find('[Mm]otes?%s*$') then
		s = s:gsub('[Mm]otes?%s*$', '')
		curr = 'motes'
	elseif s:find('[Cc]oins?%s*$') then
		s = s:gsub('[Cc]oins?%s*$', '')
		currency = 'coins'
	else return end
	
	if s then
		assertTrue(type(tostring(s)) == 'string')
		
		temp = string.split(tostring(s), '-')
		if (#temp == 2) then
			s = temp[1]
			m = temp[2]
		end
	end
	
	if not currency then currency = 'coins' end
	currency = currencyData[currency]
	
	-- If first value is scientific notation
	if s and string.find(tostring(s), '10%^%d*') then
		--if not useShort, return scientific notation. if useShort, convert scientific notation to number
		if s:find('%*') then
			local number = s:match('(%d*)%s?*%s+?10%^%d*')
			local exponent = s:match('%d*%s?*%s+?10%^(%d*)')
			s = tonumber(number) * math.pow(10, tonumber(exponent))
		else
			local toShort = s:match('10%^(%d*)')
			s = math.pow(10, tonumber(toShort))
		end
	-- If first value isn't a valid number, return nil as conversion failed
	-- If first value is null, also return nil as conversion failed
	elseif toNumber(s) == nil then
		return
	end
	
	-- Convert params to numbers - nil if not a number
	s, m = toNumber(s), toNumber(m)
	local sAsNumber = s
	
	image = ''
	local source = currency.images or currency.placeholder
	local item = getImg(m or s, source)[1]
	
	local grouped_num, tooltip
	if m then
		grouped_num = ('%.f-%.f'):format(s, m)
		tooltip = formatNum(s) .. '-' .. formatNum(m)
	elseif s then
		grouped_num = ('%.f'):format(s)
		tooltip = formatNum(s)
	end
	
	return {
		name = item,
		link = noLink and '' or currency.destPage,
		title = ('%s%s %s'):format(
			currency.tooltipcolor, 
			tooltip,
			lang:plural(sAsNumber, currency.name, currency.pluralName or (currency.name .. 's'))
			),
		text = '',
		num = s,
		num2 = m,
	}
end

local function generateCurrencyFunc(currencyName, substituteRegex)
	local function _main(s, m, useShort, useApproximate, useImage, noLink, imageInFront, alt)
		return p._currency(s, m, {
			useShort = useShort, 
			useApproximate = useApproximate, 
			useImage = useImage, 
			noLink = noLink, 
			imageInFront = imageInFront, 
			alt = alt, 
			currency = currencyName,
		})
	end
	local function main(frame)
		local args = getArgs(frame)
		local callfn = _main
		
		if args[1] and args[1]:match('</font>') then return args[1] end
		if args[1] then
			s = args[1]:lower():gsub('[%[%]]+', ''):gsub(substituteRegex, '')
		end
		local m = args[2]
		local useShort = yesno(args['short'] or args['s'] , false)
		local useApproximate = yesno(args['approximate'] or args['approx'] or args['a'], false)
		local useImage = yesno(args['image'] or args['i'] or args['si'] or args['show_image'], false)
		local noLink = yesno(args['nolink'] or args['nl'], false)
		local imageInFront = yesno(args['image_in_front'] or args['iif'], false)
		local alt = args['alt']
		
		if args['approximate'] or args['approx'] or args['a'] == yesno then
			mw.getCurrentFrame():preprocess('<span style="border-bottom:2px dotted #fff;>' .. args['approximate'] or args['approx'] or args['a'] .. '</span>')
		end
		return callfn(s, m, useShort, useApproximate, useImage, noLink, imageInFront, alt)
	end
	return main, _main
end

-- Template:Coins access point
p.coins, p._coins = generateCurrencyFunc('coins', '%s*coins?')

-- Template:Bits access point
p.bits, p._bits = generateCurrencyFunc('bits', '%s*(skyblock)?bits?')

-- Template:Gems access point
p.gems, p._gems = generateCurrencyFunc('gems', '%s*(skyblock)?gems?')

-- Template:Pelts access point
p.pelts, p._pelts = generateCurrencyFunc('pelts', '%s*pelts?')

-- Template:Tokens access point
p.tokens, p._tokens = generateCurrencyFunc('tokens', '%s*tokens?')

-- Template:Bingo Points access point
p.bingoPoints, p._bingoPoints = generateCurrencyFunc('bingoPoints', '%s*bingo points?')

-- Template:North Stars access point
p.northStars, p._northStars = generateCurrencyFunc('northStars', '%s*north stars?')

-- Template:Copper access point
p.copper, p._copper = generateCurrencyFunc('copper', '%s*copper')

-- Template:Motes access point
p.motes, p._motes = generateCurrencyFunc('motes', '%s*motes?')

----------------------------------------------------------------------------------------------------
-- Template:Coins to Dollars
--
-- Used to convert value in coins to US Dollars, based on Booster Cookie prices
----------------------------------------------------------------------------------------------------
function p.coins_to_dollars(frame)
	local args = getArgs(frame)
	local coins = args[1]
	local isFormatted = yesno(args['format'] or args['f'], true)
	
	return p._coins_to_dollars(coins, type)
end

function p._coins_to_dollars(coins, isFormatted)
	if isFormatted == nil then isFormatted = true end
	local cookiePriceCoins = bazaarData.products.BOOSTER_COOKIE.buy
	local cookiePriceGems = 325
	local cookiePriceDollars = 4.99 / 675 * cookiePriceGems
	local coinPriceDollars = cookiePriceCoins / cookiePriceDollars
	
	if not isFormatted then return coins / coinPriceDollars end
	local dollars = coins / coinPriceDollars
	dollars = dollars * 100
	dollars = math.floor(dollars)
	dollars = dollars / 100
	return '$' .. formatNum(dollars)
end

----------------------------------------------------------------------------------------------------
-- Template:Bits to Coins
--
-- Used to convert value in cbits to coins, based on prices across items with fairly stable prices and demand
----------------------------------------------------------------------------------------------------
function p.bits_to_coins(frame)
	local args = getArgs(frame)
	local bits = args[1]
	
	return p._bits_to_coins(bits)
end

function p._bits_to_coins(bits)
	local conversion = (bazaarData['products']['MAGMA_BUCKET']['buy']-bazaarData['products']['ENCHANTED_LAVA_BUCKET']['buy']*2)/3000
	local coins = bits * conversion
	return p._coins(coins, nil, true, true)
end
----------------------------------------------------------------------------------------------------
-- Template:Profit
--
-- Used to display a number as a profit or loss
----------------------------------------------------------------------------------------------------
function p.profit(frame)
	local args = getArgs(frame)
	local value = args[1]
	
	return p._profit(value)
end
function p._profit(value)
	value = toNumber(value)
	if value > 0 then
		value = formatNum(value);
		return colorMdl._colorTemplates('green', "+"..value)
	elseif value < 0 then
		value = formatNum(value);
		return colorMdl._colorTemplates('red', value)
	else
		return colorMdl._colorTemplates('grey', value)
	end
end

return p