- 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.
From Dark and Darker Wiki
(Made iconbox slightly more dynamic. Changed merchant td to display iconbox instead of a plain link.) |
(Refactored craft list filtering for draw_ingredients_table. Split get_craft_list calls into get_craft_names_list:string[ ] and get_crafts_by_ingredient:obj[ ]) |
||
| (13 intermediate revisions by 2 users not shown) | |||
| Line 103: | Line 103: | ||
end | end | ||
local function | -- args: item?, ingredient?, merchant? -- return: array of strings (names) | ||
local function get_craft_names_list(args) | |||
local list = {} | local list = {} | ||
if args.item then | if args.item then | ||
list[#list+1] = args.item | list[#list+1] = args.item | ||
else | else | ||
local set = {} | |||
for craft_name,craft in spairs(MD[args.merchant].crafts) do | for craft_name,craft in spairs(MD[args.merchant].crafts) do | ||
-- if filtering for ingredients, ensure the craft requires the ingredient | -- if filtering for ingredients, ensure the craft requires the ingredient | ||
if args.ingredient then | if args.ingredient then | ||
if has_ingredient( | 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 | end | ||
-- else include all crafts | -- else include all crafts | ||
| Line 122: | Line 128: | ||
return list | 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 | end | ||
| Line 128: | Line 166: | ||
function p.draw_table(f) | function p.draw_table(f) | ||
local item_list = | local item_list = get_craft_names_list(f.args) | ||
local merchant_name = f.args.merchant | local merchant_name = f.args.merchant | ||
if #item_list == 0 then return "" end | if #item_list == 0 then return "" end | ||
| Line 136: | Line 174: | ||
craft_table_header(wt) | craft_table_header(wt) | ||
for _,craft_name in ipairs(item_list) do | for _,craft_name in ipairs(item_list) do | ||
craft_row_for_each_rarity(wt,merchant_name,craft_name) | |||
end | end | ||
wt[#wt+1] = BASIC_TABLE.close | wt[#wt+1] = BASIC_TABLE.close | ||
return table.concat(wt) | 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 | end | ||
| Line 157: | Line 225: | ||
end | end | ||
function | local function draw_ingredient_table(wt,ingredient,category,data) | ||
local | local merchants = data[category] and data[category][ingredient] and data[category][ingredient].incraftingrecipefor or {} | ||
if count(merchants) == 0 then return "" end | if count(merchants) == 0 then return "" end | ||
wt[#wt+1] = "<h2>Ingredient</h2>" | wt[#wt+1] = "<h2>Ingredient</h2>" | ||
wt[#wt+1] = ingredient | wt[#wt+1] = ingredient | ||
| Line 171: | Line 235: | ||
craft_table_header(wt) | craft_table_header(wt) | ||
for _,merchant_name in ipairs(merchants) do | for _,merchant_name in ipairs(merchants) do | ||
for _, | for _,craft in ipairs(get_crafts_by_ingredient({ingredient=ingredient,merchant=merchant_name})) do | ||
craft_row(wt,false, | craft_row(wt,false,craft,merchant_name) | ||
end | end | ||
end | end | ||
wt[#wt+1] = BASIC_TABLE.close | wt[#wt+1] = BASIC_TABLE.close | ||
end | end | ||
function | local function draw_craftable_table(wt,item,category,data) | ||
local | local merchants = data[category] and data[category][item] and data[category][item].iscraftableby or {} | ||
if count(merchants) == 0 then return "" end | if count(merchants) == 0 then return "" end | ||
wt[#wt+1] = "<h2>Craftable</h2>" | wt[#wt+1] = "<h2>Craftable</h2>" | ||
wt[#wt+1] = item | wt[#wt+1] = item | ||
| Line 194: | Line 252: | ||
craft_table_header(wt) | craft_table_header(wt) | ||
for _,merchant_name in ipairs(merchants) do | for _,merchant_name in ipairs(merchants) do | ||
craft_row_for_each_rarity(wt,merchant_name,item) | |||
end | end | ||
wt[#wt+1] = BASIC_TABLE.close | 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) | return table.concat(wt) | ||
end | end | ||
| Line 205: | Line 271: | ||
-- Test on the wiki with these console inputs | --[=[ | ||
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"}})) | |||
]=] | |||
Latest revision as of 09:56, 1 May 2026
Overview
Functions for making Crafting table. Data comes from Data:Merchant.json.
Functions
draw_table
Creates a table of craftables
{{#invoke:Craft|draw_table|item=Void Blade|merchant=Weaponsmith}}
| Name | Ingredients | Merchant |
|---|---|---|
{{#invoke:Craft|draw_table|merchant=Weaponsmith|ingredient=Iron Ingot}}
| Name | Ingredients | Merchant |
|---|---|---|
{{#invoke:Craft|draw_table|merchant=Weaponsmith}}
| Name | Ingredients | Merchant |
|---|---|---|
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.| Name | Ingredients | Merchant |
|---|---|---|
{{#invoke:Craft|page|category=Misc|item=Iron Ingot}}
Craftable
Iron Ingot is craftable with the following crafting recipes.| Name | Ingredients | Merchant |
|---|---|---|
Ingredient
Iron Ingot is used in the following crafting recipes.| Name | Ingredients | Merchant |
|---|---|---|
{{#invoke:Craft|page|category=Misc|item=Iron Ore}}
Ingredient
Iron Ore is used in the following crafting recipes.| Name | Ingredients | Merchant |
|---|---|---|
--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"}}))
]=]