2024-12-10 02:34:33 +01:00

344 lines
11 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)