poikilos
3 years ago
20 changed files with 3416 additions and 0 deletions
@ -0,0 +1 @@ |
|||
See "oldcoder.txt". |
@ -0,0 +1,19 @@ |
|||
Unified Inventory for Minetest |
|||
Copyright (C) 2012-2014 Maciej Kasatkin (RealBadAngel) |
|||
|
|||
This library is free software; you can redistribute it and/or |
|||
modify it under the terms of the GNU Library General Public |
|||
License as published by the Free Software Foundation; either |
|||
version 2 of the License, or (at your option) any later version. |
|||
|
|||
This library 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 |
|||
Library General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Library General Public |
|||
License along with this library; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
Contact information: |
|||
Examine a git patch to get the contributor's email address. |
@ -0,0 +1,96 @@ |
|||
# Unified Inventory |
|||
|
|||
Unified Inventory replaces the default survival and creative inventory. |
|||
|
|||
|
|||
## Features |
|||
|
|||
* Node, item and tool browser |
|||
* Crafting guide |
|||
* Can copy the recipe to the crafting grid |
|||
* Recipe search function by ingredients |
|||
* Up to four bags with up to 24 slots each |
|||
* Home function to teleport |
|||
* Trash slot |
|||
* Lite mode: reduces the item browser width |
|||
* Mod API for modders: see [mod_api.txt](doc/mod_api.txt) |
|||
* Setting-determinated features: see [settingtypes.txt](settingtypes.txt) |
|||
|
|||
|
|||
## Requirements |
|||
|
|||
* Minetest 5.0.0+ since commit 4403b69 |
|||
* Minetest 0.4.16+ prior commit 4403b69 |
|||
|
|||
|
|||
# Licenses |
|||
|
|||
Copyright (C) 2012-2014 Maciej Kasatkin (RealBadAngel) |
|||
|
|||
Copyright (C) 2012-? Various minetest-mods contributors |
|||
|
|||
|
|||
## Code |
|||
|
|||
GNU LGPLv2+, see [license notice](LICENSE.txt) |
|||
|
|||
|
|||
## Textures |
|||
|
|||
VanessaE: (CC-BY-4.0) |
|||
|
|||
* `ui_group.png` |
|||
|
|||
Tango Project: (Public Domain, CC-BY-4.0) |
|||
|
|||
* [`ui_reset_icon.png`](https://commons.wikimedia.org/wiki/File:Edit-clear.svg) |
|||
* [`ui_doubleleft_icon.png`](http://commons.wikimedia.org/wiki/File:Media-seek-backward.svg) |
|||
* [`ui_doubleright_icon.png`](http://commons.wikimedia.org/wiki/File:Media-seek-forward.svg) |
|||
* [`ui_left_icon.png` / `ui_right_icon.png`](http://commons.wikimedia.org/wiki/File:Media-playback-start.svg) |
|||
* [`ui_skip_backward_icon.png`](http://commons.wikimedia.org/wiki/File:Media-skip-backward.svg) |
|||
* [`ui_skip_forward_icon.png`](http://commons.wikimedia.org/wiki/File:Media-skip-forward.svg) |
|||
|
|||
From http://www.clker.com (Public Domain, CC-BY-4.0): |
|||
|
|||
* [`bags_small.png`](http://www.clker.com/clipart-moneybag-empty.html) |
|||
* [`bags_medium.png`](http://www.clker.com/clipart-backpack-1.html) |
|||
* [`bags_large.png` / `ui_bags_icon.png`](http://www.clker.com/clipart-backpack-green-brown.html) |
|||
* `ui_trash_icon.png`: <http://www.clker.com/clipart-29090.html> and <http://www.clker.com/clipart-trash.html> |
|||
* [`ui_search_icon.png`](http://www.clker.com/clipart-24887.html) |
|||
* [`ui_off_icon.png` / `ui_on_icon.png`](http://www.clker.com/clipart-on-off-switches.html) |
|||
* [`ui_waypoints_icon.png`](http://www.clker.com/clipart-map-pin-red.html) |
|||
* [`ui_circular_arrows_icon.png`](http://www.clker.com/clipart-circular-arrow-pattern.html) |
|||
* [`ui_pencil_icon.pnc`](http://www.clker.com/clipart-2256.html) |
|||
* [`ui_waypoint_set_icon.png`](http://www.clker.com/clipart-larger-flag.html) |
|||
|
|||
Everaldo Coelho (YellowIcon) (LGPL v2.1+): |
|||
|
|||
* [`ui_craftguide_icon.png` / `ui_craft_icon.png`](http://commons.wikimedia.org/wiki/File:Advancedsettings.png) |
|||
|
|||
Gregory H. Revera: (CC-BY-SA 3.0) |
|||
|
|||
* [`ui_moon_icon.png`](http://commons.wikimedia.org/wiki/File:FullMoon2010.jpg) |
|||
|
|||
Thomas Bresson: (CC-BY 3.0) |
|||
|
|||
* [`ui_sun_icon.png`](http://commons.wikimedia.org/wiki/File:2012-10-13_15-29-35-sun.jpg) |
|||
|
|||
Fibonacci: (Public domain, CC-BY 4.0) |
|||
|
|||
* [`ui_xyz_off_icon.png`](http://commons.wikimedia.org/wiki/File:No_sign.svg) |
|||
|
|||
Gregory Maxwell: (Public domain, CC-BY 4.0) |
|||
|
|||
* [`ui_ok_icon.png`](http://commons.wikimedia.org/wiki/File:Yes_check.svg) |
|||
|
|||
Adrien Facélina: (LGPL v2.1+) |
|||
|
|||
* [`inventory_plus_worldedit_gui.png`](http://commons.wikimedia.org/wiki/File:Erioll_world_2.svg) |
|||
|
|||
Other files from Wikimedia Commons: |
|||
|
|||
* [`ui_gohome_icon.png` / `ui_home_icon.png` / `ui_sethome_icon.png`](http://commons.wikimedia.org/wiki/File:Home_256x256.png) (GPL v2+) |
|||
|
|||
RealBadAngel: (CC-BY-4.0) |
|||
|
|||
* Everything else. |
@ -0,0 +1,321 @@ |
|||
local S = function (str) return str end |
|||
local F = minetest.formspec_escape |
|||
|
|||
-- Create detached creative inventory after loading all mods |
|||
minetest.after(0.01, function() |
|||
local rev_aliases = {} |
|||
for source, target in pairs(minetest.registered_aliases) do |
|||
if not rev_aliases[target] then rev_aliases[target] = {} end |
|||
table.insert(rev_aliases[target], source) |
|||
end |
|||
unified_inventory.items_list = {} |
|||
for name, def in pairs(minetest.registered_items) do |
|||
if (not def.groups.not_in_creative_inventory or |
|||
def.groups.not_in_creative_inventory == 0) and |
|||
def.description and def.description ~= "" then |
|||
table.insert(unified_inventory.items_list, name) |
|||
local all_names = rev_aliases[name] or {} |
|||
table.insert(all_names, name) |
|||
for _, name in ipairs(all_names) do |
|||
local recipes = minetest.get_all_craft_recipes(name) |
|||
if recipes then |
|||
for _, recipe in ipairs(recipes) do |
|||
|
|||
local unknowns |
|||
|
|||
for _,chk in pairs(recipe.items) do |
|||
local groupchk = string.find(chk, "group:") |
|||
if (not groupchk and not minetest.registered_items[chk]) |
|||
or (groupchk and not unified_inventory.get_group_item(string.gsub(chk, "group:", "")).item) |
|||
or minetest.get_item_group(chk, "not_in_craft_guide") ~= 0 then |
|||
unknowns = true |
|||
end |
|||
end |
|||
|
|||
if not unknowns then |
|||
unified_inventory.register_craft(recipe) |
|||
end |
|||
end |
|||
end |
|||
end |
|||
end |
|||
end |
|||
table.sort(unified_inventory.items_list) |
|||
unified_inventory.items_list_size = #unified_inventory.items_list |
|||
print("Unified Inventory. inventory size: "..unified_inventory.items_list_size) |
|||
for _, name in ipairs(unified_inventory.items_list) do |
|||
local def = minetest.registered_items[name] |
|||
-- Simple drops |
|||
if type(def.drop) == "string" then |
|||
local dstack = ItemStack(def.drop) |
|||
if not dstack:is_empty() and dstack:get_name() ~= name then |
|||
unified_inventory.register_craft({ |
|||
type = "digging", |
|||
items = {name}, |
|||
output = def.drop, |
|||
width = 0, |
|||
}) |
|||
|
|||
end |
|||
-- Complex drops. Yes, it's really complex! |
|||
elseif type(def.drop) == "table" then |
|||
--[[ Extract single items from the table and save them into dedicated tables |
|||
to register them later, in order to avoid duplicates. These tables counts |
|||
the total number of guaranteed drops and drops by chance (“maybes”) for each item. |
|||
For “maybes”, the final count is the theoretical maximum number of items, not |
|||
neccessarily the actual drop count. ]] |
|||
local drop_guaranteed = {} |
|||
local drop_maybe = {} |
|||
-- This is for catching an obscure corner case: If the top items table has |
|||
-- only items with rarity = 1, but max_items is set, then only the first |
|||
-- max_items will be part of the drop, any later entries are logically |
|||
-- impossible, so this variable is for keeping track of this |
|||
local max_items_left = def.drop.max_items |
|||
-- For checking whether we still encountered only guaranteed only so far; |
|||
-- for the first “maybe” item it will become false which will cause ALL |
|||
-- later items to be considered “maybes”. |
|||
-- A common idiom is: |
|||
-- { max_items 1, { items = { |
|||
-- { items={"example:1"}, rarity = 5 }, |
|||
-- { items={"example:2"}, rarity = 1 }, }}} |
|||
-- example:2 must be considered a “maybe” because max_items is set and it |
|||
-- appears after a “maybe” |
|||
local max_start = true |
|||
-- Let's iterate through the items madness! |
|||
-- Handle invalid drop entries gracefully. |
|||
local drop_items = def.drop.items or { } |
|||
for i=1,#drop_items do |
|||
if max_items_left ~= nil and max_items_left <= 0 then break end |
|||
local itit = drop_items[i] |
|||
for j=1,#itit.items do |
|||
local dstack = ItemStack(itit.items[j]) |
|||
if not dstack:is_empty() and dstack:get_name() ~= name then |
|||
local dname = dstack:get_name() |
|||
local dcount = dstack:get_count() |
|||
-- Guaranteed drops AND we are not yet in “maybe mode” |
|||
if #itit.items == 1 and itit.rarity == 1 and max_start then |
|||
if drop_guaranteed[dname] == nil then |
|||
drop_guaranteed[dname] = 0 |
|||
end |
|||
drop_guaranteed[dname] = drop_guaranteed[dname] + dcount |
|||
|
|||
if max_items_left ~= nil then |
|||
max_items_left = max_items_left - 1 |
|||
if max_items_left <= 0 then break end |
|||
end |
|||
-- Drop was a “maybe” |
|||
else |
|||
if max_items_left ~= nil then max_start = false end |
|||
if drop_maybe[dname] == nil then |
|||
drop_maybe[dname] = 0 |
|||
end |
|||
drop_maybe[dname] = drop_maybe[dname] + dcount |
|||
end |
|||
end |
|||
end |
|||
end |
|||
for itemstring, count in pairs(drop_guaranteed) do |
|||
unified_inventory.register_craft({ |
|||
type = "digging", |
|||
items = {name}, |
|||
output = itemstring .. " " .. count, |
|||
width = 0, |
|||
}) |
|||
end |
|||
for itemstring, count in pairs(drop_maybe) do |
|||
unified_inventory.register_craft({ |
|||
type = "digging_chance", |
|||
items = {name}, |
|||
output = itemstring .. " " .. count, |
|||
width = 0, |
|||
}) |
|||
end |
|||
end |
|||
end |
|||
for _, recipes in pairs(unified_inventory.crafts_for.recipe) do |
|||
for _, recipe in ipairs(recipes) do |
|||
local ingredient_items = {} |
|||
for _, spec in pairs(recipe.items) do |
|||
local matches_spec = unified_inventory.canonical_item_spec_matcher(spec) |
|||
for _, name in ipairs(unified_inventory.items_list) do |
|||
if matches_spec(name) then |
|||
ingredient_items[name] = true |
|||
end |
|||
end |
|||
end |
|||
for name, _ in pairs(ingredient_items) do |
|||
if unified_inventory.crafts_for.usage[name] == nil then |
|||
unified_inventory.crafts_for.usage[name] = {} |
|||
end |
|||
table.insert(unified_inventory.crafts_for.usage[name], recipe) |
|||
end |
|||
end |
|||
end |
|||
end) |
|||
|
|||
|
|||
-- load_home |
|||
local function load_home() |
|||
local input = io.open(unified_inventory.home_filename, "r") |
|||
if not input then |
|||
unified_inventory.home_pos = {} |
|||
return |
|||
end |
|||
while true do |
|||
local x = input:read("*n") |
|||
if not x then break end |
|||
local y = input:read("*n") |
|||
local z = input:read("*n") |
|||
local name = input:read("*l") |
|||
unified_inventory.home_pos[name:sub(2)] = {x = x, y = y, z = z} |
|||
end |
|||
io.close(input) |
|||
end |
|||
load_home() |
|||
|
|||
function unified_inventory.set_home(player, pos) |
|||
|
|||
local ds = unified_inventory.disable_set |
|||
if ds ~= nil and ds then return end |
|||
|
|||
local player_name = player:get_player_name() |
|||
unified_inventory.home_pos[player_name] = vector.round(pos) |
|||
-- save the home data from the table to the file |
|||
local output = io.open(unified_inventory.home_filename, "w") |
|||
for k, v in pairs(unified_inventory.home_pos) do |
|||
output:write(v.x.." "..v.y.." "..v.z.." "..k.."\n") |
|||
end |
|||
io.close(output) |
|||
|
|||
-- RJK: |
|||
if _G ["ocsethome"] ~= nil and |
|||
ocsethome ~= nil and |
|||
ocsethome.sethome ~= nil then |
|||
|
|||
unified_inventory.disable_set = true |
|||
ocsethome.sethome (player_name, "1") |
|||
unified_inventory.disable_set = false |
|||
end |
|||
end |
|||
|
|||
function unified_inventory.go_home(player) |
|||
local pos = unified_inventory.home_pos[player:get_player_name()] |
|||
if pos then |
|||
player:set_pos(pos) |
|||
end |
|||
end |
|||
|
|||
-- register_craft |
|||
function unified_inventory.register_craft(options) |
|||
if not options.output then |
|||
return |
|||
end |
|||
local itemstack = ItemStack(options.output) |
|||
if itemstack:is_empty() then |
|||
return |
|||
end |
|||
if options.type == "normal" and options.width == 0 then |
|||
options = { type = "shapeless", items = options.items, output = options.output, width = 0 } |
|||
end |
|||
if not unified_inventory.crafts_for.recipe[itemstack:get_name()] then |
|||
unified_inventory.crafts_for.recipe[itemstack:get_name()] = {} |
|||
end |
|||
table.insert(unified_inventory.crafts_for.recipe[itemstack:get_name()],options) |
|||
end |
|||
|
|||
|
|||
local craft_type_defaults = { |
|||
width = 3, |
|||
height = 3, |
|||
uses_crafting_grid = false, |
|||
} |
|||
|
|||
|
|||
function unified_inventory.craft_type_defaults(name, options) |
|||
if not options.description then |
|||
options.description = name |
|||
end |
|||
setmetatable(options, {__index = craft_type_defaults}) |
|||
return options |
|||
end |
|||
|
|||
|
|||
function unified_inventory.register_craft_type(name, options) |
|||
unified_inventory.registered_craft_types[name] = |
|||
unified_inventory.craft_type_defaults(name, options) |
|||
end |
|||
|
|||
|
|||
unified_inventory.register_craft_type("normal", { |
|||
description = F(S("Crafting")), |
|||
icon = "ui_craftgrid_icon.png", |
|||
width = 3, |
|||
height = 3, |
|||
get_shaped_craft_width = function (craft) return craft.width end, |
|||
dynamic_display_size = function (craft) |
|||
local w = craft.width |
|||
local h = math.ceil(table.maxn(craft.items) / craft.width) |
|||
local g = w < h and h or w |
|||
return { width = g, height = g } |
|||
end, |
|||
uses_crafting_grid = true, |
|||
}) |
|||
|
|||
|
|||
unified_inventory.register_craft_type("shapeless", { |
|||
description = F(S("Mixing")), |
|||
icon = "ui_craftgrid_icon.png", |
|||
width = 3, |
|||
height = 3, |
|||
dynamic_display_size = function (craft) |
|||
local maxn = table.maxn(craft.items) |
|||
local g = 1 |
|||
while g*g < maxn do g = g + 1 end |
|||
return { width = g, height = g } |
|||
end, |
|||
uses_crafting_grid = true, |
|||
}) |
|||
|
|||
|
|||
unified_inventory.register_craft_type("cooking", { |
|||
description = F(S("Cooking")), |
|||
icon = "default_furnace_front.png", |
|||
width = 1, |
|||
height = 1, |
|||
}) |
|||
|
|||
|
|||
unified_inventory.register_craft_type("digging", { |
|||
description = F(S("Digging")), |
|||
icon = "default_tool_steelpick.png", |
|||
width = 1, |
|||
height = 1, |
|||
}) |
|||
|
|||
unified_inventory.register_craft_type("digging_chance", { |
|||
description = "Digging (by chance)", |
|||
icon = "default_tool_steelpick.png^[transformFY.png", |
|||
width = 1, |
|||
height = 1, |
|||
}) |
|||
|
|||
function unified_inventory.register_page(name, def) |
|||
unified_inventory.pages[name] = def |
|||
end |
|||
|
|||
|
|||
function unified_inventory.register_button(name, def) |
|||
if not def.action then |
|||
def.action = function(player) |
|||
unified_inventory.set_inventory_formspec(player, name) |
|||
end |
|||
end |
|||
def.name = name |
|||
table.insert(unified_inventory.buttons, def) |
|||
end |
|||
|
|||
|
|||
function unified_inventory.is_creative(playername) |
|||
return minetest.check_player_privs(playername, {creative=true}) |
|||
or minetest.settings:get_bool("creative_mode") |
|||
end |
@ -0,0 +1,6 @@ |
|||
default |
|||
creative? |
|||
sfinv? |
|||
datastorage? |
|||
farming? |
|||
ocsethome? |
@ -0,0 +1,127 @@ |
|||
local S = function (str) return str end |
|||
|
|||
function unified_inventory.canonical_item_spec_matcher(spec) |
|||
local specname = ItemStack(spec):get_name() |
|||
if specname:sub(1, 6) ~= "group:" then |
|||
return function (itemname) |
|||
return itemname == specname |
|||
end |
|||
end |
|||
|
|||
local group_names = specname:sub(7):split(",") |
|||
return function (itemname) |
|||
local itemdef = minetest.registered_items[itemname] |
|||
for _, group_name in ipairs(group_names) do |
|||
if (itemdef.groups[group_name] or 0) == 0 then |
|||
return false |
|||
end |
|||
end |
|||
return true |
|||
end |
|||
end |
|||
|
|||
function unified_inventory.item_matches_spec(item, spec) |
|||
local itemname = ItemStack(item):get_name() |
|||
return unified_inventory.canonical_item_spec_matcher(spec)(itemname) |
|||
end |
|||
|
|||
function unified_inventory.extract_groupnames(groupname) |
|||
local specname = ItemStack(groupname):get_name() |
|||
if specname:sub(1, 6) ~= "group:" then |
|||
return nil, 0 |
|||
end |
|||
local group_names = specname:sub(7):split(",") |
|||
return table.concat(group_names, S(" and ")), #group_names |
|||
end |
|||
|
|||
unified_inventory.registered_group_items = { |
|||
mesecon_conductor_craftable = "mesecons:wire_00000000_off", |
|||
stone = "default:cobble", |
|||
wood = "default:wood", |
|||
book = "default:book", |
|||
sand = "default:sand", |
|||
leaves = "default:leaves", |
|||
tree = "default:tree", |
|||
vessel = "vessels:glass_bottle", |
|||
wool = "wool:white", |
|||
} |
|||
|
|||
function unified_inventory.register_group_item(groupname, itemname) |
|||
unified_inventory.registered_group_items[groupname] = itemname |
|||
end |
|||
|
|||
|
|||
-- This is used when displaying craft recipes, where an ingredient is |
|||
-- specified by group rather than as a specific item. A single-item group |
|||
-- is represented by that item, with the single-item status signalled |
|||
-- in the "sole" field. If the group contains no items at all, the item |
|||
-- field will be nil. |
|||
-- |
|||
-- Within a multiple-item group, we prefer to use an item that has the |
|||
-- same specific name as the group, and if there are more than one of |
|||
-- those items we prefer the one registered for the group by a mod. |
|||
-- Among equally-preferred items, we just pick the one with the |
|||
-- lexicographically earliest name. |
|||
-- |
|||
-- The parameter to this function isn't just a single group name. |
|||
-- It may be a comma-separated list of group names. This is really a |
|||
-- "group:..." ingredient specification, minus the "group:" prefix. |
|||
|
|||
local function compute_group_item(group_name_list) |
|||
local group_names = group_name_list:split(",") |
|||
local candidate_items = {} |
|||
for itemname, itemdef in pairs(minetest.registered_items) do |
|||
if (itemdef.groups.not_in_creative_inventory or 0) == 0 then |
|||
local all = true |
|||
for _, group_name in ipairs(group_names) do |
|||
if (itemdef.groups[group_name] or 0) == 0 then |
|||
all = false |
|||
end |
|||
end |
|||
if all then table.insert(candidate_items, itemname) end |
|||
end |
|||
end |
|||
local num_candidates = #candidate_items |
|||
if num_candidates == 0 then |
|||
return {sole = true} |
|||
elseif num_candidates == 1 then |
|||
return {item = candidate_items[1], sole = true} |
|||
end |
|||
local is_group = {} |
|||
local registered_rep = {} |
|||
for _, group_name in ipairs(group_names) do |
|||
is_group[group_name] = true |
|||
local rep = unified_inventory.registered_group_items[group_name] |
|||
if rep then registered_rep[rep] = true end |
|||
end |
|||
local bestitem = "" |
|||
local bestpref = 0 |
|||
for _, item in ipairs(candidate_items) do |
|||
local pref |
|||
if registered_rep[item] then |
|||
pref = 4 |
|||
elseif string.sub(item, 1, 8) == "default:" and is_group[string.sub(item, 9)] then |
|||
pref = 3 |
|||
elseif is_group[item:gsub("^[^:]*:", "")] then |
|||
pref = 2 |
|||
else |
|||
pref = 1 |
|||
end |
|||
if pref > bestpref or (pref == bestpref and item < bestitem) then |
|||
bestitem = item |
|||
bestpref = pref |
|||
end |
|||
end |
|||
return {item = bestitem, sole = false} |
|||
end |
|||
|
|||
|
|||
local group_item_cache = {} |
|||
|
|||
function unified_inventory.get_group_item(group_name) |
|||
if not group_item_cache[group_name] then |
|||
group_item_cache[group_name] = compute_group_item(group_name) |
|||
end |
|||
return group_item_cache[group_name] |
|||
end |
|||
|
@ -0,0 +1,375 @@ |
|||
local S = function (str) return str end |
|||
local F = minetest.formspec_escape |
|||
|
|||
-- This pair of encoding functions is used where variable text must go in |
|||
-- button names, where the text might contain formspec metacharacters. |
|||
-- We can escape button names for the formspec, to avoid screwing up |
|||
-- form structure overall, but they then don't get de-escaped, and so |
|||
-- the input we get back from the button contains the formspec escaping. |
|||
-- This is a game engine bug, and in the anticipation that it might be |
|||
-- fixed some day we don't want to rely on it. So for safety we apply |
|||
-- an encoding that avoids all formspec metacharacters. |
|||
function unified_inventory.mangle_for_formspec(str) |
|||
return string.gsub(str, "([^A-Za-z0-9])", function (c) return string.format("_%d_", string.byte(c)) end) |
|||
end |
|||
function unified_inventory.demangle_for_formspec(str) |
|||
return string.gsub(str, "_([0-9]+)_", function (v) return string.char(v) end) |
|||
end |
|||
|
|||
function unified_inventory.get_per_player_formspec(player_name) |
|||
local lite = unified_inventory.lite_mode and not minetest.check_player_privs(player_name, {ui_full=true}) |
|||
|
|||
local ui = {} |
|||
ui.pagecols = unified_inventory.pagecols |
|||
ui.pagerows = unified_inventory.pagerows |
|||
ui.page_y = unified_inventory.page_y |
|||
ui.formspec_y = unified_inventory.formspec_y |
|||
ui.main_button_x = unified_inventory.main_button_x |
|||
ui.main_button_y = unified_inventory.main_button_y |
|||
ui.craft_result_x = unified_inventory.craft_result_x |
|||
ui.craft_result_y = unified_inventory.craft_result_y |
|||
ui.form_header_y = unified_inventory.form_header_y |
|||
|
|||
if lite then |
|||
ui.pagecols = 4 |
|||
ui.pagerows = 6 |
|||
ui.page_y = 0.25 |
|||
ui.formspec_y = 0.47 |
|||
ui.main_button_x = 8.2 |
|||
ui.main_button_y = 6.5 |
|||
ui.craft_result_x = 2.8 |
|||
ui.craft_result_y = 3.4 |
|||
ui.form_header_y = -0.1 |
|||
end |
|||
|
|||
ui.items_per_page = ui.pagecols * ui.pagerows |
|||
return ui, lite |
|||
end |
|||
|
|||
function unified_inventory.get_formspec(player, page) |
|||
|
|||
if not player then |
|||
return "" |
|||
end |
|||
|
|||
local player_name = player:get_player_name() |
|||
local ui_peruser,draw_lite_mode = unified_inventory.get_per_player_formspec(player_name) |
|||
|
|||
unified_inventory.current_page[player_name] = page |
|||
local pagedef = unified_inventory.pages[page] |
|||
|
|||
if not pagedef then |
|||
return "" -- Invalid page name |
|||
end |
|||
|
|||
local formspec = { |
|||
"size[14,10]", |
|||
pagedef.formspec_prepend and "" or "no_prepend[]", |
|||
"background[-0.19,-0.25;14.4,10.75;ui_form_bg.png]" -- Background |
|||
} |
|||
local n = 4 |
|||
|
|||
if draw_lite_mode then |
|||
formspec[1] = "size[11,7.7]" |
|||
formspec[3] = "background[-0.19,-0.2;11.4,8.4;ui_form_bg.png]" |
|||
end |
|||
|
|||
if unified_inventory.is_creative(player_name) |
|||
and page == "craft" then |
|||
formspec[n] = "background[0,"..(ui_peruser.formspec_y + 2)..";1,1;ui_single_slot.png]" |
|||
n = n+1 |
|||
end |
|||
|
|||
local perplayer_formspec = unified_inventory.get_per_player_formspec(player_name) |
|||
local fsdata = pagedef.get_formspec(player, perplayer_formspec) |
|||
|
|||
formspec[n] = fsdata.formspec |
|||
n = n+1 |
|||
|
|||
local button_row = 0 |
|||
local button_col = 0 |
|||
|
|||
-- Main buttons |
|||
|
|||
local filtered_inv_buttons = {} |
|||
|
|||
for i, def in pairs(unified_inventory.buttons) do |
|||
if not (draw_lite_mode and def.hide_lite) then |
|||
table.insert(filtered_inv_buttons, def) |
|||
end |
|||
end |
|||
|
|||
for i, def in pairs(filtered_inv_buttons) do |
|||
|
|||
if draw_lite_mode and i > 4 then |
|||
button_row = 1 |
|||
button_col = 1 |
|||
end |
|||
|
|||
if def.type == "image" then |
|||
if (def.condition == nil or def.condition(player) == true) then |
|||
formspec[n] = "image_button[" |
|||
formspec[n+1] = ( ui_peruser.main_button_x + 0.65 * (i - 1) - button_col * 0.65 * 4) |
|||
formspec[n+2] = ","..(ui_peruser.main_button_y + button_row * 0.7)..";0.8,0.8;" |
|||
formspec[n+3] = F(def.image)..";" |
|||
formspec[n+4] = F(def.name)..";]" |
|||
formspec[n+5] = "tooltip["..F(def.name) |
|||
formspec[n+6] = ";"..(def.tooltip or "").."]" |
|||
n = n+7 |
|||
else |
|||
formspec[n] = "image[" |
|||
formspec[n+1] = ( ui_peruser.main_button_x + 0.65 * (i - 1) - button_col * 0.65 * 4) |
|||
formspec[n+2] = ","..(ui_peruser.main_button_y + button_row * 0.7)..";0.8,0.8;" |
|||
formspec[n+3] = F(def.image).."^[colorize:#808080:alpha]" |
|||
n = n+4 |
|||
|
|||
end |
|||
end |
|||
end |
|||
|
|||
if fsdata.draw_inventory ~= false then |
|||
-- Player inventory |
|||
formspec[n] = "listcolors[#00000000;#00000000]" |
|||
formspec[n+1] = "list[current_player;main;0,"..(ui_peruser.formspec_y + 3.5)..";8,4;]" |
|||
n = n+2 |
|||
end |
|||
|
|||
if fsdata.draw_item_list == false then |
|||
return table.concat(formspec, "") |
|||
end |
|||
|
|||
-- Controls to flip items pages |
|||
local start_x = 9.2 |
|||
|
|||
if not draw_lite_mode then |
|||
formspec[n] = |
|||
"image_button[" .. (start_x + 0.6 * 0) |
|||
.. ",9;.8,.8;ui_skip_backward_icon.png;start_list;]" |
|||
.. "tooltip[start_list;" .. F(S("First page")) .. "]" |
|||
|
|||
.. "image_button[" .. (start_x + 0.6 * 1) |
|||
.. ",9;.8,.8;ui_doubleleft_icon.png;rewind3;]" |
|||
.. "tooltip[rewind3;" .. F(S("Back three pages")) .. "]" |
|||
.. "image_button[" .. (start_x + 0.6 * 2) |
|||
.. ",9;.8,.8;ui_left_icon.png;rewind1;]" |
|||
.. "tooltip[rewind1;" .. F(S("Back one page")) .. "]" |
|||
|
|||
.. "image_button[" .. (start_x + 0.6 * 3) |
|||
.. ",9;.8,.8;ui_right_icon.png;forward1;]" |
|||
.. "tooltip[forward1;" .. F(S("Forward one page")) .. "]" |
|||
.. "image_button[" .. (start_x + 0.6 * 4) |
|||
.. ",9;.8,.8;ui_doubleright_icon.png;forward3;]" |
|||
.. "tooltip[forward3;" .. F(S("Forward three pages")) .. "]" |
|||
|
|||
.. "image_button[" .. (start_x + 0.6 * 5) |
|||
.. ",9;.8,.8;ui_skip_forward_icon.png;end_list;]" |
|||
.. "tooltip[end_list;" .. F(S("Last page")) .. "]" |
|||
else |
|||
formspec[n] = |
|||
"image_button[" .. (8.2 + 0.65 * 0) |
|||
.. ",5.8;.8,.8;ui_skip_backward_icon.png;start_list;]" |
|||
.. "tooltip[start_list;" .. F(S("First page")) .. "]" |
|||
.. "image_button[" .. (8.2 + 0.65 * 1) |
|||
.. ",5.8;.8,.8;ui_left_icon.png;rewind1;]" |
|||
.. "tooltip[rewind1;" .. F(S("Back one page")) .. "]" |
|||
.. "image_button[" .. (8.2 + 0.65 * 2) |
|||
.. ",5.8;.8,.8;ui_right_icon.png;forward1;]" |
|||
.. "tooltip[forward1;" .. F(S("Forward one page")) .. "]" |
|||
.. "image_button[" .. (8.2 + 0.65 * 3) |
|||
.. ",5.8;.8,.8;ui_skip_forward_icon.png;end_list;]" |
|||
.. "tooltip[end_list;" .. F(S("Last page")) .. "]" |
|||
end |
|||
n = n+1 |
|||
|
|||
-- Search box |
|||
formspec[n] = "field_close_on_enter[searchbox;false]" |
|||
n = n+1 |
|||
|
|||
if not draw_lite_mode then |
|||
formspec[n] = "field[9.5,8.325;3,1;searchbox;;" |
|||
.. F(unified_inventory.current_searchbox[player_name]) .. "]" |
|||
formspec[n+1] = "image_button[12.2,8.1;.8,.8;ui_search_icon.png;searchbutton;]" |
|||
.. "tooltip[searchbutton;" ..F(S("Search")) .. "]" |
|||
formspec[n+2] = "image_button[12.9,8.1;.8,.8;ui_reset_icon.png;searchresetbutton;]" |
|||
.. "tooltip[searchbutton;" ..F(S("Search")) .. "]" |
|||
.. "tooltip[searchresetbutton;" ..F(S("Reset search and display everything")) .. "]" |
|||
else |
|||
formspec[n] = "field[8.5,5.225;2.2,1;searchbox;;" |
|||
.. F(unified_inventory.current_searchbox[player_name]) .. "]" |
|||
formspec[n+1] = "image_button[10.3,5;.8,.8;ui_search_icon.png;searchbutton;]" |
|||
.. "tooltip[searchbutton;" ..F(S("Search")) .. "]" |
|||
formspec[n+2] = "image_button[11,5;.8,.8;ui_reset_icon.png;searchresetbutton;]" |
|||
.. "tooltip[searchbutton;" ..F(S("Search")) .. "]" |
|||
.. "tooltip[searchresetbutton;" ..F(S("Reset search and display everything")) .. "]" |
|||
end |
|||
n = n+3 |
|||
|
|||
local no_matches = S("No matching items") |
|||
if draw_lite_mode then |
|||
no_matches = S("No matches.") |
|||
end |
|||
|
|||
-- Items list |
|||
if #unified_inventory.filtered_items_list[player_name] == 0 then |
|||
formspec[n] = "label[8.2,"..ui_peruser.form_header_y..";" .. F(no_matches) .. "]" |
|||
else |
|||
local dir = unified_inventory.active_search_direction[player_name] |
|||
local list_index = unified_inventory.current_index[player_name] |
|||
local page = math.floor(list_index / (ui_peruser.items_per_page) + 1) |
|||
local pagemax = math.floor( |
|||
(#unified_inventory.filtered_items_list[player_name] - 1) |
|||
/ (ui_peruser.items_per_page) + 1) |
|||
local item = {} |
|||
for y = 0, ui_peruser.pagerows - 1 do |
|||
for x = 0, ui_peruser.pagecols - 1 do |
|||
local name = unified_inventory.filtered_items_list[player_name][list_index] |
|||
local item = minetest.registered_items[name] |
|||
if item then |
|||
-- Clicked on current item: Flip crafting direction |
|||
if name == unified_inventory.current_item[player_name] then |
|||
local cdir = unified_inventory.current_craft_direction[player_name] |
|||
if cdir == "recipe" then |
|||
dir = "usage" |
|||
elseif cdir == "usage" then |
|||
dir = "recipe" |
|||
end |
|||
else |
|||
-- Default: use active search direction by default |
|||
dir = unified_inventory.active_search_direction[player_name] |
|||
end |
|||
|
|||
local button_name = "item_button_" .. dir .. "_" |
|||
.. unified_inventory.mangle_for_formspec(name) |
|||
formspec[n] = ("item_image_button[%f,%f;.81,.81;%s;%s;]"):format( |
|||
8.2 + x * 0.7, ui_peruser.formspec_y + ui_peruser.page_y + y * 0.7, |
|||
name, button_name |
|||
) |
|||
formspec[n + 1] = ("tooltip[%s;%s \\[%s\\]]"):format( |
|||
button_name, minetest.formspec_escape(item.description), |
|||
item.mod_origin or "??" |
|||
) |
|||
n = n + 2 |
|||
list_index = list_index + 1 |
|||
end |
|||
end |
|||
end |
|||
|
|||
formspec [n] = "label[8.2," .. ui_peruser.form_header_y |
|||
.. ";Page " .. page .. " of " .. pagemax .. "]" |
|||
end |
|||
n = n+1 |
|||
|
|||
if unified_inventory.activefilter[player_name] ~= "" then |
|||
formspec[n] = "label[8.2,"..(ui_peruser.form_header_y + 0.4)..";" .. F(S("Filter")) .. ":]" |
|||
formspec[n+1] = "label[9.1,"..(ui_peruser.form_header_y + 0.4)..";"..F(unified_inventory.activefilter[player_name]).."]" |
|||
end |
|||
return table.concat(formspec, "") |
|||
end |
|||
|
|||
function unified_inventory.set_inventory_formspec(player, page) |
|||
if player then |
|||
player:set_inventory_formspec(unified_inventory.get_formspec(player, page)) |
|||
end |
|||
end |
|||
|
|||
--apply filter to the inventory list (create filtered copy of full one) |
|||
function unified_inventory.apply_filter(player, filter, search_dir) |
|||
if not player then |
|||
return false |
|||
end |
|||
local player_name = player:get_player_name() |
|||
local lfilter = string.lower(filter) |
|||
local ffilter |
|||
if lfilter:sub(1, 6) == "group:" then |
|||
local groups = lfilter:sub(7):split(",") |
|||
ffilter = function(name, def) |
|||
for _, group in ipairs(groups) do |
|||
if not def.groups[group] |
|||
or def.groups[group] <= 0 then |
|||
return false |
|||
end |
|||
end |
|||
return true |
|||
end |
|||
else |
|||
ffilter = function(name, def) |
|||
local lname = string.lower(name) |
|||
local ldesc = string.lower(def.description) |
|||
return string.find(lname, lfilter, 1, true) or string.find(ldesc, lfilter, 1, true) |
|||
end |
|||
end |
|||
unified_inventory.filtered_items_list[player_name]={} |
|||
for name, def in pairs(minetest.registered_items) do |
|||
if (not def.groups.not_in_creative_inventory |
|||
or def.groups.not_in_creative_inventory == 0) |
|||
and def.description |
|||
and def.description ~= "" |
|||
and ffilter(name, def) then |
|||
table.insert(unified_inventory.filtered_items_list[player_name], name) |
|||
end |
|||
end |
|||
table.sort(unified_inventory.filtered_items_list[player_name]) |
|||
unified_inventory.filtered_items_list_size[player_name] = #unified_inventory.filtered_items_list[player_name] |
|||
unified_inventory.current_index[player_name] = 1 |
|||
unified_inventory.activefilter[player_name] = filter |
|||
unified_inventory.active_search_direction[player_name] = search_dir |
|||
unified_inventory.set_inventory_formspec(player, |
|||
unified_inventory.current_page[player_name]) |
|||
end |
|||
|
|||
function unified_inventory.items_in_group(groups) |
|||
local items = {} |
|||
for name, item in pairs(minetest.registered_items) do |
|||
for _, group in pairs(groups:split(',')) do |
|||
if item.groups[group] then |
|||
table.insert(items, name) |
|||
end |
|||
end |
|||
end |
|||
return items |
|||
end |
|||
|
|||
function unified_inventory.sort_inventory(inv) |
|||
local inlist = inv:get_list("main") |
|||
local typecnt = {} |
|||
local typekeys = {} |
|||
for _, st in ipairs(inlist) do |
|||
if not st:is_empty() then |
|||
local n = st:get_name() |
|||
local w = st:get_wear() |
|||
local m = st:get_metadata() |
|||
local k = string.format("%s %05d %s", n, w, m) |
|||
if not typecnt[k] then |
|||
typecnt[k] = { |
|||
name = n, |
|||
wear = w, |
|||
metadata = m, |
|||
stack_max = st:get_stack_max(), |
|||
count = 0, |
|||
} |
|||
table.insert(typekeys, k) |
|||
end |
|||
typecnt[k].count = typecnt[k].count + st:get_count() |
|||
end |
|||
end |
|||
table.sort(typekeys) |
|||
local outlist = {} |
|||
for _, k in ipairs(typekeys) do |
|||
local tc = typecnt[k] |
|||
while tc.count > 0 do |
|||
local c = math.min(tc.count, tc.stack_max) |
|||
table.insert(outlist, ItemStack({ |
|||
name = tc.name, |
|||
wear = tc.wear, |
|||
metadata = tc.metadata, |
|||
count = c, |
|||
})) |
|||
tc.count = tc.count - c |
|||
end |
|||
end |
|||
if #outlist > #inlist then return end |
|||
while #outlist < #inlist do |
|||
table.insert(outlist, ItemStack(nil)) |
|||
end |
|||
inv:set_list("main", outlist) |
|||
end |
@ -0,0 +1,27 @@ |
|||
Name: unified_inventory |
|||
Source: Fork of upstream mod - Do not replace |
|||
License: See "LICENSE.txt" |
|||
|
|||
---------------------------------------------------------------------- |
|||
|
|||
1. This is a fork of an upstream mod. The starting point was obtained |
|||
initially as follows: |
|||
|
|||
rm -fr unified_inventory |
|||
git clone https://github.com/minetest-mods/unified_inventory.git |
|||
cd unified_inventory |
|||
git reset --hard ca6d9a10df5110fd75d49785adf690ae270d5a31 |
|||
|
|||
Forked after: commit indicated above |
|||
|
|||
---------------------------------------------------------------------- |
|||
|
|||
2. Partial list of changes: |
|||
|
|||
2a. Fixed a few cases where "@1" or "@2" was printed instead of the |
|||
correct output. |
|||
|
|||
2b. Modified the mod so that it stays synced with "ocsethome". |
|||
|
|||
2c. Removed "bags" code, which seems to be crashy. "prestibags" mod |
|||
has been added to the "_game" as a substitute for the "bags" feature. |
@ -0,0 +1,489 @@ |
|||
local S = function (str) return str end |
|||
local NS = function(s) return s end |
|||
local F = minetest.formspec_escape |
|||
|
|||
minetest.register_privilege("creative", { |
|||
description = S("Can use the creative inventory"), |
|||
give_to_singleplayer = false, |
|||
}) |
|||
|
|||
minetest.register_privilege("ui_full", { |
|||
description = S("Forces Unified Inventory to be displayed in Full mode if Lite mode is configured globally"), |
|||
give_to_singleplayer = false, |
|||
}) |
|||
|
|||
|
|||
local trash = minetest.create_detached_inventory("trash", { |
|||
--allow_put = function(inv, listname, index, stack, player) |
|||
-- if unified_inventory.is_creative(player:get_player_name()) then |
|||
-- return stack:get_count() |
|||
-- else |
|||
-- return 0 |
|||
-- end |
|||
--end, |
|||
on_put = function(inv, listname, index, stack, player) |
|||
inv:set_stack(listname, index, nil) |
|||
local player_name = player:get_player_name() |
|||
minetest.sound_play("trash", {to_player=player_name, gain = 1.0}) |
|||
end, |
|||
}) |
|||
trash:set_size("main", 1) |
|||
|
|||
unified_inventory.register_button("craft", { |
|||
type = "image", |
|||
image = "ui_craft_icon.png", |
|||
tooltip = S("Crafting Grid") |
|||
}) |
|||
|
|||
unified_inventory.register_button("craftguide", { |
|||
type = "image", |
|||
image = "ui_craftguide_icon.png", |
|||
tooltip = S("Crafting Guide") |
|||
}) |
|||
|
|||
unified_inventory.register_button("home_gui_set", { |
|||
type = "image", |
|||
image = "ui_sethome_icon.png", |
|||
tooltip = S("Set home position"), |
|||
hide_lite=true, |
|||
action = function(player) |
|||
local player_name = player:get_player_name() |
|||
if minetest.check_player_privs(player_name, {home=true}) then |
|||
unified_inventory.set_home(player, player:get_pos()) |
|||
local home = unified_inventory.home_pos[player_name] |
|||
if home ~= nil then |
|||
minetest.sound_play("dingdong", |
|||
{to_player=player_name, gain = 1.0}) |
|||
minetest.chat_send_player(player_name, |
|||
"Home position set to: " .. minetest.pos_to_string (home)) |
|||
end |
|||
else |
|||
minetest.chat_send_player(player_name, |
|||
S("You don't have the \"home\" privilege!")) |
|||
unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name]) |
|||
end |
|||
end, |
|||
condition = function(player) |
|||
return minetest.check_player_privs(player:get_player_name(), {home=true}) |
|||
end, |
|||
}) |
|||
|
|||
unified_inventory.register_button("home_gui_go", { |
|||
type = "image", |
|||
image = "ui_gohome_icon.png", |
|||
tooltip = S("Go home"), |
|||
hide_lite=true, |
|||
action = function(player) |
|||
local player_name = player:get_player_name() |
|||
if minetest.check_player_privs(player_name, {home=true}) then |
|||
minetest.sound_play("teleport", |
|||
{to_player=player:get_player_name(), gain = 1.0}) |
|||
unified_inventory.go_home(player) |
|||
else |
|||
minetest.chat_send_player(player_name, |
|||
S("You don't have the \"home\" privilege!")) |
|||
unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name]) |
|||
end |
|||
end, |
|||
condition = function(player) |
|||
return minetest.check_player_privs(player:get_player_name(), {home=true}) |
|||
end, |
|||
}) |
|||
|
|||
unified_inventory.register_button("misc_set_day", { |
|||
type = "image", |
|||
image = "ui_sun_icon.png", |
|||
tooltip = S("Set time to day"), |
|||
hide_lite=true, |
|||
action = function(player) |
|||
local player_name = player:get_player_name() |
|||
if minetest.check_player_privs(player_name, {settime=true}) then |
|||
minetest.sound_play("birds", |
|||
{to_player=player_name, gain = 1.0}) |
|||
minetest.set_timeofday((6000 % 24000) / 24000) |
|||
minetest.chat_send_player(player_name, |
|||
S("Time of day set to 6am")) |
|||
else |
|||
minetest.chat_send_player(player_name, |
|||
S("You don't have the settime privilege!")) |
|||
unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name]) |
|||
end |
|||
end, |
|||
condition = function(player) |
|||
return minetest.check_player_privs(player:get_player_name(), {settime=true}) |
|||
end, |
|||
}) |
|||
|
|||
unified_inventory.register_button("misc_set_night", { |
|||
type = "image", |
|||
image = "ui_moon_icon.png", |
|||
tooltip = S("Set time to night"), |
|||
hide_lite=true, |
|||
action = function(player) |
|||
local player_name = player:get_player_name() |
|||
if minetest.check_player_privs(player_name, {settime=true}) then |
|||
minetest.sound_play("owl", |
|||
{to_player=player_name, gain = 1.0}) |
|||
minetest.set_timeofday((21000 % 24000) / 24000) |
|||
minetest.chat_send_player(player_name, |
|||
S("Time of day set to 9pm")) |
|||
else |
|||
minetest.chat_send_player(player_name, |
|||
S("You don't have the settime privilege!")) |
|||
unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name]) |
|||
end |
|||
end, |
|||
condition = function(player) |
|||
return minetest.check_player_privs(player:get_player_name(), {settime=true}) |
|||
end, |
|||
}) |
|||
|
|||
unified_inventory.register_button("clear_inv", { |
|||
type = "image", |
|||
image = "ui_trash_icon.png", |
|||
tooltip = S("Clear inventory"), |
|||
action = function(player) |
|||
local player_name = player:get_player_name() |
|||
if not unified_inventory.is_creative(player_name) then |
|||
minetest.chat_send_player(player_name, |
|||
S("This button has been disabled outside" |
|||
.." of creative mode to prevent" |
|||
.." accidental inventory trashing." |
|||
.."\nUse the trash slot instead.")) |
|||
unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name]) |
|||
return |
|||
end |
|||
player:get_inventory():set_list("main", {}) |
|||
minetest.chat_send_player(player_name, S('Inventory cleared!')) |
|||
minetest.sound_play("trash_all", |
|||
{to_player=player_name, gain = 1.0}) |
|||
end, |
|||
condition = function(player) |
|||
return unified_inventory.is_creative(player:get_player_name()) |
|||
end, |
|||
}) |
|||
|
|||
unified_inventory.register_page("craft", { |
|||
get_formspec = function(player, perplayer_formspec) |
|||
|
|||
local formspecy = perplayer_formspec.formspec_y |
|||
local formheadery = perplayer_formspec.form_header_y |
|||
|
|||
local player_name = player:get_player_name() |
|||
local formspec = "background[2,"..formspecy..";6,3;ui_crafting_form.png]" |
|||
formspec = formspec.."background[0,"..(formspecy + 3.5)..";8,4;ui_main_inventory.png]" |
|||
formspec = formspec.."label[0,"..formheadery..";" ..F(S("Crafting")).."]" |
|||
formspec = formspec.."listcolors[#00000000;#00000000]" |
|||
formspec = formspec.."list[current_player;craftpreview;6,"..formspecy..";1,1;]" |
|||
formspec = formspec.."list[current_player;craft;2,"..formspecy..";3,3;]" |
|||
if unified_inventory.trash_enabled or unified_inventory.is_creative(player_name) or minetest.get_player_privs(player_name).give then |
|||
formspec = formspec.."label[7,"..(formspecy + 1.5)..";" .. F(S("Trash:")) .. "]" |
|||
formspec = formspec.."background[7,"..(formspecy + 2)..";1,1;ui_single_slot.png]" |
|||
formspec = formspec.."list[detached:trash;main;7,"..(formspecy + 2)..";1,1;]" |
|||
end |
|||
formspec = formspec.."listring[current_name;craft]" |
|||
formspec = formspec.."listring[current_player;main]" |
|||
if unified_inventory.is_creative(player_name) then |
|||
formspec = formspec.."label[0,"..(formspecy + 1.5)..";" .. F(S("Refill:")) .. "]" |
|||
formspec = formspec.."list[detached:"..F(player_name).."refill;main;0,"..(formspecy +2)..";1,1;]" |
|||
end |
|||
return {formspec=formspec} |
|||
end, |
|||
}) |
|||
|
|||
-- stack_image_button(): generate a form button displaying a stack of items |
|||
-- |
|||
-- The specified item may be a group. In that case, the group will be |
|||
-- represented by some item in the group, along with a flag indicating |
|||
-- that it's a group. If the group contains only one item, it will be |
|||
-- treated as if that item had been specified directly. |
|||
|
|||
local function stack_image_button(x, y, w, h, buttonname_prefix, item) |
|||
local name = item:get_name() |
|||
local count = item:get_count() |
|||
local show_is_group = false |
|||
local displayitem = name.." "..count |
|||
local selectitem = name |
|||
if name:sub(1, 6) == "group:" then |
|||
local group_name = name:sub(7) |
|||
local group_item = unified_inventory.get_group_item(group_name) |
|||
show_is_group = not group_item.sole |
|||
displayitem = group_item.item or "unknown" |
|||
selectitem = group_item.sole and displayitem or name |
|||
end |
|||
local label = show_is_group and "G" or "" |
|||
local buttonname = F(buttonname_prefix..unified_inventory.mangle_for_formspec(selectitem)) |
|||
local button = string.format("item_image_button[%f,%f;%f,%f;%s;%s;%s]", |
|||
x, y, w, h, |
|||
F(displayitem), buttonname, label) |
|||
if show_is_group then |
|||
local groupstring, andcount = unified_inventory.extract_groupnames(name) |
|||
local grouptip |
|||
if andcount == 1 then |
|||
grouptip = "Any item belonging to the " .. groupstring .. " group" |
|||
elseif andcount > 1 then |
|||
grouptip = "Any item belonging to the groups " .. groupstring |
|||
end |
|||
grouptip = F(grouptip) |
|||
if andcount >= 1 then |
|||
button = button .. string.format("tooltip[%s;%s]", buttonname, grouptip) |
|||
end |
|||
end |
|||
return button |
|||
end |
|||
|
|||
local recipe_text = { |
|||
recipe = NS("Recipe @1 of @2"), |
|||
usage = NS("Usage @1 of @2"), |
|||
} |
|||
local no_recipe_text = { |
|||
recipe = S("No recipes"), |
|||
usage = S("No usages"), |
|||
} |
|||
local role_text = { |
|||
recipe = S("Result"), |
|||
usage = S("Ingredient"), |
|||
} |
|||
local next_alt_text = { |
|||
recipe = S("Show next recipe"), |
|||
usage = S("Show next usage"), |
|||
} |
|||
local prev_alt_text = { |
|||
recipe = S("Show previous recipe"), |
|||
usage = S("Show previous usage"), |
|||
} |
|||
local other_dir = { |
|||
recipe = "usage", |
|||
usage = "recipe", |
|||
} |
|||
|
|||
unified_inventory.register_page("craftguide", { |
|||
get_formspec = function(player, perplayer_formspec) |
|||
|
|||
local formspecy = perplayer_formspec.formspec_y |
|||
local formheadery = perplayer_formspec.form_header_y |
|||
local craftresultx = perplayer_formspec.craft_result_x |
|||
local craftresulty = perplayer_formspec.craft_result_y |
|||
|
|||
local player_name = player:get_player_name() |
|||
local player_privs = minetest.get_player_privs(player_name) |
|||
local fs = { |
|||
"background[0,"..(formspecy + 3.5)..";8,4;ui_main_inventory.png]", |
|||
"label[0,"..formheadery..";" .. F(S("Crafting Guide")) .. "]", |
|||
"listcolors[#00000000;#00000000]" |
|||
} |
|||
local item_name = unified_inventory.current_item[player_name] |
|||
if not item_name then |
|||
return { formspec = table.concat(fs) } |
|||
end |
|||
|
|||
local item_name_shown |
|||
if minetest.registered_items[item_name] |
|||
and minetest.registered_items[item_name].description then |
|||
item_name_shown = S("@1 (@2)", |
|||
minetest.registered_items[item_name].description, item_name) |
|||
else |
|||
item_name_shown = item_name |
|||
end |
|||
|
|||
local dir = unified_inventory.current_craft_direction[player_name] |
|||
local rdir = dir == "recipe" and "usage" or "recipe" |
|||
|
|||
local crafts = unified_inventory.crafts_for[dir][item_name] |
|||
local alternate = unified_inventory.alternate[player_name] |
|||
local alternates, craft |
|||
if crafts and #crafts > 0 then |
|||
alternates = #crafts |
|||
craft = crafts[alternate] |
|||
end |
|||
local has_give = player_privs.give or unified_inventory.is_creative(player_name) |
|||
|
|||
fs[#fs + 1] = "background[0.5,"..(formspecy + 0.2)..";8,3;ui_craftguide_form.png]" |
|||
fs[#fs + 1] = string.format("textarea[%f,%f;10,1;;%s: %s;]", |
|||
craftresultx, craftresulty, F(role_text[dir]), item_name_shown) |
|||
fs[#fs + 1] = stack_image_button(0, formspecy, 1.1, 1.1, |
|||
"item_button_" .. rdir .. "_", ItemStack(item_name)) |
|||
|
|||
if not craft then |
|||
-- No craft recipes available for this item. |
|||
fs[#fs + 1] = "label[5.5,"..(formspecy + 2.35)..";" |
|||
.. F(no_recipe_text[dir]) .. "]" |
|||
local no_pos = dir == "recipe" and 4.5 or 6.5 |
|||
local item_pos = dir == "recipe" and 6.5 or 4.5 |
|||
fs[#fs + 1] = "image["..no_pos..","..formspecy..";1.1,1.1;ui_no.png]" |
|||
fs[#fs + 1] = stack_image_button(item_pos, formspecy, 1.1, 1.1, |
|||
"item_button_" .. other_dir[dir] .. "_", ItemStack(item_name)) |
|||
if has_give then |
|||
fs[#fs + 1] = "label[0," .. (formspecy + 2.10) .. ";" .. F(S("Give me:")) .. "]" |
|||
.. "button[0, " .. (formspecy + 2.7) .. ";0.6,0.5;craftguide_giveme_1;1]" |
|||
.. "button[0.6," .. (formspecy + 2.7) .. ";0.7,0.5;craftguide_giveme_10;10]" |
|||
.. "button[1.3," .. (formspecy + 2.7) .. ";0.8,0.5;craftguide_giveme_99;99]" |
|||
end |
|||
return { formspec = table.concat(fs) } |
|||
end |
|||
|
|||
local craft_type = unified_inventory.registered_craft_types[craft.type] or |
|||
unified_inventory.craft_type_defaults(craft.type, {}) |
|||
if craft_type.icon then |
|||
fs[#fs + 1] = string.format("image[%f,%f;%f,%f;%s]", |
|||
5.7, (formspecy + 0.05), 0.5, 0.5, craft_type.icon) |
|||
end |
|||
fs[#fs + 1] = "label[5.5,"..(formspecy + 1)..";" .. F(craft_type.description).."]" |
|||
fs[#fs + 1] = stack_image_button(6.5, formspecy, 1.1, 1.1, |
|||
"item_button_usage_", ItemStack(craft.output)) |
|||
|
|||
local display_size = craft_type.dynamic_display_size |
|||
and craft_type.dynamic_display_size(craft) |
|||
or { width = craft_type.width, height = craft_type.height } |
|||
local craft_width = craft_type.get_shaped_craft_width |
|||
and craft_type.get_shaped_craft_width(craft) |
|||
or display_size.width |
|||
|
|||
-- This keeps recipes aligned to the right, |
|||
-- so that they're close to the arrow. |
|||
local xoffset = 5.5 |
|||
-- Offset factor for crafting grids with side length > 4 |
|||
local of = (3/math.max(3, math.max(display_size.width, display_size.height))) |
|||
local od = 0 |
|||
-- Minimum grid size at which size optimazation measures kick in |
|||
local mini_craft_size = 6 |
|||
if display_size.width >= mini_craft_size then |
|||
od = math.max(1, display_size.width - 2) |
|||
xoffset = xoffset - 0.1 |
|||
end |
|||
-- Size modifier factor |
|||
local sf = math.min(1, of * (1.05 + 0.05*od)) |
|||
-- Button size |
|||
local bsize_h = 1.1 * sf |
|||
local bsize_w = bsize_h |
|||
if display_size.width >= mini_craft_size then |
|||
bsize_w = 1.175 * sf |
|||
end |
|||
if (bsize_h > 0.35 and display_size.width) then |
|||
for y = 1, display_size.height do |
|||
for x = 1, display_size.width do |
|||
local item |
|||
if craft and x <= craft_width then |
|||
item = craft.items[(y-1) * craft_width + x] |
|||
end |
|||
-- Flipped x, used to build formspec buttons from right to left |
|||
local fx = display_size.width - (x-1) |
|||
-- x offset, y offset |
|||
local xof = (fx-1) * of + of |
|||
local yof = (y-1) * of + 1 |
|||
if item then |
|||
fs[#fs + 1] = stack_image_button( |
|||
xoffset - xof, formspecy - 1 + yof, bsize_w, bsize_h, |
|||
"item_button_recipe_", |
|||
ItemStack(item)) |
|||
else |
|||
-- Fake buttons just to make grid |
|||
fs[#fs + 1] = string.format("image_button[%f,%f;%f,%f;ui_blank_image.png;;]", |
|||
xoffset - xof, formspecy - 1 + yof, bsize_w, bsize_h) |
|||
end |
|||
end |
|||
end |
|||
else |
|||
-- Error |
|||
fs[#fs + 1] = string.format("label[2,%f;%s]", |
|||
formspecy, F(S("This recipe is too\nlarge to be displayed."))) |
|||
end |
|||
|
|||
if craft_type.uses_crafting_grid and display_size.width <= 3 then |
|||
fs[#fs + 1] = "label[0," .. (formspecy + 0.9) .. ";" .. F(S("To craft grid:")) .. "]" |
|||
.. "button[0, " .. (formspecy + 1.5) .. ";0.6,0.5;craftguide_craft_1;1]" |
|||
.. "button[0.6," .. (formspecy + 1.5) .. ";0.7,0.5;craftguide_craft_10;10]" |
|||
.. "button[1.3," .. (formspecy + 1.5) .. ";0.8,0.5;craftguide_craft_max;" .. F(S("All")) .. "]" |
|||
end |
|||
if has_give then |
|||
fs[#fs + 1] = "label[0," .. (formspecy + 2.1) .. ";" .. F(S("Give me:")) .. "]" |
|||
.. "button[0, " .. (formspecy + 2.7) .. ";0.6,0.5;craftguide_giveme_1;1]" |
|||
.. "button[0.6," .. (formspecy + 2.7) .. ";0.7,0.5;craftguide_giveme_10;10]" |
|||
.. "button[1.3," .. (formspecy + 2.7) .. ";0.8,0.5;craftguide_giveme_99;99]" |
|||
end |
|||
|
|||
if alternates and alternates > 1 then |
|||
fs[#fs + 1] = "label[5.5," .. (formspecy + 1.6) .. ";" |
|||
.. F(S(recipe_text[dir], alternate, alternates)) .. "]" |
|||
.. "image_button[5.5," .. (formspecy + 2) .. ";1,1;ui_left_icon.png;alternate_prev;]" |
|||
.. "image_button[6.5," .. (formspecy + 2) .. ";1,1;ui_right_icon.png;alternate;]" |
|||
.. "tooltip[alternate_prev;" .. F(prev_alt_text[dir]) .. "]" |
|||
.. "tooltip[alternate;" .. F(next_alt_text[dir]) .. "]" |
|||
end |
|||
return { formspec = table.concat(fs) } |
|||
end, |
|||
}) |
|||
|
|||
local function craftguide_giveme(player, formname, fields) |
|||
local player_name = player:get_player_name() |
|||
local player_privs = minetest.get_player_privs(player_name) |
|||
if not player_privs.give and |
|||
not unified_inventory.is_creative(player_name) then |
|||
minetest.log("action", "[unified_inventory] Denied give action to player " .. |
|||
player_name) |
|||
return |
|||
end |
|||
|
|||
local amount |
|||
for k, v in pairs(fields) do |
|||
amount = k:match("craftguide_giveme_(.*)") |
|||
if amount then break end |
|||
end |
|||
|
|||
amount = tonumber(amount) or 0 |
|||
if amount == 0 then return end |
|||
|
|||
local output = unified_inventory.current_item[player_name] |
|||
if (not output) or (output == "") then return end |
|||
|
|||
local player_inv = player:get_inventory() |
|||
|
|||
player_inv:add_item("main", {name = output, count = amount}) |
|||
end |
|||
|
|||
local function craftguide_craft(player, formname, fields) |
|||
local amount |
|||
for k, v in pairs(fields) do |
|||
amount = k:match("craftguide_craft_(.*)") |
|||
if amount then break end |
|||
end |
|||
if not amount then return end |
|||
|
|||
amount = tonumber(amount) or -1 -- fallback for "all" |
|||
if amount == 0 or amount < -1 or amount > 99 then return end |
|||
|
|||
local player_name = player:get_player_name() |
|||
|
|||
local output = unified_inventory.current_item[player_name] or "" |
|||
if output == "" then return end |
|||
|
|||
local crafts = unified_inventory.crafts_for[ |
|||
unified_inventory.current_craft_direction[player_name]][output] or {} |
|||
if #crafts == 0 then return end |
|||
|
|||
local alternate = unified_inventory.alternate[player_name] |
|||
|
|||
local craft = crafts[alternate] |
|||
if craft.width > 3 then return end |
|||
|
|||
unified_inventory.craftguide_match_craft(player, "main", "craft", craft, amount) |
|||
|
|||
unified_inventory.set_inventory_formspec(player, "craft") |
|||
end |
|||
|
|||
minetest.register_on_player_receive_fields(function(player, formname, fields) |
|||
if formname ~= "" then |
|||
return |
|||
end |
|||
|
|||
for k, v in pairs(fields) do |
|||
if k:match("craftguide_craft_") then |
|||
craftguide_craft(player, formname, fields) |
|||
return |
|||
end |
|||
if k:match("craftguide_giveme_") then |
|||
craftguide_giveme(player, formname, fields) |
|||
return |
|||
end |
|||
end |
|||
end) |
@ -0,0 +1,247 @@ |
|||
local S = function (str) return str end |
|||
local F = minetest.formspec_escape |
|||
|
|||
local hud_colors = { |
|||
{"#FFFFFF", 0xFFFFFF, S("White")}, |
|||
{"#DBBB00", 0xf1d32c, S("Yellow")}, |
|||
{"#DD0000", 0xDD0000, S("Red")}, |
|||
{"#2cf136", 0x2cf136, S("Green")}, |
|||
{"#2c4df1", 0x2c4df1, S("Blue")}, |
|||
} |
|||
|
|||
local hud_colors_max = #hud_colors |
|||
|
|||
-- Stores temporary player data (persists until player leaves) |
|||
local waypoints_temp = {} |
|||
|
|||
unified_inventory.register_page("waypoints", { |
|||
get_formspec = function(player) |
|||
local player_name = player:get_player_name() |
|||
|
|||
-- build a "fake" temp entry if the server took too long |
|||
-- during sign-on and returned an empty entry |
|||
if not waypoints_temp[player_name] then waypoints_temp[player_name] = {hud = 1} end |
|||
|
|||
local waypoints = datastorage.get(player_name, "waypoints") |
|||
local formspec = "background[0,4.5;8,4;ui_main_inventory.png]" .. |
|||
"image[0,0;1,1;ui_waypoints_icon.png]" .. |
|||
"label[1,0;" .. F(S("Waypoints")) .. "]" |
|||
|
|||
-- Tabs buttons: |
|||
for i = 1, 5, 1 do |
|||
formspec = formspec .. |
|||
"image_button[0.0," .. 0.2 + i * 0.7 .. ";.8,.8;" .. |
|||
(i == waypoints.selected and "ui_blue_icon_background.png^" or "") .. |
|||
"ui_" .. i .. "_icon.png;" .. |
|||
"select_waypoint" .. i .. ";]" .. |
|||
"tooltip[select_waypoint" .. i .. ";" |
|||
.. S("Select Waypoint #@1", i).."]" |
|||
end |
|||
|
|||
local i = waypoints.selected or 1 |
|||
local waypoint = waypoints[i] or {} |
|||
local temp = waypoints_temp[player_name][i] or {} |
|||
local default_name = S("Waypoint @1", i) |
|||
|
|||
-- Main buttons: |
|||
formspec = formspec .. |
|||
"image_button[4.5,3.7;.8,.8;".. |
|||
"ui_waypoint_set_icon.png;".. |
|||
"set_waypoint"..i..";]".. |
|||
"tooltip[set_waypoint" .. i .. ";" |
|||
.. F(S("Set waypoint to current location")).."]" |
|||
|
|||
formspec = formspec .. |
|||
"image_button[5.2,3.7;.8,.8;".. |
|||
(waypoint.active and "ui_on_icon.png" or "ui_off_icon.png")..";".. |
|||
"toggle_waypoint"..i..";]".. |
|||
"tooltip[toggle_waypoint" .. i .. ";" |
|||
.. F(S("Make waypoint @1", |
|||
waypoint.active and S("invisible") or S("visible"))).."]" |
|||
|
|||
formspec = formspec .. |
|||
"image_button[5.9,3.7;.8,.8;".. |
|||
(waypoint.display_pos and "ui_green_icon_background.png" or "ui_red_icon_background.png").."^ui_xyz_icon.png;".. |
|||
"toggle_display_pos" .. i .. ";]".. |
|||
"tooltip[toggle_display_pos" .. i .. ";" |
|||
.. F(S("@1 display of waypoint coordinates", |
|||
waypoint.display_pos and S("Disable") or S("Enable"))) .."]" |
|||
|
|||
formspec = formspec .. |
|||
"image_button[6.6,3.7;.8,.8;".. |
|||
"ui_circular_arrows_icon.png;".. |
|||
"toggle_color"..i..";]".. |
|||
"tooltip[toggle_color" .. i .. ";" |
|||
.. F(S("Change color of waypoint display")).."]" |
|||
|
|||
formspec = formspec .. |
|||
"image_button[7.3,3.7;.8,.8;".. |
|||
"ui_pencil_icon.png;".. |
|||
"rename_waypoint"..i..";]".. |
|||
"tooltip[rename_waypoint" .. i .. ";" |
|||
.. F(S("Edit waypoint name")).."]" |
|||
|
|||
-- Waypoint's info: |
|||
if waypoint.active then |
|||
formspec = formspec .. "label[1,0.8;"..F(S("Waypoint active")).."]" |
|||
else |
|||
formspec = formspec .. "label[1,0.8;"..F(S("Waypoint inactive")).."]" |
|||
end |
|||
|
|||
if temp.edit then |
|||
formspec = formspec .. |
|||
"field[1.3,3.2;6,.8;rename_box" .. i .. ";;" |
|||
..(waypoint.name or default_name).."]" .. |
|||
"image_button[7.3,2.9;.8,.8;".. |
|||
"ui_ok_icon.png;".. |
|||
"confirm_rename"..i.. ";]".. |
|||
"tooltip[confirm_rename" .. i .. ";" |
|||
.. F(S("Finish editing")).."]" |
|||
end |
|||
|
|||
formspec = formspec .. "label[1,1.3;"..F(S("World position"))..": " .. |
|||
minetest.pos_to_string(waypoint.world_pos or vector.new()) .. "]" .. |
|||
"label[1,1.8;"..F(S("Name"))..": ".. (waypoint.name or default_name) .. "]" .. |
|||
"label[1,2.3;"..F(S("HUD text color"))..": " .. |
|||
hud_colors[waypoint.color or 1][3] .. "]" |
|||
|
|||
return {formspec=formspec} |
|||
end, |
|||
}) |
|||
|
|||
unified_inventory.register_button("waypoints", { |
|||
type = "image", |
|||
image = "ui_waypoints_icon.png", |
|||
tooltip = S("Waypoints"), |
|||
hide_lite=true |
|||
}) |
|||
|
|||
local function update_hud(player, waypoints, temp, i) |
|||
local waypoint = waypoints[i] |
|||
if not waypoint then return end |
|||
temp[i] = temp[i] or {} |
|||
temp = temp[i] |
|||
local pos = waypoint.world_pos or vector.new() |
|||
local name |
|||
if waypoint.display_pos then |
|||
name = minetest.pos_to_string(pos) |
|||
if waypoint.name then |
|||
name = name..", "..waypoint.name |
|||
end |
|||
else |
|||
name = waypoint.name or "Waypoint "..i |
|||
end |
|||
if temp.hud then |
|||
player:hud_remove(temp.hud) |
|||
end |
|||
if waypoint.active then |
|||
temp.hud = player:hud_add({ |
|||
hud_elem_type = "waypoint", |
|||
number = hud_colors[waypoint.color or 1][2] , |
|||
name = name, |
|||
text = "m", |
|||
world_pos = pos |
|||
}) |
|||
else |
|||
temp.hud = nil |
|||
end |
|||
end |
|||
|
|||
minetest.register_on_player_receive_fields(function(player, formname, fields) |
|||
if formname ~= "" then return end |
|||
|
|||
local player_name = player:get_player_name() |
|||
local update_formspec = false |
|||
local need_update_hud = false |
|||
local hit = false |
|||
|
|||
local waypoints = datastorage.get(player_name, "waypoints") |
|||
local temp = waypoints_temp[player_name] |
|||
for i = 1, 5, 1 do |
|||
if fields["select_waypoint"..i] then |
|||
hit = true |
|||
waypoints.selected = i |
|||
update_formspec = true |
|||
end |
|||
|
|||
if fields["toggle_waypoint"..i] then |
|||
hit = true |
|||
waypoints[i] = waypoints[i] or {} |
|||
waypoints[i].active = not (waypoints[i].active) |
|||
need_update_hud = true |
|||
update_formspec = true |
|||
end |
|||
|
|||
if fields["set_waypoint"..i] then |
|||
hit = true |
|||
local pos = player:get_pos() |
|||
pos.x = math.floor(pos.x) |
|||
pos.y = math.floor(pos.y) |
|||
pos.z = math.floor(pos.z) |
|||
waypoints[i] = waypoints[i] or {} |
|||
waypoints[i].world_pos = pos |
|||
need_update_hud = true |
|||
update_formspec = true |
|||
end |
|||
|
|||
if fields["rename_waypoint"..i] then |
|||
hit = true |
|||
temp[i] = temp[i] or {} |
|||
temp[i].edit = true |
|||
update_formspec = true |
|||
end |
|||
|
|||
if fields["toggle_display_pos"..i] then |
|||
hit = true |
|||
waypoints[i] = waypoints[i] or {} |
|||
waypoints[i].display_pos = not waypoints[i].display_pos |
|||
need_update_hud = true |
|||
update_formspec = true |
|||
end |
|||
|
|||
if fields["toggle_color"..i] then |
|||
hit = true |
|||
waypoints[i] = waypoints[i] or {} |
|||
local color = waypoints[i].color or 1 |
|||
color = color + 1 |
|||
if color > hud_colors_max then |
|||
color = 1 |
|||
end |
|||
waypoints[i].color = color |
|||
need_update_hud = true |
|||
update_formspec = true |
|||
end |
|||
|
|||
if fields["confirm_rename"..i] then |
|||
hit = true |
|||
waypoints[i] = waypoints[i] or {} |
|||
temp[i].edit = false |
|||
waypoints[i].name = fields["rename_box"..i] |
|||
need_update_hud = true |
|||
update_formspec = true |
|||
end |
|||
if need_update_hud then |
|||
update_hud(player, waypoints, temp, i) |
|||
end |
|||
if update_formspec then |
|||
unified_inventory.set_inventory_formspec(player, "waypoints") |
|||
end |
|||
if hit then return end |
|||
end |
|||
end) |
|||
|
|||
|
|||
minetest.register_on_joinplayer(function(player) |
|||
local player_name = player:get_player_name() |
|||
local waypoints = datastorage.get(player_name, "waypoints") |
|||
local temp = {} |
|||
waypoints_temp[player_name] = temp |
|||
for i = 1, 5 do |
|||
update_hud(player, waypoints, temp, i) |
|||
end |
|||
end) |
|||
|
|||
minetest.register_on_leaveplayer(function(player) |
|||
waypoints_temp[player:get_player_name()] = nil |
|||
end) |
|||
|
@ -0,0 +1 @@ |
|||
See "oldcoder.txt". |
@ -0,0 +1,19 @@ |
|||
Unified Inventory for Minetest |
|||
Copyright (C) 2012-2014 Maciej Kasatkin (RealBadAngel) |
|||
|
|||
This library is free software; you can redistribute it and/or |
|||
modify it under the terms of the GNU Library General Public |
|||
License as published by the Free Software Foundation; either |
|||
version 2 of the License, or (at your option) any later version. |
|||
|
|||
This library 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 |
|||
Library General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Library General Public |
|||
License along with this library; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
Contact information: |
|||
Examine a git patch to get the contributor's email address. |
@ -0,0 +1,96 @@ |
|||
# Unified Inventory |
|||
|
|||
Unified Inventory replaces the default survival and creative inventory. |
|||
|
|||
|
|||
## Features |
|||
|
|||
* Node, item and tool browser |
|||
* Crafting guide |
|||
* Can copy the recipe to the crafting grid |
|||
* Recipe search function by ingredients |
|||
* Up to four bags with up to 24 slots each |
|||
* Home function to teleport |
|||
* Trash slot |
|||
* Lite mode: reduces the item browser width |
|||
* Mod API for modders: see [mod_api.txt](doc/mod_api.txt) |
|||
* Setting-determinated features: see [settingtypes.txt](settingtypes.txt) |
|||
|
|||
|
|||
## Requirements |
|||
|
|||
* Minetest 5.0.0+ since commit 4403b69 |
|||
* Minetest 0.4.16+ prior commit 4403b69 |
|||
|
|||
|
|||
# Licenses |
|||
|
|||
Copyright (C) 2012-2014 Maciej Kasatkin (RealBadAngel) |
|||
|
|||
Copyright (C) 2012-? Various minetest-mods contributors |
|||
|
|||
|
|||
## Code |
|||
|
|||
GNU LGPLv2+, see [license notice](LICENSE.txt) |
|||
|
|||
|
|||
## Textures |
|||
|
|||
VanessaE: (CC-BY-4.0) |
|||
|
|||
* `ui_group.png` |
|||
|
|||
Tango Project: (Public Domain, CC-BY-4.0) |
|||
|
|||
* [`ui_reset_icon.png`](https://commons.wikimedia.org/wiki/File:Edit-clear.svg) |
|||
* [`ui_doubleleft_icon.png`](http://commons.wikimedia.org/wiki/File:Media-seek-backward.svg) |
|||
* [`ui_doubleright_icon.png`](http://commons.wikimedia.org/wiki/File:Media-seek-forward.svg) |
|||
* [`ui_left_icon.png` / `ui_right_icon.png`](http://commons.wikimedia.org/wiki/File:Media-playback-start.svg) |
|||
* [`ui_skip_backward_icon.png`](http://commons.wikimedia.org/wiki/File:Media-skip-backward.svg) |
|||
* [`ui_skip_forward_icon.png`](http://commons.wikimedia.org/wiki/File:Media-skip-forward.svg) |
|||
|
|||
From http://www.clker.com (Public Domain, CC-BY-4.0): |
|||
|
|||
* [`bags_small.png`](http://www.clker.com/clipart-moneybag-empty.html) |
|||
* [`bags_medium.png`](http://www.clker.com/clipart-backpack-1.html) |
|||
* [`bags_large.png` / `ui_bags_icon.png`](http://www.clker.com/clipart-backpack-green-brown.html) |
|||
* `ui_trash_icon.png`: <http://www.clker.com/clipart-29090.html> and <http://www.clker.com/clipart-trash.html> |
|||
* [`ui_search_icon.png`](http://www.clker.com/clipart-24887.html) |
|||
* [`ui_off_icon.png` / `ui_on_icon.png`](http://www.clker.com/clipart-on-off-switches.html) |
|||
* [`ui_waypoints_icon.png`](http://www.clker.com/clipart-map-pin-red.html) |
|||
* [`ui_circular_arrows_icon.png`](http://www.clker.com/clipart-circular-arrow-pattern.html) |
|||
* [`ui_pencil_icon.pnc`](http://www.clker.com/clipart-2256.html) |
|||
* [`ui_waypoint_set_icon.png`](http://www.clker.com/clipart-larger-flag.html) |
|||
|
|||
Everaldo Coelho (YellowIcon) (LGPL v2.1+): |
|||
|
|||
* [`ui_craftguide_icon.png` / `ui_craft_icon.png`](http://commons.wikimedia.org/wiki/File:Advancedsettings.png) |
|||
|
|||
Gregory H. Revera: (CC-BY-SA 3.0) |
|||
|
|||
* [`ui_moon_icon.png`](http://commons.wikimedia.org/wiki/File:FullMoon2010.jpg) |
|||
|
|||
Thomas Bresson: (CC-BY 3.0) |
|||
|
|||
* [`ui_sun_icon.png`](http://commons.wikimedia.org/wiki/File:2012-10-13_15-29-35-sun.jpg) |
|||
|
|||
Fibonacci: (Public domain, CC-BY 4.0) |
|||
|
|||
* [`ui_xyz_off_icon.png`](http://commons.wikimedia.org/wiki/File:No_sign.svg) |
|||
|
|||
Gregory Maxwell: (Public domain, CC-BY 4.0) |
|||
|
|||
* [`ui_ok_icon.png`](http://commons.wikimedia.org/wiki/File:Yes_check.svg) |
|||
|
|||
Adrien Facélina: (LGPL v2.1+) |
|||
|
|||
* [`inventory_plus_worldedit_gui.png`](http://commons.wikimedia.org/wiki/File:Erioll_world_2.svg) |
|||
|
|||
Other files from Wikimedia Commons: |
|||
|
|||
* [`ui_gohome_icon.png` / `ui_home_icon.png` / `ui_sethome_icon.png`](http://commons.wikimedia.org/wiki/File:Home_256x256.png) (GPL v2+) |
|||
|
|||
RealBadAngel: (CC-BY-4.0) |
|||
|
|||
* Everything else. |
@ -0,0 +1,321 @@ |
|||
local S = function (str) return str end |
|||
local F = minetest.formspec_escape |
|||
|
|||
-- Create detached creative inventory after loading all mods |
|||
minetest.after(0.01, function() |
|||
local rev_aliases = {} |
|||
for source, target in pairs(minetest.registered_aliases) do |
|||
if not rev_aliases[target] then rev_aliases[target] = {} end |
|||
table.insert(rev_aliases[target], source) |
|||
end |
|||
unified_inventory.items_list = {} |
|||
for name, def in pairs(minetest.registered_items) do |
|||
if (not def.groups.not_in_creative_inventory or |
|||
def.groups.not_in_creative_inventory == 0) and |
|||
def.description and def.description ~= "" then |
|||
table.insert(unified_inventory.items_list, name) |
|||
local all_names = rev_aliases[name] or {} |
|||
table.insert(all_names, name) |
|||
for _, name in ipairs(all_names) do |
|||
local recipes = minetest.get_all_craft_recipes(name) |
|||
if recipes then |
|||
for _, recipe in ipairs(recipes) do |
|||
|
|||
local unknowns |
|||
|
|||
for _,chk in pairs(recipe.items) do |
|||
local groupchk = string.find(chk, "group:") |
|||
if (not groupchk and not minetest.registered_items[chk]) |
|||
or (groupchk and not unified_inventory.get_group_item(string.gsub(chk, "group:", "")).item) |
|||
or minetest.get_item_group(chk, "not_in_craft_guide") ~= 0 then |
|||
unknowns = true |
|||
end |
|||
end |
|||
|
|||
if not unknowns then |
|||
unified_inventory.register_craft(recipe) |
|||
end |
|||
end |
|||
end |
|||
end |
|||
end |
|||
end |
|||
table.sort(unified_inventory.items_list) |
|||
unified_inventory.items_list_size = #unified_inventory.items_list |
|||
print("Unified Inventory. inventory size: "..unified_inventory.items_list_size) |
|||
for _, name in ipairs(unified_inventory.items_list) do |
|||
local def = minetest.registered_items[name] |
|||
-- Simple drops |
|||
if type(def.drop) == "string" then |
|||
local dstack = ItemStack(def.drop) |
|||
if not dstack:is_empty() and dstack:get_name() ~= name then |
|||
unified_inventory.register_craft({ |
|||
type = "digging", |
|||
items = {name}, |
|||
output = def.drop, |
|||
width = 0, |
|||
}) |
|||
|
|||
end |
|||
-- Complex drops. Yes, it's really complex! |
|||
elseif type(def.drop) == "table" then |
|||
--[[ Extract single items from the table and save them into dedicated tables |
|||
to register them later, in order to avoid duplicates. These tables counts |
|||
the total number of guaranteed drops and drops by chance (“maybes”) for each item. |
|||
For “maybes”, the final count is the theoretical maximum number of items, not |
|||
neccessarily the actual drop count. ]] |
|||
local drop_guaranteed = {} |
|||
local drop_maybe = {} |
|||
-- This is for catching an obscure corner case: If the top items table has |
|||
-- only items with rarity = 1, but max_items is set, then only the first |
|||
-- max_items will be part of the drop, any later entries are logically |
|||
-- impossible, so this variable is for keeping track of this |
|||
local max_items_left = def.drop.max_items |
|||
-- For checking whether we still encountered only guaranteed only so far; |
|||
-- for the first “maybe” item it will become false which will cause ALL |
|||
-- later items to be considered “maybes”. |
|||
-- A common idiom is: |
|||
-- { max_items 1, { items = { |
|||
-- { items={"example:1"}, rarity = 5 }, |
|||
-- { items={"example:2"}, rarity = 1 }, }}} |
|||
-- example:2 must be considered a “maybe” because max_items is set and it |
|||
-- appears after a “maybe” |
|||
local max_start = true |
|||
-- Let's iterate through the items madness! |
|||
-- Handle invalid drop entries gracefully. |
|||
local drop_items = def.drop.items or { } |
|||
for i=1,#drop_items do |
|||
if max_items_left ~= nil and max_items_left <= 0 then break end |
|||
local itit = drop_items[i] |
|||
for j=1,#itit.items do |
|||
local dstack = ItemStack(itit.items[j]) |
|||
if not dstack:is_empty() and dstack:get_name() ~= name then |
|||
local dname = dstack:get_name() |
|||
local dcount = dstack:get_count() |
|||
-- Guaranteed drops AND we are not yet in “maybe mode” |
|||
if #itit.items == 1 and itit.rarity == 1 and max_start then |
|||
if drop_guaranteed[dname] == nil then |
|||
drop_guaranteed[dname] = 0 |
|||
end |
|||
drop_guaranteed[dname] = drop_guaranteed[dname] + dcount |
|||
|
|||
if max_items_left ~= nil then |
|||
max_items_left = max_items_left - 1 |
|||
if max_items_left <= 0 then break end |
|||
end |
|||
-- Drop was a “maybe” |
|||
else |
|||
if max_items_left ~= nil then max_start = false end |
|||
if drop_maybe[dname] == nil then |
|||
drop_maybe[dname] = 0 |
|||
end |
|||
drop_maybe[dname] = drop_maybe[dname] + dcount |
|||
end |
|||
end |
|||
end |
|||
end |
|||
for itemstring, count in pairs(drop_guaranteed) do |
|||
unified_inventory.register_craft({ |
|||
type = "digging", |
|||
items = {name}, |
|||
output = itemstring .. " " .. count, |
|||
width = 0, |
|||
}) |
|||
end |
|||
for itemstring, count in pairs(drop_maybe) do |
|||
unified_inventory.register_craft({ |
|||
type = "digging_chance", |
|||
items = {name}, |
|||
output = itemstring .. " " .. count, |
|||
width = 0, |
|||
}) |
|||
end |
|||
end |
|||
end |
|||
for _, recipes in pairs(unified_inventory.crafts_for.recipe) do |
|||
for _, recipe in ipairs(recipes) do |
|||
local ingredient_items = {} |
|||
for _, spec in pairs(recipe.items) do |
|||
local matches_spec = unified_inventory.canonical_item_spec_matcher(spec) |
|||
for _, name in ipairs(unified_inventory.items_list) do |
|||
if matches_spec(name) then |
|||
ingredient_items[name] = true |
|||
end |
|||
end |
|||
end |
|||
for name, _ in pairs(ingredient_items) do |
|||
if unified_inventory.crafts_for.usage[name] == nil then |
|||
unified_inventory.crafts_for.usage[name] = {} |
|||
end |
|||
table.insert(unified_inventory.crafts_for.usage[name], recipe) |
|||
end |
|||
end |
|||
end |
|||
end) |
|||
|
|||
|
|||
-- load_home |
|||
local function load_home() |
|||
local input = io.open(unified_inventory.home_filename, "r") |
|||
if not input then |
|||
unified_inventory.home_pos = {} |
|||
return |
|||
end |
|||
while true do |
|||
local x = input:read("*n") |
|||
if not x then break end |
|||
local y = input:read("*n") |
|||
local z = input:read("*n") |
|||
local name = input:read("*l") |
|||
unified_inventory.home_pos[name:sub(2)] = {x = x, y = y, z = z} |
|||
end |
|||
io.close(input) |
|||
end |
|||
load_home() |
|||
|
|||
function unified_inventory.set_home(player, pos) |
|||
|
|||
local ds = unified_inventory.disable_set |
|||
if ds ~= nil and ds then return end |
|||
|
|||
local player_name = player:get_player_name() |
|||
unified_inventory.home_pos[player_name] = vector.round(pos) |
|||
-- save the home data from the table to the file |
|||
local output = io.open(unified_inventory.home_filename, "w") |
|||
for k, v in pairs(unified_inventory.home_pos) do |
|||
output:write(v.x.." "..v.y.." "..v.z.." "..k.."\n") |
|||
end |
|||
io.close(output) |
|||
|
|||
-- RJK: |
|||
if _G ["ocsethome"] ~= nil and |
|||
ocsethome ~= nil and |
|||
ocsethome.sethome ~= nil then |
|||
|
|||
unified_inventory.disable_set = true |
|||
ocsethome.sethome (player_name, "1") |
|||
unified_inventory.disable_set = false |
|||
end |
|||
end |
|||
|
|||
function unified_inventory.go_home(player) |
|||
local pos = unified_inventory.home_pos[player:get_player_name()] |
|||
if pos then |
|||
player:set_pos(pos) |
|||
end |
|||
end |
|||
|
|||
-- register_craft |
|||
function unified_inventory.register_craft(options) |
|||
if not options.output then |
|||
return |
|||
end |
|||
local itemstack = ItemStack(options.output) |
|||
if itemstack:is_empty() then |
|||
return |
|||
end |
|||
if options.type == "normal" and options.width == 0 then |
|||
options = { type = "shapeless", items = options.items, output = options.output, width = 0 } |
|||
end |
|||
if not unified_inventory.crafts_for.recipe[itemstack:get_name()] then |
|||
unified_inventory.crafts_for.recipe[itemstack:get_name()] = {} |
|||
end |
|||
table.insert(unified_inventory.crafts_for.recipe[itemstack:get_name()],options) |
|||
end |
|||
|
|||
|
|||
local craft_type_defaults = { |
|||
width = 3, |
|||
height = 3, |
|||
uses_crafting_grid = false, |
|||
} |
|||
|
|||
|
|||
function unified_inventory.craft_type_defaults(name, options) |
|||
if not options.description then |
|||
options.description = name |
|||
end |
|||
setmetatable(options, {__index = craft_type_defaults}) |
|||
return options |
|||
end |
|||
|
|||
|
|||
function unified_inventory.register_craft_type(name, options) |
|||
unified_inventory.registered_craft_types[name] = |
|||
unified_inventory.craft_type_defaults(name, options) |
|||
end |
|||
|
|||
|
|||
unified_inventory.register_craft_type("normal", { |
|||
description = F(S("Crafting")), |
|||
icon = "ui_craftgrid_icon.png", |
|||
width = 3, |
|||
height = 3, |
|||
get_shaped_craft_width = function (craft) return craft.width end, |
|||
dynamic_display_size = function (craft) |
|||
local w = craft.width |
|||
local h = math.ceil(table.maxn(craft.items) / craft.width) |
|||
local g = w < h and h or w |
|||
return { width = g, height = g } |
|||
end, |
|||
uses_crafting_grid = true, |
|||
}) |
|||
|
|||
|
|||
unified_inventory.register_craft_type("shapeless", { |
|||
description = F(S("Mixing")), |
|||
icon = "ui_craftgrid_icon.png", |
|||
width = 3, |
|||
height = 3, |
|||
dynamic_display_size = function (craft) |
|||
local maxn = table.maxn(craft.items) |
|||
local g = 1 |
|||
while g*g < maxn do g = g + 1 end |
|||
return { width = g, height = g } |
|||
end, |
|||
uses_crafting_grid = true, |
|||
}) |
|||
|
|||
|
|||
unified_inventory.register_craft_type("cooking", { |
|||
description = F(S("Cooking")), |
|||
icon = "default_furnace_front.png", |
|||
width = 1, |
|||
height = 1, |
|||
}) |
|||
|
|||
|
|||
unified_inventory.register_craft_type("digging", { |
|||
description = F(S("Digging")), |
|||
icon = "default_tool_steelpick.png", |
|||
width = 1, |
|||
height = 1, |
|||
}) |
|||
|
|||
unified_inventory.register_craft_type("digging_chance", { |
|||
description = "Digging (by chance)", |
|||
icon = "default_tool_steelpick.png^[transformFY.png", |
|||
width = 1, |
|||
height = 1, |
|||
}) |
|||
|
|||
function unified_inventory.register_page(name, def) |
|||
unified_inventory.pages[name] = def |
|||
end |
|||
|
|||
|
|||
function unified_inventory.register_button(name, def) |
|||
if not def.action then |
|||
def.action = function(player) |
|||
unified_inventory.set_inventory_formspec(player, name) |
|||
end |
|||
end |
|||
def.name = name |
|||
table.insert(unified_inventory.buttons, def) |
|||
end |
|||
|
|||
|
|||
function unified_inventory.is_creative(playername) |
|||
return minetest.check_player_privs(playername, {creative=true}) |
|||
or minetest.settings:get_bool("creative_mode") |
|||
end |
@ -0,0 +1,6 @@ |
|||
default |
|||
creative? |
|||
sfinv? |
|||
datastorage? |
|||
farming? |
|||
ocsethome? |
@ -0,0 +1,127 @@ |
|||
local S = function (str) return str end |
|||
|
|||
function unified_inventory.canonical_item_spec_matcher(spec) |
|||
local specname = ItemStack(spec):get_name() |
|||
if specname:sub(1, 6) ~= "group:" then |
|||
return function (itemname) |
|||
return itemname == specname |
|||
end |
|||
end |
|||
|
|||
local group_names = specname:sub(7):split(",") |
|||
return function (itemname) |
|||
local itemdef = minetest.registered_items[itemname] |
|||
for _, group_name in ipairs(group_names) do |
|||
if (itemdef.groups[group_name] or 0) == 0 then |
|||
return false |
|||
end |
|||
end |
|||
return true |
|||
end |
|||
end |
|||
|
|||
function unified_inventory.item_matches_spec(item, spec) |
|||
local itemname = ItemStack(item):get_name() |
|||
return unified_inventory.canonical_item_spec_matcher(spec)(itemname) |
|||
end |
|||
|
|||
function unified_inventory.extract_groupnames(groupname) |
|||
local specname = ItemStack(groupname):get_name() |
|||
if specname:sub(1, 6) ~= "group:" then |
|||
return nil, 0 |
|||
end |
|||
local group_names = specname:sub(7):split(",") |
|||
return table.concat(group_names, S(" and ")), #group_names |
|||
end |
|||
|
|||
unified_inventory.registered_group_items = { |
|||
mesecon_conductor_craftable = "mesecons:wire_00000000_off", |
|||
stone = "default:cobble", |
|||
wood = "default:wood", |
|||
book = "default:book", |
|||
sand = "default:sand", |
|||
leaves = "default:leaves", |
|||
tree = "default:tree", |
|||
vessel = "vessels:glass_bottle", |
|||
wool = "wool:white", |
|||
} |
|||
|
|||
function unified_inventory.register_group_item(groupname, itemname) |
|||
unified_inventory.registered_group_items[groupname] = itemname |
|||
end |
|||
|
|||
|
|||
-- This is used when displaying craft recipes, where an ingredient is |
|||
-- specified by group rather than as a specific item. A single-item group |
|||
-- is represented by that item, with the single-item status signalled |
|||
-- in the "sole" field. If the group contains no items at all, the item |
|||
-- field will be nil. |
|||
-- |
|||
-- Within a multiple-item group, we prefer to use an item that has the |
|||
-- same specific name as the group, and if there are more than one of |
|||
-- those items we prefer the one registered for the group by a mod. |
|||
-- Among equally-preferred items, we just pick the one with the |
|||
-- lexicographically earliest name. |
|||
-- |
|||
-- The parameter to this function isn't just a single group name. |
|||
-- It may be a comma-separated list of group names. This is really a |
|||
-- "group:..." ingredient specification, minus the "group:" prefix. |
|||
|
|||
local function compute_group_item(group_name_list) |
|||
local group_names = group_name_list:split(",") |
|||
local candidate_items = {} |
|||
for itemname, itemdef in pairs(minetest.registered_items) do |
|||
if (itemdef.groups.not_in_creative_inventory or 0) == 0 then |
|||
local all = true |
|||
for _, group_name in ipairs(group_names) do |
|||
if (itemdef.groups[group_name] or 0) == 0 then |
|||
all = false |
|||
end |
|||
end |
|||
if all then table.insert(candidate_items, itemname) end |
|||
end |
|||
end |
|||
local num_candidates = #candidate_items |
|||
if num_candidates == 0 then |
|||
return {sole = true} |
|||
elseif num_candidates == 1 then |
|||
return {item = candidate_items[1], sole = true} |
|||
end |
|||
local is_group = {} |
|||
local registered_rep = {} |
|||
for _, group_name in ipairs(group_names) do |
|||
is_group[group_name] = true |
|||
local rep = unified_inventory.registered_group_items[group_name] |
|||
if rep then registered_rep[rep] = true end |
|||
end |
|||
local bestitem = "" |
|||
local bestpref = 0 |
|||
for _, item in ipairs(candidate_items) do |
|||
local pref |
|||
if registered_rep[item] then |
|||
pref = 4 |
|||
elseif string.sub(item, 1, 8) == "default:" and is_group[string.sub(item, 9)] then |
|||
pref = 3 |
|||
elseif is_group[item:gsub("^[^:]*:", "")] then |
|||
pref = 2 |
|||
else |
|||
pref = 1 |
|||
end |
|||
if pref > bestpref or (pref == bestpref and item < bestitem) then |
|||
bestitem = item |
|||
bestpref = pref |
|||
end |
|||
end |
|||
return {item = bestitem, sole = false} |
|||
end |
|||
|
|||
|
|||
local group_item_cache = {} |
|||
|
|||
function unified_inventory.get_group_item(group_name) |
|||
if not group_item_cache[group_name] then |
|||
group_item_cache[group_name] = compute_group_item(group_name) |
|||
end |
|||
return group_item_cache[group_name] |
|||
end |
|||
|
@ -0,0 +1,375 @@ |
|||
local S = function (str) return str end |
|||
local F = minetest.formspec_escape |
|||
|
|||
-- This pair of encoding functions is used where variable text must go in |
|||
-- button names, where the text might contain formspec metacharacters. |
|||
-- We can escape button names for the formspec, to avoid screwing up |
|||
-- form structure overall, but they then don't get de-escaped, and so |
|||
-- the input we get back from the button contains the formspec escaping. |
|||
-- This is a game engine bug, and in the anticipation that it might be |
|||
-- fixed some day we don't want to rely on it. So for safety we apply |
|||
-- an encoding that avoids all formspec metacharacters. |
|||
function unified_inventory.mangle_for_formspec(str) |
|||
return string.gsub(str, "([^A-Za-z0-9])", function (c) return string.format("_%d_", string.byte(c)) end) |
|||
end |
|||
function unified_inventory.demangle_for_formspec(str) |
|||
return string.gsub(str, "_([0-9]+)_", function (v) return string.char(v) end) |
|||
end |
|||
|
|||
function unified_inventory.get_per_player_formspec(player_name) |
|||
local lite = unified_inventory.lite_mode and not minetest.check_player_privs(player_name, {ui_full=true}) |
|||
|
|||
local ui = {} |
|||
ui.pagecols = unified_inventory.pagecols |
|||
ui.pagerows = unified_inventory.pagerows |
|||
ui.page_y = unified_inventory.page_y |
|||
ui.formspec_y = unified_inventory.formspec_y |
|||
ui.main_button_x = unified_inventory.main_button_x |
|||
ui.main_button_y = unified_inventory.main_button_y |
|||
ui.craft_result_x = unified_inventory.craft_result_x |
|||
ui.craft_result_y = unified_inventory.craft_result_y |
|||
ui.form_header_y = unified_inventory.form_header_y |
|||
|
|||
if lite then |
|||
ui.pagecols = 4 |
|||
ui.pagerows = 6 |
|||
ui.page_y = 0.25 |
|||
ui.formspec_y = 0.47 |
|||
ui.main_button_x = 8.2 |
|||
ui.main_button_y = 6.5 |
|||
ui.craft_result_x = 2.8 |
|||
ui.craft_result_y = 3.4 |
|||
ui.form_header_y = -0.1 |
|||
end |
|||
|
|||
ui.items_per_page = ui.pagecols * ui.pagerows |
|||
return ui, lite |
|||
end |
|||
|
|||
function unified_inventory.get_formspec(player, page) |
|||
|
|||
if not player then |
|||
return "" |
|||
end |
|||
|
|||
local player_name = player:get_player_name() |
|||
local ui_peruser,draw_lite_mode = unified_inventory.get_per_player_formspec(player_name) |
|||
|
|||
unified_inventory.current_page[player_name] = page |
|||
local pagedef = unified_inventory.pages[page] |
|||
|
|||
if not pagedef then |
|||
return "" -- Invalid page name |
|||
end |
|||
|
|||
local formspec = { |
|||
"size[14,10]", |
|||
pagedef.formspec_prepend and "" or "no_prepend[]", |
|||
"background[-0.19,-0.25;14.4,10.75;ui_form_bg.png]" -- Background |
|||
} |
|||
local n = 4 |
|||
|
|||
if draw_lite_mode then |
|||
formspec[1] = "size[11,7.7]" |
|||
formspec[3] = "background[-0.19,-0.2;11.4,8.4;ui_form_bg.png]" |
|||
end |
|||
|
|||
if unified_inventory.is_creative(player_name) |
|||
and page == "craft" then |
|||
formspec[n] = "background[0,"..(ui_peruser.formspec_y + 2)..";1,1;ui_single_slot.png]" |
|||
n = n+1 |
|||
end |
|||
|
|||
local perplayer_formspec = unified_inventory.get_per_player_formspec(player_name) |
|||
local fsdata = pagedef.get_formspec(player, perplayer_formspec) |
|||
|
|||
formspec[n] = fsdata.formspec |
|||
n = n+1 |
|||
|
|||
local button_row = 0 |
|||
local button_col = 0 |
|||
|
|||
-- Main buttons |
|||
|
|||
local filtered_inv_buttons = {} |
|||
|
|||
for i, def in pairs(unified_inventory.buttons) do |
|||
if not (draw_lite_mode and def.hide_lite) then |
|||
table.insert(filtered_inv_buttons, def) |
|||
end |
|||
end |
|||
|
|||
for i, def in pairs(filtered_inv_buttons) do |
|||
|
|||
if draw_lite_mode and i > 4 then |
|||
button_row = 1 |
|||
button_col = 1 |
|||
end |
|||
|
|||
if def.type == "image" then |
|||
if (def.condition == nil or def.condition(player) == true) then |
|||
formspec[n] = "image_button[" |
|||
formspec[n+1] = ( ui_peruser.main_button_x + 0.65 * (i - 1) - button_col * 0.65 * 4) |
|||
formspec[n+2] = ","..(ui_peruser.main_button_y + button_row * 0.7)..";0.8,0.8;" |
|||
formspec[n+3] = F(def.image)..";" |
|||
formspec[n+4] = F(def.name)..";]" |
|||
formspec[n+5] = "tooltip["..F(def.name) |
|||
formspec[n+6] = ";"..(def.tooltip or "").."]" |
|||
n = n+7 |
|||
else |
|||
formspec[n] = "image[" |
|||
formspec[n+1] = ( ui_peruser.main_button_x + 0.65 * (i - 1) - button_col * 0.65 * 4) |
|||
formspec[n+2] = ","..(ui_peruser.main_button_y + button_row * 0.7)..";0.8,0.8;" |
|||
formspec[n+3] = F(def.image).."^[colorize:#808080:alpha]" |
|||
n = n+4 |
|||
|
|||
end |
|||
end |
|||
end |
|||
|
|||
if fsdata.draw_inventory ~= false then |
|||
-- Player inventory |
|||
formspec[n] = "listcolors[#00000000;#00000000]" |
|||
formspec[n+1] = "list[current_player;main;0,"..(ui_peruser.formspec_y + 3.5)..";8,4;]" |
|||
n = n+2 |
|||
end |
|||
|
|||
if fsdata.draw_item_list == false then |
|||
return table.concat(formspec, "") |
|||
end |
|||
|
|||
-- Controls to flip items pages |
|||
local start_x = 9.2 |
|||
|
|||
if not draw_lite_mode then |
|||
formspec[n] = |
|||
"image_button[" .. (start_x + 0.6 * 0) |
|||
.. ",9;.8,.8;ui_skip_backward_icon.png;start_list;]" |
|||
.. "tooltip[start_list;" .. F(S("First page")) .. "]" |
|||
|
|||
.. "image_button[" .. (start_x + 0.6 * 1) |
|||
.. ",9;.8,.8;ui_doubleleft_icon.png;rewind3;]" |
|||
.. "tooltip[rewind3;" .. F(S("Back three pages")) .. "]" |
|||
.. "image_button[" .. (start_x + 0.6 * 2) |
|||
.. ",9;.8,.8;ui_left_icon.png;rewind1;]" |
|||
.. "tooltip[rewind1;" .. F(S("Back one page")) .. "]" |
|||
|
|||
.. "image_button[" .. (start_x + 0.6 * 3) |
|||
.. ",9;.8,.8;ui_right_icon.png;forward1;]" |
|||
.. "tooltip[forward1;" .. F(S("Forward one page")) .. "]" |
|||
.. "image_button[" .. (start_x + 0.6 * 4) |
|||
.. ",9;.8,.8;ui_doubleright_icon.png;forward3;]" |
|||
.. "tooltip[forward3;" .. F(S("Forward three pages")) .. "]" |
|||
|
|||
.. "image_button[" .. (start_x + 0.6 * 5) |
|||
.. ",9;.8,.8;ui_skip_forward_icon.png;end_list;]" |
|||
.. "tooltip[end_list;" .. F(S("Last page")) .. "]" |
|||
else |
|||
formspec[n] = |
|||
"image_button[" .. (8.2 + 0.65 * 0) |
|||
.. ",5.8;.8,.8;ui_skip_backward_icon.png;start_list;]" |
|||
.. "tooltip[start_list;" .. F(S("First page")) .. "]" |
|||
.. "image_button[" .. (8.2 + 0.65 * 1) |
|||
.. ",5.8;.8,.8;ui_left_icon.png;rewind1;]" |
|||
.. "tooltip[rewind1;" .. F(S("Back one page")) .. "]" |
|||
.. "image_button[" .. (8.2 + 0.65 * 2) |
|||
.. ",5.8;.8,.8;ui_right_icon.png;forward1;]" |
|||
.. "tooltip[forward1;" .. F(S("Forward one page")) .. "]" |
|||
.. "image_button[" .. (8.2 + 0.65 * 3) |
|||
.. ",5.8;.8,.8;ui_skip_forward_icon.png;end_list;]" |
|||
.. "tooltip[end_list;" .. F(S("Last page")) .. "]" |
|||
end |
|||
n = n+1 |
|||
|
|||
-- Search box |
|||
formspec[n] = "field_close_on_enter[searchbox;false]" |
|||
n = n+1 |
|||
|
|||
if not draw_lite_mode then |
|||
formspec[n] = "field[9.5,8.325;3,1;searchbox;;" |
|||
.. F(unified_inventory.current_searchbox[player_name]) .. "]" |
|||
formspec[n+1] = "image_button[12.2,8.1;.8,.8;ui_search_icon.png;searchbutton;]" |
|||
.. "tooltip[searchbutton;" ..F(S("Search")) .. "]" |
|||
formspec[n+2] = "image_button[12.9,8.1;.8,.8;ui_reset_icon.png;searchresetbutton;]" |
|||
.. "tooltip[searchbutton;" ..F(S("Search")) .. "]" |
|||
.. "tooltip[searchresetbutton;" ..F(S("Reset search and display everything")) .. "]" |
|||
else |
|||
formspec[n] = "field[8.5,5.225;2.2,1;searchbox;;" |
|||
.. F(unified_inventory.current_searchbox[player_name]) .. "]" |
|||
formspec[n+1] = "image_button[10.3,5;.8,.8;ui_search_icon.png;searchbutton;]" |
|||
.. "tooltip[searchbutton;" ..F(S("Search")) .. "]" |
|||
formspec[n+2] = "image_button[11,5;.8,.8;ui_reset_icon.png;searchresetbutton;]" |
|||
.. "tooltip[searchbutton;" ..F(S("Search")) .. "]" |
|||
.. "tooltip[searchresetbutton;" ..F(S("Reset search and display everything")) .. "]" |
|||
end |
|||
n = n+3 |
|||
|
|||
local no_matches = S("No matching items") |
|||
if draw_lite_mode then |
|||
no_matches = S("No matches.") |
|||
end |
|||
|
|||
-- Items list |
|||
if #unified_inventory.filtered_items_list[player_name] == 0 then |
|||
formspec[n] = "label[8.2,"..ui_peruser.form_header_y..";" .. F(no_matches) .. "]" |
|||
else |
|||
local dir = unified_inventory.active_search_direction[player_name] |
|||
local list_index = unified_inventory.current_index[player_name] |
|||
local page = math.floor(list_index / (ui_peruser.items_per_page) + 1) |
|||
local pagemax = math.floor( |
|||
(#unified_inventory.filtered_items_list[player_name] - 1) |
|||
/ (ui_peruser.items_per_page) + 1) |
|||
local item = {} |
|||
for y = 0, ui_peruser.pagerows - 1 do |
|||
for x = 0, ui_peruser.pagecols - 1 do |
|||
local name = unified_inventory.filtered_items_list[player_name][list_index] |
|||
local item = minetest.registered_items[name] |
|||
if item then |
|||
-- Clicked on current item: Flip crafting direction |
|||
if name == unified_inventory.current_item[player_name] then |
|||
local cdir = unified_inventory.current_craft_direction[player_name] |
|||
if cdir == "recipe" then |
|||
dir = "usage" |
|||
elseif cdir == "usage" then |
|||
dir = "recipe" |
|||
end |
|||
else |
|||
-- Default: use active search direction by default |
|||
dir = unified_inventory.active_search_direction[player_name] |
|||
end |
|||
|
|||
local button_name = "item_button_" .. dir .. "_" |
|||
.. unified_inventory.mangle_for_formspec(name) |
|||
formspec[n] = ("item_image_button[%f,%f;.81,.81;%s;%s;]"):format( |
|||
8.2 + x * 0.7, ui_peruser.formspec_y + ui_peruser.page_y + y * 0.7, |
|||
name, button_name |
|||
) |
|||
formspec[n + 1] = ("tooltip[%s;%s \\[%s\\]]"):format( |
|||
button_name, minetest.formspec_escape(item.description), |
|||
item.mod_origin or "??" |
|||
) |
|||
n = n + 2 |
|||
list_index = list_index + 1 |
|||
end |
|||
end |
|||
end |
|||
|
|||
formspec [n] = "label[8.2," .. ui_peruser.form_header_y |
|||
.. ";Page " .. page .. " of " .. pagemax .. "]" |
|||
end |
|||
n = n+1 |
|||
|
|||
if unified_inventory.activefilter[player_name] ~= "" then |
|||
formspec[n] = "label[8.2,"..(ui_peruser.form_header_y + 0.4)..";" .. F(S("Filter")) .. ":]" |
|||
formspec[n+1] = "label[9.1,"..(ui_peruser.form_header_y + 0.4)..";"..F(unified_inventory.activefilter[player_name]).."]" |
|||
end |
|||
return table.concat(formspec, "") |
|||
end |
|||
|
|||
function unified_inventory.set_inventory_formspec(player, page) |
|||
if player then |
|||
player:set_inventory_formspec(unified_inventory.get_formspec(player, page)) |
|||
end |
|||
end |
|||
|
|||
--apply filter to the inventory list (create filtered copy of full one) |
|||
function unified_inventory.apply_filter(player, filter, search_dir) |
|||
if not player then |
|||
return false |
|||
end |
|||
local player_name = player:get_player_name() |
|||
local lfilter = string.lower(filter) |
|||
local ffilter |
|||
if lfilter:sub(1, 6) == "group:" then |
|||
local groups = lfilter:sub(7):split(",") |
|||
ffilter = function(name, def) |
|||
for _, group in ipairs(groups) do |
|||
if not def.groups[group] |
|||
or def.groups[group] <= 0 then |
|||
return false |
|||
end |
|||
end |
|||
return true |
|||
end |
|||
else |
|||
ffilter = function(name, def) |
|||
local lname = string.lower(name) |
|||
local ldesc = string.lower(def.description) |
|||
return string.find(lname, lfilter, 1, true) or string.find(ldesc, lfilter, 1, true) |
|||
end |
|||
end |
|||
unified_inventory.filtered_items_list[player_name]={} |
|||
for name, def in pairs(minetest.registered_items) do |
|||
if (not def.groups.not_in_creative_inventory |
|||
or def.groups.not_in_creative_inventory == 0) |
|||
and def.description |
|||
and def.description ~= "" |
|||
and ffilter(name, def) then |
|||
table.insert(unified_inventory.filtered_items_list[player_name], name) |
|||
end |
|||
end |
|||
table.sort(unified_inventory.filtered_items_list[player_name]) |
|||
unified_inventory.filtered_items_list_size[player_name] = #unified_inventory.filtered_items_list[player_name] |
|||
unified_inventory.current_index[player_name] = 1 |
|||
unified_inventory.activefilter[player_name] = filter |
|||
unified_inventory.active_search_direction[player_name] = search_dir |
|||
unified_inventory.set_inventory_formspec(player, |
|||
unified_inventory.current_page[player_name]) |
|||
end |
|||
|
|||
function unified_inventory.items_in_group(groups) |
|||
local items = {} |
|||
for name, item in pairs(minetest.registered_items) do |
|||
for _, group in pairs(groups:split(',')) do |
|||
if item.groups[group] then |
|||
table.insert(items, name) |
|||
end |
|||
end |
|||
end |
|||
return items |
|||
end |
|||
|
|||
function unified_inventory.sort_inventory(inv) |
|||
local inlist = inv:get_list("main") |
|||
local typecnt = {} |
|||
local typekeys = {} |
|||
for _, st in ipairs(inlist) do |
|||
if not st:is_empty() then |
|||
local n = st:get_name() |
|||
local w = st:get_wear() |
|||
local m = st:get_metadata() |
|||
local k = string.format("%s %05d %s", n, w, m) |
|||
if not typecnt[k] then |
|||
typecnt[k] = { |
|||
name = n, |
|||
wear = w, |
|||
metadata = m, |
|||
stack_max = st:get_stack_max(), |
|||
count = 0, |
|||
} |
|||
table.insert(typekeys, k) |
|||
end |
|||
typecnt[k].count = typecnt[k].count + st:get_count() |
|||
end |
|||
end |
|||
table.sort(typekeys) |
|||
local outlist = {} |
|||
for _, k in ipairs(typekeys) do |
|||
local tc = typecnt[k] |
|||
while tc.count > 0 do |
|||
local c = math.min(tc.count, tc.stack_max) |
|||
table.insert(outlist, ItemStack({ |
|||
name = tc.name, |
|||
wear = tc.wear, |
|||
metadata = tc.metadata, |
|||
count = c, |
|||
})) |
|||
tc.count = tc.count - c |
|||
end |
|||
end |
|||
if #outlist > #inlist then return end |
|||
while #outlist < #inlist do |
|||
table.insert(outlist, ItemStack(nil)) |
|||
end |
|||
inv:set_list("main", outlist) |
|||
end |
@ -0,0 +1,27 @@ |
|||
Name: unified_inventory |
|||
Source: Fork of upstream mod - Do not replace |
|||
License: See "LICENSE.txt" |
|||
|
|||
---------------------------------------------------------------------- |
|||
|
|||
1. This is a fork of an upstream mod. The starting point was obtained |
|||
initially as follows: |
|||
|
|||
rm -fr unified_inventory |
|||
git clone https://github.com/minetest-mods/unified_inventory.git |
|||
cd unified_inventory |
|||
git reset --hard ca6d9a10df5110fd75d49785adf690ae270d5a31 |
|||
|
|||
Forked after: commit indicated above |
|||
|
|||
---------------------------------------------------------------------- |
|||
|
|||
2. Partial list of changes: |
|||
|
|||
2a. Fixed a few cases where "@1" or "@2" was printed instead of the |
|||
correct output. |
|||
|
|||
2b. Modified the mod so that it stays synced with "ocsethome". |
|||
|
|||
2c. Removed "bags" code, which seems to be crashy. "prestibags" mod |
|||
has been added to the "_game" as a substitute for the "bags" feature. |
@ -0,0 +1,489 @@ |
|||
local S = function (str) return str end |
|||
local NS = function(s) return s end |
|||
local F = minetest.formspec_escape |
|||
|
|||
minetest.register_privilege("creative", { |
|||
description = S("Can use the creative inventory"), |
|||
give_to_singleplayer = false, |
|||
}) |
|||
|
|||
minetest.register_privilege("ui_full", { |
|||
description = S("Forces Unified Inventory to be displayed in Full mode if Lite mode is configured globally"), |
|||
give_to_singleplayer = false, |
|||
}) |
|||
|
|||
|
|||
local trash = minetest.create_detached_inventory("trash", { |
|||
--allow_put = function(inv, listname, index, stack, player) |
|||
-- if unified_inventory.is_creative(player:get_player_name()) then |
|||
-- return stack:get_count() |
|||
-- else |
|||
-- return 0 |
|||
-- end |
|||
--end, |
|||
on_put = function(inv, listname, index, stack, player) |
|||
inv:set_stack(listname, index, nil) |
|||
local player_name = player:get_player_name() |
|||
minetest.sound_play("trash", {to_player=player_name, gain = 1.0}) |
|||
end, |
|||
}) |
|||
trash:set_size("main", 1) |
|||
|
|||
unified_inventory.register_button("craft", { |
|||
type = "image", |
|||
image = "ui_craft_icon.png", |
|||
tooltip = S("Crafting Grid") |
|||
}) |
|||
|
|||
unified_inventory.register_button("craftguide", { |
|||
type = "image", |
|||
image = "ui_craftguide_icon.png", |
|||
tooltip = S("Crafting Guide") |
|||
}) |
|||
|
|||
unified_inventory.register_button("home_gui_set", { |
|||
type = "image", |
|||
image = "ui_sethome_icon.png", |
|||
tooltip = S("Set home position"), |
|||
hide_lite=true, |
|||
action = function(player) |
|||
local player_name = player:get_player_name() |
|||
if minetest.check_player_privs(player_name, {home=true}) then |
|||
unified_inventory.set_home(player, player:get_pos()) |
|||
local home = unified_inventory.home_pos[player_name] |
|||
if home ~= nil then |
|||
minetest.sound_play("dingdong", |
|||
{to_player=player_name, gain = 1.0}) |
|||
minetest.chat_send_player(player_name, |
|||
"Home position set to: " .. minetest.pos_to_string (home)) |
|||
end |
|||
else |
|||
minetest.chat_send_player(player_name, |
|||
S("You don't have the \"home\" privilege!")) |
|||
unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name]) |
|||
end |
|||
end, |
|||
condition = function(player) |
|||
return minetest.check_player_privs(player:get_player_name(), {home=true}) |
|||
end, |
|||
}) |
|||
|
|||
unified_inventory.register_button("home_gui_go", { |
|||
type = "image", |
|||
image = "ui_gohome_icon.png", |
|||
tooltip = S("Go home"), |
|||
hide_lite=true, |
|||
action = function(player) |
|||
local player_name = player:get_player_name() |
|||
if minetest.check_player_privs(player_name, {home=true}) then |
|||
minetest.sound_play("teleport", |
|||
{to_player=player:get_player_name(), gain = 1.0}) |
|||
unified_inventory.go_home(player) |
|||
else |
|||
minetest.chat_send_player(player_name, |
|||
S("You don't have the \"home\" privilege!")) |
|||
unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name]) |
|||
end |
|||
end, |
|||
condition = function(player) |
|||
return minetest.check_player_privs(player:get_player_name(), {home=true}) |
|||
end, |
|||
}) |
|||
|
|||
unified_inventory.register_button("misc_set_day", { |
|||
type = "image", |
|||
image = "ui_sun_icon.png", |
|||
tooltip = S("Set time to day"), |
|||
hide_lite=true, |
|||
action = function(player) |
|||
local player_name = player:get_player_name() |
|||
if minetest.check_player_privs(player_name, {settime=true}) then |
|||
minetest.sound_play("birds", |
|||
{to_player=player_name, gain = 1.0}) |
|||
minetest.set_timeofday((6000 % 24000) / 24000) |
|||
minetest.chat_send_player(player_name, |
|||
S("Time of day set to 6am")) |
|||
else |
|||
minetest.chat_send_player(player_name, |
|||
S("You don't have the settime privilege!")) |
|||
unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name]) |
|||
end |
|||
end, |
|||
condition = function(player) |
|||
return minetest.check_player_privs(player:get_player_name(), {settime=true}) |
|||
end, |
|||
}) |
|||
|
|||
unified_inventory.register_button("misc_set_night", { |
|||
type = "image", |
|||
image = "ui_moon_icon.png", |
|||
tooltip = S("Set time to night"), |
|||
hide_lite=true, |
|||
action = function(player) |
|||
local player_name = player:get_player_name() |
|||
if minetest.check_player_privs(player_name, {settime=true}) then |
|||
minetest.sound_play("owl", |
|||
{to_player=player_name, gain = 1.0}) |
|||
minetest.set_timeofday((21000 % 24000) / 24000) |
|||
minetest.chat_send_player(player_name, |
|||
S("Time of day set to 9pm")) |
|||
else |
|||
minetest.chat_send_player(player_name, |
|||
S("You don't have the settime privilege!")) |
|||
unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name]) |
|||
end |
|||
end, |
|||
condition = function(player) |
|||
return minetest.check_player_privs(player:get_player_name(), {settime=true}) |
|||
end, |
|||
}) |
|||
|
|||
unified_inventory.register_button("clear_inv", { |
|||
type = "image", |
|||
image = "ui_trash_icon.png", |
|||
tooltip = S("Clear inventory"), |
|||
action = function(player) |
|||
local player_name = player:get_player_name() |
|||
if not unified_inventory.is_creative(player_name) then |
|||
minetest.chat_send_player(player_name, |
|||
S("This button has been disabled outside" |
|||
.." of creative mode to prevent" |
|||
.." accidental inventory trashing." |
|||
.."\nUse the trash slot instead.")) |
|||
unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name]) |
|||
return |
|||
end |
|||
player:get_inventory():set_list("main", {}) |
|||
minetest.chat_send_player(player_name, S('Inventory cleared!')) |
|||
minetest.sound_play("trash_all", |
|||
{to_player=player_name, gain = 1.0}) |
|||
end, |
|||
condition = function(player) |
|||
return unified_inventory.is_creative(player:get_player_name()) |
|||
end, |
|||
}) |
|||
|
|||
unified_inventory.register_page("craft", { |
|||
get_formspec = function(player, perplayer_formspec) |
|||
|
|||
local formspecy = perplayer_formspec.formspec_y |
|||
local formheadery = perplayer_formspec.form_header_y |
|||
|
|||
local player_name = player:get_player_name() |
|||
local formspec = "background[2,"..formspecy..";6,3;ui_crafting_form.png]" |
|||
formspec = formspec.."background[0,"..(formspecy + 3.5)..";8,4;ui_main_inventory.png]" |
|||
formspec = formspec.."label[0,"..formheadery..";" ..F(S("Crafting")).."]" |
|||
formspec = formspec.."listcolors[#00000000;#00000000]" |
|||
formspec = formspec.."list[current_player;craftpreview;6,"..formspecy..";1,1;]" |
|||
formspec = formspec.."list[current_player;craft;2,"..formspecy..";3,3;]" |
|||
if unified_inventory.trash_enabled or unified_inventory.is_creative(player_name) or minetest.get_player_privs(player_name).give then |
|||
formspec = formspec.."label[7,"..(formspecy + 1.5)..";" .. F(S("Trash:")) .. "]" |
|||
formspec = formspec.."background[7,"..(formspecy + 2)..";1,1;ui_single_slot.png]" |
|||
formspec = formspec.."list[detached:trash;main;7,"..(formspecy + 2)..";1,1;]" |
|||
end |
|||
formspec = formspec.."listring[current_name;craft]" |
|||
formspec = formspec.."listring[current_player;main]" |
|||
if unified_inventory.is_creative(player_name) then |
|||
formspec = formspec.."label[0,"..(formspecy + 1.5)..";" .. F(S("Refill:")) .. "]" |
|||
formspec = formspec.."list[detached:"..F(player_name).."refill;main;0,"..(formspecy +2)..";1,1;]" |
|||
end |
|||
return {formspec=formspec} |
|||
end, |
|||
}) |
|||
|
|||
-- stack_image_button(): generate a form button displaying a stack of items |
|||
-- |
|||
-- The specified item may be a group. In that case, the group will be |
|||
-- represented by some item in the group, along with a flag indicating |
|||
-- that it's a group. If the group contains only one item, it will be |
|||
-- treated as if that item had been specified directly. |
|||
|
|||
local function stack_image_button(x, y, w, h, buttonname_prefix, item) |
|||
local name = item:get_name() |
|||
local count = item:get_count() |
|||
local show_is_group = false |
|||
local displayitem = name.." "..count |
|||
local selectitem = name |
|||
if name:sub(1, 6) == "group:" then |
|||
local group_name = name:sub(7) |
|||
local group_item = unified_inventory.get_group_item(group_name) |
|||
show_is_group = not group_item.sole |
|||
displayitem = group_item.item or "unknown" |
|||
selectitem = group_item.sole and displayitem or name |
|||
end |
|||
local label = show_is_group and "G" or "" |
|||
local buttonname = F(buttonname_prefix..unified_inventory.mangle_for_formspec(selectitem)) |
|||
local button = string.format("item_image_button[%f,%f;%f,%f;%s;%s;%s]", |
|||
x, y, w, h, |
|||
F(displayitem), buttonname, label) |
|||
if show_is_group then |
|||
local groupstring, andcount = unified_inventory.extract_groupnames(name) |
|||
local grouptip |
|||
if andcount == 1 then |
|||
grouptip = "Any item belonging to the " .. groupstring .. " group" |
|||
elseif andcount > 1 then |
|||
grouptip = "Any item belonging to the groups " .. groupstring |
|||
end |
|||
grouptip = F(grouptip) |
|||
if andcount >= 1 then |
|||
button = button .. string.format("tooltip[%s;%s]", buttonname, grouptip) |
|||
end |
|||
end |
|||
return button |
|||
end |
|||
|
|||
local recipe_text = { |
|||
recipe = NS("Recipe @1 of @2"), |
|||
usage = NS("Usage @1 of @2"), |
|||
} |
|||
local no_recipe_text = { |
|||
recipe = S("No recipes"), |
|||
usage = S("No usages"), |
|||
} |
|||
local role_text = { |
|||
recipe = S("Result"), |
|||
usage = S("Ingredient"), |
|||
} |
|||
local next_alt_text = { |
|||
recipe = S("Show next recipe"), |
|||
usage = S("Show next usage"), |
|||
} |
|||
local prev_alt_text = { |
|||
recipe = S("Show previous recipe"), |
|||
usage = S("Show previous usage"), |
|||
} |
|||
local other_dir = { |
|||
recipe = "usage", |
|||
usage = "recipe", |
|||
} |
|||
|
|||
unified_inventory.register_page("craftguide", { |
|||
get_formspec = function(player, perplayer_formspec) |
|||
|
|||
local formspecy = perplayer_formspec.formspec_y |
|||
local formheadery = perplayer_formspec.form_header_y |
|||
local craftresultx = perplayer_formspec.craft_result_x |
|||
local craftresulty = perplayer_formspec.craft_result_y |
|||
|
|||
local player_name = player:get_player_name() |
|||
local player_privs = minetest.get_player_privs(player_name) |
|||
local fs = { |
|||
"background[0,"..(formspecy + 3.5)..";8,4;ui_main_inventory.png]", |
|||
"label[0,"..formheadery..";" .. F(S("Crafting Guide")) .. "]", |
|||
"listcolors[#00000000;#00000000]" |
|||
} |
|||
local item_name = unified_inventory.current_item[player_name] |
|||
if not item_name then |
|||
return { formspec = table.concat(fs) } |
|||
end |
|||
|
|||
local item_name_shown |
|||
if minetest.registered_items[item_name] |
|||
and minetest.registered_items[item_name].description then |
|||
item_name_shown = S("@1 (@2)", |
|||
minetest.registered_items[item_name].description, item_name) |
|||
else |
|||
item_name_shown = item_name |
|||
end |
|||
|
|||
local dir = unified_inventory.current_craft_direction[player_name] |
|||
local rdir = dir == "recipe" and "usage" or "recipe" |
|||
|
|||
local crafts = unified_inventory.crafts_for[dir][item_name] |
|||
local alternate = unified_inventory.alternate[player_name] |
|||
local alternates, craft |
|||
if crafts and #crafts > 0 then |
|||
alternates = #crafts |
|||
craft = crafts[alternate] |
|||
end |
|||
local has_give = player_privs.give or unified_inventory.is_creative(player_name) |
|||
|
|||
fs[#fs + 1] = "background[0.5,"..(formspecy + 0.2)..";8,3;ui_craftguide_form.png]" |
|||
fs[#fs + 1] = string.format("textarea[%f,%f;10,1;;%s: %s;]", |
|||
craftresultx, craftresulty, F(role_text[dir]), item_name_shown) |
|||
fs[#fs + 1] = stack_image_button(0, formspecy, 1.1, 1.1, |
|||
"item_button_" .. rdir .. "_", ItemStack(item_name)) |
|||
|
|||
if not craft then |
|||
-- No craft recipes available for this item. |
|||
fs[#fs + 1] = "label[5.5,"..(formspecy + 2.35)..";" |
|||
.. F(no_recipe_text[dir]) .. "]" |
|||
local no_pos = dir == "recipe" and 4.5 or 6.5 |
|||
local item_pos = dir == "recipe" and 6.5 or 4.5 |
|||
fs[#fs + 1] = "image["..no_pos..","..formspecy..";1.1,1.1;ui_no.png]" |
|||
fs[#fs + 1] = stack_image_button(item_pos, formspecy, 1.1, 1.1, |
|||
"item_button_" .. other_dir[dir] .. "_", ItemStack(item_name)) |
|||
if has_give then |
|||
fs[#fs + 1] = "label[0," .. (formspecy + 2.10) .. ";" .. F(S("Give me:")) .. "]" |
|||
.. "button[0, " .. (formspecy + 2.7) .. ";0.6,0.5;craftguide_giveme_1;1]" |
|||
.. "button[0.6," .. (formspecy + 2.7) .. ";0.7,0.5;craftguide_giveme_10;10]" |
|||
.. "button[1.3," .. (formspecy + 2.7) .. ";0.8,0.5;craftguide_giveme_99;99]" |
|||
end |
|||
return { formspec = table.concat(fs) } |
|||
end |
|||
|
|||
local craft_type = unified_inventory.registered_craft_types[craft.type] or |
|||
unified_inventory.craft_type_defaults(craft.type, {}) |
|||
if craft_type.icon then |
|||
fs[#fs + 1] = string.format("image[%f,%f;%f,%f;%s]", |
|||
5.7, (formspecy + 0.05), 0.5, 0.5, craft_type.icon) |
|||
end |
|||
fs[#fs + 1] = "label[5.5,"..(formspecy + 1)..";" .. F(craft_type.description).."]" |
|||
fs[#fs + 1] = stack_image_button(6.5, formspecy, 1.1, 1.1, |
|||
"item_button_usage_", ItemStack(craft.output)) |
|||
|
|||
local display_size = craft_type.dynamic_display_size |
|||
and craft_type.dynamic_display_size(craft) |
|||
or { width = craft_type.width, height = craft_type.height } |
|||
local craft_width = craft_type.get_shaped_craft_width |
|||
and craft_type.get_shaped_craft_width(craft) |
|||
or display_size.width |
|||
|
|||
-- This keeps recipes aligned to the right, |
|||
-- so that they're close to the arrow. |
|||
local xoffset = 5.5 |
|||
-- Offset factor for crafting grids with side length > 4 |
|||
local of = (3/math.max(3, math.max(display_size.width, display_size.height))) |
|||
local od = 0 |
|||
-- Minimum grid size at which size optimazation measures kick in |
|||
local mini_craft_size = 6 |
|||
if display_size.width >= mini_craft_size then |
|||
od = math.max(1, display_size.width - 2) |
|||
xoffset = xoffset - 0.1 |
|||
end |
|||
-- Size modifier factor |
|||
local sf = math.min(1, of * (1.05 + 0.05*od)) |
|||
-- Button size |
|||
local bsize_h = 1.1 * sf |
|||
local bsize_w = bsize_h |
|||
if display_size.width >= mini_craft_size then |
|||
bsize_w = 1.175 * sf |
|||
end |
|||
if (bsize_h > 0.35 and display_size.width) then |
|||
for y = 1, display_size.height do |
|||
for x = 1, display_size.width do |
|||
local item |
|||
if craft and x <= craft_width then |
|||
item = craft.items[(y-1) * craft_width + x] |
|||
end |
|||
-- Flipped x, used to build formspec buttons from right to left |
|||
local fx = display_size.width - (x-1) |
|||
-- x offset, y offset |
|||
local xof = (fx-1) * of + of |
|||
local yof = (y-1) * of + 1 |
|||
if item then |
|||
fs[#fs + 1] = stack_image_button( |
|||
xoffset - xof, formspecy - 1 + yof, bsize_w, bsize_h, |
|||
"item_button_recipe_", |
|||
ItemStack(item)) |
|||
else |
|||
-- Fake buttons just to make grid |
|||
fs[#fs + 1] = string.format("image_button[%f,%f;%f,%f;ui_blank_image.png;;]", |
|||
xoffset - xof, formspecy - 1 + yof, bsize_w, bsize_h) |
|||
end |
|||
end |
|||
end |
|||
else |
|||
-- Error |
|||
fs[#fs + 1] = string.format("label[2,%f;%s]", |
|||
formspecy, F(S("This recipe is too\nlarge to be displayed."))) |
|||
end |
|||
|
|||
if craft_type.uses_crafting_grid and display_size.width <= 3 then |
|||
fs[#fs + 1] = "label[0," .. (formspecy + 0.9) .. ";" .. F(S("To craft grid:")) .. "]" |
|||
.. "button[0, " .. (formspecy + 1.5) .. ";0.6,0.5;craftguide_craft_1;1]" |
|||
.. "button[0.6," .. (formspecy + 1.5) .. ";0.7,0.5;craftguide_craft_10;10]" |
|||
.. "button[1.3," .. (formspecy + 1.5) .. ";0.8,0.5;craftguide_craft_max;" .. F(S("All")) .. "]" |
|||
end |
|||
if has_give then |
|||
fs[#fs + 1] = "label[0," .. (formspecy + 2.1) .. ";" .. F(S("Give me:")) .. "]" |
|||
.. "button[0, " .. (formspecy + 2.7) .. ";0.6,0.5;craftguide_giveme_1;1]" |
|||
.. "button[0.6," .. (formspecy + 2.7) .. ";0.7,0.5;craftguide_giveme_10;10]" |
|||
.. "button[1.3," .. (formspecy + 2.7) .. ";0.8,0.5;craftguide_giveme_99;99]" |
|||
end |
|||
|
|||
if alternates and alternates > 1 then |
|||
fs[#fs + 1] = "label[5.5," .. (formspecy + 1.6) .. ";" |
|||
.. F(S(recipe_text[dir], alternate, alternates)) .. "]" |
|||
.. "image_button[5.5," .. (formspecy + 2) .. ";1,1;ui_left_icon.png;alternate_prev;]" |
|||
.. "image_button[6.5," .. (formspecy + 2) .. ";1,1;ui_right_icon.png;alternate;]" |
|||
.. "tooltip[alternate_prev;" .. F(prev_alt_text[dir]) .. "]" |
|||
.. "tooltip[alternate;" .. F(next_alt_text[dir]) .. "]" |
|||
end |
|||
return { formspec = table.concat(fs) } |
|||
end, |
|||
}) |
|||
|
|||
local function craftguide_giveme(player, formname, fields) |
|||
local player_name = player:get_player_name() |
|||
local player_privs = minetest.get_player_privs(player_name) |
|||
if not player_privs.give and |
|||
not unified_inventory.is_creative(player_name) then |
|||
minetest.log("action", "[unified_inventory] Denied give action to player " .. |
|||
player_name) |
|||
return |
|||
end |
|||
|
|||
local amount |
|||
for k, v in pairs(fields) do |
|||
amount = k:match("craftguide_giveme_(.*)") |
|||
if amount then break end |
|||
end |
|||
|
|||
amount = tonumber(amount) or 0 |
|||
if amount == 0 then return end |
|||
|
|||
local output = unified_inventory.current_item[player_name] |
|||
if (not output) or (output == "") then return end |
|||
|
|||
local player_inv = player:get_inventory() |
|||
|
|||
player_inv:add_item("main", {name = output, count = amount}) |
|||
end |
|||
|
|||
local function craftguide_craft(player, formname, fields) |
|||
local amount |
|||
for k, v in pairs(fields) do |
|||
amount = k:match("craftguide_craft_(.*)") |
|||
if amount then break end |
|||
end |
|||
if not amount then return end |
|||
|
|||
amount = tonumber(amount) or -1 -- fallback for "all" |
|||
if amount == 0 or amount < -1 or amount > 99 then return end |
|||
|
|||
local player_name = player:get_player_name() |
|||
|
|||
local output = unified_inventory.current_item[player_name] or "" |
|||
if output == "" then return end |
|||
|
|||
local crafts = unified_inventory.crafts_for[ |
|||
unified_inventory.current_craft_direction[player_name]][output] or {} |
|||
if #crafts == 0 then return end |
|||
|
|||
local alternate = unified_inventory.alternate[player_name] |
|||
|
|||
local craft = crafts[alternate] |
|||
if craft.width > 3 then return end |
|||
|
|||
unified_inventory.craftguide_match_craft(player, "main", "craft", craft, amount) |
|||
|
|||
unified_inventory.set_inventory_formspec(player, "craft") |
|||
end |
|||
|
|||
minetest.register_on_player_receive_fields(function(player, formname, fields) |
|||
if formname ~= "" then |
|||
return |
|||
end |
|||
|
|||
for k, v in pairs(fields) do |
|||
if k:match("craftguide_craft_") then |
|||
craftguide_craft(player, formname, fields) |
|||
return |
|||
end |
|||
if k:match("craftguide_giveme_") then |
|||
craftguide_giveme(player, formname, fields) |
|||
return |
|||
end |
|||
end |
|||
end) |
@ -0,0 +1,247 @@ |
|||
local S = function (str) return str end |
|||
local F = minetest.formspec_escape |
|||
|
|||
local hud_colors = { |
|||
{"#FFFFFF", 0xFFFFFF, S("White")}, |
|||
{"#DBBB00", 0xf1d32c, S("Yellow")}, |
|||
{"#DD0000", 0xDD0000, S("Red")}, |
|||
{"#2cf136", 0x2cf136, S("Green")}, |
|||
{"#2c4df1", 0x2c4df1, S("Blue")}, |
|||
} |
|||
|
|||
local hud_colors_max = #hud_colors |
|||
|
|||
-- Stores temporary player data (persists until player leaves) |
|||
local waypoints_temp = {} |
|||
|
|||
unified_inventory.register_page("waypoints", { |
|||
get_formspec = function(player) |
|||
local player_name = player:get_player_name() |
|||
|
|||
-- build a "fake" temp entry if the server took too long |
|||
-- during sign-on and returned an empty entry |
|||
if not waypoints_temp[player_name] then waypoints_temp[player_name] = {hud = 1} end |
|||
|
|||
local waypoints = datastorage.get(player_name, "waypoints") |
|||
local formspec = "background[0,4.5;8,4;ui_main_inventory.png]" .. |
|||
"image[0,0;1,1;ui_waypoints_icon.png]" .. |
|||
"label[1,0;" .. F(S("Waypoints")) .. "]" |
|||
|
|||
-- Tabs buttons: |
|||
for i = 1, 5, 1 do |
|||
formspec = formspec .. |
|||
"image_button[0.0," .. 0.2 + i * 0.7 .. ";.8,.8;" .. |
|||
(i == waypoints.selected and "ui_blue_icon_background.png^" or "") .. |
|||
"ui_" .. i .. "_icon.png;" .. |
|||
"select_waypoint" .. i .. ";]" .. |
|||
"tooltip[select_waypoint" .. i .. ";" |
|||
.. S("Select Waypoint #@1", i).."]" |
|||
end |
|||
|
|||
local i = waypoints.selected or 1 |
|||
local waypoint = waypoints[i] or {} |
|||
local temp = waypoints_temp[player_name][i] or {} |
|||
local default_name = S("Waypoint @1", i) |
|||
|
|||
-- Main buttons: |
|||
formspec = formspec .. |
|||
"image_button[4.5,3.7;.8,.8;".. |
|||
"ui_waypoint_set_icon.png;".. |
|||
"set_waypoint"..i..";]".. |
|||
"tooltip[set_waypoint" .. i .. ";" |
|||
.. F(S("Set waypoint to current location")).."]" |
|||
|
|||
formspec = formspec .. |
|||
"image_button[5.2,3.7;.8,.8;".. |
|||
(waypoint.active and "ui_on_icon.png" or "ui_off_icon.png")..";".. |
|||
"toggle_waypoint"..i..";]".. |
|||
"tooltip[toggle_waypoint" .. i .. ";" |
|||
.. F(S("Make waypoint @1", |
|||
waypoint.active and S("invisible") or S("visible"))).."]" |
|||
|
|||
formspec = formspec .. |
|||
"image_button[5.9,3.7;.8,.8;".. |
|||
(waypoint.display_pos and "ui_green_icon_background.png" or "ui_red_icon_background.png").."^ui_xyz_icon.png;".. |
|||
"toggle_display_pos" .. i .. ";]".. |
|||
"tooltip[toggle_display_pos" .. i .. ";" |
|||
.. F(S("@1 display of waypoint coordinates", |
|||
waypoint.display_pos and S("Disable") or S("Enable"))) .."]" |
|||
|
|||
formspec = formspec .. |
|||
"image_button[6.6,3.7;.8,.8;".. |
|||
"ui_circular_arrows_icon.png;".. |
|||
"toggle_color"..i..";]".. |
|||
"tooltip[toggle_color" .. i .. ";" |
|||
.. F(S("Change color of waypoint display")).."]" |
|||
|
|||
formspec = formspec .. |
|||
"image_button[7.3,3.7;.8,.8;".. |
|||
"ui_pencil_icon.png;".. |
|||
"rename_waypoint"..i..";]".. |
|||
"tooltip[rename_waypoint" .. i .. ";" |
|||
.. F(S("Edit waypoint name")).."]" |
|||
|
|||
-- Waypoint's info: |
|||
if waypoint.active then |
|||
formspec = formspec .. "label[1,0.8;"..F(S("Waypoint active")).."]" |
|||
else |
|||
formspec = formspec .. "label[1,0.8;"..F(S("Waypoint inactive")).."]" |
|||
end |
|||
|
|||
if temp.edit then |
|||
formspec = formspec .. |
|||
"field[1.3,3.2;6,.8;rename_box" .. i .. ";;" |
|||
..(waypoint.name or default_name).."]" .. |
|||
"image_button[7.3,2.9;.8,.8;".. |
|||
"ui_ok_icon.png;".. |
|||
"confirm_rename"..i.. ";]".. |
|||
"tooltip[confirm_rename" .. i .. ";" |
|||
.. F(S("Finish editing")).."]" |
|||
end |
|||
|
|||
formspec = formspec .. "label[1,1.3;"..F(S("World position"))..": " .. |
|||
minetest.pos_to_string(waypoint.world_pos or vector.new()) .. "]" .. |
|||
"label[1,1.8;"..F(S("Name"))..": ".. (waypoint.name or default_name) .. "]" .. |
|||
"label[1,2.3;"..F(S("HUD text color"))..": " .. |
|||
hud_colors[waypoint.color or 1][3] .. "]" |
|||
|
|||
return {formspec=formspec} |
|||
end, |
|||
}) |
|||
|
|||
unified_inventory.register_button("waypoints", { |
|||
type = "image", |
|||
image = "ui_waypoints_icon.png", |
|||
tooltip = S("Waypoints"), |
|||
hide_lite=true |
|||
}) |
|||
|
|||
local function update_hud(player, waypoints, temp, i) |
|||
local waypoint = waypoints[i] |
|||
if not waypoint then return end |
|||
temp[i] = temp[i] or {} |
|||
temp = temp[i] |
|||
local pos = waypoint.world_pos or vector.new() |
|||
local name |
|||
if waypoint.display_pos then |
|||
name = minetest.pos_to_string(pos) |
|||
if waypoint.name then |
|||
name = name..", "..waypoint.name |
|||
end |
|||
else |
|||
name = waypoint.name or "Waypoint "..i |
|||
end |
|||
if temp.hud then |
|||
player:hud_remove(temp.hud) |
|||
end |
|||
if waypoint.active then |
|||
temp.hud = player:hud_add({ |
|||
hud_elem_type = "waypoint", |
|||
number = hud_colors[waypoint.color or 1][2] , |
|||
name = name, |
|||
text = "m", |
|||
world_pos = pos |
|||
}) |
|||
else |
|||
temp.hud = nil |
|||
end |
|||
end |
|||
|
|||
minetest.register_on_player_receive_fields(function(player, formname, fields) |
|||
if formname ~= "" then return end |
|||
|
|||
local player_name = player:get_player_name() |
|||
local update_formspec = false |
|||
local need_update_hud = false |
|||
local hit = false |
|||
|
|||
local waypoints = datastorage.get(player_name, "waypoints") |
|||
local temp = waypoints_temp[player_name] |
|||
for i = 1, 5, 1 do |
|||
if fields["select_waypoint"..i] then |
|||
hit = true |
|||
waypoints.selected = i |
|||
update_formspec = true |
|||
end |
|||
|
|||
if fields["toggle_waypoint"..i] then |
|||
hit = true |
|||
waypoints[i] = waypoints[i] or {} |
|||
waypoints[i].active = not (waypoints[i].active) |
|||
need_update_hud = true |
|||
update_formspec = true |
|||
end |
|||
|
|||
if fields["set_waypoint"..i] then |
|||
hit = true |
|||
local pos = player:get_pos() |
|||
pos.x = math.floor(pos.x) |
|||
pos.y = math.floor(pos.y) |
|||
pos.z = math.floor(pos.z) |
|||
waypoints[i] = waypoints[i] or {} |
|||
waypoints[i].world_pos = pos |
|||
need_update_hud = true |
|||
update_formspec = true |
|||
end |
|||
|
|||
if fields["rename_waypoint"..i] then |
|||
hit = true |
|||
temp[i] = temp[i] or {} |
|||
temp[i].edit = true |
|||
update_formspec = true |
|||
end |
|||
|
|||
if fields["toggle_display_pos"..i] then |
|||
hit = true |
|||
waypoints[i] = waypoints[i] or {} |
|||
waypoints[i].display_pos = not waypoints[i].display_pos |
|||
need_update_hud = true |
|||
update_formspec = true |
|||
end |
|||
|
|||
if fields["toggle_color"..i] then |
|||
hit = true |
|||
waypoints[i] = waypoints[i] or {} |
|||
local color = waypoints[i].color or 1 |
|||
color = color + 1 |
|||
if color > hud_colors_max then |
|||
color = 1 |
|||
end |
|||
waypoints[i].color = color |
|||
need_update_hud = true |
|||
update_formspec = true |
|||
end |
|||
|
|||
if fields["confirm_rename"..i] then |
|||
hit = true |
|||
waypoints[i] = waypoints[i] or {} |
|||
temp[i].edit = false |
|||
waypoints[i].name = fields["rename_box"..i] |
|||
need_update_hud = true |
|||
update_formspec = true |
|||
end |
|||
if need_update_hud then |
|||
update_hud(player, waypoints, temp, i) |
|||
end |
|||
if update_formspec then |
|||
unified_inventory.set_inventory_formspec(player, "waypoints") |
|||
end |
|||
if hit then return end |
|||
end |
|||
end) |
|||
|
|||
|
|||
minetest.register_on_joinplayer(function(player) |
|||
local player_name = player:get_player_name() |
|||
local waypoints = datastorage.get(player_name, "waypoints") |
|||
local temp = {} |
|||
waypoints_temp[player_name] = temp |
|||
for i = 1, 5 do |
|||
update_hud(player, waypoints, temp, i) |
|||
end |
|||
end) |
|||
|
|||
minetest.register_on_leaveplayer(function(player) |
|||
waypoints_temp[player:get_player_name()] = nil |
|||
end) |
|||
|
Loading…
Reference in new issue