Compare commits

..

1 Commits

Author SHA1 Message Date
3be06cf94a Add an API for custom recipes 2018-12-16 23:47:09 +01:00
2 changed files with 214 additions and 210 deletions

View File

@ -20,8 +20,9 @@ Use the command `/craft` to show the recipe(s) of the pointed node.
#### Registering a custom crafting type ####
```Lua
craftguide.register_craft_type("digging", {
description = "Digging",
icon = "default_tool_steelpick.png",
description = S("Digging"),
icon = "default_tool_steelpick.png",
width = 1,
})
```
@ -29,8 +30,7 @@ craftguide.register_craft_type("digging", {
```Lua
craftguide.register_craft({
type = "digging",
width = 1,
output = "default:cobble 2",
output = "default:cobble",
items = {"default:stone"},
})
```

416
init.lua
View File

@ -1,4 +1,4 @@
craftguide = {
local craftguide = {
custom_crafts = {},
craft_types = {},
}
@ -21,7 +21,7 @@ craftguide.intllib = S
-- Lua 5.3 removed `table.maxn`, use this alternative in case of breakage:
-- https://github.com/kilbith/xdecor/blob/master/handlers/helpers.lua#L1
local remove, maxn, sort, concat = table.remove, table.maxn, table.sort, table.concat
local remove, maxn, sort = table.remove, table.maxn, table.sort
local vector_add, vector_mul = vector.add, vector.multiply
local min, max, floor, ceil = math.min, math.max, math.floor, math.ceil
@ -55,7 +55,8 @@ end
craftguide.register_craft_type("digging", {
description = S("Digging"),
icon = "default_tool_steelpick.png",
icon = "default_tool_steelpick.png",
width = 1,
})
function craftguide.register_craft(def)
@ -64,7 +65,6 @@ end
craftguide.register_craft({
type = "digging",
width = 1,
output = "default:cobble",
items = {"default:stone"},
})
@ -86,7 +86,7 @@ local function reset_datas(data)
data.fuel = nil
end
local function in_table(T)
local function player_has_item(T)
for i = 1, #T do
if T[i] then
return true
@ -110,7 +110,39 @@ local function item_in_inv(inv, item)
return inv:contains_item("main", item)
end
local function group_to_item(item)
local show_fs = function(player, player_name)
if sfinv_only then
local context = sfinv.get_or_create_context(player)
sfinv.set_player_inventory_formspec(player, context)
else
craftguide:get_formspec(player_name)
end
end
local function init_datas(user, name)
datas[name] = {filter = "", pagenum = 1, iX = sfinv_only and 8 or DEFAULT_SIZE}
if progressive_mode then
craftguide:get_filter_items(datas[name], user)
end
end
local function add_custom_recipes(item, recipes)
for j = 1, #craftguide.custom_crafts do
local craft = craftguide.custom_crafts[j]
if craft.output:match("%S*") == item then
recipes[#recipes + 1] = {
width = craftguide.craft_types[craft.type].width,
type = craft.type,
items = craft.items,
output = craft.output,
}
end
end
return recipes
end
function craftguide:group_to_item(item)
if item:sub(1,6) == "group:" then
local itemsub = item:sub(7)
if group_stereotypes[itemsub] then
@ -129,7 +161,7 @@ local function group_to_item(item)
return item:sub(1,6) == "group:" and "" or item
end
local function get_tooltip(item, recipe_type, cooktime, groups)
function craftguide:get_tooltip(item, recipe_type, cooktime, groups)
local tooltip, item_desc = "tooltip[" .. item .. ";", ""
local fueltime = get_fueltime(item)
local has_extras = groups or recipe_type == "cooking" or fueltime > 0
@ -166,13 +198,14 @@ local function get_tooltip(item, recipe_type, cooktime, groups)
return has_extras and tooltip .. "]" or ""
end
local function _get_recipe(iX, iY, xoffset, recipe_num, recipes, show_usage)
local fs, recipes_total = {}, #recipes
function craftguide:get_recipe(iX, iY, xoffset, recipe_num, recipes, show_usage)
local formspec, recipes_total = "", #recipes
if recipes_total > 1 then
fs[#fs + 1] = "button[" .. (iX - (sfinv_only and 2.2 or 2.6)) .. "," ..
(iY + (sfinv_only and 3.9 or 3.3)) .. ";2.2,1;alternate;" ..
(show_usage and S("Usage") or S("Recipe")) .. " " ..
S("@1 of @2", recipe_num, recipes_total) .. "]"
formspec = formspec ..
"button[" .. (iX - (sfinv_only and 2.2 or 2.6)) .. "," ..
(iY + (sfinv_only and 3.9 or 3.3)) .. ";2.2,1;alternate;" ..
(show_usage and S("Usage") or S("Recipe")) .. " " ..
S("@1 of @2", recipe_num, recipes_total) .. "]"
end
local recipe_type = recipes[recipe_num].type
@ -189,18 +222,18 @@ local function _get_recipe(iX, iY, xoffset, recipe_num, recipes, show_usage)
local rows = ceil(maxn(items) / width)
local rightest, s_btn_size = 0
if recipe_type ~= "cooking" and (width > GRID_LIMIT or rows > GRID_LIMIT) then
fs[#fs + 1] = "label[" .. ((iX / 2) - 2) .. "," .. (iY + 2.2) .. ";" ..
S("Recipe is too big to be displayed (@1x@2)", width, rows) .. "]"
return concat(fs)
if recipe_type ~= "fuel" and (width > GRID_LIMIT or rows > GRID_LIMIT) then
formspec = formspec ..
"label[" .. ((iX / 2) - 2) .. "," .. (iY + 2.2) .. ";" ..
S("Recipe is too big to be displayed (@1x@2)", width, rows) .. "]"
return formspec
else
for i, v in pairs(items) do
local X = ceil((i - 1) % width + xoffset - width) -
(sfinv_only and 0 or 0.2)
local Y = ceil(i / width + (iY + 2) - min(2, rows))
if recipe_type ~= "cooking" and (width > 3 or rows > 3) then
if recipe_type ~= "fuel" and (width > 3 or rows > 3) then
BUTTON_SIZE = width > 3 and 3 / width or 3 / rows
s_btn_size = BUTTON_SIZE
X = BUTTON_SIZE * (i % width) + xoffset - 2.65
@ -213,19 +246,20 @@ local function _get_recipe(iX, iY, xoffset, recipe_num, recipes, show_usage)
local groups = extract_groups(v)
local label = groups and "\nG" or ""
local item_r = group_to_item(v)
local tltip = get_tooltip(item_r, recipe_type, cooktime, groups)
local item_r = self:group_to_item(v)
local tltip = self:get_tooltip(item_r, recipe_type, cooktime, groups)
fs[#fs + 1] = "item_image_button[" .. X .. "," ..
(Y + (sfinv_only and 0.7 or 0.2)) .. ";" ..
BUTTON_SIZE .. "," .. BUTTON_SIZE .. ";" .. item_r ..
";" .. item_r:match("%S*") .. ";" .. label .. "]" .. tltip
formspec = formspec ..
"item_image_button[" .. X .. "," ..
(Y + (sfinv_only and 0.7 or 0.2)) .. ";" ..
BUTTON_SIZE .. "," .. BUTTON_SIZE .. ";" .. item_r ..
";" .. item_r .. ";" .. label .. "]" .. tltip
end
BUTTON_SIZE = 1.1
end
local custom_recipe = craftguide.craft_types[recipe_type]
local custom_recipe = self.craft_types[recipe_type]
if recipe_type == "cooking" or (recipe_type == "normal" and width == 0) or
custom_recipe then
local icon = recipe_type == "cooking" and "furnace" or "shapeless"
@ -233,13 +267,13 @@ local function _get_recipe(iX, iY, xoffset, recipe_num, recipes, show_usage)
(iY + (sfinv_only and 2.2 or 1.7)) ..
";0.5,0.5;"
fs[#fs + 1] = "image[" .. coords ..
(custom_recipe and custom_recipe.icon or
"craftguide_" .. icon .. ".png^[resize:16x16") .. "]"
fs[#fs + 1] = "tooltip[" .. coords ..
(custom_recipe and custom_recipe.description or
recipe_type:gsub("^%l", string.upper)) .. "]"
formspec = formspec ..
"image[" .. coords ..
(custom_recipe and custom_recipe.icon or
"craftguide_" .. icon .. ".png") .. "]" ..
"tooltip[" .. coords ..
(custom_recipe and custom_recipe.description or
recipe_type:gsub("^%l", string.upper)) .. "]"
end
local output = recipes[recipe_num].output
@ -249,31 +283,33 @@ local function _get_recipe(iX, iY, xoffset, recipe_num, recipes, show_usage)
local arrow_X = rightest + (s_btn_size or BUTTON_SIZE)
local output_X = arrow_X + 0.9
fs[#fs + 1] = "image[" .. arrow_X .. "," ..
(iY + (sfinv_only and 2.85 or 2.35)) ..
";0.9,0.7;craftguide_arrow.png]"
formspec = formspec ..
"image[" .. arrow_X .. "," ..
(iY + (sfinv_only and 2.85 or 2.35)) ..
";0.9,0.7;craftguide_arrow.png]" ..
fs[#fs + 1] = "item_image_button[" .. output_X .. "," ..
(iY + (sfinv_only and 2.7 or 2.2)) .. ";" ..
BUTTON_SIZE .. "," .. BUTTON_SIZE .. ";" ..
output .. ";" .. output_s .. ";]"
"item_image_button[" .. output_X .. "," ..
(iY + (sfinv_only and 2.7 or 2.2)) .. ";" ..
BUTTON_SIZE .. "," .. BUTTON_SIZE .. ";" ..
output .. ";" .. output_s .. ";]" ..
fs[#fs + 1] = get_tooltip(output_s)
self:get_tooltip(output_s)
if output_is_fuel then
fs[#fs + 1] = "image[" .. (output_X + 1) .. "," ..
(iY + (sfinv_only and 2.83 or 2.33)) ..
";0.6,0.4;craftguide_arrow.png]"
formspec = formspec ..
"image[" .. (output_X + 1) .. "," ..
(iY + (sfinv_only and 2.83 or 2.33)) ..
";0.6,0.4;craftguide_arrow.png]" ..
fs[#fs + 1] = "image[" .. (output_X + 1.6) .. "," ..
(iY + (sfinv_only and 2.68 or 2.18)) ..
";0.6,0.6;craftguide_fire.png]"
"image[" .. (output_X + 1.6) .. "," ..
(iY + (sfinv_only and 2.68 or 2.18)) ..
";0.6,0.6;craftguide_fire.png]"
end
return concat(fs)
return formspec
end
local function get_formspec(player_name)
function craftguide:get_formspec(player_name)
local data = datas[player_name]
local iY = sfinv_only and 4 or data.iX - 5
local ipp = data.iX * iY
@ -284,42 +320,42 @@ local function get_formspec(player_name)
data.pagemax = max(1, ceil(#data.items / ipp))
local fs = {}
local formspec = ""
if not sfinv_only then
fs[#fs + 1] = "size[" .. (data.iX - 0.35) .. "," .. (iY + 4) .. ";]"
fs[#fs + 1] = "background[1,1;1,1;craftguide_bg.png;true]"
fs[#fs + 1] = "tooltip[size_inc;" .. S("Increase window size") .. "]"
fs[#fs + 1] = "tooltip[size_dec;" .. S("Decrease window size") .. "]"
fs[#fs + 1] = "image_button[" .. (data.iX * 0.47) ..
",0.12;0.8,0.8;craftguide_zoomin_icon.png;size_inc;]"
fs[#fs + 1] = "image_button[" .. ((data.iX * 0.47) + 0.6) ..
formspec = formspec ..
"size[" .. (data.iX - 0.35) .. "," .. (iY + 4) .. ";]" ..
"background[1,1;1,1;craftguide_bg.png;true]" ..
"tooltip[size_inc;" .. S("Increase window size") .. "]" ..
"tooltip[size_dec;" .. S("Decrease window size") .. "]" ..
"image_button[" .. (data.iX * 0.47) ..
",0.12;0.8,0.8;craftguide_zoomin_icon.png;size_inc;]" ..
"image_button[" .. ((data.iX * 0.47) + 0.6) ..
",0.12;0.8,0.8;craftguide_zoomout_icon.png;size_dec;]"
end
fs[#fs + 1] = [[
image_button[2.4,0.12;0.8,0.8;craftguide_search_icon.png;search;]
image_button[3.05,0.12;0.8,0.8;craftguide_clear_icon.png;clear;]
field_close_on_enter[filter;false]
]]
fs[#fs + 1] = "tooltip[search;" .. S("Search") .. "]"
fs[#fs + 1] = "tooltip[clear;" .. S("Reset") .. "]"
fs[#fs + 1] = "tooltip[prev;" .. S("Previous page") .. "]"
fs[#fs + 1] = "tooltip[next;" .. S("Next page") .. "]"
fs[#fs + 1] = "image_button[" .. (data.iX - (sfinv_only and 2.6 or 3.1)) ..
",0.12;0.8,0.8;craftguide_prev_icon.png;prev;]"
fs[#fs + 1] = "label[" .. (data.iX - (sfinv_only and 1.7 or 2.2)) .. ",0.22;" ..
colorize(data.pagenum) .. " / " .. data.pagemax .. "]"
fs[#fs + 1] = "image_button[" .. (data.iX - (sfinv_only and 0.7 or 1.2) -
(data.iX >= 11 and 0.08 or 0)) ..
",0.12;0.8,0.8;craftguide_next_icon.png;next;]"
fs[#fs + 1] = "field[0.3,0.32;2.5,1;filter;;" .. mt.formspec_escape(data.filter) .. "]"
formspec = formspec .. [[
image_button[2.4,0.12;0.8,0.8;craftguide_search_icon.png;search;]
image_button[3.05,0.12;0.8,0.8;craftguide_clear_icon.png;clear;]
field_close_on_enter[filter;false]
]] ..
"tooltip[search;" .. S("Search") .. "]" ..
"tooltip[clear;" .. S("Reset") .. "]" ..
"tooltip[prev;" .. S("Previous page") .. "]" ..
"tooltip[next;" .. S("Next page") .. "]" ..
"image_button[" .. (data.iX - (sfinv_only and 2.6 or 3.1)) ..
",0.12;0.8,0.8;craftguide_prev_icon.png;prev;]" ..
"label[" .. (data.iX - (sfinv_only and 1.7 or 2.2)) .. ",0.22;" ..
colorize(data.pagenum) .. " / " .. data.pagemax .. "]" ..
"image_button[" .. (data.iX - (sfinv_only and 0.7 or 1.2) -
(data.iX >= 11 and 0.08 or 0)) ..
",0.12;0.8,0.8;craftguide_next_icon.png;next;]" ..
"field[0.3,0.32;2.5,1;filter;;" .. mt.formspec_escape(data.filter) .. "]"
local xoffset = data.iX / 2.15
if not next(data.items) then
fs[#fs + 1] = "label[" .. ((data.iX / 2) - 1) ..
",2;" .. S("No item to show") .. "]"
formspec = formspec ..
"label[" .. ((data.iX / 2) - 1) .. ",2;" .. S("No item to show") .. "]"
end
local first_item = (data.pagenum - 1) * ipp
@ -329,61 +365,53 @@ local function get_formspec(player_name)
local X = i % data.iX
local Y = (i % ipp - X) / data.iX + 1
fs[#fs + 1] = "item_image_button[" ..
(X - (sfinv_only and 0 or (X * 0.05))) .. "," ..
Y .. ";" .. BUTTON_SIZE .. "," .. BUTTON_SIZE .. ";" ..
name .. ";" .. name .. "_inv;]"
formspec = formspec ..
"item_image_button[" ..
(X - (sfinv_only and 0 or (X * 0.05))) .. "," ..
Y .. ";" .. BUTTON_SIZE .. "," .. BUTTON_SIZE .. ";" ..
name .. ";" .. name .. "_inv;]"
end
if data.item and reg_items[data.item] then
if not data.recipes_item or (data.fuel and not get_recipe(data.item).items) then
local X = floor(xoffset) - (sfinv_only and 0 or 0.2)
formspec = formspec ..
"item_image_button[" .. X .. "," ..
(iY + (sfinv_only and 2.7 or 2.2)) ..
";" .. BUTTON_SIZE .. "," .. BUTTON_SIZE ..
";" .. data.item .. ";" .. data.item .. ";]" ..
fs[#fs + 1] = "item_image_button[" .. X .. "," ..
(iY + (sfinv_only and 2.7 or 2.2)) ..
";" .. BUTTON_SIZE .. "," .. BUTTON_SIZE ..
";" .. data.item .. ";" .. data.item .. ";]"
"image[" .. (X + 1.1) .. "," ..
(iY + (sfinv_only and 2.85 or 2.35)) ..
";0.9,0.7;craftguide_arrow.png]" ..
fs[#fs + 1] = "image[" .. (X + 1.1) .. "," ..
(iY + (sfinv_only and 2.85 or 2.35)) ..
";0.9,0.7;craftguide_arrow.png]"
self:get_tooltip(data.item) ..
fs[#fs + 1] = get_tooltip(data.item)
fs[#fs + 1] = "image[" .. (X + 2.1) .. "," ..
(iY + (sfinv_only and 2.68 or 2.18)) ..
";1.1,1.1;craftguide_fire.png]"
"image[" .. (X + 2.1) .. "," ..
(iY + (sfinv_only and 2.68 or 2.18)) ..
";1.1,1.1;craftguide_fire.png]"
else
local usage = data.show_usage
fs[#fs + 1] = _get_recipe(data.iX,
iY,
xoffset,
data.rnum,
(usage and data.usages or data.recipes_item),
usage)
local show_usage = data.show_usage
formspec = formspec ..
self:get_recipe(data.iX,
iY,
xoffset,
data.rnum,
(show_usage and data.usages or data.recipes_item),
show_usage)
end
end
fs = concat(fs)
data.formspec = fs
data.formspec = formspec
if sfinv_only then
return fs
return formspec
else
show_formspec(player_name, "craftguide", fs)
show_formspec(player_name, "craftguide", formspec)
end
end
local show_fs = function(player, player_name)
if sfinv_only then
local context = sfinv.get_or_create_context(player)
sfinv.set_player_inventory_formspec(player, context)
else
get_formspec(player_name)
end
end
local function recipe_in_inv(inv, item_name, recipes_f)
function craftguide:recipe_in_inv(inv, item_name, recipes_f)
local recipes = recipes_f or get_recipes(item_name) or {}
local show_item_recipes = {}
@ -411,10 +439,53 @@ local function recipe_in_inv(inv, item_name, recipes_f)
end
end
return recipes, in_table(show_item_recipes)
return recipes, player_has_item(show_item_recipes)
end
local function get_filter_items(data, player)
function craftguide:get_init_items()
local items_list, c = {}, 0
local function list(name)
c = c + 1
items_list[c] = name
end
for name, def in pairs(reg_items) do
local is_fuel = get_fueltime(name) > 0
if (not (def.groups.not_in_craft_guide == 1 or
def.groups.not_in_creative_inventory == 1)) and
(get_recipe(name).items or is_fuel) and
def.description and def.description ~= "" then
list(name)
end
end
for i = 1, #self.custom_crafts do
local craft = self.custom_crafts[i]
local output = craft.output:match("%S*")
local listed
for j = 1, #items_list do
local listed_item = items_list[j]
if output == listed_item then
listed = true
break
end
end
if not listed then
list(output)
end
end
sort(items_list)
datas.init_items = items_list
end
mt.register_on_mods_loaded(function()
craftguide:get_init_items()
end)
function craftguide:get_filter_items(data, player)
local filter = data.filter
if datas.searches[filter] then
data.items = datas.searches[filter]
@ -435,7 +506,7 @@ local function get_filter_items(data, player)
filtered_list[counter] = item
end
elseif progressive_mode then
local _, has_item = recipe_in_inv(inv, item)
local _, has_item = self:recipe_in_inv(inv, item)
if has_item then
counter = counter + 1
filtered_list[counter] = item
@ -459,73 +530,7 @@ local function get_filter_items(data, player)
data.items = filtered_list
end
local function init_datas(user, name)
datas[name] = {filter = "", pagenum = 1, iX = sfinv_only and 8 or DEFAULT_SIZE}
if progressive_mode then
get_filter_items(datas[name], user)
end
end
local function add_custom_recipes(item, recipes)
for j = 1, #craftguide.custom_crafts do
local craft = craftguide.custom_crafts[j]
if craft.output:match("%S*") == item then
recipes[#recipes + 1] = {
type = craft.type,
width = craft.width,
items = craft.items,
output = craft.output,
}
end
end
return recipes
end
local function get_init_items()
local items_list, c = {}, 0
local function list(name)
c = c + 1
items_list[c] = name
end
for name, def in pairs(reg_items) do
local is_fuel = get_fueltime(name) > 0
if (not (def.groups.not_in_craft_guide == 1 or
def.groups.not_in_creative_inventory == 1)) and
(get_recipe(name).items or is_fuel) and
def.description and def.description ~= "" then
list(name)
end
end
for i = 1, #craftguide.custom_crafts do
local craft = craftguide.custom_crafts[i]
local output = craft.output:match("%S*")
local listed
for j = 1, #items_list do
local listed_item = items_list[j]
if output == listed_item then
listed = true
break
end
end
if not listed then
list(output)
end
end
sort(items_list)
datas.init_items = items_list
end
mt.register_on_mods_loaded(function()
get_init_items()
end)
local function get_item_usages(item)
function craftguide:get_item_usages(item)
local usages = {}
for name, def in pairs(reg_items) do
if not (def.groups.not_in_craft_guide == 1 or
@ -586,7 +591,7 @@ local function get_fields(player, ...)
fields.filter ~= "" then
data.filter = fields.filter:lower()
data.pagenum = 1
get_filter_items(data, player)
craftguide:get_filter_items(data, player)
show_fs(player, player_name)
elseif fields.prev or fields.next then
@ -620,7 +625,7 @@ local function get_fields(player, ...)
if not next(recipes) and not is_fuel then return end
if not data.show_usage and item == data.item and not progressive_mode then
data.usages = get_item_usages(item)
data.usages = craftguide:get_item_usages(item)
if next(data.usages) then
data.show_usage = true
data.rnum = 1
@ -630,9 +635,10 @@ local function get_fields(player, ...)
else
if progressive_mode then
local inv = player:get_inventory()
local has_item
recipes, has_item = recipe_in_inv(inv, item, recipes)
local _, has_item = craftguide:recipe_in_inv(inv, item)
if not has_item then return end
recipes = craftguide:recipe_in_inv(inv, item, recipes)
end
data.item = item
@ -656,7 +662,7 @@ if sfinv_only then
return sfinv.make_formspec(
player,
context,
get_formspec(player_name)
craftguide:get_formspec(player_name)
)
end,
on_enter = function(self, player, context)
@ -674,13 +680,13 @@ if sfinv_only then
else
mt.register_on_player_receive_fields(get_fields)
local function on_use(itemstack, user)
function craftguide:on_use(itemstack, user)
local player_name = user:get_player_name()
local data = datas[player_name]
if progressive_mode or not data then
init_datas(user, player_name)
get_formspec(player_name)
self:get_formspec(player_name)
else
show_formspec(player_name, "craftguide", data.formspec)
end
@ -693,7 +699,7 @@ else
stack_max = 1,
groups = {book = 1},
on_use = function(itemstack, user)
on_use(itemstack, user)
craftguide:on_use(itemstack, user)
end
})
@ -718,7 +724,7 @@ else
meta:set_string("infotext", S("Crafting Guide Sign"))
end,
on_rightclick = function(pos, node, user, itemstack)
on_use(itemstack, user)
craftguide:on_use(itemstack, user)
end
})
@ -745,17 +751,17 @@ else
recipe = "craftguide:sign",
burntime = 10
})
end
if rawget(_G, "sfinv_buttons") then
sfinv_buttons.register_button("craftguide", {
title = S("Crafting Guide"),
tooltip = S("Shows a list of available crafting recipes, cooking recipes and fuels"),
action = function(player)
on_use(nil, player)
end,
image = "craftguide_book.png",
})
end
if rawget(_G, "sfinv_buttons") then
sfinv_buttons.register_button("craftguide", {
title = S("Crafting Guide"),
tooltip = S("Shows a list of available crafting recipes, cooking recipes and fuels"),
action = function(player)
craftguide:on_use(nil, player)
end,
image = "craftguide_book.png",
})
end
if not progressive_mode then
@ -830,6 +836,4 @@ for x = 1, 6 do
recipe = cr[x]
})
end
]]
minetest.log("action", "[craftguide] loaded.")
]]