diff --git a/buildscripts/03_my_updates.sh b/buildscripts/03_my_updates.sh index c024fe1..2d771b2 100755 --- a/buildscripts/03_my_updates.sh +++ b/buildscripts/03_my_updates.sh @@ -11,7 +11,7 @@ DST="$PROJ"/mods/ # Game mods dir #copy files -MOD_PATH=("player/skinsdb" "gui/smart_inventory" "mobs/mobs_mobkit/petz/petz") #temporary not used: "minetest_game" +MOD_PATH=("player/skinsdb" "mobs/mobs_mobkit/petz/petz") #temporary not used: "minetest_game" for MOD_INDEX in ${!MOD_PATH[*]} do diff --git a/game.conf b/game.conf index 1b72526..8dfa5c4 100644 --- a/game.conf +++ b/game.conf @@ -1,2 +1,2 @@ name = A Planet Alive -min_minetest_version = 5.3 +min_minetest_version = 5.4 diff --git a/mods/gui/i3/.luacheckrc b/mods/gui/i3/.luacheckrc new file mode 100644 index 0000000..e767495 --- /dev/null +++ b/mods/gui/i3/.luacheckrc @@ -0,0 +1,22 @@ +allow_defined_top = true + +ignore = { + "get_debug_grid", +} + +read_globals = { + "minetest", + "default", + "armor", + "skins", + "awards", + "vector", + "string", + "table", + "ItemStack", +} + +globals = { + "i3", + "core", +} diff --git a/mods/gui/i3/API.md b/mods/gui/i3/API.md new file mode 100644 index 0000000..8a43e65 --- /dev/null +++ b/mods/gui/i3/API.md @@ -0,0 +1,262 @@ +## API + +### Custom tabs + +#### `i3.new_tab(def)` + +Custom tabs can be added to the `i3` inventory as follow (example): + +```Lua +i3.new_tab { + name = "stuff", + description = "Stuff", + image = "image.png", -- Optional, adds an image next to the tab description + + -- Determine if the tab is visible by a player, `false` or `nil` hide the tab + access = function(player, data) + local name = player:get_player_name() + if name == "singleplayer" then + return false + end + end, + + formspec = function(player, data, fs) + fs("label[3,1;This is just a test]") + end, + + fields = function(player, data, fields) + + end, + + -- Determine if the recipe panels must be hidden or not (must return a boolean) + hide_panels = function(player, data) + local name = player:get_player_name() + return core.is_creative_enabled(name) + end, +} +``` + +- `player` is an `ObjectRef` to the user. +- `data` are the user data. +- `fs` is the formspec table which is callable with a metamethod. Each call adds a new entry. + +#### `i3.set_fs(player[, extra_formspec])` + +Updates the current formspec. `extra_formspec` adds an additional formspec string. + +#### `i3.delete_tab(tabname)` + +Deletes a tab by name. + +#### `i3.set_tab(player[, tabname])` + +Sets the current tab by name. `player` is an `ObjectRef` to the user. +`tabname` can be omitted to get an empty tab. + +#### `i3.override_tab(tabname, def)` + +Overrides a tab by name. `def` is the tab definition like seen in `i3.set_tab`. + +#### `i3.get_tabs()` + +Returns the list of registered tabs. + +--- + +### Custom recipes + +Custom recipes are nonconventional crafts outside the main crafting grid. +They can be registered in-game dynamically and have a size beyond 3x3 items. + +**Note:** the registration format differs from the default registration format in everything. +The width is automatically calculated depending where you place the commas. Look at the examples attentively. + +#### Registering a custom crafting type (example) + +```Lua +i3.register_craft_type("digging", { + description = "Digging", + icon = "default_tool_steelpick.png", +}) +``` + +#### Registering a custom crafting recipe (examples) + +```Lua +i3.register_craft({ + type = "digging", + result = "default:cobble 2", + items = {"default:stone"}, +}) +``` + +```Lua +i3.register_craft({ + result = "default:cobble 16", + items = { + "default:stone, default:stone, default:stone", + "default:stone, , default:stone", + "default:stone, default:stone, default:stone", + } +}) +``` + +Recipes can be registered in a Minecraft-like way: + +```Lua +i3.register_craft({ + grid = { + "X #", + " ## ", + "X#X#", + "X X", + }, + key = { + ['#'] = "default:wood", + ['X'] = "default:glass", + }, + result = "default:mese 3", +}) +``` + +Multiples recipes can also be registered: + +```Lua +i3.register_craft({ + { + result = "default:mese", + items = { + "default:mese_crystal, default:mese_crystal", + "default:mese_crystal, default:mese_crystal", + } + }, + + big = { + result = "default:mese 4", + items = { + "default:mese_crystal, default:mese_crystal", + "default:mese_crystal, default:mese_crystal", + "default:mese_crystal, default:mese_crystal", + "default:mese_crystal, default:mese_crystal", + } + }, +}) +``` + +Recipes can be registered from a given URL containing a JSON file (HTTP support is required¹): + +```Lua +i3.register_craft({ + url = "https://raw.githubusercontent.com/minetest-mods/i3/main/test_online_recipe.json" +}) +``` + +--- + +### Recipe filters + +Recipe filters can be used to filter the recipes shown to players. Progressive +mode is implemented as a recipe filter. + +#### `i3.add_recipe_filter(name, function(recipes, player))` + +Adds a recipe filter with the given `name`. The filter function returns the +recipes to be displayed, given the available recipes and an `ObjectRef` to the +user. Each recipe is a table of the form returned by +`minetest.get_craft_recipe`. + +Example function to hide recipes for items from a mod called "secretstuff": + +```lua +i3.add_recipe_filter("Hide secretstuff", function(recipes) + local filtered = {} + for _, recipe in ipairs(recipes) do + if recipe.output:sub(1,12) ~= "secretstuff:" then + filtered[#filtered + 1] = recipe + end + end + + return filtered +end) +``` + +#### `i3.set_recipe_filter(name, function(recipe, player))` + +Removes all recipe filters and adds a new one. + +#### `i3.delete_recipe_filter(name)` + +Removes the recipe filter with the given `name`. + +#### `i3.get_recipe_filters()` + +Returns a map of recipe filters, indexed by name. + +--- + +### Search filters + +Search filters are used to perform specific searches inside the search field. +These filters are cumulative to perform a specific search. +They can be used like so: ` +=,,<...>` + +Example usages: + +- `+groups=cracky,crumbly`: search for groups `cracky` and `crumbly` in all items. +- `wood +groups=flammable +types=node`: search for group `flammable` amongst items which contain + `wood` in their names AND have a `node` drawtype. + +Notes: +- If `optional_name` is omitted, the search filter will apply to all items, without pre-filtering. +- The `+groups` and `+types` filters are currently implemented by default. + +#### `i3.add_search_filter(name, function(item, values))` + +Adds a search filter with the given `name`. `values` is a table of all possible values. +The search function must return a boolean value (whether the given item should be listed or not). + +Example function sorting items by drawtype: + +```lua +i3.add_search_filter("types", function(item, drawtypes) + local t = {} + + for i, dt in ipairs(drawtypes) do + t[i] = (dt == "node" and reg_nodes[item] and 1) or + (dt == "item" and reg_craftitems[item] and 1) or + (dt == "tool" and reg_tools[item] and 1) or nil + end + + return #t > 0 +end) +``` + +#### `i3.remove_search_filter(name)` + +Removes the search filter with the given `name`. + +#### `i3.get_search_filters()` + +Returns a map of search filters, indexed by name. + +--- + +### Miscellaneous + +#### `i3.group_stereotypes` + +This is the table indexing the item groups by stereotypes. +You can add a stereotype like so: + +```Lua +i3.group_stereotypes.radioactive = "mod:item" +``` + +#### `i3.export_url` + +If set, the mod will export all the cached recipes and usages in a JSON format +to the given URL (HTTP support is required¹). + +--- + +**¹** Add `i3` to the `secure.http_mods` or `secure.trusted_mods` setting in `minetest.conf`. diff --git a/mods/gui/i3/LICENSE b/mods/gui/i3/LICENSE new file mode 100644 index 0000000..1abebab --- /dev/null +++ b/mods/gui/i3/LICENSE @@ -0,0 +1,60 @@ +License of source code +---------------------- + +The MIT License (MIT) + +Copyright (c) 2020-2021 Jean-Patrick Guerrero and contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +Licenses of media (textures) +---------------------------- + +paramat (CC BY-SA 3.0): + i3_arrow.png - derived from a texture by BlockMen (CC BY-SA 3.0) + i3_hotbar.png + +You are free to: +Share — copy and redistribute the material in any medium or format. +Adapt — remix, transform, and build upon the material for any purpose, even commercially. +The licensor cannot revoke these freedoms as long as you follow the license terms. + +Under the following terms: + +Attribution — You must give appropriate credit, provide a link to the license, and +indicate if changes were made. You may do so in any reasonable manner, but not in any way +that suggests the licensor endorses you or your use. + +ShareAlike — If you remix, transform, or build upon the material, you must distribute +your contributions under the same license as the original. + +No additional restrictions — You may not apply legal terms or technological measures that +legally restrict others from doing anything the license permits. + +Notices: + +You do not have to comply with the license for elements of the material in the public +domain or where your use is permitted by an applicable exception or limitation. +No warranties are given. The license may not give you all of the permissions necessary +for your intended use. For example, other rights such as publicity, privacy, or moral +rights may limit how you use the material. + +For more details: +http://creativecommons.org/licenses/by-sa/3.0/ diff --git a/mods/gui/i3/README.md b/mods/gui/i3/README.md new file mode 100644 index 0000000..58273d7 --- /dev/null +++ b/mods/gui/i3/README.md @@ -0,0 +1,50 @@ +# i3 + +[![ContentDB](https://content.minetest.net/packages/jp/i3/shields/title/)](https://content.minetest.net/packages/jp/i3/) [![ContentDB](https://content.minetest.net/packages/jp/i3/shields/downloads/)](https://content.minetest.net/packages/jp/i3/) + +#### **`i3`** is a next-generation inventory for Minetest. + +This inventory offers a slick, modern UI made with the latest technologies of the Minetest engine. +**`i3`** provides a rich [**API**](https://github.com/minetest-mods/i3/blob/master/API.md) for mod developers who want to extend it. + +This mod requires **Minetest 5.4+** + +#### List of features: + - Crafting Guide (only in survival mode) + - Progressive Mode¹ + - Quick Crafting + - Backpacks + - 3D Player Model Preview + - Inventory Sorting (alphabetical + item stack compression) + - Item Bookmarks + +**¹** *This mode is a Terraria-like system that shows recipes you can craft from items you ever had in your inventory. +To enable it: `i3_progressive_mode = true` in `minetest.conf`.* + + +#### This mod officially supports the following mods: + - [**`3d_armor`**](https://content.minetest.net/packages/stu/3d_armor/) + - [**`skinsdb`**](https://content.minetest.net/packages/bell07/skinsdb/) + - [**`awards`**](https://content.minetest.net/packages/rubenwardy/awards/) + +#### Recommendations + +To use this mod in the best conditions, it's recommended to follow these recommendations: + + - Use LuaJIT + - Use a HiDPI widescreen display + - Use the default Freetype font style + +#### Notes + +`i3` uses a larger inventory than the usual inventories in Minetest games. +Thus, most chests will be unadapted to this inventory size. +The `i3` inventory is 9 slots wide by default (without backpack), such as in Minecraft. + +Report any bug on the [**Bug Tracker**](https://github.com/minetest-mods/i3/issues). + +Love this mod? Donations are appreciated: https://www.paypal.me/jpg84240 + +Demo video: https://www.youtube.com/watch?v=25nCAaqeacU + +![Preview](https://user-images.githubusercontent.com/7883281/109045805-4f450600-76d4-11eb-90f7-b99ab939246a.png) diff --git a/mods/gui/i3/init.lua b/mods/gui/i3/init.lua new file mode 100644 index 0000000..39d9264 --- /dev/null +++ b/mods/gui/i3/init.lua @@ -0,0 +1,3138 @@ +i3 = {} + +-- Caches +local pdata = {} +local init_items = {} +local searches = {} +local recipes_cache = {} +local usages_cache = {} +local fuel_cache = {} +local replacements = {fuel = {}} +local toolrepair + +local tabs = {} + +local progressive_mode = core.settings:get_bool "i3_progressive_mode" +local damage_enabled = core.settings:get_bool "enable_damage" + +local __3darmor, __skinsdb, __awards +local sfinv, unified_inventory, old_unified_inventory_fn + +local http = core.request_http_api() +local singleplayer = core.is_singleplayer() + +local reg_items = core.registered_items +local reg_nodes = core.registered_nodes +local reg_craftitems = core.registered_craftitems +local reg_tools = core.registered_tools +local reg_entities = core.registered_entities +local reg_aliases = core.registered_aliases + +local log = core.log +local after = core.after +local clr = core.colorize +local parse_json = core.parse_json +local write_json = core.write_json + +local chat_send = core.chat_send_player +local show_formspec = core.show_formspec +local check_privs = core.check_player_privs +local globalstep = core.register_globalstep +local on_shutdown = core.register_on_shutdown +local get_players = core.get_connected_players +local get_craft_result = core.get_craft_result +local translate = minetest.get_translated_string +local on_joinplayer = core.register_on_joinplayer +local get_all_recipes = core.get_all_craft_recipes +local slz, dslz = core.serialize, core.deserialize +local on_mods_loaded = core.register_on_mods_loaded +local on_leaveplayer = core.register_on_leaveplayer +local get_player_info = core.get_player_information +local create_inventory = core.create_detached_inventory +local on_receive_fields = core.register_on_player_receive_fields + +local ESC = core.formspec_escape +local S = core.get_translator "i3" + +local ES = function(...) + return ESC(S(...)) +end + +local maxn, sort, concat, copy, insert, remove = + table.maxn, table.sort, table.concat, table.copy, + table.insert, table.remove + +local sprintf, find, gmatch, match, sub, split, upper, lower = + string.format, string.find, string.gmatch, string.match, + string.sub, string.split, string.upper, string.lower + +local min, max, floor, ceil, abs = math.min, math.max, math.floor, math.ceil, math.abs + +local pairs, ipairs, next, type, setmetatable, tonum, unpack = + pairs, ipairs, next, type, setmetatable, tonumber, unpack + +local vec_add, vec_mul = vector.add, vector.multiply + +local ROWS = 9 +local LINES = 10 +local IPP = ROWS * LINES +local MAX_FAVS = 6 +local ITEM_BTN_SIZE = 1.1 + +local INV_SIZE = 36 +local HOTBAR_COUNT = 9 + +-- Progressive mode +local POLL_FREQ = 0.25 +local HUD_TIMER_MAX = 1.5 + +local MIN_FORMSPEC_VERSION = 4 + +local META_SAVES = {"bag_size", "skin_id"} + +local BAG_SIZES = { + small = INV_SIZE + 3, + medium = INV_SIZE + 6, + large = INV_SIZE + 9, +} + +local PNG = { + bg = "i3_bg.png", + bg_full = "i3_bg_full.png", + hotbar = "i3_hotbar.png", + search = "i3_search.png", + heart = "i3_heart.png", + heart_half = "i3_heart_half.png", + heart_grey = "i3_heart_grey.png", + prev = "i3_next.png^\\[transformFX", + next = "i3_next.png", + arrow = "i3_arrow.png", + trash = "i3_trash.png", + sort_az = "i3_sort_az.png", + sort_za = "i3_sort_za.png", + compress = "i3_compress.png", + fire = "i3_fire.png", + fire_anim = "i3_fire_anim.png", + book = "i3_book.png", + sign = "i3_sign.png", + cancel = "i3_cancel.png", + export = "i3_export.png", + slot = "i3_slot.png", + tab = "i3_tab.png", + tab_top = "i3_tab.png^\\[transformFY", + furnace_anim = "i3_furnace_anim.png", + + cancel_hover = "i3_cancel.png^\\[brighten", + search_hover = "i3_search.png^\\[brighten", + export_hover = "i3_export.png^\\[brighten", + trash_hover = "i3_trash.png^\\[brighten^\\[colorize:#f00:100", + compress_hover = "i3_compress.png^\\[brighten", + sort_az_hover = "i3_sort_az.png^\\[brighten", + sort_za_hover = "i3_sort_za.png^\\[brighten", + prev_hover = "i3_next_hover.png^\\[transformFX", + next_hover = "i3_next_hover.png", + tab_hover = "i3_tab_hover.png", + tab_hover_top = "i3_tab_hover.png^\\[transformFY", +} + +local fs_elements = { + label = "label[%f,%f;%s]", + box = "box[%f,%f;%f,%f;%s]", + image = "image[%f,%f;%f,%f;%s]", + tooltip = "tooltip[%f,%f;%f,%f;%s]", + button = "button[%f,%f;%f,%f;%s;%s]", + item_image = "item_image[%f,%f;%f,%f;%s]", + hypertext = "hypertext[%f,%f;%f,%f;%s;%s]", + bg9 = "background9[%f,%f;%f,%f;%s;false;%u]", + scrollbar = "scrollbar[%f,%f;%f,%f;%s;%s;%u]", + model = "model[%f,%f;%f,%f;%s;%s;%s;%s;%s;%s;%s]", + image_button = "image_button[%f,%f;%f,%f;%s;%s;%s]", + animated_image = "animated_image[%f,%f;%f,%f;;%s;%u;%u]", + item_image_button = "item_image_button[%f,%f;%f,%f;%s;%s;%s]", +} + +local styles = sprintf([[ + style_type[label,field;font_size=16] + style_type[image_button;border=false;sound=i3_click] + style_type[item_image_button;border=false;bgimg_hovered=%s;sound=i3_click] + + style[filter;border=false] + style[cancel;fgimg=%s;fgimg_hovered=%s;content_offset=0] + style[search;fgimg=%s;fgimg_hovered=%s;content_offset=0] + style[prev_page;fgimg=%s;fgimg_hovered=%s] + style[next_page;fgimg=%s;fgimg_hovered=%s] + style[prev_recipe;fgimg=%s;fgimg_hovered=%s] + style[next_recipe;fgimg=%s;fgimg_hovered=%s] + style[prev_usage;fgimg=%s;fgimg_hovered=%s] + style[next_usage;fgimg=%s;fgimg_hovered=%s] + style[pagenum,no_item,no_rcp;border=false;font=bold;font_size=18;content_offset=0] + style[btn_bag,btn_armor,btn_skins;font=bold;font_size=18;border=false;content_offset=0;sound=i3_click] + style[craft_rcp,craft_usg;border=false;noclip=true;font_size=16;sound=i3_craft; + bgimg=i3_btn9.png;bgimg_hovered=i3_btn9_hovered.png; + bgimg_pressed=i3_btn9_pressed.png;bgimg_middle=4,6] +]], +PNG.slot, +PNG.cancel, PNG.cancel_hover, +PNG.search, PNG.search_hover, +PNG.prev, PNG.prev_hover, +PNG.next, PNG.next_hover, +PNG.prev, PNG.prev_hover, +PNG.next, PNG.next_hover, +PNG.prev, PNG.prev_hover, +PNG.next, PNG.next_hover) + +local function get_lang_code(info) + return info and info.lang_code +end + +local function get_formspec_version(info) + return info and info.formspec_version or 1 +end + +local function outdated(name) + local fs = sprintf("size[5.8,1.3]image[0,0;1,1;%s]label[1,0;%s]button_exit[2.4,0.8;1,1;;OK]", + PNG.book, + "Your Minetest client is outdated.\nGet the latest version on minetest.net to use i3") + + show_formspec(name, "i3", fs) +end + +local old_is_creative_enabled = core.is_creative_enabled + +function core.is_creative_enabled(name) + return check_privs(name, {creative = true}) or old_is_creative_enabled(name) +end + +i3.group_stereotypes = { + dye = "dye:white", + wool = "wool:white", + wood = "default:wood", + tree = "default:tree", + sand = "default:sand", + glass = "default:glass", + stick = "default:stick", + stone = "default:stone", + leaves = "default:leaves", + coal = "default:coal_lump", + vessel = "vessels:glass_bottle", + flower = "flowers:dandelion_yellow", + water_bucket = "bucket:bucket_water", + mesecon_conductor_craftable = "mesecons:wire_00000000_off", +} + +local group_names = { + dye = S"Any dye", + coal = S"Any coal", + sand = S"Any sand", + tree = S"Any tree", + wool = S"Any wool", + glass = S"Any glass", + stick = S"Any stick", + stone = S"Any stone", + carpet = S"Any carpet", + flower = S"Any flower", + leaves = S"Any leaves", + vessel = S"Any vessel", + wood = S"Any wood planks", + mushroom = S"Any mushroom", + + ["color_red,flower"] = S"Any red flower", + ["color_blue,flower"] = S"Any blue flower", + ["color_black,flower"] = S"Any black flower", + ["color_white,flower"] = S"Any white flower", + ["color_green,flower"] = S"Any green flower", + ["color_orange,flower"] = S"Any orange flower", + ["color_yellow,flower"] = S"Any yellow flower", + ["color_violet,flower"] = S"Any violet flower", + + ["color_red,dye"] = S"Any red dye", + ["color_blue,dye"] = S"Any blue dye", + ["color_grey,dye"] = S"Any grey dye", + ["color_pink,dye"] = S"Any pink dye", + ["color_cyan,dye"] = S"Any cyan dye", + ["color_black,dye"] = S"Any black dye", + ["color_white,dye"] = S"Any white dye", + ["color_brown,dye"] = S"Any brown dye", + ["color_green,dye"] = S"Any green dye", + ["color_orange,dye"] = S"Any orange dye", + ["color_yellow,dye"] = S"Any yellow dye", + ["color_violet,dye"] = S"Any violet dye", + ["color_magenta,dye"] = S"Any magenta dye", + ["color_dark_grey,dye"] = S"Any dark grey dye", + ["color_dark_green,dye"] = S"Any dark green dye", +} + +i3.model_alias = { + ["boats:boat"] = {name = "boats:boat", drawtype = "entity"}, + ["carts:cart"] = {name = "carts:cart", drawtype = "entity", frames = "0,0"}, + ["default:chest"] = {name = "default:chest_open"}, + ["default:chest_locked"] = {name = "default:chest_locked_open"}, + ["doors:door_wood"] = {name = "doors:door_wood_a"}, + ["doors:door_glass"] = {name = "doors:door_glass_a"}, + ["doors:door_obsidian_glass"] = {name = "doors:door_obsidian_glass_a"}, + ["doors:door_steel"] = {name = "doors:door_steel_a"}, + ["xpanes:door_steel_bar"] = {name = "xpanes:door_steel_bar_a"}, +} + +local function err(str) + return log("error", str) +end + +local function msg(name, str) + return chat_send(name, sprintf("[i3] %s", str)) +end + +local function is_num(x) + return type(x) == "number" +end + +local function is_str(x) + return type(x) == "string" +end + +local function true_str(str) + return is_str(str) and str ~= "" +end + +local function is_table(x) + return type(x) == "table" +end + +local function is_func(x) + return type(x) == "function" +end + +local function is_group(item) + return sub(item, 1, 6) == "group:" +end + +local function fmt(elem, ...) + if not fs_elements[elem] then + return sprintf(elem, ...) + end + + return sprintf(fs_elements[elem], ...) +end + +local function clean_name(item) + if sub(item, 1, 1) == ":" then + item = sub(item, 2) + end + + return item +end + +local function array_diff(t1, t2) + local hash = {} + + for i = 1, #t1 do + local v = t1[i] + hash[v] = true + end + + for i = 1, #t2 do + local v = t2[i] + hash[v] = nil + end + + local diff, c = {}, 0 + + for i = 1, #t1 do + local v = t1[i] + if hash[v] then + c = c + 1 + diff[c] = v + end + end + + return diff +end + +local function table_eq(T1, T2) + local avoid_loops = {} + + local function recurse(t1, t2) + if type(t1) ~= type(t2) then return end + + if not is_table(t1) then + return t1 == t2 + end + + if avoid_loops[t1] then + return avoid_loops[t1] == t2 + end + + avoid_loops[t1] = t2 + local t2k, t2kv = {}, {} + + for k in pairs(t2) do + if is_table(k) then + insert(t2kv, k) + end + + t2k[k] = true + end + + for k1, v1 in pairs(t1) do + local v2 = t2[k1] + if type(k1) == "table" then + local ok + for i = 1, #t2kv do + local tk = t2kv[i] + if table_eq(k1, tk) and recurse(v1, t2[tk]) then + remove(t2kv, i) + t2k[tk] = nil + ok = true + break + end + end + + if not ok then return end + else + if v2 == nil then return end + t2k[k1] = nil + if not recurse(v1, v2) then return end + end + end + + if next(t2k) then return end + return true + end + + return recurse(T1, T2) +end + +local function table_merge(t1, t2, hash) + t1 = t1 or {} + t2 = t2 or {} + + if hash then + for k, v in pairs(t2) do + t1[k] = v + end + else + local c = #t1 + + for i = 1, #t2 do + c = c + 1 + t1[c] = t2[i] + end + end + + return t1 +end + +local function table_replace(t, val, new) + for k, v in pairs(t) do + if v == val then + t[k] = new + end + end +end + +local function save_meta(player, entries) + local name = player:get_player_name() + local data = pdata[name] + if not data then return end + + local meta = player:get_meta() + + for _, entry in ipairs(entries) do + meta:set_string(entry, slz(data[entry])) + end +end + +local craft_types = {} + +function i3.register_craft_type(name, def) + if not true_str(name) then + return err "i3.register_craft_type: name missing" + end + + if not is_str(def.description) then + def.description = "" + end + + if not is_str(def.icon) then + def.icon = "" + end + + craft_types[name] = def +end + +function i3.register_craft(def) + local width, c = 0, 0 + + if true_str(def.url) then + if not http then + return err(fmt([[i3.register_craft(): Unable to reach %s. + No HTTP support for this mod: add it to the `secure.http_mods` or + `secure.trusted_mods` setting.]], def.url)) + end + + http.fetch({url = def.url}, function(result) + if result.succeeded then + local t = parse_json(result.data) + if is_table(t) then + return i3.register_craft(t) + end + end + end) + + return + end + + if not is_table(def) or not next(def) then + return err "i3.register_craft: craft definition missing" + end + + if #def > 1 then + for _, v in pairs(def) do + i3.register_craft(v) + end + return + end + + if def.result then + def.output = def.result -- Backward compatibility + def.result = nil + end + + if not true_str(def.output) then + return err "i3.register_craft: output missing" + end + + if not is_table(def.items) then + def.items = {} + end + + if def.grid then + if not is_table(def.grid) then + def.grid = {} + end + + if not is_table(def.key) then + def.key = {} + end + + local cp = copy(def.grid) + sort(cp, function(a, b) + return #a > #b + end) + + width = #cp[1] + + for i = 1, #def.grid do + while #def.grid[i] < width do + def.grid[i] = def.grid[i] .. " " + end + end + + for symbol in gmatch(concat(def.grid), ".") do + c = c + 1 + def.items[c] = def.key[symbol] + end + else + local items, len = def.items, #def.items + def.items = {} + + for i = 1, len do + items[i] = items[i]:gsub(",", ", ") + local rlen = #split(items[i], ",") + + if rlen > width then + width = rlen + end + end + + for i = 1, len do + while #split(items[i], ",") < width do + items[i] = items[i] .. ", " + end + end + + for name in gmatch(concat(items, ","), "[%s%w_:]+") do + c = c + 1 + def.items[c] = match(name, "%S+") + end + end + + local item = match(def.output, "%S+") + recipes_cache[item] = recipes_cache[item] or {} + + def.custom = true + def.width = width + + insert(recipes_cache[item], def) +end + +local recipe_filters = {} + +function i3.add_recipe_filter(name, f) + if not true_str(name) then + return err "i3.add_recipe_filter: name missing" + elseif not is_func(f) then + return err "i3.add_recipe_filter: function missing" + end + + recipe_filters[name] = f +end + +function i3.set_recipe_filter(name, f) + if not is_str(name) then + return err "i3.set_recipe_filter: name missing" + elseif not is_func(f) then + return err "i3.set_recipe_filter: function missing" + end + + recipe_filters = {[name] = f} +end + +function i3.delete_recipe_filter(name) + recipe_filters[name] = nil +end + +function i3.get_recipe_filters() + return recipe_filters +end + +local function apply_recipe_filters(recipes, player) + for _, filter in pairs(recipe_filters) do + recipes = filter(recipes, player) + end + + return recipes +end + +local search_filters = {} + +function i3.add_search_filter(name, f) + if not true_str(name) then + return err "i3.add_search_filter: name missing" + elseif not is_func(f) then + return err "i3.add_search_filter: function missing" + end + + search_filters[name] = f +end + +function i3.remove_search_filter(name) + search_filters[name] = nil +end + +function i3.get_search_filters() + return search_filters +end + +local function weird_desc(str) + return not true_str(str) or find(str, "\n") or not find(str, "%u") +end + +local function toupper(str) + return str:gsub("%f[%w]%l", upper):gsub("_", " ") +end + +local function snip(str, limit) + return #str > limit and fmt("%s...", sub(str, 1, limit - 3)) or str +end + +local function get_desc(item) + if sub(item, 1, 1) == "_" then + item = sub(item, 2) + end + + local def = reg_items[item] + + if def then + local desc = ItemStack(item):get_short_description() + + if true_str(desc) then + desc = desc:trim() + + if not find(desc, "%u") then + desc = toupper(desc) + end + + return desc + + elseif true_str(item) then + return toupper(match(item, ":(.*)")) + end + end + + return S("Unknown Item (@1)", item) +end + +local function item_has_groups(item_groups, groups) + for i = 1, #groups do + local group = groups[i] + if (item_groups[group] or 0) == 0 then return end + end + + return true +end + +local function extract_groups(str) + if sub(str, 1, 6) == "group:" then + return split(sub(str, 7), ",") + end +end + +local function get_filtered_items(player, data) + local items, known, c = {}, 0, 0 + + for i = 1, #init_items do + local item = init_items[i] + local recipes = recipes_cache[item] + local usages = usages_cache[item] + + recipes = #apply_recipe_filters(recipes or {}, player) + usages = #apply_recipe_filters(usages or {}, player) + + if recipes > 0 or usages > 0 then + c = c + 1 + items[c] = item + + if data then + known = known + recipes + usages + end + end + end + + if data then + data.known_recipes = known + end + + return items +end + +local function get_burntime(item) + return get_craft_result{method = "fuel", items = {item}}.time +end + +local function cache_fuel(item) + local burntime = get_burntime(item) + if burntime > 0 then + fuel_cache[item] = { + type = "fuel", + items = {item}, + burntime = burntime, + replacements = replacements.fuel[item], + } + end +end + +local function show_item(def) + return def and def.groups.not_in_creative_inventory ~= 1 and + def.description and def.description ~= "" +end + +local function search(data) + local filter = data.filter + + if searches[filter] then + data.items = searches[filter] + return + end + + local opt = "^(.-)%+([%w_]+)=([%w_,]+)" + local search_filter = next(search_filters) and match(filter, opt) + local filters = {} + + if search_filter then + for filter_name, values in gmatch(filter, sub(opt, 6)) do + if search_filters[filter_name] then + values = split(values, ",") + filters[filter_name] = values + end + end + end + + local filtered_list, c = {}, 0 + + for i = 1, #data.items_raw do + local item = data.items_raw[i] + local def = reg_items[item] + local desc = lower(translate(data.lang_code, def and def.description)) or "" + local search_in = fmt("%s %s", item, desc) + local temp, j, to_add = {}, 1 + + if search_filter then + for filter_name, values in pairs(filters) do + if values then + local func = search_filters[filter_name] + to_add = (j > 1 and temp[item] or j == 1) and + func(item, values) and (search_filter == "" or + find(search_in, search_filter, 1, true)) + + if to_add then + temp[item] = true + end + + j = j + 1 + end + end + else + to_add = find(search_in, filter, 1, true) + end + + if to_add then + c = c + 1 + filtered_list[c] = item + end + end + + if not next(recipe_filters) then + -- Cache the results only if searched 2 times + if searches[filter] == nil then + searches[filter] = false + else + searches[filter] = filtered_list + end + end + + data.items = filtered_list +end + +local function get_item_usages(item, recipe, added) + local groups = extract_groups(item) + + if groups then + for name, def in pairs(reg_items) do + if not added[name] and show_item(def) and item_has_groups(def.groups, groups) then + local usage = copy(recipe) + table_replace(usage.items, item, name) + + usages_cache[name] = usages_cache[name] or {} + insert(usages_cache[name], 1, usage) + + added[name] = true + end + end + elseif show_item(reg_items[item]) then + usages_cache[item] = usages_cache[item] or {} + insert(usages_cache[item], 1, recipe) + end +end + +local function get_usages(recipe) + local added = {} + + for _, item in pairs(recipe.items) do + item = reg_aliases[item] or item + + if not added[item] then + get_item_usages(item, recipe, added) + added[item] = true + end + end +end + +local function cache_usages(item) + local recipes = recipes_cache[item] or {} + + for i = 1, #recipes do + get_usages(recipes[i]) + end + + if fuel_cache[item] then + usages_cache[item] = table_merge(usages_cache[item] or {}, {fuel_cache[item]}) + end +end + +local function drop_table(name, drop) + local count_sure = 0 + local drop_items = drop.items or {} + local max_items = drop.max_items + + for i = 1, #drop_items do + local di = drop_items[i] + local valid_rarity = di.rarity and di.rarity > 1 + + if di.rarity or not max_items or + (max_items and not di.rarity and count_sure < max_items) then + for j = 1, #di.items do + local dstack = ItemStack(di.items[j]) + local dname = dstack:get_name() + local dcount = dstack:get_count() + local empty = dstack:is_empty() + + if not empty and (dname ~= name or (dname == name and dcount > 1)) then + local rarity = valid_rarity and di.rarity + + i3.register_craft{ + type = rarity and "digging_chance" or "digging", + items = {name}, + output = fmt("%s %u", dname, dcount), + rarity = rarity, + tools = di.tools, + } + end + end + end + + if not di.rarity then + count_sure = count_sure + 1 + end + end +end + +local function cache_drops(name, drop) + if true_str(drop) then + local dstack = ItemStack(drop) + local dname = dstack:get_name() + local empty = dstack:is_empty() + + if not empty and dname ~= name then + i3.register_craft{ + type = "digging", + items = {name}, + output = drop, + } + end + elseif is_table(drop) then + drop_table(name, drop) + end +end + +local function cache_recipes(item) + local recipes = get_all_recipes(item) + + if replacements[item] then + local _recipes = {} + + for k, v in ipairs(recipes or {}) do + _recipes[#recipes + 1 - k] = v + end + + local shift = 0 + local size_rpl = maxn(replacements[item]) + local size_rcp = #_recipes + + if size_rpl > size_rcp then + shift = size_rcp - size_rpl + end + + for k, v in pairs(replacements[item]) do + k = k + shift + + if _recipes[k] then + _recipes[k].replacements = v + end + end + + recipes = _recipes + end + + if recipes then + recipes_cache[item] = table_merge(recipes, recipes_cache[item] or {}) + end +end + +local function get_recipes(player, item) + local clean_item = reg_aliases[item] or item + local recipes = recipes_cache[clean_item] + local usages = usages_cache[clean_item] + + if recipes then + recipes = apply_recipe_filters(recipes, player) + end + + local no_recipes = not recipes or #recipes == 0 + if no_recipes and not usages then return end + usages = apply_recipe_filters(usages, player) + + local no_usages = not usages or #usages == 0 + + return not no_recipes and recipes or nil, + not no_usages and usages or nil +end + +local function groups_to_items(groups, get_all) + if not get_all and #groups == 1 then + local group = groups[1] + local stereotype = i3.group_stereotypes[group] + local def = reg_items[stereotype] + + if def and show_item(def) then + return stereotype + end + end + + local names = {} + for name, def in pairs(reg_items) do + if show_item(def) and item_has_groups(def.groups, groups) then + if get_all then + names[#names + 1] = name + else + return name + end + end + end + + return get_all and names or "" +end + +local function __sort(inv, reverse) + sort(inv, function(a, b) + if not is_str(a) then + a = a:get_name() + end + + if not is_str(b) then + b = b:get_name() + end + + if reverse then + return a > b + end + + return a < b + end) +end + +local function sort_itemlist(player, az) + local inv = player:get_inventory() + local list = inv:get_list("main") + local size = inv:get_size("main") + local new_inv, stack_meta = {}, {} + + for i = 1, size do + local stack = list[i] + local name = stack:get_name() + local count = stack:get_count() + local empty = stack:is_empty() + local meta = stack:get_meta():to_table() + local wear = stack:get_wear() > 0 + + if not empty then + if next(meta.fields) or wear then + stack_meta[#stack_meta + 1] = stack + else + new_inv[#new_inv + 1] = fmt("%s %u", name, count) + end + end + end + + for i = 1, #stack_meta do + new_inv[#new_inv + 1] = stack_meta[i] + end + + if az then + __sort(new_inv) + else + __sort(new_inv, true) + end + + inv:set_list("main", new_inv) +end + +local function compress_items(player) + local inv = player:get_inventory() + local list = inv:get_list("main") + local size = inv:get_size("main") + local new_inv, _new_inv, special = {}, {}, {} + + for i = 1, size do + local stack = list[i] + local name = stack:get_name() + local count = stack:get_count() + local stackmax = stack:get_stack_max() + local empty = stack:is_empty() + local meta = stack:get_meta():to_table() + local wear = stack:get_wear() > 0 + + if not empty then + if next(meta.fields) or wear or count >= stackmax then + special[#special + 1] = stack + else + new_inv[name] = new_inv[name] or 0 + new_inv[name] = new_inv[name] + count + end + end + end + + for name, count in pairs(new_inv) do + local stackmax = ItemStack(name):get_stack_max() + local iter = ceil(count / stackmax) + local leftover = count + + for _ = 1, iter do + _new_inv[#_new_inv + 1] = fmt("%s %u", name, min(stackmax, leftover)) + leftover = leftover - stackmax + end + end + + for i = 1, #special do + _new_inv[#_new_inv + 1] = special[i] + end + + __sort(_new_inv) + inv:set_list("main", _new_inv) +end + +local function get_stack_max(inv, data, is_recipe, rcp) + local list = inv:get_list("main") + local size = inv:get_size("main") + local counts_inv, counts_rcp, counts = {}, {}, {} + local rcp_usg = is_recipe and "recipe" or "usage" + + for _, it in pairs(rcp.items) do + counts_rcp[it] = (counts_rcp[it] or 0) + 1 + end + + data.export_counts[rcp_usg] = {} + data.export_counts[rcp_usg].rcp = counts_rcp + + for i = 1, size do + local stack = list[i] + + if not stack:is_empty() then + local item = stack:get_name() + local count = stack:get_count() + + for name in pairs(counts_rcp) do + if is_group(name) then + local def = reg_items[item] + + if def then + local groups = extract_groups(name) + + if item_has_groups(def.groups, groups) then + counts_inv[name] = (counts_inv[name] or 0) + count + end + end + end + end + + counts_inv[item] = (counts_inv[item] or 0) + count + end + end + + data.export_counts[rcp_usg].inv = counts_inv + + for name in pairs(counts_rcp) do + counts[name] = floor((counts_inv[name] or 0) / (counts_rcp[name] or 0)) + end + + local max_stacks = math.huge + + for _, count in pairs(counts) do + if count < max_stacks then + max_stacks = count + end + end + + return max_stacks +end + +local function spawn_item(player, stack) + local dir = player:get_look_dir() + local ppos = player:get_pos() + ppos.y = ppos.y + 1.625 + local look_at = vec_add(ppos, vec_mul(dir, 1)) + + core.add_item(look_at, stack) +end + +local function get_stack(player, pname, stack, message) + local inv = player:get_inventory() + + if inv:room_for_item("main", stack) then + inv:add_item("main", stack) + msg(pname, S("@1 added in your inventory", message)) + else + spawn_item(player, stack) + msg(pname, S("@1 spawned", message)) + end +end + +local function craft_stack(player, pname, data, craft_rcp) + local inv = player:get_inventory() + local rcp_usg = craft_rcp and "recipe" or "usage" + local output = craft_rcp and data.recipes[data.rnum].output or data.usages[data.unum].output + output = ItemStack(output) + local stackname, stackcount, stackmax = output:get_name(), output:get_count(), output:get_stack_max() + local scrbar_val = data[fmt("scrbar_%s", craft_rcp and "rcp" or "usg")] or 1 + + for name, count in pairs(data.export_counts[rcp_usg].rcp) do + local items = {[name] = count} + + if is_group(name) then + items = {} + local groups = extract_groups(name) + local item_groups = groups_to_items(groups, true) + local remaining = count + + for _, item in ipairs(item_groups) do + for _name, _count in pairs(data.export_counts[rcp_usg].inv) do + if item == _name and remaining > 0 then + local c = min(remaining, _count) + items[item] = c + remaining = remaining - c + end + + if remaining == 0 then break end + end + end + end + + for k, v in pairs(items) do + inv:remove_item("main", fmt("%s %s", k, v * scrbar_val)) + end + end + + local count = stackcount * scrbar_val + local desc = get_desc(stackname) + local iter = ceil(count / stackmax) + local leftover = count + + for _ = 1, iter do + local c = min(stackmax, leftover) + local message + + if c > 1 then + message = clr("#ff0", fmt("%s x %s", c, desc)) + else + message = clr("#ff0", fmt("%s", desc)) + end + + local stack = ItemStack(fmt("%s %s", stackname, c)) + get_stack(player, pname, stack, message) + leftover = leftover - stackmax + end +end + +local function select_item(player, name, data, _f) + local item + + for field in pairs(_f) do + if find(field, ":") then + item = field + break + end + end + + if not item then + return + elseif sub(item, -4) == "_inv" then + item = sub(item, 1, -5) + elseif sub(item, 1, 1) == "_" then + item = sub(item, 2) + elseif sub(item, 1, 6) == "group|" then + item = match(item, "([%w:_]+)$") + end + + item = reg_aliases[item] or item + if not reg_items[item] then return end + + if core.is_creative_enabled(name) then + local stack = ItemStack(item) + local stackmax = stack:get_stack_max() + stack = fmt("%s %s", item, stackmax) + get_stack(player, name, stack, clr("#ff0", fmt("%u x %s", stackmax, item))) + return + end + + if item == data.query_item then return end + local recipes, usages = get_recipes(player, item) + + data.query_item = item + data.recipes = recipes + data.usages = usages + data.rnum = 1 + data.unum = 1 + data.scrbar_rcp = 1 + data.scrbar_usg = 1 + data.export_rcp = nil + data.export_usg = nil +end + +local function repairable(tool) + local def = reg_tools[tool] + return toolrepair and def and def.groups and def.groups.disable_repair ~= 1 +end + +local function is_fav(favs, query_item) + local fav, i + for j = 1, #favs do + if favs[j] == query_item then + fav = true + i = j + break + end + end + + return fav, i +end + +local function get_tooltip(item, info) + local tooltip + + if info.groups then + sort(info.groups) + tooltip = group_names[concat(info.groups, ",")] + + if not tooltip then + local groupstr = {} + + for i = 1, #info.groups do + insert(groupstr, clr("#ff0", info.groups[i])) + end + + groupstr = concat(groupstr, ", ") + tooltip = S("Any item belonging to the groups: @1", groupstr) + end + else + tooltip = get_desc(item) + end + + local function add(str) + return fmt("%s\n%s", tooltip, str) + end + + if info.cooktime then + tooltip = add(S("Cooking time: @1", clr("#ff0", info.cooktime))) + end + + if info.burntime then + tooltip = add(S("Burning time: @1", clr("#ff0", info.burntime))) + end + + if info.replace then + for i = 1, #info.replace.items do + local rpl = match(info.replace.items[i], "%S+") + local desc = clr("#ff0", get_desc(rpl)) + + if info.replace.type == "cooking" then + tooltip = add(S("Replaced by @1 on smelting", desc)) + elseif info.replace.type == "fuel" then + tooltip = add(S("Replaced by @1 on burning", desc)) + else + tooltip = add(S("Replaced by @1 on crafting", desc)) + end + end + end + + if info.repair then + tooltip = add(S("Repairable by step of @1", clr("#ff0", toolrepair .. "%"))) + end + + if info.rarity then + local chance = (1 / max(1, info.rarity)) * 100 + tooltip = add(S("@1 of chance to drop", clr("#ff0", chance .. "%"))) + end + + if info.tools then + local several = #info.tools > 1 + local names = several and "\n" or "" + + if several then + for i = 1, #info.tools do + names = fmt("%s\t\t- %s\n", names, clr("#ff0", get_desc(info.tools[i]))) + end + + tooltip = add(S("Only drop if using one of these tools: @1", sub(names, 1, -2))) + else + tooltip = add(S("Only drop if using this tool: @1", + clr("#ff0", get_desc(info.tools[1])))) + end + end + + return fmt("tooltip[%s;%s]", item, ESC(tooltip)) +end + +local function get_output_fs(fs, data, rcp, is_recipe, shapeless, right, btn_size, _btn_size) + local custom_recipe = craft_types[rcp.type] + + if custom_recipe or shapeless or rcp.type == "cooking" then + local icon = custom_recipe and custom_recipe.icon or shapeless and "shapeless" or "furnace" + + if not custom_recipe then + icon = fmt("i3_%s.png^\\[resize:16x16", icon) + end + + local pos_x = right + btn_size + 0.42 + local pos_y = data.yoffset + 0.9 + + if sub(icon, 1, 10) == "i3_furnace" then + fs(fmt("animated_image", pos_x, pos_y, 0.5, 0.5, PNG.furnace_anim, 8, 180)) + else + fs(fmt("image", pos_x, pos_y, 0.5, 0.5, icon)) + end + + local tooltip = custom_recipe and custom_recipe.description or + shapeless and S"Shapeless" or S"Cooking" + + fs(fmt("tooltip", pos_x, pos_y, 0.5, 0.5, ESC(tooltip))) + end + + local arrow_X = right + 0.2 + (_btn_size or ITEM_BTN_SIZE) + local X = arrow_X + 1.2 + local Y = data.yoffset + 1.4 + + fs(fmt("image", arrow_X, Y + 0.06, 1, 1, PNG.arrow)) + + if rcp.type == "fuel" then + fs(fmt("animated_image", X + 0.05, Y, ITEM_BTN_SIZE, ITEM_BTN_SIZE, PNG.fire_anim, 8, 180)) + else + local item = rcp.output + item = ItemStack(clean_name(item)) + local name = item:get_name() + local count = item:get_count() + local bt_s = ITEM_BTN_SIZE * 1.2 + + fs(fmt("image", X, Y - 0.11, bt_s, bt_s, PNG.slot)) + + local _name = fmt("_%s", name) + + fs(fmt("item_image_button", + X + 0.11, Y, ITEM_BTN_SIZE, ITEM_BTN_SIZE, + fmt("%s %u", name, count * (is_recipe and data.scrbar_rcp or data.scrbar_usg or 1)), + _name, "")) + + local def = reg_items[name] + local unknown = not def or nil + local desc = def and def.description + local weird = name ~= "" and desc and weird_desc(desc) or nil + local burntime = fuel_cache[name] and fuel_cache[name].burntime + + local infos = { + unknown = unknown, + weird = weird, + burntime = burntime, + repair = repairable(name), + rarity = rcp.rarity, + tools = rcp.tools, + } + + if next(infos) then + fs(get_tooltip(_name, infos)) + end + end +end + +local function get_grid_fs(fs, data, rcp, is_recipe) + local width = rcp.width or 1 + local right, btn_size, _btn_size = 0, ITEM_BTN_SIZE + local cooktime, shapeless + + if rcp.type == "cooking" then + cooktime, width = width, 1 + elseif width == 0 and not rcp.custom then + shapeless = true + local n = #rcp.items + width = (n < 5 and n > 1) and 2 or min(3, max(1, n)) + end + + local rows = ceil(maxn(rcp.items) / width) + local large_recipe = width > 3 or rows > 3 + + if large_recipe then + fs("style_type[item_image_button;border=true]") + end + + for i = 1, width * rows do + local item = rcp.items[i] or "" + item = clean_name(item) + local name = match(item, "%S*") + + local X = ceil((i - 1) % width - width) + X = X + (X * 0.2) + data.xoffset + 3.9 + + local Y = ceil(i / width) - min(2, rows) + Y = Y + (Y * 0.15) + data.yoffset + 1.4 + + if large_recipe then + btn_size = (3 / width) * (3 / rows) + 0.3 + _btn_size = btn_size + + local xi = (i - 1) % width + local yi = floor((i - 1) / width) + + X = btn_size * xi + data.xoffset + 0.3 + (xi * 0.05) + Y = btn_size * yi + data.yoffset + 0.2 + (yi * 0.05) + end + + if X > right then + right = X + end + + local groups + + if is_group(name) then + groups = extract_groups(name) + item = groups_to_items(groups) + end + + local label = groups and "\nG" or "" + local replace + + for j = 1, #(rcp.replacements or {}) do + local replacement = rcp.replacements[j] + if replacement[1] == name then + replace = replace or {type = rcp.type, items = {}} + + local added + + for _, v in ipairs(replace.items) do + if replacement[2] == v then + added = true + break + end + end + + if not added then + label = fmt("%s%s\nR", label ~= "" and "\n" or "", label) + replace.items[#replace.items + 1] = replacement[2] + end + end + end + + if not large_recipe then + fs(fmt("image", X, Y, btn_size, btn_size, PNG.slot)) + end + + local btn_name = groups and fmt("group|%s|%s", groups[1], item) or item + + fs(fmt("item_image_button", X, Y, btn_size, btn_size, + fmt("%s %u", item, is_recipe and data.scrbar_rcp or data.scrbar_usg or 1), + btn_name, label)) + + local def = reg_items[name] + local unknown = not def or nil + unknown = not groups and unknown or nil + local desc = def and def.description + local weird = name ~= "" and desc and weird_desc(desc) or nil + local burntime = fuel_cache[name] and fuel_cache[name].burntime + + local infos = { + unknown = unknown, + weird = weird, + groups = groups, + burntime = burntime, + cooktime = cooktime, + replace = replace, + } + + if next(infos) then + fs(get_tooltip(btn_name, infos)) + end + end + + if large_recipe then + fs("style_type[item_image_button;border=false]") + end + + get_output_fs(fs, data, rcp, is_recipe, shapeless, right, btn_size, _btn_size) +end + +local function get_rcp_lbl(fs, data, panel, rn, is_recipe) + local lbl = ES("Usage @1 of @2", data.unum, rn) + + if is_recipe then + lbl = ES("Recipe @1 of @2", data.rnum, rn) + end + + local _lbl = translate(data.lang_code, lbl) + local lbl_len = #_lbl:gsub("[\128-\191]", "") -- Count chars, not bytes in UTF-8 strings + local shift = min(0.9, abs(12 - max(12, lbl_len)) * 0.15) + + fs(fmt("label", data.xoffset + 5.65 - shift, data.yoffset + 3.37, lbl)) + + if rn > 1 then + local btn_suffix = is_recipe and "recipe" or "usage" + local prev_name = fmt("prev_%s", btn_suffix) + local next_name = fmt("next_%s", btn_suffix) + local x_arrow = data.xoffset + 5.09 + local y_arrow = data.yoffset + 3.2 + + fs(fmt("image_button", x_arrow - shift, y_arrow, 0.3, 0.3, "", prev_name, ""), + fmt("image_button", x_arrow + 2.3, y_arrow, 0.3, 0.3, "", next_name, "")) + end + + local rcp = is_recipe and panel.rcp[data.rnum] or panel.rcp[data.unum] + get_grid_fs(fs, data, rcp, is_recipe) +end + +local function get_model_fs(fs, data, def, model_alias) + if model_alias then + if model_alias.drawtype == "entity" then + def = reg_entities[model_alias.name] + local init_props = def.initial_properties + def.textures = init_props and init_props.textures or def.textures + def.mesh = init_props and init_props.mesh or def.mesh + else + def = reg_items[model_alias.name] + end + end + + local tiles = def.tiles or def.textures or {} + local t = {} + + for _, v in ipairs(tiles) do + local _name + + if v.color then + if is_num(v.color) then + local hex = fmt("%02x", v.color) + + while #hex < 8 do + hex = "0" .. hex + end + + _name = fmt("%s^[multiply:%s", v.name, + fmt("#%s%s", sub(hex, 3), sub(hex, 1, 2))) + else + _name = fmt("%s^[multiply:%s", v.name, v.color) + end + elseif v.animation then + _name = fmt("%s^[verticalframe:%u:0", v.name, v.animation.aspect_h) + end + + t[#t + 1] = _name or v.name or v + end + + while #t < 6 do + t[#t + 1] = t[#t] + end + + fs(fmt("model", + data.xoffset + 6.6, data.yoffset + 0.05, 1.3, 1.3, "", + def.mesh, concat(t, ","), "0,0", "true", "true", + model_alias and model_alias.frames or "")) +end + +local function get_header(fs, data) + local fav = is_fav(data.favs, data.query_item) + local nfavs = #data.favs + local star_x, star_y, star_size = data.xoffset + 0.4, data.yoffset + 0.5, 0.4 + + if nfavs < MAX_FAVS or (nfavs == MAX_FAVS and fav) then + local fav_marked = fmt("i3_fav%s.png", fav and "_off" or "") + + fs(fmt("style[fav;fgimg=%s;fgimg_hovered=%s;fgimg_pressed=%s]", + fmt("i3_fav%s.png", fav and "" or "_off"), fav_marked, fav_marked), + fmt("image_button", star_x, star_y, star_size, star_size, "", "fav", ""), + fmt("tooltip[fav;%s]", fav and ES"Unmark this item" or ES"Mark this item")) + else + fs(fmt("style[nofav;fgimg=%s;fgimg_hovered=%s;fgimg_pressed=%s]", + "i3_fav_off.png", PNG.cancel, PNG.cancel), + fmt("image_button", star_x, star_y, star_size, star_size, "", "nofav", ""), + fmt("tooltip[nofav;%s]", ES"Cannot mark this item. Bookmark limit reached.")) + end + + local desc_lim, name_lim = 32, 34 + local desc = ESC(get_desc(data.query_item)) + local tech_name = data.query_item + local X = data.xoffset + 1.05 + local Y1 = data.yoffset + 0.47 + local Y2 = Y1 + 0.5 + + if #desc > desc_lim then + fs(fmt("tooltip", X, Y1 - 0.1, 5.7, 0.24, desc)) + desc = snip(desc, desc_lim) + end + + if #tech_name > name_lim then + fs(fmt("tooltip", X, Y2 - 0.1, 5.7, 0.24, tech_name)) + tech_name = snip(tech_name, name_lim) + end + + fs("style_type[label;font=bold;font_size=22]", + fmt("label", X, Y1, desc), "style_type[label;font=mono;font_size=16]", + fmt("label", X, Y2, clr("#7bf", tech_name)), "style_type[label;font=normal;font_size=16]") + + local def = reg_items[data.query_item] + local model_alias = i3.model_alias[data.query_item] + + if def.drawtype == "mesh" or model_alias then + get_model_fs(fs, data, def, model_alias) + else + fs(fmt("item_image", data.xoffset + 6.8, data.yoffset + 0.17, 1.1, 1.1, data.query_item)) + end +end + +local function get_export_fs(fs, data, is_recipe, is_usage, max_stacks_rcp, max_stacks_usg) + local name = is_recipe and "rcp" or "usg" + local show_export = (is_recipe and data.export_rcp) or (is_usage and data.export_usg) + + fs(fmt("style[export_%s;fgimg=%s;fgimg_hovered=%s]", + name, fmt("%s", show_export and PNG.export_hover or PNG.export), PNG.export_hover), + fmt("image_button", + data.xoffset + 7.35, data.yoffset + 0.2, 0.45, 0.45, "", fmt("export_%s", name), ""), + fmt("tooltip[export_%s;%s]", name, ES"Quick crafting")) + + if not show_export then return end + + local craft_max = is_recipe and max_stacks_rcp or max_stacks_usg + local stack_fs = (is_recipe and data.scrbar_rcp) or (is_usage and data.scrbar_usg) or 1 + + if stack_fs > craft_max then + stack_fs = craft_max + + if is_recipe then + data.scrbar_rcp = craft_max + elseif is_usage then + data.scrbar_usg = craft_max + end + end + + fs(fmt("style[scrbar_%s;noclip=true]", name), + fmt("scrollbaroptions[min=1;max=%u;smallstep=1]", craft_max), + fmt("scrollbar", data.xoffset + 8.1, data.yoffset, 3, 0.35, + "horizontal", fmt("scrbar_%s", name), stack_fs), + fmt("button", data.xoffset + 8.1, data.yoffset + 0.4, 3, 0.7, fmt("craft_%s", name), + ES("Craft (x@1)", stack_fs))) +end + +local function get_rcp_extra(player, data, fs, panel, is_recipe, is_usage) + local rn = panel.rcp and #panel.rcp + + if rn then + local rcp_normal = is_recipe and panel.rcp[data.rnum].type == "normal" + local usg_normal = is_usage and panel.rcp[data.unum].type == "normal" + local max_stacks_rcp, max_stacks_usg = 0, 0 + local inv = player:get_inventory() + + if rcp_normal then + max_stacks_rcp = get_stack_max(inv, data, is_recipe, panel.rcp[data.rnum]) + end + + if usg_normal then + max_stacks_usg = get_stack_max(inv, data, is_recipe, panel.rcp[data.unum]) + end + + if is_recipe and max_stacks_rcp == 0 then + data.export_rcp = nil + data.scrbar_rcp = 1 + elseif is_usage and max_stacks_usg == 0 then + data.export_usg = nil + data.scrbar_usg = 1 + end + + if max_stacks_rcp > 0 or max_stacks_usg > 0 then + get_export_fs(fs, data, is_recipe, is_usage, max_stacks_rcp, max_stacks_usg) + end + + get_rcp_lbl(fs, data, panel, rn, is_recipe) + else + local lbl = is_recipe and ES"No recipes" or ES"No usages" + fs(fmt("button", data.xoffset + 0.1, data.yoffset + (panel.height / 2) - 0.5, + 7.8, 1, "no_rcp", lbl)) + end +end + +local function get_favs(fs, data) + fs(fmt("label", data.xoffset + 0.4, data.yoffset + 0.4, ES"Bookmarks")) + + for i = 1, #data.favs do + local item = data.favs[i] + local X = data.xoffset - 0.7 + (i * 1.2) + local Y = data.yoffset + 0.8 + + if data.query_item == item then + fs(fmt("image", X, Y, ITEM_BTN_SIZE, ITEM_BTN_SIZE, PNG.slot)) + end + + fs(fmt("item_image_button", X, Y, ITEM_BTN_SIZE, ITEM_BTN_SIZE, item, item, "")) + end +end + +local function get_panels(player, data, fs) + local _title = {name = "title", height = 1.4} + local _favs = {name = "favs", height = 2.23} + local _recipes = {name = "recipes", rcp = data.recipes, height = 3.9} + local _usages = {name = "usages", rcp = data.usages, height = 3.9} + local panels = {_title, _recipes, _usages, _favs} + + for idx = 1, #panels do + local panel = panels[idx] + data.yoffset = 0 + + if idx > 1 then + for _idx = idx - 1, 1, -1 do + data.yoffset = data.yoffset + panels[_idx].height + 0.1 + end + end + + fs(fmt("bg9", data.xoffset + 0.1, data.yoffset, 7.9, panel.height, PNG.bg_full, 10)) + + local is_recipe, is_usage = panel.name == "recipes", panel.name == "usages" + + if is_recipe or is_usage then + get_rcp_extra(player, data, fs, panel, is_recipe, is_usage) + elseif panel.name == "title" then + get_header(fs, data) + elseif panel.name == "favs" then + get_favs(fs, data) + end + end +end + +local function add_subtitle(fs, title, x, y, ctn_len, font_size) + font_size = font_size or 18 + + fs(fmt("style_type[label;font=bold;font_size=%u]", font_size), fmt("label", x, y, title), + "style_type[label;font=normal;font_size=16]", + "style_type[box;colors=#bababa,#bababa30,#bababa30,#bababa]", + fmt("box", x, y + 0.3, ctn_len, 0.045, "#")) +end + +local function get_award_list(fs, ctn_len, yextra, award_list, awards_unlocked, award_list_nb) + local percent = fmt("%.1f%%", (awards_unlocked * 100) / award_list_nb):gsub(".0", "") + + add_subtitle(fs, ES("Achievements: @1 of @2 (@3)", awards_unlocked, award_list_nb, percent), + 0, yextra, ctn_len) + + for i = 1, award_list_nb do + local award = award_list[i] + local y = yextra - 0.7 + i + (i * 0.3) + + local def, progress = award.def, award.progress + local title = def.title + local desc = def.description and def.description:gsub("%.$", "") or "" + + local title_lim, _title = 27 + local desc_lim, _desc = 40 + local icon_size = 1.1 + local box_len = ctn_len - icon_size + 0.1 + + if #title > title_lim then + _title = snip(title, title_lim) + fs[#fs + 1] = fmt("tooltip", icon_size + 0.2, y + 0.14, box_len, 0.24, title) + end + + if #desc > desc_lim then + _desc = snip(desc, desc_lim) + fs[#fs + 1] = fmt("tooltip", icon_size + 0.2, y + 0.68, box_len, 0.26, desc) + end + + if not award.unlocked and def.secret then + title = ES"Secret award" + desc = ES"Unlock this award to find out what it is" + end + + local icon = def.icon or "awards_unknown.png" + + if not award.unlocked then + icon = fmt("%s^\\[colorize:#000:180", icon) + end + + fs[#fs + 1] = fmt("image", 0, y + 0.01, icon_size, icon_size, icon) + fs[#fs + 1] = "style_type[box;colors=#bababa30,#bababa30,#bababa05,#bababa05]" + fs[#fs + 1] = fmt("box", icon_size + 0.1, y, box_len, icon_size, "") + + if progress then + local current, target = progress.current, progress.target + local curr_bar = (current * box_len) / target + + fs[#fs + 1] = fmt("box", icon_size + 0.1, y + 0.8, box_len, 0.3, "#101010") + fs[#fs + 1] = "style_type[box;colors=#9dc34c80,#9dc34c,#9dc34c,#9dc34c80]" + fs[#fs + 1] = fmt("box", icon_size + 0.1, y + 0.8, curr_bar, 0.3, "") + fs[#fs + 1] = "style_type[label;font_size=14]" + fs[#fs + 1] = fmt("label", icon_size + 0.5, y + 0.97, fmt("%u / %u", current, target)) + + y = y - 0.14 + end + + fs[#fs + 1] = "style_type[label;font=bold;font_size=17]" + fs[#fs + 1] = fmt("label", icon_size + 0.2, y + 0.4, _title or title) + fs[#fs + 1] = "style_type[label;font=normal;font_size=15]" + fs[#fs + 1] = fmt("label", icon_size + 0.2, y + 0.75, clr("#bbbbbb", _desc or desc)) + fs[#fs + 1] = "style_type[label;font_size=16]" + end +end + +local function get_ctn_content(fs, data, player, xoffset, yoffset, ctn_len, award_list, awards_unlocked, + award_list_nb) + local name = player:get_player_name() + add_subtitle(fs, ESC(name), xoffset, yoffset + 0.2, ctn_len, 22) + + local hp = damage_enabled and (data.hp or player:get_hp()) or 20 + local half = ceil((hp / 2) % 1) + local hearts = (hp / 2) + half + local heart_size = 0.35 + local heart_hgt = yoffset + 0.7 + + for i = 1, 10 do + fs(fmt("image", xoffset + ((i - 1) * (heart_size + 0.1)), heart_hgt, + heart_size, heart_size, PNG.heart_grey)) + end + + if damage_enabled then + for i = 1, hearts do + fs(fmt("image", xoffset + ((i - 1) * (heart_size + 0.1)), heart_hgt, + heart_size, heart_size, + (half == 1 and i == floor(hearts)) and PNG.heart_half or PNG.heart)) + end + end + + fs(fmt("list[current_player;craft;%f,%f;3,3;]", xoffset, yoffset + 1.45), + fmt("image", xoffset + 3.47, yoffset + 2.69, 0.85, 0.85, PNG.arrow), + fmt("list[current_player;craftpreview;%f,%f;1,1;]", xoffset + 4.45, yoffset + 2.6), + "listring[detached:i3_trash;main]", + fmt("list[detached:i3_trash;main;%f,%f;1,1;]", xoffset + 4.45, yoffset + 3.75), + fmt("image", xoffset + 4.45, yoffset + 3.75, 1, 1, PNG.trash)) + + local yextra = 5.4 + local bag_equip = data.equip == "bag" + local armor_equip = data.equip == "armor" + local skins_equip = data.equip == "skins" + + fs(fmt("style[btn_bag;textcolor=%s]", bag_equip and "#fff" or "#aaa"), + fmt("style[btn_armor;textcolor=%s]", armor_equip and "#fff" or "#aaa"), + fmt("style[btn_skins;textcolor=%s]", skins_equip and "#fff" or "#aaa"), + "style_type[button:hovered;textcolor=#fff]", + fmt("button", -0.14, yextra - 0.2, 2, 0.6, "btn_bag", ES"Bag"), + fmt("button", 1.87, yextra - 0.2, 2, 0.6, "btn_armor", ES"Armor"), + fmt("button", 3.87, yextra - 0.2, 2, 0.6, "btn_skins", ES"Skins")) + + fs(fmt("box", 0, yextra + 0.4, ctn_len, 0.045, "#bababa50"), + fmt("box", (bag_equip and 0) or (armor_equip and 2) or (skins_equip and 4), + yextra + 0.4, 1.7, 0.045, "#f9826c")) + + if bag_equip then + fs(fmt("list[detached:%s_backpack;main;0,%f;1,1;]", ESC(name), yextra + 0.7)) + + if not data.bag:get_stack("main", 1):is_empty() then + fs(fmt("hypertext", 1.2, yextra + 0.89, ctn_len - 1.9, 0.8, "", + ES("The inventory is extended by @1 slots", + BAG_SIZES[data.bag_size] - INV_SIZE))) + end + + elseif armor_equip then + if __3darmor then + fs(fmt("list[detached:%s_armor;armor;0,%f;3,2;]", ESC(name), yextra + 0.7)) + + local armor_def = armor.def[name] + + fs(fmt("label", 3.65, yextra + 1.55, fmt("%s: %s", ES"Level", armor_def.level)), + fmt("label", 3.65, yextra + 2.05, fmt("%s: %s", ES"Heal", armor_def.heal))) + else + fs(fmt("hypertext", 0, yextra + 0.9, ctn_len, 0.4, "", + "
not installed
")) + end + + elseif skins_equip then + if __skinsdb then + local _skins = skins.get_skinlist_for_player(name) + local t = {} + + for _, skin in ipairs(_skins) do + t[#t + 1] = skin.name + end + + fs(fmt("dropdown[0,%f;3.55,0.6;skins;%s;%u;true]", + yextra + 0.7, concat(t, ","), data.skin_id or 1)) + else + fs(fmt("hypertext", 0, yextra + 0.9, ctn_len, 0.6, "", + "
not installed
")) + end + end + + if __awards then + if bag_equip then + yextra = yextra + 2.4 + elseif armor_equip then + yextra = yextra + (__3darmor and 3.6 or 1.9) + elseif skins_equip then + yextra = yextra + 1.9 + end + + get_award_list(fs, ctn_len, yextra, award_list, awards_unlocked, award_list_nb) + end +end + +local function get_tabs_fs(player, data, fs, full_height) + local tab_len, tab_hgh, c, over = 3, 0.5, 0 + local _tabs = copy(tabs) + + for i, def in ipairs(tabs) do + if def.access and not def.access(player, data) then + remove(_tabs, i) + end + end + + local shift = min(3, #_tabs) + + for i, def in ipairs(_tabs) do + if not over and c > 2 then + over = true + c = 0 + end + + local btm = i <= 3 + + if not btm then + shift = #_tabs - 3 + end + + local selected = i == data.current_tab + + fs(fmt([[style_type[image_button;fgimg=%s;fgimg_hovered=%s;noclip=true; + font_size=16;textcolor=%s;content_offset=0;sound=i3_tab] ]], + selected and (btm and PNG.tab_hover or PNG.tab_hover_top) or (btm and PNG.tab or PNG.tab_top), + btm and PNG.tab_hover or PNG.tab_hover_top, selected and "#fff" or "#ddd")) + + local X = (data.xoffset / 2) + (c * (tab_len + 0.1)) - ((tab_len + 0.05) * (shift / 2)) + local Y = btm and full_height or -tab_hgh + + fs("style_type[image_button:hovered;textcolor=#fff]") + fs(fmt("image_button", X, Y, tab_len, tab_hgh, "", fmt("tab_%s", def.name), + ESC(def.description))) + + if def.image and def.image ~= "" then + fs("style_type[image;noclip=true]") + local desc = translate(data.lang_code, def.description) + fs(fmt("image", X + (tab_len / 2) - ((#desc * 0.1) / 2) - 0.55, + Y + 0.05, 0.35, 0.35, fmt("%s^\\[resize:16x16", def.image))) + end + + c = c + 1 + end +end + +local function get_debug_grid(data, fs, full_height) + local spacing = 0.2 + + for x = 0, data.xoffset, spacing do + fs(fmt("box", x, 0, 0.01, full_height, "#ff0")) + end + + for y = 0, full_height, spacing do + fs(fmt("box", 0, y, data.xoffset, 0.01, "#ff0")) + end + + fs(fmt("box", data.xoffset / 2, 0, 0.01, full_height, "#f00")) + fs(fmt("box", 0, full_height / 2, data.xoffset, 0.01, "#f00")) +end + +local function make_fs(player, data) + --local start = os.clock() + + local fs = setmetatable({}, { + __call = function(t, ...) + t[#t + 1] = concat({...}) + end + }) + + data.xoffset = ROWS + 1.23 + local full_height = LINES + 1.73 + + local tab = tabs[data.current_tab] + local hide_panels = tab and tab.hide_panels and tab.hide_panels(player, data) + local show_panels = data.query_item and tab and not hide_panels + + fs(fmt("formspec_version[%u]size[%f,%f]no_prepend[]bgcolor[#0000]", + MIN_FORMSPEC_VERSION, data.xoffset + (show_panels and 8 or 0), full_height), styles) + + fs(fmt("bg9", 0, 0, data.xoffset, full_height, PNG.bg_full, 10)) + + if tab then + tab.formspec(player, data, fs) + end + + if show_panels then + get_panels(player, data, fs) + end + + get_tabs_fs(player, data, fs, full_height) + + --get_debug_grid(data, fs, full_height) + --print("make_fs()", fmt("%.2f ms", (os.clock() - start) * 1000)) + + return concat(fs) +end + +function i3.set_fs(player, _fs) + local name = player:get_player_name() + local data = pdata[name] + if not data then return end + + local fs = fmt("%s%s", make_fs(player, data), _fs or "") + player:set_inventory_formspec(fs) +end + +local set_fs = i3.set_fs + +function i3.new_tab(def) + if not is_table(def) or not next(def) then + return err "i3.new_tab: tab definition missing" + end + + if not true_str(def.name) then + return err "i3.new_tab: tab name missing" + end + + if not true_str(def.description) then + return err "i3.new_tab: description missing" + end + + if #tabs == 6 then + return err(fmt("i3.new_tab: cannot add '%s' tab. Limit reached (6).", def.name)) + end + + tabs[#tabs + 1] = def +end + +function i3.get_tabs() + return tabs +end + +function i3.delete_tab(tabname) + if not true_str(tabname) then + return err "i3.delete_tab: tab name missing" + end + + for i, def in ipairs(tabs) do + if tabname == def.name then + remove(tabs, i) + end + end +end + +function i3.set_tab(player, tabname) + local name = player:get_player_name() + local data = pdata[name] + + if not tabname or tabname == "" then + data.current_tab = 0 + return + end + + local found + + for i, def in ipairs(tabs) do + if not found and def.name == tabname then + data.current_tab = i + found = true + end + end + + if not found then + return err(fmt("i3.set_tab: tab name '%s' does not exist", tabname)) + end +end + +local set_tab = i3.set_tab + +function i3.override_tab(tabname, newdef) + if not is_table(newdef) or not next(newdef) then + return err "i3.override_tab: tab definition missing" + end + + if not true_str(newdef.name) then + return err "i3.override_tab: tab name missing" + end + + if not true_str(newdef.description) then + return err "i3.override_tab: description missing" + end + + for i, def in ipairs(tabs) do + if def.name == tabname then + tabs[i] = newdef + end + end +end + +local function init_data(player, info) + local name = player:get_player_name() + + pdata[name] = { + filter = "", + pagenum = 1, + items = init_items, + items_raw = init_items, + favs = {}, + export_counts = {}, + current_tab = 1, + equip = "bag", + lang_code = get_lang_code(info), + } + + local data = pdata[name] + local meta = player:get_meta() + + data.bag_size = dslz(meta:get_string "bag_size") + + if __skinsdb then + data.skin_id = tonum(dslz(meta:get_string "skin_id") or 1) + end + + after(0, set_fs, player) +end + +local function reset_data(data) + data.filter = "" + data.pagenum = 1 + data.rnum = 1 + data.unum = 1 + data.scrbar_rcp = 1 + data.scrbar_usg = 1 + data.query_item = nil + data.recipes = nil + data.usages = nil + data.export_rcp = nil + data.export_usg = nil + data.items = data.items_raw +end + +local function panel_fields(player, data, fields) + local name = player:get_player_name() + local sb_rcp, sb_usg = fields.scrbar_rcp, fields.scrbar_usg + + if fields.prev_recipe or fields.next_recipe then + local num = data.rnum + (fields.prev_recipe and -1 or 1) + data.rnum = data.recipes[num] and num or (fields.prev_recipe and #data.recipes or 1) + data.export_rcp = nil + data.scrbar_rcp = 1 + + elseif fields.prev_usage or fields.next_usage then + local num = data.unum + (fields.prev_usage and -1 or 1) + data.unum = data.usages[num] and num or (fields.prev_usage and #data.usages or 1) + data.export_usg = nil + data.scrbar_usg = 1 + + elseif fields.fav then + local fav, i = is_fav(data.favs, data.query_item) + local total = #data.favs + + if total < MAX_FAVS and not fav then + data.favs[total + 1] = data.query_item + elseif fav then + remove(data.favs, i) + end + + elseif fields.export_rcp or fields.export_usg then + if fields.export_rcp then + data.export_rcp = not data.export_rcp + + if not data.export_rcp then + data.scrbar_rcp = 1 + end + else + data.export_usg = not data.export_usg + + if not data.export_usg then + data.scrbar_usg = 1 + end + end + + elseif (sb_rcp and sub(sb_rcp, 1, 3) == "CHG") or (sb_usg and sub(sb_usg, 1, 3) == "CHG") then + data.scrbar_rcp = sb_rcp and tonum(match(sb_rcp, "%d+")) + data.scrbar_usg = sb_usg and tonum(match(sb_usg, "%d+")) + + elseif fields.craft_rcp or fields.craft_usg then + craft_stack(player, name, data, fields.craft_rcp) + else + select_item(player, name, data, fields) + end +end + +local function get_inv_slots(data, fs) + local inv_x, inv_y = 0.22, 6.6 + local width, size, spacing = HOTBAR_COUNT, 1, 0.1 + local bag = data.bag_size + + fs("style_type[box;colors=#77777710,#77777710,#777,#777]") + + for i = 0, HOTBAR_COUNT - 1 do + fs(fmt("box", i * size + inv_x + (i * spacing), inv_y, size, size, "")) + end + + fs(fmt("style_type[list;size=%f;spacing=%f]", size, spacing), + fmt("list[current_player;main;%f,%f;%u,1;]", inv_x, inv_y, HOTBAR_COUNT)) + + if bag then + if bag == "small" then + width, size = 10, 0.892 + elseif bag == "medium" then + width, size = 11, 0.8 + elseif bag == "large" then + width, size = 12, 0.726 + end + end + + fs(fmt("style_type[list;size=%f;spacing=%f]", size, spacing), + fmt("list[current_player;main;%f,%f;%u,%u;%u]", inv_x, inv_y + 1.15, + width, (bag and BAG_SIZES[data.bag_size] or INV_SIZE) / width, HOTBAR_COUNT), + "style_type[list;size=1;spacing=0.15]") +end + +local function get_inventory_fs(player, data, fs) + fs("listcolors[#bababa50;#bababa99]listring[current_player;main]") + + get_inv_slots(data, fs) + + local props = player:get_properties() + local name = player:get_player_name() + + local ctn_len, ctn_hgt = 5.7, 6 + local xoffset, yoffset = 0, 0 + + if props.mesh ~= "" then + local anim = player:get_local_animation() + --fs("style[player_model;bgcolor=black]") + local armor_skin = __3darmor or __skinsdb + + fs(fmt("model", 0.2, 0.2, armor_skin and 4 or 3.4, armor_skin and ctn_hgt or 5.8, + "player_model", + props.mesh, concat(props.textures, ","), "0,-150", "false", "false", + fmt("%u,%u", anim.x, anim.y))) + else + local size = 2.5 + fs(fmt("image", 0.7, 0.2, size, size * props.visual_size.y, props.textures[1])) + end + + local award_list, award_list_nb + local awards_unlocked = 0 + + local max_val = 13 + + if __3darmor and data.equip == "armor" then + if data.scrbar_inv == max_val then + data.scrbar_inv = data.scrbar_inv + 9 + end + + max_val = max_val + 9 + end + + if __awards then + award_list = awards.get_award_states(name) + award_list_nb = #award_list + + for i = 1, award_list_nb do + local award = award_list[i] + + if award.unlocked then + awards_unlocked = awards_unlocked + 1 + end + end + + max_val = max_val + (award_list_nb * 13.17) + end + + fs(fmt([[ + scrollbaroptions[arrows=hide;thumbsize=%u;max=%u] + scrollbar[%f,0.2;0.2,%f;vertical;scrbar_inv;%u] + scrollbaroptions[arrows=default;thumbsize=0;max=1000] + ]], + (max_val * 4) / 15, max_val, 9.8, ctn_hgt, data.scrbar_inv or 0)) + + fs(fmt("scroll_container[3.9,0.2;%f,%f;scrbar_inv;vertical]", ctn_len, ctn_hgt)) + + get_ctn_content(fs, data, player, xoffset, yoffset, ctn_len, award_list, awards_unlocked, + award_list_nb) + + fs("scroll_container_end[]") + + local btn = { + {"trash", ES"Trash all items"}, + {"sort_az", ES"Sort items (A-Z)"}, + {"sort_za", ES"Sort items (Z-A)"}, + {"compress", ES"Compress items"}, + } + + for i, v in ipairs(btn) do + local btn_name, tooltip = unpack(v) + + fs(fmt("style[%s;fgimg=%s;fgimg_hovered=%s;content_offset=0]", + btn_name, PNG[btn_name], PNG[fmt("%s_hover", btn_name)])) + + fs(fmt("image_button", i + 3.447 - (i * 0.4), 11.13, 0.35, 0.35, "", btn_name, "")) + fs(fmt("tooltip[%s;%s]", btn_name, tooltip)) + end +end + +local function get_items_fs(_, data, fs) + local filtered = data.filter ~= "" + + fs("box[0.2,0.2;4.55,0.6;#bababa25]", "set_focus[filter]") + fs(fmt("field[0.3,0.2;%f,0.6;filter;;%s]", filtered and 3.45 or 3.9, ESC(data.filter))) + fs("field_close_on_enter[filter;false]") + + if filtered then + fs(fmt("image_button", 3.75, 0.35, 0.3, 0.3, "", "cancel", "")) + end + + fs(fmt("image_button", 4.25, 0.32, 0.35, 0.35, "", "search", "")) + + fs(fmt("image_button", data.xoffset - 2.73, 0.3, 0.35, 0.35, "", "prev_page", ""), + fmt("image_button", data.xoffset - 0.55, 0.3, 0.35, 0.35, "", "next_page", "")) + + data.pagemax = max(1, ceil(#data.items / IPP)) + + fs(fmt("button", data.xoffset - 2.4, 0.14, 1.88, 0.7, "pagenum", + fmt("%s / %u", clr("#ff0", data.pagenum), data.pagemax))) + + if #data.items == 0 then + local lbl = ES"No item to show" + + if next(recipe_filters) and #init_items > 0 and data.filter == "" then + lbl = ES"Collect items to reveal more recipes" + end + + fs(fmt("button", 0, 3, data.xoffset, 1, "no_item", lbl)) + end + + local first_item = (data.pagenum - 1) * IPP + + for i = first_item, first_item + IPP - 1 do + local item = data.items[i + 1] + if not item then break end + + local X = i % ROWS + X = X + (X * 0.1) + 0.2 + + local Y = floor((i % IPP - X) / ROWS + 1) + Y = Y + (Y * 0.06) + 1 + + if data.query_item == item then + fs(fmt("image", X, Y, 1, 1, PNG.slot)) + end + + fs[#fs + 1] = fmt("item_image_button", X, Y, 1, 1, item, fmt("%s_inv", item), "") + end +end + +i3.new_tab { + name = "inventory", + description = S"Inventory", + formspec = get_inventory_fs, + + fields = function(player, data, fields) + local name = player:get_player_name() + local sb_inv = fields.scrbar_inv + + if not core.is_creative_enabled(name) then + panel_fields(player, data, fields) + end + + if fields.skins and data.skin_id ~= tonum(fields.skins) then + data.skin_id = tonum(fields.skins) + local _skins = skins.get_skinlist_for_player(name) + skins.set_player_skin(player, _skins[data.skin_id]) + end + + if fields.trash then + local inv = player:get_inventory() + if not inv:is_empty("main") then + inv:set_list("main", {}) + end + + elseif fields.compress then + compress_items(player) + + elseif fields.sort_az or fields.sort_za then + sort_itemlist(player, fields.sort_az) + + elseif sb_inv and sub(sb_inv, 1, 3) == "CHG" then + data.scrbar_inv = tonum(match(sb_inv, "%d+")) + return + + elseif fields.btn_bag or fields.btn_armor or fields.btn_skins then + for k in pairs(fields) do + if sub(k, 1, 4) == "btn_" then + data.equip = sub(k, 5) + break + end + end + end + + return set_fs(player) + end, + + hide_panels = function(player) + local name = player:get_player_name() + return core.is_creative_enabled(name) + end, +} + +i3.new_tab { + name = "items", + description = S"Items", + formspec = get_items_fs, + + fields = function(player, data, fields) + panel_fields(player, data, fields) + + if fields.cancel then + reset_data(data) + + elseif fields.key_enter_field == "filter" or fields.search then + if fields.filter == "" then + reset_data(data) + return set_fs(player) + end + + local str = lower(fields.filter) + if data.filter == str then return end + + data.filter = str + data.pagenum = 1 + + search(data) + + elseif fields.prev_page or fields.next_page then + if data.pagemax == 1 then return end + data.pagenum = data.pagenum - (fields.prev_page and 1 or -1) + + if data.pagenum > data.pagemax then + data.pagenum = 1 + elseif data.pagenum == 0 then + data.pagenum = data.pagemax + end + end + + return set_fs(player) + end, +} + +local trash = create_inventory("i3_trash", { + allow_put = function(_, _, _, stack) + return stack:get_count() + end, + on_put = function(inv, listname, _, _, player) + inv:set_list(listname, {}) + + local name = player:get_player_name() + + if not core.is_creative_enabled(name) then + set_fs(player) + end + end, +}) + +trash:set_size("main", 1) + +core.register_on_player_inventory_action(function(player, _, _, info) + local name = player:get_player_name() + + if not core.is_creative_enabled(name) and + ((info.from_list == "main" and info.to_list == "craft") or + (info.from_list == "craft" and info.to_list == "main") or + (info.from_list == "craftresult" and info.to_list == "main")) then + set_fs(player) + end +end) + +if rawget(_G, "armor") then + __3darmor = true + armor:register_on_update(set_fs) +end + +if rawget(_G, "skins") then + __skinsdb = true + insert(META_SAVES, "skin_id") +end + +if rawget(_G, "awards") then + __awards = true + + core.register_on_craft(function(_, player) + set_fs(player) + end) + + core.register_on_dignode(function(_, _, player) + set_fs(player) + end) + + core.register_on_placenode(function(_, _, player) + set_fs(player) + end) + + core.register_on_chat_message(function(name) + local player = core.get_player_by_name(name) + set_fs(player) + end) + + core.register_on_dieplayer(set_fs) +end + +core.register_on_chatcommand(function(name) + local player = core.get_player_by_name(name) + after(0, set_fs, player) +end) + +i3.register_craft_type("digging", { + description = ES"Digging", + icon = "i3_steelpick.png", +}) + +i3.register_craft_type("digging_chance", { + description = ES"Digging (by chance)", + icon = "i3_mesepick.png", +}) + +i3.add_search_filter("groups", function(item, groups) + local def = reg_items[item] + local has_groups = true + + for _, group in ipairs(groups) do + if not def.groups[group] then + has_groups = nil + break + end + end + + return has_groups +end) + +i3.add_search_filter("types", function(item, drawtypes) + local t = {} + + for i, dt in ipairs(drawtypes) do + t[i] = (dt == "node" and reg_nodes[item] and 1) or + (dt == "item" and reg_craftitems[item] and 1) or + (dt == "tool" and reg_tools[item] and 1) or nil + end + + return #t > 0 +end) + +--[[ As `core.get_craft_recipe` and `core.get_all_craft_recipes` do not + return the fuel, replacements and toolrepair recipes, we have to + override `core.register_craft` and do some reverse engineering. + See engine's issues #4901, #5745 and #8920. ]] + +local old_register_craft = core.register_craft +local rcp_num = {} + +core.register_craft = function(def) + old_register_craft(def) + + if def.type == "toolrepair" then + toolrepair = def.additional_wear * -100 + end + + local output = def.output or (true_str(def.recipe) and def.recipe) or nil + if not output then return end + output = {match(output, "%S+")} + + local groups + + if is_group(output[1]) then + groups = extract_groups(output[1]) + output = groups_to_items(groups, true) + end + + for i = 1, #output do + local item = output[i] + rcp_num[item] = (rcp_num[item] or 0) + 1 + + if def.replacements then + if def.type == "fuel" then + replacements.fuel[item] = def.replacements + else + replacements[item] = replacements[item] or {} + replacements[item][rcp_num[item]] = def.replacements + end + end + end +end + +local old_clear_craft = core.clear_craft + +core.clear_craft = function(def) + old_clear_craft(def) + + if true_str(def) then + return -- TODO + elseif is_table(def) then + return -- TODO + end +end + +local function resolve_aliases(hash) + for oldname, newname in pairs(reg_aliases) do + cache_recipes(oldname) + local recipes = recipes_cache[oldname] + + if recipes then + if not recipes_cache[newname] then + recipes_cache[newname] = {} + end + + local similar + + for i = 1, #recipes_cache[oldname] do + local rcp_old = recipes_cache[oldname][i] + + for j = 1, #recipes_cache[newname] do + local rcp_new = copy(recipes_cache[newname][j]) + rcp_new.output = oldname + + if table_eq(rcp_old, rcp_new) then + similar = true + break + end + end + + if not similar then + insert(recipes_cache[newname], rcp_old) + end + end + end + + if newname ~= "" and recipes_cache[oldname] and not hash[newname] then + init_items[#init_items + 1] = newname + end + end +end + +local function get_init_items() + local _select, _preselect = {}, {} + + for name, def in pairs(reg_items) do + if name ~= "" and show_item(def) then + cache_drops(name, def.drop) + cache_fuel(name) + cache_recipes(name) + + _preselect[name] = true + end + end + + for name in pairs(_preselect) do + cache_usages(name) + + init_items[#init_items + 1] = name + _select[name] = true + end + + resolve_aliases(_select) + sort(init_items) + + if http and true_str(i3.export_url) then + local post_data = { + recipes = recipes_cache, + usages = usages_cache, + } + + http.fetch_async{ + url = i3.export_url, + post_data = write_json(post_data), + } + end +end + +on_mods_loaded(function() + get_init_items() + + sfinv = rawget(_G, "sfinv") + + if sfinv then + sfinv.enabled = false + end + + unified_inventory = rawget(_G, "unified_inventory") + + if unified_inventory then + old_unified_inventory_fn = unified_inventory.set_inventory_formspec + function unified_inventory.set_inventory_formspec() return end + end +end) + +local function init_backpack(player) + local name = player:get_player_name() + local data = pdata[name] + + local player_inv = player:get_inventory() + player_inv:set_size("main", INV_SIZE) + + data.bag = create_inventory(fmt("%s_backpack", name), { + allow_put = function(inv, listname, _, stack) + local empty = inv:get_stack(listname, 1):is_empty() + + if empty and sub(stack:get_name(), 1, 7) == "i3:bag_" then + return 1 + end + + return 0 + end, + + on_put = function(_, _, _, stack) + data.bag_size = match(stack:get_name(), "_(%w+)$") + player_inv:set_size("main", BAG_SIZES[data.bag_size]) + set_fs(player) + end, + + on_take = function() + for i = INV_SIZE + 1, BAG_SIZES[data.bag_size] do + local stack = player_inv:get_stack("main", i) + + if not stack:is_empty() then + spawn_item(player, stack) + end + end + + data.bag_size = nil + player_inv:set_size("main", INV_SIZE) + + set_fs(player) + end, + }) + + data.bag:set_size("main", 1) + + if data.bag_size then + data.bag:set_stack("main", 1, fmt("i3:bag_%s", data.bag_size)) + player_inv:set_size("main", BAG_SIZES[data.bag_size]) + end +end + +on_joinplayer(function(player) + local name = player:get_player_name() + local info = get_player_info(name) + + if get_formspec_version(info) < MIN_FORMSPEC_VERSION then + if sfinv then + sfinv.enabled = true + end + + if unified_inventory then + unified_inventory.set_inventory_formspec = old_unified_inventory_fn + + if sfinv then + sfinv.enabled = false + end + end + + return outdated(name) + end + + init_data(player, info) + init_backpack(player) + + after(0, function() + player:hud_set_hotbar_itemcount(HOTBAR_COUNT) + player:hud_set_hotbar_image(PNG.hotbar) + end) +end) + +core.register_on_dieplayer(function(player) + local name = player:get_player_name() + local data = pdata[name] + + data.bag_size = nil + data.bag:set_list("main", {}) + + local inv = player:get_inventory() + inv:set_size("main", INV_SIZE) + + set_fs(player) +end) + +on_leaveplayer(function(player) + save_meta(player, META_SAVES) + + local name = player:get_player_name() + pdata[name] = nil +end) + +on_shutdown(function() + local players = get_players() + + for i = 1, #players do + local player = players[i] + save_meta(player, META_SAVES) + end +end) + +on_receive_fields(function(player, formname, fields) + if formname ~= "" then + return false + end + + local name = player:get_player_name() + local data = pdata[name] + if not data then return end + + for f in pairs(fields) do + if sub(f, 1, 4) == "tab_" then + local tabname = sub(f, 5) + set_tab(player, tabname) + break + end + end + + local tab = tabs[data.current_tab] + + if tab and tab.fields then + return true, tab.fields(player, data, fields) + end + + return true, set_fs(player) +end) + +core.register_on_player_hpchange(function(player, hpchange) + local name = player:get_player_name() + local data = pdata[name] + if not data then return end + + local hp_max = player:get_properties().hp_max + data.hp = min(hp_max, player:get_hp() + hpchange) + + set_fs(player) +end) + +if progressive_mode then + local function item_in_inv(item, inv_items) + local inv_items_size = #inv_items + + if is_group(item) then + local groups = extract_groups(item) + for i = 1, inv_items_size do + local def = reg_items[inv_items[i]] + + if def then + if item_has_groups(def.groups, groups) then + return true + end + end + end + else + for i = 1, inv_items_size do + if inv_items[i] == item then + return true + end + end + end + end + + local function recipe_in_inv(rcp, inv_items) + for _, item in pairs(rcp.items) do + if not item_in_inv(item, inv_items) then return end + end + + return true + end + + local function progressive_filter(recipes, player) + if not recipes then + return {} + end + + local name = player:get_player_name() + local data = pdata[name] + + if #data.inv_items == 0 then + return {} + end + + local filtered, c = {}, 0 + + for i = 1, #recipes do + local recipe = recipes[i] + if recipe_in_inv(recipe, data.inv_items) then + c = c + 1 + filtered[c] = recipe + end + end + + return filtered + end + + local item_lists = {"main", "craft", "craftpreview"} + + local function get_inv_items(player) + local inv = player:get_inventory() + local stacks = {} + + for i = 1, #item_lists do + local list = inv:get_list(item_lists[i]) + table_merge(stacks, list) + end + + local inv_items, c = {}, 0 + + for i = 1, #stacks do + local stack = stacks[i] + if not stack:is_empty() then + local name = stack:get_name() + if reg_items[name] then + c = c + 1 + inv_items[c] = name + end + end + end + + return inv_items + end + + local function init_hud(player, data) + data.hud = { + bg = player:hud_add{ + hud_elem_type = "image", + position = {x = 0.78, y = 1}, + alignment = {x = 1, y = 1}, + scale = {x = 370, y = 112}, + text = PNG.bg, + z_index = 0xDEAD, + }, + + book = player:hud_add{ + hud_elem_type = "image", + position = {x = 0.79, y = 1.02}, + alignment = {x = 1, y = 1}, + scale = {x = 4, y = 4}, + text = PNG.book, + z_index = 0xDEAD, + }, + + text = player:hud_add{ + hud_elem_type = "text", + position = {x = 0.84, y = 1.04}, + alignment = {x = 1, y = 1}, + number = 0xffffff, + text = "", + z_index = 0xDEAD, + }, + } + end + + local function show_hud_success(player, data) + -- It'd better to have an engine function `hud_move` to only need + -- 2 calls for the notification's back and forth. + + local hud_info_bg = player:hud_get(data.hud.bg) + local dt = 0.016 + + if hud_info_bg.position.y <= 0.9 then + data.show_hud = false + data.hud_timer = (data.hud_timer or 0) + dt + end + + if data.show_hud then + for _, def in pairs(data.hud) do + local hud_info = player:hud_get(def) + + player:hud_change(def, "position", { + x = hud_info.position.x, + y = hud_info.position.y - (dt / 5) + }) + end + + player:hud_change(data.hud.text, "text", + S("@1 new recipe(s) discovered!", data.discovered)) + + elseif data.show_hud == false then + if data.hud_timer >= HUD_TIMER_MAX then + for _, def in pairs(data.hud) do + local hud_info = player:hud_get(def) + + player:hud_change(def, "position", { + x = hud_info.position.x, + y = hud_info.position.y + (dt / 5) + }) + end + + if hud_info_bg.position.y >= 1 then + data.show_hud = nil + data.hud_timer = nil + end + end + end + end + + -- Workaround. Need an engine call to detect when the contents of + -- the player inventory changed, instead. + local function poll_new_items() + local players = get_players() + for i = 1, #players do + local player = players[i] + local name = player:get_player_name() + local data = pdata[name] + + local inv_items = get_inv_items(player) + local diff = array_diff(inv_items, data.inv_items) + + if #diff > 0 then + data.inv_items = table_merge(diff, data.inv_items) + local oldknown = data.known_recipes or 0 + local items = get_filtered_items(player, data) + data.discovered = data.known_recipes - oldknown + + if data.show_hud == nil and data.discovered > 0 then + data.show_hud = true + end + + data.items_raw = items + search(data) + set_fs(player) + end + end + + after(POLL_FREQ, poll_new_items) + end + + poll_new_items() + + globalstep(function() + local players = get_players() + for i = 1, #players do + local player = players[i] + local name = player:get_player_name() + local data = pdata[name] + + if data.show_hud ~= nil and singleplayer then + show_hud_success(player, data) + end + end + end) + + i3.add_recipe_filter("Default progressive filter", progressive_filter) + + on_joinplayer(function(player) + local name = player:get_player_name() + local meta = player:get_meta() + local data = pdata[name] + + data.inv_items = dslz(meta:get_string "inv_items") or {} + data.known_recipes = dslz(meta:get_string "known_recipes") or 0 + + local items = get_filtered_items(player, data) + data.items_raw = items + search(data) + + if singleplayer then + init_hud(player, data) + end + end) + + table_merge(META_SAVES, {"inv_items", "known_recipes"}) +end + +local bag_recipes = { + small = { + {"", "farming:string", ""}, + {"group:wool", "group:wool", "group:wool"}, + {"group:wool", "group:wool", "group:wool"}, + }, + medium = { + {"farming:string", "i3:bag_small", "farming:string"}, + {"farming:string", "i3:bag_small", "farming:string"}, + }, + large = { + {"farming:string", "i3:bag_medium", "farming:string"}, + {"farming:string", "i3:bag_medium", "farming:string"}, + }, +} + +for size, rcp in pairs(bag_recipes) do + local bagname = fmt("i3:bag_%s", size) + + core.register_craftitem(bagname, { + description = fmt("%s Backpack", size:gsub("^%l", upper)), + inventory_image = fmt("i3_bag_%s.png", size), + stack_max = 1, + }) + + core.register_craft {output = bagname, recipe = rcp} + core.register_craft {type = "fuel", recipe = bagname, burntime = 3} +end + +--dofile(core.get_modpath("i3") .. "/test_tabs.lua") +--dofile(core.get_modpath("i3") .. "/test_custom_recipes.lua") diff --git a/mods/gui/i3/locale/i3.fr.tr b/mods/gui/i3/locale/i3.fr.tr new file mode 100644 index 0000000..2c57546 --- /dev/null +++ b/mods/gui/i3/locale/i3.fr.tr @@ -0,0 +1,85 @@ +# textdomain: i3 + +### init.lua ### + +@1 added in your inventory=@1 ajouté à votre inventaire +@1 new recipe(s) discovered!=@1 nouvelle(s) recette(s) découverte(s)! +@1 of chance to drop=@1 de chance de tomber +@1 spawned=@1 spawné +Achievements: @1 of @2 (@3)=Succès : @1 sur @2 (@3) +Any black dye=N'importe quel colorant noir +Any black flower=N'importe quelle fleur noire +Any blue dye=N'importe quel colorant bleu +Any blue flower=N'importe quelle fleur bleue +Any brown dye=N'importe quel colorant marron +Any carpet=N'importe quel tapis +Any coal=N'importe quel charbon +Any cyan dye=N'importe quel colorant bleu turquoise +Any dark green dye=N'importe quel colorant vert foncé +Any dark grey dye=N'importe quel colorant gris foncé +Any dye=N'importe quel colorant +Any flower=N'importe quelle fleur +Any glass=N'importe quel verre +Any green dye=N'importe quel colorant vert +Any green flower=N'importe quelle fleur verte +Any grey dye=N'importe quel colorant gris +Any item belonging to the groups: @1=Tout item appartenant aux groupes : @1 +Any leaves=N'importe quelles feuilles d'arbre +Any magenta dye=N'importe quel colorant mauve +Any mushroom=N'importe quel champignon +Any orange dye=N'importe quel colorant orange +Any orange flower=N'importe quelle fleur orange +Any pink dye=N'importe quel colorant rose +Any red dye=N'importe quel colorant rouge +Any red flower=N'importe quelle fleur rouge +Any sand=N'importe quel sable +Any stick=N'importe quel bâton +Any stone=N'importe quelle roche +Any tree=N'importe quel tronc d'arbre +Any vessel=N'importe quel couvert +Any violet dye=N'importe quel colorant violet +Any violet flower=N'importe quelle fleur violette +Any white dye=N'importe quel colorant blanc +Any white flower=N'importe quelle fleur blanche +Any wood planks=N'importe quelles planches de bois +Any wool=N'importe quelle laine +Any yellow dye=N'importe quel colorant jaune +Any yellow flower=N'importe quelle fleur jaune +Armor=Armure +Bag=Sac +Bookmarks=Favoris +Burning time: @1=Temps de combustion : @1 +Cannot mark this item. Bookmark limit reached.= +Collect items to reveal more recipes=Collecter des items pour révéler des recettes +Compress items=Compresser les items +Cooking=Cuisson +Cooking time: @1=Temps de cuisson : @1 +Craft (x@1)=Fabriquer (x@1) +Digging=Minage +Digging (by chance)=Minage (par chance) +Heal=Guérison +Inventory=Inventaire +Items=Items +Level=Niveau +Mark this item=Ajouter aux favoris +No item to show=Aucun item à montrer +No recipes=Aucune recette +No usages=Aucun usage +Only drop if using one of these tools: @1=Tombe seulement en utilisant un de ces outils : @1 +Only drop if using this tool: @1=Tombe seulement en utilisant cet outil : @1 +Quick crafting=Fabrication rapide +Recipe @1 of @2=Recette @1 sur @2 +Repairable by step of @1=Réparable par étape de @1 +Replaced by @1 on burning=Remplacé par @1 à la combustion +Replaced by @1 on crafting=Remplacé par @1 à la fabrication +Replaced by @1 on smelting=Remplacé par @1 à la cuisson +Shapeless=Sans forme particulière +Skins=Skins +Sort items (A-Z)=Trier les items (A-Z) +Sort items (Z-A)=Trier les items (Z-A) +The inventory is extended by @1 slots=L'inventaire est étendu de @1 slots +Trash all items=Détruire tous les items +Unknown Item (@1)=Item inconnu (@1) +Unmark this item=Enlever des favoris +Usage @1 of @2=Usage @1 sur @2 + \ No newline at end of file diff --git a/mods/gui/i3/locale/template b/mods/gui/i3/locale/template new file mode 100644 index 0000000..1046d60 --- /dev/null +++ b/mods/gui/i3/locale/template @@ -0,0 +1,84 @@ +# textdomain: i3 + +### init.lua ### + +@1 added in your inventory= +@1 new recipe(s) discovered!= +@1 of chance to drop= +@1 spawned= +Achievements: @1 of @2 (@3)= +Any black dye= +Any black flower= +Any blue dye= +Any blue flower= +Any brown dye= +Any carpet= +Any coal= +Any cyan dye= +Any dark green dye= +Any dark grey dye= +Any dye= +Any flower= +Any glass= +Any green dye= +Any green flower= +Any grey dye= +Any item belonging to the groups: @1= +Any leaves= +Any magenta dye= +Any mushroom= +Any orange dye= +Any orange flower= +Any pink dye= +Any red dye= +Any red flower= +Any sand= +Any stick= +Any stone= +Any tree= +Any vessel= +Any violet dye= +Any violet flower= +Any white dye= +Any white flower= +Any wood planks= +Any wool= +Any yellow dye= +Any yellow flower= +Armor= +Bag= +Bookmarks= +Burning time: @1= +Cannot mark this item. Bookmark limit reached.= +Collect items to reveal more recipes= +Compress items= +Cooking= +Cooking time: @1= +Craft (x@1)= +Digging= +Digging (by chance)= +Heal= +Inventory= +Items= +Level= +Mark this item= +No item to show= +No recipes= +No usages= +Only drop if using one of these tools: @1= +Only drop if using this tool: @1= +Quick crafting= +Recipe @1 of @2= +Repairable by step of @1= +Replaced by @1 on burning= +Replaced by @1 on crafting= +Replaced by @1 on smelting= +Shapeless= +Skins= +Sort items (A-Z)= +Sort items (Z-A)= +The inventory is extended by @1 slots= +Trash all items= +Unknown Item (@1)= +Unmark this item= +Usage @1 of @2= diff --git a/mods/gui/i3/mod.conf b/mods/gui/i3/mod.conf new file mode 100644 index 0000000..745282f --- /dev/null +++ b/mods/gui/i3/mod.conf @@ -0,0 +1,3 @@ +name = i3 +description = Tiling inventory for Minetest +optional_depends = 3d_armor, skinsdb, awards diff --git a/mods/gui/i3/settingtypes.txt b/mods/gui/i3/settingtypes.txt new file mode 100644 index 0000000..a97f54a --- /dev/null +++ b/mods/gui/i3/settingtypes.txt @@ -0,0 +1,2 @@ +# The progressive mode shows recipes you can craft from items you ever had in your inventory. +i3_progressive_mode (Learn crafting recipes progressively) bool false diff --git a/mods/gui/i3/sounds/i3_click.ogg b/mods/gui/i3/sounds/i3_click.ogg new file mode 100755 index 0000000..2983f59 Binary files /dev/null and b/mods/gui/i3/sounds/i3_click.ogg differ diff --git a/mods/gui/i3/sounds/i3_craft.ogg b/mods/gui/i3/sounds/i3_craft.ogg new file mode 100755 index 0000000..bdaa95e Binary files /dev/null and b/mods/gui/i3/sounds/i3_craft.ogg differ diff --git a/mods/gui/i3/sounds/i3_tab.ogg b/mods/gui/i3/sounds/i3_tab.ogg new file mode 100755 index 0000000..1ed4380 Binary files /dev/null and b/mods/gui/i3/sounds/i3_tab.ogg differ diff --git a/mods/gui/i3/test_custom_recipes.lua b/mods/gui/i3/test_custom_recipes.lua new file mode 100644 index 0000000..0c19925 --- /dev/null +++ b/mods/gui/i3/test_custom_recipes.lua @@ -0,0 +1,159 @@ +i3.register_craft({ + grid = { + "X #", + " ## ", + "X#X#", + "X X", + }, + key = { + ['#'] = "default:wood", + ['X'] = "default:glass", + }, + result = "default:mese 3", +}) + +i3.register_craft({ + grid = { + "X #", + " ## ", + "X#X#X", + "X X", + }, + key = { + ['#'] = "default:wood", + ['X'] = "default:glass", + }, + result = "default:mese 3", +}) + +i3.register_craft({ + grid = { + "X #", + " ## ", + "X#X#", + "#X#X#", + "X X", + }, + key = { + ['#'] = "default:wood", + ['X'] = "default:glass", + }, + result = "default:mese 3", +}) + +i3.register_craft({ + grid = { + "X #", + " ## ", + "X#X#", + "#X#X#", + "X X##", + }, + key = { + ['#'] = "default:wood", + ['X'] = "default:glass", + }, + result = "default:mese 3", +}) + + +i3.register_craft({ + grid = { + "X #", + " ## ", + "X#X#", + "#X#X#", + "X X##", + " ## ", + }, + key = { + ['#'] = "default:wood", + ['X'] = "default:glass", + }, + result = "default:mese 3", +}) + +i3.register_craft({ + grid = { + "X #", + " ## ", + "X#X#", + "#X#X#", + "X X##X", + " ## ", + }, + key = { + ['#'] = "default:wood", + ['X'] = "default:glass", + }, + result = "default:mese 3", +}) + +i3.register_craft({ + grid = { + "X #", + " ## ", + "X#X#", + "#X#X#", + "X X##X#", + " ## ", + }, + key = { + ['#'] = "default:wood", + ['X'] = "default:glass", + }, + result = "default:mese 3", +}) + +i3.register_craft({ + grid = { + "X #", + " ## ", + "X#X#", + "#X#X#", + "X X##X#X", + " ## ", + }, + key = { + ['#'] = "default:wood", + ['X'] = "default:glass", + }, + result = "default:mese 3", +}) + +i3.register_craft({ + grid = { + "X #", + " ## ", + "X#X#", + "#X#X#", + "X X##X#X", + " ## ", + "#X#X#", + "#X#X#", + }, + key = { + ['#'] = "default:wood", + ['X'] = "default:glass", + }, + result = "default:mese 3", +}) + +i3.register_craft({ + grid = { + "X #", + " ## ", + "X#X#", + "#X#X#", + "X X##X#X", + " ## ", + "#X#X#", + "#X#X#", + "X #", + }, + key = { + ['#'] = "default:wood", + ['X'] = "default:glass", + }, + result = "default:mese 3", +}) diff --git a/mods/gui/i3/test_online_recipe.json b/mods/gui/i3/test_online_recipe.json new file mode 100644 index 0000000..aa08e59 --- /dev/null +++ b/mods/gui/i3/test_online_recipe.json @@ -0,0 +1,8 @@ +{ + "items": [ + "default:stone, default:stone, default:stone", + "default:stone, , default:stone", + "default:stone, default:stone, default:stone" + ], + "result": "default:cobble 16" +} diff --git a/mods/gui/i3/test_tabs.lua b/mods/gui/i3/test_tabs.lua new file mode 100644 index 0000000..7d0b5ec --- /dev/null +++ b/mods/gui/i3/test_tabs.lua @@ -0,0 +1,49 @@ +i3.new_tab { + name = "test1", + description = "Test 1 Test 1", + image = "i3_heart.png", + + formspec = function(player, data, fs) + fs("label[3,1;Test 1]") + end, +} + +i3.new_tab { + name = "test2", + description = "Test 2", + image = "i3_mesepick.png", + + formspec = function(player, data, fs) + fs("label[3,1;Test 2]") + end, +} + +i3.new_tab { + name = "test3", + description = "Test 3", + + access = function(player, data) + local name = player:get_player_name() + if name == "singleplayer" then + return true + end + end, + + formspec = function(player, data, fs) + fs("label[3,1;Test 3]") + end, + + fields = function(player, data, fields) + i3.set_fs(player, "label[3,2;Test extra_fs]") + end, +} + +i3.override_tab("test2", { + name = "test2", + description = "Test override", + image = "i3_mesepick.png", + + formspec = function(player, data, fs) + fs("label[3,1;Override!]") + end, +}) diff --git a/mods/gui/i3/textures/i3_arrow.png b/mods/gui/i3/textures/i3_arrow.png new file mode 100644 index 0000000..df1bbdb Binary files /dev/null and b/mods/gui/i3/textures/i3_arrow.png differ diff --git a/mods/gui/i3/textures/i3_bag_large.png b/mods/gui/i3/textures/i3_bag_large.png new file mode 100644 index 0000000..bfeb875 Binary files /dev/null and b/mods/gui/i3/textures/i3_bag_large.png differ diff --git a/mods/gui/i3/textures/i3_bag_medium.png b/mods/gui/i3/textures/i3_bag_medium.png new file mode 100644 index 0000000..0b4c11d Binary files /dev/null and b/mods/gui/i3/textures/i3_bag_medium.png differ diff --git a/mods/gui/i3/textures/i3_bag_small.png b/mods/gui/i3/textures/i3_bag_small.png new file mode 100644 index 0000000..e4a3c62 Binary files /dev/null and b/mods/gui/i3/textures/i3_bag_small.png differ diff --git a/mods/gui/i3/textures/i3_bg.png b/mods/gui/i3/textures/i3_bg.png new file mode 100644 index 0000000..383d532 Binary files /dev/null and b/mods/gui/i3/textures/i3_bg.png differ diff --git a/mods/gui/i3/textures/i3_bg_full.png b/mods/gui/i3/textures/i3_bg_full.png new file mode 100644 index 0000000..9b29903 Binary files /dev/null and b/mods/gui/i3/textures/i3_bg_full.png differ diff --git a/mods/gui/i3/textures/i3_book.png b/mods/gui/i3/textures/i3_book.png new file mode 100644 index 0000000..69855fd Binary files /dev/null and b/mods/gui/i3/textures/i3_book.png differ diff --git a/mods/gui/i3/textures/i3_btn9.png b/mods/gui/i3/textures/i3_btn9.png new file mode 100644 index 0000000..34433ac Binary files /dev/null and b/mods/gui/i3/textures/i3_btn9.png differ diff --git a/mods/gui/i3/textures/i3_btn9_hovered.png b/mods/gui/i3/textures/i3_btn9_hovered.png new file mode 100644 index 0000000..01c2dc7 Binary files /dev/null and b/mods/gui/i3/textures/i3_btn9_hovered.png differ diff --git a/mods/gui/i3/textures/i3_btn9_pressed.png b/mods/gui/i3/textures/i3_btn9_pressed.png new file mode 100644 index 0000000..0cbac75 Binary files /dev/null and b/mods/gui/i3/textures/i3_btn9_pressed.png differ diff --git a/mods/gui/i3/textures/i3_cancel.png b/mods/gui/i3/textures/i3_cancel.png new file mode 100644 index 0000000..0f6a2c4 Binary files /dev/null and b/mods/gui/i3/textures/i3_cancel.png differ diff --git a/mods/gui/i3/textures/i3_compress.png b/mods/gui/i3/textures/i3_compress.png new file mode 100644 index 0000000..754179c Binary files /dev/null and b/mods/gui/i3/textures/i3_compress.png differ diff --git a/mods/gui/i3/textures/i3_export.png b/mods/gui/i3/textures/i3_export.png new file mode 100644 index 0000000..e179c07 Binary files /dev/null and b/mods/gui/i3/textures/i3_export.png differ diff --git a/mods/gui/i3/textures/i3_fav.png b/mods/gui/i3/textures/i3_fav.png new file mode 100644 index 0000000..b1ff1d5 Binary files /dev/null and b/mods/gui/i3/textures/i3_fav.png differ diff --git a/mods/gui/i3/textures/i3_fav_off.png b/mods/gui/i3/textures/i3_fav_off.png new file mode 100644 index 0000000..61aa7de Binary files /dev/null and b/mods/gui/i3/textures/i3_fav_off.png differ diff --git a/mods/gui/i3/textures/i3_fire_anim.png b/mods/gui/i3/textures/i3_fire_anim.png new file mode 100644 index 0000000..22dc40a Binary files /dev/null and b/mods/gui/i3/textures/i3_fire_anim.png differ diff --git a/mods/gui/i3/textures/i3_furnace_anim.png b/mods/gui/i3/textures/i3_furnace_anim.png new file mode 100644 index 0000000..472c382 Binary files /dev/null and b/mods/gui/i3/textures/i3_furnace_anim.png differ diff --git a/mods/gui/i3/textures/i3_heart.png b/mods/gui/i3/textures/i3_heart.png new file mode 100644 index 0000000..8d88fc8 Binary files /dev/null and b/mods/gui/i3/textures/i3_heart.png differ diff --git a/mods/gui/i3/textures/i3_heart_grey.png b/mods/gui/i3/textures/i3_heart_grey.png new file mode 100644 index 0000000..bbceb43 Binary files /dev/null and b/mods/gui/i3/textures/i3_heart_grey.png differ diff --git a/mods/gui/i3/textures/i3_heart_half.png b/mods/gui/i3/textures/i3_heart_half.png new file mode 100644 index 0000000..7132b20 Binary files /dev/null and b/mods/gui/i3/textures/i3_heart_half.png differ diff --git a/mods/gui/i3/textures/i3_hotbar.png b/mods/gui/i3/textures/i3_hotbar.png new file mode 100644 index 0000000..b487f4b Binary files /dev/null and b/mods/gui/i3/textures/i3_hotbar.png differ diff --git a/mods/gui/i3/textures/i3_mesepick.png b/mods/gui/i3/textures/i3_mesepick.png new file mode 100644 index 0000000..1531c3f Binary files /dev/null and b/mods/gui/i3/textures/i3_mesepick.png differ diff --git a/mods/gui/i3/textures/i3_next.png b/mods/gui/i3/textures/i3_next.png new file mode 100644 index 0000000..9392fe0 Binary files /dev/null and b/mods/gui/i3/textures/i3_next.png differ diff --git a/mods/gui/i3/textures/i3_next_hover.png b/mods/gui/i3/textures/i3_next_hover.png new file mode 100644 index 0000000..193b983 Binary files /dev/null and b/mods/gui/i3/textures/i3_next_hover.png differ diff --git a/mods/gui/i3/textures/i3_search.png b/mods/gui/i3/textures/i3_search.png new file mode 100644 index 0000000..9528709 Binary files /dev/null and b/mods/gui/i3/textures/i3_search.png differ diff --git a/mods/gui/i3/textures/i3_shapeless.png b/mods/gui/i3/textures/i3_shapeless.png new file mode 100644 index 0000000..75a4e03 Binary files /dev/null and b/mods/gui/i3/textures/i3_shapeless.png differ diff --git a/mods/gui/i3/textures/i3_slot.png b/mods/gui/i3/textures/i3_slot.png new file mode 100644 index 0000000..93e4211 Binary files /dev/null and b/mods/gui/i3/textures/i3_slot.png differ diff --git a/mods/gui/i3/textures/i3_sort_az.png b/mods/gui/i3/textures/i3_sort_az.png new file mode 100644 index 0000000..971bd72 Binary files /dev/null and b/mods/gui/i3/textures/i3_sort_az.png differ diff --git a/mods/gui/i3/textures/i3_sort_za.png b/mods/gui/i3/textures/i3_sort_za.png new file mode 100644 index 0000000..e395669 Binary files /dev/null and b/mods/gui/i3/textures/i3_sort_za.png differ diff --git a/mods/gui/i3/textures/i3_steelpick.png b/mods/gui/i3/textures/i3_steelpick.png new file mode 100644 index 0000000..7764446 Binary files /dev/null and b/mods/gui/i3/textures/i3_steelpick.png differ diff --git a/mods/gui/i3/textures/i3_tab.png b/mods/gui/i3/textures/i3_tab.png new file mode 100644 index 0000000..4dea611 Binary files /dev/null and b/mods/gui/i3/textures/i3_tab.png differ diff --git a/mods/gui/i3/textures/i3_tab_hover.png b/mods/gui/i3/textures/i3_tab_hover.png new file mode 100644 index 0000000..44c9adc Binary files /dev/null and b/mods/gui/i3/textures/i3_tab_hover.png differ diff --git a/mods/gui/i3/textures/i3_trash.png b/mods/gui/i3/textures/i3_trash.png new file mode 100644 index 0000000..962eac7 Binary files /dev/null and b/mods/gui/i3/textures/i3_trash.png differ diff --git a/mods/gui/smart_inventory/API.md b/mods/gui/smart_inventory/API.md deleted file mode 100644 index dfeb075..0000000 --- a/mods/gui/smart_inventory/API.md +++ /dev/null @@ -1,99 +0,0 @@ -# API definition to working together with smart_inventory - -## Pages framework - -### Register page -To get own page in smart_inventory the next register method should be used -``` -smart_inventory.register_page({ - name = string, - icon = string, - label = string, - tooltip = string, - smartfs_callback = function, - sequence = number, - on_button_click = function, - is_visible_func = function, - }) -``` -- name - unique short name, used for identification -- icon - Image displayed on page button. Optional -- label - Label displayed on page button. Optional -- tooltip - Text displayed at mouseover on the page button. Optional -- smartfs_callback(state) - smartfs callback function See [smartfs documentation](https://github.com/minetest-mods/smartfs/blob/master/docs) and existing pages implementations for reference. -- sequence - The buttons are sorted by this number (crafting=10, creative=15, player=20) -- on_button_click(state) - function called each page button click -- is_visible_func(state) - function for dynamic page enabelling. Should return bool value. - -### Get the definition for registered smart_inventory page -```smart_inventory.get_registered_page(pagename)``` - -### Get smartfs state for players inventory -```smart_inventory.get_player_state(playername)``` -Get the root smartfs state for players inventory. Note: In workbench mode the function return nil if the player does not have the form open - -### Get smartfs state for a registered page in players inventory -```smart_inventory.get_page_state(pagename, playername)``` - -## Filter framework -Smart_inventory uses a filter-framework for dynamic grouping in creative and crafting page. The filter framework allow to register additional classify filters for beter dynamic grouping results. -Maybe the framework will be moved to own mod in the feature if needed. Please note the smart_inventory caches all results at init time so static groups only allowed. The groups will not be re-checked at runtime. - -### Register new filter -``` -smart_inventory.filter.register_filter({ - name = string, - check_item_by_def = function, - get_description = function, - get_keyword = function, - is_valid = function, - }) -``` - - name - unique filter name - - check_item_by_def(fltobj, itemdef) - function to check the item classify by item definition. Item definition is the reference to minetest.registered_items[item] entry - next return values allowed: - - true -> direct (belongs to) assignment to the classify group named by filtername - - string -> dimension, steps splitted by ":" (`a:b:c:d results in filtername, filtername:a, filtername:a:b, filtername:a:b:c, filtername:a:b:c:d`) - - key/value table -> multiple groups assignment. Values could be dimensions as above (`{a,b} results in filtername, filtername:a, filtername:b`) - - nil -> no group assingment by this filter - - get_description(fltobj, group) - optional - get human readable description for the dimension string (`filtername:a:b:c`) - - get_keyword(fltobj, group) - get string that should be used for searches or nil if the group should not used in for searches - - is_valid(fltobj, groupname) - Check if the groupname is valid. By default all groups are valid - - -### Filter Object methods - -smart_inventory.filter.get(name) get filter object by registered name. Returns filter object fltobj - - fltobj:check_item_by_name(itemname) classify by itemname (wrapper for check_item_by_def) - - fltobj:check_item_by_def(def) classify by item definition - - fltobj:get_description(group) get group description - - fltobj:get_keyword(group) get string that should be used for searches - -## Cache framework -cache.register_on_cache_filled(function, parameter) - hook to call additional initializations after the cache is filled - - -## Crecipes framework -The should be used trough cache.register_on_cache_filled to be sure all items are already known -crecipes.add_recipes_from_list(recipeslist) - Add Custom-Type Recipes to the smart inventory database - -## Example usage for cache and crecipe -``` -if minetest.global_exists("smart_inventory") then - -- add grinder recipes to smart inventory database - local crecipes = smart_inventory.crecipes - local cache = smart_inventory.cache - local function fill_citem_recipes() - local recipelist = {} - for _, e in ipairs(crushingfurnace_receipes) do - table.insert(recipelist, { - output = e[2], - items = {e[1]}, - type = "grinding" - }) - end - crecipes.add_recipes_from_list(recipelist) - end - cache.register_on_cache_filled(fill_citem_recipes) -end -``` diff --git a/mods/gui/smart_inventory/LICENSE b/mods/gui/smart_inventory/LICENSE deleted file mode 100644 index 65c5ca8..0000000 --- a/mods/gui/smart_inventory/LICENSE +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/mods/gui/smart_inventory/README.md b/mods/gui/smart_inventory/README.md deleted file mode 100644 index 94b216e..0000000 --- a/mods/gui/smart_inventory/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# smart_inventory - -## Overview -A fast Minetest inventory with focus on a great number of items and big screens. The special feature of this inventory is the dynamic classification filters that allow fast searching and browsing trough available items and show relevant invormations only to the user. - -The mod is organized in multiple pages, each page does have own focus and follow own vision. - -## Crafting page -![Screenshot](https://github.com/bell07/minetest-smart_inventory/blob/master/screenshot_crafting.png) -The vision is to not affect the gameplay trough crafting helpers. The dynamic search helper display currently relevant craft recipes only based on inventory content by default. -- Contains the usual player-, and crafting inventory -- Additional view of "craftable items" based on players inventory content -- Dynamic grouping of craftable items for better overview -- Lookup field to get all recipes with item in it - with filter for revealed items if the doc system is used -- Search field - with filter for revealed items if the doc system is used -- Compress - use the stack max size in inventory -- Sweep - move content of crafting inventory back to the main inventory - -### Optional support for other mods -doc_items - if the doc system is found the crafting page shows only items craftable by known (revealed) items. -A lookup button is available on already known items to jump to the documntation entry - - -## Creative page -![Screenshot](https://github.com/bell07/minetest-smart_inventory/blob/master/screenshot_creative.png) -The vision is to get items fast searchable and gettable -- 3 dynamic filters + text search field for fast items search -- Sort out "mass"-groups to a special "Shaped" group -- just click to the item to get it in inventory -- cleanup of inventory trough "Trash" field -- clean whole inventory trough "Trash all" button -- save and restore inventory content in 3x save slots - -## Player page -![Screenshot](https://github.com/bell07/minetest-smart_inventory/blob/master/screenshot_player.png) -The vision is to get all skins and player customizations visual exposed. - -### 3d_armor / clothing -In creative mode all useable armor and clothing items available. The players inventory is not used in this mode. In survival only the armor and clothing from players inventory -is shown. - -### skins -tested only with my fork https://github.com/bell07/minetest-skinsdb -But it should be work with any fork that uses skins.skins[] and have *_preview.png files - -## Doc page -![Screenshot](https://github.com/bell07/minetest-smart_inventory/blob/master/screenshot_doc.png) -The vision is to get all ingame documentation available in a fast way. So navigation from crafting page is possible directly to the doc_item entry -The doc and doc_items mods required to get the page - - -## Dependencies: -Screen size at least 1024x768 / big screen. On my mobile with "full HD" it does not work. -Minetest stable 0.4.15 or newer -default mod (some graphics are used from this mod) - - - -## Settings -``` -#If enabled, the mod will show alternative human readable filterstrings if available. -smart_inventory_friendly_group_names (Show “friendly” filter grouping names) bool true - -#List of groups defined for special handling of "Shaped nodes" (Comma separated). -#Items in this groups ignores the "not_in_inventory" group and are moved to separate "Shaped" category -smart_inventory_shaped_groups (List of groups to be handled as separate) string carpet,door,fence,stair,slab,wall,micro,panel,slope,dye - -#If enabled, the the mod does not replace other inventory mods. -#The functionality is provided in a workbench. -smart_inventory_workbench_mode (Use workbench instead of players inventory) bool false -``` - -License: [LGPL-3](https://github.com/bell07/minetest-smart_inventory/blob/master/LICENSE) -Textures: - - Workbench: WTFPL (credits: to xdecor project) - - Buttons: WTFPL (credits to Stix (Minetest-forum)) - - Arrow buttons: WTFPL (credits to daretmavi) diff --git a/mods/gui/smart_inventory/depends.txt b/mods/gui/smart_inventory/depends.txt deleted file mode 100644 index 6efe4bb..0000000 --- a/mods/gui/smart_inventory/depends.txt +++ /dev/null @@ -1,6 +0,0 @@ -creative? -sfinv? -3d_armor? -skinsdb? -doc_items? -unifieddyes? diff --git a/mods/gui/smart_inventory/doc_addon.lua b/mods/gui/smart_inventory/doc_addon.lua deleted file mode 100644 index ad241f1..0000000 --- a/mods/gui/smart_inventory/doc_addon.lua +++ /dev/null @@ -1,73 +0,0 @@ -smart_inventory.doc_items_mod = minetest.get_modpath("doc_items") - -local doc_addon = {} - -local doc_item_entries = {} - -function doc_addon.is_revealed_item(itemname, playername) - if not smart_inventory.doc_items_mod then - return true - end - - doc_addon.get_category_list() - itemname = minetest.registered_aliases[itemname] or itemname - local doc_entry = doc_item_entries[itemname] - if doc_entry then - return doc.entry_revealed(playername, doc_entry.cid, doc_entry.eid) - else - -- unknown item - return false - end - return true -end - -function doc_addon.show(itemname, playername) - if not smart_inventory.doc_items_mod then - return - end - - doc_addon.get_category_list() - itemname = minetest.registered_aliases[itemname] or itemname - local doc_entry = doc_item_entries[itemname] - if doc_entry then - doc.mark_entry_as_viewed(playername, doc_entry.cid, doc_entry.eid) - local state = smart_inventory.get_page_state("doc", playername) - local codebox = state:get("code") - codebox.edata = doc_entry - doc.data.players[playername].galidx = 1 - codebox:submit() --update the page - state.location.parentState:get("doc_button"):submit() -- switch to the tab - end - -end - --- Get sorted category list -local doc_category_list = nil - -function doc_addon.get_category_list() - -- build on first usage - if not doc_category_list and smart_inventory.doc_items_mod then - doc_category_list = {} - for _, category_name in ipairs(doc.data.category_order) do - local category = doc.data.categories[category_name] - if category then - local entries_data = {} - for _, eid in ipairs(doc.get_sorted_entry_names(category_name)) do - local entry = doc.data.categories[category_name].entries[eid] - if entry.data.itemstring and - minetest.registered_items[entry.data.itemstring] and - (entry.data.def == minetest.registered_items[entry.data.itemstring] or entry.data.def.door) then - local edata = {cid = category_name, cid_data = category, eid = eid, data = entry} - table.insert(entries_data, edata) - doc_item_entries[entry.data.itemstring] = edata - end - end - table.insert(doc_category_list, {cid = category_name, data = category, entries = entries_data}) - end - end - end - return doc_category_list -end - -------------------------- -return doc_addon diff --git a/mods/gui/smart_inventory/init.lua b/mods/gui/smart_inventory/init.lua deleted file mode 100644 index 3d30f86..0000000 --- a/mods/gui/smart_inventory/init.lua +++ /dev/null @@ -1,31 +0,0 @@ -smart_inventory = {} -smart_inventory.modpath = minetest.get_modpath(minetest.get_current_modname()) -local modpath = smart_inventory.modpath - --- load libs -smart_inventory.txt = dofile(modpath.."/libs/simple_po_reader.lua") -smart_inventory.smartfs = dofile(modpath.."/libs/smartfs.lua") -smart_inventory.smartfs_elements = dofile(modpath.."/libs/smartfs-elements.lua") - -smart_inventory.doc_addon = dofile(modpath.."/doc_addon.lua") - -smart_inventory.filter = dofile(modpath.."/libs/filter.lua") -smart_inventory.cache = dofile(modpath.."/libs/cache.lua") -smart_inventory.crecipes = dofile(modpath.."/libs/crecipes.lua") -smart_inventory.maininv = dofile(modpath.."/libs/maininv.lua") - -smart_inventory.ui_tools = dofile(modpath.."/ui_tools.lua") --- register pages -dofile(modpath.."/inventory_framework.lua") -dofile(modpath.."/pages/crafting.lua") -dofile(modpath.."/pages/creative.lua") -dofile(modpath.."/pages/player.lua") -dofile(modpath.."/pages/doc.lua") -dofile(modpath.."/pages/awards.lua") - --- Cleanup inventories -minetest.register_on_leaveplayer(function(player) - local player_name = player:get_player_name() - minetest.remove_detached_inventory(player_name.."_crafting_inv") - minetest.remove_detached_inventory(player_name.."_trash_inv") -end) diff --git a/mods/gui/smart_inventory/inventory_framework.lua b/mods/gui/smart_inventory/inventory_framework.lua deleted file mode 100644 index 10ac079..0000000 --- a/mods/gui/smart_inventory/inventory_framework.lua +++ /dev/null @@ -1,139 +0,0 @@ -local smartfs = smart_inventory.smartfs -local maininv = smart_inventory.maininv -local modpath = smart_inventory.modpath - --- smartfs callback -local inventory_form = smartfs.create("smart_inventory:main", function(state) - - -- enhanced object to the main inventory functions - state.param.invobj = maininv.get(state.location.player) - - -- Set language code - local player_info = minetest.get_player_information(state.location.player) - if player_info and player_info.lang_code ~= "" then - state.lang_code = player_info.lang_code - end - - -- tabbed view controller - local tab_controller = { - _tabs = {}, - active_name = nil, - set_active = function(self, tabname) - for name, def in pairs(self._tabs) do - if name == tabname then - def.button:setBackground("halo.png") - def.view:setVisible(true) - else - def.button:setBackground(nil) - def.view:setVisible(false) - end - end - self.active_name = tabname - end, - tab_add = function(self, name, def) - def.viewstate:size(20,10) --size of tab view - self._tabs[name] = def - end, - get_active_name = function(self) - return self.active_name - end, - } - - --set screen size - state:size(20,12) - state:label(1,0.2,"header","Smart Inventory") - state:image(0,0,1,1,"header_logo", "logo.png") - state:image_button(19,0,1,1,"exit", "","smart_inventory_exit_button.png", true):setTooltip("Close the inventory") - local button_x = 0.1 - table.sort(smart_inventory.registered_pages, function(a,b) - if not a.sequence then - return false - elseif not b.sequence then - return true - elseif a.sequence > b.sequence then - return false - else - return true - end - end) - for _, def in ipairs(smart_inventory.registered_pages) do - assert(def.smartfs_callback, "Callback function needed") - assert(def.name, "Name is needed") - if not def.is_visible_func or def.is_visible_func(state) then - local tabdef = {} - local label - if not def.label then - label = "" - else - label = def.label - end - tabdef.button = state:button(button_x,11.2,1,1,def.name.."_button",label) - if def.icon then - tabdef.button:setImage(def.icon) - end - tabdef.button:setTooltip(def.tooltip) - tabdef.button:onClick(function(self) - tab_controller:set_active(def.name) - if def.on_button_click then - def.on_button_click(tabdef.viewstate) - end - end) - tabdef.view = state:container(0,1,def.name.."_container") - tabdef.viewstate = tabdef.view:getContainerState() - def.smartfs_callback(tabdef.viewstate) - tab_controller:tab_add(def.name, tabdef) - button_x = button_x + 1 - end - end - tab_controller:set_active(smart_inventory.registered_pages[1].name) -end) - -if minetest.settings:get_bool("smart_inventory_workbench_mode") then - dofile(modpath.."/workbench.lua") - smart_inventory.get_player_state = function(playername) - -- check the inventory is shown - local state = smartfs.opened[playername] - if state and (not state.obsolete) and - state.location.type == "player" and - state.def.name == "smart_inventory:main" then - return state - end - end -else - smartfs.set_player_inventory(inventory_form) - smart_inventory.get_player_state = function(playername) - return smartfs.inv[playername] - end -end - --- pages list -smart_inventory.registered_pages = {} - --- add new page -function smart_inventory.register_page(def) - table.insert(smart_inventory.registered_pages, def) -end - --- smart_inventory.get_player_state(playername) defined above - --- get state of active page -function smart_inventory.get_page_state(pagename, playername) - local rootstate = smart_inventory.get_player_state(playername) - if not rootstate then - return - end - local view = rootstate:get(pagename.."_container") - if not view then - return - end - return view:getContainerState() -end - --- get definition of registered page -function smart_inventory.get_registered_page(pagename) - for _, registred_page in ipairs(smart_inventory.registered_pages) do - if registred_page.name == pagename then - return registred_page - end - end -end diff --git a/mods/gui/smart_inventory/libs/cache.lua b/mods/gui/smart_inventory/libs/cache.lua deleted file mode 100644 index 3d18680..0000000 --- a/mods/gui/smart_inventory/libs/cache.lua +++ /dev/null @@ -1,139 +0,0 @@ -local filter = smart_inventory.filter - -local cache = {} -cache.cgroups = {} -- cache groups -cache.itemgroups = {} -- raw item groups for recipe checks -cache.citems = {} - ------------------------------------------------------ --- Add an Item to the cache ------------------------------------------------------ -function cache.add_item(item_def) - - -- already in cache. Skip duplicate processing - if cache.citems[item_def.name] then - return - end - - -- fill raw groups cache for recipes - for group, value in pairs(item_def.groups) do - cache.itemgroups[group] = cache.itemgroups[group] or {} - cache.itemgroups[group][item_def.name] = item_def - end - - local entry = { - name = item_def.name, - in_output_recipe = {}, - in_craft_recipe = {}, - cgroups = {} - } - cache.citems[item_def.name] = entry - -- classify the item - for _, flt in pairs(filter.registered_filter) do - local filter_result = flt:check_item_by_def(item_def) - if filter_result then - if filter_result == true then - cache.assign_to_group(flt.name, item_def, flt) - else - if type(filter_result) ~= "table" then - if tonumber(filter_result) ~= nil then - filter_result = {[flt.name..":"..filter_result] = true} - else - filter_result = {[filter_result] = true} - end - end - for key, val in pairs(filter_result) do - local filter_entry = tostring(key) - if val ~= true then - filter_entry = filter_entry..":"..tostring(val) - end - cache.assign_to_group(filter_entry, item_def, flt) - end - end - end - end -end - - ------------------------------------------------------ --- Add a item to cache group ------------------------------------------------------ -function cache.assign_to_group(group_name, itemdef, flt) - --- check and build filter chain - local abs_group - local parent_ref - local parent_stringpos - - for rel_group in group_name:gmatch("[^:]+") do - -- get parent relation and absolute path - if abs_group then - parent_ref = cache.cgroups[abs_group] - parent_stringpos = string.len(abs_group)+2 - abs_group = abs_group..":"..rel_group - else - abs_group = rel_group - end - if flt:is_valid(abs_group) then - -- check if group is new, create it - if not cache.cgroups[abs_group] then - if parent_ref then - parent_ref.childs[abs_group] = string.sub(group_name, parent_stringpos) - end - local group = { - name = abs_group, - items = {}, - parent = parent_ref, - childs = {}, - } - group.group_desc = flt:get_description(group) - group.keyword = flt:get_keyword(group) - cache.cgroups[abs_group] = group - end - - -- set relation - cache.cgroups[abs_group].items[itemdef.name] = itemdef - cache.citems[itemdef.name].cgroups[abs_group] = cache.cgroups[abs_group] - end - end -end - ------------------------------------------------------ --- Hook / Event for further initializations of the cache is filled ------------------------------------------------------ -cache.registered_on_cache_filled = {} -function cache.register_on_cache_filled(func, ...) - assert(type(func) == "function", "register_on_cache_filled needs a function") - table.insert(cache.registered_on_cache_filled, { func = func, opt = {...}}) -end - -local function process_on_cache_filled() - for _, hook in ipairs(cache.registered_on_cache_filled) do - hook.func(unpack(hook.opt)) - end -end - - ------------------------------------------------------ --- Fill the cache at init ------------------------------------------------------ -local function fill_cache() - local shape_filter = filter.get("shape") - for _, def in pairs(minetest.registered_items) do - - -- build groups and items cache - if def.description and def.description ~= "" and - (not def.groups.not_in_creative_inventory or shape_filter:check_item_by_def(def)) then - cache.add_item(def) - end - end - - -- call hooks - minetest.after(0, process_on_cache_filled) -end -minetest.after(0, fill_cache) - ------------------------------------------------------ --- return the reference to the mod ------------------------------------------------------ -return cache diff --git a/mods/gui/smart_inventory/libs/crecipes.lua b/mods/gui/smart_inventory/libs/crecipes.lua deleted file mode 100644 index 8c45b66..0000000 --- a/mods/gui/smart_inventory/libs/crecipes.lua +++ /dev/null @@ -1,400 +0,0 @@ -local doc_addon = smart_inventory.doc_addon -local cache = smart_inventory.cache -local filter = smart_inventory.filter - -local crecipes = {} -crecipes.crecipes = {} --list of all recipes - ------------------------------------------------------ --- crecipe: Class ------------------------------------------------------ -local crecipe_class = {} -local crecipe_class_mt = {__index = crecipe_class} -crecipes.crecipe_class = crecipe_class - ------------------------------------------------------ --- crecipes: analyze all data. Return false if invalid recipe. true on success ------------------------------------------------------ -function crecipe_class:analyze() - -- check recipe output - - if self._recipe.type == "cooking" then - return false --fuel not supported - end - - if self._recipe.output == "" then - minetest.log("[smartfs_inventory] broken recipe without output "..dump(self._recipe)) - return false - end - - local outstack = ItemStack(self._recipe.output) - if outstack:get_meta():get_int("palette_index") > 0 then - minetest.log("verbose", "[smartfs_inventory] ignore unifieddyes recipe "..self._recipe.output) - return -- not supported - end - - self.out_item = outstack:get_definition() - - if not self.out_item or not self.out_item.name then - minetest.log("[smartfs_inventory] unknown recipe result "..self._recipe.output) - return false - end - - -- check recipe items/groups - for _, recipe_item in pairs(self._recipe.items) do - if recipe_item ~= "" then - if self._items[recipe_item] then - self._items[recipe_item].count = self._items[recipe_item].count + 1 - else - self._items[recipe_item] = {count = 1} - end - end - end - for recipe_item, iteminfo in pairs(self._items) do - if recipe_item:sub(1, 6) ~= "group:" then - local itemname = minetest.registered_aliases[recipe_item] or recipe_item - if minetest.registered_items[itemname] then - iteminfo.items = {[itemname] = minetest.registered_items[itemname]} - else - minetest.log("[smartfs_inventory] unknown item in recipe: "..itemname.." for result "..self.out_item.name) - return false - end - else - local retitems - for groupname in string.gmatch(recipe_item:sub(7), '([^,]+)') do - if not retitems then --first entry - if cache.itemgroups[groupname] then - retitems = {} - for k,v in pairs(cache.itemgroups[groupname]) do - retitems[k] = v - end - else - minetest.log("[smartfs_inventory] unknown group description in recipe: "..recipe_item.." / "..groupname.." for result "..self.out_item.name) - return false - end - else - for itemname, itemdef in pairs(retitems) do - if not minetest.registered_items[itemname].groups[groupname] then - retitems[itemname] = nil - end - end - end - if not retitems or not next(retitems) then - minetest.log("[smartfs_inventory] no items matches group: "..recipe_item.." for result "..self.out_item.name) - return false - end - end - iteminfo.items = retitems - end - end - - -- invalid recipe - if not self._items then - minetest.log("[smartfs_inventory] skip recipe for: "..recipe_item) - return false - else - return true - end -end - ------------------------------------------------------ --- crecipes: Check if the recipe is revealed to the player ------------------------------------------------------ -function crecipe_class:is_revealed(playername, recursiv_checked_items) - local recipe_valid = true - for _, entry in pairs(self._items) do - recipe_valid = false - for _, itemdef in pairs(entry.items) do - if doc_addon.is_revealed_item(itemdef.name, playername) then - recipe_valid = true - break - end - - if cache.citems[itemdef.name].cgroups["shape"] then -- Check shapes recursive - recursiv_checked_items = recursiv_checked_items or {} - for _, recipe in ipairs(cache.citems[itemdef.name].in_output_recipe) do - local crecipe = crecipes.crecipes[recipe] - if recursiv_checked_items[crecipe.out_item.name] == nil then - recursiv_checked_items[crecipe.out_item.name] = false --avoid re-recursion - recursiv_checked_items[crecipe.out_item.name] = crecipe:is_revealed(playername, recursiv_checked_items) - end - if recursiv_checked_items[crecipe.out_item.name] == true then - recipe_valid = true - break - end - end - if recipe_valid then - break - end - end - end - if not recipe_valid then - break - end - end - return recipe_valid -end - ------------------------------------------------------ --- crecipes: Returns recipe without groups, with replacements ------------------------------------------------------ -function crecipe_class:get_with_placeholder(playername, inventory_tab) - local recipe = {} - for k, v in pairs(self._recipe) do - recipe[k] = v - end - recipe.items = {} - for k, v in pairs(self._recipe.items) do - recipe.items[k] = v - end - - local recursiv_checked_items = {} - if inventory_tab then - for k, v in pairs(inventory_tab) do - recursiv_checked_items[k] = v - end - end - self:is_revealed(playername, recursiv_checked_items) -- enhance recursiv_checked_items - - for key, recipe_item in pairs(recipe.items) do - local item - - -- Check for matching item in inventory and revealed cache - if inventory_tab then - local itemcount = 0 - for _, item_in_list in pairs(self._items[recipe_item].items) do - local in_inventory = inventory_tab[item_in_list.name] - if in_inventory == true then - item = item_in_list.name - break - elseif in_inventory and in_inventory > itemcount then - item = item_in_list.name - itemcount = in_inventory - end - end - end - - -- second try, revealed by recipe item - if not item then - for _, item_in_list in pairs(self._items[recipe_item].items) do - if recursiv_checked_items[item_in_list.name] then - item = item_in_list.name - break - end - end - end - - -- third try, get any revealed item - if not item then - for _, item_in_list in pairs(self._items[recipe_item].items) do - if doc_addon.is_revealed_item(item_in_list.name, playername) then - item = item_in_list.name - break - end - end - end - - -- last try, just get one item - if not item and self._items[recipe_item].items[1] then - item = self._items[recipe_item].items[1].name - end - - -- set recipe item - if item then - if recipe_item ~= item then - recipe.items[key] = { - item = item, - tooltip = recipe_item, - text = 'G', - } - end - end - end - return recipe -end - ------------------------------------------------------ --- crecipes: Check if recipe contains only items provided in reference_items ------------------------------------------------------ -function crecipe_class:is_craftable_by_items(reference_items) - local item_ok = false - for _, entry in pairs(self._items) do - item_ok = false - for _, itemdef in pairs(entry.items) do - if reference_items[itemdef.name] then - item_ok = true - end - end - if item_ok == false then - break - end - end - return item_ok -end - ------------------------------------------------------ --- crecipes: Check if the items placed in grid matches the recipe ------------------------------------------------------ -function crecipe_class:check_craftable_by_grid(grid) - -- only "normal" recipes supported - if self.recipe_type ~= "normal" then - return false - end - - for i = 1, 9 do - local grid_item = grid[i]:get_name() - -- check only fields filled in crafting grid - if grid_item and grid_item ~= "" then - -- check if item defined in recipe at this place - local item_ok = false - local recipe_item - -- default case - 3x3 crafting grid - local width = self._recipe.width - if not width or width == 0 or width == 3 then - recipe_item = self._recipe.items[i] - else - -- complex case - recalculate to the 3x3 crafting grid - local x = math.fmod((i-1),3)+1 - if x <= width then - local y = math.floor((i-1)/3+1) - local coord = (y-1)*width+x - recipe_item = self._recipe.items[coord] - else - recipe_item = "" - end - end - - if not recipe_item or recipe_item == "" then - return false - end - - -- check if it is a compatible item - for _, itemdef in pairs(self._items[recipe_item].items) do - if itemdef.name == grid_item then - item_ok = true - break - end - end - if not item_ok then - return false - end - end - end - return true -end - ------------------------------------------------------ --- Recipe object Constructor ------------------------------------------------------ -function crecipes.new(recipe) - local self = setmetatable({}, crecipe_class_mt) - -- self.out_item = nil - self._recipe = recipe - self.recipe_type = recipe.type - self._items = {} - return self -end - ------------------------------------------------------ --- Get all revealed recipes with at least one item in reference_items table ------------------------------------------------------ -function crecipes.get_revealed_recipes_with_items(playername, reference_items) - local recipelist = {} - local revealed_items_cache = {} - for itemname, _ in pairs(reference_items) do - if cache.citems[itemname] and cache.citems[itemname].in_craft_recipe then - for _, recipe in ipairs(cache.citems[itemname].in_craft_recipe) do - local crecipe = crecipes.crecipes[recipe] - if crecipe and crecipe:is_revealed(playername, revealed_items_cache) then - recipelist[recipe] = crecipe - end - -- lookup one step forward for shapes - if cache.citems[crecipe.out_item.name].cgroups["shape"] then - for _, recipe2 in ipairs(cache.citems[crecipe.out_item.name].in_craft_recipe) do - local crecipe = crecipes.crecipes[recipe2] - if crecipe and crecipe:is_revealed(playername, revealed_items_cache) then - recipelist[recipe2] = crecipe - end - end - end - end - end - if cache.citems[itemname] and cache.citems[itemname].in_output_recipe then - for _, recipe in ipairs(cache.citems[itemname].in_output_recipe) do - local crecipe = crecipes.crecipes[recipe] - if crecipe and crecipe:is_revealed(playername, revealed_items_cache) then - recipelist[recipe] = crecipe - end - end - end - end - return recipelist -end - ------------------------------------------------------ --- Get all recipes with all required items in reference items ------------------------------------------------------ -function crecipes.get_recipes_craftable(playername, reference_items) - local all = crecipes.get_revealed_recipes_with_items(playername, reference_items) - local craftable = {} - for recipe, crecipe in pairs(all) do - if crecipe:is_craftable_by_items(reference_items) then - craftable[recipe] = crecipe - end - end - return craftable -end - ------------------------------------------------------ --- Get all recipes that match to already placed items on crafting grid ------------------------------------------------------ -function crecipes.get_recipes_started_craft(playername, grid, reference_items) - local all = crecipes.get_revealed_recipes_with_items(playername, reference_items) - local craftable = {} - for recipe, crecipe in pairs(all) do - if crecipe:check_craftable_by_grid(grid) then - craftable[recipe] = crecipe - end - end - return craftable -end - -function crecipes.add_recipes_from_list(recipelist) - if recipelist then - for _, recipe in ipairs(recipelist) do - local recipe_obj = crecipes.new(recipe) - if recipe_obj:analyze() then - -- probably hidden therefore not indexed previous. But Items with recipe should be allways visible - cache.add_item(minetest.registered_items[recipe_obj.out_item.name]) - table.insert(cache.citems[recipe_obj.out_item.name].in_output_recipe, recipe) - crecipes.crecipes[recipe] = recipe_obj - if recipe_obj.recipe_type ~= "normal" then - cache.assign_to_group("recipetype:"..recipe_obj.recipe_type, recipe_obj.out_item, filter.get("recipetype")) - end - for _, entry in pairs(recipe_obj._items) do - for itemname, itemdef in pairs(entry.items) do - cache.add_item(itemdef) -- probably hidden therefore not indexed previous. But Items with recipe should be allways visible - table.insert(cache.citems[itemdef.name].in_craft_recipe, recipe) - cache.assign_to_group("ingredient:"..itemdef.name, recipe_obj.out_item, filter.get("ingredient")) - end - end - end - end - end -end - ------------------------------------------------------ --- Fill the recipes cache at init ------------------------------------------------------ -local function fill_recipe_cache() - for itemname, _ in pairs(minetest.registered_items) do - crecipes.add_recipes_from_list(minetest.get_all_craft_recipes(itemname)) - end - for itemname, _ in pairs(minetest.registered_aliases) do - crecipes.add_recipes_from_list(minetest.get_all_craft_recipes(itemname)) - end -end --- register to process after cache is filled -cache.register_on_cache_filled(fill_recipe_cache) - -return crecipes diff --git a/mods/gui/smart_inventory/libs/filter.lua b/mods/gui/smart_inventory/libs/filter.lua deleted file mode 100644 index 611538c..0000000 --- a/mods/gui/smart_inventory/libs/filter.lua +++ /dev/null @@ -1,409 +0,0 @@ -local txt = smart_inventory.txt - --------------------------------------------------------------- --- Filter class --------------------------------------------------------------- -local filter_class = {} -local filter_class_mt = {__index = filter_class} - -function filter_class:check_item_by_name(itemname) - if minetest.registered_items[itemname] then - return self:check_item_by_def(minetest.registered_items[itemname]) - end -end - -function filter_class:check_item_by_def(def) - error("check_item_by_def needs redefinition:"..debug.traceback()) -end - -function filter_class:_get_description(group) - if txt then - if txt[group.name] then - return txt[group.name].." ("..group.name..")" - elseif group.parent and group.parent.childs[group.name] and txt[group.parent.name] then - return txt[group.parent.name].." "..group.parent.childs[group.name].." ("..group.name..")" - else - return group.name - end - else - return group.name - end -end -filter_class.get_description = filter_class._get_description - -function filter_class:_get_keyword(group) - return group.group_desc -end - -filter_class.get_keyword = filter_class._get_keyword - -function filter_class:is_valid(group) - return true -end - -local filter = {} -filter.registered_filter = {} - -function filter.get(name) - return filter.registered_filter[name] -end - -function filter.register_filter(def) - assert(def.name, "filter needs a name") - assert(def.check_item_by_def, "filter function check_item_by_def required") - assert(not filter.registered_filter[def.name], "filter already exists") - setmetatable(def, filter_class_mt) - filter.registered_filter[def.name] = def -end - - --- rename groups for beter consistency -filter.group_rename = { - customnode_default = "customnode", -} - --- group configurations per basename --- true means is dimension --- 1 means replace the base only ("food_choco_powder" => food:choco_powder") -filter.base_group_config = { - armor = true, - physics = true, - basecolor = true, - excolor = true, - color = true, - unicolor = true, - food = 1, - customnode = true, -} - --- hide this groups -filter.group_hide_config = { - armor_count = true, - not_in_creative_inventory = false, -} - --- value of this group will be recalculated to % -filter.group_wear_config = { - armor_use = true, -} - --- Ususally 1 means true for group values. This is an exceptions table for this rule -filter.group_with_value_1_config = { - oddly_breakable_by_hand = true, -} - --------------------------------------------------------------- --- Filter group --------------------------------------------------------------- -filter.register_filter({ - name = "group", - check_item_by_def = function(self, def) - local ret = {} - for k_orig, v in pairs(def.groups) do - local k = filter.group_rename[k_orig] or k_orig - local mk, mv - - -- Check group base - local basename - for z in k:gmatch("[^_]+") do - basename = z - break - end - local basegroup_config = filter.base_group_config[basename] - if basegroup_config == true then - mk = string.gsub(k, "_", ":") - elseif basegroup_config == 1 then - mk = string.gsub(k, "^"..basename.."_", basename..":") - else - mk = k - end - - -- stack wear related value - if filter.group_wear_config[k] then - mv = tostring(math.floor(v / 65535 * 10000 + 0.5)/100).." %" - -- value-expandable groups - elseif v ~= 1 or k == filter.group_with_value_1_config[k] then - mv = v - else - mv = true - end - - if v ~= 0 and mk and not filter.group_hide_config[k] then - ret[mk] = mv - end - end - return ret - end, - }) - -filter.register_filter({ - name = "type", - check_item_by_def = function(self, def) - return self.name..":"..def.type - end, - get_keyword = function(self, group) - if group.name ~= self.name then - return group.parent.childs[group.name] - end - end - }) - -filter.register_filter({ - name = "mod", - check_item_by_def = function(self, def) - if def.mod_origin then - return self.name..":"..def.mod_origin - end - end, - get_keyword = function(self, group) - if group.name ~= self.name then - return group.parent.childs[group.name] - end - end - }) - -filter.register_filter({ - name = "translucent", - check_item_by_def = function(self, def) - if def.sunlight_propagates ~= 0 then - return def.sunlight_propagates - end - end, - }) - -filter.register_filter({ - name = "light", - check_item_by_def = function(self, def) - if def.light_source ~= 0 then - return def.light_source - end - end, - }) - -filter.register_filter({ - name = "metainv", - check_item_by_def = function(self, def) - if def.allow_metadata_inventory_move or - def.allow_metadata_inventory_take or - def.allow_metadata_inventory_put or - def.on_metadata_inventory_move or - def.on_metadata_inventory_take or - def.on_metadata_inventory_put then - return true - end - end, - }) - ---[[ does it sense to filter them? I cannot define the human readable groups for them -filter.register_filter({ - name = "drawtype", - check_item_by_def = function(self, def) - if def.drawtype ~= "normal" then - return def.drawtype - end - end, - }) -]] - -local shaped_groups = {} -local shaped_list = minetest.setting_get("smart_inventory_shaped_groups") or "carpet,door,fence,stair,slab,wall,micro,panel,slope" -if shaped_list then - for z in shaped_list:gmatch("[^,]+") do - shaped_groups[z] = true - end -end - -filter.register_filter({ - name = "shape", - check_item_by_def = function(self, def) - local door_groups - if shaped_groups["door"] then - local door_filter = filter.get("door") - door_groups = door_filter:check_item_by_def(def) - if door_groups and door_groups.door then - return true - end - end - - for k, v in pairs(def.groups) do - if k ~= "door" and shaped_groups[k] then - return true - end - end - end, - }) - ---[[ disabled since debug.getupvalue is not usable to secure environment -filter.register_filter({ - name = "food", - check_item_by_def = function(self, def) - if def.on_use then - local name,change=debug.getupvalue(def.on_use, 1) - if name~=nil and name=="hp_change" and change > 0 then - return tostring(change) - end - end - end, - }) - -filter.register_filter({ - name = "toxic", - check_item_by_def = function(self, def) - if def.on_use then - local name,change=debug.getupvalue(def.on_use, 1) - if name~=nil and name=="hp_change" and change < 0 then - return tostring(change) - end - end - end, - }) -]] - -filter.register_filter({ - name = "tool", - check_item_by_def = function(self, def) - if not def.tool_capabilities then - return - end - local rettab = {} - for k, v in pairs(def.tool_capabilities) do - if type(v) ~= "table" and v ~= 0 then - rettab["tool:"..k] = v - end - end - if def.tool_capabilities.damage_groups then - for k, v in pairs(def.tool_capabilities.damage_groups) do - if v ~= 0 then - rettab["damage:"..k] = v - end - end - end ---[[ disabled, I cannot find right human readable interpretation for this - if def.tool_capabilities.groupcaps then - for groupcap, gdef in pairs(def.tool_capabilities.groupcaps) do - for k, v in pairs(gdef) do - if type(v) ~= "table" then - rettab["groupcaps:"..groupcap..":"..k] = v - end - end - end - end -]] - return rettab - end, - get_keyword = function(self, group) - if group.name == "tool" or group.name == "damage" then - return nil - else - return self:_get_keyword(group) - end - end - }) - -filter.register_filter({ - name = "armor", - check_item_by_def = function(self, def) - return def.armor_groups - end, - }) - -filter.register_filter({ - name = 'clothing_cape', - check_item_by_def = function(self, def) - if def.groups.cape then - return 'clothing' - end - end - }) - - --- Burn times -filter.register_filter({ - name = "fuel", - check_item_by_def = function(self, def) - local burntime = minetest.get_craft_result({method="fuel",width=1,items={def.name}}).time - if burntime > 0 then - return "fuel:"..burntime - end - end -}) - - --- Group assignment done in cache framework internally -filter.register_filter({ - name = "recipetype", - check_item_by_def = function(self, def) end, - get_keyword = function(self, group) - if group.name ~= self.name then - return group.parent.childs[group.name] - end - end -}) - --- Group assignment done in cache framework internally -filter.register_filter({ - name = "ingredient", - check_item_by_def = function(self, def) end, - get_description = function(self, group) - local itemname = group.name:sub(12) - if txt and txt["ingredient"] and - minetest.registered_items[itemname] and minetest.registered_items[itemname].description then - return txt["ingredient"] .." "..minetest.registered_items[itemname].description.." ("..group.name..")" - else - return group.name - end - end, - get_keyword = function(self, group) - -- not searchable by ingedient - return nil - end, - is_valid = function(self, groupname) - local itemname = groupname:sub(12) - if itemname ~= "" and minetest.registered_items[itemname] then - return true - end - end - }) - - -local door_groups -local function fill_door_groups() - door_groups = {} - for _, extend_def in pairs(minetest.registered_items) do - local base_def - if extend_def.groups and extend_def.groups.door then - if extend_def.door then - base_def = minetest.registered_items[extend_def.door.name] - elseif extend_def.drop and type(extend_def.drop) == "string" then - base_def = minetest.registered_items[extend_def.drop] - end - end - if base_def then - door_groups[base_def.name] = extend_def - door_groups[extend_def.name] = false - end - end -end - -filter.register_filter({ - name = "door", - check_item_by_def = function(self, def) - if not door_groups then - fill_door_groups() - end - if not door_groups[def.name] then - return - end - - local group_filter = filter.get("group") - local ret = group_filter:check_item_by_def(door_groups[def.name]) - if ret then - ret["not_in_creative_inventory"] = nil - return ret - end - end - }) - ----------------- -return filter - diff --git a/mods/gui/smart_inventory/libs/maininv.lua b/mods/gui/smart_inventory/libs/maininv.lua deleted file mode 100644 index b8e2fbe..0000000 --- a/mods/gui/smart_inventory/libs/maininv.lua +++ /dev/null @@ -1,160 +0,0 @@ --- Enhanced main inventory methods -local maininvClass = {} -maininvClass_mt = {__index = maininvClass} - --- Clear the inventory -function maininvClass:remove_all() - for idx = 1, self.inventory:get_size("main") do - self.inventory:set_stack("main", idx, "") - end -end - --- Save inventory content to a slot (file) -function maininvClass:save_to_slot(slot) - local savedata = {} - for idx, stack in ipairs(self.inventory:get_list("main")) do - if not stack:is_empty() then - savedata[idx] = stack:to_string() - end - end - - local player = minetest.get_player_by_name(self.playername) - player:set_attribute("inv_save_slot_"..tostring(slot), minetest.serialize(savedata)) -end - --- Get restore the inventory content from a slot (file) -function maininvClass:restore_from_slot(slot) - local player = minetest.get_player_by_name(self.playername) - local savedata = minetest.deserialize(player:get_attribute("inv_save_slot_"..tostring(slot))) - if savedata then - for idx = 1, self.inventory:get_size("main") do - self.inventory:set_stack("main", idx, savedata[idx]) - end - end -end - --- Add a item to inventory -function maininvClass:add_item(item) - return self.inventory:add_item("main", item) -end - -function maininvClass:add_sepearate_stack(item) - for idx, stack in ipairs(self.inventory:get_list("main")) do - if stack:is_empty() then - self.inventory:set_stack("main", idx, item) - item = "" - break - end - end - return item -end - --- Get inventory content as consolidated table -function maininvClass:get_items() - local items_in_inventory = {} - - for _, stack in ipairs(self.inventory:get_list("main")) do - local itemname = stack:get_name() - if itemname and itemname ~= "" then - if not items_in_inventory[itemname] then - items_in_inventory[itemname] = stack:get_count() - else - items_in_inventory[itemname] = items_in_inventory[itemname] + stack:get_count() - end - end - end - --- add items in crafting field to the available items in inventory - for _, stack in ipairs(self.inventory:get_list("craft")) do - local itemname = stack:get_name() - if itemname and itemname ~= "" then - if not items_in_inventory[itemname] then - items_in_inventory[itemname] = stack:get_count() - else - items_in_inventory[itemname] = items_in_inventory[itemname] + stack:get_count() - end - end - end - - return items_in_inventory -end - --- try to get empty stacks by move items to other stacky up to max_size -function maininvClass:compress() - for idx1 = self.inventory:get_size("main"), 1, -1 do - local stack1 = self.inventory:get_stack("main", idx1) - if not stack1:is_empty() then - for idx2 = 1, idx1 do - local stack2 = self.inventory:get_stack("main", idx2) - if idx1 ~= idx2 and stack1:get_name() == stack2:get_name() then - stack1 = stack2:add_item(stack1) - self.inventory:set_stack("main", idx1, stack1) - self.inventory:set_stack("main", idx2, stack2) - if stack1:is_empty() then - break - end - end - end - end - end -end - --- move items to crafting grid to craft item -function maininvClass:craft_item(grid) - for idx_main, stack_main in ipairs(self.inventory:get_list("main")) do - for x, col in pairs(grid) do - for y, item in pairs(col) do - local idx_craft = (y-1)*3+x - local stack_craft = self.inventory:get_stack("craft", idx_craft ) - if not stack_main:is_empty() and stack_main:get_name() == item then --right item - local left = stack_craft:add_item(stack_main:take_item(1)) - stack_main:add_item(left) - self.inventory:set_stack("craft", idx_craft, stack_craft) - self.inventory:set_stack("main", idx_main, stack_main) - end - end - end - end -end - - --- move all items from crafting inventory back to main inventory -function maininvClass:sweep_crafting_inventory() - for idx = 1, self.inventory:get_size("craft") do - local stack = self.inventory:get_stack("craft", idx) - if not stack:is_empty() then - local left = self.inventory:add_item("main", stack) - self.inventory:set_stack("craft", idx, left) - end - end -end - --- Swap row to the top. Asumption the inventory is 8x4, the row number should be 2, 3 or 4 -function maininvClass:swap_row_to_top(row) - local width = 8 - for idx1 = 1, width do - local idx2 = (row -1) * width + idx1 - local stack1 = self.inventory:get_stack("main", idx1) - local stack2 = self.inventory:get_stack("main", idx2) - self.inventory:set_stack("main", idx2, stack1) - self.inventory:set_stack("main", idx1, stack2) - end -end - --- player inventory class -local maininv = {} -function maininv.get(playername) - local self = setmetatable({}, maininvClass_mt) - self.playername = playername - self.inventory = minetest.get_player_by_name(playername):get_inventory() - self.inventory:set_width("craft", 3) - self.inventory:set_size("craft", 9) - return self -end - --- Check if player has creative privilege. -function maininvClass:get_has_creative() - return minetest.is_creative_enabled(self.playername) -end - -return maininv diff --git a/mods/gui/smart_inventory/libs/simple_po_reader.lua b/mods/gui/smart_inventory/libs/simple_po_reader.lua deleted file mode 100644 index b371d5a..0000000 --- a/mods/gui/smart_inventory/libs/simple_po_reader.lua +++ /dev/null @@ -1,41 +0,0 @@ -local txt_usage = minetest.setting_getbool("smart_inventory_friendly_group_names") --or true -if txt_usage == false then - return false -end - -local modpath = minetest.get_modpath(minetest.get_current_modname()).."/locale" - -local LANG = minetest.setting_get("language") -if not (LANG and (LANG ~= "")) then LANG = os.getenv("LANG") end -if not (LANG and (LANG ~= "")) then LANG = "en" end -local pofile = modpath.."/groups_"..LANG:sub(1,2)..".po" - -local f=io.open(pofile,"r") ---fallback to en -if not f then - pofile = modpath.."/groups_en.po" - f=io.open(pofile,"r") -end - -local texttab = {} - -local msgid -local msgstr - -for line in f:lines() do - if line:sub(1,5) == 'msgid' then -- msgid "" - msgid = line:sub(8, line:len()-1) - elseif line:sub(1,6) == 'msgstr' then -- msgstr "" - msgstr = line:sub(9, line:len()-1) - end - if msgid and msgstr then - if msgid ~= "" and msgstr ~= "" then - texttab[msgid] = msgstr - end - msgid = nil - msgstr = nil - end -end - -io.close(f) -return texttab diff --git a/mods/gui/smart_inventory/libs/smartfs-elements.lua b/mods/gui/smart_inventory/libs/smartfs-elements.lua deleted file mode 100644 index 3b8ddce..0000000 --- a/mods/gui/smart_inventory/libs/smartfs-elements.lua +++ /dev/null @@ -1,287 +0,0 @@ -local smartfs = smart_inventory.smartfs -local elements = {} - ------------------------------------------------------ ---- Crafting Preview applet ------------------------------------------------------ --- enhanced / prepared container --- Additional methods --- craft_preview:setCraft(craft) --- craft_preview:onButtonClicked(function(self, itemname, player)) --- if craft=nil, the view will be initialized - -local craft_preview = table.copy(smartfs._edef.container) -function craft_preview:onCreate() - self.data.relative = true - smartfs._edef.container.onCreate(self) - for x = 1, 3 do - for y = 1, 3 do - local button = self._state:image_button( - (x-1)*self.data.zoom+self.data.pos.x, - (y-1)*self.data.zoom+self.data.pos.y, - self.data.zoom, self.data.zoom, - "craft:"..x..":"..y,"") - button:setVisible(false) - button:onClick(function(self, state, player) - local parent_element = state.location.containerElement - if parent_element._button_click then - parent_element._button_click(parent_element, self.data.item, player) - end - end) - end - end - if self.data.recipe then - self:setCraft(self.data.recipe) - end -end - -function craft_preview:onButtonClicked(func) - self._button_click = func -end - --- Update fields -function craft_preview:setCraft(craft) - local width - if craft then -- adjust width to 1 if the recipe contains just 1 item - width = craft.width or 3 - if width == 0 then - width = 3 - end - if craft.items[1] and next(craft.items, 1) == nil then - width = 1 - end - end - for x = 1, 3 do - for y = 1, 3 do - local item = nil - if craft then - if width <= 1 then - if x == 2 then - item = craft.items[y] - end - elseif x <= width then - item = craft.items[(y-1)*width+x] - end - end - local btn = self._state:get("craft:"..x..":"..y) - if item then - if type(item) == "string" then - btn:setItem(item) - btn:setTooltip() - btn:setText("") - else - btn:setItem(item.item) - btn:setTooltip(item.tooltip) - btn:setText(item.text) - end - btn:setVisible(true) - else - btn:setVisible(false) - end - end - end -end - --- get the preview as table -function craft_preview:getCraft() - local grid = {} - for x = 1, 3 do - grid[x] = {} - for y = 1, 3 do - local button = self._state:get("craft:"..x..":"..y) - if button:getVisible() then - grid[x][y] = button:getItem() - end - end - end - return grid -end - -smartfs.element("craft_preview", craft_preview) - -function elements:craft_preview(x, y, name, zoom, recipe) - return self:element("craft_preview", { - pos = {x=x, y=y}, - name = name, - recipe = recipe, - zoom = zoom or 1 - }) -end - - ------------------------------------------------------ ---- Pagable grid buttons ------------------------------------------------------ ---[[ enhanced / prepared container - Additional methods - buttons_grid:setList(craft) - buttons_grid:onClick(function(state, index, player)...end) - buttons_grid:setList(iconlist) - buttons_grid:getFirstVisible() - buttons_grid:setFirstVisible(index) - - iconslist is a list of next entries: - entry = { - image | item = - tooltip= - is_button = true, - size = {w=,h=} - } -]] -local buttons_grid = table.copy(smartfs._edef.container) -function buttons_grid:onCreate() - self.data.relative = true - assert(self.data.size and self.data.size.w and self.data.size.h, "button needs valid size") - smartfs._edef.container.onCreate(self) - if not self.data.cell_size or not self.data.cell_size.w or not self.data.cell_size.h then - self.data.cell_size = {w=1, h=1} - end - self:setSize(self.data.size.w, self.data.size.h) -- view size for background - self.data.grid_size = {w = math.floor(self.data.size.w/self.data.cell_size.w), h = math.floor(self.data.size.h/self.data.cell_size.h)} - self.data.list_start = self.data.list_start or 1 - self.data.list = self.data.list or {} - for x = 1, self.data.grid_size.w do - for y=1, self.data.grid_size.h do - local button = self._state:button( - self.data.pos.x + (x-1)*self.data.cell_size.w, - self.data.pos.y + (y-1)*self.data.cell_size.h, - self.data.cell_size.w, - self.data.cell_size.h, - tostring((y-1)*self.data.grid_size.w+x), - "") - button:onClick(function(self, state, player) - local rel = tonumber(self.name) - local parent_element = state.location.containerElement - local idx = rel - if parent_element.data.list_start > 1 then - idx = parent_element.data.list_start + rel - 2 - end - if rel == 1 and parent_element.data.list_start > 1 then - -- page back - local full_pagesize = parent_element.data.grid_size.w * parent_element.data.grid_size.h - if parent_element.data.list_start <= full_pagesize then - parent_element.data.list_start = 1 - else - --prev page use allways 2x navigation buttons at list_start > 1 and the next page (we navigate from) exists - parent_element.data.list_start = parent_element.data.list_start - (full_pagesize-2) - end - parent_element:update() - elseif rel == (parent_element.data.grid_size.w * parent_element.data.grid_size.h) and - parent_element.data.list[parent_element.data.list_start+parent_element.data.pagesize] then - -- page forward - parent_element.data.list_start = parent_element.data.list_start+parent_element.data.pagesize - parent_element:update() - else - -- pass call to the button function - if parent_element._click then - parent_element:_click(parent_element.root, idx, player) - end - end - end) - button:setVisible(false) - end - end -end - -function buttons_grid:reset(x, y, w, h, col_size, row_size) - self._state = nil - - self.data.pos.x = x or self.data.pos.x - self.data.pos.y = y or self.data.pos.y - self.data.size.w = w or self.data.size.w - self.data.size.h = h or self.data.size.h - self.data.cell_size.w = col_size or self.data.cell_size.w - self.data.cell_size.h = row_size or self.data.cell_size.h - - self:onCreate() - self:update() -end - -function buttons_grid:onClick(func) - self._click = func -end -function buttons_grid:getFirstVisible() - return self.data.list_start -end -function buttons_grid:setFirstVisible(idx) - self.data.list_start = idx -end -function buttons_grid:setList(iconlist) - self.data.list = iconlist or {} - self:update() -end - -function buttons_grid:update() - --init pagesize - self.data.pagesize = self.data.grid_size.w * self.data.grid_size.h - --adjust start position - if self.data.list_start > #self.data.list then - self.data.list_start = #self.data.list - self.data.pagesize - end - if self.data.list_start < 1 then - self.data.list_start = 1 - end - - local itemindex = self.data.list_start - for btnid = 1, self.data.grid_size.w * self.data.grid_size.h do - local button = self._state:get(tostring(btnid)) - if btnid == 1 and self.data.list_start > 1 then - -- setup back button - button:setVisible(true) - button:setImage("smart_inventory_left_arrow.png") - button:setText(tostring(self.data.list_start-1)) - button:setSize(self.data.cell_size.w, self.data.cell_size.h) - self.data.pagesize = self.data.pagesize - 1 - elseif btnid == self.data.grid_size.w * self.data.grid_size.h and self.data.list[itemindex+1] then - -- setup next button - button:setVisible(true) - button:setImage("smart_inventory_right_arrow.png") - self.data.pagesize = self.data.pagesize - 1 - button:setText(tostring(#self.data.list-self.data.list_start-self.data.pagesize+1)) - button:setSize(self.data.cell_size.w, self.data.cell_size.h) - else - -- functional button - local entry = self.data.list[itemindex] - if entry then - if entry.size then - button:setSize(entry.size.w, entry.size.h) - else - button:setSize(self.data.cell_size.w, self.data.cell_size.h) - end - if entry.item and entry.is_button == true then - button:setVisible(true) - button:setItem(entry.item) - button:setText(entry.text or "") - button:setTooltip(nil) - elseif entry.image and entry.is_button == true then - button:setVisible(true) - button:setImage(entry.image) - button:setText(entry.text or "") - button:setTooltip(entry.tooltip) - -- TODO 1: entry.image to display *.png - -- TODO 2: entry.text to display label on button - -- TODO 3,4,5: is_button == false to get just pic or label without button - end - else - button:setVisible(false) - end - itemindex = itemindex + 1 - end - end -end - - -smartfs.element("buttons_grid", buttons_grid) - -function elements:buttons_grid(x, y, w, h, name, col_size, row_size) - return self:element("buttons_grid", { - pos = {x=x, y=y}, - size = {w=w, h=h}, - cell_size = {w=col_size, h=row_size}, - name = name - }) -end - - -------------------------- -return elements diff --git a/mods/gui/smart_inventory/libs/smartfs.lua b/mods/gui/smart_inventory/libs/smartfs.lua deleted file mode 100644 index e5b1dc5..0000000 --- a/mods/gui/smart_inventory/libs/smartfs.lua +++ /dev/null @@ -1,1524 +0,0 @@ ---------------------------- --- SmartFS: Smart Formspecs --- License: CC0 or WTFPL --- by Rubenwardy ---------------------------- - -local smartfs = { - _fdef = {}, - _edef = {}, - _ldef = {}, - opened = {}, - inv = {} -} - -local function boolToStr(v) - return v and "true" or "false" -end - --- the smartfs() function -function smartfs.__call(self, name) - return smartfs.get(name) -end - -function smartfs.get(name) - return smartfs._fdef[name] -end - ------------------------------------------------------- --- Smartfs Interface - Creates a new form and adds elements to it by running the function. Use before Minetest loads. (like minetest.register_node) ------------------------------------------------------- --- Register forms and elements -function smartfs.create(name, onload) - assert(not smartfs._fdef[name], - "SmartFS - (Error) Form "..name.." already exists!") - assert(not smartfs.loaded or smartfs._loaded_override, - "SmartFS - (Error) Forms should be declared while the game loads.") - - smartfs._fdef[name] = { - form_setup_callback = onload, - name = name, - show = smartfs._show_, - attach_to_node = smartfs._attach_to_node_ - } - - return smartfs._fdef[name] -end - ------------------------------------------------------- --- Smartfs Interface - Creates a new element type ------------------------------------------------------- -function smartfs.element(name, data) - assert(not smartfs._edef[name], - "SmartFS - (Error) Element type "..name.." already exists!") - - assert(data.onCreate, "element requires onCreate method") - smartfs._edef[name] = data - return smartfs._edef[name] -end - ------------------------------------------------------- --- Smartfs Interface - Creates a dynamic form. Returns state ------------------------------------------------------- -function smartfs.dynamic(name,player) - if not smartfs._dynamic_warned then - smartfs._dynamic_warned = true - minetest.log("warning", "SmartFS - (Warning) On the fly forms are being used. May cause bad things to happen") - end - local statelocation = smartfs._ldef.player._make_state_location_(player) - local state = smartfs._makeState_({name=name}, nil, statelocation, player) - smartfs.opened[player] = state - return state -end - ------------------------------------------------------- --- Smartfs Interface - Returns the name of an installed and supported inventory mod that will be used above, or nil ------------------------------------------------------- -function smartfs.inventory_mod() - if minetest.global_exists("unified_inventory") then - return "unified_inventory" - elseif minetest.global_exists("inventory_plus") then - return "inventory_plus" - elseif minetest.global_exists("sfinv") then - return "sfinv" - else - return nil - end -end - ------------------------------------------------------- --- Smartfs Interface - Adds a form to an installed advanced inventory. Returns true on success. ------------------------------------------------------- -function smartfs.add_to_inventory(form, icon, title) - local ldef - local invmod = smartfs.inventory_mod() - if invmod then - ldef = smartfs._ldef[invmod] - else - return false - end - return ldef.add_to_inventory(form, icon, title) -end - ------------------------------------------------------- --- Smartfs Interface - Set the form as players inventory ------------------------------------------------------- -function smartfs.set_player_inventory(form) - smartfs._ldef.inventory.set_inventory(form) -end ------------------------------------------------------- --- Smartfs Interface - Allows you to use smartfs.create after the game loads. Not recommended! ------------------------------------------------------- -function smartfs.override_load_checks() - smartfs._loaded_override = true -end - ------------------------------------------------------- --- Smartfs formspec locations ------------------------------------------------------- --- Unified inventory plugin -smartfs._ldef.unified_inventory = { - add_to_inventory = function(form, icon, title) - unified_inventory.register_button(form.name, { - type = "image", - image = icon, - }) - unified_inventory.register_page(form.name, { - get_formspec = function(player, formspec) - local name = player:get_player_name() - local state - if smartfs.inv[name] and smartfs.inv[name].def.name == form.name then - state = smartfs.inv[name] - else - local statelocation = smartfs._ldef.unified_inventory._make_state_location_(name) - state = smartfs._makeState_(form, nil, statelocation, name) - if form.form_setup_callback(state) ~= false then - smartfs.inv[name] = state - else - smartfs.inv[name] = nil - return "" - end - end - return {formspec = state:_buildFormspec_(false)} - end - }) - end, - _make_state_location_ = function(player) - return { - type = "inventory", - player = player, - _show_ = function(state) - unified_inventory.set_inventory_formspec(minetest.get_player_by_name(state.location.player), state.def.name) - end - } - end -} - --- Inventory plus plugin -smartfs._ldef.inventory_plus = { - add_to_inventory = function(form, icon, title) - minetest.register_on_joinplayer(function(player) - inventory_plus.register_button(player, form.name, title) - end) - minetest.register_on_player_receive_fields(function(player, formname, fields) - if formname == "" and fields[form.name] then - local name = player:get_player_name() - local statelocation = smartfs._ldef.inventory_plus._make_state_location_(name) - local state = smartfs._makeState_(form, nil, statelocation, name) - if form.form_setup_callback(state) ~= false then - smartfs.inv[name] = state - state:show() - end - end - end) - end, - _make_state_location_ = function(player) - return { - type = "inventory", - player = player, - _show_ = function(state) - inventory_plus.set_inventory_formspec(minetest.get_player_by_name(state.location.player), state:_buildFormspec_(true)) - end - } - end -} - --- Sfinv plugin -smartfs._ldef.sfinv = { - add_to_inventory = function(form, icon, title) - sfinv.register_page(form.name, { - title = title, - get = function(self, player, context) - local name = player:get_player_name() - local state - if smartfs.inv[name] then - state = smartfs.inv[name] - else - local statelocation = smartfs._ldef.sfinv._make_state_location_(name) - state = smartfs._makeState_(form, nil, statelocation, name) - smartfs.inv[name] = state - if form.form_setup_callback(state) ~= false then - smartfs.inv[name] = state - else - return "" - end - end - local fs = state:_buildFormspec_(false) - return sfinv.make_formspec(player, context, fs, true) - end, - on_player_receive_fields = function(self, player, _, fields) - local name = player:get_player_name() - if smartfs.inv[name] then - smartfs.inv[name]:_sfs_on_receive_fields_(name, fields) - end - end, - on_leave = function(self, player) - local name = player:get_player_name() - if smartfs.inv[name] then - smartfs.inv[name].players:disconnect(name) - smartfs.inv[name] = nil - end - end, - }) - end, - _make_state_location_ = function(player) - return { - type = "inventory", - inventory_handles_fields = true, - player = player, - _show_ = function(state) - sfinv.set_player_inventory_formspec(minetest.get_player_by_name(state.location.player)) - end, - } - end -} - --- Show to player -smartfs._ldef.player = { - _make_state_location_ = function(player) - return { - type = "player", - player = player, - _show_ = function(state) - if not state._show_queued then - state._show_queued = true - minetest.after(0, function(state) - if state then - state._show_queued = nil - if (not state.closed) and (not state.obsolete) then - minetest.show_formspec(state.location.player, state.def.name, state:_buildFormspec_(true)) - end - end - end, state) -- state given as reference. Maybe additional updates are done in the meantime or the form is obsolete - end - end - } - end -} - --- Standalone inventory -smartfs._ldef.inventory = { - set_inventory = function(form) - if sfinv and sfinv.enabled then - sfinv.enabled = nil - end - minetest.register_on_joinplayer(function(player) - local name = player:get_player_name() - local statelocation = smartfs._ldef.inventory._make_state_location_(name) - local state = smartfs._makeState_(form, nil, statelocation, name) - if form.form_setup_callback(state) ~= false then - smartfs.inv[name] = state - state:show() - end - end) - minetest.register_on_leaveplayer(function(player) - local name = player:get_player_name() - smartfs.inv[name].obsolete = true - smartfs.inv[name] = nil - end) - end, - _make_state_location_ = function(name) - return { - type = "inventory", - player = name, - _show_ = function(state) - if not state._show_queued then - state._show_queued = true - minetest.after(0, function(state) - if state then - state._show_queued = nil - local player = minetest.get_player_by_name(state.location.player) - --print("smartfs formspec:", state:_buildFormspec_(true)) - player:set_inventory_formspec(state:_buildFormspec_(true)) - end - end, state) - end - end - } - end -} - --- Node metadata -smartfs._ldef.nodemeta = { - _make_state_location_ = function(nodepos) - return { - type = "nodemeta", - pos = nodepos, - _show_ = function(state) - if not state._show_queued then - state._show_queued = true - minetest.after(0, function(state) - if state then - state._show_queued = nil - local meta = minetest.get_meta(state.location.pos) - meta:set_string("formspec", state:_buildFormspec_(true)) - meta:set_string("smartfs_name", state.def.name) - meta:mark_as_private("smartfs_name") - end - end, state) - end - end, - } - end -} - --- Sub-container (internally used) -smartfs._ldef.container = { - _make_state_location_ = function(element) - local self = { - type = "container", - containerElement = element, - parentState = element.root - } - if self.parentState.location.type == "container" then - self.rootState = self.parentState.location.rootState - else - self.rootState = self.parentState - end - return self - end -} - ------------------------------------------------------- --- Minetest Interface - on_receive_fields callback can be used in minetest.register_node for nodemeta forms ------------------------------------------------------- -function smartfs.nodemeta_on_receive_fields(nodepos, formname, fields, sender, params) - local meta = minetest.get_meta(nodepos) - local nodeform = meta:get_string("smartfs_name") - if not nodeform then - print("SmartFS - (Warning) smartfs.nodemeta_on_receive_fields for node without smarfs data") - return false - end - - -- get the currentsmartfs state - local opened_id = minetest.pos_to_string(nodepos) - local state - local form = smartfs.get(nodeform) - if not smartfs.opened[opened_id] or -- If opened first time - smartfs.opened[opened_id].def.name ~= nodeform or -- Or form is changed - smartfs.opened[opened_id].obsolete then - local statelocation = smartfs._ldef.nodemeta._make_state_location_(nodepos) - state = smartfs._makeState_(form, params, statelocation) - if smartfs.opened[opened_id] then - smartfs.opened[opened_id].obsolete = true - end - smartfs.opened[opened_id] = state - form.form_setup_callback(state) - else - state = smartfs.opened[opened_id] - end - - -- Set current sender check for multiple users on node - local name - if sender then - name = sender:get_player_name() - state.players:connect(name) - end - - -- take the input - state:_sfs_on_receive_fields_(name, fields) - - -- Reset form if all players disconnected - if sender and not state.players:get_first() and not state.obsolete then - local statelocation = smartfs._ldef.nodemeta._make_state_location_(nodepos) - local resetstate = smartfs._makeState_(form, params, statelocation) - if form.form_setup_callback(resetstate) ~= false then - resetstate:show() - end - smartfs.opened[opened_id] = nil - end -end - ------------------------------------------------------- --- Minetest Interface - on_player_receive_fields callback in case of inventory or player ------------------------------------------------------- -minetest.register_on_player_receive_fields(function(player, formname, fields) - local name = player:get_player_name() - if smartfs.opened[name] and smartfs.opened[name].location.type == "player" then - if smartfs.opened[name].def.name == formname then - local state = smartfs.opened[name] - state:_sfs_on_receive_fields_(name, fields) - - -- disconnect player if form closed - if not state.players:get_first() then - smartfs.opened[name].obsolete = true - smartfs.opened[name] = nil - end - end - elseif smartfs.inv[name] and smartfs.inv[name].location.type == "inventory" and not smartfs.inv[name].location.inventory_handles_fields then - local state = smartfs.inv[name] - state:_sfs_on_receive_fields_(name, fields) - end - return false -end) - ------------------------------------------------------- --- Minetest Interface - Notify loading of smartfs is done ------------------------------------------------------- -minetest.after(0, function() - smartfs.loaded = true -end) - ------------------------------------------------------- --- Form Interface [linked to form:show()] - Shows the form to a player ------------------------------------------------------- -function smartfs._show_(form, name, params) - assert(form) - assert(type(name) == "string", "smartfs: name needs to be a string") - assert(minetest.get_player_by_name(name), "player does not exist") - local statelocation = smartfs._ldef.player._make_state_location_(name) - local state = smartfs._makeState_(form, params, statelocation, name) - if form.form_setup_callback(state) ~= false then - if smartfs.opened[name] then -- set maybe previous form to obsolete - smartfs.opened[name].obsolete = true - end - smartfs.opened[name] = state - state:show() - end - return state -end - ------------------------------------------------------- --- Form Interface [linked to form:attach_to_node()] - Attach a formspec to a node meta ------------------------------------------------------- -function smartfs._attach_to_node_(form, nodepos, params) - assert(form) - assert(nodepos and nodepos.x) - - local statelocation = smartfs._ldef.nodemeta._make_state_location_(nodepos) - local state = smartfs._makeState_(form, params, statelocation) - if form.form_setup_callback(state) ~= false then - local opened_id = minetest.pos_to_string(nodepos) - if smartfs.opened[opened_id] then -- set maybe previous form to obsolete - smartfs.opened[opened_id].obsolete = true - end - state:show() - end - return state -end - ------------------------------------------------------- --- Smartfs Framework - Element class Methods ------------------------------------------------------- -local element_class = {} - -function element_class:remove() - self.root._ele[self.name] = nil -end - -function element_class:setPosition(x,y) - self.data.pos = {x=x,y=y} -end - -function element_class:getPosition() - return self.data.pos -end - -function element_class:setSize(w,h) - self.data.size = {w=w,h=h} -end - -function element_class:getSize() - return self.data.size -end - -function element_class:setVisible(visible) - if visible == nil then - self.data.visible = true - else - self.data.visible = visible - end -end - -function element_class:getVisible() - return self.data.visible -end - -function element_class:getAbsName() - return self.root:getNamespace()..self.name -end - -function element_class:setBackground(image) - self.data.background = image -end - -function element_class:getBackground() - return self.data.background -end - -function element_class:getBackgroundString() - if self.data.background then - local size = self:getSize() - if size then - return "background[".. - self.data.pos.x..","..self.data.pos.y..";".. - size.w..","..size.h..";".. - self.data.background.."]" - else - return "" - end - else - return "" - end -end - -function element_class:setValue(value) - self.data.value = value -end - -function element_class:setTooltip(text) - self.data.tooltip = minetest.formspec_escape(text) -end - -function element_class:getTooltip() - return self.data.tooltip -end - -function element_class:getTooltipString() - if self.data.tooltip then - return "tooltip["..self:getAbsName()..";"..self:getTooltip().."]" - else - return "" - end -end - ------------------------------------------------------- --- Smartfs Framework - State class Methods ------------------------------------------------------- -local state_class = {} -function state_class:get(name) - return self._ele[name] -end - -function state_class:close() - self.closed = true -end - -function state_class:getSize() - return self._size -end - -function state_class:size(w,h) - self._size = {w=w,h=h} -end -state_class.setSize = state_class.size - -function state_class:getNamespace() - local ref = self - local namespace = "" - while ref.location.type == "container" do - namespace = ref.location.containerElement.name.."#"..namespace - ref = ref.location.parentState -- step near to the root - end - return namespace -end - -function state_class:_buildFormspec_(size) - local res = "" - if self._size and size then - res = "size["..self._size.w..","..self._size.h.."]" - end - for key,val in pairs(self._ele) do - if val:getVisible() then - res = res .. val:getBackgroundString() .. val:build() .. val:getTooltipString() - end - end - return res -end - -function state_class:_get_element_recursive_(field) - local topfield - for z in field:gmatch("[^#]+") do - topfield = z - break - end - local element = self._ele[topfield] - if element and field == topfield then - return element - elseif element then - if element._getSubElement_ then - local rel_field = string.sub(field, string.len(topfield)+2) - return element:_getSubElement_(rel_field) - else - return element - end - else - return nil - end -end - --- process onInput hook for the state -function state_class:_sfs_process_oninput_(fields, player) - if self._onInput then - self:_onInput(fields, player) - end - -- recursive all onInput hooks on visible containers - for elename, eledef in pairs(self._ele) do - if eledef.getContainerState and eledef:getVisible() then - eledef:getContainerState():_sfs_process_oninput_(fields, player) - end - end -end - --- Receive fields and actions from formspec -function state_class:_sfs_on_receive_fields_(player, fields) - local fields_todo = {} - for field, value in pairs(fields) do - local element = self:_get_element_recursive_(field) - if element then - fields_todo[field] = { element = element, value = value } - end - end - - for field, todo in pairs(fields_todo) do - todo.element:setValue(todo.value) - end - - self:_sfs_process_oninput_(fields, player) - - for field, todo in pairs(fields_todo) do - if todo.element.submit then - todo.element:submit(todo.value, player) - end - end - -- handle key_enter - if fields.key_enter and fields.key_enter_field then - local element = self:_get_element_recursive_(fields.key_enter_field) - if element and element.submit_key_enter then - element:submit_key_enter(fields[fields.key_enter_field], player) - end - end - - if not fields.quit and not self.closed and not self.obsolete then - self:show() - else - self.players:disconnect(player) - if not fields.quit and self.closed and not self.obsolete then - --closed by application (without fields.quit). currently not supported, see: https://github.com/minetest/minetest/pull/4675 - minetest.show_formspec(player,"","size[5,1]label[0,0;Formspec closing not yet created!]") - end - end - return true -end - -function state_class:onInput(func) - self._onInput = func -- (fields, player) -end - -function state_class:load(file) - local file = io.open(file, "r") - if file then - local table = minetest.deserialize(file:read("*all")) - if type(table) == "table" then - if table.size then - self._size = table.size - end - for key,val in pairs(table.ele) do - self:element(val.type,val) - end - return true - end - end - return false -end - -function state_class:save(file) - local res = {ele={}} - - if self._size then - res.size = self._size - end - - for key,val in pairs(self._ele) do - res.ele[key] = val.data - end - - local file = io.open(file, "w") - if file then - file:write(minetest.serialize(res)) - file:close() - return true - end - return false -end - -function state_class:setparam(key,value) - if not key then return end - self.param[key] = value - return true -end - -function state_class:getparam(key,default) - if not key then return end - return self.param[key] or default -end - -function state_class:element(typen,data) - local type = smartfs._edef[typen] - assert(type, "Element type "..typen.." does not exist!") - assert(not self._ele[data.name], "Element "..data.name.." already exists") - - local ele = setmetatable({}, {__index = element_class}) - ele.name = data.name - ele.root = self - ele.data = data - ele.data.type = typen - ele.data.visible = true - for key, val in pairs(type) do - ele[key] = val - end - self._ele[data.name] = ele - type.onCreate(ele) - return self._ele[data.name] -end - ------------------------------------------------------- --- Smartfs Framework - State class Methods - Build time only ------------------------------------------------------- -function state_class:button(x, y, w, h, name, text, exitf) - return self:element("button", { - pos = {x=x,y=y}, - size = {w=w,h=h}, - name = name, - value = text, - closes = exitf or false - }) -end - -function state_class:image_button(x, y, w, h, name, text, image, exitf) - return self:element("button", { - pos = {x=x,y=y}, - size = {w=w,h=h}, - name = name, - value = text, - image = image, - closes = exitf or false - }) -end - -function state_class:item_image_button(x, y, w, h, name, text, item, exitf) - return self:element("button", { - pos = {x=x,y=y}, - size = {w=w,h=h}, - name = name, - value = text, - item = item, - closes = exitf or false - }) -end - -function state_class:label(x, y, name, text) - return self:element("label", { - pos = {x=x,y=y}, - name = name, - value = text, - vertical = false - }) -end - -function state_class:vertlabel(x, y, name, text) - return self:element("label", { - pos = {x=x,y=y}, - name = name, - value = text, - vertical = true - }) -end - -function state_class:toggle(x, y, w, h, name, list) - return self:element("toggle", { - pos = {x=x, y=y}, - size = {w=w, h=h}, - name = name, - id = 1, - list = list - }) -end - -function state_class:field(x, y, w, h, name, label) - return self:element("field", { - pos = {x=x, y=y}, - size = {w=w, h=h}, - name = name, - value = "", - label = label - }) -end - -function state_class:pwdfield(x, y, w, h, name, label) - local res = self:element("field", { - pos = {x=x, y=y}, - size = {w=w, h=h}, - name = name, - value = "", - label = label - }) - res:isPassword(true) - return res -end - -function state_class:textarea(x, y, w, h, name, label) - local res = self:element("field", { - pos = {x=x, y=y}, - size = {w=w, h=h}, - name = name, - value = "", - label = label - }) - res:isMultiline(true) - return res -end - -function state_class:image(x, y, w, h, name, img) - return self:element("image", { - pos = {x=x, y=y}, - size = {w=w, h=h}, - name = name, - value = img, - imgtype = "image" - }) -end - -function state_class:background(x, y, w, h, name, img) - return self:element("image", { - pos = {x=x, y=y}, - size = {w=w, h=h}, - name = name, - background = img, - imgtype = "background" - }) -end - -function state_class:item_image(x, y, w, h, name, img) - return self:element("image", { - pos = {x=x, y=y}, - size = {w=w, h=h}, - name = name, - value = img, - imgtype = "item" - }) -end - -function state_class:checkbox(x, y, name, label, selected) - return self:element("checkbox", { - pos = {x=x, y=y}, - name = name, - value = selected, - label = label - }) -end - -function state_class:listbox(x, y, w, h, name, selected, transparent) - return self:element("list", { - pos = {x=x, y=y}, - size = {w=w, h=h}, - name = name, - selected = selected, - transparent = transparent - }) -end - -function state_class:dropdown(x, y, w, h, name, selected) - return self:element("dropdown", { - pos = {x=x, y=y}, - size = {w=w, h=h}, - name = name, - selected = selected - }) -end - -function state_class:inventory(x, y, w, h, name) - return self:element("inventory", { - pos = {x=x, y=y}, - size = {w=w, h=h}, - name = name - }) -end - -function state_class:container(x, y, name, relative) - return self:element("container", { - pos = {x=x, y=y}, - name = name, - relative = false - }) -end - -function state_class:view(x, y, name, relative) - return self:element("container", { - pos = {x=x, y=y}, - name = name, - relative = true - }) -end - ------------------------------------------------------- --- Smartfs Framework - create a form object (state) ------------------------------------------------------- -function smartfs._makeState_(form, params, location, newplayer) - ------------------------------------------------------ - -- State - -- Object to manage players - ------------------------------------------------------ - local function _make_players_(newplayer) - local self = { - _list = {} - } - function self.connect(self, player) - self._list[player] = true - end - function self.disconnect(self, player) - self._list[player] = nil - end - function self.get_first(self) - return next(self._list) - end - if newplayer then - self:connect(newplayer) - end - return self - end - - local state = { - _ele = {}, - def = form, - players = _make_players_(newplayer), - location = location, - is_inv = (location.type == "inventory"), -- obsolete. Please use location.type="inventory" instead - player = newplayer, -- obsolete. Please use location.player - param = params or {}, - show = location._show_, - } - return setmetatable(state, {__index = state_class}) -end - ------------------------------------------------------------------ -------------------------- ELEMENTS ---------------------------- ------------------------------------------------------------------ - -smartfs.element("button", { - onCreate = function(self) - assert(self.data.pos and self.data.pos.x and self.data.pos.y, "button needs valid pos") - assert(self.data.size and self.data.size.w and self.data.size.h, "button needs valid size") - assert(self.name, "button needs name") - assert(self.data.value, "button needs label") - end, - build = function(self) - local specstring - if self.data.image then - if self.data.closes then - specstring = "image_button_exit[" - else - specstring = "image_button[" - end - elseif self.data.item then - if self.data.closes then - specstring = "item_image_button_exit[" - else - specstring = "item_image_button[" - end - else - if self.data.closes then - specstring = "button_exit[" - else - specstring = "button[" - end - end - - specstring = specstring .. - self.data.pos.x..","..self.data.pos.y..";".. - self.data.size.w..","..self.data.size.h..";" - if self.data.image then - specstring = specstring..minetest.formspec_escape(self.data.image)..";" - elseif self.data.item then - specstring = specstring..self.data.item..";" - end - specstring = specstring..self:getAbsName()..";".. - minetest.formspec_escape(self.data.value).."]" - return specstring - end, - submit = function(self, field, player) - if self._click then - self:_click(self.root, player) - end - end, - onClick = function(self,func) - self._click = func - end, - click = function(self,func) - self._click = func - end, - setText = function(self,text) - self:setValue(text) - end, - getText = function(self) - return self.data.value - end, - setImage = function(self,image) - self.data.image = image - self.data.item = nil - end, - getImage = function(self) - return self.data.image - end, - setItem = function(self,item) - self.data.item = item - self.data.image = nil - end, - getItem = function(self) - return self.data.item - end, - setClose = function(self,bool) - self.data.closes = bool - end, - getClose = function(self) - return self.data.closes or false - end -}) - -smartfs.element("toggle", { - onCreate = function(self) - assert(self.data.pos and self.data.pos.x and self.data.pos.y, "toggle needs valid pos") - assert(self.data.size and self.data.size.w and self.data.size.h, "toggle needs valid size") - assert(self.name, "toggle needs name") - assert(self.data.list, "toggle needs data") - end, - build = function(self) - return "button[".. - self.data.pos.x..","..self.data.pos.y.. - ";".. - self.data.size.w..","..self.data.size.h.. - ";".. - self:getAbsName().. - ";".. - minetest.formspec_escape(self.data.list[self.data.id]).. - "]" - end, - submit = function(self, field, player) - self.data.id = self.data.id + 1 - if self.data.id > #self.data.list then - self.data.id = 1 - end - if self._tog then - self:_tog(self.root, player) - end - end, - onToggle = function(self,func) - self._tog = func - end, - setId = function(self,id) - self.data.id = id - end, - getId = function(self) - return self.data.id - end, - getText = function(self) - return self.data.list[self.data.id] - end -}) - -smartfs.element("label", { - onCreate = function(self) - assert(self.data.pos and self.data.pos.x and self.data.pos.y, "label needs valid pos") - assert(self.data.value, "label needs text") - end, - build = function(self) - local specstring - if self.data.vertical then - specstring = "vertlabel[" - else - specstring = "label[" - end - return specstring.. - self.data.pos.x..","..self.data.pos.y.. - ";".. - minetest.formspec_escape(self.data.value).. - "]" - end, - setText = function(self,text) - self:setValue(text) - end, - getText = function(self) - return self.data.value - end -}) - -smartfs.element("field", { - onCreate = function(self) - assert(self.data.pos and self.data.pos.x and self.data.pos.y, "field needs valid pos") - assert(self.data.size and self.data.size.w and self.data.size.h, "field needs valid size") - assert(self.name, "field needs name") - self.data.value = self.data.value or "" - self.data.label = self.data.label or "" - end, - build = function(self) - if self.data.ml then - return "textarea[".. - self.data.pos.x..","..self.data.pos.y.. - ";".. - self.data.size.w..","..self.data.size.h.. - ";".. - self:getAbsName().. - ";".. - minetest.formspec_escape(self.data.label).. - ";".. - minetest.formspec_escape(self.data.value).. - "]" - elseif self.data.pwd then - return "pwdfield[".. - self.data.pos.x..","..self.data.pos.y.. - ";".. - self.data.size.w..","..self.data.size.h.. - ";".. - self:getAbsName().. - ";".. - minetest.formspec_escape(self.data.label).. - "]".. - self:getCloseOnEnterString() - else - return "field[".. - self.data.pos.x..","..self.data.pos.y.. - ";".. - self.data.size.w..","..self.data.size.h.. - ";".. - self:getAbsName().. - ";".. - minetest.formspec_escape(self.data.label).. - ";".. - minetest.formspec_escape(self.data.value).. - "]".. - self:getCloseOnEnterString() - end - end, - setText = function(self,text) - self:setValue(text) - end, - getText = function(self) - return self.data.value - end, - isPassword = function(self,bool) - self.data.pwd = bool - end, - isMultiline = function(self,bool) - self.data.ml = bool - end, - getCloseOnEnterString = function(self) - if self.close_on_enter == nil then - return "" - else - return "field_close_on_enter["..self:getAbsName()..";"..tostring(self.close_on_enter).."]" - end - end, - setCloseOnEnter = function(self, value) - self.close_on_enter = value - end, - getCloseOnEnter = function(self) - return self.close_on_enter - end, - submit_key_enter = function(self, field, player) - if self._key_enter then - self:_key_enter(self.root, player) - end - end, - onKeyEnter = function(self,func) - self._key_enter = func - end, -}) - -smartfs.element("image", { - onCreate = function(self) - assert(self.data.pos and self.data.pos.x and self.data.pos.y, "image needs valid pos") - assert(self.data.size and self.data.size.w and self.data.size.h, "image needs valid size") - self.data.value = self.data.value or "" - end, - build = function(self) - if self.data.imgtype == "background" then - return "" -- handled in _buildFormspec_ trough getBackgroundString() - elseif self.data.imgtype == "item" then - return "item_image[".. - self.data.pos.x..","..self.data.pos.y.. - ";".. - self.data.size.w..","..self.data.size.h.. - ";".. - minetest.formspec_escape(self.data.value).. - "]" - else - return "image[".. - self.data.pos.x..","..self.data.pos.y.. - ";".. - self.data.size.w..","..self.data.size.h.. - ";".. - minetest.formspec_escape(self.data.value).. - "]" - end - end, - setImage = function(self,text) - if self.data.imgtype == "background" then - self.data.background = text - else - self:setValue(text) - end - end, - getImage = function(self) - if self.data.imgtype == "background" then - return self.data.background - else - return self.data.value - end - end -}) - -smartfs.element("checkbox", { - onCreate = function(self) - assert(self.data.pos and self.data.pos.x and self.data.pos.y, "checkbox needs valid pos") - assert(self.name, "checkbox needs name") - self.data.value = minetest.is_yes(self.data.value) - self.data.label = self.data.label or "" - end, - build = function(self) - return "checkbox[".. - self.data.pos.x..","..self.data.pos.y.. - ";".. - self:getAbsName().. - ";".. - minetest.formspec_escape(self.data.label).. - ";" .. boolToStr(self.data.value) .."]" - end, - submit = function(self, field, player) - -- call the toggle function if defined - if self._tog then - self:_tog(self.root, player) - end - end, - setValue = function(self, value) - self.data.value = minetest.is_yes(value) - end, - getValue = function(self) - return self.data.value - end, - onToggle = function(self,func) - self._tog = func - end, -}) - -smartfs.element("list", { - onCreate = function(self) - assert(self.data.pos and self.data.pos.x and self.data.pos.y, "list needs valid pos") - assert(self.data.size and self.data.size.w and self.data.size.h, "list needs valid size") - assert(self.name, "list needs name") - self.data.value = minetest.is_yes(self.data.value) - self.data.items = self.data.items or {} - end, - build = function(self) - if not self.data.items then - self.data.items = {} - end - return "textlist[".. - self.data.pos.x..","..self.data.pos.y.. - ";".. - self.data.size.w..","..self.data.size.h.. - ";".. - self:getAbsName().. - ";".. - table.concat(self.data.items, ",").. - ";".. - tostring(self.data.selected or "").. - ";".. - tostring(self.data.transparent or "false").."]" - end, - submit = function(self, field, player) - local _type = string.sub(field,1,3) - local index = tonumber(string.sub(field,5)) - self.data.selected = index - if _type == "CHG" and self._click then - self:_click(self.root, index, player) - elseif _type == "DCL" and self._doubleClick then - self:_doubleClick(self.root, index, player) - end - end, - onClick = function(self, func) - self._click = func - end, - click = function(self, func) - self._click = func - end, - onDoubleClick = function(self, func) - self._doubleClick = func - end, - doubleclick = function(self, func) - self._doubleClick = func - end, - addItem = function(self, item) - table.insert(self.data.items, minetest.formspec_escape(item)) - -- return the index of item. It is the last one - return #self.data.items - end, - removeItem = function(self,idx) - table.remove(self.data.items,idx) - end, - getItem = function(self, idx) - return self.data.items[idx] - end, - popItem = function(self) - local item = self.data.items[#self.data.items] - table.remove(self.data.items) - return item - end, - clearItems = function(self) - self.data.items = {} - end, - setSelected = function(self,idx) - self.data.selected = idx - end, - getSelected = function(self) - return self.data.selected - end, - getSelectedItem = function(self) - return self:getItem(self:getSelected()) - end, -}) - -smartfs.element("dropdown", { - onCreate = function(self) - assert(self.data.pos and self.data.pos.x and self.data.pos.y, "dropdown needs valid pos") - assert(self.data.size and self.data.size.w and self.data.size.h, "dropdown needs valid size") - assert(self.name, "dropdown needs name") - self.data.items = self.data.items or {} - self.data.selected = self.data.selected or 1 - self.data.value = "" - end, - build = function(self) - return "dropdown[".. - self.data.pos.x..","..self.data.pos.y.. - ";".. - self.data.size.w..","..self.data.size.h.. - ";".. - self:getAbsName().. - ";".. - table.concat(self.data.items, ",").. - ";".. - tostring(self:getSelected()).. - "]" - end, - submit = function(self, field, player) - self:getSelected() - if self._select then - self:_select(self.root, field, player) - end - end, - onSelect = function(self, func) - self._select = func - end, - addItem = function(self, item) - table.insert(self.data.items, item) - if #self.data.items == self.data.selected then - self.data.value = item - end - -- return the index of item. It is the last one - return #self.data.items - end, - removeItem = function(self,idx) - table.remove(self.data.items,idx) - end, - getItem = function(self, idx) - return self.data.items[idx] - end, - popItem = function(self) - local item = self.data.items[#self.data.items] - table.remove(self.data.items) - return item - end, - clearItems = function(self) - self.data.items = {} - end, - setSelected = function(self,idx) - self.data.selected = idx - self.data.value = self:getItem(idx) or "" - end, - getSelected = function(self) - self.data.selected = 1 - if #self.data.items > 1 then - for i = 1, #self.data.items do - if self.data.items[i] == self.data.value then - self.data.selected = i - end - end - end - return self.data.selected - end, - getSelectedItem = function(self) - return self.data.value - end, -}) - -smartfs.element("inventory", { - onCreate = function(self) - assert(self.data.pos and self.data.pos.x and self.data.pos.y, "list needs valid pos") - assert(self.data.size and self.data.size.w and self.data.size.h, "list needs valid size") - assert(self.name, "list needs name") - end, - build = function(self) - return "list[".. - (self.data.invlocation or "current_player") .. - ";".. - self.name.. --no namespacing - ";".. - self.data.pos.x..","..self.data.pos.y.. - ";".. - self.data.size.w..","..self.data.size.h.. - ";".. - (self.data.index or "") .. - "]" - end, - -- available inventory locations - -- "current_player": Player to whom the menu is shown - -- "player:": Any player - -- "nodemeta:,,": Any node metadata - -- "detached:": A detached inventory - -- "context" does not apply to smartfs, since there is no node-metadata as context available - setLocation = function(self,invlocation) - self.data.invlocation = invlocation - end, - getLocation = function(self) - return self.data.invlocation or "current_player" - end, - usePosition = function(self, pos) - self.data.invlocation = string.format("nodemeta:%d,%d,%d", pos.x, pos.y, pos.z) - end, - usePlayer = function(self, name) - self.data.invlocation = "player:" .. name - end, - useDetached = function(self, name) - self.data.invlocation = "detached:" .. name - end, - setIndex = function(self,index) - self.data.index = index - end, - getIndex = function(self) - return self.data.index - end -}) - -smartfs.element("code", { - onCreate = function(self) - self.data.code = self.data.code or "" - end, - build = function(self) - if self._build then - self:_build() - end - - return self.data.code - end, - submit = function(self, field, player) - if self._sub then - self:_sub(self.root, field, player) - end - end, - onSubmit = function(self,func) - self._sub = func - end, - onBuild = function(self,func) - self._build = func - end, - setCode = function(self,code) - self.data.code = code - end, - getCode = function(self) - return self.data.code - end -}) - -smartfs.element("container", { - onCreate = function(self) - assert(self.data.pos and self.data.pos.x and self.data.pos.y, "container needs valid pos") - assert(self.name, "container needs name") - local statelocation = smartfs._ldef.container._make_state_location_(self) - self._state = smartfs._makeState_(nil, self.root.param, statelocation) - end, - - -- redefinitions. The size is not handled by data.size but by container-state:size - setSize = function(self,w,h) - self:getContainerState():setSize(w,h) - end, - getSize = function(self) - return self:getContainerState():getSize() - end, - - -- element interface methods - build = function(self) - if self.data.relative ~= true then - return "container["..self.data.pos.x..","..self.data.pos.y.."]".. - self:getContainerState():_buildFormspec_(false).. - "container_end[]" - else - return self:getContainerState():_buildFormspec_(false) - end - end, - getContainerState = function(self) - return self._state - end, - _getSubElement_ = function(self, field) - return self:getContainerState():_get_element_recursive_(field) - end, -}) - -return smartfs diff --git a/mods/gui/smart_inventory/locale/groups_de.po b/mods/gui/smart_inventory/locale/groups_de.po deleted file mode 100644 index f7b45f3..0000000 --- a/mods/gui/smart_inventory/locale/groups_de.po +++ /dev/null @@ -1,651 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Language: de\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 1.8.5\n" - -msgid "all" -msgstr "Alles" - -msgid "other" -msgstr "Weitere" - -msgid "antiportal" -msgstr "" - -msgid "armor" -msgstr "Rüstung" - -msgid "armor:feet" -msgstr "Stiefel" - -msgid "armor:fire" -msgstr "Feuerschutz" - -msgid "armor:head" -msgstr "Kopfschutz" - -msgid "armor:heal" -msgstr "Heilen" - -msgid "armor:legs" -msgstr "Beinschutz" - -msgid "armor:shield" -msgstr "Schild" - -msgid "armor:torso" -msgstr "Körperschutz" - -msgid "armor:use" -msgstr "Abnutzung bei Schaden" - -msgid "attached_node" -msgstr "Verbundbar" - -msgid "bag" -msgstr "Tasche" - -msgid "basecolor" -msgstr "" - -msgid "basecolor:black" -msgstr "" - -msgid "basecolor:blue" -msgstr "" - -msgid "basecolor:brown" -msgstr "" - -msgid "basecolor:cyan" -msgstr "" - -msgid "basecolor:green" -msgstr "" - -msgid "basecolor:grey" -msgstr "" - -msgid "basecolor:magenta" -msgstr "" - -msgid "basecolor:orange" -msgstr "" - -msgid "basecolor:red" -msgstr "" - -msgid "basecolor:white" -msgstr "" - -msgid "basecolor:yellow" -msgstr "" - -msgid "bed" -msgstr "Bett" - -msgid "bendy" -msgstr "" - -msgid "book" -msgstr "Buch" - -msgid "bouncy" -msgstr "Federnd" - -msgid "cannon" -msgstr "" - -msgid "cannonstand" -msgstr "" - -msgid "choppy" -msgstr "Abgehackt" - -msgid "coal" -msgstr "Kohle" - -msgid "color" -msgstr "Farbe" - -msgid "color:blue" -msgstr "" - -msgid "color:orange" -msgstr "" - -msgid "color:red" -msgstr "" - -msgid "color:violet" -msgstr "" - -msgid "color:white" -msgstr "" - -msgid "color:yellow" -msgstr "" - -msgid "connect_to_raillike" -msgstr "Schienenartig" - -msgid "cools_lava" -msgstr "" - -msgid "cracky" -msgstr "Knackig" - -msgid "crossbrace_connectable" -msgstr "" - -msgid "crumbly" -msgstr "Brüchig" - -msgid "customnode" -msgstr "Dekorativer Block" - -msgid "damage" -msgstr "Schaden" - -msgid "damage:fleshy" -msgstr "Fleischverletzung" - -msgid "desert" -msgstr "" - -msgid "dig_immediate" -msgstr "Schnell abbaubar" - -msgid "dig_immediate:3" -msgstr "Sofort abbaubar" - -msgid "disable_jump" -msgstr "Klebrig" - -msgid "door" -msgstr "Tür" - -msgid "dry_grass" -msgstr "" - -msgid "dye" -msgstr "Farbstoff" - -msgid "eatable" -msgstr "Essbar" - -msgid "excolor" -msgstr "" - -msgid "excolor:black" -msgstr "" - -msgid "excolor:blue" -msgstr "" - -msgid "excolor:cyan" -msgstr "" - -msgid "excolor:darkgrey" -msgstr "" - -msgid "excolor:green" -msgstr "" - -msgid "excolor:grey" -msgstr "" - -msgid "excolor:orange" -msgstr "" - -msgid "excolor:red" -msgstr "" - -msgid "excolor:red:violet" -msgstr "" - -msgid "excolor:violet" -msgstr "" - -msgid "excolor:white" -msgstr "" - -msgid "excolor:yellow" -msgstr "" - -msgid "fall_damage_add_percent" -msgstr "Aufprallschaden" - -msgid "falling_node" -msgstr "Fallend" - -msgid "false" -msgstr "" - -msgid "fence" -msgstr "Zaun" - -msgid "flammable" -msgstr "Brennbar" - -msgid "fleshy" -msgstr "Fleischig" - -msgid "flora" -msgstr "Flora" - -msgid "flower" -msgstr "Blume" - -msgid "food" -msgstr "Nahrung" - -msgid "food:apple" -msgstr "" - -msgid "food:blueberry" -msgstr "" - -msgid "food:bowl" -msgstr "" - -msgid "food:butter" -msgstr "" - -msgid "food:cactus" -msgstr "" - -msgid "food:carrot" -msgstr "" - -msgid "food:cheese" -msgstr "" - -msgid "food:chicken" -msgstr "" - -msgid "food:choco" -msgstr "" - -msgid "food:choco:powder" -msgstr "" - -msgid "food:cocoa" -msgstr "" - -msgid "food:cup" -msgstr "" - -msgid "food:dark" -msgstr "" - -msgid "food:dark:chocolate" -msgstr "" - -msgid "food:egg" -msgstr "" - -msgid "food:flour" -msgstr "" - -msgid "food:lemon" -msgstr "" - -msgid "food:meat" -msgstr "" - -msgid "food:meat:raw" -msgstr "" - -msgid "food:milk" -msgstr "" - -msgid "food:milk:chocolate" -msgstr "" - -msgid "food:nut" -msgstr "" - -msgid "food:orange" -msgstr "" - -msgid "food:pasta" -msgstr "" - -msgid "food:potato" -msgstr "" - -msgid "food:rhubarb" -msgstr "" - -msgid "food:strawberry" -msgstr "" - -msgid "food:sugar" -msgstr "" - -msgid "food:tomato" -msgstr "" - -msgid "food:walnut" -msgstr "" - -msgid "food:wheat" -msgstr "" - -msgid "fuel" -msgstr "Brennstoff" - -msgid "grass" -msgstr "Gras" - -msgid "grassland" -msgstr "" - -msgid "gunpowder" -msgstr "" - -msgid "hot" -msgstr "" - -msgid "igniter" -msgstr "" - -msgid "ingredient" -msgstr "Erzeugt aus" - -msgid "key" -msgstr "Schlüssel" - -msgid "lava" -msgstr "" - -msgid "leaves" -msgstr "Laub" - -msgid "level" -msgstr "Wertvoll" - -msgid "light" -msgstr "Lichtquelle" - -msgid "liquid" -msgstr "" - -msgid "marble" -msgstr "" - -msgid "meat" -msgstr "" - -msgid "melty" -msgstr "" - -msgid "metainv" -msgstr "Mit Inventar" - -msgid "mod" -msgstr "Mod" - -msgid "not_cuttable" -msgstr "" - -msgid "not_in_creative_inventory" -msgstr "" - -msgid "oddly_breakable_by_hand" -msgstr "Ohne Werkzeug abbaubar" - -msgid "pane" -msgstr "" - -msgid "physics" -msgstr "" - -msgid "physics:gravity" -msgstr "Erdanziehung" - -msgid "physics:speed" -msgstr "Geschwindigkeit" - -msgid "plant" -msgstr "" - -msgid "poison" -msgstr "" - -msgid "potting_soil" -msgstr "" - -msgid "puts_out_fire" -msgstr "" - -msgid "rail" -msgstr "" - -msgid "recipetype" -msgstr "" - -msgid "recipetype:cooking" -msgstr "Gekocht" - -msgid "rock" -msgstr "" - -msgid "sand" -msgstr "Sand" - -msgid "sapling" -msgstr "" - -msgid "seed" -msgstr "Samen" - -msgid "shape" -msgstr "Geformt" - -msgid "slab" -msgstr "Platte" - -msgid "snappy" -msgstr "Schnittig" - -msgid "snowy" -msgstr "" - -msgid "soil" -msgstr "Erde" - -msgid "soil:1" -msgstr "Ackerboden" - -msgid "soil:2" -msgstr "Trockener Ackerboden" - -msgid "soil:3" -msgstr "Nasser Ackerboden" - -msgid "spreading_dirt_type" -msgstr "" - -msgid "stair" -msgstr "Treppe" - -msgid "stick" -msgstr "Stock" - -msgid "stone" -msgstr "Stein" - -msgid "surface_hot" -msgstr "" - -msgid "tar_block" -msgstr "" - -msgid "tool" -msgstr "" - -msgid "tool:full_punch_interval" -msgstr "Verwendungsinterval" - -msgid "tool:max_drop_level" -msgstr "Max drop Level" - -msgid "torch" -msgstr "" - -msgid "translucent" -msgstr "Lichtdurchläßig" - -msgid "tree" -msgstr "Baum" - -msgid "type" -msgstr "" - -msgid "type:craft" -msgstr "Gegenstand" - -msgid "type:node" -msgstr "Block" - -msgid "type:tool" -msgstr "Werkzeug" - -msgid "ud_param2_colorable" -msgstr "Färbbar" - -msgid "unicolor" -msgstr "" - -msgid "unicolor:black" -msgstr "" - -msgid "unicolor:blue" -msgstr "" - -msgid "unicolor:cyan" -msgstr "" - -msgid "unicolor:dark" -msgstr "" - -msgid "unicolor:dark:green" -msgstr "" - -msgid "unicolor:dark:orange" -msgstr "" - -msgid "unicolor:darkgrey" -msgstr "" - -msgid "unicolor:green" -msgstr "" - -msgid "unicolor:grey" -msgstr "" - -msgid "unicolor:light" -msgstr "" - -msgid "unicolor:light:red" -msgstr "" - -msgid "unicolor:orange" -msgstr "" - -msgid "unicolor:red" -msgstr "" - -msgid "unicolor:red:violet" -msgstr "" - -msgid "unicolor:violet" -msgstr "" - -msgid "unicolor:white" -msgstr "" - -msgid "unicolor:yellow" -msgstr "" - -msgid "vessel" -msgstr "Behälter" - -msgid "wall" -msgstr "Mauer" - -msgid "water" -msgstr "Wasser" - -msgid "water_bucket" -msgstr "Eimer" - -msgid "wet" -msgstr "Nass" - -msgid "wood" -msgstr "Holz" - -msgid "wool" -msgstr "Wolle" - -#~ msgid "physics:jump" -#~ msgstr "Sprunghöhe" - -#~ msgid "armor:state" -#~ msgstr "Rüstungsstatus" - -#~ msgid "damage:choppy" -#~ msgstr "Abgehackt" - -#~ msgid "slope" -#~ msgstr "Neigung" - -#~ msgid "leavedecay" -#~ msgstr "Verwelkbar" - -#~ msgid "damage:snappy" -#~ msgstr "Schnittverletzung" - -#~ msgid "micro" -#~ msgstr "Mikro" - -#~ msgid "panel" -#~ msgstr "Paneel" - -#~ msgid "armor:level" -#~ msgstr "Level der Rüstung" - -#~ msgid "carpet" -#~ msgstr "Teppich" - -#~ msgid "explody" -#~ msgstr "Explosiv" - -#~ msgid "armor:water" -#~ msgstr "Wasserschutz" - -#~ msgid "radiation" -#~ msgstr "Schutz gegen Radioaktivität" - -#~ msgid "tool:damage:choppy" -#~ msgstr "Hack-Schaden" - -#~ msgid "transluc" -#~ msgstr "Lichtdurchlässig" - -#~ msgid "tool:damage:snappy" -#~ msgstr "Schnitt-Schaden" - -#~ msgid "tool:damage:fleshy" -#~ msgstr "Wund-Schaden" diff --git a/mods/gui/smart_inventory/locale/groups_en.po b/mods/gui/smart_inventory/locale/groups_en.po deleted file mode 100644 index ccb5e56..0000000 --- a/mods/gui/smart_inventory/locale/groups_en.po +++ /dev/null @@ -1,636 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Language: en\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=iso-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 1.8.5\n" - -msgid "all" -msgstr "All items" - -msgid "other" -msgstr "Other items" - -msgid "antiportal" -msgstr "" - -msgid "armor" -msgstr "Armor" - -msgid "armor:feet" -msgstr "Feet protection" - -msgid "armor:fire" -msgstr "Fire protection" - -msgid "armor:head" -msgstr "Head protection" - -msgid "armor:heal" -msgstr "Heal" - -msgid "armor:legs" -msgstr "Legs protection" - -msgid "armor:shield" -msgstr "Shield" - -msgid "armor:torso" -msgstr "Torso protection" - -msgid "armor:use" -msgstr "Wear on damage" - -msgid "attached_node" -msgstr "Attachable" - -msgid "bag" -msgstr "" - -msgid "basecolor" -msgstr "" - -msgid "basecolor:black" -msgstr "" - -msgid "basecolor:blue" -msgstr "" - -msgid "basecolor:brown" -msgstr "" - -msgid "basecolor:cyan" -msgstr "" - -msgid "basecolor:green" -msgstr "" - -msgid "basecolor:grey" -msgstr "" - -msgid "basecolor:magenta" -msgstr "" - -msgid "basecolor:orange" -msgstr "" - -msgid "basecolor:red" -msgstr "" - -msgid "basecolor:white" -msgstr "" - -msgid "basecolor:yellow" -msgstr "" - -msgid "bed" -msgstr "Bed" - -msgid "bendy" -msgstr "" - -msgid "book" -msgstr "Book" - -msgid "bouncy" -msgstr "Bouncy" - -msgid "cannon" -msgstr "" - -msgid "cannonstand" -msgstr "" - -msgid "choppy" -msgstr "Choppy" - -msgid "coal" -msgstr "Coal" - -msgid "color" -msgstr "Colour" - -msgid "color:blue" -msgstr "" - -msgid "color:orange" -msgstr "" - -msgid "color:red" -msgstr "" - -msgid "color:violet" -msgstr "" - -msgid "color:white" -msgstr "" - -msgid "color:yellow" -msgstr "" - -msgid "connect_to_raillike" -msgstr "Rail-like" - -msgid "cools_lava" -msgstr "" - -msgid "cracky" -msgstr "Cracky" - -msgid "crossbrace_connectable" -msgstr "" - -msgid "crumbly" -msgstr "Crumbly" - -msgid "customnode" -msgstr "Decorative node" - -msgid "damage" -msgstr "Damage" - -msgid "damage:fleshy" -msgstr "Fleshy damage" - -msgid "desert" -msgstr "Desert" - -msgid "dig_immediate" -msgstr "Fast diggable" - -msgid "dig_immediate:3" -msgstr "Immediate diggable" - -msgid "disable_jump" -msgstr "Sticky" - -msgid "door" -msgstr "Door" - -msgid "dry_grass" -msgstr "Dry grass" - -msgid "dye" -msgstr "Dye" - -msgid "eatable" -msgstr "Eatable" - -msgid "excolor" -msgstr "" - -msgid "excolor:black" -msgstr "" - -msgid "excolor:blue" -msgstr "" - -msgid "excolor:cyan" -msgstr "" - -msgid "excolor:darkgrey" -msgstr "" - -msgid "excolor:green" -msgstr "" - -msgid "excolor:grey" -msgstr "" - -msgid "excolor:orange" -msgstr "" - -msgid "excolor:red" -msgstr "" - -msgid "excolor:red:violet" -msgstr "" - -msgid "excolor:violet" -msgstr "" - -msgid "excolor:white" -msgstr "" - -msgid "excolor:yellow" -msgstr "" - -msgid "fall_damage_add_percent" -msgstr "Fall damage" - -msgid "falling_node" -msgstr "Falling" - -msgid "false" -msgstr "" - -msgid "fence" -msgstr "Fence" - -msgid "flammable" -msgstr "Flammable" - -msgid "fleshy" -msgstr "Fleshy" - -msgid "flora" -msgstr "Flora" - -msgid "flower" -msgstr "Flower" - -msgid "food" -msgstr "Food" - -msgid "food:apple" -msgstr "" - -msgid "food:blueberry" -msgstr "" - -msgid "food:bowl" -msgstr "" - -msgid "food:butter" -msgstr "" - -msgid "food:cactus" -msgstr "" - -msgid "food:carrot" -msgstr "" - -msgid "food:cheese" -msgstr "" - -msgid "food:chicken" -msgstr "" - -msgid "food:choco" -msgstr "" - -msgid "food:choco:powder" -msgstr "" - -msgid "food:cocoa" -msgstr "" - -msgid "food:cup" -msgstr "" - -msgid "food:dark" -msgstr "" - -msgid "food:dark:chocolate" -msgstr "" - -msgid "food:egg" -msgstr "" - -msgid "food:flour" -msgstr "" - -msgid "food:lemon" -msgstr "" - -msgid "food:meat" -msgstr "" - -msgid "food:meat:raw" -msgstr "" - -msgid "food:milk" -msgstr "" - -msgid "food:milk:chocolate" -msgstr "" - -msgid "food:nut" -msgstr "" - -msgid "food:orange" -msgstr "" - -msgid "food:pasta" -msgstr "" - -msgid "food:potato" -msgstr "" - -msgid "food:rhubarb" -msgstr "" - -msgid "food:strawberry" -msgstr "" - -msgid "food:sugar" -msgstr "" - -msgid "food:tomato" -msgstr "" - -msgid "food:walnut" -msgstr "" - -msgid "food:wheat" -msgstr "" - -msgid "fuel" -msgstr "Fuel" - -msgid "grass" -msgstr "Grass" - -msgid "grassland" -msgstr "" - -msgid "gunpowder" -msgstr "" - -msgid "hot" -msgstr "Hot" - -msgid "igniter" -msgstr "" - -msgid "ingredient" -msgstr "Crafted with" - -msgid "key" -msgstr "Key" - -msgid "lava" -msgstr "" - -msgid "leaves" -msgstr "Leaves" - -msgid "level" -msgstr "Valuable" - -msgid "light" -msgstr "Light source" - -msgid "liquid" -msgstr "" - -msgid "marble" -msgstr "" - -msgid "meat" -msgstr "" - -msgid "melty" -msgstr "" - -msgid "metainv" -msgstr "With inventory" - -msgid "mod" -msgstr "Mod" - -msgid "not_cuttable" -msgstr "" - -msgid "not_in_creative_inventory" -msgstr "" - -msgid "oddly_breakable_by_hand" -msgstr "Oddly breakable" - -msgid "pane" -msgstr "" - -msgid "physics" -msgstr "" - -msgid "physics:gravity" -msgstr "Gravity" - -msgid "physics:speed" -msgstr "Walking speed" - -msgid "plant" -msgstr "" - -msgid "poison" -msgstr "" - -msgid "potting_soil" -msgstr "" - -msgid "puts_out_fire" -msgstr "" - -msgid "rail" -msgstr "" - -msgid "recipetype" -msgstr "" - -msgid "recipetype:cooking" -msgstr "Cooking result" - -msgid "rock" -msgstr "" - -msgid "sand" -msgstr "Sand" - -msgid "sapling" -msgstr "Sapling" - -msgid "seed" -msgstr "Seed" - -msgid "shape" -msgstr "Shape" - -msgid "slab" -msgstr "Slab" - -msgid "snappy" -msgstr "Snappy" - -msgid "snowy" -msgstr "Snowy" - -msgid "soil" -msgstr "Soil" - -msgid "soil:1" -msgstr "Basic soil" - -msgid "soil:2" -msgstr "Dry farming soil" - -msgid "soil:3" -msgstr "Wet farming" - -msgid "spreading_dirt_type" -msgstr "" - -msgid "stair" -msgstr "Stair" - -msgid "stick" -msgstr "Stick" - -msgid "stone" -msgstr "Stone" - -msgid "surface_hot" -msgstr "" - -msgid "tar_block" -msgstr "" - -msgid "tool" -msgstr "" - -msgid "tool:full_punch_interval" -msgstr "Punch interval" - -msgid "tool:max_drop_level" -msgstr "Max drop level" - -msgid "torch" -msgstr "Torch" - -msgid "translucent" -msgstr "Translucent" - -msgid "tree" -msgstr "Tree" - -msgid "type" -msgstr "" - -msgid "type:craft" -msgstr "Item" - -msgid "type:node" -msgstr "Node" - -msgid "type:tool" -msgstr "Tool" - -msgid "ud_param2_colorable" -msgstr "Colorable by dye punch" - -msgid "unicolor" -msgstr "" - -msgid "unicolor:black" -msgstr "" - -msgid "unicolor:blue" -msgstr "" - -msgid "unicolor:cyan" -msgstr "" - -msgid "unicolor:dark" -msgstr "" - -msgid "unicolor:dark:green" -msgstr "" - -msgid "unicolor:dark:orange" -msgstr "" - -msgid "unicolor:darkgrey" -msgstr "" - -msgid "unicolor:green" -msgstr "" - -msgid "unicolor:grey" -msgstr "" - -msgid "unicolor:light" -msgstr "" - -msgid "unicolor:light:red" -msgstr "" - -msgid "unicolor:orange" -msgstr "" - -msgid "unicolor:red" -msgstr "" - -msgid "unicolor:red:violet" -msgstr "" - -msgid "unicolor:violet" -msgstr "" - -msgid "unicolor:white" -msgstr "" - -msgid "unicolor:yellow" -msgstr "" - -msgid "vessel" -msgstr "Vessel" - -msgid "wall" -msgstr "Wall" - -msgid "water" -msgstr "Water" - -msgid "water_bucket" -msgstr "Bucket" - -msgid "wet" -msgstr "" - -msgid "wood" -msgstr "Wood" - -msgid "wool" -msgstr "Wool" - -#~ msgid "physics:jump" -#~ msgstr "Jump high" - -#~ msgid "armor:state" -#~ msgstr "Armor state" - -#~ msgid "damage:choppy" -#~ msgstr "Choppy damage" - -#~ msgid "slope" -#~ msgstr "Slope" - -#~ msgid "leavedecay" -#~ msgstr "Decayable" - -#~ msgid "damage:snappy" -#~ msgstr "Snappy damage" - -#~ msgid "micro" -#~ msgstr "Micro" - -#~ msgid "panel" -#~ msgstr "Panel" - -#~ msgid "armor:level" -#~ msgstr "Armor level" - -#~ msgid "carpet" -#~ msgstr "Carpet" - -#~ msgid "explody" -#~ msgstr "Explosive" - -#~ msgid "armor:water" -#~ msgstr "Water protection" diff --git a/mods/gui/smart_inventory/locale/groups_template.pot b/mods/gui/smart_inventory/locale/groups_template.pot deleted file mode 100644 index 44ecd18..0000000 --- a/mods/gui/smart_inventory/locale/groups_template.pot +++ /dev/null @@ -1,588 +0,0 @@ -msgid "all" -msgstr "" - -msgid "other" -msgstr "" - -msgid "antiportal" -msgstr "" - -msgid "armor" -msgstr "" - -msgid "armor:feet" -msgstr "" - -msgid "armor:fire" -msgstr "" - -msgid "armor:head" -msgstr "" - -msgid "armor:heal" -msgstr "" - -msgid "armor:legs" -msgstr "" - -msgid "armor:shield" -msgstr "" - -msgid "armor:torso" -msgstr "" - -msgid "armor:use" -msgstr "" - -msgid "attached_node" -msgstr "" - -msgid "bag" -msgstr "" - -msgid "basecolor" -msgstr "" - -msgid "basecolor:black" -msgstr "" - -msgid "basecolor:blue" -msgstr "" - -msgid "basecolor:brown" -msgstr "" - -msgid "basecolor:cyan" -msgstr "" - -msgid "basecolor:green" -msgstr "" - -msgid "basecolor:grey" -msgstr "" - -msgid "basecolor:magenta" -msgstr "" - -msgid "basecolor:orange" -msgstr "" - -msgid "basecolor:red" -msgstr "" - -msgid "basecolor:white" -msgstr "" - -msgid "basecolor:yellow" -msgstr "" - -msgid "bed" -msgstr "" - -msgid "bendy" -msgstr "" - -msgid "book" -msgstr "" - -msgid "bouncy" -msgstr "" - -msgid "cannon" -msgstr "" - -msgid "cannonstand" -msgstr "" - -msgid "choppy" -msgstr "" - -msgid "coal" -msgstr "" - -msgid "color" -msgstr "" - -msgid "color:blue" -msgstr "" - -msgid "color:orange" -msgstr "" - -msgid "color:red" -msgstr "" - -msgid "color:violet" -msgstr "" - -msgid "color:white" -msgstr "" - -msgid "color:yellow" -msgstr "" - -msgid "connect_to_raillike" -msgstr "" - -msgid "cools_lava" -msgstr "" - -msgid "cracky" -msgstr "" - -msgid "crossbrace_connectable" -msgstr "" - -msgid "crumbly" -msgstr "" - -msgid "customnode" -msgstr "" - -msgid "damage" -msgstr "" - -msgid "damage:fleshy" -msgstr "" - -msgid "desert" -msgstr "" - -msgid "dig_immediate" -msgstr "" - -msgid "dig_immediate:3" -msgstr "" - -msgid "disable_jump" -msgstr "" - -msgid "door" -msgstr "" - -msgid "dry_grass" -msgstr "" - -msgid "dye" -msgstr "" - -msgid "eatable" -msgstr "" - -msgid "excolor" -msgstr "" - -msgid "excolor:black" -msgstr "" - -msgid "excolor:blue" -msgstr "" - -msgid "excolor:cyan" -msgstr "" - -msgid "excolor:darkgrey" -msgstr "" - -msgid "excolor:green" -msgstr "" - -msgid "excolor:grey" -msgstr "" - -msgid "excolor:orange" -msgstr "" - -msgid "excolor:red" -msgstr "" - -msgid "excolor:red:violet" -msgstr "" - -msgid "excolor:violet" -msgstr "" - -msgid "excolor:white" -msgstr "" - -msgid "excolor:yellow" -msgstr "" - -msgid "fall_damage_add_percent" -msgstr "" - -msgid "falling_node" -msgstr "" - -msgid "false" -msgstr "" - -msgid "fence" -msgstr "" - -msgid "flammable" -msgstr "" - -msgid "fleshy" -msgstr "" - -msgid "flora" -msgstr "" - -msgid "flower" -msgstr "" - -msgid "food" -msgstr "" - -msgid "food:apple" -msgstr "" - -msgid "food:blueberry" -msgstr "" - -msgid "food:bowl" -msgstr "" - -msgid "food:butter" -msgstr "" - -msgid "food:cactus" -msgstr "" - -msgid "food:carrot" -msgstr "" - -msgid "food:cheese" -msgstr "" - -msgid "food:chicken" -msgstr "" - -msgid "food:choco" -msgstr "" - -msgid "food:choco:powder" -msgstr "" - -msgid "food:cocoa" -msgstr "" - -msgid "food:cup" -msgstr "" - -msgid "food:dark" -msgstr "" - -msgid "food:dark:chocolate" -msgstr "" - -msgid "food:egg" -msgstr "" - -msgid "food:flour" -msgstr "" - -msgid "food:lemon" -msgstr "" - -msgid "food:meat" -msgstr "" - -msgid "food:meat:raw" -msgstr "" - -msgid "food:milk" -msgstr "" - -msgid "food:milk:chocolate" -msgstr "" - -msgid "food:nut" -msgstr "" - -msgid "food:orange" -msgstr "" - -msgid "food:pasta" -msgstr "" - -msgid "food:potato" -msgstr "" - -msgid "food:rhubarb" -msgstr "" - -msgid "food:strawberry" -msgstr "" - -msgid "food:sugar" -msgstr "" - -msgid "food:tomato" -msgstr "" - -msgid "food:walnut" -msgstr "" - -msgid "food:wheat" -msgstr "" - -msgid "fuel" -msgstr "" - -msgid "grass" -msgstr "" - -msgid "grassland" -msgstr "" - -msgid "gunpowder" -msgstr "" - -msgid "hot" -msgstr "" - -msgid "igniter" -msgstr "" - -msgid "ingredient" -msgstr "" - -msgid "key" -msgstr "" - -msgid "lava" -msgstr "" - -msgid "leaves" -msgstr "" - -msgid "level" -msgstr "" - -msgid "light" -msgstr "" - -msgid "liquid" -msgstr "" - -msgid "marble" -msgstr "" - -msgid "meat" -msgstr "" - -msgid "melty" -msgstr "" - -msgid "metainv" -msgstr "" - -msgid "mod" -msgstr "" - -msgid "not_cuttable" -msgstr "" - -msgid "not_in_creative_inventory" -msgstr "" - -msgid "oddly_breakable_by_hand" -msgstr "" - -msgid "pane" -msgstr "" - -msgid "physics" -msgstr "" - -msgid "physics:gravity" -msgstr "" - -msgid "physics:speed" -msgstr "" - -msgid "plant" -msgstr "" - -msgid "poison" -msgstr "" - -msgid "potting_soil" -msgstr "" - -msgid "puts_out_fire" -msgstr "" - -msgid "rail" -msgstr "" - -msgid "recipetype" -msgstr "" - -msgid "recipetype:cooking" -msgstr "" - -msgid "rock" -msgstr "" - -msgid "sand" -msgstr "" - -msgid "sapling" -msgstr "" - -msgid "seed" -msgstr "" - -msgid "shape" -msgstr "" - -msgid "slab" -msgstr "" - -msgid "snappy" -msgstr "" - -msgid "snowy" -msgstr "" - -msgid "soil" -msgstr "" - -msgid "soil:1" -msgstr "" - -msgid "soil:2" -msgstr "" - -msgid "soil:3" -msgstr "" - -msgid "spreading_dirt_type" -msgstr "" - -msgid "stair" -msgstr "" - -msgid "stick" -msgstr "" - -msgid "stone" -msgstr "" - -msgid "surface_hot" -msgstr "" - -msgid "tar_block" -msgstr "" - -msgid "tool" -msgstr "" - -msgid "tool:full_punch_interval" -msgstr "" - -msgid "tool:max_drop_level" -msgstr "" - -msgid "torch" -msgstr "" - -msgid "translucent" -msgstr "" - -msgid "tree" -msgstr "" - -msgid "type" -msgstr "" - -msgid "type:craft" -msgstr "" - -msgid "type:node" -msgstr "" - -msgid "type:tool" -msgstr "" - -msgid "ud_param2_colorable" -msgstr "" - -msgid "unicolor" -msgstr "" - -msgid "unicolor:black" -msgstr "" - -msgid "unicolor:blue" -msgstr "" - -msgid "unicolor:cyan" -msgstr "" - -msgid "unicolor:dark" -msgstr "" - -msgid "unicolor:dark:green" -msgstr "" - -msgid "unicolor:dark:orange" -msgstr "" - -msgid "unicolor:darkgrey" -msgstr "" - -msgid "unicolor:green" -msgstr "" - -msgid "unicolor:grey" -msgstr "" - -msgid "unicolor:light" -msgstr "" - -msgid "unicolor:light:red" -msgstr "" - -msgid "unicolor:orange" -msgstr "" - -msgid "unicolor:red" -msgstr "" - -msgid "unicolor:red:violet" -msgstr "" - -msgid "unicolor:violet" -msgstr "" - -msgid "unicolor:white" -msgstr "" - -msgid "unicolor:yellow" -msgstr "" - -msgid "vessel" -msgstr "" - -msgid "wall" -msgstr "" - -msgid "water" -msgstr "" - -msgid "water_bucket" -msgstr "" - -msgid "wet" -msgstr "" - -msgid "wood" -msgstr "" - -msgid "wool" -msgstr "" - diff --git a/mods/gui/smart_inventory/mod.conf b/mods/gui/smart_inventory/mod.conf deleted file mode 100644 index fd9e50f..0000000 --- a/mods/gui/smart_inventory/mod.conf +++ /dev/null @@ -1,4 +0,0 @@ -name = smart_inventory -title = Smart Inventory -author = bell07 -license = CC0 diff --git a/mods/gui/smart_inventory/pages/awards.lua b/mods/gui/smart_inventory/pages/awards.lua deleted file mode 100644 index 7f208dd..0000000 --- a/mods/gui/smart_inventory/pages/awards.lua +++ /dev/null @@ -1,33 +0,0 @@ -if not minetest.get_modpath("awards") then - return -end - -local function awards_callback(state) - local codebox = state:element("code", { name = "code", code = "", playername = state.location.rootState.location.player }) - codebox:onBuild(function(self) - local formspec = awards.getFormspec(self.data.playername, self.data.playername, self.data.awards_idx or 1) - - -- patch elememt sizes and positions - formspec = formspec:gsub('textarea%[0.25,3.75;3.9,4.2', 'textarea[-0.75,3.75;5.9,4.2') - formspec = formspec:gsub('box%[%-0.05,3.75;3.9,4.2', 'box[-1.05,3.75;5.9,4.2') - formspec = formspec:gsub('textlist%[4,0;3.8,8.6', 'textlist[6,0;6.8,8.6') - self.data.code = "container[3,0]".. formspec .."container_end[]" - end) - - state:onInput(function(state, fields, player) - if fields.awards then - local event = minetest.explode_textlist_event(fields.awards) - if event.type == "CHG" then - state:get("code").data.awards_idx = event.index - end - end - end) -end - -smart_inventory.register_page({ - name = "awards", - icon = "awards_ui_icon.png", - tooltip = "Awards", - smartfs_callback = awards_callback, - sequence = 25, -}) diff --git a/mods/gui/smart_inventory/pages/crafting.lua b/mods/gui/smart_inventory/pages/crafting.lua deleted file mode 100644 index 8ac5ccb..0000000 --- a/mods/gui/smart_inventory/pages/crafting.lua +++ /dev/null @@ -1,760 +0,0 @@ -local cache = smart_inventory.cache -local crecipes = smart_inventory.crecipes -local doc_addon = smart_inventory.doc_addon -local ui_tools = smart_inventory.ui_tools - ------------------------------------------------------ --- Update recipe preview item informations about the selected item ------------------------------------------------------ -local function update_crafting_preview(state) - local player = state.location.rootState.location.player - local listentry = state.param.crafting_recipes_preview_listentry - local selected = state.param.crafting_recipes_preview_selected - local itemdef = listentry.itemdef - local inf_state = state:get("inf_area"):getContainerState() - local cr_type_img = state:get("cr_type_img") - local craft_result = inf_state:get("craft_result") - local group_list = inf_state:get("item_groups") - - -- get recipe to display, check paging buttons needed - local all_recipes - local valid_recipes = {} - local recipe - local revealed_items_cache = {} - - if listentry.recipes then -- preselected recipes (ie. craftable) - all_recipes = listentry.recipes - elseif cache.citems[listentry.item] then -- check all available recipes (ie. search) - all_recipes = cache.citems[listentry.item].in_output_recipe or {} - else -- no recipes - all_recipes = {} - end - - for _, recipe in ipairs(all_recipes) do - if crecipes.crecipes[recipe]:is_revealed(player, revealed_items_cache) then - table.insert(valid_recipes, recipe) - end - end - - if valid_recipes[1] then - if not valid_recipes[selected] then - selected = 1 - end - state.param.crafting_recipes_preview_selected = selected - if selected > 1 and valid_recipes[selected-1] then - state:get("preview_prev"):setVisible(true) - else - state:get("preview_prev"):setVisible(false) - end - if valid_recipes[selected+1] then - state:get("preview_next"):setVisible(true) - else - state:get("preview_next"):setVisible(false) - end - - if valid_recipes[selected] then - recipe = valid_recipes[selected] - local crecipe = crecipes.crecipes[recipe] - if crecipe then - recipe = crecipe:get_with_placeholder(player, state.param.crafting_items_in_inventory) - end - end - else - state:get("preview_prev"):setVisible(false) - state:get("preview_next"):setVisible(false) - end - - -- display the recipe result or selected item - if recipe then - if recipe.type == "normal" then - state:get("cr_type"):setText("") - cr_type_img:setVisible(false) - state:get("ac1"):setVisible(true) - elseif recipe.type == "cooking" then - state:get("cr_type"):setText(recipe.type) - state:get("cr_type"):setText("") - cr_type_img:setVisible(true) - cr_type_img:setImage("smart_inventory_furnace.png") - state:get("ac1"):setVisible(false) - else - state:get("cr_type"):setText(recipe.type) - cr_type_img:setVisible(false) - state:get("ac1"):setVisible(false) - end - craft_result:setImage(recipe.output) - craft_result:setVisible() - state:get("craft_preview"):setCraft(recipe) - else - state:get("cr_type"):setText("") - state:get("craft_preview"):setCraft(nil) - cr_type_img:setVisible(false) - state:get("ac1"):setVisible(false) - if itemdef then - craft_result:setVisible(true) - craft_result:setImage(itemdef.name) - else - craft_result:setVisible(false) - end - end - - -- display docs icon if revealed item - if smart_inventory.doc_items_mod then - inf_state:get("doc_btn"):setVisible(false) - local outitem = craft_result:getImage() - if outitem then - for z in outitem:gmatch("[^%s]+") do - if doc_addon.is_revealed_item(z, player) then - inf_state:get("doc_btn"):setVisible(true) - end - break - end - end - end - - -- update info area - if itemdef then - inf_state:get("info1"):setText(itemdef.description) - inf_state:get("info2"):setText("("..itemdef.name..")") - if itemdef._doc_items_longdesc then - inf_state:get("info3"):setText(itemdef._doc_items_longdesc) - else - inf_state:get("info3"):setText("") - end - - group_list:clearItems() - cache.add_item(listentry.itemdef) -- Note: this addition does not affect the already prepared root lists - if cache.citems[itemdef.name] then - for _, groupdef in ipairs(ui_tools.get_tight_groups(cache.citems[itemdef.name].cgroups)) do - group_list:addItem(groupdef.group_desc) - end - end - elseif listentry.item then - inf_state:get("info1"):setText("") - inf_state:get("info2"):setText("("..listentry.item..")") - inf_state:get("info3"):setText("") - else - inf_state:get("info1"):setText("") - inf_state:get("info2"):setText("") - inf_state:get("info3"):setText("") - group_list:clearItems() - end -end - ------------------------------------------------------ --- Update the group selection table ------------------------------------------------------ -local function update_group_selection(state, rebuild) - local grouped = state.param.crafting_grouped_items - local groups_sel = state:get("groups_sel") - local grid = state:get("buttons_grid") - local label = state:get("inf_area"):getContainerState():get("groups_label") - - if rebuild then - state.param.crafting_group_list = ui_tools.update_group_selection(grouped, groups_sel, state.param.crafting_group_list) - end - - local sel_id = groups_sel:getSelected() - if state.param.crafting_group_list[sel_id] then - state.param.crafting_craftable_list = grouped[state.param.crafting_group_list[sel_id]].items - table.sort(state.param.crafting_craftable_list, function(a,b) - return a.sort_value < b.sort_value - end) - grid:setList(state.param.crafting_craftable_list) - label:setText(groups_sel:getSelectedItem()) - else - label:setText("Empty List") - grid:setList({}) - end -end - ------------------------------------------------------ --- Update the items list ------------------------------------------------------ -local function update_from_recipelist(state, recipelist, preview_item, replace_not_in_list) - local old_preview_entry, old_preview_item, new_preview_entry, new_preview_item - if state.param.crafting_recipes_preview_listentry then - old_preview_item = state.param.crafting_recipes_preview_listentry.item - end - if preview_item == "" then - new_preview_item = nil - else - new_preview_item = preview_item - end - - local duplicate_index_tmp = {} - local craftable_itemlist = {} - - for recipe, _ in pairs(recipelist) do - local def = crecipes.crecipes[recipe].out_item - local itemname = def.name - if duplicate_index_tmp[itemname] then - table.insert(duplicate_index_tmp[itemname].recipes, recipe) - else - local entry = {} - for k,v in pairs(cache.citems[itemname].ui_item) do - entry[k] = v - end - - entry.recipes = {} - duplicate_index_tmp[itemname] = entry - table.insert(entry.recipes, recipe) - table.insert(craftable_itemlist, entry) - if new_preview_item and itemname == new_preview_item then - new_preview_entry = entry - end - if old_preview_item and itemname == old_preview_item then - old_preview_entry = entry - end - end - end - - -- update crafting preview if the old is not in list anymore - if new_preview_item then - if not replace_not_in_list or not old_preview_entry then - if not new_preview_entry then - new_preview_entry = { - itemdef = minetest.registered_items[new_preview_item], - item = new_preview_item - } - end - state.param.crafting_recipes_preview_selected = 1 - state.param.crafting_recipes_preview_listentry = new_preview_entry - update_crafting_preview(state) - if state:get("info_tog"):getId() == 1 then - state:get("info_tog"):submit() - end - end - elseif replace_not_in_list and not old_preview_entry then - state.param.crafting_recipes_preview_selected = 1 - state.param.crafting_recipes_preview_listentry = {} - update_crafting_preview(state) - end - - -- update the groups selection - state.param.crafting_grouped_items = ui_tools.get_list_grouped(craftable_itemlist) - update_group_selection(state, true) -end - ------------------------------------------------------ --- Build list matching the placed grid ------------------------------------------------------ -local function update_from_grid(state, craft_grid, lookup_item) - -- get all grid items for reference - local player = state.location.rootState.location.player - local reference_items = {} - local items_hash = "" - for _, stack in ipairs(craft_grid) do - local name = stack:get_name() - if name and name ~= "" then - reference_items[name] = true - items_hash=items_hash.."|"..name - else - items_hash=items_hash.."|empty" - end - end - - if items_hash ~= state.param.survival_grid_items_hash then - state.param.survival_grid_items_hash = items_hash - if next(reference_items) then - -- update the grid with matched recipe items - local recipes = crecipes.get_recipes_started_craft(player, craft_grid, reference_items) - update_from_recipelist(state, recipes, lookup_item, true) -- replace_not_in_list=true - end - end -end - ------------------------------------------------------ --- Lookup for item lookup_item ------------------------------------------------------ -local function do_lookup_item(state, playername, lookup_item) - state.param.crafting_items_in_inventory = state.param.invobj:get_items() - state.param.crafting_items_in_inventory[lookup_item] = true -- prefer in recipe preview - -- get all craftable recipes with lookup-item as ingredient. Add recipes of lookup item to the list - local recipes = crecipes.get_revealed_recipes_with_items(playername, {[lookup_item] = true }) - update_from_recipelist(state, recipes, lookup_item) - state.param.crafting_ui_controller:update_list_variant("lookup", lookup_item) -end - ------------------------------------------------------ --- Lookup inventory ------------------------------------------------------ -local function create_lookup_inv(name) - local player = minetest.get_player_by_name(name) - local invname = name.."_crafting_inv" - local plistname = "crafting_inv_lookup" - local listname = "lookup" - local pinv = player:get_inventory() - local inv = minetest.get_inventory({type="detached", name=invname}) - if not inv then - inv = minetest.create_detached_inventory(invname, { - allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) - return 0 - end, - allow_put = function(inv, listname, index, stack, player) - if pinv:is_empty(plistname) then - return 99 - else - return 0 - end - end, - allow_take = function(inv, listname, index, stack, player) - return 99 - end, - on_put = function(inv, listname, index, stack, player) - pinv:set_stack(plistname, index, stack) - local name = player:get_player_name() - local state = smart_inventory.get_page_state("crafting", name) - do_lookup_item(state, name, stack:get_name()) - - -- we are outsite of usual smartfs processing. So trigger the formspec update byself - state.location.rootState:show() - - -- put back - minetest.after(1, function() - -- Check maybe player is away from the game - local player = minetest.get_player_by_name(name) - if not player then - return - end - - -- Check the player did not removed item from lookup field - local pinv = player:get_inventory() - local inv = minetest.get_inventory({type="detached", name=invname}) - local stack = pinv:get_stack(plistname, 1) - - -- put back - local applied = pinv:add_item("main", stack) - pinv:set_stack(plistname, 1, applied) - inv:set_stack(listname, 1, applied) - end) - end, - on_take = function(inv, listname, index, stack, player) - pinv:set_stack(plistname, index, nil) - end, - }, name) - end - -- copy the item from player:listname inventory to the detached - inv:set_size(listname, 1) - pinv:set_size(plistname, 1) - local stack = pinv:get_stack(plistname, 1) - inv:set_stack(listname, 1, stack) -end - ------------------------------------------------------ --- Page layout definition ------------------------------------------------------ -local function crafting_callback(state) - local player = state.location.rootState.location.player - - -- build up UI controller - local ui_controller = {} - ui_controller.state = state - ui_controller.player = minetest.get_player_by_name(state.location.rootState.location.player) - state.param.crafting_ui_controller = ui_controller - - function ui_controller:set_ui_variant(new_ui) - -- check if change needed - if new_ui == self.toggle1 or new_ui == self.toggle2 then - return - end - - -- toggle show/hide elements - if new_ui == 'list_small' then - self.toggle1 = new_ui - self.state:get("craft_img2"):setVisible(true) --rahmen oben - self.state:get("lookup_icon"):setPosition(10, 4) - self.state:get("lookup"):setPosition(10, 4) - self.state:get("craftable"):setPosition(11, 4) - self.state:get("btn_all"):setPosition(11, 4.5) - self.state:get("btn_grid"):setPosition(11.5, 4.0) - if smart_inventory.doc_items_mod then - self.state:get("reveal_tipp"):setPosition(11.5, 4.5) - end - self.state:get("search"):setPosition(12.2, 4.5) - self.state:get("search_bg"):setPosition(12, 4) - self.state:get("search_btn"):setPosition(15.2, 4.2) - self.state:get("info_tog"):setPosition(16.2, 4.2) - - self.state:get("buttons_grid_Bg"):setPosition(10, 5) - self.state:get("buttons_grid_Bg"):setSize(8, 4) - self.state:get("buttons_grid"):reset(10.25, 5.15, 8, 4) - - elseif new_ui == 'list_big' then - self.toggle1 = new_ui - self.state:get("craft_img2"):setVisible(false) --rahmen oben - self.state:get("lookup_icon"):setPosition(10, 0) - self.state:get("lookup"):setPosition(10, 0) - self.state:get("craftable"):setPosition(11, 0) - self.state:get("btn_all"):setPosition(11, 0.5) - self.state:get("btn_grid"):setPosition(11.5, 0.0) - if smart_inventory.doc_items_mod then - self.state:get("reveal_tipp"):setPosition(11.5, 0.5) - end - self.state:get("search"):setPosition(12.2, 0.5) - self.state:get("search_bg"):setPosition(12, 0) - self.state:get("search_btn"):setPosition(15.2, 0.2) - self.state:get("info_tog"):setPosition(16.2, 0.2) - - self.state:get("groups_sel"):setVisible(false) - self.state:get("inf_area"):setVisible(false) - - self.state:get("buttons_grid_Bg"):setPosition(10, 1) - self.state:get("buttons_grid_Bg"):setSize(8, 8) - self.state:get("buttons_grid"):reset(10.25, 1.15, 8, 8) - self.state:get("info_tog"):setId(3) - else - self.toggle2 = new_ui - end - - if self.toggle1 == 'list_small' then - if self.toggle2 == 'info' then - self.state:get("groups_sel"):setVisible(false) - self.state:get("inf_area"):setVisible(true) - self.state:get("info_tog"):setId(1) - elseif self.toggle2 == 'groups' then - self.state:get("groups_sel"):setVisible(true) - self.state:get("inf_area"):setVisible(false) - self.state:get("info_tog"):setId(2) - end - end - self:save() - end - - function ui_controller:update_list_variant(list_variant, add_info) - self.add_info = add_info - -- reset group selection and search field on proposal mode change - if self.list_variant ~= list_variant then - self.list_variant = list_variant - self.state:get("groups_sel"):setSelected(1) - if list_variant ~= "search" then - self.state:get("search"):setText("") - end - end - - -- auto-switch to the groups - if list_variant == "lookup" or list_variant == "reveal_tipp" then - state.param.crafting_ui_controller:set_ui_variant("info") - else - state.param.crafting_ui_controller:set_ui_variant("groups") - end - - - state:get("lookup_icon"):setBackground() - state:get("search_bg"):setBackground() - state:get("craftable"):setBackground() - state:get("btn_grid"):setBackground() - state:get("btn_all"):setBackground() - if smart_inventory.doc_items_mod then - state:get("reveal_tipp"):setBackground() - end - -- highlight the right button - if list_variant == "lookup" then - state:get("lookup_icon"):setBackground("halo.png") - elseif list_variant == "search" then - state:get("search_bg"):setBackground("halo.png") - elseif list_variant == "craftable" then - state:get("craftable"):setBackground("halo.png") - elseif list_variant == "grid" then - state:get("btn_grid"):setBackground("halo.png") - elseif list_variant == "btn_all" then - state:get("btn_all"):setBackground("halo.png") - elseif list_variant == "reveal_tipp" then - state:get("reveal_tipp"):setBackground("halo.png") - end - self:save() - end - - function ui_controller:save() - local savedata = minetest.deserialize(self.player:get_attribute("smart_inventory_settings")) or {} - savedata.survival_list_variant = self.list_variant - savedata.survival_toggle1 = self.toggle1 - savedata.survival_toggle2 = self.toggle2 - savedata.survival_lookup_item = self.lookup_item - savedata.survival_add_info = self.add_info - self.player:set_attribute("smart_inventory_settings", minetest.serialize(savedata)) - end - - function ui_controller:restore() - local savedata = minetest.deserialize(self.player:get_attribute("smart_inventory_settings")) or {} - - if savedata.survival_toggle1 then - self:set_ui_variant(savedata.survival_toggle1) - end - if savedata.survival_toggle2 then - self:set_ui_variant(savedata.survival_toggle2) - end - if savedata.survival_list_variant then - if savedata.survival_list_variant == "search" then - local ui_text = self.state:get(savedata.survival_list_variant) - ui_text:setText(savedata.survival_add_info) - ui_text:submit_key_enter("unused", self.state.location.rootState.location.player) - elseif savedata.survival_list_variant == "lookup" then - do_lookup_item(self.state, self.state.location.rootState.location.player, savedata.survival_add_info) - else - local ui_button = self.state:get(savedata.survival_list_variant) - if ui_button then - ui_button:submit("unused", self.state.location.rootState.location.player) - end - end - else - self.state:get("craftable"):submit("unused", self.state.location.rootState.location.player) - self:set_ui_variant("groups") - self:update_list_variant("craftable") - end - end - - -- set inventory style - state:element("code", {name = "additional_code", code = - "listcolors[#00000069;#5A5A5A;#141318;#30434C;#FFF]".. - "listring[current_player;main]listring[current_player;craft]" - }) - - --Inventorys / left site - state:inventory(1, 5, 8, 4,"main") - state:inventory(1.2, 0.2, 3, 3,"craft") - state:inventory(4.3, 1.2, 1, 1,"craftpreview") - state:background(1, 0, 4.5, 3.5, "img1", "menu_bg.png") - - - -- crafting helper buttons - local btn_ac1 = state:image_button(4.4, 0.3, 0.8, 0.8, "ac1", "", "smart_inventory_preview_to_crafting_field.png") - btn_ac1:onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "crafting", "ac1") - local grid = state:get("craft_preview"):getCraft() - state.param.invobj:craft_item(grid) - end) - btn_ac1:setVisible(false) - - -- swap slots buttons - state:image_button(0, 6, 1, 1, "swap1", "", "smart_inventory_swapline_button.png"):onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "crafting", "swap1") - state.param.invobj:swap_row_to_top(2) - end) - state:image_button(0, 7, 1, 1, "swap2", "", "smart_inventory_swapline_button.png"):onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "crafting", "swap2") - state.param.invobj:swap_row_to_top(3) - end) - state:image_button(0, 8, 1, 1, "swap3", "", "smart_inventory_swapline_button.png"):onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "crafting", "swap3") - state.param.invobj:swap_row_to_top(4) - end) - - ui_tools.create_trash_inv(state, player) - state:image(8,9,1,1,"trash_icon","smart_inventory_trash.png") - state:inventory(8, 9, 1, 1, "trash"):useDetached(player.."_trash_inv") - - local btn_compress = state:image_button(1, 3.8, 1, 1, "compress", "","smart_inventory_compress_button.png") - btn_compress:setTooltip("Merge stacks with same items to get free place") - btn_compress:onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "crafting", "compress") - state.param.invobj:compress() - end) - - local btn_sweep = state:image_button(2, 3.8, 1, 1, "clear", "", "smart_inventory_sweep_button.png") - btn_sweep:setTooltip("Move all items from crafting grid back to inventory") - btn_sweep:onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "crafting", "clear") - state.param.invobj:sweep_crafting_inventory() - end) - - -- recipe preview area - smart_inventory.smartfs_elements.craft_preview(state, 6, 0, "craft_preview"):onButtonClicked(function(self, item, player) - do_lookup_item(state, player, item) - end) - state:image(7,2.8,1,1,"cr_type_img",""):setVisible(false) - state:label(7,3,"cr_type", "") - local pr_prev_btn = state:button(6, 3, 1, 0.5, "preview_prev", "<<") - pr_prev_btn:onClick(function(self, state, player) - state.param.crafting_recipes_preview_selected = state.param.crafting_recipes_preview_selected -1 - update_crafting_preview(state) - end) - pr_prev_btn:setVisible(false) - local pr_next_btn = state:button(8, 3, 1, 0.5, "preview_next", ">>") - pr_next_btn:onClick(function(self, state, player) - state.param.crafting_recipes_preview_selected = state.param.crafting_recipes_preview_selected +1 - update_crafting_preview(state) - end) - pr_next_btn:setVisible(false) - - -- (dynamic-1) group selection - local group_sel = state:listbox(10.2, 0.15, 7.6, 3.6, "groups_sel",nil, true) - group_sel:onClick(function(self, state, player) - local selected = self:getSelectedItem() - if selected then - update_group_selection(state, false) - end - end) - - -- (dynamic-2) item preview area - state:background(10.0, 0.1, 8, 3.8, "craft_img2", "minimap_overlay_square.png") - local inf_area = state:view(6.4, 0.1, "inf_area") - local inf_state = inf_area:getContainerState() - inf_state:label(11.5,0.5,"info1", "") - inf_state:label(11.5,1.0,"info2", "") - inf_state:label(11.5,1.5,"info3", "") - inf_state:item_image(10.2,0.3, 1, 1, "craft_result",nil):setVisible(false) - if smart_inventory.doc_items_mod then - local doc_btn = inf_state:image_button(10.4,2.3, 0.7, 0.7, "doc_btn","", "doc_button_icon_lores.png") - doc_btn:setTooltip("Show documentation for revealed item") - doc_btn:setVisible(false) - doc_btn:onClick(function(self, state, player) - local outitem = state:get("craft_result"):getImage() - if outitem then - for z in outitem:gmatch("[^%s]+") do - if minetest.registered_items[z] then - doc_addon.show(z, player) - end - break - end - end - end) - end - inf_state:label(10.3, 3.25, "groups_label", "All") - - inf_state:listbox(12, 2, 5.7, 1.3, "item_groups",nil, true) - inf_area:setVisible(false) - - -- Lookup - create_lookup_inv(player) - state:image(10, 4, 1, 1,"lookup_icon", "smart_inventory_lookup_field.png") - local inv_lookup = state:inventory(10, 4.0, 1, 1,"lookup"):useDetached(player.."_crafting_inv") - - - -- Get craftable by items in inventory - local btn_craftable = state:image_button(11, 4, 0.5, 0.5, "craftable", "", "smart_inventory_craftable_button.png") - btn_craftable:setTooltip("Show items crafteable by items in inventory") - btn_craftable:onClick(function(self, state, player) - state.param.crafting_items_in_inventory = state.param.invobj:get_items() - local craftable = crecipes.get_recipes_craftable(player, state.param.crafting_items_in_inventory) - update_from_recipelist(state, craftable) - ui_controller:update_list_variant("craftable") - end) - - local grid_btn = state:image_button(11.5, 4, 0.5, 0.5, "btn_grid", "", "smart_inventory_craftable_button.png") - grid_btn:setTooltip("Search for recipes matching the grid") - grid_btn:onClick(function(self, state, player) - local player = state.location.rootState.location.player - state.param.crafting_ui_controller:update_list_variant("grid") - local craft_grid = state.param.invobj.inventory:get_list("craft") - local ret_item = state.param.invobj.inventory:get_list("craftpreview")[1] - update_from_grid(state, craft_grid, ret_item:get_name()) - end) - - -- Get craftable by items in inventory - local btn_all = state:image_button(11, 4.5, 0.5, 0.5, "btn_all", "", "smart_inventory_creative_button.png") - if smart_inventory.doc_items_mod then - btn_all:setTooltip("Show all already revealed items") - else - btn_all:setTooltip("Show all items") - end - btn_all:onClick(function(self, state, player) - local all_revealed = ui_tools.filter_by_revealed(ui_tools.root_list_all, player, true) - state.param.crafting_recipes_preview_selected = 1 - state.param.crafting_recipes_preview_listentry = all_revealed[1] or {} - update_crafting_preview(state) - state.param.crafting_grouped_items = ui_tools.get_list_grouped(all_revealed) - - update_group_selection(state, true) - ui_controller:update_list_variant("btn_all") - end) - - -- Reveal tipps button - if smart_inventory.doc_items_mod then - local reveal_button = state:image_button(11.5, 4.5, 0.5, 0.5, "reveal_tipp", "", "smart_inventory_reveal_tips_button.png") - reveal_button:setTooltip("Show proposal what should be crafted to reveal more items") - reveal_button:onClick(function(self, state, player) - local all_revealed = ui_tools.filter_by_revealed(ui_tools.root_list_all, player) - local top_revealed = ui_tools.filter_by_top_reveal(all_revealed, player) - state.param.crafting_recipes_preview_selected = 1 - state.param.crafting_recipes_preview_listentry = top_revealed[1] or {} - update_crafting_preview(state) - state.param.crafting_grouped_items = ui_tools.get_list_grouped(top_revealed) - - update_group_selection(state, true) - ui_controller:update_list_variant("reveal_tipp") - end) - end - - -- search - state:background(12, 4, 4, 0.9, "search_bg", nil) --field background not usable - local searchfield = state:field(12.2, 4.5, 3.4, 0.5, "search") - searchfield:setCloseOnEnter(false) - searchfield:onKeyEnter(function(self, state, player) - local search_string = self:getText() - if string.len(search_string) < 3 then - return - end - - local filtered_list = ui_tools.filter_by_searchstring(ui_tools.root_list_all, search_string, state.location.rootState.lang_code) - filtered_list = ui_tools.filter_by_revealed(filtered_list, player) - state.param.crafting_grouped_items = ui_tools.get_list_grouped(filtered_list) - update_group_selection(state, true) - ui_controller:update_list_variant("search", search_string) - end) - - local search_button = state:button(15.2, 4.2, 0.8, 0.5, "search_btn", "Go") - search_button:setTooltip("Perform search action") - search_button:onClick(function(self, state, player) - state:get("search"):submit_key_enter("", player) - end) - - -- groups toggle - local info_tog = state:toggle(16.2,4.2,1.8,0.5, "info_tog", {"Info", "Groups", "Hide"}) - info_tog:onToggle(function(self, state, player) - local id = self:getId() - if id == 1 then - state.param.crafting_ui_controller:set_ui_variant("list_small") - state.param.crafting_ui_controller:set_ui_variant("info") - elseif id == 2 then - state.param.crafting_ui_controller:set_ui_variant("list_small") - state.param.crafting_ui_controller:set_ui_variant("groups") - elseif id == 3 then - state.param.crafting_ui_controller:set_ui_variant("list_big") - end - end) - - -- craftable items grid - state:background(10, 5, 8, 4, "buttons_grid_Bg", "minimap_overlay_square.png") - local grid = smart_inventory.smartfs_elements.buttons_grid(state, 10.25, 5.15, 8 , 4, "buttons_grid", 0.75,0.75) - grid:onClick(function(self, state, index, player) - local listentry = state.param.crafting_craftable_list[index] - if ui_controller.list_variant == "lookup" then - do_lookup_item(state, state.location.rootState.location.player, listentry.item) - else - state.param.crafting_recipes_preview_selected = 1 - state.param.crafting_recipes_preview_listentry = listentry - update_crafting_preview(state) - end - state.param.crafting_ui_controller:set_ui_variant("info") - end) - - ui_controller:restore() -end - ------------------------------------------------------ --- Register page in smart_inventory ------------------------------------------------------ -smart_inventory.register_page({ - name = "crafting", - tooltip = "Craft new items", - icon = "smart_inventory_crafting_inventory_button.png", - smartfs_callback = crafting_callback, - sequence = 10 -}) - ------------------------------------------------------ --- Use lookup for predict item ------------------------------------------------------ -minetest.register_craft_predict(function(stack, player, old_craft_grid, craft_inv) - local name = player:get_player_name() - local state = smart_inventory.get_page_state("crafting", name) - if not state then - return - end - - if state.param.crafting_ui_controller.list_variant ~= 'grid' then - return - end - update_from_grid(state, old_craft_grid, stack:get_name()) - state.location.rootState:show() -end) diff --git a/mods/gui/smart_inventory/pages/creative.lua b/mods/gui/smart_inventory/pages/creative.lua deleted file mode 100644 index a29d9ff..0000000 --- a/mods/gui/smart_inventory/pages/creative.lua +++ /dev/null @@ -1,310 +0,0 @@ -local cache = smart_inventory.cache -local ui_tools = smart_inventory.ui_tools - ------------------------------------------------------ --- Update on group selection change ------------------------------------------------------ -local function update_group_selection(state, changed_group) - local grouped = state.param.creative_grouped_items - local groups_sel1 = state:get("groups_sel1") - local groups_sel2 = state:get("groups_sel2") - local groups_sel3 = state:get("groups_sel3") - local grid = state:get("buttons_grid") - local outlist - - if state.param.creative_grouped_shape_items and - next(state.param.creative_grouped_shape_items) then - local group_info = {} - group_info.name = "shape" - group_info.cgroup = cache.cgroups["shape"] - group_info.group_desc = "#01DF74> "..group_info.cgroup.group_desc - group_info.items = state.param.creative_grouped_shape_items - grouped["shape"] = group_info - end - - -- update group 1 - if changed_group < 1 or not state.param.creative_group_list1 then - state.param.creative_group_list1 = ui_tools.update_group_selection(grouped, groups_sel1, state.param.creative_group_list1) - end - - local sel_id = groups_sel1:getSelected() - if state.param.creative_group_list1[sel_id] == "all" - or not state.param.creative_group_list1[sel_id] - or not grouped[state.param.creative_group_list1[sel_id]] then - outlist = grouped["all"].items - groups_sel2:clearItems() - groups_sel3:clearItems() - else - -- update group 2 - grouped = ui_tools.get_list_grouped(grouped[state.param.creative_group_list1[sel_id]].items) - if changed_group < 2 or not state.param.creative_group_list2 then - state.param.creative_group_list2 = ui_tools.update_group_selection(grouped, groups_sel2, state.param.creative_group_list2) - end - - sel_id = groups_sel2:getSelected() - if state.param.creative_group_list2[sel_id] == "all" - or not state.param.creative_group_list2[sel_id] - or not grouped[state.param.creative_group_list2[sel_id]] then - outlist = grouped["all"].items - groups_sel3:clearItems() - else - -- update group 3 - grouped = ui_tools.get_list_grouped(grouped[state.param.creative_group_list2[sel_id]].items) - if changed_group < 3 or not state.param.creative_group_list3 then - state.param.creative_group_list3 = ui_tools.update_group_selection(grouped, groups_sel3, state.param.creative_group_list3) - end - sel_id = groups_sel3:getSelected() - outlist = grouped[state.param.creative_group_list3[sel_id]].items - end - end - - -- update grid list - if outlist then - table.sort(outlist, function(a,b) - return a.sort_value < b.sort_value - end) - grid:setList(outlist) - state.param.creative_outlist = outlist - else - grid:setList({}) - end -end - ------------------------------------------------------ --- Page layout definition ------------------------------------------------------ -local function creative_callback(state) - - local player = state.location.rootState.location.player - - -- build up UI controller - local ui_controller = {} - ui_controller.state = state - state.param.creative_ui_controller = ui_controller - ui_controller.player = minetest.get_player_by_name(state.location.rootState.location.player) - - function ui_controller:set_ui_variant(new_ui) - -- check if change needed - if new_ui == self.ui_toggle then - return - end - - -- toggle show/hide elements - if new_ui == 'list_small' then - self.ui_toggle = new_ui - self.state:get("groups_sel1"):setSize(5.6, 3) - self.state:get("groups_sel2"):setVisible(true) - self.state:get("groups_sel3"):setVisible(true) - self.state:get("buttons_grid"):reset(9.55, 3.75, 9.0 , 6.5) - self.state:get("buttons_grid_bg"):setPosition(9.2, 3.5) - self.state:get("buttons_grid_bg"):setSize(9.5, 6.5) - self.state:get("btn_tog"):setId(1) - elseif new_ui == 'list_big' then - self.ui_toggle = new_ui - self.state:get("groups_sel1"):setSize(7.8, 3) - self.state:get("groups_sel2"):setVisible(false) - self.state:get("groups_sel3"):setVisible(false) - self.state:get("buttons_grid"):reset(9.55, 0.25, 9.0 , 10) - self.state:get("buttons_grid_bg"):setPosition(9.2, 0) - self.state:get("buttons_grid_bg"):setSize(9.5, 10) - self.state:get("btn_tog"):setId(2) - end - self:save() - end - function ui_controller:save() - local savedata = minetest.deserialize(self.player:get_attribute("smart_inventory_settings")) or {} - savedata.creative_ui_toggle = self.ui_toggle - self.player:set_attribute("smart_inventory_settings", minetest.serialize(savedata)) - end - - function ui_controller:restore() - local savedata = minetest.deserialize(self.player:get_attribute("smart_inventory_settings")) or {} - - if savedata.creative_ui_toggle then - ui_controller:set_ui_variant(savedata.creative_ui_toggle) - end - end - - -- groups 1-3 - local group_sel1 = state:listbox(1, 0.15, 5.6, 3, "groups_sel1",nil, false) - group_sel1:onClick(function(self, state, player) - local selected = self:getSelectedItem() - if selected then - state:get("groups_sel2"):setSelected(1) - state:get("groups_sel3"):setSelected(1) - update_group_selection(state, 1) - end - end) - - local group_sel2 = state:listbox(7, 0.15, 5.6, 3, "groups_sel2",nil, false) - group_sel2:onClick(function(self, state, player) - local selected = self:getSelectedItem() - if selected then - state:get("groups_sel3"):setSelected(1) - update_group_selection(state, 2) - end - end) - - local group_sel3 = state:listbox(13, 0.15, 5.6, 3, "groups_sel3",nil, false) - group_sel3:onClick(function(self, state, player) - local selected = self:getSelectedItem() - if selected then - update_group_selection(state, 3) - end - end) - - -- functions - local searchfield = state:field(1.3, 4.1, 4.2, 0.5, "search") - searchfield:setCloseOnEnter(false) - searchfield:onKeyEnter(function(self, state, player) - local search_string = self:getText() - local lang_code = state.location.rootState.lang_code - local filtered_list = ui_tools.filter_by_searchstring(ui_tools.root_list, search_string, lang_code) - state.param.creative_grouped_items = ui_tools.get_list_grouped(filtered_list) - filtered_list = ui_tools.filter_by_searchstring(ui_tools.root_list_shape, search_string, lang_code) - state.param.creative_grouped_shape_items = filtered_list - update_group_selection(state, 0) - end) - - local search_button = state:button(5.0, 3.8, 1, 0.5, "search_btn", "Go") - search_button:setTooltip("Perform search action") - search_button:onClick(function(self, state, player) - state:get("search"):submit_key_enter("", player) - end) - - - -- action mode toggle - state:toggle(6, 3.8,1.5,0.5, "btn_tog_mode", {"Give 1", "Give stack"}) - - -- groups toggle - local btn_toggle = state:toggle(7.5, 3.8,1.5,0.5, "btn_tog", {"Groups", "Hide"}) - btn_toggle:onToggle(function(self, state, player) - local id = self:getId() - if id == 1 then - state.param.creative_ui_controller:set_ui_variant("list_small") - elseif id == 2 then - state.param.creative_ui_controller:set_ui_variant("list_big") - end - end) - - -- craftable items grid - state:background(9.2, 3.5, 9.5, 6.5, "buttons_grid_bg", "minimap_overlay_square.png") - local grid = smart_inventory.smartfs_elements.buttons_grid(state, 9.55, 3.75, 9.0 , 6.5, "buttons_grid", 0.75,0.75) - grid:onClick(function(self, state, index, player) - local mode = state:get("btn_tog_mode"):getId() or 1 - local selected = ItemStack(state.param.creative_outlist[index].item) - if mode == 1 then -- give 1 item - state.param.invobj:add_item(selected) - elseif mode == 2 then --give full stack - selected:set_count(selected:get_stack_max()) - state.param.invobj:add_sepearate_stack(selected) - end - end) - - -- inventory - state:inventory(1, 5, 8, 4,"main") - ui_tools.create_trash_inv(state, player) - state:image(8,9,1,1,"trash_icon","smart_inventory_trash.png") - state:element("code", {name = "trash_bg_code", code = "listcolors[#00000069;#5A5A5A;#141318;#30434C;#FFF]"}) - state:inventory(8,9,1,1, "trash"):useDetached(player.."_trash_inv") - - -- swap slots buttons - state:image_button(0, 6, 1, 1, "swap1", "", "smart_inventory_swapline_button.png"):onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "creative", "swap1") - state.param.invobj:swap_row_to_top(2) - end) - state:image_button(0, 7, 1, 1, "swap2", "", "smart_inventory_swapline_button.png"):onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "creative", "swap2") - state.param.invobj:swap_row_to_top(3) - end) - state:image_button(0, 8, 1, 1, "swap3", "", "smart_inventory_swapline_button.png"):onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "creative", "swap3") - state.param.invobj:swap_row_to_top(4) - end) - - -- trash button - local trash_all = state:image_button(7,9,1,1, "trash_all", "", "smart_inventory_trash_all_button.png") - trash_all:setTooltip("Trash all inventory content") - trash_all:onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "creative", "trash_all") - state.param.invobj:remove_all() - end) - - -- save/restore buttons - local btn_save1 = state:image_button(1,9,1,1, "save1", "", "smart_inventory_save1_button.png") - btn_save1:setTooltip("Save inventory content to slot 1") - btn_save1:onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "creative", "save1") - state.param.invobj:save_to_slot(1) - end) - local btn_save2 = state:image_button(1.9,9,1,1, "save2", "", "smart_inventory_save2_button.png") - btn_save2:setTooltip("Save inventory content to slot 2") - btn_save2:onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "creative", "save2") - state.param.invobj:save_to_slot(2) - end) - local btn_save3 = state:image_button(2.8,9,1,1, "save3", "", "smart_inventory_save3_button.png") - btn_save3:setTooltip("Save inventory content to slot 3") - btn_save3:onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "creative", "save3") - state.param.invobj:save_to_slot(3) - end) - local btn_restore1 = state:image_button(4,9,1,1, "restore1", "", "smart_inventory_get1_button.png") - btn_restore1:setTooltip("Restore inventory content from slot 1") - btn_restore1:onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "creative", "restore1") - state.param.invobj:restore_from_slot(1) - end) - local btn_restore2 = state:image_button(4.9,9,1,1, "restore2", "", "smart_inventory_get2_button.png") - btn_restore2:setTooltip("Restore inventory content from slot 2") - btn_restore2:onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "creative", "restore2") - state.param.invobj:restore_from_slot(2) - end) - local btn_restore3 = state:image_button(5.8,9,1,1, "restore3", "", "smart_inventory_get3_button.png") - btn_restore3:setTooltip("Restore inventory content from slot 3") - btn_restore3:onClick(function(self, state, player) - ui_tools.image_button_feedback(player, "creative", "restore3") - state.param.invobj:restore_from_slot(3) - end) - - -- fill with data - state.param.creative_grouped_items = ui_tools.get_list_grouped(ui_tools.root_list) - state.param.creative_grouped_shape_items = ui_tools.root_list_shape - update_group_selection(state, 0) - ui_controller:restore() -end - -local function player_has_creative(state) - return state.param.invobj:get_has_creative() -end - ------------------------------------------------------ --- Register page in smart_inventory ------------------------------------------------------ -smart_inventory.register_page({ - name = "creative", - tooltip = "The creative way to get items", - icon = "smart_inventory_creative_button.png", - smartfs_callback = creative_callback, - is_visible_func = player_has_creative, - sequence = 15 -}) - --- Redefinition for sfinv method maybe called from other mods -if minetest.global_exists("sfinv") then - function sfinv.set_player_inventory_formspec(player, context) - local playername = player:get_player_name() - - local page_state = smart_inventory.get_page_state("creative", playername) - if page_state then - local state = page_state.location.parentState - local has_creative = player_has_creative(state) - state:get("creative_button"):setVisible(has_creative) - if not has_creative then - state:get("crafting_button"):submit(nil, playername) - end - state:show() - end - end -end diff --git a/mods/gui/smart_inventory/pages/doc.lua b/mods/gui/smart_inventory/pages/doc.lua deleted file mode 100644 index 2c1c588..0000000 --- a/mods/gui/smart_inventory/pages/doc.lua +++ /dev/null @@ -1,138 +0,0 @@ -local doc_addon = smart_inventory.doc_addon - -if not smart_inventory.doc_items_mod then - return -end - -local COLOR_NOT_VIEWED = "#00FFFF" -- cyan -local COLOR_VIEWED = "#FFFFFF" -- white -local COLOR_HIDDEN = "#999999" -- gray -local COLOR_ERROR = "#FF0000" -- red - -local function update_entry_list(state, selected_eid) - local playername = state.location.rootState.location.player - local category_id, category - - category_id = state:get("category"):getSelected() - if category_id then - category = doc_addon.get_category_list()[category_id] - end - if not category then - return - end - - local total = doc.get_entry_count(category.cid) - local revealed = doc.get_revealed_count(playername, category.cid) - local viewed = doc.get_viewed_count(playername, category.cid) - local hidden = total - revealed - local new = total - viewed - hidden - - state:get("lbl_category"):setText(category.data.def.description) - state:get("lbl_viewed"):setText(minetest.colorize(COLOR_VIEWED,"Viewed: "..viewed.."/"..revealed)) - state:get("lbl_hidden"):setText(minetest.colorize(COLOR_HIDDEN,"Hidden: "..hidden.."/"..total)) - state:get("lbl_new"):setText(minetest.colorize(COLOR_NOT_VIEWED,"New: "..new)) - local entries_box = state:get("entries") - entries_box:clearItems() - state.param.doc_entry_list = {} - for _, edata in ipairs(category.entries) do - local viewedprefix - if doc.entry_revealed(playername, category.cid, edata.eid) then - if doc.entry_viewed(playername, category.cid, edata.eid) then - viewedprefix = COLOR_VIEWED - else - viewedprefix = COLOR_NOT_VIEWED - end - - local name = edata.data.name - if name == nil or name == "" then - name = edata.eid - end - - local idx = entries_box:addItem(viewedprefix..name) - table.insert(state.param.doc_entry_list, edata) - if selected_eid == edata.eid then - entries_box:setSelected(idx) - end - end - end -end - -local function doc_callback(state) - local playername = state.location.rootState.location.player - - state:label(0, 0, "lbl_category", "") - state:background(0,0,20,0.5,"cat_bg", "halo.png") --- state:label(0, 0.5, "lbl_revealed", "") - state:label(0, 0.5, "lbl_viewed", "") - state:label(3, 0.5, "lbl_new", "") - state:label(0, 1, "lbl_hidden", "") - - local category_box = state:listbox(0, 1.5, 4.8, 2, "category", nil, false) - category_box:onClick(function(self, state, player) - update_entry_list(state) - end) - - state:listbox(0, 4, 4.8, 6, "entries", nil, false):onDoubleClick(function(self, state, player) - local playername = state.location.rootState.location.player - local selected = self:getSelected() - local doc_entry = state.param.doc_entry_list[selected] - if doc_entry then - doc.mark_entry_as_viewed(playername, doc_entry.cid, doc_entry.eid) - state:get("code").edata = doc_entry - doc.data.players[playername].galidx = 1 - update_entry_list(state) - end - end) - - local codebox = state:element("code", { name = "code", code = "" }) - codebox:onBuild(function(self) - if self.edata then - local state = self.root - local playername = state.location.rootState.location.player - self.data.code = "container[5.5,0]".. self.edata.cid_data.def.build_formspec(self.edata.data.data, playername).."container_end[]" - else - self.data.code = "" - end - end) - - -- to trigger the page update - codebox:onSubmit(function(self, state) - -- select the right category - for idx, category in ipairs(doc_addon.get_category_list()) do - if category.cid == self.edata.cid then - state:get("category"):setSelected(idx) - break - end - end - - -- update page for new category - update_entry_list(state, self.edata.eid) - end) - - state:onInput(function(state, fields, player) - if fields.doc_button_gallery_prev then - if doc.data.players[playername].galidx - doc.data.players[playername].galrows > 0 then - doc.data.players[playername].galidx = doc.data.players[playername].galidx - doc.data.players[playername].galrows - end - - elseif fields.doc_button_gallery_next then - if doc.data.players[playername].galidx + doc.data.players[playername].galrows <= doc.data.players[playername].maxgalidx then - doc.data.players[playername].galidx = doc.data.players[playername].galidx + doc.data.players[playername].galrows - end - end - end) - - -- fill category table - for _, category in ipairs(doc_addon.get_category_list()) do - category_box:addItem(category.data.def.name) - end -end - -smart_inventory.register_page({ - name = "doc", - icon = "doc_awards_icon_generic.png", - tooltip = "Ingame documentation", - smartfs_callback = doc_callback, - sequence = 30, - on_button_click = update_entry_list, -}) diff --git a/mods/gui/smart_inventory/pages/player.lua b/mods/gui/smart_inventory/pages/player.lua deleted file mode 100644 index bacdf29..0000000 --- a/mods/gui/smart_inventory/pages/player.lua +++ /dev/null @@ -1,445 +0,0 @@ -smart_inventory.skins_mod = minetest.get_modpath("skinsdb") -smart_inventory.armor_mod = minetest.get_modpath("3d_armor") -smart_inventory.clothing_mod = minetest.get_modpath("clothing") - -if not smart_inventory.skins_mod and - not smart_inventory.armor_mod and - not smart_inventory.clothing_mod then - return -end - -local filter = smart_inventory.filter -local cache = smart_inventory.cache -local ui_tools = smart_inventory.ui_tools -local txt = smart_inventory.txt - -local function update_grid(state, listname) - local player_has_creative = state.param.invobj:get_has_creative() --- Update the users inventory grid - local list = {} - state.param["player_"..listname.."_list"] = list - local name = state.location.rootState.location.player - local player = minetest.get_player_by_name(name) - local invlist_tab = {} - if listname == "main" then - local inventory = player:get_inventory() - local invlist = inventory and inventory:get_list("main") - table.insert(invlist_tab, invlist) - else - if smart_inventory.armor_mod then - local inventory = minetest.get_inventory({type="detached", name=name.."_armor"}) - local invlist = inventory and inventory:get_list("armor") - if invlist then - table.insert(invlist_tab, invlist) - end - end - if smart_inventory.clothing_mod then - local clothing_meta = player:get_attribute("clothing:inventory") - state.param.player_clothing_data = clothing_meta and minetest.deserialize(clothing_meta) or {} - local invlist = {} - for i=1,6 do - table.insert(invlist, ItemStack(state.param.player_clothing_data[i])) - end - table.insert(invlist_tab, invlist) - end - end - - local list_dedup = {} - for _, invlist in ipairs(invlist_tab) do - for stack_index, stack in ipairs(invlist) do - local itemdef = stack:get_definition() - local is_armor = false - if itemdef then - cache.add_item(itemdef) -- analyze groups in case of hidden armor - if cache.citems[itemdef.name].cgroups["armor"] or cache.citems[itemdef.name].cgroups["clothing"] then - local entry = {} - for k, v in pairs(cache.citems[itemdef.name].ui_item) do - entry[k] = v - end - entry.stack_index = stack_index - local wear = stack:get_wear() - if wear > 0 then - entry.text = tostring(math.floor((1 - wear / 65535) * 100 + 0.5)).." %" - end - table.insert(list, entry) - list_dedup[itemdef.name] = itemdef - end - end - end - end - - -- add all usable in creative available armor to the main list - if listname == "main" and player_has_creative == true then - if smart_inventory.armor_mod then - for _, itemdef in pairs(cache.cgroups["armor"].items) do - if not list_dedup[itemdef.name] and not itemdef.groups.not_in_creative_inventory then - list_dedup[itemdef.name] = itemdef - table.insert(list, cache.citems[itemdef.name].ui_item) - end - end - end - if smart_inventory.clothing_mod then - for _, itemdef in pairs(cache.cgroups["clothing"].items) do - if not list_dedup[itemdef.name] and not itemdef.groups.not_in_creative_inventory then - list_dedup[itemdef.name] = itemdef - table.insert(list, cache.citems[itemdef.name].ui_item) - end - end - end - end - - local grid = state:get(listname.."_grid") - grid:setList(list) -end - -local function update_selected_item(state, listentry) - local name = state.location.rootState.location.player - state.param.armor_selected_item = listentry or state.param.armor_selected_item - listentry = state.param.armor_selected_item - if not listentry then - return - end - local i_list = state:get("i_list") - i_list:clearItems() - for _, groupdef in ipairs(ui_tools.get_tight_groups(cache.citems[listentry.itemdef.name].cgroups)) do - i_list:addItem(groupdef.group_desc) - end - state:get("item_name"):setText(listentry.itemdef.description) - state:get("item_image"):setImage(listentry.item) -end - -local function update_page(state) - local name = state.location.rootState.location.player - local player_obj = minetest.get_player_by_name(name) - local skin_obj = smart_inventory.skins_mod and skins.get_player_skin(player_obj) - - -- Update grid lines - if smart_inventory.armor_mod or smart_inventory.clothing_mod then - update_grid(state, "main") - update_grid(state, "overlay") - end - - -- Update preview area and armor informations list - if smart_inventory.armor_mod then - state:get("preview"):setImage(armor.textures[name].preview) - state.location.parentState:get("player_button"):setImage(armor.textures[name].preview) - local a_list = state:get("a_list") - a_list:clearItems() - for k, v in pairs(armor.def[name]) do - local grouptext - if k == "groups" then - for gn, gv in pairs(v) do - if txt and txt["armor:"..gn] then - grouptext = txt["armor:"..gn] - else - grouptext = "armor:"..gn - end - if grouptext and gv ~= 0 then - a_list:addItem(grouptext..": "..gv) - end - end - else - local is_physics = false - for _, group in ipairs(armor.physics) do - if group == k then - is_physics = true - break - end - end - if is_physics then - if txt and txt["physics:"..k] then - grouptext = txt["physics:"..k] - else - grouptext = "physics:"..k - end - if grouptext and v ~= 1 then - a_list:addItem(grouptext..": "..v) - end - else - if txt and txt["armor:"..k] then - grouptext = txt["armor:"..k] - else - grouptext = "armor:"..k - end - if grouptext and v ~= 0 then - if k == "state" then - a_list:addItem(grouptext..": "..tostring(math.floor((1 - v / armor.def[name].count / 65535) * 100 + 0.5)).." %") - else - a_list:addItem(grouptext..": "..v) - end - end - end - end - end - update_selected_item(state) - elseif skin_obj then - local skin_preview = skin_obj:get_preview() - state.location.parentState:get("player_button"):setImage(skin_preview) - state:get("preview"):setImage(skin_preview) - elseif smart_inventory.clothing_mod then - update_selected_item(state) - state.location.parentState:get("player_button"):setImage('inventory_plus_clothing.png') - state:get("preview"):setImage('blank.png') --TODO: build up clothing preview - end - - -- Update skins list and skins info area - if skin_obj then - local m_name = skin_obj:get_meta_string("name") - local m_author = skin_obj:get_meta_string("author") - local m_license = skin_obj:get_meta_string("license") - if m_name then - state:get("skinname"):setText("Skin name: "..(skin_obj:get_meta_string("name"))) - else - state:get("skinname"):setText("") - end - if m_author then - state:get("skinauthor"):setText("Author: "..(skin_obj:get_meta_string("author"))) - else - state:get("skinauthor"):setText("") - end - if m_license then - state:get("skinlicense"):setText("License: "..(skin_obj:get_meta_string("license"))) - else - state:get("skinlicense"):setText("") - end - - -- set the skins list - state.param.skins_list = skins.get_skinlist_for_player(name) - local cur_skin = skins.get_player_skin(player_obj) - local skins_grid_data = {} - local grid_skins = state:get("skins_grid") - for idx, skin in ipairs(state.param.skins_list) do - table.insert(skins_grid_data, { - image = skin:get_preview(), - tooltip = skin:get_meta_string("name"), - is_button = true, - size = { w = 0.87, h = 1.30 } - }) - if not state.param.skins_initial_page_adjusted and skin:get_key() == cur_skin:get_key() then - grid_skins:setFirstVisible(idx - 19) --8x5 (grid size) / 2 -1 - state.param.skins_initial_page_adjusted = true - end - end - grid_skins:setList(skins_grid_data) - end -end - -local function move_item_to_armor(state, item) - local player_has_creative = state.param.invobj:get_has_creative() - local name = state.location.rootState.location.player - local player = minetest.get_player_by_name(name) - local inventory = player:get_inventory() - local armor_inv = minetest.get_inventory({type="detached", name=name.."_armor"}) - - -- get item to be moved to armor inventory - local itemstack, itemname, itemdef - if player_has_creative == true then - itemstack = ItemStack(item.item) - itemname = item.item - else - itemstack = inventory:get_stack("main", item.stack_index) - itemname = itemstack:get_name() - end - itemdef = minetest.registered_items[itemname] - local new_groups = {} - for _, element in ipairs(armor.elements) do - if itemdef.groups["armor_"..element] then - new_groups["armor_"..element] = true - end - end - - -- remove all items with the same group - local removed_items = {} - for stack_index, stack in ipairs(armor_inv:get_list("armor")) do - local old_def = stack:get_definition() - if old_def then - for groupname, groupdef in pairs(old_def.groups) do - if new_groups[groupname] then - table.insert(removed_items, stack) - armor_inv:set_stack("armor", stack_index, {}) --take old - minetest.detached_inventories[name.."_armor"].on_take(armor_inv, "armor", stack_index, stack, player) - if armor_inv:set_stack("armor", stack_index, itemstack) then --put new - minetest.detached_inventories[name.."_armor"].on_put(armor_inv, "armor", stack_index, itemstack, player) - itemstack = ItemStack("") - end - end - end - end - if stack:is_empty() and not itemstack:is_empty() then - if armor_inv:set_stack("armor", stack_index, itemstack) then - minetest.detached_inventories[name.."_armor"].on_put(armor_inv, "armor", stack_index, itemstack, player) - itemstack = ItemStack("") - end - end - end - - -- handle put backs in non-creative to not lost items - if player_has_creative == false then - inventory:set_stack("main", item.stack_index, itemstack) - for _, stack in ipairs(removed_items) do - stack = inventory:add_item("main", stack) - if not stack:is_empty() then - armor_inv:add_item("armor", stack) - end - end - end -end - -local function move_item_to_clothing(state, item) - local name = state.location.rootState.location.player - local player = minetest.get_player_by_name(name) - local inventory = player:get_inventory() - - local clothes = state.param.player_clothing_data - local clothes_ordered = {} - - for i=1, 6 do - if clothes[i] then - table.insert(clothes_ordered, clothes[i]) - end - end - - if #clothes_ordered < 6 then - table.insert(clothes_ordered, item.item) - player:set_attribute("clothing:inventory", minetest.serialize(clothes_ordered)) - clothing:set_player_clothing(player) - state.param.player_clothing_data = clothes_ordered - -- handle put backs in non-creative to not lost items - if player_has_creative == false then - local itemstack = inventory:get_stack("main", item.stack_index) - itemstack:take_item() - inventory:set_stack("main", item.stack_index, itemstack) - end - end -end - -local function move_item_to_inv(state, item) - local player_has_creative = state.param.invobj:get_has_creative() - local name = state.location.rootState.location.player - local player = minetest.get_player_by_name(name) - local inventory = player:get_inventory() - - if cache.cgroups["armor"] and cache.cgroups["armor"].items[item.item] then - local armor_inv = minetest.get_inventory({type="detached", name=name.."_armor"}) - local itemstack = armor_inv:get_stack("armor", item.stack_index) - if player_has_creative == true then - -- trash armor item in creative - itemstack = ItemStack("") - else - itemstack = inventory:add_item("main", itemstack) - end - armor_inv:set_stack("armor", item.stack_index, itemstack) - minetest.detached_inventories[name.."_armor"].on_take(armor_inv, "armor", item.stack_index, itemstack, player) - - elseif cache.cgroups["clothing"] and cache.cgroups["clothing"].items[item.item] then - local clothes = state.param.player_clothing_data - - if player_has_creative ~= true and clothes[item.stack_index] then - local itemstack = inventory:add_item("main", ItemStack(clothes[item.stack_index])) - if itemstack:is_empty() then - clothes[item.stack_index] = nil - end - else - clothes[item.stack_index] = nil - end - player:set_attribute("clothing:inventory", minetest.serialize(clothes)) - clothing:set_player_clothing(player) - end - -end - -local function player_callback(state) - local name = state.location.rootState.location.player - state:background(0, 1.2, 6, 6.6, "it_bg", "smart_inventory_background_border.png") - state:item_image(0.8, 1.5,2,2,"item_image","") - state:label(2.5,1.2,"item_name", "") - state:listbox(0.8,3.3,5.1,4,"i_list", nil, true) - - state:background(6.2, 1.2, 6, 6.6, "pl_bg", "smart_inventory_background_border.png") - state:image(6.7,1.7,2,4,"preview","") - state:listbox(8.6,1.7,3.5,3,"a_list", nil, true) - state:label(6.7,5.5,"skinname","") - state:label(6.7,6.0,"skinauthor", "") - state:label(6.7,6.5, "skinlicense", "") - - state:background(0, 0, 20, 1, "top_bg", "halo.png") - state:background(0, 8, 20, 2, "bottom_bg", "halo.png") - if smart_inventory.armor_mod or smart_inventory.clothing_mod then - local grid_overlay = smart_inventory.smartfs_elements.buttons_grid(state, 0, 0, 20, 1, "overlay_grid") - - grid_overlay:onClick(function(self, state, index, player) - if state.param.player_overlay_list[index] then - update_selected_item(state, state.param.player_overlay_list[index]) - move_item_to_inv(state, state.param.player_overlay_list[index]) - update_page(state) - end - end) - - local grid_main = smart_inventory.smartfs_elements.buttons_grid(state, 0, 8, 20, 2, "main_grid") - grid_main:onClick(function(self, state, index, player) - update_selected_item(state, state.param.player_main_list[index]) - local item = state.param.player_main_list[index] - if cache.citems[item.item].cgroups["armor"] then - move_item_to_armor(state, state.param.player_main_list[index]) - elseif cache.citems[item.item].cgroups["clothing"] then - move_item_to_clothing(state, state.param.player_main_list[index]) - end - update_page(state) - end) - end - - if smart_inventory.skins_mod then - local player_obj = minetest.get_player_by_name(name) - -- Skins Grid - local grid_skins = smart_inventory.smartfs_elements.buttons_grid(state, 12.9, 1.5, 7 , 7, "skins_grid", 0.80, 1.20) - state:background(12.4, 1.2, 7.5 , 6.6, "bg_skins", "smart_inventory_background_border.png") - grid_skins:onClick(function(self, state, index, player) - local cur_skin = state.param.skins_list[index] - if state.location.rootState.location.type ~= "inventory" and cur_skin._key:sub(1,17) == "character_creator" then - state.location.rootState.obsolete = true -- other screen appears, obsolete the inventory session - end - skins.set_player_skin(player_obj, cur_skin) - if smart_inventory.armor_mod then - armor.textures[name].skin = cur_skin:get_texture() - armor:set_player_armor(player_obj) - end - update_page(state) - end) - end - - -- not visible update plugin for updates from outsite (API) - state:element("code", { name = "update_hook" }):onSubmit(function(self, state) - update_page(state) - state.location.rootState:show() - end) - - update_page(state) -end - -smart_inventory.register_page({ - name = "player", - icon = "player.png", - tooltip = "Customize yourself", - smartfs_callback = player_callback, - sequence = 20, - on_button_click = update_page -}) - --- register callback in 3d_armor for updates -if smart_inventory.armor_mod and armor.register_on_update then - - local function submit_update_hook(player) - local name = player:get_player_name() - local state = smart_inventory.get_page_state("player", name) - if state then - state:get("update_hook"):submit() - end - end - - armor:register_on_update(submit_update_hook) - - -- There is no callback in 3d_armor for wear change in on_hpchange implementation - minetest.register_on_player_hpchange(function(player, hp_change) - minetest.after(0, submit_update_hook, player) - end) -end diff --git a/mods/gui/smart_inventory/screenshot.png b/mods/gui/smart_inventory/screenshot.png deleted file mode 100644 index b2179b1..0000000 Binary files a/mods/gui/smart_inventory/screenshot.png and /dev/null differ diff --git a/mods/gui/smart_inventory/screenshot_crafting.png b/mods/gui/smart_inventory/screenshot_crafting.png deleted file mode 100644 index f92ae96..0000000 Binary files a/mods/gui/smart_inventory/screenshot_crafting.png and /dev/null differ diff --git a/mods/gui/smart_inventory/screenshot_creative.png b/mods/gui/smart_inventory/screenshot_creative.png deleted file mode 100644 index 5e0c866..0000000 Binary files a/mods/gui/smart_inventory/screenshot_creative.png and /dev/null differ diff --git a/mods/gui/smart_inventory/screenshot_doc.png b/mods/gui/smart_inventory/screenshot_doc.png deleted file mode 100644 index 97774f7..0000000 Binary files a/mods/gui/smart_inventory/screenshot_doc.png and /dev/null differ diff --git a/mods/gui/smart_inventory/screenshot_player.png b/mods/gui/smart_inventory/screenshot_player.png deleted file mode 100644 index 0bda0b1..0000000 Binary files a/mods/gui/smart_inventory/screenshot_player.png and /dev/null differ diff --git a/mods/gui/smart_inventory/settingtypes.txt b/mods/gui/smart_inventory/settingtypes.txt deleted file mode 100644 index da274d2..0000000 --- a/mods/gui/smart_inventory/settingtypes.txt +++ /dev/null @@ -1,10 +0,0 @@ -#If enabled, the mod will show alternative human readable filterstrings if available. -smart_inventory_friendly_group_names (Show “friendly” filter grouping names) bool true - -#List of groups defined for special handling of "Shaped nodes" (Comma separated). -#Items in this groups ignores the "not_in_inventory" group and are moved to separate "Shaped" category -smart_inventory_shaped_groups (List of groups to be handled as separate) string carpet,door,fence,stair,slab,wall,micro,panel,slope,dye - -#If enabled, the the mod does not replace other inventory mods. -#The functionality is provided in a workbench. -smart_inventory_workbench_mode (Use workbench instead of players inventory) bool false diff --git a/mods/gui/smart_inventory/textures/README b/mods/gui/smart_inventory/textures/README deleted file mode 100644 index 93314f0..0000000 --- a/mods/gui/smart_inventory/textures/README +++ /dev/null @@ -1,2 +0,0 @@ -The background border image goes where the skin button would normally go. -Inside the bordered area should be put the fron texture of a skins head. \ No newline at end of file diff --git a/mods/gui/smart_inventory/textures/doc_awards_icon_generic.png b/mods/gui/smart_inventory/textures/doc_awards_icon_generic.png deleted file mode 100644 index 97f2435..0000000 Binary files a/mods/gui/smart_inventory/textures/doc_awards_icon_generic.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/doc_button_icon_lores.png b/mods/gui/smart_inventory/textures/doc_button_icon_lores.png deleted file mode 100644 index 97f2435..0000000 Binary files a/mods/gui/smart_inventory/textures/doc_button_icon_lores.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/inventory_plus_doc_inventory_plus.png b/mods/gui/smart_inventory/textures/inventory_plus_doc_inventory_plus.png deleted file mode 100644 index 97f2435..0000000 Binary files a/mods/gui/smart_inventory/textures/inventory_plus_doc_inventory_plus.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_background_border.png b/mods/gui/smart_inventory/textures/smart_inventory_background_border.png deleted file mode 100644 index 9b007bb..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_background_border.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_compress_button.png b/mods/gui/smart_inventory/textures/smart_inventory_compress_button.png deleted file mode 100644 index 66d9672..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_compress_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_craftable_button.png b/mods/gui/smart_inventory/textures/smart_inventory_craftable_button.png deleted file mode 100644 index 43f95c5..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_craftable_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_crafting_inventory_button.png b/mods/gui/smart_inventory/textures/smart_inventory_crafting_inventory_button.png deleted file mode 100644 index b8af7bf..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_crafting_inventory_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_creative_button.png b/mods/gui/smart_inventory/textures/smart_inventory_creative_button.png deleted file mode 100644 index d8322cf..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_creative_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_exit_button.png b/mods/gui/smart_inventory/textures/smart_inventory_exit_button.png deleted file mode 100644 index 867a927..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_exit_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_furnace.png b/mods/gui/smart_inventory/textures/smart_inventory_furnace.png deleted file mode 100644 index 201d279..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_furnace.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_get1_button.png b/mods/gui/smart_inventory/textures/smart_inventory_get1_button.png deleted file mode 100644 index e085770..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_get1_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_get2_button.png b/mods/gui/smart_inventory/textures/smart_inventory_get2_button.png deleted file mode 100644 index 03ba990..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_get2_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_get3_button.png b/mods/gui/smart_inventory/textures/smart_inventory_get3_button.png deleted file mode 100644 index 2d039ea..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_get3_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_left_arrow.png b/mods/gui/smart_inventory/textures/smart_inventory_left_arrow.png deleted file mode 100644 index 4953ac5..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_left_arrow.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_lookup_field.png b/mods/gui/smart_inventory/textures/smart_inventory_lookup_field.png deleted file mode 100644 index cecafc1..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_lookup_field.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_preview_to_crafting_field.png b/mods/gui/smart_inventory/textures/smart_inventory_preview_to_crafting_field.png deleted file mode 100644 index 9e52a26..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_preview_to_crafting_field.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_reveal_tips_button.png b/mods/gui/smart_inventory/textures/smart_inventory_reveal_tips_button.png deleted file mode 100644 index 849aa5a..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_reveal_tips_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_right_arrow.png b/mods/gui/smart_inventory/textures/smart_inventory_right_arrow.png deleted file mode 100644 index 0b76e29..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_right_arrow.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_save1_button.png b/mods/gui/smart_inventory/textures/smart_inventory_save1_button.png deleted file mode 100644 index 2c5144b..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_save1_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_save2_button.png b/mods/gui/smart_inventory/textures/smart_inventory_save2_button.png deleted file mode 100644 index 89be1ab..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_save2_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_save3_button.png b/mods/gui/smart_inventory/textures/smart_inventory_save3_button.png deleted file mode 100644 index e26defc..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_save3_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_swapline_button.png b/mods/gui/smart_inventory/textures/smart_inventory_swapline_button.png deleted file mode 100644 index de67b18..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_swapline_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_sweep_button.png b/mods/gui/smart_inventory/textures/smart_inventory_sweep_button.png deleted file mode 100644 index dc86463..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_sweep_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_trash.png b/mods/gui/smart_inventory/textures/smart_inventory_trash.png deleted file mode 100644 index 2ad1e6c..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_trash.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_trash_all_button.png b/mods/gui/smart_inventory/textures/smart_inventory_trash_all_button.png deleted file mode 100644 index bf6e63f..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_trash_all_button.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_workbench_front.png b/mods/gui/smart_inventory/textures/smart_inventory_workbench_front.png deleted file mode 100644 index cc2142d..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_workbench_front.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_workbench_sides.png b/mods/gui/smart_inventory/textures/smart_inventory_workbench_sides.png deleted file mode 100644 index 29c4b3b..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_workbench_sides.png and /dev/null differ diff --git a/mods/gui/smart_inventory/textures/smart_inventory_workbench_top.png b/mods/gui/smart_inventory/textures/smart_inventory_workbench_top.png deleted file mode 100644 index f9a36cf..0000000 Binary files a/mods/gui/smart_inventory/textures/smart_inventory_workbench_top.png and /dev/null differ diff --git a/mods/gui/smart_inventory/ui_tools.lua b/mods/gui/smart_inventory/ui_tools.lua deleted file mode 100644 index d3ec146..0000000 --- a/mods/gui/smart_inventory/ui_tools.lua +++ /dev/null @@ -1,486 +0,0 @@ -local cache = smart_inventory.cache -local crecipes = smart_inventory.crecipes -local txt = smart_inventory.txt -local doc_addon = smart_inventory.doc_addon - -local ui_tools = {} ------------------------------------------------------ --- Group item list and prepare for output ------------------------------------------------------ --- Parameters: --- grouped: grouped items list (result of cache.get_list_grouped) --- groups_sel: smartfs Element (table) that should contain the groups --- groups_tab: shadow table that with items per group that will be updated in this method --- Return: updated groups_tab - - -function ui_tools.update_group_selection(grouped, groups_sel, groups_tab) - -- save old selection - local sel_id = groups_sel:getSelected() - local sel_grp - if sel_id and groups_tab then - sel_grp = groups_tab[sel_id] - end - - -- sort the groups - local group_sorted = {} - for _, group in pairs(grouped) do - table.insert(group_sorted, group) - end - - table.sort(group_sorted, function(a,b) - local sort_fixed_order = { - ["all"] = 5, -- at the begin - ["other"] = 80, -- at the end - ["shape"] = 90, --at the end - } - local aval = sort_fixed_order[a.name] or 10 - local bval = sort_fixed_order[b.name] or 10 - if aval ~= bval then - return aval < bval - else - return a.name < b.name - end - end) - - -- apply groups to the groups_sel table and to the new groups_tab - groups_sel:clearItems() - groups_tab = {} - for _, group in ipairs(group_sorted) do - if #group.items > 0 then - local idx = groups_sel:addItem(group.group_desc.." ("..#group.items..")") - groups_tab[idx] = group.name - if sel_grp == group.name then - sel_id = idx - end - end - end - - -- restore selection - if not groups_tab[sel_id] then - sel_id = 1 - end - groups_sel:setSelected(sel_id) - - return groups_tab -end - - ------------------------------------------------------ --- Create trash inventory ------------------------------------------------------ -function ui_tools.create_trash_inv(state, name) - local player = minetest.get_player_by_name(name) - local invname = name.."_trash_inv" - local listname = "trash" - local inv = minetest.get_inventory({type="detached", name=invname}) - if not inv then - inv = minetest.create_detached_inventory(invname, { - allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) - return 0 - end, - allow_put = function(inv, listname, index, stack, player) - return 99 - end, - allow_take = function(inv, listname, index, stack, player) - return 99 - end, - on_move = function(inv, from_list, from_index, to_list, to_index, count, player) - end, - on_put = function(inv, listname, index, stack, player) - minetest.after(1, function(stack) - inv:set_stack(listname, index, nil) - end) - end, - on_take = function(inv, listname, index, stack, player) - inv:set_stack(listname, index, nil) - end, - }, name) - end - inv:set_size(listname, 1) -end - - ------------------------------------------------------ --- Filter a list by search string ------------------------------------------------------ -function ui_tools.filter_by_searchstring(list, search_string, lang_code) - local filtered_list = {} - search_string = search_string:lower() - for _, entry in ipairs(list) do - local def = minetest.registered_items[entry.item] - local description = def.description - if lang_code then - description = minetest.get_translated_string(lang_code, description) - end - if string.find(description:lower(), search_string) or - string.find(def.name:lower(), search_string) then - table.insert(filtered_list, entry) - else - for _, cgroup in pairs(entry.citem.cgroups) do - if cgroup.keyword then - if string.find(cgroup.keyword:lower():gsub("_", ":"), search_string:gsub("_", ":"))then - table.insert(filtered_list, entry) - break - end - end - if cgroup.group_desc then - local group_desc =txt[cgroup.group_desc] or cgroup.group_desc - if string.find(group_desc:lower(), search_string)then - table.insert(filtered_list, entry) - break - end - end - end - end - end - return filtered_list -end - ------------------------------------------------------ --- Get all revealed items available ------------------------------------------------------ -function ui_tools.filter_by_revealed(list, playername, by_item_only) - if not smart_inventory.doc_items_mod then - return list - end - local revealed_items_cache = {} - local filtered_list = {} - for _, entry in ipairs(list) do - -- check recipes - local revealed_by_recipe = false - if by_item_only ~= true and - cache.citems[entry.item] and - cache.citems[entry.item].in_output_recipe then - for _, recipe in ipairs(cache.citems[entry.item].in_output_recipe) do - if crecipes.crecipes[recipe]:is_revealed(playername, revealed_items_cache) then - revealed_by_recipe = true - break - end - end - end - if revealed_by_recipe or doc_addon.is_revealed_item(entry.item, playername) then - table.insert(filtered_list, entry) - end - end - return filtered_list -end - ------------------------------------------------------ --- Get all revealed items available ------------------------------------------------------ -function ui_tools.filter_by_top_reveal(list, playername) - -- check the list for not revealed only. Create search index - local craftable_only = {} - for _, entry in ipairs(list) do - -- only not revealed could be in tipp - if not doc_addon.is_revealed_item(entry.item, playername) then - craftable_only[entry.item] = entry - end - end - - local rating_tab = {} - local revealed_items_cache = {} - - for itemname, entry in pairs(craftable_only) do - -- Check all recipes - --print("check", itemname) - local rating_value = 0 - -- Check all items - for _, recipe in ipairs(cache.citems[itemname].in_craft_recipe) do - if crecipes.crecipes[recipe] then - local crecipe = crecipes.crecipes[recipe] - if not doc_addon.is_revealed_item(crecipe.out_item.name, playername) then - --print("check recipe out:", crecipe.out_item.name) - - local revealed_by_other_recipe = false - for _, recipe in ipairs(cache.citems[crecipe.out_item.name].in_output_recipe) do - if crecipes.crecipes[recipe]:is_revealed(playername, revealed_items_cache) then - revealed_by_other_recipe = true - break - end - end - - if not revealed_by_other_recipe then - for recipe_itemname, iteminfo in pairs(crecipe._items) do - -- in recipe - if recipe_itemname == itemname or minetest.registered_aliases[recipe_itemname] == itemname then - rating_value = rating_value + 1 - --print("by name", recipe_itemname, iteminfo.items[recipe_itemname].name) - elseif recipe_itemname:sub(1, 6) == "group:" and iteminfo.items[itemname] then - local is_revealed = false - for alt_itemname, _ in pairs (iteminfo.items) do - if doc_addon.is_revealed_item(alt_itemname, playername) then - is_revealed = true - break - end - end - if not is_revealed then - --print("by group", recipe_itemname, itemname) - rating_value = rating_value + 1 - end - end - end - end - end - end - --print("rating", itemname, rating_value) - rating_tab[itemname] = (rating_tab[itemname] or 0) + rating_value - end - end - - -- prepare output list - local sorted_rating = {} - for itemname, rating in pairs(rating_tab) do - table.insert(sorted_rating, {itemname = itemname, rating = rating}) - end - table.sort(sorted_rating, function(a,b) return a.rating > b.rating end) - - local out_count = 0 - local filtered_list = {} - local top_rating = 0 - for _, v in ipairs(sorted_rating) do - -- top 10 but show all with the same rating - if out_count < 20 or v.rating == top_rating then - top_rating = v.rating - local entry = craftable_only[v.itemname] - if v.rating > 0 then - entry = {} - for kk, vv in pairs(craftable_only[v.itemname]) do - entry[kk] = vv - end - entry.text = v.rating - end - table.insert(filtered_list, entry) - out_count = out_count + 1 - else - break - end - end - return filtered_list -end - ------------------------------------------------------ --- Select tight groups only to display info about item ------------------------------------------------------ -function ui_tools.get_tight_groups(cgroups) - local out_list = {} - for group1, groupdef1 in pairs(cgroups) do - if groupdef1.keyword then - out_list[group1] = groupdef1 - for group2, groupdef2 in pairs(out_list) do - if string.len(group1) > string.len(group2) and - string.sub(group1,1,string.len(group2)) == group2 then - -- group2 is top-group of group1. Remove the group2 - out_list[group2] = nil - elseif string.len(group1) < string.len(group2) and - string.sub(group2,1,string.len(group1)) == group1 then - -- group2 is top-group of group1. Remove the group2 - out_list[group1] = nil - end - end - end - end - local out_list_sorted = {} - for group, groupdef in pairs(out_list) do - table.insert(out_list_sorted, groupdef) - end - table.sort(out_list_sorted, function(a,b) - return a.group_desc < b.group_desc - end) - return out_list_sorted -end - ------------------------------------------------------ --- Sort items to groups and decide which groups should be displayed ------------------------------------------------------ -function ui_tools.get_list_grouped(itemtable) - local grouped = {} - -- sort the entries to groups - for _, entry in ipairs(itemtable) do - if cache.citems[entry.item] then - for _, group in pairs(cache.citems[entry.item].cgroups) do - if not grouped[group.name] then - local group_info = {} - group_info.name = group.name - group_info.cgroup = cache.cgroups[group.name] - group_info.items = {} - grouped[group.name] = group_info - end - table.insert(grouped[group.name].items, entry) - end - end - end - - -- magic to calculate relevant groups - local itemcount = #itemtable - local best_group_count = itemcount ^(1/3) - local best_group_size = (itemcount / best_group_count) * 1.5 - best_group_count = math.floor(best_group_count) - local sorttab = {} - - for k,v in pairs(grouped) do - if #v.items < 3 or #v.items >= itemcount - 3 then - grouped[k] = nil - else - v.group_size = #v.items - v.unique_count = #v.items - v.best_group_size = best_group_size - v.diff = math.abs(v.group_size - v.best_group_size) - table.insert(sorttab, v) - end - end - - local outtab = {} - local assigned_items = {} - if best_group_count > 0 then - for i = 1, best_group_count do - -- sort by best size - table.sort(sorttab, function(a,b) - return a.diff < b.diff - end) - - local sel = sorttab[1] - - if not sel then - break - end - outtab[sel.name] = { - name = sel.name, - group_desc = sel.cgroup.group_desc, - items = sel.items - } - table.remove(sorttab, 1) - - - for _, item in ipairs(sel.items) do - assigned_items[item.item] = true - -- update the not selected groups - for _, group in pairs(cache.citems[item.item].cgroups) do - if group.name ~= sel.name then - local u = grouped[group.name] - if u and u.unique_count and u.group_size > 0 then - u.unique_count = u.unique_count-1 - if (u.group_size < u.best_group_size) or - (u.group_size - u.best_group_size) < (u.best_group_size - u.unique_count) then - sel.diff = u.best_group_size - u.unique_count - end - end - end - end - end - - for idx = #sorttab, 1, -1 do - if sorttab[idx].unique_count < 3 or - ( sel.cgroup.parent and sel.cgroup.parent.name == sorttab[idx].name ) or - ( sel.cgroup.childs and sel.cgroup.childs[sorttab[idx].name] ) - then - grouped[sorttab[idx].name] = nil - table.remove(sorttab, idx) - end - end - end - end - - -- fill other group - local other = {} - for _, item in ipairs(itemtable) do - if not assigned_items[item.item] then - table.insert(other, item) - end - end - - -- default groups - outtab.all = {} - outtab.all.name = "all" - outtab.all.items = itemtable - - outtab.other = {} - outtab.other.name = "other" - outtab.other.items = other - - if txt then - outtab.all.group_desc = txt[outtab.all.name] or "all" - outtab.other.group_desc = txt[outtab.other.name] or "other" - else - outtab.all.group_desc = "all" - outtab.other.group_desc = "other" - end - - return outtab -end - - -local function unifieddyes_sort_order() end -if minetest.global_exists("unifieddyes") then - function unifieddyes_sort_order(entry) - local ret = unifieddyes.getpaletteidx(entry.item, "extended") - if ret then - local ret2 = string.format("%02X", ret) - return 'dye '..ret2 - end - end -end - -local function armor_sort_order(entry) end -if minetest.global_exists("armor") then - function armor_sort_order(entry) - if not entry.citem.cgroups["armor"] then - return - end - local split = entry.item:split("_") - return "armor "..split[#split] .. entry.item - end -end - ------------------------------------------------------ --- Prepare root lists for all users ------------------------------------------------------ -local function prepare_root_lists() - ui_tools.root_list = {} - ui_tools.root_list_shape = {} - ui_tools.root_list_all = {} - - for itemname, citem in pairs(cache.citems) do - local entry = { - citem = citem, - itemdef = minetest.registered_items[itemname], - - -- buttons_grid related - item = itemname, - is_button = true - } - - entry.sort_value = unifieddyes_sort_order(entry) or armor_sort_order(entry) or itemname - citem.ui_item = entry - if citem.cgroups["shape"] then - table.insert(ui_tools.root_list_shape, entry) - else - table.insert(ui_tools.root_list, entry) - end - table.insert(ui_tools.root_list_all, entry) - end -end -cache.register_on_cache_filled(prepare_root_lists) - ------------------------------------------------------ --- Take a visual feedback on pressing button since the minetest client does nothing visible on pressing button ------------------------------------------------------ -function ui_tools.image_button_feedback(playername, page, element) - local function reset_background(playername, page, element) - local state = smart_inventory.get_page_state(page, playername) - if state then - state:get(element):setBackground(nil) - state.location.rootState:show() - end - end - - local state = smart_inventory.get_page_state(page, playername) - if state then - state:get(element):setBackground("halo.png") - minetest.after(0.3, reset_background, playername, page, element) - end - -end --------------------------------- -return ui_tools diff --git a/mods/gui/smart_inventory/workbench.lua b/mods/gui/smart_inventory/workbench.lua deleted file mode 100644 index 993fd7e..0000000 --- a/mods/gui/smart_inventory/workbench.lua +++ /dev/null @@ -1,34 +0,0 @@ -local smartfs = smart_inventory.smartfs - -local function on_rightclick(pos, node, player, itemstack, pointed_thing) - smartfs.get("smart_inventory:main"):show(player:get_player_name()) -end - -local sound = nil -if minetest.global_exists("default") then - sound = default.node_sound_wood_defaults() -end - --- Return smart inventory workbench definition if enabled -minetest.register_node("smart_inventory:workbench", { - description = "Smart inventory workbench", - groups = {cracky=2, choppy=2, oddly_breakable_by_hand=1}, - sounds = sound, - tiles = { - "smart_inventory_workbench_top.png", - "smart_inventory_workbench_top.png", - "smart_inventory_workbench_sides.png", - "smart_inventory_workbench_sides.png", - "smart_inventory_workbench_front.png", - "smart_inventory_workbench_front.png" - }, - on_rightclick = on_rightclick -}) - -minetest.register_craft({ -output = "smart_inventory:workbench", - recipe = { - {"default:coral_skeleton", "default:coral_skeleton"}, - {"default:coral_skeleton", "default:coral_skeleton"} - } -})