<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://darkanddarker.wiki.spellsandguns.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Iodred</id>
	<title>Dark and Darker Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://darkanddarker.wiki.spellsandguns.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Iodred"/>
	<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/Special:Contributions/Iodred"/>
	<updated>2026-04-30T20:56:12Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.0</generator>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=66382</id>
		<title>Module:Craft</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=66382"/>
		<updated>2026-04-30T19:41:43Z</updated>

		<summary type="html">&lt;p&gt;Iodred: Fixed draw_ingredient_table and draw_craftable_table. Since ingredients are now listed under the rarities key, iterate over rarities and print a row for each rarity that matches ingredient list&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--Either draw table of all craftable weapons for a merchant, or a singular item table&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Merchant.json&amp;quot;) --Global var holding tables from Data:Merchant.json&lt;br /&gt;
&lt;br /&gt;
local function iconbox(wt,name,amount,rarity,has_caption)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..rarity..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;[[File:&amp;quot;..name..&amp;quot;.png|x80px|link=&amp;quot;..name..&amp;quot;]]&amp;quot;&lt;br /&gt;
	if amount then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..amount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	if has_caption then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;[[&amp;quot;..name..&amp;quot;|&amp;lt;b class=cr&amp;quot;..rarity..&amp;quot;&amp;gt;&amp;quot;..name..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function name(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Name&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,craft.itemname,craft.quantity,craft.rarity,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ingredients(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:5%&#039;&amp;gt;Ingredients&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ingredient in ipairs(craft.ingredients) do&lt;br /&gt;
		local amount = string.match(ingredient,&amp;quot;^(%d*)-&amp;quot;)&lt;br /&gt;
		local ingredient_name = string.match(ingredient,&amp;quot;-(.*)-&amp;quot;)&lt;br /&gt;
		local rarity = string.match(ingredient,&amp;quot;-.*-(.*)$&amp;quot;)&lt;br /&gt;
		iconbox(wt,ingredient_name,amount,rarity)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function merchant(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Merchant&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,merchant_name,false,&amp;quot;-1&amp;quot;,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function affinity_to_unlock(wt,is_header,craft,merchant)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th class=&#039;tooltip&#039; style=&#039;width:2%&#039;&amp;gt;[[Template:Merchant Affinity|Affinity to Unlock]]&amp;lt;span class=&#039;tooltiptext-left&#039; style=&#039;left:50%; transform:translate(-50%); bottom:75%; width:100%&#039;&amp;gt;Affinity values are collected manually by the community.  If you spot an error, please follow the link and fix it!&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROW = {&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,ingredients,merchant}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function craft_row(wt,is_header,craft,merchant_name)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,craft_td in ipairs(ROW.Default) do&lt;br /&gt;
		craft_td(wt,is_header,craft,merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function craft_table_header(wt)&lt;br /&gt;
	craft_row(wt,true)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function spairs(dict)&lt;br /&gt;
    if not dict then return function() return nil,nil end end&lt;br /&gt;
&lt;br /&gt;
    local set = {}&lt;br /&gt;
    for key,_ in pairs(dict) do&lt;br /&gt;
        if key ~= &amp;quot;order&amp;quot; then&lt;br /&gt;
            set[#set+1] = key&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    table.sort(set,function(a,b)&lt;br /&gt;
    if type(a) ~= type(b) then&lt;br /&gt;
        return tostring(a) &amp;lt; tostring(b)&lt;br /&gt;
    else&lt;br /&gt;
        return a &amp;lt; b&lt;br /&gt;
    end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    local i = 0&lt;br /&gt;
    return function()&lt;br /&gt;
        i = i + 1&lt;br /&gt;
        local key = set[i]&lt;br /&gt;
        if key ~= nil then&lt;br /&gt;
            return key, dict[key]&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function has_ingredient(list,ingredient)&lt;br /&gt;
	for _,val in ipairs(list) do&lt;br /&gt;
		if string.match(val,ingredient) then return true end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function get_craft_list(args)&lt;br /&gt;
	local list = {}&lt;br /&gt;
	if args.item then&lt;br /&gt;
		list[#list+1] = args.item&lt;br /&gt;
	else&lt;br /&gt;
		for craft_name,craft in spairs(MD[args.merchant].crafts) do&lt;br /&gt;
			-- if filtering for ingredients, ensure the craft requires the ingredient&lt;br /&gt;
			if args.ingredient then&lt;br /&gt;
				for index,value in pairs(craft.rarities) do &lt;br /&gt;
					if has_ingredient(value.ingredients,args.ingredient) then&lt;br /&gt;
						list[#list+1] = craft_name&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
				&lt;br /&gt;
			-- else include all crafts&lt;br /&gt;
			elseif args.merchant ~= nil then&lt;br /&gt;
				list[#list+1] = craft_name&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return list&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local BASIC_TABLE = { open = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:70%;min-width:500px;text-align:center;vertical-align:middle&#039;&amp;gt;&amp;quot;,&lt;br /&gt;
					  close= &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(f)&lt;br /&gt;
	local item_list = get_craft_list(f.args)&lt;br /&gt;
	local merchant_name = f.args.merchant&lt;br /&gt;
	if #item_list == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,craft_name in ipairs(item_list) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = [=[ class=&amp;quot;tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	if active == &amp;quot;yes&amp;quot; then&lt;br /&gt;
		class = [=[ class=&amp;quot;selected-tab tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	end&lt;br /&gt;
	return [=[&amp;lt;div style=&amp;quot;display:inline-block&amp;quot;]=]..class..[=[ data-tabid=&amp;quot;]=]..tabid..[=[&amp;quot; data-tab=&amp;quot;]=]..title:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;)..[=[&amp;quot; title=&amp;quot;]=]..title..[=[&amp;quot;&amp;gt;]=]..content..[=[&amp;lt;/div&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.crafting_page(frame)&lt;br /&gt;
	local text = {}&lt;br /&gt;
	text[#text+1] = [=[&amp;lt;div style=&amp;quot;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;&amp;quot;&amp;gt;]=]&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;None&amp;quot;,&amp;quot;[[File:None_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
	for _,merchant_name in ipairs(MD.craft_order) do&lt;br /&gt;
		if merchant_name == &amp;quot;Skeleton&amp;quot; or merchant_name == &amp;quot;Cockatrice&amp;quot; then&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant_name:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant_name..&amp;quot;_Merchant.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
		else&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant_name:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant_name..&amp;quot;.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;All&amp;quot;,&amp;quot;[[File:All_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;yes&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;center&amp;gt;&amp;lt;div class=&#039;None-data&#039;&amp;gt; &amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for _,merchant_name in ipairs(MD.craft_order) do&lt;br /&gt;
		text[#text+1] = [=[&amp;lt;div class=&amp;quot;]=]..merchant_name:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;):gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;)..[=[-data All-data&amp;quot;&amp;gt;]=]&lt;br /&gt;
		text[#text+1] = p.draw_table({args={merchant=merchant_name}})&lt;br /&gt;
		text[#text+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/center&amp;gt;&amp;quot;&lt;br /&gt;
	return table.concat(text)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	Weapon    = {Data = &amp;quot;Data:Weapon.json&amp;quot;},&lt;br /&gt;
					Armor     = {Data = &amp;quot;Data:Armor.json&amp;quot;},&lt;br /&gt;
					Accessory = {Data = &amp;quot;Data:Accessory.json&amp;quot;},&lt;br /&gt;
					Utility   = {Data = &amp;quot;Data:Utility.json&amp;quot;},&lt;br /&gt;
					Misc      = {Data = &amp;quot;Data:Misc.json&amp;quot;},&lt;br /&gt;
					Monster   = {Data = &amp;quot;Data:Monster.json&amp;quot;},&lt;br /&gt;
					Prop      = {Data = &amp;quot;Data:Prop.json&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function draw_ingredient_table(wt,ingredient,category,data)&lt;br /&gt;
	local merchants = data[category] and data[category][ingredient] and data[category][ingredient].incraftingrecipefor or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Ingredient&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = ingredient&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is used in the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		for _,craft_name in ipairs(get_craft_list({ingredient=ingredient,merchant=merchant_name})) do&lt;br /&gt;
			craft = MD[merchant_name].crafts[craft_name]&lt;br /&gt;
			for index,value in pairs(craft.rarities) do 	&lt;br /&gt;
				mappedCraft = {}&lt;br /&gt;
				mappedCraft[&amp;quot;itemname&amp;quot;] = craft.itemname&lt;br /&gt;
				mappedCraft[&amp;quot;rarity&amp;quot;] = index&lt;br /&gt;
				mappedCraft[&amp;quot;ingredients&amp;quot;] = value.ingredients&lt;br /&gt;
				mappedCraft[&amp;quot;quantity&amp;quot;] = value.quantity&lt;br /&gt;
				craft_row(wt,false,mappedCraft,merchant_name)&lt;br /&gt;
			end&lt;br /&gt;
			-- craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function draw_craftable_table(wt,item,category,data)&lt;br /&gt;
	local merchants = data[category] and data[category][item] and data[category][item].iscraftableby or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Craftable&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = item&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is craftable with the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		craft = MD[merchant_name].crafts[item]&lt;br /&gt;
		for index,value in pairs(craft.rarities) do &lt;br /&gt;
			mappedCraft = {}&lt;br /&gt;
			mappedCraft[&amp;quot;itemname&amp;quot;] = craft.itemname&lt;br /&gt;
			mappedCraft[&amp;quot;rarity&amp;quot;] = index&lt;br /&gt;
			mappedCraft[&amp;quot;ingredients&amp;quot;] = value.ingredients&lt;br /&gt;
			mappedCraft[&amp;quot;quantity&amp;quot;] = value.quantity&lt;br /&gt;
			craft_row(wt,false,mappedCraft,merchant_name)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.page(f)&lt;br /&gt;
	local category = f.args.category&lt;br /&gt;
	local item = f.args.item&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[category].Data)&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	draw_craftable_table(wt,item,category,data)&lt;br /&gt;
	draw_ingredient_table(wt,item,category,data)&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on the wiki with these console inputs&lt;br /&gt;
-- mw.log(p.draw_table({args={item=&amp;quot;Void Blade&amp;quot;,merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;,ingredient=&amp;quot;Iron Ingot&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Void Blade&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Iron Ingot&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Iron Ore&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Iodred</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=66381</id>
		<title>Module:Craft</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=66381"/>
		<updated>2026-04-30T18:04:53Z</updated>

		<summary type="html">&lt;p&gt;Iodred: Undo revision 66380 by Iodred (talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--Either draw table of all craftable weapons for a merchant, or a singular item table&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Merchant.json&amp;quot;) --Global var holding tables from Data:Merchant.json&lt;br /&gt;
&lt;br /&gt;
local function iconbox(wt,name,amount,rarity,has_caption)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..rarity..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;[[File:&amp;quot;..name..&amp;quot;.png|x80px|link=&amp;quot;..name..&amp;quot;]]&amp;quot;&lt;br /&gt;
	if amount then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..amount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	if has_caption then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;[[&amp;quot;..name..&amp;quot;|&amp;lt;b class=cr&amp;quot;..rarity..&amp;quot;&amp;gt;&amp;quot;..name..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function name(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Name&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,craft.itemname,craft.quantity,craft.rarity,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ingredients(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:5%&#039;&amp;gt;Ingredients&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ingredient in ipairs(craft.ingredients) do&lt;br /&gt;
		local amount = string.match(ingredient,&amp;quot;^(%d*)-&amp;quot;)&lt;br /&gt;
		local ingredient_name = string.match(ingredient,&amp;quot;-(.*)-&amp;quot;)&lt;br /&gt;
		local rarity = string.match(ingredient,&amp;quot;-.*-(.*)$&amp;quot;)&lt;br /&gt;
		iconbox(wt,ingredient_name,amount,rarity)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function merchant(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Merchant&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,merchant_name,false,&amp;quot;-1&amp;quot;,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function affinity_to_unlock(wt,is_header,craft,merchant)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th class=&#039;tooltip&#039; style=&#039;width:2%&#039;&amp;gt;[[Template:Merchant Affinity|Affinity to Unlock]]&amp;lt;span class=&#039;tooltiptext-left&#039; style=&#039;left:50%; transform:translate(-50%); bottom:75%; width:100%&#039;&amp;gt;Affinity values are collected manually by the community.  If you spot an error, please follow the link and fix it!&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROW = {&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,ingredients,merchant}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function craft_row(wt,is_header,craft,merchant_name)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,craft_td in ipairs(ROW.Default) do&lt;br /&gt;
		craft_td(wt,is_header,craft,merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function craft_table_header(wt)&lt;br /&gt;
	craft_row(wt,true)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function spairs(dict)&lt;br /&gt;
    if not dict then return function() return nil,nil end end&lt;br /&gt;
&lt;br /&gt;
    local set = {}&lt;br /&gt;
    for key,_ in pairs(dict) do&lt;br /&gt;
        if key ~= &amp;quot;order&amp;quot; then&lt;br /&gt;
            set[#set+1] = key&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    table.sort(set,function(a,b)&lt;br /&gt;
    if type(a) ~= type(b) then&lt;br /&gt;
        return tostring(a) &amp;lt; tostring(b)&lt;br /&gt;
    else&lt;br /&gt;
        return a &amp;lt; b&lt;br /&gt;
    end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    local i = 0&lt;br /&gt;
    return function()&lt;br /&gt;
        i = i + 1&lt;br /&gt;
        local key = set[i]&lt;br /&gt;
        if key ~= nil then&lt;br /&gt;
            return key, dict[key]&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function has_ingredient(list,ingredient)&lt;br /&gt;
	for _,val in ipairs(list) do&lt;br /&gt;
		if string.match(val,ingredient) then return true end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function get_craft_list(args)&lt;br /&gt;
	local list = {}&lt;br /&gt;
	if args.item then&lt;br /&gt;
		list[#list+1] = args.item&lt;br /&gt;
	else&lt;br /&gt;
		for craft_name,craft in spairs(MD[args.merchant].crafts) do&lt;br /&gt;
			-- if filtering for ingredients, ensure the craft requires the ingredient&lt;br /&gt;
			if args.ingredient then&lt;br /&gt;
				if has_ingredient(craft.ingredients,args.ingredient) then&lt;br /&gt;
					list[#list+1] = craft_name&lt;br /&gt;
				end&lt;br /&gt;
			-- else include all crafts&lt;br /&gt;
			elseif args.merchant ~= nil then&lt;br /&gt;
				list[#list+1] = craft_name&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return list&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local BASIC_TABLE = { open = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:70%;min-width:500px;text-align:center;vertical-align:middle&#039;&amp;gt;&amp;quot;,&lt;br /&gt;
					  close= &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(f)&lt;br /&gt;
	local item_list = get_craft_list(f.args)&lt;br /&gt;
	local merchant_name = f.args.merchant&lt;br /&gt;
	if #item_list == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,craft_name in ipairs(item_list) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = [=[ class=&amp;quot;tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	if active == &amp;quot;yes&amp;quot; then&lt;br /&gt;
		class = [=[ class=&amp;quot;selected-tab tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	end&lt;br /&gt;
	return [=[&amp;lt;div style=&amp;quot;display:inline-block&amp;quot;]=]..class..[=[ data-tabid=&amp;quot;]=]..tabid..[=[&amp;quot; data-tab=&amp;quot;]=]..title:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;)..[=[&amp;quot; title=&amp;quot;]=]..title..[=[&amp;quot;&amp;gt;]=]..content..[=[&amp;lt;/div&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.crafting_page(frame)&lt;br /&gt;
	local text = {}&lt;br /&gt;
	text[#text+1] = [=[&amp;lt;div style=&amp;quot;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;&amp;quot;&amp;gt;]=]&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;None&amp;quot;,&amp;quot;[[File:None_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
	for _,merchant_name in ipairs(MD.craft_order) do&lt;br /&gt;
		if merchant_name == &amp;quot;Skeleton&amp;quot; or merchant_name == &amp;quot;Cockatrice&amp;quot; then&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant_name:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant_name..&amp;quot;_Merchant.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
		else&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant_name:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant_name..&amp;quot;.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;All&amp;quot;,&amp;quot;[[File:All_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;yes&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;center&amp;gt;&amp;lt;div class=&#039;None-data&#039;&amp;gt; &amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for _,merchant_name in ipairs(MD.craft_order) do&lt;br /&gt;
		text[#text+1] = [=[&amp;lt;div class=&amp;quot;]=]..merchant_name:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;):gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;)..[=[-data All-data&amp;quot;&amp;gt;]=]&lt;br /&gt;
		text[#text+1] = p.draw_table({args={merchant=merchant_name}})&lt;br /&gt;
		text[#text+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/center&amp;gt;&amp;quot;&lt;br /&gt;
	return table.concat(text)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	Weapon    = {Data = &amp;quot;Data:Weapon.json&amp;quot;},&lt;br /&gt;
					Armor     = {Data = &amp;quot;Data:Armor.json&amp;quot;},&lt;br /&gt;
					Accessory = {Data = &amp;quot;Data:Accessory.json&amp;quot;},&lt;br /&gt;
					Utility   = {Data = &amp;quot;Data:Utility.json&amp;quot;},&lt;br /&gt;
					Misc      = {Data = &amp;quot;Data:Misc.json&amp;quot;},&lt;br /&gt;
					Monster   = {Data = &amp;quot;Data:Monster.json&amp;quot;},&lt;br /&gt;
					Prop      = {Data = &amp;quot;Data:Prop.json&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function draw_ingredient_table(wt,ingredient,category,data)&lt;br /&gt;
	local merchants = data[category] and data[category][ingredient] and data[category][ingredient].incraftingrecipefor or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Ingredient&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = ingredient&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is used in the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		for _,craft_name in ipairs(get_craft_list({ingredient=ingredient,merchant=merchant_name})) do&lt;br /&gt;
			craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function draw_craftable_table(wt,item,category,data)&lt;br /&gt;
	local merchants = data[category] and data[category][item] and data[category][item].iscraftableby or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Craftable&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = item&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is craftable with the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[item],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.page(f)&lt;br /&gt;
	local category = f.args.category&lt;br /&gt;
	local item = f.args.item&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[category].Data)&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	draw_craftable_table(wt,item,category,data)&lt;br /&gt;
	draw_ingredient_table(wt,item,category,data)&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on the wiki with these console inputs&lt;br /&gt;
-- mw.log(p.draw_table({args={item=&amp;quot;Void Blade&amp;quot;,merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;,ingredient=&amp;quot;Iron Ingot&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Void Blade&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Iron Ingot&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Iron Ore&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Iodred</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=66380</id>
		<title>Module:Craft</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=66380"/>
		<updated>2026-04-30T18:04:31Z</updated>

		<summary type="html">&lt;p&gt;Iodred: p.page({args={item=&amp;quot;Iron Ingot&amp;quot;,category=&amp;quot;Misc&amp;quot;}})&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--Either draw table of all craftable weapons for a merchant, or a singular item table&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Merchant.json&amp;quot;) --Global var holding tables from Data:Merchant.json&lt;br /&gt;
&lt;br /&gt;
local function iconbox(wt,name,amount,rarity,has_caption)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..rarity..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;[[File:&amp;quot;..name..&amp;quot;.png|x80px|link=&amp;quot;..name..&amp;quot;]]&amp;quot;&lt;br /&gt;
	if amount then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..amount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	if has_caption then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;[[&amp;quot;..name..&amp;quot;|&amp;lt;b class=cr&amp;quot;..rarity..&amp;quot;&amp;gt;&amp;quot;..name..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function name(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Name&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
	mw.logObject(craft)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,craft.itemname,craft.quantity,craft.rarity,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ingredients(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:5%&#039;&amp;gt;Ingredients&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ingredient in ipairs(craft.ingredients) do&lt;br /&gt;
		local amount = string.match(ingredient,&amp;quot;^(%d*)-&amp;quot;)&lt;br /&gt;
		local ingredient_name = string.match(ingredient,&amp;quot;-(.*)-&amp;quot;)&lt;br /&gt;
		local rarity = string.match(ingredient,&amp;quot;-.*-(.*)$&amp;quot;)&lt;br /&gt;
		iconbox(wt,ingredient_name,amount,rarity)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function merchant(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Merchant&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,merchant_name,false,&amp;quot;-1&amp;quot;,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function affinity_to_unlock(wt,is_header,craft,merchant)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th class=&#039;tooltip&#039; style=&#039;width:2%&#039;&amp;gt;[[Template:Merchant Affinity|Affinity to Unlock]]&amp;lt;span class=&#039;tooltiptext-left&#039; style=&#039;left:50%; transform:translate(-50%); bottom:75%; width:100%&#039;&amp;gt;Affinity values are collected manually by the community.  If you spot an error, please follow the link and fix it!&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROW = {&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,ingredients,merchant}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function craft_row(wt,is_header,craft,merchant_name)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,craft_td in ipairs(ROW.Default) do&lt;br /&gt;
		craft_td(wt,is_header,craft,merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function craft_table_header(wt)&lt;br /&gt;
	craft_row(wt,true)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function spairs(dict)&lt;br /&gt;
    if not dict then return function() return nil,nil end end&lt;br /&gt;
&lt;br /&gt;
    local set = {}&lt;br /&gt;
    for key,_ in pairs(dict) do&lt;br /&gt;
        if key ~= &amp;quot;order&amp;quot; then&lt;br /&gt;
            set[#set+1] = key&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    table.sort(set,function(a,b)&lt;br /&gt;
    if type(a) ~= type(b) then&lt;br /&gt;
        return tostring(a) &amp;lt; tostring(b)&lt;br /&gt;
    else&lt;br /&gt;
        return a &amp;lt; b&lt;br /&gt;
    end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    local i = 0&lt;br /&gt;
    return function()&lt;br /&gt;
        i = i + 1&lt;br /&gt;
        local key = set[i]&lt;br /&gt;
        if key ~= nil then&lt;br /&gt;
            return key, dict[key]&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function has_ingredient(list,ingredient)&lt;br /&gt;
	for _,val in ipairs(list) do&lt;br /&gt;
		if string.match(val,ingredient) then return true end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function get_craft_list(args)&lt;br /&gt;
	local list = {}&lt;br /&gt;
	if args.item then&lt;br /&gt;
		list[#list+1] = args.item&lt;br /&gt;
	else&lt;br /&gt;
		for craft_name,craft in spairs(MD[args.merchant].crafts) do&lt;br /&gt;
			-- if filtering for ingredients, ensure the craft requires the ingredient&lt;br /&gt;
			if args.ingredient then&lt;br /&gt;
				if has_ingredient(craft.ingredients,args.ingredient) then&lt;br /&gt;
					list[#list+1] = craft_name&lt;br /&gt;
				end&lt;br /&gt;
			-- else include all crafts&lt;br /&gt;
			elseif args.merchant ~= nil then&lt;br /&gt;
				list[#list+1] = craft_name&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return list&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local BASIC_TABLE = { open = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:70%;min-width:500px;text-align:center;vertical-align:middle&#039;&amp;gt;&amp;quot;,&lt;br /&gt;
					  close= &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(f)&lt;br /&gt;
	local item_list = get_craft_list(f.args)&lt;br /&gt;
	local merchant_name = f.args.merchant&lt;br /&gt;
	if #item_list == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,craft_name in ipairs(item_list) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = [=[ class=&amp;quot;tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	if active == &amp;quot;yes&amp;quot; then&lt;br /&gt;
		class = [=[ class=&amp;quot;selected-tab tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	end&lt;br /&gt;
	return [=[&amp;lt;div style=&amp;quot;display:inline-block&amp;quot;]=]..class..[=[ data-tabid=&amp;quot;]=]..tabid..[=[&amp;quot; data-tab=&amp;quot;]=]..title:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;)..[=[&amp;quot; title=&amp;quot;]=]..title..[=[&amp;quot;&amp;gt;]=]..content..[=[&amp;lt;/div&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.crafting_page(frame)&lt;br /&gt;
	local text = {}&lt;br /&gt;
	text[#text+1] = [=[&amp;lt;div style=&amp;quot;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;&amp;quot;&amp;gt;]=]&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;None&amp;quot;,&amp;quot;[[File:None_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
	for _,merchant_name in ipairs(MD.craft_order) do&lt;br /&gt;
		if merchant_name == &amp;quot;Skeleton&amp;quot; or merchant_name == &amp;quot;Cockatrice&amp;quot; then&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant_name:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant_name..&amp;quot;_Merchant.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
		else&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant_name:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant_name..&amp;quot;.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;All&amp;quot;,&amp;quot;[[File:All_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;yes&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;center&amp;gt;&amp;lt;div class=&#039;None-data&#039;&amp;gt; &amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for _,merchant_name in ipairs(MD.craft_order) do&lt;br /&gt;
		text[#text+1] = [=[&amp;lt;div class=&amp;quot;]=]..merchant_name:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;):gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;)..[=[-data All-data&amp;quot;&amp;gt;]=]&lt;br /&gt;
		text[#text+1] = p.draw_table({args={merchant=merchant_name}})&lt;br /&gt;
		text[#text+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/center&amp;gt;&amp;quot;&lt;br /&gt;
	return table.concat(text)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	Weapon    = {Data = &amp;quot;Data:Weapon.json&amp;quot;},&lt;br /&gt;
					Armor     = {Data = &amp;quot;Data:Armor.json&amp;quot;},&lt;br /&gt;
					Accessory = {Data = &amp;quot;Data:Accessory.json&amp;quot;},&lt;br /&gt;
					Utility   = {Data = &amp;quot;Data:Utility.json&amp;quot;},&lt;br /&gt;
					Misc      = {Data = &amp;quot;Data:Misc.json&amp;quot;},&lt;br /&gt;
					Monster   = {Data = &amp;quot;Data:Monster.json&amp;quot;},&lt;br /&gt;
					Prop      = {Data = &amp;quot;Data:Prop.json&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function draw_ingredient_table(wt,ingredient,category,data)&lt;br /&gt;
	local merchants = data[category] and data[category][ingredient] and data[category][ingredient].incraftingrecipefor or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Ingredient&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = ingredient&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is used in the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		for _,craft_name in ipairs(get_craft_list({ingredient=ingredient,merchant=merchant_name})) do&lt;br /&gt;
			craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function draw_craftable_table(wt,item,category,data)&lt;br /&gt;
	local merchants = data[category] and data[category][item] and data[category][item].iscraftableby or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Craftable&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = item&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is craftable with the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[item],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.page(f)&lt;br /&gt;
	local category = f.args.category&lt;br /&gt;
	local item = f.args.item&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[category].Data)&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	draw_craftable_table(wt,item,category,data)&lt;br /&gt;
	draw_ingredient_table(wt,item,category,data)&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on the wiki with these console inputs&lt;br /&gt;
-- mw.log(p.draw_table({args={item=&amp;quot;Void Blade&amp;quot;,merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;,ingredient=&amp;quot;Iron Ingot&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Void Blade&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Iron Ingot&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Iron Ore&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Iodred</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=66379</id>
		<title>Module:Craft</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=66379"/>
		<updated>2026-04-30T17:58:00Z</updated>

		<summary type="html">&lt;p&gt;Iodred: Undo revision 66378 by Iodred (talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--Either draw table of all craftable weapons for a merchant, or a singular item table&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Merchant.json&amp;quot;) --Global var holding tables from Data:Merchant.json&lt;br /&gt;
&lt;br /&gt;
local function iconbox(wt,name,amount,rarity,has_caption)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..rarity..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;[[File:&amp;quot;..name..&amp;quot;.png|x80px|link=&amp;quot;..name..&amp;quot;]]&amp;quot;&lt;br /&gt;
	if amount then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..amount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	if has_caption then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;[[&amp;quot;..name..&amp;quot;|&amp;lt;b class=cr&amp;quot;..rarity..&amp;quot;&amp;gt;&amp;quot;..name..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function name(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Name&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,craft.itemname,craft.quantity,craft.rarity,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ingredients(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:5%&#039;&amp;gt;Ingredients&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ingredient in ipairs(craft.ingredients) do&lt;br /&gt;
		local amount = string.match(ingredient,&amp;quot;^(%d*)-&amp;quot;)&lt;br /&gt;
		local ingredient_name = string.match(ingredient,&amp;quot;-(.*)-&amp;quot;)&lt;br /&gt;
		local rarity = string.match(ingredient,&amp;quot;-.*-(.*)$&amp;quot;)&lt;br /&gt;
		iconbox(wt,ingredient_name,amount,rarity)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function merchant(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Merchant&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,merchant_name,false,&amp;quot;-1&amp;quot;,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function affinity_to_unlock(wt,is_header,craft,merchant)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th class=&#039;tooltip&#039; style=&#039;width:2%&#039;&amp;gt;[[Template:Merchant Affinity|Affinity to Unlock]]&amp;lt;span class=&#039;tooltiptext-left&#039; style=&#039;left:50%; transform:translate(-50%); bottom:75%; width:100%&#039;&amp;gt;Affinity values are collected manually by the community.  If you spot an error, please follow the link and fix it!&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROW = {&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,ingredients,merchant}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function craft_row(wt,is_header,craft,merchant_name)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,craft_td in ipairs(ROW.Default) do&lt;br /&gt;
		craft_td(wt,is_header,craft,merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function craft_table_header(wt)&lt;br /&gt;
	craft_row(wt,true)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function spairs(dict)&lt;br /&gt;
    if not dict then return function() return nil,nil end end&lt;br /&gt;
&lt;br /&gt;
    local set = {}&lt;br /&gt;
    for key,_ in pairs(dict) do&lt;br /&gt;
        if key ~= &amp;quot;order&amp;quot; then&lt;br /&gt;
            set[#set+1] = key&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    table.sort(set,function(a,b)&lt;br /&gt;
    if type(a) ~= type(b) then&lt;br /&gt;
        return tostring(a) &amp;lt; tostring(b)&lt;br /&gt;
    else&lt;br /&gt;
        return a &amp;lt; b&lt;br /&gt;
    end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    local i = 0&lt;br /&gt;
    return function()&lt;br /&gt;
        i = i + 1&lt;br /&gt;
        local key = set[i]&lt;br /&gt;
        if key ~= nil then&lt;br /&gt;
            return key, dict[key]&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function has_ingredient(list,ingredient)&lt;br /&gt;
	for _,val in ipairs(list) do&lt;br /&gt;
		if string.match(val,ingredient) then return true end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function get_craft_list(args)&lt;br /&gt;
	local list = {}&lt;br /&gt;
	if args.item then&lt;br /&gt;
		list[#list+1] = args.item&lt;br /&gt;
	else&lt;br /&gt;
		for craft_name,craft in spairs(MD[args.merchant].crafts) do&lt;br /&gt;
			-- if filtering for ingredients, ensure the craft requires the ingredient&lt;br /&gt;
			if args.ingredient then&lt;br /&gt;
				if has_ingredient(craft.ingredients,args.ingredient) then&lt;br /&gt;
					list[#list+1] = craft_name&lt;br /&gt;
				end&lt;br /&gt;
			-- else include all crafts&lt;br /&gt;
			elseif args.merchant ~= nil then&lt;br /&gt;
				list[#list+1] = craft_name&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return list&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local BASIC_TABLE = { open = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:70%;min-width:500px;text-align:center;vertical-align:middle&#039;&amp;gt;&amp;quot;,&lt;br /&gt;
					  close= &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(f)&lt;br /&gt;
	local item_list = get_craft_list(f.args)&lt;br /&gt;
	local merchant_name = f.args.merchant&lt;br /&gt;
	if #item_list == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,craft_name in ipairs(item_list) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = [=[ class=&amp;quot;tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	if active == &amp;quot;yes&amp;quot; then&lt;br /&gt;
		class = [=[ class=&amp;quot;selected-tab tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	end&lt;br /&gt;
	return [=[&amp;lt;div style=&amp;quot;display:inline-block&amp;quot;]=]..class..[=[ data-tabid=&amp;quot;]=]..tabid..[=[&amp;quot; data-tab=&amp;quot;]=]..title:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;)..[=[&amp;quot; title=&amp;quot;]=]..title..[=[&amp;quot;&amp;gt;]=]..content..[=[&amp;lt;/div&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.crafting_page(frame)&lt;br /&gt;
	local text = {}&lt;br /&gt;
	text[#text+1] = [=[&amp;lt;div style=&amp;quot;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;&amp;quot;&amp;gt;]=]&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;None&amp;quot;,&amp;quot;[[File:None_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
	for _,merchant_name in ipairs(MD.craft_order) do&lt;br /&gt;
		if merchant_name == &amp;quot;Skeleton&amp;quot; or merchant_name == &amp;quot;Cockatrice&amp;quot; then&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant_name:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant_name..&amp;quot;_Merchant.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
		else&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant_name:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant_name..&amp;quot;.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;All&amp;quot;,&amp;quot;[[File:All_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;yes&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;center&amp;gt;&amp;lt;div class=&#039;None-data&#039;&amp;gt; &amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for _,merchant_name in ipairs(MD.craft_order) do&lt;br /&gt;
		text[#text+1] = [=[&amp;lt;div class=&amp;quot;]=]..merchant_name:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;):gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;)..[=[-data All-data&amp;quot;&amp;gt;]=]&lt;br /&gt;
		text[#text+1] = p.draw_table({args={merchant=merchant_name}})&lt;br /&gt;
		text[#text+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/center&amp;gt;&amp;quot;&lt;br /&gt;
	return table.concat(text)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	Weapon    = {Data = &amp;quot;Data:Weapon.json&amp;quot;},&lt;br /&gt;
					Armor     = {Data = &amp;quot;Data:Armor.json&amp;quot;},&lt;br /&gt;
					Accessory = {Data = &amp;quot;Data:Accessory.json&amp;quot;},&lt;br /&gt;
					Utility   = {Data = &amp;quot;Data:Utility.json&amp;quot;},&lt;br /&gt;
					Misc      = {Data = &amp;quot;Data:Misc.json&amp;quot;},&lt;br /&gt;
					Monster   = {Data = &amp;quot;Data:Monster.json&amp;quot;},&lt;br /&gt;
					Prop      = {Data = &amp;quot;Data:Prop.json&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function draw_ingredient_table(wt,ingredient,category,data)&lt;br /&gt;
	local merchants = data[category] and data[category][ingredient] and data[category][ingredient].incraftingrecipefor or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Ingredient&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = ingredient&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is used in the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		for _,craft_name in ipairs(get_craft_list({ingredient=ingredient,merchant=merchant_name})) do&lt;br /&gt;
			craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function draw_craftable_table(wt,item,category,data)&lt;br /&gt;
	local merchants = data[category] and data[category][item] and data[category][item].iscraftableby or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Craftable&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = item&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is craftable with the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[item],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.page(f)&lt;br /&gt;
	local category = f.args.category&lt;br /&gt;
	local item = f.args.item&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[category].Data)&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	draw_craftable_table(wt,item,category,data)&lt;br /&gt;
	draw_ingredient_table(wt,item,category,data)&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on the wiki with these console inputs&lt;br /&gt;
-- mw.log(p.draw_table({args={item=&amp;quot;Void Blade&amp;quot;,merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;,ingredient=&amp;quot;Iron Ingot&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Void Blade&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Iron Ingot&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Iron Ore&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Iodred</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=66378</id>
		<title>Module:Craft</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=66378"/>
		<updated>2026-04-30T17:55:02Z</updated>

		<summary type="html">&lt;p&gt;Iodred: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--Either draw table of all craftable weapons for a merchant, or a singular item table&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Merchant.json&amp;quot;) --Global var holding tables from Data:Merchant.json&lt;br /&gt;
&lt;br /&gt;
local function iconbox(wt,name,amount,rarity,has_caption)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..rarity..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;[[File:&amp;quot;..name..&amp;quot;.png|x80px|link=&amp;quot;..name..&amp;quot;]]&amp;quot;&lt;br /&gt;
	if amount then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..amount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	if has_caption then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;[[&amp;quot;..name..&amp;quot;|&amp;lt;b class=cr&amp;quot;..rarity..&amp;quot;&amp;gt;&amp;quot;..name..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function name(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Name&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,craft.itemname,craft.quantity,craft.rarity,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ingredients(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:5%&#039;&amp;gt;Ingredients&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ingredient in ipairs(craft.ingredients) do&lt;br /&gt;
		local amount = string.match(ingredient,&amp;quot;^(%d*)-&amp;quot;)&lt;br /&gt;
		local ingredient_name = string.match(ingredient,&amp;quot;-(.*)-&amp;quot;)&lt;br /&gt;
		local rarity = string.match(ingredient,&amp;quot;-.*-(.*)$&amp;quot;)&lt;br /&gt;
		iconbox(wt,ingredient_name,amount,rarity)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function merchant(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Merchant&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,merchant_name,false,&amp;quot;-1&amp;quot;,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function affinity_to_unlock(wt,is_header,craft,merchant)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th class=&#039;tooltip&#039; style=&#039;width:2%&#039;&amp;gt;[[Template:Merchant Affinity|Affinity to Unlock]]&amp;lt;span class=&#039;tooltiptext-left&#039; style=&#039;left:50%; transform:translate(-50%); bottom:75%; width:100%&#039;&amp;gt;Affinity values are collected manually by the community.  If you spot an error, please follow the link and fix it!&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROW = {&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,ingredients,merchant}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function craft_row(wt,is_header,craft,merchant_name)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	mw.logObject(craft)&lt;br /&gt;
	for _,craft_td in ipairs(ROW.Default) do&lt;br /&gt;
		craft_td(wt,is_header,craft,merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function craft_table_header(wt)&lt;br /&gt;
	craft_row(wt,true)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function spairs(dict)&lt;br /&gt;
    if not dict then return function() return nil,nil end end&lt;br /&gt;
&lt;br /&gt;
    local set = {}&lt;br /&gt;
    for key,_ in pairs(dict) do&lt;br /&gt;
        if key ~= &amp;quot;order&amp;quot; then&lt;br /&gt;
            set[#set+1] = key&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    table.sort(set,function(a,b)&lt;br /&gt;
    if type(a) ~= type(b) then&lt;br /&gt;
        return tostring(a) &amp;lt; tostring(b)&lt;br /&gt;
    else&lt;br /&gt;
        return a &amp;lt; b&lt;br /&gt;
    end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    local i = 0&lt;br /&gt;
    return function()&lt;br /&gt;
        i = i + 1&lt;br /&gt;
        local key = set[i]&lt;br /&gt;
        if key ~= nil then&lt;br /&gt;
            return key, dict[key]&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function has_ingredient(list,ingredient)&lt;br /&gt;
	for _,val in ipairs(list) do&lt;br /&gt;
		if string.match(val,ingredient) then return true end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function get_craft_list(args)&lt;br /&gt;
	local list = {}&lt;br /&gt;
	if args.item then&lt;br /&gt;
		list[#list+1] = args.item&lt;br /&gt;
	else&lt;br /&gt;
		for craft_name,craft in spairs(MD[args.merchant].crafts) do&lt;br /&gt;
			-- if filtering for ingredients, ensure the craft requires the ingredient&lt;br /&gt;
			if args.ingredient then&lt;br /&gt;
				if has_ingredient(craft.ingredients,args.ingredient) then&lt;br /&gt;
					list[#list+1] = craft_name&lt;br /&gt;
				end&lt;br /&gt;
			-- else include all crafts&lt;br /&gt;
			elseif args.merchant ~= nil then&lt;br /&gt;
				list[#list+1] = craft_name&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return list&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local BASIC_TABLE = { open = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:70%;min-width:500px;text-align:center;vertical-align:middle&#039;&amp;gt;&amp;quot;,&lt;br /&gt;
					  close= &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(f)&lt;br /&gt;
	local item_list = get_craft_list(f.args)&lt;br /&gt;
	local merchant_name = f.args.merchant&lt;br /&gt;
	if #item_list == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,craft_name in ipairs(item_list) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = [=[ class=&amp;quot;tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	if active == &amp;quot;yes&amp;quot; then&lt;br /&gt;
		class = [=[ class=&amp;quot;selected-tab tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	end&lt;br /&gt;
	return [=[&amp;lt;div style=&amp;quot;display:inline-block&amp;quot;]=]..class..[=[ data-tabid=&amp;quot;]=]..tabid..[=[&amp;quot; data-tab=&amp;quot;]=]..title:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;)..[=[&amp;quot; title=&amp;quot;]=]..title..[=[&amp;quot;&amp;gt;]=]..content..[=[&amp;lt;/div&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.crafting_page(frame)&lt;br /&gt;
	local text = {}&lt;br /&gt;
	text[#text+1] = [=[&amp;lt;div style=&amp;quot;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;&amp;quot;&amp;gt;]=]&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;None&amp;quot;,&amp;quot;[[File:None_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
	for _,merchant_name in ipairs(MD.craft_order) do&lt;br /&gt;
		if merchant_name == &amp;quot;Skeleton&amp;quot; or merchant_name == &amp;quot;Cockatrice&amp;quot; then&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant_name:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant_name..&amp;quot;_Merchant.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
		else&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant_name:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant_name..&amp;quot;.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;All&amp;quot;,&amp;quot;[[File:All_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;yes&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;center&amp;gt;&amp;lt;div class=&#039;None-data&#039;&amp;gt; &amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for _,merchant_name in ipairs(MD.craft_order) do&lt;br /&gt;
		text[#text+1] = [=[&amp;lt;div class=&amp;quot;]=]..merchant_name:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;):gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;)..[=[-data All-data&amp;quot;&amp;gt;]=]&lt;br /&gt;
		text[#text+1] = p.draw_table({args={merchant=merchant_name}})&lt;br /&gt;
		text[#text+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/center&amp;gt;&amp;quot;&lt;br /&gt;
	return table.concat(text)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	Weapon    = {Data = &amp;quot;Data:Weapon.json&amp;quot;},&lt;br /&gt;
					Armor     = {Data = &amp;quot;Data:Armor.json&amp;quot;},&lt;br /&gt;
					Accessory = {Data = &amp;quot;Data:Accessory.json&amp;quot;},&lt;br /&gt;
					Utility   = {Data = &amp;quot;Data:Utility.json&amp;quot;},&lt;br /&gt;
					Misc      = {Data = &amp;quot;Data:Misc.json&amp;quot;},&lt;br /&gt;
					Monster   = {Data = &amp;quot;Data:Monster.json&amp;quot;},&lt;br /&gt;
					Prop      = {Data = &amp;quot;Data:Prop.json&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function draw_ingredient_table(wt,ingredient,category,data)&lt;br /&gt;
	local merchants = data[category] and data[category][ingredient] and data[category][ingredient].incraftingrecipefor or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Ingredient&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = ingredient&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is used in the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		for _,craft_name in ipairs(get_craft_list({ingredient=ingredient,merchant=merchant_name})) do&lt;br /&gt;
			craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function draw_craftable_table(wt,item,category,data)&lt;br /&gt;
	local merchants = data[category] and data[category][item] and data[category][item].iscraftableby or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Craftable&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = item&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is craftable with the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[item],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.page(f)&lt;br /&gt;
	local category = f.args.category&lt;br /&gt;
	local item = f.args.item&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[category].Data)&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	draw_craftable_table(wt,item,category,data)&lt;br /&gt;
	draw_ingredient_table(wt,item,category,data)&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on the wiki with these console inputs&lt;br /&gt;
-- mw.log(p.draw_table({args={item=&amp;quot;Void Blade&amp;quot;,merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;,ingredient=&amp;quot;Iron Ingot&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Void Blade&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Iron Ingot&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Iron Ore&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Iodred</name></author>
	</entry>
</feed>