344 lines
11 KiB
Lua
344 lines
11 KiB
Lua
local F = minetest.formspec_escape
|
||
local S = minetest.get_translator("lzr_getitem")
|
||
|
||
lzr_getitem = {}
|
||
|
||
local detached_inventories = {}
|
||
|
||
-- Per-player lists (indexed by player name)
|
||
local current_pages = {} -- current page number
|
||
local current_max_pages = {} -- current max. page number
|
||
local current_searches = {} -- current search string
|
||
local current_item_lists = {} -- currently active list of items
|
||
|
||
local SLOTS_W = 10
|
||
local SLOTS_H = 5
|
||
local SLOTS = SLOTS_W * SLOTS_H
|
||
|
||
local all_items_list_editor -- cached list of all items (editor)
|
||
local all_items_list_dev -- cached list of all items (dev mode)
|
||
|
||
local function allow_check(player, msg_disallowed)
|
||
if player and player:is_player() then
|
||
local name = player:get_player_name()
|
||
local state = lzr_gamestate.get_state()
|
||
if state == lzr_gamestate.EDITOR or state == lzr_gamestate.DEV or minetest.check_player_privs(name, "give") then
|
||
return -1
|
||
else
|
||
minetest.chat_send_player(name, msg_disallowed)
|
||
minetest.close_formspec(name, "lzr_getitem:getitem")
|
||
return 0
|
||
end
|
||
else
|
||
return -1
|
||
end
|
||
end
|
||
|
||
-- Create detached inventories
|
||
local function add_detached_inventories(player)
|
||
local name = player:get_player_name()
|
||
local inv_items = minetest.create_detached_inventory("lzr_getitem_items_"..name, {
|
||
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||
return 0
|
||
end,
|
||
allow_put = function(inv, listname, index, stack, player)
|
||
return 0
|
||
end,
|
||
allow_take = function(inv, listname, index, stack, player)
|
||
return allow_check(player, S("You can only take items in the level editor, in development mode, or when you have the ‘give’ privilege."))
|
||
end,
|
||
}, name)
|
||
local inv_trash = minetest.create_detached_inventory("lzr_getitem_trash_"..name, {
|
||
allow_take = function(inv, listname, index, stack, player)
|
||
return 0
|
||
end,
|
||
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||
return 0
|
||
end,
|
||
allow_put = function(inv, listname, index, stack, player)
|
||
local ret = allow_check(player, S("You can only trash items in the level editor, in development mode, or when you have the ‘give’ privilege."))
|
||
if ret == -1 then
|
||
return stack:get_count()
|
||
else
|
||
return 0
|
||
end
|
||
end,
|
||
on_put = function(inv, listname, index, stack, player)
|
||
inv:set_list(listname, {})
|
||
end,
|
||
}, name)
|
||
inv_trash:set_size("main", 1)
|
||
detached_inventories[name] = { items = inv_items, trash = inv_trash }
|
||
end
|
||
|
||
local sort_items_by_type = function(item1, item2)
|
||
--[[ Sort items in this order:
|
||
* Tools
|
||
* Craftitems
|
||
* Nodes
|
||
* items from this mod (come last) ]]
|
||
local def1 = minetest.registered_items[item1]
|
||
local def2 = minetest.registered_items[item2]
|
||
local tool1 = def1.type == "tool"
|
||
local tool2 = def2.type == "tool"
|
||
local craftitem1 = def1.type == "craft"
|
||
local craftitem2 = def2.type == "craft"
|
||
local node1 = def1.type == "node"
|
||
local node2 = def2.type == "node"
|
||
if tool1 and not tool2 then
|
||
return true
|
||
elseif not tool1 and tool2 then
|
||
return false
|
||
elseif craftitem1 and not craftitem2 then
|
||
return true
|
||
elseif not craftitem1 and craftitem2 then
|
||
return false
|
||
elseif node1 and not node2 then
|
||
return true
|
||
elseif not node1 and node2 then
|
||
return false
|
||
else
|
||
return item1 < item2
|
||
end
|
||
end
|
||
|
||
local collect_items = function(devmode, filter, lang_code)
|
||
local items = {}
|
||
if filter then
|
||
filter = string.trim(filter)
|
||
filter = string.lower(filter) -- to make sure the search is case-insensitive
|
||
end
|
||
for itemstring, def in pairs(minetest.registered_items) do
|
||
local advanced_items
|
||
if devmode then
|
||
advanced_items = true
|
||
else
|
||
advanced_items = minetest.get_item_group(itemstring, "not_in_creative_inventory") == 0
|
||
end
|
||
if itemstring ~= "" and itemstring ~= "unknown" and itemstring ~= "ignore" and advanced_items then
|
||
if filter and lang_code then
|
||
local desc = ItemStack(itemstring):get_description()
|
||
local matches
|
||
-- First, try to match original description
|
||
if desc ~= "" then
|
||
local ldesc = string.lower(desc)
|
||
matches = string.find(ldesc, filter, 1, true) ~= nil
|
||
-- Second, try to match translated description
|
||
if not matches then
|
||
local tdesc = minetest.get_translated_string(lang_code, desc)
|
||
if tdesc ~= "" then
|
||
tdesc = string.lower(tdesc)
|
||
matches = string.find(tdesc, filter, 1, true) ~= nil
|
||
end
|
||
end
|
||
-- Third, try to match translated short description
|
||
if not matches then
|
||
local sdesc = ItemStack(itemstring):get_short_description()
|
||
if sdesc ~= "" then
|
||
sdesc = minetest.get_translated_string(lang_code, sdesc)
|
||
sdesc = string.lower(sdesc)
|
||
matches = string.find(sdesc, filter, 1, true) ~= nil
|
||
end
|
||
end
|
||
|
||
end
|
||
-- Fourth, try to match itemstring
|
||
if not matches then
|
||
matches = string.find(itemstring, filter, 1, true) ~= nil
|
||
end
|
||
|
||
-- If item was matched, add to item list
|
||
if matches then
|
||
table.insert(items, itemstring)
|
||
end
|
||
else
|
||
table.insert(items, itemstring)
|
||
end
|
||
end
|
||
end
|
||
table.sort(items, sort_items_by_type)
|
||
|
||
return items
|
||
end
|
||
|
||
local function update_inventory(devmode, name)
|
||
local search = current_searches[name] or ""
|
||
local items
|
||
if search == "" then
|
||
if devmode then
|
||
items = all_items_list_dev
|
||
else
|
||
items = all_items_list_editor
|
||
end
|
||
else
|
||
local lang_code = minetest.get_player_information(name).lang_code
|
||
items = collect_items(devmode, search, lang_code)
|
||
end
|
||
local max_page = math.ceil(#items / SLOTS)
|
||
current_max_pages[name] = max_page
|
||
|
||
local inv = detached_inventories[name].items
|
||
inv:set_size("main", #items)
|
||
inv:set_list("main", items)
|
||
if not current_pages[name] then
|
||
current_pages[name] = 1
|
||
end
|
||
if current_pages[name] > max_page then
|
||
current_pages[name] = max_page
|
||
end
|
||
if current_pages[name] < 1 then
|
||
current_pages[name] = 1
|
||
end
|
||
end
|
||
|
||
local function get_formspec(page, name)
|
||
local start = 0 + (page-1)*SLOTS
|
||
if not name then
|
||
return ""
|
||
end
|
||
local player = minetest.get_player_by_name(name)
|
||
local playerinvsize = player:get_inventory():get_size("main")
|
||
local hotbarsize = player:hud_get_hotbar_itemcount()
|
||
local pinv_w, pinv_h, pinv_x
|
||
pinv_w = hotbarsize
|
||
pinv_h = math.ceil(playerinvsize / pinv_w)
|
||
pinv_w = math.min(pinv_w, 10)
|
||
pinv_h = math.min(pinv_w, 4)
|
||
pinv_x = 0
|
||
if pinv_w < 9 then
|
||
pinv_x = 1
|
||
elseif pinv_w == 9 then
|
||
pinv_x = 0.5
|
||
end
|
||
|
||
local pagestr = ""
|
||
local max_page = current_max_pages[name]
|
||
if max_page > 1 then
|
||
--~ Previous page button in item selection
|
||
pagestr = "button[0,5.45;1,1;lzr_getitem_prev;"..F(S("<")).."]"..
|
||
--~ Next page button in item selection
|
||
"button[1,5.45;1,1;lzr_getitem_next;"..F(S(">")).."]"..
|
||
--~ Page counter in item selection. @1 = current page, @2 = total pages
|
||
"label[0,5.1;"..F(S("Page: @1/@2", page, max_page)).."]"
|
||
end
|
||
|
||
local search_text = current_searches[name] or ""
|
||
|
||
local inventory_list
|
||
if current_max_pages[name] > 0 then
|
||
inventory_list = "list[detached:lzr_getitem_items_"..name..";main;0,0;"..SLOTS_W..","..SLOTS_H..";"..start.."]"
|
||
else
|
||
inventory_list = "label[2.5,2.5;"..F(S("No items found.")).."]"
|
||
if search_text ~= "" then
|
||
inventory_list = inventory_list .. "button[2.5,3.25;3,0.8;search_button_reset_big;"..F(S("Reset search")).."]"
|
||
end
|
||
end
|
||
|
||
return "size[10,10.5]"..
|
||
inventory_list ..
|
||
"list[current_player;main;"..pinv_x..",6.75;"..pinv_w..","..pinv_h..";]" ..
|
||
"label[9,5.1;"..F(S("Trash:")).."]" ..
|
||
"list[detached:lzr_getitem_trash_"..name..";main;9,5.5;1,1]" ..
|
||
"field[2.2,5.75;4,1;search;;"..F(search_text).."]" ..
|
||
"field_close_on_enter[search;false]" ..
|
||
"button[6,5.45;1.6,1;search_button_start;"..F(S("Search")).."]" ..
|
||
--~ Reset search button label in item selection
|
||
"button[7.6,5.45;0.8,1;search_button_reset;"..F(S("X")).."]" ..
|
||
"tooltip[search_button_reset;"..F(S("Reset search")).."]" ..
|
||
pagestr ..
|
||
"listring[detached:lzr_getitem_items_"..name..";main]"..
|
||
"listring[current_player;main]"..
|
||
"listring[detached:lzr_getitem_trash_"..name..";main]"
|
||
end
|
||
|
||
lzr_getitem.show_formspec = function(name)
|
||
local state = lzr_gamestate.get_state()
|
||
if state ~= lzr_gamestate.EDITOR and state ~= lzr_gamestate.DEV and not minetest.check_player_privs(name, "give") and not lzr_game then
|
||
minetest.chat_send_player(name, S("This is only available in the level editor, or when you have the 'give' privilege."))
|
||
minetest.close_formspec(name, "lzr_getitem:getitem")
|
||
return false
|
||
end
|
||
local page = current_pages[name]
|
||
local form = get_formspec(page, name)
|
||
minetest.show_formspec(name, "lzr_getitem:getitem", form)
|
||
return true
|
||
end
|
||
|
||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||
if formname == "lzr_getitem:getitem" then
|
||
local name = player:get_player_name()
|
||
local state = lzr_gamestate.get_state()
|
||
if state ~= lzr_gamestate.EDITOR and state ~= lzr_gamestate.DEV and not minetest.check_player_privs(name, "give") then
|
||
minetest.chat_send_player(name, S("This is only available in the level editor, or when you have the 'give' privilege."))
|
||
minetest.close_formspec(name, "lzr_getitem:getitem")
|
||
return
|
||
end
|
||
local page = current_pages[name]
|
||
local old_page = page
|
||
-- Next page or previous page
|
||
if fields.lzr_getitem_next or fields.lzr_getitem_prev then
|
||
if fields.lzr_getitem_next then
|
||
page = page + 1
|
||
elseif fields.lzr_getitem_prev then
|
||
page = page - 1
|
||
end
|
||
-- Handle page change
|
||
if page < 1 then
|
||
page = 1
|
||
end
|
||
local max_page = current_max_pages[name]
|
||
if page > max_page then
|
||
page = max_page
|
||
end
|
||
if page ~= old_page then
|
||
current_pages[name] = page
|
||
lzr_getitem.show_formspec(name)
|
||
end
|
||
return
|
||
-- Search
|
||
elseif (fields.search_button_start or (fields.key_enter and fields.key_enter_field == "search")) and fields.search then
|
||
current_searches[name] = fields.search
|
||
update_inventory(lzr_gamestate.get_state() == lzr_gamestate.DEV, name)
|
||
lzr_getitem.show_formspec(name, fields.search)
|
||
return
|
||
-- Reset search
|
||
elseif (fields.search_button_reset or fields.search_button_reset_big) then
|
||
current_searches[name] = ""
|
||
update_inventory(lzr_gamestate.get_state() == lzr_gamestate.DEV, name)
|
||
lzr_getitem.show_formspec(name)
|
||
return
|
||
end
|
||
end
|
||
end)
|
||
|
||
minetest.register_on_mods_loaded(function()
|
||
all_items_list_dev = collect_items(true)
|
||
all_items_list_editor = collect_items(false)
|
||
end)
|
||
|
||
minetest.register_on_joinplayer(function(player)
|
||
local name = player:get_player_name()
|
||
current_searches[name] = ""
|
||
current_pages[name] = 1
|
||
current_max_pages[name] = 0
|
||
add_detached_inventories(player)
|
||
update_inventory(false, name)
|
||
end)
|
||
|
||
lzr_gamestate.register_on_enter_state(function(new_state)
|
||
if new_state == lzr_gamestate.EDITOR or new_state == lzr_gamestate.DEV then
|
||
local name = "singleplayer"
|
||
if minetest.get_player_by_name(name) then
|
||
update_inventory(new_state == lzr_gamestate.DEV, name)
|
||
end
|
||
end
|
||
end)
|
||
|
||
minetest.register_on_leaveplayer(function(player)
|
||
local name = player:get_player_name()
|
||
current_pages[name] = nil
|
||
current_max_pages[name] = nil
|
||
current_searches[name] = nil
|
||
current_item_lists[name] = nil
|
||
end)
|