[WIP] Add item picker

WARNING: Do not revert back to the previous commits after this one!
Doing so will give away items for free (as players will be able to take
out any items that they prevously selected with the item picker)

If you want to be able to revert then maybe it'd be better to make the
existing code store a copy of the item in its own inventory first (like
mese tubes in pipeworks).

The GUI currently uses flow because I'm lazy, if more performance is
needed then I can probably convert it to a formspec.
This commit is contained in:
luk3yx 2022-11-06 12:20:15 +13:00
parent c79c4adf39
commit c370b8051a
2 changed files with 220 additions and 9 deletions

View File

@ -1,4 +1,4 @@
name = exchange_shop name = exchange_shop
description = An improved exchange shop description = An improved exchange shop
depends = default depends = default, flow
optional_depends = mesecons_mvps optional_depends = mesecons_mvps

227
shop.lua
View File

@ -9,6 +9,7 @@ local S = exchange_shop.S
local shop_positions = {} local shop_positions = {}
local tconcat = table.concat local tconcat = table.concat
local lower = utf8.lower
local function get_exchange_shop_formspec(mode, pos, meta) local function get_exchange_shop_formspec(mode, pos, meta)
local name = "nodemeta:" .. pos.x .. "," .. pos.y .. "," .. pos.z local name = "nodemeta:" .. pos.x .. "," .. pos.y .. "," .. pos.z
@ -67,7 +68,20 @@ local function get_exchange_shop_formspec(mode, pos, meta)
main_image .. main_image ..
"list[current_player;main;0," .. inv_pos .. ";9,4;]" "list[current_player;main;0," .. inv_pos .. ";9,4;]"
return formspec return formspec
end
local function make_slots_btns(x, y, w, h, list, label)
local fs = {make_slots(x, y, w, h, list, label)}
local i = 0
for y2 = 1, h do
for x2 = 1, w do
i = i + 1
fs[#fs + 1] = ("image_button[%s,%s;1.2,1.2;;%s_%s;;false;false]"):format(x + x2 - 1.1, y + y2 - 1.1, list, i)
end
end
print(dump(fs))
return tconcat(fs)
end end
if mode == "owner_custm" or mode == "owner_stock" then if mode == "owner_custm" or mode == "owner_stock" then
@ -75,12 +89,12 @@ local function get_exchange_shop_formspec(mode, pos, meta)
-- owner -- owner
local formspec = ( local formspec = (
"size[10,10]" .. "formspec_version[3]size[10,10]real_coordinates[false]" ..
"item_image[0,-0.1;1,1;".. exchange_shop.shopname .. "]" .. "item_image[0,-0.1;1,1;".. exchange_shop.shopname .. "]" ..
"label[0.9,0.1;" .. S("Exchange Shop") .. "]" .. "label[0.9,0.1;" .. S("Exchange Shop") .. "]" ..
default.gui_close_btn("9.3,-0.1") .. default.gui_close_btn("9.3,-0.1") ..
make_slots(0.1, 2, 2, 2, "cust_ow", S("You need:")) .. make_slots_btns(0.1, 2, 2, 2, "cust_ow", S("You need:")) ..
make_slots(2.6, 2, 2, 2, "cust_og", S("You give:")) .. make_slots_btns(2.6, 2, 2, 2, "cust_og", S("You give:")) ..
"label[5,0.4;" .. S("Current stock:") .. "]" "label[5,0.4;" .. S("Current stock:") .. "]"
) )
@ -140,6 +154,178 @@ local function get_exchange_shop_formspec(mode, pos, meta)
end end
local function shop_valid(pos, player)
return minetest.get_node(pos).name == exchange_shop.shopname and
not minetest.is_protected(pos, player:get_player_name())
end
-- TODO: Maybe not use flow
local function go_back(player, ctx)
if shop_valid(ctx.pos, player) then
local name = player:get_player_name()
shop_positions[name] = ctx.pos
minetest.show_formspec(name, "exchange_shop:shop_formspec",
get_exchange_shop_formspec("owner_custm", ctx.pos))
end
end
local items_cache = {}
minetest.after(0, function()
for item, def in pairs(minetest.registered_items) do
if (not def.groups or (def.groups.not_in_creative_inventory ~= 1 and
def.groups.stairs ~= 1)) and def.description ~= "" then
items_cache[#items_cache + 1] = item
end
end
table.sort(items_cache)
end)
local function matches_search(query, description, lang)
return query == "" or
lower(minetest.get_translated_string(lang, description)):find(query, 1, true)
end
local gui = flow.widgets
local item_picker = flow.make_gui(function(player, ctx)
local rows = {name='items', w = 11.8, h = 5.8, custom_scrollbar = {
w = 0.9,
scrollbar_bg = "inventory_creative_scrollbar_bg.png",
slider = "inventory_creative_slider.png",
arrow_up = "inventory_creative_arrow_up.png",
arrow_down = "inventory_creative_arrow_down.png",
}}
local query = ctx.form.Dsearch and lower(ctx.form.Dsearch) or ""
-- Reset items scrollbar
if ctx.query ~= query then
ctx.form["_scrollbar-items"] = 0
ctx.query = query
end
local name = player:get_player_name()
local info = minetest.get_player_information(name)
local lang = info and info.lang_code or ""
local row = {}
for _, item in ipairs(items_cache) do
local description = minetest.registered_items[item].description
if matches_search(query, description, lang) then
if #row >= 10 then
rows[#rows + 1] = gui.HBox(row)
row = {}
end
row[#row + 1] = gui.ItemImageButton{
w = 1, h = 1,
item_name = item,
on_event = function(p, c)
if c.item ~= item or c.form.amount ~= "1" then
c.item = item
c.desc = description or item
c.form.amount = "1"
return true
end
end,
}
end
end
if #rows > 0 or #row > 0 then
rows[#rows + 1] = gui.HBox(row)
else
rows[#rows + 1] = gui.Label{label = S("No items found.")}
end
local item_preview
return gui.VBox{
gui.HBox{
gui.Style{selectors = {"back"}, props = {border = false}},
gui.ItemImageButton{
item_name = exchange_shop.shopname, w = 1, h = 1,
on_event = go_back, name = "back",
},
gui.Label{label = S("Select item"), align_h = "left", expand = true},
-- Search box
gui.HBox{
align_v = "centre",
bgimg = "inventory_search_bg9.png",
bgimg_middle = 25,
spacing = 0,
gui.Spacer{expand = false, padding = 0.06},
gui.Style{selectors = {"Dsearch"}, props = {border = false, bgcolor = "transparent"}},
gui.Field{name = "Dsearch", w = 3, h = 0.7},
gui.ImageButton{
w = 0.7, h = 0.7, drawborder = false, padding = 0.05,
texture_name = "inventory_search.png",
},
gui.ImageButton{
w = 0.7, h = 0.7, drawborder = false, padding = 0.05,
texture_name = "inventory_search_clear.png",
on_event = function(_, c)
if c.form.Dsearch ~= "" then
c.form.Dsearch = ""
return true
end
end
},
}
},
gui.StyleType{
selectors = {"item_image_button"},
props = {
bgimg = "formspec_cell.png",
bgimg_hovered = "formspec_cell.png^[brighten",
border = false,
}
},
gui.ScrollableVBox(rows),
gui.HBox{
gui.ItemImage{w = 1, h = 1, item_name = ctx.item},
gui.Label{
label = ctx.desc and ctx.item ~= "" and
S("Selected item: @1", ctx.desc) or
S("No item selected")
},
},
gui.HBox{
gui.Field{name = "amount", label = S("Amount"), default = "1"},
gui.HBox{
expand = true, align_h = "end", align_v = "end",
gui.Button{
label = S("Clear"),
w = 3.5,
on_event = function(p, c)
c.item = ""
c.form.amount = "0"
return true
end,
},
gui.Button{
label = S("Save"),
w = 3.5,
on_event = function(p, c)
if shop_valid(c.pos, p) then
local item = ItemStack(c.item)
local amount = tonumber(c.form.amount)
if amount and amount == amount and amount >= 1 then
item:set_count(math.min(amount, item:get_stack_max()))
end
shop_positions[name] = c.pos
local meta = minetest.get_meta(c.pos)
meta:get_inventory():set_stack(c.list, c.idx, item)
minetest.show_formspec(name, "exchange_shop:shop_formspec",
get_exchange_shop_formspec("owner_custm", c.pos, meta))
end
end,
},
},
},
}
end)
minetest.register_on_player_receive_fields(function(sender, formname, fields) minetest.register_on_player_receive_fields(function(sender, formname, fields)
if formname ~= "exchange_shop:shop_formspec" then if formname ~= "exchange_shop:shop_formspec" then
return return
@ -183,6 +369,23 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields)
end end
minetest.show_formspec(player_name, "exchange_shop:shop_formspec", minetest.show_formspec(player_name, "exchange_shop:shop_formspec",
get_exchange_shop_formspec(mode, pos, meta)) get_exchange_shop_formspec(mode, pos, meta))
else
for field in pairs(fields) do
local list, idx = field:match("(cust_o[wg])_([1-4])")
if list then
idx = tonumber(idx)
local stack = minetest.get_meta(pos):get_inventory():get_stack(list, idx)
item_picker:show(sender, {
pos = pos,
list = list,
idx = idx,
item = stack:get_name(),
desc = stack:get_short_description(),
form = {amount = stack:get_count()}
})
return
end
end
end end
end) end)
@ -243,7 +446,11 @@ minetest.register_node(exchange_shop.shopname, {
get_exchange_shop_formspec(mode, pos, meta)) get_exchange_shop_formspec(mode, pos, meta))
end, end,
allow_metadata_inventory_move = function(pos, _, _, _, _, count, player) allow_metadata_inventory_move = function(pos, from_list, to_list, _, _, count, player)
if from_list:sub(1, 6) == "cust_o" or to_list:sub(1, 6) == "cust_o" then
return 0
end
local player_name = player:get_player_name() local player_name = player:get_player_name()
return not minetest.is_protected(pos, player_name) and count or 0 return not minetest.is_protected(pos, player_name) and count or 0
end, end,
@ -257,15 +464,19 @@ minetest.register_node(exchange_shop.shopname, {
return 0 return 0
end end
if not minetest.is_protected(pos, player_name) if not minetest.is_protected(pos, player_name)
and listname ~= "custm_ej" then and listname ~= "custm_ej" and listname:sub(1, 6) ~= "cust_o" then
return stack:get_count() return stack:get_count()
end end
return 0 return 0
end, end,
allow_metadata_inventory_take = function(pos, _, _, stack, player) allow_metadata_inventory_take = function(pos, listname, _, stack, player)
local player_name = player:get_player_name() local player_name = player:get_player_name()
return not minetest.is_protected(pos, player_name) and stack:get_count() or 0 if minetest.is_protected(pos, player_name) or
listname:sub(1, 6) == "cust_o" then
return 0
end
return stack:get_count()
end end
}) })