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