From Dark and Darker Wiki

Revision as of 17:58, 30 April 2026 by Iodred (talk | contribs) (Undo revision 66378 by Iodred (talk))

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

Lua error at line 7: attempt to concatenate local 'rarity' (a nil value).



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

Lua error in ...ribunto/includes/engines/LuaCommon/lualib/mwInit.lua at line 23: bad argument #1 to 'old_ipairs' (table expected, got nil).



{{#invoke:Craft|draw_table|merchant=Weaponsmith}}

Lua error at line 7: attempt to concatenate local 'rarity' (a nil value).

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

Lua error at line 7: attempt to concatenate local 'rarity' (a nil value).



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

Lua error at line 7: attempt to concatenate local 'rarity' (a nil value).



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

Lua error in ...ribunto/includes/engines/LuaCommon/lualib/mwInit.lua at line 23: bad argument #1 to 'old_ipairs' (table expected, got nil).


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

local function get_craft_list(args)
	local list = {}
	if args.item then
		list[#list+1] = args.item
	else
		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
				if has_ingredient(craft.ingredients,args.ingredient) then
					list[#list+1] = craft_name
				end
			-- else include all crafts
			elseif args.merchant ~= nil then
				list[#list+1] = craft_name
			end
		end
	end

	return list
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_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(wt,false,MD[merchant_name].crafts[craft_name],merchant_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_name in ipairs(get_craft_list({ingredient=ingredient,merchant=merchant_name})) do
			craft_row(wt,false,MD[merchant_name].crafts[craft_name],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(wt,false,MD[merchant_name].crafts[item],merchant_name)
	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"}}))