mirror of
https://github.com/minetest-mods/craftguide.git
synced 2024-11-26 12:33:43 +01:00
Add an API for custom formspec elements
This commit is contained in:
parent
92cf2307db
commit
fa30a0d076
@ -7,4 +7,5 @@ read_globals = {
|
|||||||
"sfinv",
|
"sfinv",
|
||||||
"sfinv_buttons",
|
"sfinv_buttons",
|
||||||
"vector",
|
"vector",
|
||||||
|
"string",
|
||||||
}
|
}
|
||||||
|
41
API.md
41
API.md
@ -61,6 +61,47 @@ Removes all recipe filters and adds a new one.
|
|||||||
|
|
||||||
Returns a map of recipe filters, indexed by name.
|
Returns a map of recipe filters, indexed by name.
|
||||||
|
|
||||||
|
### Custom formspec elements
|
||||||
|
|
||||||
|
#### `craftguide.add_formspec_element(name, def)`
|
||||||
|
|
||||||
|
Adds a formspec element to the current formspec.
|
||||||
|
Supported types: `box`, `label`, `image`, `button`, `tooltip`, `item_image`, `image_button`, `item_image_button`
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
craftguide.add_formspec_element("export", {
|
||||||
|
type = "button",
|
||||||
|
element = function(data)
|
||||||
|
-- Should return a table of parameters according to the formspec element type.
|
||||||
|
-- Note: for all buttons, the 'name' parameter *must not* be specified!
|
||||||
|
if data.recipes then
|
||||||
|
return {
|
||||||
|
data.iX - 3.7, -- X
|
||||||
|
sfinv_only and 7.9 or 8, -- Y
|
||||||
|
1.6, -- W
|
||||||
|
1, -- H
|
||||||
|
ESC(S("Export")) -- label
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
-- Optional.
|
||||||
|
action = function(player, data)
|
||||||
|
-- When the button is pressed.
|
||||||
|
print("Exported!")
|
||||||
|
end
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `craftguide.remove_formspec_element(name)`
|
||||||
|
|
||||||
|
Removes the formspec element with the given name.
|
||||||
|
|
||||||
|
#### `craftguide.get_formspec_elements()`
|
||||||
|
|
||||||
|
Returns a map of formspec elements, indexed by name.
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
|
|
||||||
#### `craftguide.show(player_name, item, show_usages)`
|
#### `craftguide.show(player_name, item, show_usages)`
|
||||||
|
112
init.lua
112
init.lua
@ -26,13 +26,12 @@ local serialize, deserialize = M.serialize, M.deserialize
|
|||||||
local ESC = M.formspec_escape
|
local ESC = M.formspec_escape
|
||||||
local S = M.get_translator("craftguide")
|
local S = M.get_translator("craftguide")
|
||||||
|
|
||||||
-- Lua 5.3 removed `table.maxn`, use this alternative in case of breakage:
|
local maxn, sort, concat, insert = table.maxn, table.sort, table.concat, table.insert
|
||||||
-- https://github.com/kilbith/xdecor/blob/master/handlers/helpers.lua#L1
|
|
||||||
local maxn, sort, concat = table.maxn, table.sort, table.concat
|
|
||||||
local vector_add, vector_mul = vector.add, vector.multiply
|
|
||||||
local min, max, floor, ceil = math.min, math.max, math.floor, math.ceil
|
local min, max, floor, ceil = math.min, math.max, math.floor, math.ceil
|
||||||
local fmt, find, sub, gmatch = string.format, string.find, string.sub, string.gmatch
|
local fmt, find, match, sub, split =
|
||||||
local pairs, next = pairs, next
|
string.format, string.find, string.match, string.sub, string.split
|
||||||
|
local pairs, next, unpack = pairs, next, unpack
|
||||||
|
local vec_add, vec_mul = vector.add, vector.multiply
|
||||||
|
|
||||||
local DEFAULT_SIZE = 10
|
local DEFAULT_SIZE = 10
|
||||||
local MIN_LIMIT, MAX_LIMIT = 10, 12
|
local MIN_LIMIT, MAX_LIMIT = 10, 12
|
||||||
@ -41,9 +40,16 @@ DEFAULT_SIZE = min(MAX_LIMIT, max(MIN_LIMIT, DEFAULT_SIZE))
|
|||||||
local GRID_LIMIT = 5
|
local GRID_LIMIT = 5
|
||||||
local POLL_FREQ = 0.25
|
local POLL_FREQ = 0.25
|
||||||
|
|
||||||
local FMT_label = "label[%f,%f;%s]"
|
local FMT = {
|
||||||
local FMT_image = "image[%f,%f;%f,%f;%s]"
|
box = "box[%f,%f;%f,%f;%s]",
|
||||||
local FMT_tooltip = "tooltip[%f,%f;%f,%f;%s]"
|
label = "label[%f,%f;%s]",
|
||||||
|
image = "image[%f,%f;%f,%f;%s]",
|
||||||
|
button = "button[%f,%f;%f,%f;%s;%s]",
|
||||||
|
tooltip = "tooltip[%s;%s]",
|
||||||
|
item_image = "item_image[%f,%f;%f,%f;%s]",
|
||||||
|
image_button = "image_button[%f,%f;%f,%f;%s;%s;%s]",
|
||||||
|
item_image_button = "item_image_button[%f,%f;%f,%f;%s;%s;%s]",
|
||||||
|
}
|
||||||
|
|
||||||
local group_stereotypes = {
|
local group_stereotypes = {
|
||||||
wool = "wool:white",
|
wool = "wool:white",
|
||||||
@ -150,6 +156,29 @@ local function apply_recipe_filters(recipes, player)
|
|||||||
return recipes
|
return recipes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local formspec_elements = {}
|
||||||
|
|
||||||
|
function craftguide.add_formspec_element(name, def)
|
||||||
|
local func = "craftguide." .. __func() .. "(): "
|
||||||
|
assert(def.element, func .. "'element' field not defined")
|
||||||
|
assert(def.type, func .. "'type' field not defined")
|
||||||
|
assert(FMT[def.type], func .. "'" .. def.type .. "' type not supported by the API")
|
||||||
|
|
||||||
|
formspec_elements[name] = {
|
||||||
|
type = def.type,
|
||||||
|
element = def.element,
|
||||||
|
action = def.action,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function craftguide.remove_formspec_element(name)
|
||||||
|
formspec_elements[name] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function craftguide.get_formspec_elements()
|
||||||
|
return formspec_elements
|
||||||
|
end
|
||||||
|
|
||||||
local function item_has_groups(item_groups, groups)
|
local function item_has_groups(item_groups, groups)
|
||||||
for i = 1, #groups do
|
for i = 1, #groups do
|
||||||
local group = groups[i]
|
local group = groups[i]
|
||||||
@ -161,18 +190,8 @@ local function item_has_groups(item_groups, groups)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local function split(str)
|
|
||||||
local t, c = {}, 0
|
|
||||||
for s in gmatch(str, "([^,]+)") do
|
|
||||||
c = c + 1
|
|
||||||
t[c] = s
|
|
||||||
end
|
|
||||||
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
|
|
||||||
local function extract_groups(str)
|
local function extract_groups(str)
|
||||||
return split(sub(str, 7))
|
return split(sub(str, 7), ",")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function item_in_recipe(item, recipe)
|
local function item_in_recipe(item, recipe)
|
||||||
@ -259,7 +278,7 @@ local function cache_recipes(output)
|
|||||||
|
|
||||||
for i = 1, #craftguide.custom_crafts do
|
for i = 1, #craftguide.custom_crafts do
|
||||||
local custom_craft = craftguide.custom_crafts[i]
|
local custom_craft = craftguide.custom_crafts[i]
|
||||||
if custom_craft.output:match("%S*") == output then
|
if match(custom_craft.output, "%S*") == output then
|
||||||
c = c + 1
|
c = c + 1
|
||||||
recipes[c] = custom_craft
|
recipes[c] = custom_craft
|
||||||
end
|
end
|
||||||
@ -356,7 +375,7 @@ local function get_tooltip(item, groups, cooktime, burntime)
|
|||||||
S("Burning time: @1", colorize("yellow", burntime))
|
S("Burning time: @1", colorize("yellow", burntime))
|
||||||
end
|
end
|
||||||
|
|
||||||
return fmt("tooltip[%s;%s]", item, ESC(tooltip))
|
return fmt(FMT.tooltip, item, ESC(tooltip))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_recipe_fs(data, iY)
|
local function get_recipe_fs(data, iY)
|
||||||
@ -377,7 +396,7 @@ local function get_recipe_fs(data, iY)
|
|||||||
local rightest, btn_size, s_btn_size = 0, 1.1
|
local rightest, btn_size, s_btn_size = 0, 1.1
|
||||||
|
|
||||||
if width > GRID_LIMIT or rows > GRID_LIMIT then
|
if width > GRID_LIMIT or rows > GRID_LIMIT then
|
||||||
fs[#fs + 1] = fmt(FMT_label,
|
fs[#fs + 1] = fmt(FMT.label,
|
||||||
(data.iX / 2) - 2,
|
(data.iX / 2) - 2,
|
||||||
iY + 2.2,
|
iY + 2.2,
|
||||||
ESC(S("Recipe is too big to be displayed (@1x@2)", width, rows)))
|
ESC(S("Recipe is too big to be displayed (@1x@2)", width, rows)))
|
||||||
@ -409,13 +428,13 @@ local function get_recipe_fs(data, iY)
|
|||||||
|
|
||||||
local label = groups and "\nG" or ""
|
local label = groups and "\nG" or ""
|
||||||
|
|
||||||
fs[#fs + 1] = fmt("item_image_button[%f,%f;%f,%f;%s;%s;%s]",
|
fs[#fs + 1] = fmt(FMT.item_image_button,
|
||||||
X,
|
X,
|
||||||
Y + (sfinv_only and 0.7 or 0.2),
|
Y + (sfinv_only and 0.7 or 0.2),
|
||||||
btn_size,
|
btn_size,
|
||||||
btn_size,
|
btn_size,
|
||||||
item,
|
item,
|
||||||
item:match("%S*"),
|
match(item, "%S*"),
|
||||||
ESC(label))
|
ESC(label))
|
||||||
|
|
||||||
local burntime = fuel_cache[item]
|
local burntime = fuel_cache[item]
|
||||||
@ -435,7 +454,7 @@ local function get_recipe_fs(data, iY)
|
|||||||
icon = fmt("craftguide_%s.png^[resize:16x16", icon)
|
icon = fmt("craftguide_%s.png^[resize:16x16", icon)
|
||||||
end
|
end
|
||||||
|
|
||||||
fs[#fs + 1] = fmt(FMT_image,
|
fs[#fs + 1] = fmt(FMT.image,
|
||||||
rightest + 1.2,
|
rightest + 1.2,
|
||||||
iY + (sfinv_only and 2.2 or 1.7),
|
iY + (sfinv_only and 2.2 or 1.7),
|
||||||
0.5,
|
0.5,
|
||||||
@ -445,7 +464,7 @@ local function get_recipe_fs(data, iY)
|
|||||||
local tooltip = custom_recipe and custom_recipe.description or
|
local tooltip = custom_recipe and custom_recipe.description or
|
||||||
shapeless and S("Shapeless") or S("Cooking")
|
shapeless and S("Shapeless") or S("Cooking")
|
||||||
|
|
||||||
fs[#fs + 1] = fmt(FMT_tooltip,
|
fs[#fs + 1] = fmt("tooltip[%f,%f;%f,%f;%s]",
|
||||||
rightest + 1.2,
|
rightest + 1.2,
|
||||||
iY + (sfinv_only and 2.2 or 1.7),
|
iY + (sfinv_only and 2.2 or 1.7),
|
||||||
0.5,
|
0.5,
|
||||||
@ -456,7 +475,7 @@ local function get_recipe_fs(data, iY)
|
|||||||
local arrow_X = rightest + (s_btn_size or 1.1)
|
local arrow_X = rightest + (s_btn_size or 1.1)
|
||||||
local output_X = arrow_X + 0.9
|
local output_X = arrow_X + 0.9
|
||||||
|
|
||||||
fs[#fs + 1] = fmt(FMT_image,
|
fs[#fs + 1] = fmt(FMT.image,
|
||||||
arrow_X,
|
arrow_X,
|
||||||
iY + (sfinv_only and 2.85 or 2.35),
|
iY + (sfinv_only and 2.85 or 2.35),
|
||||||
0.9,
|
0.9,
|
||||||
@ -464,35 +483,36 @@ local function get_recipe_fs(data, iY)
|
|||||||
"craftguide_arrow.png")
|
"craftguide_arrow.png")
|
||||||
|
|
||||||
if recipe.type == "fuel" then
|
if recipe.type == "fuel" then
|
||||||
fs[#fs + 1] = fmt(FMT_image,
|
fs[#fs + 1] = fmt(FMT.image,
|
||||||
output_X,
|
output_X,
|
||||||
iY + (sfinv_only and 2.68 or 2.18),
|
iY + (sfinv_only and 2.68 or 2.18),
|
||||||
1.1,
|
1.1,
|
||||||
1.1,
|
1.1,
|
||||||
"craftguide_fire.png")
|
"craftguide_fire.png")
|
||||||
else
|
else
|
||||||
local output_name = recipe.output:match("%S+")
|
local output_name = match(recipe.output, "%S+")
|
||||||
local burntime = fuel_cache[output_name]
|
local burntime = fuel_cache[output_name]
|
||||||
|
|
||||||
fs[#fs + 1] = fmt("item_image_button[%f,%f;%f,%f;%s;%s;]",
|
fs[#fs + 1] = fmt(FMT.item_image_button,
|
||||||
output_X,
|
output_X,
|
||||||
iY + (sfinv_only and 2.7 or 2.2),
|
iY + (sfinv_only and 2.7 or 2.2),
|
||||||
1.1,
|
1.1,
|
||||||
1.1,
|
1.1,
|
||||||
recipe.output,
|
recipe.output,
|
||||||
ESC(output_name))
|
ESC(output_name),
|
||||||
|
"")
|
||||||
|
|
||||||
if burntime then
|
if burntime then
|
||||||
fs[#fs + 1] = get_tooltip(output_name, nil, nil, burntime)
|
fs[#fs + 1] = get_tooltip(output_name, nil, nil, burntime)
|
||||||
|
|
||||||
fs[#fs + 1] = fmt(FMT_image,
|
fs[#fs + 1] = fmt(FMT.image,
|
||||||
output_X + 1,
|
output_X + 1,
|
||||||
iY + (sfinv_only and 2.83 or 2.33),
|
iY + (sfinv_only and 2.83 or 2.33),
|
||||||
0.6,
|
0.6,
|
||||||
0.4,
|
0.4,
|
||||||
"craftguide_arrow.png")
|
"craftguide_arrow.png")
|
||||||
|
|
||||||
fs[#fs + 1] = fmt(FMT_image,
|
fs[#fs + 1] = fmt(FMT.image,
|
||||||
output_X + 1.6,
|
output_X + 1.6,
|
||||||
iY + (sfinv_only and 2.68 or 2.18),
|
iY + (sfinv_only and 2.68 or 2.18),
|
||||||
0.6,
|
0.6,
|
||||||
@ -582,7 +602,7 @@ local function make_formspec(name)
|
|||||||
pos = pos - 1
|
pos = pos - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
fs[#fs + 1] = fmt(FMT_label, pos, 2, ESC(no_item))
|
fs[#fs + 1] = fmt(FMT.label, pos, 2, ESC(no_item))
|
||||||
end
|
end
|
||||||
|
|
||||||
local first_item = (data.pagenum - 1) * ipp
|
local first_item = (data.pagenum - 1) * ipp
|
||||||
@ -608,6 +628,17 @@ local function make_formspec(name)
|
|||||||
fs[#fs + 1] = get_recipe_fs(data, iY)
|
fs[#fs + 1] = get_recipe_fs(data, iY)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for elem_name, def in pairs(formspec_elements) do
|
||||||
|
local element = def.element(data)
|
||||||
|
if element then
|
||||||
|
if find(def.type, "button") then
|
||||||
|
insert(element, #element, elem_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
fs[#fs + 1] = fmt(FMT[def.type], unpack(element))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return concat(fs)
|
return concat(fs)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -656,7 +687,8 @@ local function get_inv_items(player)
|
|||||||
local stacks = {}
|
local stacks = {}
|
||||||
|
|
||||||
for i = 1, #item_lists do
|
for i = 1, #item_lists do
|
||||||
stacks = table_merge(stacks, inv:get_list(item_lists[i]))
|
local l = inv:get_list(item_lists[i])
|
||||||
|
stacks = table_merge(stacks, l)
|
||||||
end
|
end
|
||||||
|
|
||||||
local inv_items, c = {}, 0
|
local inv_items, c = {}, 0
|
||||||
@ -723,6 +755,12 @@ local function on_receive_fields(player, fields)
|
|||||||
local name = player:get_player_name()
|
local name = player:get_player_name()
|
||||||
local data = player_data[name]
|
local data = player_data[name]
|
||||||
|
|
||||||
|
for elem_name, def in pairs(formspec_elements) do
|
||||||
|
if fields[elem_name] and def.action then
|
||||||
|
return def.action(player, data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if fields.clear then
|
if fields.clear then
|
||||||
reset_data(data)
|
reset_data(data)
|
||||||
show_fs(player, name)
|
show_fs(player, name)
|
||||||
@ -1026,7 +1064,7 @@ M.register_chatcommand("craft", {
|
|||||||
local node_name
|
local node_name
|
||||||
|
|
||||||
for i = 1, 10 do
|
for i = 1, 10 do
|
||||||
local look_at = vector_add(eye_h, vector_mul(dir, i))
|
local look_at = vec_add(eye_h, vec_mul(dir, i))
|
||||||
local node = M.get_node(look_at)
|
local node = M.get_node(look_at)
|
||||||
|
|
||||||
if node.name ~= "air" then
|
if node.name ~= "air" then
|
||||||
|
Loading…
Reference in New Issue
Block a user