Minetest_Game_Mushroom_Fork/mods/specific to Mushroom Fork/level_menu/init.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)