ModuleCurrency

--

```
local p = {}

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

local string, table, yesno = loader.require('String', 'Table', 'Yesno')
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('') 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('[Bb]ingo [Pp]oints?') then
		s = s:gsub('[Bb]ingo [Pp]oints?', *)*
		curr = 'bingoPoints'
	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

	if not currency then currency = 'coins' end

	-- Default values
	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%1')
			return mw.html.create('span')
				:css('color', currency.color)
				:wikitext(table.concat({
					'',
 s,
 '',
 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 mw.html.create('span')
 :css('color', currency.color)
 :wikitext(table.concat({
 '',
 alt and alt or currency.name..'s',
 ''
 }))
 end

 -- Convert params to numbers - nil if not a number
 s = toNumber(s)
 m = toNumber(m)
 local sAsNumber = s

 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..' '
 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

 g = mw.html.create('span')
 :attr('title', tooltip)
 :css('font-variant-numeric', 'tabular-nums')

 if useApproximate then
 g:wikitext('≈')
 end

 if m then
 t = s..'–'..m..' '
 g:wikitext(t)
 elseif s then
 t = s..' '
 g:wikitext(t)
 end

 return mw.html.create('span')
 :css('color', currency.color)
 :wikitext(imageInFront and image or *)*
 :node(g)
 :wikitext(
 (imageInFront and  *or image),*
 '',
 alt and alt or lang:plural(sAsNumber, currency.name, currency.name..'s'),
 ''
 )
end

function p.currencySlot(s, m, noLink, noarg1)
 local curr
 if type(s) ~= 'string' then return end
 if s:find('[Gg]ems?%s\*$') then
 s = s:gsub('[Gg]ems?%s\*$', *)*
 curr = 'gems'
 elseif s:find('[Bb]its?%s\*$') then
 s = s:gsub('[Bb]its?%s\*$', *)*
 curr = 'bits'
 elseif s:find('[Pp]elts?%s\*$') then
 s = s:gsub('[Pp]elts?%s\*$', *)*
 curr = 'pelts'
 elseif s:find('[Bb]ingo [Pp]oints?%s\*$') then
 s = s:gsub('[Bb]ingo [Pp]ointselts?%s\*$', *)*
 curr = 'bingoPoints'
 elseif s:find('[Cc]oins?%s\*$') then
 s = s:gsub('[Cc]oins?%s\*$', *)*
 curr = 'coins'
 else return end

 return p.\_currencySlot(s, m, {
 noLink = noLink or false,
 noarg1 = noarg1 or false,
 currency = curr,
 })
end

function p.\_currencySlot(...)
 local s, m, options, noLink, noarg1, currency
 -- Support numbered arguments
 if type(({...})[3]) ~= 'table' then
 s, m, noLink, noarg1, currency = checkTypeArgs({
 { 'string', 'number', nilOk=true },
 { 'string', 'number', nilOk=true },
 { 'boolean', nilOk=true },
 { 'boolean', nilOk=true },
 { 'string', nilOk=true },
 }, ...)
 else
 s, m, options = checkTypeArgs({ { 'string', 'number', nilOk=true }, { 'string', 'number', nilOk=true }, { 'table', nilOk=true } }, ...)
 noLink, noarg1, currency =
 options.noLink,
 options.noarg1,
 options.currency
 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

 local ret = {
 name = item,
 link = noLink and  *or currency.destPage,*
 title = ('%s%s %s'):format(
 currency.tooltipcolor,
 tooltip,
 lang:plural(sAsNumber, currency.name, currency.name..'s')
 ),
 text = *,*
 }

 if not noarg1 then
 ret[1] = grouped\_num and ('%s,%s'):format(item,grouped\_num) or item
 end

 return ret
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('') 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']

 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:Bingo Points access point
p.bingoPoints, p.\_bingoPoints = generateCurrencyFunc('bingoPoints', '%s\*bingo coins?')

-- 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

return p
```