From Dark and Darker Wiki

Overview

Functions for making Crafting table. Data comes from Data:Merchant.json.

Functions

draw_table

Creates a table of craftables

  • merchant - Merchant of interest.
  • item - Singular item for which to create a table.
  • ingredient - Ingredient name. Used to filter the provided merchant's crafts for those which use the ingredient.

{{#invoke:Craft|draw_table|item=Void Blade|merchant=Weaponsmith}}
NameIngredientsMerchant
1
1
3
50



{{#invoke:Craft|draw_table|merchant=Weaponsmith|ingredient=Iron Ingot}}
NameIngredientsMerchant
2
1



{{#invoke:Craft|draw_table|merchant=Weaponsmith}}
NameIngredientsMerchant
2
1
1
1
1
1
1
1
1
1
3
50
1
1
1
1
5
50
3
3
1
1
3
50
1
1
1
1
2
3
50
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1
1
2
3
50
1
2
1
1
1
1
1
1
1
1
3
3
1
2
1
1
1
2
1
1
2
2
50
1
1
1
2
50
3
1
1
3
3
50
1
1
1
2
50
1
2
1
1
3
1
1
1
1
1
1
1
1
2
1
1
3
6
50
1
1
1
2
3
1
1
60
1
1
3
3
50
1
2
5
3
50
1
1
3
3
50
1
1
3
50
1
1
1
1
1
1
1
1
1
1
2
1
1
1
1
1
1
3
1
1
1
50
1
1
5
2
50
1
1
3
50

page

Creates, if possible, two tables. The first presents recipes for if the given item is craftable. The second presents recipes that use the item to create other items.

  • item - Singular item for which to create a table.
  • category - Item's item category. Used to lookup merchant crafting presence.

{{#invoke:Craft|page|item=Void Blade|category=Weapon}}

Craftable

Void Blade is craftable with the following crafting recipes.
NameIngredientsMerchant
1
1
3
50



{{#invoke:Craft|page|category=Misc|item=Iron Ingot}}

Craftable

Iron Ingot is craftable with the following crafting recipes.
NameIngredientsMerchant
3
3

Ingredient

Iron Ingot is used in the following crafting recipes.
NameIngredientsMerchant
1
1
3
3
50
2
1
1
1
2
1
1
1
1
2
1
1
3
3
50
1
2
1
1
1
2



{{#invoke:Craft|page|category=Misc|item=Iron Ore}}

Ingredient

Iron Ore is used in the following crafting recipes.
NameIngredientsMerchant
2
3
3

--Either draw table of all craftable weapons for a merchant, or a singular item table

local p = {}
local MD = mw.loadJsonData("Data:Merchant.json") --Global var holding tables from Data:Merchant.json

local function iconbox(wt,name,amount,rarity,has_caption)
	wt[#wt+1] = "<div class='iconbox' style='width:max-content;align-items:center'><div class='rarity"..rarity.." rounded relative'>"
	wt[#wt+1] = "[[File:"..name..".png|x80px|link="..name.."]]"
	if amount then
		wt[#wt+1] = "<span class='iconamount' style='pointer-events:none;color:#EEEA;font-size:16px'>"..amount.."</span>"
	end
	wt[#wt+1] = "</div>"
	if has_caption then
		wt[#wt+1] = "<br>[["..name.."|<b class=cr"..rarity..">"..name.."</b>]]"
	end
	wt[#wt+1] = "</div>"
end

local function name(wt,is_header,craft,merchant_name)
	if is_header then wt[#wt+1] = "<th style='width:2%'>Name</th>" return end

	wt[#wt+1] = "<td>"
	iconbox(wt,craft.itemname,craft.quantity,craft.rarity,true)
	wt[#wt+1] = "</td>"
end

local function ingredients(wt,is_header,craft,merchant_name)
	if is_header then wt[#wt+1] = "<th style='width:5%'>Ingredients</th>" return end

	wt[#wt+1] = "<td>"
	for _,ingredient in ipairs(craft.ingredients) do
		local amount = string.match(ingredient,"^(%d*)-")
		local ingredient_name = string.match(ingredient,"-(.*)-")
		local rarity = string.match(ingredient,"-.*-(.*)$")
		iconbox(wt,ingredient_name,amount,rarity)
	end
	wt[#wt+1] = "</td>"
end

local function merchant(wt,is_header,craft,merchant_name)
	if is_header then wt[#wt+1] = "<th style='width:2%'>Merchant</th>" return end

	wt[#wt+1] = "<td>"
	iconbox(wt,merchant_name,false,"-1",true)
	wt[#wt+1] = "</td>"
end

local function affinity_to_unlock(wt,is_header,craft,merchant)
	if is_header then wt[#wt+1] = "<th class='tooltip' style='width:2%'>[[Template:Merchant Affinity|Affinity to Unlock]]<span class='tooltiptext-left' style='left:50%; transform:translate(-50%); bottom:75%; width:100%'>Affinity values are collected manually by the community.  If you spot an error, please follow the link and fix it!</span></th></tr>" return end

end

local ROW = {
	["Default"] = {name,ingredients,merchant}
}

local function craft_row(wt,is_header,craft,merchant_name)
	wt[#wt+1] = "<tr>"
	for _,craft_td in ipairs(ROW.Default) do
		craft_td(wt,is_header,craft,merchant_name)
	end
	wt[#wt+1] = "</tr>"
end

local function craft_table_header(wt)
	craft_row(wt,true)
end

local function spairs(dict)
    if not dict then return function() return nil,nil end end

    local set = {}
    for key,_ in pairs(dict) do
        if key ~= "order" then
            set[#set+1] = key
        end
    end

    table.sort(set,function(a,b)
    if type(a) ~= type(b) then
        return tostring(a) < tostring(b)
    else
        return a < b
    end
    end)

    local i = 0
    return function()
        i = i + 1
        local key = set[i]
        if key ~= nil then
            return key, dict[key]
        end
    end
end

local function has_ingredient(list,ingredient)
	for _,val in ipairs(list) do
		if string.match(val,ingredient) then return true end
	end

	return false
end

-- args: item?, ingredient?, merchant? -- return: array of strings (names)
local function get_craft_names_list(args) 
	local list = {}
	if args.item then
		list[#list+1] = args.item
	else
		local set = {}
		for craft_name,craft in spairs(MD[args.merchant].crafts) do
			-- if filtering for ingredients, ensure the craft requires the ingredient
			if args.ingredient then
				for _,value in spairs(craft.rarities) do
					if has_ingredient(value.ingredients,args.ingredient) and set[args.ingredient] ~= true then
						 -- Mock hashset of ingredient names so that ingredients are only added once to list
						set[args.ingredient] = true
						list[#list+1] = craft_name
					end
				end
			-- else include all crafts
			elseif args.merchant ~= nil then
				list[#list+1] = craft_name
			end
		end
	end

	return list
end

-- return object <{itemname, rarity, ingredients, quantity}[]>
local function map_rarity_to_craft(name, rarityIndex, rarityObject)
	local mappedCraft = {}
	mappedCraft["itemname"] = name
	mappedCraft["rarity"] = rarityIndex
	mappedCraft["ingredients"] = rarityObject.ingredients
	mappedCraft["quantity"] = rarityObject.quantity
	return mappedCraft
end 

-- args: ingredient, merchant -- return: array <{itemname, rarity, ingredients, quantity}[]>
local function get_crafts_by_ingredient(args) 
	local list = {}
	if args.ingredient ~= nil then 
		for craft_name,craft in spairs(MD[args.merchant].crafts) do
			for index,value in spairs(craft.rarities) do
				if has_ingredient(value.ingredients,args.ingredient) then
					list[#list+1] = map_rarity_to_craft(craft_name,index,value)
				end
			end
		end
	end
	return list
end

local function craft_row_for_each_rarity(wt,merchant_name,craft_name)
	local craft = MD[merchant_name].crafts[craft_name]
	for index,value in spairs(craft.rarities) do
		craft_row(wt,false,map_rarity_to_craft(craft.itemname,index,value),merchant_name)
	end
end

local BASIC_TABLE = { open = "<table cellspacing='0' class='wikitable sortable jquery-tablesorter' style='width:70%;min-width:500px;text-align:center;vertical-align:middle'>",
					  close= "</table>"}

function p.draw_table(f)
	local item_list = get_craft_names_list(f.args)
	local merchant_name = f.args.merchant
	if #item_list == 0 then return "" end

	local wt = {}
	wt[#wt+1] = BASIC_TABLE.open
	craft_table_header(wt)
	for _,craft_name in ipairs(item_list) do
		craft_row_for_each_rarity(wt,merchant_name,craft_name)
	end
	wt[#wt+1] = BASIC_TABLE.close

	return table.concat(wt)
end

local function tab_toggle(title,content,active,tabid)
	local class = [=[ class="tab-toggle tab"]=]
	if active == "yes" then
		class = [=[ class="selected-tab tab-toggle tab"]=]
	end
	return [=[<div style="display:inline-block"]=]..class..[=[ data-tabid="]=]..tabid..[=[" data-tab="]=]..title:gsub(" ","")..[=[" title="]=]..title..[=[">]=]..content..[=[</div>]=]
end

function p.crafting_page(frame)
	local text = {}
	text[#text+1] = [=[<div style="display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;">]=]
	text[#text+1] = tab_toggle("None","[[File:None_Merchants_Tab.png|link=|130px]]","","0")
	for _,merchant_name in ipairs(MD.craft_order) do
		if merchant_name == "Skeleton" or merchant_name == "Cockatrice" then
			text[#text+1] = tab_toggle(merchant_name:gsub("'",""),"[[File:"..merchant_name.."_Merchant.png|link=|130px]]","","0")
		else
			text[#text+1] = tab_toggle(merchant_name:gsub("'",""),"[[File:"..merchant_name..".png|link=|130px]]","","0")
		end
	end
	text[#text+1] = tab_toggle("All","[[File:All_Merchants_Tab.png|link=|130px]]","yes","0")
	text[#text+1] = "</div><center><div class='None-data'> </div>"
	for _,merchant_name in ipairs(MD.craft_order) do
		text[#text+1] = [=[<div class="]=]..merchant_name:gsub(" ",""):gsub("'","")..[=[-data All-data">]=]
		text[#text+1] = p.draw_table({args={merchant=merchant_name}})
		text[#text+1] = "</div>"
	end
	text[#text+1] = "</center>"
	return table.concat(text)
end

local CATEGORY = {	Weapon    = {Data = "Data:Weapon.json"},
					Armor     = {Data = "Data:Armor.json"},
					Accessory = {Data = "Data:Accessory.json"},
					Utility   = {Data = "Data:Utility.json"},
					Misc      = {Data = "Data:Misc.json"},
					Monster   = {Data = "Data:Monster.json"},
					Prop      = {Data = "Data:Prop.json"}}

local function count(t)
	local c = 0
	for _ in pairs(t) do c = c + 1 end
	return c
end

local function draw_ingredient_table(wt,ingredient,category,data)
	local merchants = data[category] and data[category][ingredient] and data[category][ingredient].incraftingrecipefor or {}
	if count(merchants) == 0 then return "" end

	wt[#wt+1] = "<h2>Ingredient</h2>"
	wt[#wt+1] = ingredient
	wt[#wt+1] = " is used in the following [[crafting]] recipes."
	wt[#wt+1] = BASIC_TABLE.open
	craft_table_header(wt)
	for _,merchant_name in ipairs(merchants) do
		for _,craft in ipairs(get_crafts_by_ingredient({ingredient=ingredient,merchant=merchant_name})) do
			craft_row(wt,false,craft,merchant_name)
		end
	end
	wt[#wt+1] = BASIC_TABLE.close
end

local function draw_craftable_table(wt,item,category,data)
	local merchants = data[category] and data[category][item] and data[category][item].iscraftableby or {}
	if count(merchants) == 0 then return "" end

	wt[#wt+1] = "<h2>Craftable</h2>"
	wt[#wt+1] = item
	wt[#wt+1] = " is craftable with the following [[crafting]] recipes."
	wt[#wt+1] = BASIC_TABLE.open
	craft_table_header(wt)
	for _,merchant_name in ipairs(merchants) do
		craft_row_for_each_rarity(wt,merchant_name,item)
	end
	wt[#wt+1] = BASIC_TABLE.close
end

function p.page(f)
	local category = f.args.category
	local item = f.args.item
	local data = mw.loadJsonData(CATEGORY[category].Data)

	local wt = {}
	draw_craftable_table(wt,item,category,data)
	draw_ingredient_table(wt,item,category,data)
	return table.concat(wt)
end

return p


--[=[
Test on the wiki with these console inputs
mw.log(p.draw_table({args={item="Void Blade",merchant="Weaponsmith"}}))
mw.log(p.draw_table({args={merchant="Weaponsmith"}}))
mw.log(p.draw_table({args={merchant="Weaponsmith",ingredient="Iron Ingot"}}))
mw.log(p.page({args={item="Void Blade",category="Weapon"}}))
mw.log(p.page({args={item="Iron Ingot",category="Misc"}}))
mw.log(p.page({args={item="Iron Ore",category="Misc"}}))
]=]