Module:Rotations/Merchant

From the RuneScape Wiki, the wiki for all things RuneScape
Jump to navigation Jump to search
Module documentation
This documentation is transcluded from Module:Rotations/Merchant/doc. [edit] [history] [purge]
Module:Rotations/Merchant is invoked by .
Module:Rotations/Merchant loads data from Module:Rotations/Merchant/data.
Function list
L 21 — formatQuantity
L 28 — p.get
L 33 — p.slotA
L 38 — p.slotB
L 43 — p.slotC
L 48 — getSlots
L 55 — p.api
L 59 — p._api
L 102 — makeFullRow
L 124 — p.today
L 130 — p.todayManual
L 148 — p._today
L 201 — plink
L 206 — makeShortRow
L 273 — p.future
L 279 — p._future
L 287 — parseOffsetsToString
L 332 — p.orderAB
L 370 — p.orderC
L 393 — p.itemsC
L 411 — p.listItems
L 471 — p.nextItem
L 483 — p._nextItem
L 561 — check_count
L 667 — makeIconRow
L 691 — p.todayIcons
L 697 — p.todayIconsManual
L 712 — p._todayIcons

--[=[
-- Rotation of the [[Travelling Merchant's Shop]]
-- See [[Talk:Travelling Merchant's Shop#History and pattern]] for more info
--]=]
local rotations = require('Module:Rotations')
local rsrandom = require('Module:Rotations/RsRandom')
local coins = require('Module:Currency')._amount
local lang = mw.getContentLanguage()
local purge = require('Module:Purge')._purge
local removeDuplicates = require('Module:Array').unique
local storeLine = require("Module:StoreLine")._main
local p = {}

local items
do
	local _data = mw.loadData("Module:Rotations/Merchant/data")
	items = _data.items
	p.slot_map = _data.slot_map
end

local function formatQuantity(quantity)
	if quantity[1] == quantity[2] then
		return lang:formatNum(quantity[1])
	end
	return lang:formatNum(quantity[1]) .. '–' .. lang:formatNum(quantity[2])
end

function p.get(runedate, k, n)
	local seed = runedate * (2 ^ 32) + (runedate % k)
	return rsrandom.nextInt(seed, n)
end

function p.slotA(offsetFromNow)
	local index = p.get(rsrandom.runeDate_offset(offsetFromNow), 3, 19)
	return p.slot_map.A[index], items[p.slot_map.A[index]]
end

function p.slotB(offsetFromNow)
	local index = p.get(rsrandom.runeDate_offset(offsetFromNow), 8, 19)
	return p.slot_map.B[index], items[p.slot_map.B[index]]
end

function p.slotC(offsetFromNow)
	local index = p.get(rsrandom.runeDate_offset(offsetFromNow), 5, 13)
	return p.slot_map.C[index], items[p.slot_map.C[index]]
end

local function getSlots(offset)
	local slotA = {p.slotA(offset or 0)}
	local slotB = {p.slotB(offset or 0)}
	local slotC = {p.slotC(offset or 0)}
	return {A = slotA, B = slotB, C = slotC}
end

function p.api(frame)
	local args = frame:getParent().args
	return p._api(args.offset, args.format)
end
function p._api(offset, format)
	offset = tonumber(offset or 0) or 0
	local slots = getSlots(offset)
	local a, a_d = unpack(slots.A)
	local b, b_d = unpack(slots.B)
	local c, c_d = unpack(slots.C)
	
	if format == 'simple' then
		return string.format('@%s¦%s¦%s@', a, b ,c)
	end
	if format == 'json' then
		local jsonret = {}
		jsonret['timestamp'] = os.time()
		jsonret['items'] = {
			{ name = a, cost = a_d.cost, quantity = formatQuantity(a_d.quantity), use = mw.text.encode(a_d.use, '%[%]') },
			{ name = b, cost = b_d.cost, quantity = formatQuantity(b_d.quantity), use = mw.text.encode(b_d.use, '%[%]') },
			{ name = c, cost = c_d.cost, quantity = formatQuantity(c_d.quantity), use = mw.text.encode(c_d.use, '%[%]') }
		}
		return mw.text.jsonEncode(jsonret)
	end
	local ret = mw.html.create('div')
	ret	:tag('span')
			:addClass('slotA')
				:tag('span'):addClass('name'):wikitext(a):done()
				:tag('span'):addClass('cost'):wikitext(a_d.cost):done()
				:tag('span'):addClass('quantity'):wikitext(formatQuantity(a_d.quantity)):done()
				:tag('span'):addClass('use'):wikitext(a_d.use):done()
			:done()
			:addClass('slotB')
				:tag('span'):addClass('name'):wikitext(b):done()
				:tag('span'):addClass('cost'):wikitext(b_d.cost):done()
				:tag('span'):addClass('quantity'):wikitext(formatQuantity(b_d.quantity)):done()
				:tag('span'):addClass('use'):wikitext(b_d.use):done()
			:done()
			:addClass('slotC')
				:tag('span'):addClass('name'):wikitext(c):done()
				:tag('span'):addClass('cost'):wikitext(c_d.cost):done()
				:tag('span'):addClass('quantity'):wikitext(formatQuantity(c_d.quantity)):done()
				:tag('span'):addClass('use'):wikitext(c_d.use):done()
			:done()
	return ret	
end

local function makeFullRow(name, details)
	local tr = mw.html.create('tr')
	local page = details.name or string.format('[[%s]]', name)
	tr	:tag('td')
			:wikitext(string.format('[[File:%s.png|link=%s]]', name, name))
			:addClass('inventory-image')
		:done()
		:tag('td')
			:wikitext(page)
		:done()
		:tag('td')
			:wikitext(coins(details.cost, 'coins'))
		:done()
		:tag('td')
			:wikitext(formatQuantity(details.quantity))
		:done()
		:tag('td')
			:wikitext(details.use)
		:done()
	return tr
end

function p.today()
	local slots = getSlots(0)
	return p._today(slots.A, slots.B, slots.C)
end

-- Deprecated; previously used by [[Template:Travelling Merchant/manual]]
function p.todayManual(frame)
	local args = frame:getParent().args
	
	-- if date is specified, only show manual stock for that date
	if args.date == nil or lang:formatDate('F d Y', args.date) == lang:formatDate('F d Y') then
		local slotA = args.A or args.a or args[1]
		slotA = {slotA, items[slotA]}
		local slotB = args.B or args.b or args[2]
		slotB = {slotB, items[slotB]}
		local slotC = args.C or args.c or args[3]
		slotC = {slotC, items[slotC]}
		
		return p._today(slotA, slotB, slotC)
	end
	
	return p.today()
end

function p._today(slotA, slotB, slotC)
	local t = mw.html.create('table')
	t	:addClass('wikitable align-center-1 align-center-4')
		:tag('tr')
			:tag('th')
				:wikitext('Item')
				:attr('colspan', 2)
			:done()
			:tag('th')
				:wikitext('Cost')
			:done()
			:tag('th')
				:wikitext('Quantity')
			:done()
			:tag('th')
				:wikitext('Use')
			:done()
	t	:node(makeFullRow('Uncharted island map (Deep Sea Fishing)', items['Uncharted island map (Deep Sea Fishing)']))
		:node(makeFullRow(slotA[1], slotA[2]))
		:node(makeFullRow(slotB[1], slotB[2]))
		:node(makeFullRow(slotC[1], slotC[2]))
	local cd = mw.html.create('span')
	cd	:addClass('countdown')
		:attr('data-end', 'stop')
		:tag('span')
			:addClass('countdowndate')
			:wikitext(lang:formatDate('F d Y') .. ' 23:59 UTC')
	
	if mw.title.getCurrentTitle().fullText == "Travelling Merchant's Shop" then
		-- Slot 1's item acts like a normal shop; simulate {{StoreLine}} usage to populate bucket data
		storeLine{
			name = "Uncharted island map (Deep Sea Fishing)",
			stock = "1",
			sell = "800000",
			buy = "N/A",
			gemw = "no",
			alch = "no",
		}
		storeLine{
			name = "Uncharted island map (red)",
			stock = "1",
			sell = "800000",
			buy = "N/A",
			gemw = "no",
			alch = "no",
			--misc = "Very rarely obtained when purchasing the green map",
				-- removed so as not to emit an unused citation on [[Travelling Merchant's Shop]]
		}
	end
	
	return string.format("''This is the stock for %s ([[Runedate]] %s).'' <sup>%s</sup><br>Valid for %s\n%s", lang:formatDate('[[j F]] [[Y]]'), rsrandom.runeDate_today(), tostring(purge()), tostring(cd), tostring(t))
end

local function plink(img, page)
	local link = page or string.format('[[%s]]', img)
	return string.format('[[File:%s.png|link=%s]]&nbsp;%s', img, img, link)
end

local function makeShortRow(offset, onlyitem_list)
	local slots = getSlots(offset)
	local a_n, a_d = unpack(slots.A)
	local b_n, b_d = unpack(slots.B)
	local c_n, c_d = unpack(slots.C)
	local classDate, classA, classB, classC = nil,nil,nil,nil
	local founditems = {}
	
	if onlyitem_list then
		local foundonlyitem = false
		local highlightclass = 'table-bg-green'
		
		if offset == 0 then
			classDate = highlightclass
		end
		
		for _,onlyitem in ipairs(onlyitem_list) do
			if a_n == onlyitem then
				classA = highlightclass
				table.insert(founditems, onlyitem)
				foundonlyitem = true
			end
			if b_n == onlyitem then
				classB = highlightclass
				table.insert(founditems, onlyitem)
				foundonlyitem = true
			end
			if c_n == onlyitem then
				classC = highlightclass
				table.insert(founditems, onlyitem)
				foundonlyitem = true
			end
		end
		
		if not foundonlyitem then
			return {nil, {nil}}
		end
		founditems = removeDuplicates(founditems)
	end
	
	local tr = mw.html.create('tr')
	tr	:tag('td')
			:wikitext(lang:formatDate('j F Y', string.format('%s%s day', offset >= 0 and '+' or '', offset)))
			:addClass(classDate)
		:done()
		:tag('td')
			:wikitext(rsrandom.runeDate_offset(offset))
			:addClass(classDate)
		:done()
		:tag('td')
			:wikitext(plink(a_n, a_d.name))
			:addClass(classA)
			:addClass('inventory-image')
		:done()
		:tag('td')
			:wikitext(plink(b_n, b_d.name))
			:addClass(classB)
			:addClass('inventory-image')
		:done()
		:tag('td')
			:wikitext(plink(c_n, c_d.name))
			:addClass(classC)
			:addClass('inventory-image')
		:done()
	return {tr, founditems}
end

function p.future(frame)
	local args = frame:getParent().args
	local s = args.start or args[1] or 0
	local e = args['end'] or args[2] or 5
	return p._future(s,e)
end
function p._future(startOffset, endOffset)
	-- enforce numbers
	startOffset = tonumber(startOffset or 0) or 0
	endOffset = tonumber(endOffset or 5) or 5
	-- switch around if start/end mixed
	if startOffset > endOffset then
		startOffset, endOffset = endOffset, startOffset
	end
	local function parseOffsetsToString()
		local start_s = math.abs(startOffset) > 1 and 's' or ''
		local end_s = math.abs(endOffset) > 1 and 's' or ''
		if startOffset == endOffset then
			if startOffset > 0 then return string.format('%s day%s after today', startOffset, start_s)
			elseif startOffset < 0 then return string.format('%s day%s before today', startOffset*-1, start_s)
			elseif startOffset == 0 then return 'today' end
		else
			local s = ''
			if startOffset > 0 then s = string.format('%s%s day%s after today', s, startOffset, start_s)
			elseif startOffset < 0 then s = string.format('%s%s day%s before today', s, startOffset*-1, start_s)
			elseif startOffset == 0 then s = string.format('%stoday', s) end
			s = string.format('%s to ', s)
			if endOffset > 0 then s = string.format('%s%s day%s after today', s, endOffset, end_s)
			elseif endOffset < 0 then s = string.format('%s%s day%s before today', s, endOffset*-1, end_s)
			elseif endOffset == 0 then s = string.format('%stoday', s) end
			return s
		end
		return '(error: start and/or end not numbers)'
	end
	
	local t = mw.html.create('table')
	t	:addClass('wikitable sticky-header')
		:tag('tr')
			:tag('th')
				:wikitext('Date')
			:done()
			:tag('th')
				:wikitext('[[Runedate]]')
			:done()
			:tag('th')
				:wikitext('Slot A')
			:done()
			:tag('th')
				:wikitext('Slot B')
			:done()
			:tag('th')
				:wikitext('Slot C')
			:done()
	for i=startOffset,endOffset,1 do
		t:node(makeShortRow(i)[1])
	end
	return string.format("This table is the stock for %s (%s)<sup>%s</sup>\n%s", parseOffsetsToString(), lang:formatDate('j F Y'), purge(), tostring(t))
end

function p.orderAB()
	local t = mw.html.create('table')
	t	:addClass('wikitable sticky-header mw-collapsible mw-collapsed align-center-1 align-center-4 align-center-7')
		:tag('tr')
			:tag('th')
				:attr('colspan', 11)
				:wikitext('Slot A and B cycles')
			:done()
		:done()
		:tag('tr')
			:tag('th'):wikitext('Day'):done()
			:tag('th'):wikitext('Slot A'):done()
			:tag('th'):wikitext('Slot B'):done()
			:tag('th'):attr('rowspan', 41):wikitext('&nbsp;'):done()
			:tag('th'):wikitext('Day'):done()
			:tag('th'):wikitext('Slot A'):done()
			:tag('th'):wikitext('Slot B'):done()
			:tag('th'):attr('rowspan', 41):wikitext('&nbsp;'):done()
			:tag('th'):wikitext('Day'):done()
			:tag('th'):wikitext('Slot A'):done()
			:tag('th'):wikitext('Slot B'):done()
			
	for i=1,40,1 do
		t	:tag('tr')
				:tag('td'):wikitext(i):done()
				:tag('td'):wikitext(itemIDs[slotA_rotation[i]]):done()
				:tag('td'):wikitext(itemIDs[slotB_rotation[i]]):done()
				:tag('td'):wikitext(i+40):done()
				:tag('td'):wikitext(itemIDs[slotA_rotation[i+40]]):done()
				:tag('td'):wikitext(itemIDs[slotB_rotation[i+40]]):done()
				:tag('td'):wikitext(i+80):done()
				:tag('td'):wikitext(itemIDs[slotA_rotation[i+80]]):done()
				:tag('td'):wikitext(itemIDs[slotB_rotation[i+80]]):done()
				
	end
	return t
end

function p.orderC()
	local t = mw.html.create('table')
	t	:addClass('wikitable mw-collapsible align-center-1 align-center-2 align-center-3 align-center-4')
		:tag('tr')
			:tag('th'):attr('colspan', 5):wikitext('Item order (IDs)'):done()
		:done()
		:tag('tr')
			:tag('th'):wikitext('Day'):done()
			:tag('th'):wikitext('Item order ID'):done()
			:tag('th'):attr('rowspan', 21):wikitext('&nbsp;'):done()
			:tag('th'):wikitext('Day'):done()
			:tag('th'):wikitext('Item order ID'):done()
	for i=1,20,1 do
		t	:tag('tr')
				:tag('td'):wikitext(i):done()
				:tag('td'):wikitext(slotC_rotation[i]):done()
				:tag('td'):wikitext(i+20):done()
				:tag('td'):wikitext(slotC_rotation[i+20]):done()
					
	end
	return t
end

function p.itemsC()
	local t = mw.html.create('table')
	t	:addClass('wikitable mw-collapsible align-center-1')
		:tag('tr')
			:tag('th'):attr('colspan', 2):wikitext('Item map'):done()
		:done()
		:tag('tr')
			:tag('th'):wikitext('Item map ID'):done()
			:tag('th'):wikitext('Item'):done()
	for i=1,#p.slot_map.C,1 do
		t	:tag('tr')
				:tag('td'):wikitext(i):done()
				:tag('td'):wikitext(p.slot_map.C[i]):done()
					
	end
	return t
end

function p.listItems()
	local slotAB_list = {
		'Barrel of bait', 'Tangled fishbowl', 'Broken fishing rod',
		'Small goebie burial charm', 'Goebie burial charm',	'Menaphite gift offering (small)',
		'Menaphite gift offering (medium)',	'Unstable air rune', 'Anima crystal',
		'Slayer VIP Coupon', 'Distraction & Diversion reset token (daily)', 'Unfocused damage enhancer',
		'Sacred clay (Deep Sea Fishing)', 'Shattered anima', 'Advanced pulse core',
		'Livid plant (Deep Sea Fishing)', 'Gift for the Reaper', 'Silverhawk down', 'Horn of honour'
	}
	
	local slotC_list = {
		'Large goebie burial charm', 'Message in a bottle (Deep Sea Fishing)', 'Dragonkin lamp',
		'Dungeoneering Wildcard', 'Menaphite gift offering (large)', 'Taijitu',
		'Distraction & Diversion reset token (weekly)', 'Distraction & Diversion reset token (monthly)', 'Starved ancient effigy',
		'Harmonic dust', 'Crystal triskelion', 'Deathtouched dart',
		'Unfocused reward enhancer'
	}
	
	local t = mw.html.create('table')
	t	:addClass('wikitable align-center-1 align-center-4')
		:tag('tr')
			:tag('th'):attr('colspan', 5):wikitext('Slot 1 (always)'):done()
		:done()
		:tag('tr')
			:tag('th'):attr('colspan', 2):wikitext('Item'):done()
			:tag('th'):wikitext('Cost'):done()
			:tag('th'):wikitext('Quantity'):done()
			:tag('th'):wikitext('Use'):done()
		:done()
		:node(makeFullRow('Uncharted island map (Deep Sea Fishing)', items['Uncharted island map (Deep Sea Fishing)']))
		:tag('tr')
			:tag('th'):attr('colspan', 5):wikitext('Slots 2 and 3'):done()
		:done()
		:tag('tr')
			:tag('th'):attr('colspan', 2):wikitext('Item'):done()
			:tag('th'):wikitext('Cost'):done()
			:tag('th'):wikitext('Quantity'):done()
			:tag('th'):wikitext('Use'):done()
		:done()
		
	for i,v in ipairs(slotAB_list) do
		t:node(makeFullRow(v, items[v]))
	end
		
	t	:tag('tr')
			:tag('th'):attr('colspan', 5):wikitext('Slot 4'):done()
		:done()
		:tag('tr')
			:tag('th'):attr('colspan', 2):wikitext('Item'):done()
			:tag('th'):wikitext('Cost'):done()
			:tag('th'):wikitext('Quantity'):done()
			:tag('th'):wikitext('Use'):done()
		:done()
	
	for i,v in ipairs(slotC_list) do
		t:node(makeFullRow(v, items[v]))
	end
	return t
end

function p.nextItem(frame)
	local args = frame:getParent().args
	local item = args.item
	if not item then
		item = mw.title.getCurrentTitle().text
	end
	local item_list = mw.text.split(item,",%s*")
	local boolconverter = {["true"]=true,["false"]=false}
	local individual = boolconverter[args.individual]
	return p._nextItem(item_list, tonumber(args.number), tonumber(args.limit), individual)
end

function p._nextItem(item_list, n, dayslimit, individual)
	local list = {
		['Barrel of bait'] = 'ab',
		['Tangled fishbowl'] = 'ab',
		['Broken fishing rod'] = 'ab',
		['Small goebie burial charm'] = 'ab',
		['Goebie burial charm'] = 'ab',
		['Menaphite gift offering (small)'] = 'ab',
		['Menaphite gift offering (medium)'] = 'ab',
		['Unstable air rune'] = 'ab',
		['Anima crystal'] = 'ab',
		['Slayer VIP Coupon'] = 'ab',
		['Distraction & Diversion reset token (daily)'] = 'ab',
		['Unfocused damage enhancer'] = 'ab',
		['Sacred clay (Deep Sea Fishing)'] = 'ab',
		['Shattered anima'] = 'ab',
		['Advanced pulse core'] = 'ab',
		['Livid plant (Deep Sea Fishing)'] = 'ab',
		['Gift for the Reaper'] = 'ab',
		['Silverhawk down'] = 'ab',
		['Horn of honour'] = 'ab',

		['Large goebie burial charm'] = 'c',
		['Message in a bottle (Deep Sea Fishing)'] = 'c',
		['Dragonkin lamp'] = 'c',
		['Dungeoneering Wildcard'] = 'c',
		['Menaphite gift offering (large)'] = 'c',
		['Taijitu'] = 'c',
		['Distraction & Diversion reset token (weekly)'] = 'c',
		['Distraction & Diversion reset token (monthly)'] = 'c',
		['Starved ancient effigy'] = 'c',
		['Harmonic dust'] = 'c',
		['Crystal triskelion'] = 'c',
		['Deathtouched dart'] = 'c',
		['Unfocused reward enhancer'] = 'c'
	}
	
	if not n then
		n = 5
	end
	if not dayslimit then
		dayslimit = 200
	end
	if not individual or #item_list == 1 then
		individual = false
	end
	
	local unrecognised_item = ""
	for _, item in ipairs(item_list) do
		if not list[item] then
			unrecognised_item = unrecognised_item .. "Unrecognised item ''"..item.."''. Please enter the item name exactly.<br>"
		end
	end
	if unrecognised_item ~= "" then
		return unrecognised_item:sub(1, -5)
	end
	
	local t = mw.html.create('table')
	t	:addClass('wikitable sticky-header')
		:tag('tr')
			:tag('th')
				:wikitext('Date')
			:done()
			:tag('th')
				:wikitext('[[Runedate]]')
			:done()
			:tag('th')
				:wikitext('Slot A')
			:done()
			:tag('th')
				:wikitext('Slot B')
			:done()
			:tag('th')
				:wikitext('Slot C')
			:done()

	local d = 0

	local function check_count(count_all_table, n)
		local count_reached = true
		for _,v in pairs(count_all_table) do
			if v < n then
				count_reached = false
			end
		end
		return count_reached
	end
	
	local finalcount = math.huge
	
	if not individual then
		local count = 0
		while count < n and d < dayslimit do
			local r = makeShortRow(d, item_list)[1]
			if r then
				t:node(r)
				count = count + 1
			end
			d = d + 1
		end
		finalcount = count
	else
		local count = {}
		for _,item in ipairs(item_list) do
			count[item] = 0
		end
		
		local count_all = false
		while not count_all and d < dayslimit do
			local r = makeShortRow(d, item_list)
			if r[1] then
				t:node(r[1])
				for _,item in ipairs(r[2]) do
					count[item] = count[item] + 1
				end
			end
			count_all = check_count(count, n)
			d = d + 1
		end
		
		-- local test = ""
		-- for k,v in pairs(count) do
		-- 	test = test .. k .. ": " .. tostring(v) .. "<br>"
		-- end
		-- return test
		for _,v in pairs(count) do
			finalcount = math.min(finalcount, v)
		end
	end
	
	local simpleitem = ""
	for item_count = 1, #item_list do
		local item = item_list[item_count]
		
		local simpleitem_temp = item:lower()
		local iteminfo = items[item]
		if iteminfo._name then
			simpleitem_temp = iteminfo._name
		end
		
		local andor = "or"
		if individual then
			andor = "and"
		end
		
		if item_count == #item_list then
			if #item_list == 1 then
				simpleitem = simpleitem..", "
			elseif #item_list == 2 then
				simpleitem = simpleitem.." " .. andor .. " "
			else
				simpleitem = simpleitem..", " .. andor .. " "
			end
		else
			simpleitem = simpleitem..", "
		end
		simpleitem = simpleitem.."<i>[["..simpleitem_temp.."]]</i>"
	end
	simpleitem = simpleitem:sub(3)
	
	local be_string = "is"
	if individual then
		be_string = "are"
	end
	
	local intro = string.format('This table shows the next %s dates<sup>%s</sup> on which %s %s available in the [[Travelling Merchant\'s Shop]]', finalcount, tostring(purge()), simpleitem, be_string)
	
	if #item_list == 1 then
		local iteminfo = items[item_list[1]]
		local quantity_string = formatQuantity(iteminfo.quantity)
		local cost_per_string = ''
		if iteminfo.quantity[1] == iteminfo.quantity[2] then
			if iteminfo.quantity[1] == 1 then
				quantity_string = 'it'
			end
		else
			cost_per_string = string.format(' (%s–%s each)', coins(iteminfo.cost / iteminfo.quantity[2], 'nocoins'), coins(iteminfo.cost / iteminfo.quantity[1], 'nocoinsc'))
		end
		intro = intro..string.format(', where %s can be purchased for %s%s', quantity_string, coins(iteminfo.cost, 'nocoinsc'), cost_per_string)
	end

	return intro .. '.\n' .. tostring(t)
end

local function makeIconRow(slotAname, slotBname, slotCname)
	local m_n = 'Uncharted island map (Deep Sea Fishing)'
	local tr = mw.html.create('tr')

	tr	:tag('td')
			:wikitext(string.format('[[File:%s.png|link=%s]]', m_n, m_n))
			:addClass('inventory-image')
		:done()
		:tag('td')
			:wikitext(string.format('[[File:%s.png|link=%s]]', slotAname, slotAname))
			:addClass('inventory-image')
		:done()
		:tag('td')
			:wikitext(string.format('[[File:%s.png|link=%s]]', slotBname, slotBname))
			:addClass('inventory-image')
		:done()
		:tag('td')
			:wikitext(string.format('[[File:%s.png|link=%s]]', slotCname, slotCname))
			:addClass('inventory-image')
		:done()

	return tr
end
	
function p.todayIcons()
	local slots = getSlots(0)
	return p._todayIcons(slots.A[1], slots.B[1], slots.C[1])
end

-- Deprecated; previously used by [[Template:Travelling Merchant icons/manual]]
function p.todayIconsManual(frame)
	local args = frame:getParent().args
	
	-- if date is specified, only show manual icons for that date
	if args.date == nil or lang:formatDate('F d Y', args.date) == lang:formatDate('F d Y') then
		local slotA = args.A or args.a or args[1]
		local slotB = args.B or args.b or args[2]
		local slotC = args.C or args.c or args[3]

		return p._todayIcons(slotA, slotB, slotC)
	end
	
	return p.todayIcons()
end

function p._todayIcons(slotAname, slotBname, slotCname)
	local t = mw.html.create('table')

	t	:addClass('wikitable align-center-1 align-center-4')
		:tag('tr')
			:tag('th')
				:attr('colspan', 4)
				:wikitext(string.format('Stock for %s', lang:formatDate('[[j F]] [[Y]]')))
				:tag('br'):done()
				:wikitext('([[Runedate]] '..rsrandom.runeDate_today()..')')
			:done()
	t	:node(makeIconRow(slotAname, slotBname, slotCname))
	t	:tag('tr')
			:tag('td')
				:wikitext( tostring(purge()) )
				:attr('colspan', 4)
			:done()

	return t
end

return p