180 lines
7.6 KiB
Lua
180 lines
7.6 KiB
Lua
-- level_menu mod for Minetest
|
|
-- Copyright © 2020 Alex Yst <mailto:copyright@y.st>
|
|
|
|
-- This program is free software; you can redistribute it and/or
|
|
-- modify it under the terms of the GNU Lesser General Public
|
|
-- License as published by the Free Software Foundation; either
|
|
-- version 2.1 of the License, or (at your option) any later version.
|
|
|
|
-- This software is distributed in the hope that it will be useful,
|
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
-- Lesser General Public License for more details.
|
|
|
|
-- You should have received a copy of the GNU Lesser General Public
|
|
-- License along with this program. If not, see
|
|
-- <https://www.gnu.org./licenses/>.
|
|
|
|
-- This isn't a real tool. It gets a random name to discourage people
|
|
-- from trying to get one. Next time the server restarts, any instances
|
|
-- of the tool in-world become unknown items with little hope of
|
|
-- becoming actual items again, even temporarily.
|
|
local progress_tool = "level_menu:"..math.random(0, 65535)
|
|
minetest.register_tool(progress_tool, {})
|
|
|
|
-- We need to track what page a player is on so we don't jump back to
|
|
-- page zero when they check on a specific drop in multi-page mode.
|
|
local player_page = {}
|
|
|
|
-- It'd be a little annoying if players had to choose their sorting
|
|
-- order repeatedly, so we save that setting.
|
|
local storage = minetest.get_mod_storage()
|
|
|
|
-- This function returns a sorted list of drops the player has
|
|
-- encountered.
|
|
--
|
|
-- The intuitive way to do this would be to set list to the output of
|
|
-- liblevelup.meta.drop_list(), then set any values to nil that
|
|
-- represent drops the player hasn't encountered. However, that would
|
|
-- put holes in the list of keys. For example, there could be an index
|
|
-- three and an index five, but no index four. This would make the "#"
|
|
-- operator fail to return the correct value because of Lua's quirks.
|
|
-- To avoid that issue, we can just put the drops into a
|
|
-- newly-generated list as we go.
|
|
local function get_drop_list(player_name)
|
|
local list = {}
|
|
for _, drop_name in next, liblevelup.meta.drops_list() do
|
|
if liblevelup.get.drop_stat(player_name, drop_name) > 0 then
|
|
list[#list+1] = drop_name
|
|
end
|
|
end
|
|
local sort_order = storage:get(player_name..";sort")
|
|
if sort_order == "level" then
|
|
table.sort(list, function(a, b)
|
|
local a_level = liblevelup.get.player_material_level(player_name, a)
|
|
local b_level = liblevelup.get.player_material_level(player_name, b)
|
|
if a_level == b_level then
|
|
return liblevelup.get.next_level_at(player_name, a)
|
|
/ liblevelup.get.player_material_progress_bar_length(player_name, a)
|
|
> liblevelup.get.next_level_at(player_name, b)
|
|
/ liblevelup.get.player_material_progress_bar_length(player_name, b)
|
|
else
|
|
return a_level < b_level
|
|
end
|
|
end)
|
|
end
|
|
return list
|
|
end
|
|
|
|
local function generate_formspec(player, context, check_specific_drop)
|
|
local player_name = player:get_player_name()
|
|
local drop_list = get_drop_list(player_name)
|
|
-- If a player hasn't found any drops yet, no data will be available
|
|
-- to build the page with.
|
|
if #drop_list == 0 then
|
|
return sfinv.make_formspec(player, context, "label[1,1;No data available.]label[1,1.5;Try again after mining or farming.]", false)
|
|
end
|
|
local formspec = ""
|
|
local min_key = 1
|
|
local max_key = #drop_list
|
|
-- If there are more than 32 drops, switch from single-page mode, which
|
|
-- supports up to 32 items on a single page, to multi-page mode, which
|
|
-- supports 24 items on each page.
|
|
if #drop_list > 32 then
|
|
min_key = player_page[player_name] * 24 + 1
|
|
max_key = math.min(min_key + 23, #drop_list)
|
|
if player_page[player_name] > 0 then
|
|
formspec = formspec.."button[0,3;2.5,1;page;Page "..(player_page[player_name] - 1).."]"
|
|
end
|
|
if player_page[player_name] < math.ceil(#drop_list / 24) - 1 then
|
|
formspec = formspec.."button[5.5,3;2.5,1;page;Page "..(player_page[player_name] + 1).."]"
|
|
end
|
|
formspec = formspec.."label[3,3.25;Page "..player_page[player_name].."]"
|
|
end
|
|
for i = 0, max_key - min_key do
|
|
local row = math.floor(i/8)
|
|
local col = i%8
|
|
local drop_name = drop_list[i+min_key]
|
|
local progress = 65535 * liblevelup.get.next_level_at(player_name, drop_name) / liblevelup.get.player_material_progress_bar_length(player_name, drop_name)
|
|
formspec = formspec.."item_image["..col..","..row..".07;1,1;"..progress_tool.." 1 "..progress.."]item_image_button["..col..","..row..";1,1;"..drop_name.." "..liblevelup.get.player_material_level(player_name, drop_name)..";levels_menu:"..drop_name..";]"
|
|
end
|
|
-- If there is more than one drop, show buttons to set the player's
|
|
-- sorting mode. Sorting mode persists between player sessions and even
|
|
-- between server resets.
|
|
if #drop_list > 1 then
|
|
formspec = formspec.."button[1.25,4;2.5,1;sort;By name]button[4.25,4;2.5,1;sort;By level]"
|
|
end
|
|
-- A specific drop is always displayed for now until some sort of
|
|
-- all-drops summary page is designed. Until then, the first item in
|
|
-- the list, sorted by the player's preferred method, is defaulted to.
|
|
if not check_specific_drop then
|
|
check_specific_drop = drop_list[1]
|
|
end
|
|
local item = ItemStack(check_specific_drop)
|
|
local progress_numerator = liblevelup.get.next_level_at(player_name, check_specific_drop)
|
|
local progress_denominator = liblevelup.get.player_material_progress_bar_length(player_name, check_specific_drop)
|
|
local progress = 65535 * progress_numerator / progress_denominator
|
|
formspec = formspec.."item_image[0.7,5.15;3,3;"..check_specific_drop.."]"
|
|
formspec = formspec.."label[1,8.1;Level "..liblevelup.get.player_material_level(player_name, check_specific_drop).."]"
|
|
formspec = formspec.."label[4,5.5;"..item:get_description().."]"
|
|
formspec = formspec.."label[4,6;Next level progress:]"
|
|
formspec = formspec.."label[4.6,6.5;"..(progress_denominator - progress_numerator).." / "..progress_denominator.."]"
|
|
formspec = formspec.."item_image[4,0.4;4.5,8;"..progress_tool.." 1 "..progress.."]"
|
|
formspec = formspec.."label[4.25,6.9;(Next level in "..progress_numerator..")]"
|
|
formspec = formspec.."label[4,7.8;Total harvested:]"
|
|
formspec = formspec.."label[4.6,8.3;"..liblevelup.get.drop_stat(player_name, check_specific_drop).."]"
|
|
return sfinv.make_formspec(player, context, formspec, false)
|
|
end
|
|
|
|
sfinv.register_page("level_menu:level", {
|
|
title = "level_menu:level",
|
|
get = function(self, player, context)
|
|
player_page[player:get_player_name()] = 0
|
|
return generate_formspec(player, context)
|
|
end,
|
|
on_player_receive_fields = function(self, player, context, fields)
|
|
if fields.page then
|
|
player_page[player:get_player_name()] = tonumber(fields.page:sub(6))
|
|
player:set_inventory_formspec(generate_formspec(
|
|
player,
|
|
context
|
|
))
|
|
return true
|
|
end
|
|
if fields.sort then
|
|
storage:set_string(player:get_player_name()..";sort", fields.sort:sub(4))
|
|
player_page[player:get_player_name()] = 0
|
|
player:set_inventory_formspec(generate_formspec(
|
|
player,
|
|
context
|
|
))
|
|
return true
|
|
end
|
|
for field, _ in next, fields do
|
|
if field:sub(1, 12) == "levels_menu:" then
|
|
player:set_inventory_formspec(generate_formspec(
|
|
player,
|
|
context,
|
|
field:sub(13)
|
|
))
|
|
return true
|
|
end
|
|
end
|
|
end,
|
|
})
|
|
|
|
local sfinv_get_nav_fs = sfinv.get_nav_fs
|
|
function sfinv.get_nav_fs(player, context, nav, current_idx)
|
|
local modified_nav = table.copy(nav)
|
|
for index, title in next, modified_nav do
|
|
if title == "level_menu:level" then
|
|
modified_nav[index] = "Level "..liblevelup.get.player_level(player:get_player_name())
|
|
end
|
|
end
|
|
return sfinv_get_nav_fs(player, context, modified_nav, current_idx)
|
|
end
|
|
|
|
liblevelup.register.levelup_function(function(player, level)
|
|
sfinv.set_page(player, sfinv.get_page(player))
|
|
end)
|