diff --git a/inventory/.gitignore b/inventory/.gitignore new file mode 100644 index 0000000..5236e1e --- /dev/null +++ b/inventory/.gitignore @@ -0,0 +1,2 @@ +*~ + diff --git a/inventory/README.md b/inventory/README.md new file mode 100644 index 0000000..6f5e2d4 --- /dev/null +++ b/inventory/README.md @@ -0,0 +1,22 @@ +Unified inventory +================= + +Unified Inventory replaces the default survival and creative inventory. +It adds a nicer interface and a number of features, such as a crafting guide. + +License +======= + +Copyright (C) 2012-2014 Maciej Kasatkin (RealBadAngel) + +Unified inventory code is licensed under the GNU LGPLv2+. + +Licenses for textures: + +VanessaE: (WTFPL) + * ui\_group.png + +RealBadAngel: (WTFPL) + * Everything else. + + diff --git a/inventory/api.lua b/inventory/api.lua new file mode 100644 index 0000000..1ab19fc --- /dev/null +++ b/inventory/api.lua @@ -0,0 +1,223 @@ +local S +if intllib then + S = intllib.Getter() +else + S = function(s) return s end +end + +-- 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 + unified_inventory.register_craft(recipe) + 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] + 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 + end + end + for _, recipes in pairs(unified_inventory.crafts_for.recipe) do + for _, recipe in ipairs(recipes) do + local ingredient_items = {} + for _, spec in ipairs(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 input then + while true do + local x = input:read("*n") + if x == nil 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) + else + unified_inventory.home_pos = {} + end +end +load_home() + +function unified_inventory.set_home(player, pos) + local player_name = player:get_player_name() + unified_inventory.home_pos[player_name] = 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 + if v ~= nil then + output:write(math.floor(v.x).." " + ..math.floor(v.y).." " + ..math.floor(v.z).." " + ..k.."\n") + end + end + io.close(output) +end + +function unified_inventory.go_home(player) + local pos = unified_inventory.home_pos[player:get_player_name()] + if pos ~= nil then + player:setpos(pos) + end +end + +-- register_craft +function unified_inventory.register_craft(options) + if options.output == nil 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 unified_inventory.crafts_for.recipe[itemstack:get_name()] == nil 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 = "Crafting", + 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 = "Mixing", + 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 = "Cooking", + width = 1, + height = 1, +}) + + +unified_inventory.register_craft_type("digging", { + description = "Digging", + 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) + if minetest.check_player_privs(playername, {creative=true}) or + minetest.setting_getbool("creative_mode") then + return true + end +end + diff --git a/inventory/bags.lua b/inventory/bags.lua new file mode 100644 index 0000000..10196f5 --- /dev/null +++ b/inventory/bags.lua @@ -0,0 +1,158 @@ +-- Bags for Minetest + +-- Copyright (c) 2012 cornernote, Brett O'Donnell +-- License: GPLv3 + +local S +if intllib then + S = intllib.Getter() +else + S = function(s) return s end +end + +unified_inventory.register_page("bags", { + get_formspec = function(player) + local player_name = player:get_player_name() + local formspec = "background[0.06,0.99;7.92,7.52;ui_bags_main_form.png]" + formspec = formspec.."label[0,0;"..S("Bags").."]" + formspec = formspec.."button[0,2;2,0.5;bag1;Bag 1]" + formspec = formspec.."button[2,2;2,0.5;bag2;Bag 2]" + formspec = formspec.."button[4,2;2,0.5;bag3;Bag 3]" + formspec = formspec.."button[6,2;2,0.5;bag4;Bag 4]" + formspec = formspec.."listcolors[#00000000;#00000000]" + formspec = formspec.."list[detached:"..minetest.formspec_escape(player_name).."_bags;bag1;0.5,1;1,1;]" + formspec = formspec.."list[detached:"..minetest.formspec_escape(player_name).."_bags;bag2;2.5,1;1,1;]" + formspec = formspec.."list[detached:"..minetest.formspec_escape(player_name).."_bags;bag3;4.5,1;1,1;]" + formspec = formspec.."list[detached:"..minetest.formspec_escape(player_name).."_bags;bag4;6.5,1;1,1;]" + return {formspec=formspec} + end, +}) + +unified_inventory.register_button("bags", { + type = "image", + image = "ui_bags_icon.png", + tooltip = S("Bags") +}) + +for i = 1, 4 do + unified_inventory.register_page("bag"..i, { + get_formspec = function(player) + local stack = player:get_inventory():get_stack("bag"..i, 1) + local image = stack:get_definition().inventory_image + local formspec = "image[7,0;1,1;"..image.."]" + formspec = formspec.."label[0,0;Bag "..i.."]" + formspec = formspec.."listcolors[#00000000;#00000000]" + formspec = formspec.."list[current_player;bag"..i.."contents;0,1;8,3;]" + local slots = stack:get_definition().groups.bagslots + if slots == 8 then + formspec = formspec.."background[0.06,0.99;7.92,7.52;ui_bags_sm_form.png]" + elseif slots == 16 then + formspec = formspec.."background[0.06,0.99;7.92,7.52;ui_bags_med_form.png]" + elseif slots == 24 then + formspec = formspec.."background[0.06,0.99;7.92,7.52;ui_bags_lg_form.png]" + end + return {formspec=formspec} + end, + }) +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname ~= "" then + return + end + for i = 1, 4 do + if fields["bag"..i] then + local stack = player:get_inventory():get_stack("bag"..i, 1) + if not stack:get_definition().groups.bagslots then + return + end + unified_inventory.set_inventory_formspec(player, "bag"..i) + return + end + end +end) + +minetest.register_on_joinplayer(function(player) + local player_inv = player:get_inventory() + local bags_inv = minetest.create_detached_inventory(player:get_player_name().."_bags",{ + on_put = function(inv, listname, index, stack, player) + player:get_inventory():set_stack(listname, index, stack) + player:get_inventory():set_size(listname.."contents", + stack:get_definition().groups.bagslots) + end, + on_take = function(inv, listname, index, stack, player) + player:get_inventory():set_stack(listname, index, nil) + end, + allow_put = function(inv, listname, index, stack, player) + if stack:get_definition().groups.bagslots then + return 1 + else + return 0 + end + end, + allow_take = function(inv, listname, index, stack, player) + if player:get_inventory():is_empty(listname.."contents") then + return stack:get_count() + else + return 0 + end + end, + allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) + return 0 + end, + }) + for i=1,4 do + local bag = "bag"..i + player_inv:set_size(bag, 1) + bags_inv:set_size(bag, 1) + bags_inv:set_stack(bag, 1, player_inv:get_stack(bag, 1)) + end +end) + +-- register bag tools +minetest.register_tool("unified_inventory:bag_small", { + description = S("Small Bag"), + inventory_image = "bags_small.png", + groups = {bagslots=8}, +}) + +minetest.register_tool("unified_inventory:bag_medium", { + description = S("Medium Bag"), + inventory_image = "bags_medium.png", + groups = {bagslots=16}, +}) + +minetest.register_tool("unified_inventory:bag_large", { + description = S("Large Bag"), + inventory_image = "bags_large.png", + groups = {bagslots=24}, +}) + +-- register bag crafts +minetest.register_craft({ + output = "unified_inventory:bag_small", + recipe = { + {"", "default:stick", ""}, + {"group:wood", "group:wood", "group:wood"}, + {"group:wood", "group:wood", "group:wood"}, + }, +}) + +minetest.register_craft({ + output = "unified_inventory:bag_medium", + recipe = { + {"", "", ""}, + {"default:stick", "unified_inventory:bag_small", "default:stick"}, + {"default:stick", "unified_inventory:bag_small", "default:stick"}, + }, +}) + +minetest.register_craft({ + output = "unified_inventory:bag_large", + recipe = { + {"", "", ""}, + {"default:stick", "unified_inventory:bag_medium", "default:stick"}, + {"default:stick", "unified_inventory:bag_medium", "default:stick"}, + }, +}) + diff --git a/inventory/callbacks.lua b/inventory/callbacks.lua new file mode 100644 index 0000000..605d8e5 --- /dev/null +++ b/inventory/callbacks.lua @@ -0,0 +1,182 @@ +local function default_refill(stack) + stack:set_count(stack:get_stack_max()) + local itemdef = minetest.registered_items[stack:get_name()] + if itemdef and (itemdef.wear_represents or "mechanical_wear") == "mechanical_wear" and stack:get_wear() ~= 0 then + stack:set_wear(0) + end + return stack +end + +minetest.register_on_joinplayer(function(player) + local player_name = player:get_player_name() + unified_inventory.players[player_name] = {} + unified_inventory.current_index[player_name] = 1 + unified_inventory.filtered_items_list[player_name] = + unified_inventory.items_list + unified_inventory.activefilter[player_name] = "" + unified_inventory.active_search_direction[player_name] = "nochange" + unified_inventory.apply_filter(player, "", "nochange") + unified_inventory.current_searchbox[player_name] = "" + unified_inventory.alternate[player_name] = 1 + unified_inventory.current_item[player_name] = nil + unified_inventory.current_craft_direction[player_name] = "recipe" + unified_inventory.set_inventory_formspec(player, + unified_inventory.default) + + -- Refill slot + local refill = minetest.create_detached_inventory(player_name.."refill", { + allow_put = function(inv, listname, index, stack, player) + local player_name = player:get_player_name() + if unified_inventory.is_creative(player_name) then + return stack:get_count() + else + return 0 + end + end, + on_put = function(inv, listname, index, stack, player) + local player_name = player:get_player_name() + local handle_refill = (minetest.registered_items[stack:get_name()] or {}).on_refill or default_refill + stack = handle_refill(stack) + inv:set_stack(listname, index, stack) + minetest.sound_play("electricity", + {to_player=player_name, gain = 1.0}) + end, + }) + refill:set_size("main", 1) +end) + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname ~= "" then + return + end + local player_name = player:get_player_name() + + -- always take new search text, even if not searching on it yet + if fields.searchbox ~= nil and fields.searchbox ~= unified_inventory.current_searchbox[player_name] then + unified_inventory.current_searchbox[player_name] = fields.searchbox + unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name]) + end + + for i, def in pairs(unified_inventory.buttons) do + if fields[def.name] then + def.action(player) + minetest.sound_play("click", + {to_player=player_name, gain = 0.1}) + return + end + end + + -- Inventory page controls + local start = math.floor( + unified_inventory.current_index[player_name] / 80 + 1) + local start_i = start + local pagemax = math.floor( + (#unified_inventory.filtered_items_list[player_name] - 1) + / (80) + 1) + + if fields.start_list then + start_i = 1 + end + if fields.rewind1 then + start_i = start_i - 1 + end + if fields.forward1 then + start_i = start_i + 1 + end + if fields.rewind3 then + start_i = start_i - 3 + end + if fields.forward3 then + start_i = start_i + 3 + end + if fields.end_list then + start_i = pagemax + end + if start_i < 1 then + start_i = 1 + end + if start_i > pagemax then + start_i = pagemax + end + if not (start_i == start) then + minetest.sound_play("paperflip1", + {to_player=player_name, gain = 1.0}) + unified_inventory.current_index[player_name] = (start_i - 1) * 80 + 1 + unified_inventory.set_inventory_formspec(player, + unified_inventory.current_page[player_name]) + end + + local clicked_item = nil + for name, value in pairs(fields) do + if string.sub(name, 1, 12) == "item_button_" then + local new_dir, mangled_item = string.match(name, "^item_button_([a-z]+)_(.*)$") + clicked_item = unified_inventory.demangle_for_formspec(mangled_item) + if string.sub(clicked_item, 1, 6) == "group:" then + minetest.sound_play("click", {to_player=player_name, gain = 0.1}) + unified_inventory.apply_filter(player, clicked_item, new_dir) + return + end + if new_dir == "recipe" or new_dir == "usage" then + unified_inventory.current_craft_direction[player_name] = new_dir + end + break + end + end + if clicked_item then + minetest.sound_play("click", + {to_player=player_name, gain = 0.1}) + local page = unified_inventory.current_page[player_name] + if not unified_inventory.is_creative(player_name) then + page = "craftguide" + end + if page == "craftguide" then + unified_inventory.current_item[player_name] = clicked_item + unified_inventory.alternate[player_name] = 1 + unified_inventory.set_inventory_formspec(player, + "craftguide") + else + if unified_inventory.is_creative(player_name) then + local inv = player:get_inventory() + local stack = ItemStack(clicked_item) + stack:set_count(stack:get_stack_max()) + if inv:room_for_item("main", stack) then + inv:add_item("main", stack) + end + end + end + end + + if fields.searchbutton then + unified_inventory.apply_filter(player, unified_inventory.current_searchbox[player_name], "nochange") + unified_inventory.current_searchbox[player_name] = "" + unified_inventory.set_inventory_formspec(player, + unified_inventory.current_page[player_name]) + minetest.sound_play("paperflip2", + {to_player=player_name, gain = 1.0}) + end + + -- alternate button + if fields.alternate then + minetest.sound_play("click", + {to_player=player_name, gain = 0.1}) + local item_name = unified_inventory.current_item[player_name] + if item_name then + local alternates = 0 + local alternate = unified_inventory.alternate[player_name] + local crafts = unified_inventory.crafts_for[unified_inventory.current_craft_direction[player_name]][item_name] + if crafts ~= nil then + alternates = #crafts + end + if alternates > 1 then + alternate = alternate + 1 + if alternate > alternates then + alternate = 1 + end + unified_inventory.alternate[player_name] = alternate + unified_inventory.set_inventory_formspec(player, + unified_inventory.current_page[player_name]) + end + end + end +end) + diff --git a/inventory/depends.txt b/inventory/depends.txt new file mode 100644 index 0000000..6523f7a --- /dev/null +++ b/inventory/depends.txt @@ -0,0 +1,5 @@ +default +clothing +creativ? +intllib? +datastorage? diff --git a/inventory/group.lua b/inventory/group.lua new file mode 100644 index 0000000..9bf6895 --- /dev/null +++ b/inventory/group.lua @@ -0,0 +1,108 @@ +function unified_inventory.canonical_item_spec_matcher(spec) + local specname = ItemStack(spec):get_name() + if specname:sub(1, 6) == "group:" then + 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 + else + return function (itemname) return itemname == specname end + 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 + +unified_inventory.registered_group_items = { + mesecon_conductor_craftable = "mesecons:wire_00000000_off", + stone = "default:cobble", + 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 + diff --git a/inventory/image_credits.txt b/inventory/image_credits.txt new file mode 100644 index 0000000..b1489e4 --- /dev/null +++ b/inventory/image_credits.txt @@ -0,0 +1,66 @@ +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_craftguide_icon.png / ui_craft_icon.png + http://commons.wikimedia.org/wiki/File:Advancedsettings.png + +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 + +ui_gohome_icon.png / ui_home_icon.png / ui_sethome_icon.png + http://commons.wikimedia.org/wiki/File:Home_256x256.png + +ui_moon_icon.png + http://commons.wikimedia.org/wiki/File:FullMoon2010.jpg + +ui_sun_icon.png + http://commons.wikimedia.org/wiki/File:2012-10-13_15-29-35-sun.jpg + +ui_trash_icon.png + http://www.clker.com/clipart-29090.html + 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 + +ui_xyz_off_icon.png + http://commons.wikimedia.org/wiki/File:No_sign.svg + +ui_ok_icon.png + http://commons.wikimedia.org/wiki/File:Yes_check.svg + +inventory_plus_worldedit_gui.png + http://commons.wikimedia.org/wiki/File:Erioll_world_2.svg diff --git a/inventory/init.lua b/inventory/init.lua new file mode 100644 index 0000000..e1d2c02 --- /dev/null +++ b/inventory/init.lua @@ -0,0 +1,51 @@ +-- Unified Inventory for Minetest 0.4.8+ + +local modpath = minetest.get_modpath(minetest.get_current_modname()) +local worldpath = minetest.get_worldpath() + +-- Data tables definitions +unified_inventory = {} +unified_inventory.activefilter = {} +unified_inventory.active_search_direction = {} +unified_inventory.alternate = {} +unified_inventory.current_page = {} +unified_inventory.current_searchbox = {} +unified_inventory.current_index = {} +unified_inventory.current_item = {} +unified_inventory.current_craft_direction = {} +unified_inventory.registered_craft_types = {} +unified_inventory.crafts_for = { usage = {}, recipe = {} } +unified_inventory.players = {} +unified_inventory.items_list_size = 0 +unified_inventory.items_list = {} +unified_inventory.filtered_items_list_size = {} +unified_inventory.filtered_items_list = {} +unified_inventory.pages = {} +unified_inventory.buttons = {} + +-- Homepos stuff +unified_inventory.home_pos = {} +unified_inventory.home_filename = + worldpath.."/unified_inventory_home.home" + +-- Default inventory page +unified_inventory.default = "craft" + +-- Disable default creative inventory +if creative_inventory then + function creative_inventory.set_creative_formspec(player, start_i, pagenum) + return + end +end + +dofile(modpath.."/group.lua") +dofile(modpath.."/api.lua") +dofile(modpath.."/internal.lua") +dofile(modpath.."/callbacks.lua") +dofile(modpath.."/register.lua") +dofile(modpath.."/bags.lua") +dofile(modpath.."/item_names.lua") +if minetest.get_modpath("datastorage") then + dofile(modpath.."/waypoints.lua") +end + diff --git a/inventory/internal.lua b/inventory/internal.lua new file mode 100644 index 0000000..281653e --- /dev/null +++ b/inventory/internal.lua @@ -0,0 +1,233 @@ +local S +if intllib then + S = intllib.Getter() +else + S = function(s) return s end +end + +-- 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_formspec(player, page) + if not player then + return "" + end + local player_name = player:get_player_name() + unified_inventory.current_page[player_name] = page + local pagedef = unified_inventory.pages[page] + + local formspec = "size[14,10]" + local fsdata = nil + + -- Background + formspec = formspec .. "background[-0.19,-0.25;14.4,10.75;ui_form_bg.png]" + -- Current page + if unified_inventory.pages[page] then + fsdata = pagedef.get_formspec(player) + formspec = formspec .. fsdata.formspec + else + return "" -- Invalid page name + end + + -- Main buttons + for i, def in pairs(unified_inventory.buttons) do + local tooltip = def.tooltip or "" + if def.type == "image" then + formspec = formspec.."image_button[" + ..(0.65 * (i - 1))..",9;0.8,0.8;" + ..minetest.formspec_escape(def.image)..";" + ..minetest.formspec_escape(def.name)..";]" + .."tooltip["..minetest.formspec_escape(def.name) + ..";"..tooltip.."]" + end + end + + if fsdata.draw_inventory ~= false then + -- Player inventory + formspec = formspec.."listcolors[#00000000;#00000000]" + formspec = formspec .. "list[current_player;main;0,4.5;8,4;]" + end + + if fsdata.draw_item_list == false then + return formspec + end + + -- Controls to flip items pages + local start_x = 9.2 + formspec = formspec + .. "image_button[" .. (start_x + 0.6 * 0) + .. ",9;.8,.8;ui_skip_backward_icon.png;start_list;]" + .. "tooltip[start_list;" .. minetest.formspec_escape(S("First page")) .. "]" + + .. "image_button[" .. (start_x + 0.6 * 1) + .. ",9;.8,.8;ui_doubleleft_icon.png;rewind3;]" + .. "tooltip[rewind3;" .. minetest.formspec_escape(S("Back three pages")) .. "]" + + .. "image_button[" .. (start_x + 0.6 * 2) + .. ",9;.8,.8;ui_left_icon.png;rewind1;]" + .. "tooltip[rewind1;" .. minetest.formspec_escape(S("Back one page")) .. "]" + + .. "image_button[" .. (start_x + 0.6 * 3) + .. ",9;.8,.8;ui_right_icon.png;forward1;]" + .. "tooltip[forward1;" .. minetest.formspec_escape(S("Forward one page")) .. "]" + + .. "image_button[" .. (start_x + 0.6 * 4) + .. ",9;.8,.8;ui_doubleright_icon.png;forward3;]" + .. "tooltip[forward3;" .. minetest.formspec_escape(S("Forward three pages")) .. "]" + + .. "image_button[" .. (start_x + 0.6 * 5) + .. ",9;.8,.8;ui_skip_forward_icon.png;end_list;]" + .. "tooltip[end_list;" .. minetest.formspec_escape(S("Last page")) .. "]" + + -- Search box + formspec = formspec .. "field[9.5,8.325;3,1;searchbox;;" + .. minetest.formspec_escape(unified_inventory.current_searchbox[player_name]) .. "]" + formspec = formspec .. "image_button[12.2,8.1;.8,.8;ui_search_icon.png;searchbutton;]" + .. "tooltip[searchbutton;" ..S("Search") .. "]" + + -- Items list + if #unified_inventory.filtered_items_list[player_name] == 0 then + formspec = formspec.."label[8.2,0;" .. S("No matching items") .. "]" + 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 / (80) + 1) + local pagemax = math.floor( + (#unified_inventory.filtered_items_list[player_name] - 1) + / (80) + 1) + local item = {} + for y = 0, 9 do + for x = 0, 7 do + local name = unified_inventory.filtered_items_list[player_name][list_index] + if minetest.registered_items[name] then + formspec = formspec.."item_image_button[" + ..(8.2 + x * 0.7).."," + ..(1 + y * 0.7)..";.81,.81;" + ..name..";item_button_"..dir.."_" + ..unified_inventory.mangle_for_formspec(name)..";]" + list_index = list_index + 1 + end + end + end + formspec = formspec.."label[8.2,0;"..S("Page") .. ": " + .. S("%s of %s"):format(page,pagemax).."]" + end + if unified_inventory.activefilter[player_name] ~= "" then + formspec = formspec.."label[8.2,0.4;" .. S("Filter") .. ":]" + formspec = formspec.."label[9,0.4;"..minetest.formspec_escape(unified_inventory.activefilter[player_name]).."]" + end + return formspec +end + +function unified_inventory.set_inventory_formspec(player, page) + if player then + local formspec = unified_inventory.get_formspec(player, page) + player:set_inventory_formspec(formspec) + end +end + +--apply filter to the inventory list (create filtered copy of full one) +function unified_inventory.apply_filter(player, filter, search_dir) + 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 0) > 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 (def.groups.not_in_creative_inventory or 0) == 0 and (def.description or "") ~= "" 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 diff --git a/inventory/item_names.lua b/inventory/item_names.lua new file mode 100644 index 0000000..31c2b3f --- /dev/null +++ b/inventory/item_names.lua @@ -0,0 +1,58 @@ +-- code based on 4itemnames mod by 4aiman + +local wield = {} +local huds = {} +local dtimes = {} +local dlimit = 3 -- hud will be hidden after this much seconds +local airhudmod = minetest.get_modpath("4air") + +local function get_desc(item) + if minetest.registered_nodes[item] then return minetest.registered_nodes[item]["description"] end + if minetest.registered_items[item] then return minetest.registered_items[item]["description"] end + if minetest.registered_craftitems[item] then return minetest.registered_craftitems[item]["description"] end + if minetest.registered_tools[item] then return minetest.registered_tools[item]["description"] end + return "" +end + +minetest.register_on_joinplayer(function(player) + minetest.after(0.0, function() + local player_name = player:get_player_name() + local off = {x=0, y=-70} + if airhudmod then + off.y=off.y-20 + end + huds[player_name] = player:hud_add({ + hud_elem_type = "text", + position = {x=0.5, y=1}, + offset = off, + alignment = {x=0, y=0}, + number = 0xFFFFFF , + text = "", + }) + --print(dump("item hud id: "..huds[player_name])) + end) +end) + +minetest.register_globalstep(function(dtime) + local players = minetest.get_connected_players() + for i,player in ipairs(players) do + local player_name = player:get_player_name() + local wstack = player:get_wielded_item():get_name() + + if dtimes[player_name] and dtimes[player_name] < dlimit then + dtimes[player_name] = dtimes[player_name] + dtime + if dtimes[player_name] > dlimit and huds[player_name] then + player:hud_change(huds[player_name], 'text', "") + end + end + + if wstack ~= wield[player_name] then + wield[player_name] = wstack + local desc = get_desc(wstack) + dtimes[player_name] = 0 + if huds[player_name] then + player:hud_change(huds[player_name], 'text', desc) + end + end + end +end) diff --git a/inventory/locale/de.txt b/inventory/locale/de.txt new file mode 100644 index 0000000..9f12d8c --- /dev/null +++ b/inventory/locale/de.txt @@ -0,0 +1,71 @@ +# Translation by Xanthin + +### bags.lua ### +Bags = Rucksaecke +Bag 1 = Rucksack 1 +Bag 2 = Rucksack 2 +Bag 3 = Rucksack 3 +Bag 4 = Rucksack 4 +Small Bag = Rucksack (klein) +Medium Bag = Rucksack (mittel) +Large Bag = Rucksack (gross) + +### inernal.lua ### +First page = +Back three pages = +Back one page = +Forward one page = +Forward three pages = +Last page = +No matching items = +Page = Seite +%s of %s = %s von %s +Filter = Suche +Search = + +### register.lua ### +Can use the creative inventory = Kann das Kreativinventar nutzen +Home position set to: %s = Ausgangsposition nach: %s gesetzt +Time of day set to 6am = Tageszeit auf 6 Uhr morgens geaendert +You don't have the settime priviledge! = Du hast nicht das "settime" Privileg! +Time of day set to 9pm = Tageszeit auf 9 Uhr abends geaendert +This button has been disabled outside of creative mode to prevent accidental inventory trashing. Use the trash slot instead. = Diese Funktion ist ausserhalb des Kreativmodus deaktiviert um ein versehentliches Loeschen des ganzen Inventars zu verhindern.\nNutze stattdessen das Muellfeld. +Inventory Cleared! = Inventar geleert! +Crafting = Bauen +Trash: = Muell: +Refill: = Nachfuellen: +Crafting Guide = Bauanleitung +Method: = Methode: +Result: %s = Ergebnis: %s +crafting = Bauen +shapeless crafting = Formloses Bauen +cooking = Kochen +alloy cooking = Legierung Kochen +Copy to craft grid: = Kopiere ins Baufeld: +All = Alles +Recipe %s of %s = Rezept %s von %s +Alternate = Alternative +Crafting Grid = + +### waypoints.lua ### +White = +Yellow = +Red = +Green = +Blue = +Waypoints = +Waypoint active = +Waypoint inactive = +World position = +Name = +HUD text color = +Edit waypoint name = +Rename waypoint = +Change color of waypoint display = +Set waypoint to current location = +Make waypoint visible = +Make waypoint invisible = +Disable display of waypoint coordinates = +Enable display of waypoint coordinates = +Finish editing = +Select Waypoint #%d = diff --git a/inventory/locale/es.txt b/inventory/locale/es.txt new file mode 100644 index 0000000..9382c79 --- /dev/null +++ b/inventory/locale/es.txt @@ -0,0 +1,72 @@ +# Translation by Diego Martínez + +# Template +### bags.lua ### +Bags = Bolsas +Bag 1 = Bolsa 1 +Bag 2 = Bolsa 2 +Bag 3 = Bolsa 3 +Bag 4 = Bolsa 4 +Small Bag = Bolsa Pequeña +Medium Bag = Bolsa Mediana +Large Bag = Bolsa Grande + +### inernal.lua ### +First page = +Back three pages = +Back one page = +Forward one page = +Forward three pages = +Last page = +No matching items = +Page = Página +%s of %s = %s de %s +Filter = Filtro +Search = + +### register.lua ### +Can use the creative inventory = Puede usar el inventario creativo +Home position set to: %s = Posición de hogar cambiada a: %s +Time of day set to 6am = Hora del día cambiada a 6AM +You don't have the settime priviledge! = ¡No tienes el privilegio `settime'! +Time of day set to 9pm = Hora del día cambiada a 9PM +This button has been disabled outside of creative mode to prevent accidental inventory trashing. Use the trash slot instead. = Éste botón ha sido deshabilitado para prevenir la destrucción accidental del inventario.\nUsa la ranura para basura en su lugar. +Inventory Cleared! = ¡Inventario limpio! +Crafting = Elaboración +Trash: = Basura: +Refill: = Rellenar: +Crafting Guide = Guía de Elaboración +Method: = Método: +Result: %s = Resultado: %s +crafting = elaboración +shapeless crafting = elaboración sin forma +cooking = hornear +alloy cooking = horneado de aleación +Copy to craft grid: = Copiar al cuadro de elaboración +All = Todos +Recipe %s of %s = Receta %s de %s +Alternate = Alternar +Crafting Grid = + +### waypoints.lua ### +White = Blanco +Yellow = Amarillo +Red = Rojo +Green = Verde +Blue = Azul +Waypoints = Puntos de paso +Waypoint active = Punto de paso activo +Waypoint inactive = Punto de paso inactivo +World position = Posición en el mundo +Name = Nombre +HUD text color = Color del HUD +Edit waypoint name = +Rename waypoint = +Change color of waypoint display = +Set waypoint to current location = +Make waypoint visible = +Make waypoint invisible = +Disable display of waypoint coordinates = +Enable display of waypoint coordinates = +Finish editing = +Select Waypoint #%d = diff --git a/inventory/locale/pl.txt b/inventory/locale/pl.txt new file mode 100644 index 0000000..33003d8 --- /dev/null +++ b/inventory/locale/pl.txt @@ -0,0 +1,71 @@ +# Translation by RealBadAngel + +### bags.lua ### +Bags = Plecaki +Bag 1 = Plecak 1 +Bag 2 = Plecak 2 +Bag 3 = Plecak 3 +Bag 4 = Plecak 4 +Small Bag = Maly plecak +Medium Bag = Sredni plecak +Large Bag = Duzy plecak + +### inernal.lua ### +First page = Pierwsza strona +Back three pages = 3 strony w tyl +Back one page = 1 strona w tyl +Forward one page = 1 strona do przodu +Forward three pages = 3 strony do przodu +Last page = Ostatnia strona +No matching items = Brak pasujacych przedmiotow +Page = Strona +%s of %s = %s z %s +Filter = Filtr +Search = Szukaj + +### register.lua ### +Can use the creative inventory = +Home position set to: %s = Pozycja domowa ustawiona na: %s +Time of day set to 6am = Czas ustawiony na 6:00 +You don't have the settime priviledge! = Nie masz uprawnien do zmiany czasu (settime)! +Time of day set to 9pm = Czas ustawiony na 21:00 +This button has been disabled outside of creative mode to prevent accidental inventory trashing.\nUse the trash slot instead. = +Inventory Cleared! = +Crafting = +Trash: = Smietnik: +Refill: = Uzupelnianie: +Crafting Guide = +Method: = Metoda: +Result: %s = Wynik: %s +crafting = +shapeless crafting = +cooking = +alloy cooking = +Copy to craft grid: = +All = Wszystko +Recipe %s of %s = Recepta %s z %s +Alternate = Alternatywa +Crafting Grid = + +### waypoints.lua ### +White = Bialy +Yellow = Zolty +Red = Czerwony +Green = Zielony +Blue = Niebieski +Waypoints = Punkty orientacyjne +Waypoint active = Punkt wlaczony +Waypoint inactive = Punkt wylaczony +World position = Pozycja +Name = Nazwa +HUD text color = Kolor tekstu HUD +Edit waypoint name = Edytuj nazwe punktu +Rename waypoint = Zmien nazwe punktu +Change color of waypoint display = Zmien kolor punktu +Set waypoint to current location = Ustaw punkt orientacyjny na biezacej pozycji +Make waypoint visible = Pokaz punkt +Make waypoint invisible = Nie pokazuj punktu +Disable display of waypoint coordinates = Pokazuj koordynaty punktu +Enable display of waypoint coordinates = Nie pokazuj koordynatow punktu +Finish editing = Zakoncz edycje +Select Waypoint #%d = Wybierz punkt #%d diff --git a/inventory/locale/template.txt b/inventory/locale/template.txt new file mode 100644 index 0000000..ae382e3 --- /dev/null +++ b/inventory/locale/template.txt @@ -0,0 +1,72 @@ +# Translation by + +# Template +### bags.lua ### +Bags = +Bag 1 = +Bag 2 = +Bag 3 = +Bag 4 = +Small Bag = +Medium Bag = +Large Bag = + +### inernal.lua ### +First page = +Back three pages = +Back one page = +Forward one page = +Forward three pages = +Last page = +No matching items = +Page = +%s of %s = +Filter = +Search = + +### register.lua ### +Can use the creative inventory = +Home position set to: %s = +Time of day set to 6am = +You don't have the settime priviledge! = +Time of day set to 9pm = +This button has been disabled outside of creative mode to prevent accidental inventory trashing.\nUse the trash slot instead. = +Inventory Cleared! = +Crafting = +Trash: = +Refill: = +Crafting Guide = +Method: = +Result: %s = +crafting = +shapeless crafting = +cooking = +alloy cooking = +Copy to craft grid: = +All = +Recipe %s of %s = +Alternate = +Crafting Grid = + +### waypoints.lua ### +White = +Yellow = +Red = +Green = +Blue = +Waypoints = +Waypoint active = +Waypoint inactive = +World position = +Name = +HUD text color = +Edit waypoint name = +Rename waypoint = +Change color of waypoint display = +Set waypoint to current location = +Make waypoint visible = +Make waypoint invisible = +Disable display of waypoint coordinates = +Enable display of waypoint coordinates = +Finish editing = +Select Waypoint #%d = diff --git a/inventory/register.lua b/inventory/register.lua new file mode 100644 index 0000000..bc379ee --- /dev/null +++ b/inventory/register.lua @@ -0,0 +1,346 @@ +local S +if intllib then + S = intllib.Getter() +else + S = function(s) return s end +end + +minetest.register_privilege("creative", { + description = "Can use the creative inventory", + 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"), + action = function(player) + local player_name = player:get_player_name() + unified_inventory.set_home(player, player:getpos()) + 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, + S("Home position set to: %s"):format(minetest.pos_to_string(home))) + end + end, +}) + +unified_inventory.register_button("home_gui_go", { + type = "image", + image = "ui_gohome_icon.png", + tooltip = S("Go home"), + action = function(player) + minetest.sound_play("teleport", + {to_player=player:get_player_name(), gain = 1.0}) + unified_inventory.go_home(player) + end, +}) + +unified_inventory.register_button("misc_set_day", { + type = "image", + image = "ui_sun_icon.png", + tooltip = S("Set time to day"), + 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 priviledge!")) + end + end, +}) + +unified_inventory.register_button("misc_set_night", { + type = "image", + image = "ui_moon_icon.png", + tooltip = S("Set time to night"), + 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 priviledge!")) + end + 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.")) + return + end + player:get_inventory():set_list("main", {}) + minetest.chat_send_player(player_name, 'Inventory Cleared!') + minetest.sound_play("trash_all", + {to_player=player_name, gain = 1.0}) + end, +}) + +unified_inventory.register_page("craft", { + get_formspec = function(player, formspec) + local player_name = player:get_player_name() + local formspec = "background[0,1;8,3;ui_crafting_form.png]" + formspec = formspec.."background[0,4.5;8,4;ui_main_inventory.png]" + formspec = formspec.."label[0,0;Crafting]" + formspec = formspec.."listcolors[#00000000;#00000000]" + formspec = formspec.."list[current_player;craftpreview;6,1;1,1;]" + formspec = formspec.."list[current_player;craft;2,1;3,3;]" + formspec = formspec.."label[7,2.5;" .. S("Trash:") .. "]" + formspec = formspec.."list[detached:trash;main;7,3;1,1;]" + if unified_inventory.is_creative(player_name) then + formspec = formspec.."label[0,2.5;" .. S("Refill:") .. "]" + formspec = formspec.."list[detached:"..minetest.formspec_escape(player_name).."refill;main;0,3;1,1;]" + end + return {formspec=formspec} + end, +}) + + +-- stack_image_button(): generate a form button displaying a stack of items +-- +-- Normally a simple item_image_button[] is used. If the stack contains +-- more than one item, item_image_button[] doesn't have an option to +-- display an item count in the way that an inventory slot does, so +-- we have to fake it using the label facility. +-- +-- 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 + 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 = string.format("\n\n%s%7d", show_is_group and "G" or " ", count):gsub(" 1$", " .") + if label == "\n\n ." then label = "" end + return string.format("item_image_button[%u,%u;%u,%u;%s;%s;%s]", + x, y, w, h, + minetest.formspec_escape(displayitem), + minetest.formspec_escape(buttonname_prefix..unified_inventory.mangle_for_formspec(selectitem)), + label) +end + +local recipe_text = { + recipe = "Recipe", + usage = "Usage", +} +local no_recipe_text = { + recipe = "No recipes", + usage = "No usages", +} +local role_text = { + recipe = "Result", + usage = "Ingredient", +} +local other_dir = { + recipe = "usage", + usage = "recipe", +} + +unified_inventory.register_page("craftguide", { + get_formspec = function(player) + local player_name = player:get_player_name() + local formspec = "" + formspec = formspec.."background[0,4.5;8,4;ui_main_inventory.png]" + formspec = formspec.."label[0,0;" .. S("Crafting Guide") .. "]" + formspec = formspec.."listcolors[#00000000;#00000000]" + local item_name = unified_inventory.current_item[player_name] + if not item_name then return {formspec=formspec} end + + local dir = unified_inventory.current_craft_direction[player_name] + local crafts = unified_inventory.crafts_for[dir][item_name] + local alternate = unified_inventory.alternate[player_name] + local alternates, craft + if crafts ~= nil and #crafts > 0 then + alternates = #crafts + craft = crafts[alternate] + end + + formspec = formspec.."background[0,1;8,3;ui_craftguide_form.png]" + formspec = formspec.."textarea[0.3,0.6;10,1;;"..minetest.formspec_escape(role_text[dir]..": "..item_name)..";]" + + if not craft then + formspec = formspec.."label[6,3.35;"..minetest.formspec_escape(no_recipe_text[dir]).."]" + local no_pos = dir == "recipe" and 4 or 6 + local item_pos = dir == "recipe" and 6 or 4 + formspec = formspec.."image["..no_pos..",1;1.1,1.1;ui_no.png]" + formspec = formspec..stack_image_button(item_pos, 1, 1.1, 1.1, "item_button_"..other_dir[dir].."_", ItemStack(item_name)) + return {formspec = formspec} + end + + local craft_type = unified_inventory.registered_craft_types[craft.type] or + unified_inventory.craft_type_defaults(craft.type, {}) + formspec = formspec.."label[6,3.35;" .. S("Method:") .. "]" + formspec = formspec.."label[6,3.75;" + ..minetest.formspec_escape(craft_type.description).."]" + formspec = formspec..stack_image_button(6, 1, 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 = 1 + (3 - display_size.width) + 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 + if item then + formspec = formspec..stack_image_button( + xoffset + x, y, 1.1, 1.1, + "item_button_recipe_", + ItemStack(item)) + else + -- Fake buttons just to make grid + formspec = formspec.."image_button[" + ..tostring(xoffset + x)..","..tostring(y) + ..";1,1;ui_blank_image.png;;]" + end + end + end + + if craft_type.uses_crafting_grid then + formspec = formspec.."label[6,1.95;" .. S("Copy to craft grid:") .. "]" + .."button[6,2.5;0.6,0.5;craftguide_craft_1;1]" + .."button[6.6,2.5;0.6,0.5;craftguide_craft_10;10]" + .."button[7.2,2.5;0.6,0.5;craftguide_craft_max;" .. S("All") .. "]" + end + + if alternates and alternates > 1 then + formspec = formspec.."label[0,2.6;"..recipe_text[dir].." " + ..tostring(alternate).." of " + ..tostring(alternates).."]" + .."button[0,3.15;2,1;alternate;" .. S("Alternate") .. "]" + end + return {formspec = formspec} + end, +}) + +minetest.register_on_player_receive_fields(function(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 + local player_name = player:get_player_name() + + local output = unified_inventory.current_item[player_name] + if (not output) or (output == "") then return end + + local player_inv = player:get_inventory() + + local crafts = unified_inventory.crafts_for[unified_inventory.current_craft_direction[player_name]][output] + if (not crafts) or (#crafts == 0) then return end + + local alternate = unified_inventory.alternate[player_name] + + local craft = crafts[alternate] + if craft.width > 3 then return end + + local needed = craft.items + + local craft_list = player_inv:get_list("craft") + + local width = craft.width + if width == 0 then + -- Shapeless recipe + width = 3 + end + + if amount == "max" then + amount = 99 -- Arbitrary; need better way to do this. + else + amount = tonumber(amount) + end + + for iter = 1, amount do + local index = 1 + for y = 1, 3 do + for x = 1, width do + local needed_item = needed[index] + if needed_item then + local craft_index = ((y - 1) * 3) + x + local craft_item = craft_list[craft_index] + if (not craft_item) or (craft_item:is_empty()) or (craft_item:get_name() == needed_item) then + itemname = craft_item and craft_item:get_name() or needed_item + local needed_stack = ItemStack(needed_item) + if player_inv:contains_item("main", needed_stack) then + local count = (craft_item and craft_item:get_count() or 0) + 1 + if count <= needed_stack:get_definition().stack_max then + local stack = ItemStack({name=needed_item, count=count}) + craft_list[craft_index] = stack + player_inv:remove_item("main", needed_stack) + end + end + end + end + index = index + 1 + end + end + end + + player_inv:set_list("craft", craft_list) + + unified_inventory.set_inventory_formspec(player, "craft") +end) diff --git a/inventory/sounds/birds.ogg b/inventory/sounds/birds.ogg new file mode 100644 index 0000000..4a93395 Binary files /dev/null and b/inventory/sounds/birds.ogg differ diff --git a/inventory/sounds/click.ogg b/inventory/sounds/click.ogg new file mode 100644 index 0000000..3db63a0 Binary files /dev/null and b/inventory/sounds/click.ogg differ diff --git a/inventory/sounds/dingdong.ogg b/inventory/sounds/dingdong.ogg new file mode 100644 index 0000000..2c9d7ef Binary files /dev/null and b/inventory/sounds/dingdong.ogg differ diff --git a/inventory/sounds/electricity.ogg b/inventory/sounds/electricity.ogg new file mode 100644 index 0000000..4cd7c84 Binary files /dev/null and b/inventory/sounds/electricity.ogg differ diff --git a/inventory/sounds/owl.ogg b/inventory/sounds/owl.ogg new file mode 100644 index 0000000..f30d0b3 Binary files /dev/null and b/inventory/sounds/owl.ogg differ diff --git a/inventory/sounds/paperflip1.ogg b/inventory/sounds/paperflip1.ogg new file mode 100644 index 0000000..eaed13f Binary files /dev/null and b/inventory/sounds/paperflip1.ogg differ diff --git a/inventory/sounds/paperflip2.ogg b/inventory/sounds/paperflip2.ogg new file mode 100644 index 0000000..321bc48 Binary files /dev/null and b/inventory/sounds/paperflip2.ogg differ diff --git a/inventory/sounds/teleport.ogg b/inventory/sounds/teleport.ogg new file mode 100644 index 0000000..ca32f74 Binary files /dev/null and b/inventory/sounds/teleport.ogg differ diff --git a/inventory/sounds/trash.ogg b/inventory/sounds/trash.ogg new file mode 100644 index 0000000..51e4f24 Binary files /dev/null and b/inventory/sounds/trash.ogg differ diff --git a/inventory/sounds/trash_all.ogg b/inventory/sounds/trash_all.ogg new file mode 100644 index 0000000..85c3f66 Binary files /dev/null and b/inventory/sounds/trash_all.ogg differ diff --git a/inventory/textures/bags_large.png b/inventory/textures/bags_large.png new file mode 100644 index 0000000..38cf6bc Binary files /dev/null and b/inventory/textures/bags_large.png differ diff --git a/inventory/textures/bags_medium.png b/inventory/textures/bags_medium.png new file mode 100644 index 0000000..f048690 Binary files /dev/null and b/inventory/textures/bags_medium.png differ diff --git a/inventory/textures/bags_small.png b/inventory/textures/bags_small.png new file mode 100644 index 0000000..bf6fe6a Binary files /dev/null and b/inventory/textures/bags_small.png differ diff --git a/inventory/textures/ui_1_icon.png b/inventory/textures/ui_1_icon.png new file mode 100644 index 0000000..43605e0 Binary files /dev/null and b/inventory/textures/ui_1_icon.png differ diff --git a/inventory/textures/ui_2_icon.png b/inventory/textures/ui_2_icon.png new file mode 100644 index 0000000..dd64510 Binary files /dev/null and b/inventory/textures/ui_2_icon.png differ diff --git a/inventory/textures/ui_3_icon.png b/inventory/textures/ui_3_icon.png new file mode 100644 index 0000000..132dc3a Binary files /dev/null and b/inventory/textures/ui_3_icon.png differ diff --git a/inventory/textures/ui_4_icon.png b/inventory/textures/ui_4_icon.png new file mode 100644 index 0000000..9b7e430 Binary files /dev/null and b/inventory/textures/ui_4_icon.png differ diff --git a/inventory/textures/ui_5_icon.png b/inventory/textures/ui_5_icon.png new file mode 100644 index 0000000..699e08b Binary files /dev/null and b/inventory/textures/ui_5_icon.png differ diff --git a/inventory/textures/ui_bags_icon.png b/inventory/textures/ui_bags_icon.png new file mode 100644 index 0000000..38cf6bc Binary files /dev/null and b/inventory/textures/ui_bags_icon.png differ diff --git a/inventory/textures/ui_bags_lg_form.png b/inventory/textures/ui_bags_lg_form.png new file mode 100644 index 0000000..15f511d Binary files /dev/null and b/inventory/textures/ui_bags_lg_form.png differ diff --git a/inventory/textures/ui_bags_main_form.png b/inventory/textures/ui_bags_main_form.png new file mode 100644 index 0000000..26e6938 Binary files /dev/null and b/inventory/textures/ui_bags_main_form.png differ diff --git a/inventory/textures/ui_bags_med_form.png b/inventory/textures/ui_bags_med_form.png new file mode 100644 index 0000000..f786806 Binary files /dev/null and b/inventory/textures/ui_bags_med_form.png differ diff --git a/inventory/textures/ui_bags_sm_form.png b/inventory/textures/ui_bags_sm_form.png new file mode 100644 index 0000000..c77ff7c Binary files /dev/null and b/inventory/textures/ui_bags_sm_form.png differ diff --git a/inventory/textures/ui_blank_image.png b/inventory/textures/ui_blank_image.png new file mode 100644 index 0000000..f9bcda2 Binary files /dev/null and b/inventory/textures/ui_blank_image.png differ diff --git a/inventory/textures/ui_blue_icon_background.png b/inventory/textures/ui_blue_icon_background.png new file mode 100644 index 0000000..b4fa356 Binary files /dev/null and b/inventory/textures/ui_blue_icon_background.png differ diff --git a/inventory/textures/ui_circular_arrows_icon.png b/inventory/textures/ui_circular_arrows_icon.png new file mode 100644 index 0000000..968e404 Binary files /dev/null and b/inventory/textures/ui_circular_arrows_icon.png differ diff --git a/inventory/textures/ui_craft_icon.png b/inventory/textures/ui_craft_icon.png new file mode 100644 index 0000000..8884c61 Binary files /dev/null and b/inventory/textures/ui_craft_icon.png differ diff --git a/inventory/textures/ui_craftguide_form.png b/inventory/textures/ui_craftguide_form.png new file mode 100644 index 0000000..72572b5 Binary files /dev/null and b/inventory/textures/ui_craftguide_form.png differ diff --git a/inventory/textures/ui_craftguide_icon.png b/inventory/textures/ui_craftguide_icon.png new file mode 100644 index 0000000..d5b76d2 Binary files /dev/null and b/inventory/textures/ui_craftguide_icon.png differ diff --git a/inventory/textures/ui_crafting_form.png b/inventory/textures/ui_crafting_form.png new file mode 100644 index 0000000..74389ed Binary files /dev/null and b/inventory/textures/ui_crafting_form.png differ diff --git a/inventory/textures/ui_doubleleft_icon.png b/inventory/textures/ui_doubleleft_icon.png new file mode 100644 index 0000000..ca1f66f Binary files /dev/null and b/inventory/textures/ui_doubleleft_icon.png differ diff --git a/inventory/textures/ui_doubleright_icon.png b/inventory/textures/ui_doubleright_icon.png new file mode 100644 index 0000000..995b565 Binary files /dev/null and b/inventory/textures/ui_doubleright_icon.png differ diff --git a/inventory/textures/ui_form_bg.png b/inventory/textures/ui_form_bg.png new file mode 100644 index 0000000..37683f0 Binary files /dev/null and b/inventory/textures/ui_form_bg.png differ diff --git a/inventory/textures/ui_gohome_icon.png b/inventory/textures/ui_gohome_icon.png new file mode 100644 index 0000000..1141055 Binary files /dev/null and b/inventory/textures/ui_gohome_icon.png differ diff --git a/inventory/textures/ui_green_icon_background.png b/inventory/textures/ui_green_icon_background.png new file mode 100644 index 0000000..21b4f41 Binary files /dev/null and b/inventory/textures/ui_green_icon_background.png differ diff --git a/inventory/textures/ui_group.png b/inventory/textures/ui_group.png new file mode 100644 index 0000000..d7f5dce Binary files /dev/null and b/inventory/textures/ui_group.png differ diff --git a/inventory/textures/ui_home_icon.png b/inventory/textures/ui_home_icon.png new file mode 100644 index 0000000..eeb4e04 Binary files /dev/null and b/inventory/textures/ui_home_icon.png differ diff --git a/inventory/textures/ui_left_icon.png b/inventory/textures/ui_left_icon.png new file mode 100644 index 0000000..14ad064 Binary files /dev/null and b/inventory/textures/ui_left_icon.png differ diff --git a/inventory/textures/ui_main_inventory.png b/inventory/textures/ui_main_inventory.png new file mode 100644 index 0000000..b65dabb Binary files /dev/null and b/inventory/textures/ui_main_inventory.png differ diff --git a/inventory/textures/ui_misc_form.png b/inventory/textures/ui_misc_form.png new file mode 100644 index 0000000..d34d326 Binary files /dev/null and b/inventory/textures/ui_misc_form.png differ diff --git a/inventory/textures/ui_moon_icon.png b/inventory/textures/ui_moon_icon.png new file mode 100644 index 0000000..0595a6c Binary files /dev/null and b/inventory/textures/ui_moon_icon.png differ diff --git a/inventory/textures/ui_no.png b/inventory/textures/ui_no.png new file mode 100644 index 0000000..ad9470b Binary files /dev/null and b/inventory/textures/ui_no.png differ diff --git a/inventory/textures/ui_off_icon.png b/inventory/textures/ui_off_icon.png new file mode 100644 index 0000000..1933742 Binary files /dev/null and b/inventory/textures/ui_off_icon.png differ diff --git a/inventory/textures/ui_ok_icon.png b/inventory/textures/ui_ok_icon.png new file mode 100644 index 0000000..e22b2bc Binary files /dev/null and b/inventory/textures/ui_ok_icon.png differ diff --git a/inventory/textures/ui_on_icon.png b/inventory/textures/ui_on_icon.png new file mode 100644 index 0000000..bb34ceb Binary files /dev/null and b/inventory/textures/ui_on_icon.png differ diff --git a/inventory/textures/ui_pencil_icon.png b/inventory/textures/ui_pencil_icon.png new file mode 100644 index 0000000..ab5ed5e Binary files /dev/null and b/inventory/textures/ui_pencil_icon.png differ diff --git a/inventory/textures/ui_red_icon_background.png b/inventory/textures/ui_red_icon_background.png new file mode 100644 index 0000000..c925689 Binary files /dev/null and b/inventory/textures/ui_red_icon_background.png differ diff --git a/inventory/textures/ui_right_icon.png b/inventory/textures/ui_right_icon.png new file mode 100644 index 0000000..ab0195c Binary files /dev/null and b/inventory/textures/ui_right_icon.png differ diff --git a/inventory/textures/ui_search_icon.png b/inventory/textures/ui_search_icon.png new file mode 100644 index 0000000..c64900e Binary files /dev/null and b/inventory/textures/ui_search_icon.png differ diff --git a/inventory/textures/ui_sethome_icon.png b/inventory/textures/ui_sethome_icon.png new file mode 100644 index 0000000..b047102 Binary files /dev/null and b/inventory/textures/ui_sethome_icon.png differ diff --git a/inventory/textures/ui_skip_backward_icon.png b/inventory/textures/ui_skip_backward_icon.png new file mode 100644 index 0000000..92e9e8c Binary files /dev/null and b/inventory/textures/ui_skip_backward_icon.png differ diff --git a/inventory/textures/ui_skip_forward_icon.png b/inventory/textures/ui_skip_forward_icon.png new file mode 100644 index 0000000..f046b4f Binary files /dev/null and b/inventory/textures/ui_skip_forward_icon.png differ diff --git a/inventory/textures/ui_sun_icon.png b/inventory/textures/ui_sun_icon.png new file mode 100644 index 0000000..5bd24fb Binary files /dev/null and b/inventory/textures/ui_sun_icon.png differ diff --git a/inventory/textures/ui_trash_icon.png b/inventory/textures/ui_trash_icon.png new file mode 100644 index 0000000..412573c Binary files /dev/null and b/inventory/textures/ui_trash_icon.png differ diff --git a/inventory/textures/ui_waypoint_set_icon.png b/inventory/textures/ui_waypoint_set_icon.png new file mode 100644 index 0000000..ee44c4c Binary files /dev/null and b/inventory/textures/ui_waypoint_set_icon.png differ diff --git a/inventory/textures/ui_waypoints_icon.png b/inventory/textures/ui_waypoints_icon.png new file mode 100644 index 0000000..e0281af Binary files /dev/null and b/inventory/textures/ui_waypoints_icon.png differ diff --git a/inventory/textures/ui_xyz_icon.png b/inventory/textures/ui_xyz_icon.png new file mode 100644 index 0000000..dabea81 Binary files /dev/null and b/inventory/textures/ui_xyz_icon.png differ diff --git a/inventory/textures/ui_xyz_off_icon.png b/inventory/textures/ui_xyz_off_icon.png new file mode 100644 index 0000000..8e45946 Binary files /dev/null and b/inventory/textures/ui_xyz_off_icon.png differ diff --git a/inventory/textures/ui_xyz_on_icon.png b/inventory/textures/ui_xyz_on_icon.png new file mode 100644 index 0000000..dabea81 Binary files /dev/null and b/inventory/textures/ui_xyz_on_icon.png differ diff --git a/inventory/waypoints.lua b/inventory/waypoints.lua new file mode 100644 index 0000000..062814d --- /dev/null +++ b/inventory/waypoints.lua @@ -0,0 +1,245 @@ +local S +if intllib then + S = intllib.Getter() +else + S = function(s) return s end +end + +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() + 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;" .. 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 .. ";" + .. minetest.formspec_escape(S("Select Waypoint #%d"):format(i)).."]" + end + + local i = waypoints.selected or 1 + local waypoint = waypoints[i] or {} + local temp = waypoints_temp[player_name][i] or {} + local default_name = "Waypoint "..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 .. ";" + .. minetest.formspec_escape(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 .. ";" + .. minetest.formspec_escape(S("Make waypoint " + ..(waypoint.active and "invisible" or "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 .. ";" + .. minetest.formspec_escape(S((waypoint.display_pos and "Disable" or "Enable") + .." display of waypoint coordinates")).."]" + + formspec = formspec .. + "image_button[6.6,3.7;.8,.8;".. + "ui_circular_arrows_icon.png;".. + "toggle_color"..i..";]".. + "tooltip[toggle_color" .. i .. ";" + .. minetest.formspec_escape(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 .. ";" + .. minetest.formspec_escape(S("Edit waypoint name")).."]" + + -- Waypoint's info: + if waypoint.active then + formspec = formspec .. "label[1,0.8;"..S("Waypoint active").."]" + else + formspec = formspec .. "label[1,0.8;"..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 .. ";" + .. minetest.formspec_escape(S("Finish editing")).."]" + end + + formspec = formspec .. "label[1,1.3;"..S("World position")..": " .. + minetest.pos_to_string(waypoint.world_pos or vector.new()) .. "]" .. + "label[1,1.8;"..S("Name")..": ".. (waypoint.name or default_name) .. "]" .. + "label[1,2.3;"..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"), +}) + +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:getpos() + 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 + waypoints[i] = waypoints[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) +