From 2b3df66a7df4e97e8552cf881cf16e382d22860e Mon Sep 17 00:00:00 2001 From: Jaidyn Ann <10477760+JadedCtrl@users.noreply.github.com> Date: Fri, 12 Jan 2024 03:16:02 -0600 Subject: [PATCH] Rename fork to i4 Now that the API is actually changing, it seems like a good point to rename the fork. --- API.md | 110 +++---- README.md | 20 +- init.lua | 41 ++- mod.conf | 2 +- res/i4_logo.png | Bin 0 -> 371 bytes res/i4_logo_scaled.png | Bin 0 -> 248 bytes res/screenshot.png | Bin 0 -> 42419 bytes settingtypes.txt | 2 +- src/api.lua | 272 +++++++++--------- src/bags.lua | 15 +- src/caches.lua | 76 ++--- src/callbacks.lua | 55 ++-- src/common.lua | 30 +- src/compression.lua | 2 +- src/detached_inv.lua | 4 +- src/fields.lua | 31 +- src/groups.lua | 4 +- src/gui.lua | 84 +++--- src/hud.lua | 16 +- src/preprocessor.lua | 4 +- src/progressive.lua | 28 +- src/styles.lua | 4 +- textures/{i3_crafting.png => i4_crafting.png} | Bin 23 files changed, 396 insertions(+), 404 deletions(-) create mode 100644 res/i4_logo.png create mode 100644 res/i4_logo_scaled.png create mode 100644 res/screenshot.png rename textures/{i3_crafting.png => i4_crafting.png} (100%) diff --git a/API.md b/API.md index f4c2dfd..f151400 100644 --- a/API.md +++ b/API.md @@ -16,15 +16,15 @@ ### Tabs -#### `i3.new_tab(name, def)` +#### `i4.new_tab(name, def)` - `name` is the tab name. - `def` is the tab definition. -Custom tabs can be added to the `i3` inventory as follow (example): +Custom tabs can be added to the `i4` inventory as follow (example): ```Lua -i3.new_tab("stuff", { +i4.new_tab("stuff", { description = "Stuff", image = "image.png", -- Optional, add an image next to the tab description slots = true -- Optional, whether the inventory slots are shown or not. Disabled by default. @@ -62,28 +62,28 @@ i3.new_tab("stuff", { - `data` are the user data. - `fs` is the formspec table which is callable with a metamethod. Every call adds a new entry. -#### `i3.set_fs(player)` +#### `i4.set_fs(player)` Update the current formspec. -#### `i3.remove_tab(tabname)` +#### `i4.remove_tab(tabname)` Delete a tab by name. -#### `i3.get_current_tab(player)` +#### `i4.get_current_tab(player)` Return the current player tab. `player` is an `ObjectRef` to the user. -#### `i3.set_tab(player[, tabname])` +#### `i4.set_tab(player[, tabname])` Set 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)` +#### `i4.override_tab(tabname, def)` -Override a tab by name. `def` is the tab definition like seen in `i3.set_tab` +Override a tab by name. `def` is the tab definition like seen in `i4.set_tab` -#### `i3.tabs` +#### `i4.tabs` A list of registered tabs. @@ -91,7 +91,7 @@ A list of registered tabs. ### Footer buttons -`i3.new_footer_button(name, def)` +`i4.new_footer_button(name, def)` * `name` is the footer button’s name. * `def` is the button defintion. @@ -99,7 +99,7 @@ A list of registered tabs. Custom footer buttons can be added beside the trash, sort, and settings buttons. For example: ```Lua -i3.new_footer_button("broadcast_msg", { +i4.new_footer_button("broadcast_msg", { description = "Broadcast message", image = "speech_icon.png", -- Required, this is the button’s icon. @@ -114,14 +114,14 @@ i3.new_footer_button("broadcast_msg", { -- Build the formspec formspec = function(player, data, fs) - -- Button style nicked from i3 directly. + -- Button style nicked from i4 directly. fs([[ style[send_msg_button,confirm_trash_no,set_home;noclip=true;font_size=16; - bgimg=i3_btn9.png;bgimg_hovered=i3_btn9_hovered.png; - bgimg_pressed=i3_btn9_pressed.png;bgimg_middle=4,6] + bgimg=i4_btn9.png;bgimg_hovered=i4_btn9_hovered.png; + bgimg_pressed=i4_btn9_pressed.png;bgimg_middle=4,6] ]]) - fs("image[5,10.65;3,0.5;i3_bg_goto.png]") + fs("image[5,10.65;3,0.5;i4_bg_goto.png]") fs("field[5,10.65;3,0.5;chat_msg_field;;]") fs("button[8,10.65;1,0.5;send_msg_button;Send]") -- No need to return anything @@ -137,15 +137,15 @@ i3.new_footer_button("broadcast_msg", { end, }) ``` -#### `i3.remove_footer_button(button_name)` +#### `i4.remove_footer_button(button_name)` Delete a footer button by name. -#### `i3.override_footer_button(button_name, def)` +#### `i4.override_footer_button(button_name, def)` -Override a footer button by name. `def` is the button definition like seen in `i3.new_footer_button` +Override a footer button by name. `def` is the button definition like seen in `i4.new_footer_button` -#### `i3.footer_buttons` +#### `i4.footer_buttons` A list of registered footer buttons. @@ -162,7 +162,7 @@ Examples: #### Registering a custom crafting type ```Lua -i3.register_craft_type("digging", { +i4.register_craft_type("digging", { description = "Digging", icon = "default_tool_steelpick.png", }) @@ -171,7 +171,7 @@ i3.register_craft_type("digging", { #### Registering a custom crafting recipe ```Lua -i3.register_craft { +i4.register_craft { type = "digging", result = "default:cobble 2", items = {"default:stone"}, @@ -179,7 +179,7 @@ i3.register_craft { ``` ```Lua -i3.register_craft { +i4.register_craft { result = "default:cobble 16", items = { "default:stone, default:stone, default:stone", @@ -192,7 +192,7 @@ i3.register_craft { Recipes can be registered in a Minecraft-like way: ```Lua -i3.register_craft { +i4.register_craft { grid = { "X #", " ## ", @@ -210,7 +210,7 @@ i3.register_craft { Multiple recipes can also be registered at once: ```Lua -i3.register_craft { +i4.register_craft { { result = "default:mese", items = { @@ -234,8 +234,8 @@ i3.register_craft { 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/tests/test_online_recipe.json" +i4.register_craft { + url = "https://raw.githubusercontent.com/minetest-mods/i4/main/tests/test_online_recipe.json" } ``` @@ -246,7 +246,7 @@ i3.register_craft { Manage the tabs on the right panel of the inventory. Allow to make a sensible list sorted by specific groups of items. -#### `i3.new_minitab(name, def)` +#### `i4.new_minitab(name, def)` Add a new minitab (limited to 6). @@ -256,7 +256,7 @@ Add a new minitab (limited to 6). Example: ```Lua -i3.new_minitab("test", { +i4.new_minitab("test", { description = "Test", -- Whether this tab is visible or not. Optional. @@ -276,13 +276,13 @@ i3.new_minitab("test", { - `data` are the user data. - `item` is an item name string. -#### `i3.remove_minitab(name)` +#### `i4.remove_minitab(name)` Remove a minitab by name. - `name` is the name of the tab to remove. -#### `i3.minitabs` +#### `i4.minitabs` A list of registered minitabs. @@ -293,7 +293,7 @@ A list of registered minitabs. 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))` +#### `i4.add_recipe_filter(name, function(recipes, player))` Add 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 @@ -303,7 +303,7 @@ user. Each recipe is a table of the form returned by Example function to hide recipes for items from a mod called "secretstuff": ```lua -i3.add_recipe_filter("Hide secretstuff", function(recipes) +i4.add_recipe_filter("Hide secretstuff", function(recipes) local filtered = {} for _, recipe in ipairs(recipes) do if recipe.output:sub(1,12) ~= "secretstuff:" then @@ -315,11 +315,11 @@ i3.add_recipe_filter("Hide secretstuff", function(recipes) end) ``` -#### `i3.set_recipe_filter(name, function(recipe, player))` +#### `i4.set_recipe_filter(name, function(recipe, player))` Remove all recipe filters and add a new one. -#### `i3.recipe_filters` +#### `i4.recipe_filters` A map of recipe filters, indexed by name. @@ -341,7 +341,7 @@ Notes: - If `optional_name` is omitted, the search filter will apply to all items, without pre-filtering. - The `+groups` filter is currently implemented by default. -#### `i3.add_search_filter(name, function(item, values))` +#### `i4.add_search_filter(name, function(item, values))` Add a search filter. The search function must return a boolean value (whether the given item should be listed or not). @@ -352,7 +352,7 @@ The search function must return a boolean value (whether the given item should b Example function sorting items by drawtype: ```lua -i3.add_search_filter("types", function(item, drawtypes) +i4.add_search_filter("types", function(item, drawtypes) local t = {} for i, dt in ipairs(drawtypes) do @@ -365,7 +365,7 @@ i3.add_search_filter("types", function(item, drawtypes) end) ``` -#### `i3.search_filters` +#### `i4.search_filters` A map of search filters, indexed by name. @@ -375,7 +375,7 @@ A map of search filters, indexed by name. Sorting methods are used to filter the player's main inventory. -#### `i3.add_sorting_method(name, def)` +#### `i4.add_sorting_method(name, def)` Add a player inventory sorting method. @@ -385,7 +385,7 @@ Add a player inventory sorting method. Example: ```Lua -i3.add_sorting_method("test", { +i4.add_sorting_method("test", { description = "Cool sorting method", func = function(list, data) -- `list`: inventory list @@ -400,7 +400,7 @@ i3.add_sorting_method("test", { ``` -#### `i3.sorting_methods` +#### `i4.sorting_methods` A table containing all sorting methods. @@ -408,9 +408,9 @@ A table containing all sorting methods. ### Item list compression -`i3` can reduce the item list size by compressing a group of items. +`i4` can reduce the item list size by compressing a group of items. -#### `i3.compress(item, def)` +#### `i4.compress(item, def)` Add a new group of items to compress. @@ -420,14 +420,14 @@ Add a new group of items to compress. Example: ```Lua -i3.compress("default:diamondblock", { +i4.compress("default:diamondblock", { replace = "diamond", by = {"bronze", "copper", "gold", "steel", "tin"} }) ``` -#### `i3.compress_groups` +#### `i4.compress_groups` A map of all compressed item groups, indexed by stereotypes. @@ -435,9 +435,9 @@ A map of all compressed item groups, indexed by stereotypes. ### Waypoints -`i3` allows you to manage the waypoints of a specific player. +`i4` allows you to manage the waypoints of a specific player. -#### `i3.add_waypoint(player_name, def)` +#### `i4.add_waypoint(player_name, def)` Add a waypoint to specific player. @@ -447,7 +447,7 @@ Add a waypoint to specific player. Example: ```Lua -i3.add_waypoint("Test", { +i4.add_waypoint("Test", { player = "singleplayer", pos = {x = 0, y = 2, z = 0}, color = 0xffff00, @@ -455,7 +455,7 @@ i3.add_waypoint("Test", { }) ``` -#### `i3.remove_waypoint(player_name, waypoint_name)` +#### `i4.remove_waypoint(player_name, waypoint_name)` Remove a waypoint for specific player. @@ -465,10 +465,10 @@ Remove a waypoint for specific player. Example: ```Lua -i3.remove_waypoint("singleplayer", "Test") +i4.remove_waypoint("singleplayer", "Test") ``` -#### `i3.get_waypoints(player_name)` +#### `i4.get_waypoints(player_name)` Return a table of all waypoints of a specific player. @@ -478,7 +478,7 @@ Return a table of all waypoints of a specific player. ### Miscellaneous -#### `i3.hud_notif(name, msg[, img])` +#### `i4.hud_notif(name, msg[, img])` Show a Steam-like HUD notification on the bottom-left corner of the screen. @@ -486,11 +486,11 @@ Show a Steam-like HUD notification on the bottom-left corner of the screen. - `msg` is the HUD message to show. - `img` (optional) is the HUD image to show (preferably 16x16 px). -#### `i3.get_recipes(item)` +#### `i4.get_recipes(item)` Return a table of recipes and usages of `item`. -#### `i3.export_url` +#### `i4.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¹). @@ -502,4 +502,4 @@ given a number between 1 and 4. --- -**[1]** Add `i3` to the `secure.http_mods` or `secure.trusted_mods` setting in `minetest.conf`. +**[1]** Add `i4` to the `secure.http_mods` or `secure.trusted_mods` setting in `minetest.conf`. diff --git a/README.md b/README.md index ac4775d..3cb2bf4 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,9 @@ -![logo](https://user-images.githubusercontent.com/7883281/145490041-d91d6bd6-a654-438d-b208-4d5736845ab7.png) +![i4 logo](res/i4_logo_scaled.png) -[![GitHub Release](https://img.shields.io/github/release/minetest-mods/i3.svg?style=flat)]() ![workflow](https://github.com/minetest-mods/i3/actions/workflows/luacheck.yml/badge.svg) [![ContentDB](https://content.minetest.net/packages/jp/i3/shields/downloads/)](https://content.minetest.net/packages/jp/i3/) [![PayPal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.me/jpg84240) - -#### **`i3`** is a next-generation inventory for Minetest. +#### **`i4`** is an inventory mod for Minetest. This mod features a modern, powerful inventory menu with a good user experience. -**`i3`** provides a rich [**API**](https://github.com/minetest-mods/i3/blob/master/API.md) for mod developers who want to extend it. +**`i4`** provides a rich [**API**](API.md) for mod developers who want to extend it. This mod requires **Minetest 5.6+** @@ -23,7 +21,7 @@ This mod requires **Minetest 5.6+** - Home **¹** *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`.* +To enable it: `i4_progressive_mode = true` in `minetest.conf`.* #### This mod officially supports the following mods: - [**`3d_armor`**](https://content.minetest.net/packages/stu/3d_armor/) @@ -46,15 +44,13 @@ You can also use the font size slider in the inventory, settings window. #### Notes -`i3` uses a larger inventory than the usual inventories in Minetest games. +`i4` 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, such as Minecraft. +The `i4` inventory is 9 slots wide by default, such as Minecraft. -Report bugs on the [**Bug Tracker**](https://github.com/minetest-mods/i3/issues). +Report bugs on the [**Bug Tracker**](https:///notabug.org/jadedctrl/i4). -**Video review on YouTube:** https://www.youtube.com/watch?v=Xd14BCdEZ3o - -![Preview](https://user-images.githubusercontent.com/7883281/185755315-23c2fffa-203d-4115-9dc3-576c92615733.png) +![Preview](res/screenshot.png) #### License diff --git a/init.lua b/init.lua index bd34cee..671279c 100644 --- a/init.lua +++ b/init.lua @@ -1,16 +1,4 @@ -print[[ - - Powered by - - ██╗██████╗ - ██║╚════██╗ - ██║ █████╔╝ - ██║ ╚═══██╗ - ██║██████╔╝ - ╚═╝╚═════╝ -]] - -local modpath = core.get_modpath"i3" +local modpath = core.get_modpath"i4" local http = core.request_http_api() local storage = core.get_mod_storage() local _loadfile = dofile(modpath .. "/src/preprocessor.lua") @@ -19,9 +7,10 @@ local function lf(path) return assert(_loadfile(modpath .. path)) end -i3 = { +i4 = { version = 1162, data = core.deserialize(storage:get_string"data") or {}, + imported = storage:get_int"imported_from_i3", settings = { debug_mode = false, @@ -37,7 +26,7 @@ i3 = { hud_timer_max = 3, damage_enabled = core.settings:get_bool"enable_damage", - progressive_mode = core.settings:get_bool"i3_progressive_mode", + progressive_mode = core.settings:get_bool"i4_progressive_mode" or core.settings:get_bool"i3_progressive_mode", }, categories = { @@ -110,19 +99,21 @@ i3 = { sorting_methods = {}, } -i3.files.common() -i3.files.api(http) -i3.files.compress() -i3.files.detached() -i3.files.fields() -i3.files.groups() -i3.files.callbacks(http, storage) +i3 = i4 -if i3.settings.progressive_mode then - i3.files.progressive() +i4.files.common() +i4.files.api(http) +i4.files.compress() +i4.files.detached() +i4.files.fields() +i4.files.groups() +i4.files.callbacks(http, storage) + +if i4.settings.progressive_mode then + i4.files.progressive() end -if i3.settings.debug_mode then +if i4.settings.debug_mode then lf("/tests/test_tabs.lua")() lf("/tests/test_waypoints.lua")() -- lf("/tests/test_operators.lua")() diff --git a/mod.conf b/mod.conf index 04452f7..4d02197 100644 --- a/mod.conf +++ b/mod.conf @@ -1,4 +1,4 @@ -name = i3 +name = i4 description = Next-generation inventory optional_depends = 3d_armor, skinsdb, awards min_minetest_version = 5.6 diff --git a/res/i4_logo.png b/res/i4_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..52eb2638b0ac06f4d9652d3f5f44ebc21ed002f2 GIT binary patch literal 371 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$>^XV6^gdaSX}0_jaZuUz34=t7hUu z#+X;U9#f8F3#@-|aZl4nDHV@HoKu#v7pSi5>P=#eo;vM&xXvx3{+XHS83GFBrTg+; zmnXOMZ@65b{NY2y)mOiq=av2nI-R1Lb8c&{_iU%@wr0^&_8x0rcR0-T56jxqsf#D9 z>!1A0GV=JLl6UIcJTi9PvpLIjH!t_e!xUNWxf}1#TD?l^QJSsjx2enLo=*(zm)rX8 zq2jA8@tNKH?;GxiM#V8z*YbVW)!oZ5PxlmC*}Sx<0{w=k7fn{lOq)PLbE|eI?B-Hl!kOv(boV6# z75#S%w|B^@w1f2`5&znEnS6NW`|o{@;*#3%6zwT(Q<6L`@`jf+iOj$DX$CMb89ZJ6 KT-G@yGywnzqoG;= literal 0 HcmV?d00001 diff --git a/res/i4_logo_scaled.png b/res/i4_logo_scaled.png new file mode 100644 index 0000000000000000000000000000000000000000..ceb659307795fea92552180cfa6265b17ff780ab GIT binary patch literal 248 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 ipc)~lMhL1AI@Lcl6mb+Yd2}-X0000 1 then for _, v in pairs(def) do - i3.register_craft(v) + i4.register_craft(v) end return end @@ -53,7 +53,7 @@ function i3.register_craft(def) end if not true_str(def.output) and not def.url then - return err "i3.register_craft: output missing" + return err "i4.register_craft: output missing" end if not is_table(def.items) then @@ -112,63 +112,63 @@ function i3.register_craft(def) end local item = ItemStack(def.output):get_name() - i3.recipes_cache[item] = i3.recipes_cache[item] or {} + i4.recipes_cache[item] = i4.recipes_cache[item] or {} def.custom = true def.width = width - insert(i3.recipes_cache[item], def) + insert(i4.recipes_cache[item], def) end -function i3.add_recipe_filter(name, f) +function i4.add_recipe_filter(name, f) if not true_str(name) then - return err "i3.add_recipe_filter: name missing" + return err "i4.add_recipe_filter: name missing" elseif not is_func(f) then - return err "i3.add_recipe_filter: function missing" + return err "i4.add_recipe_filter: function missing" end - i3.recipe_filters[name] = f + i4.recipe_filters[name] = f end -function i3.set_recipe_filter(name, f) +function i4.set_recipe_filter(name, f) if not is_str(name) then - return err "i3.set_recipe_filter: name missing" + return err "i4.set_recipe_filter: name missing" elseif not is_func(f) then - return err "i3.set_recipe_filter: function missing" + return err "i4.set_recipe_filter: function missing" end - i3.recipe_filters = {[name] = f} + i4.recipe_filters = {[name] = f} end -function i3.add_search_filter(name, f) +function i4.add_search_filter(name, f) if not true_str(name) then - return err "i3.add_search_filter: name missing" + return err "i4.add_search_filter: name missing" elseif not is_func(f) then - return err "i3.add_search_filter: function missing" + return err "i4.add_search_filter: function missing" end - i3.search_filters[name] = f + i4.search_filters[name] = f end -function i3.get_recipes(item) +function i4.get_recipes(item) item = core.registered_aliases[item] or item - local recipes = i3.recipes_cache[item] - local usages = i3.usages_cache[item] + local recipes = i4.recipes_cache[item] + local usages = i4.usages_cache[item] return {recipes = recipes, usages = usages} end -function i3.set_fs(player) +function i4.set_fs(player) if not player or player.is_fake_player then return end local name = player:get_player_name() - local data = i3.data[name] + local data = i4.data[name] if not data then return end if data.auto_sorting then sort_inventory(player, data) end - for i, tab in ipairs(i3.tabs) do + for i, tab in ipairs(i4.tabs) do if data.tab == i and tab.access and not tab.access(player, data) then data.tab = 1 break @@ -179,137 +179,137 @@ function i3.set_fs(player) player:set_inventory_formspec(fs) end -function i3.new_tab(name, def) +function i4.new_tab(name, def) if not true_str(name) then - return err "i3.new_tab: tab name missing" + return err "i4.new_tab: tab name missing" elseif not true_table(def) then - return err "i3.new_tab: tab definition missing" + return err "i4.new_tab: tab definition missing" elseif not true_str(def.description) then - return err "i3.new_tab: description missing" - elseif #i3.tabs == 6 then - return err(fmt("i3.new_tab: cannot add '%s' tab. Limit reached (6).", def.name)) + return err "i4.new_tab: description missing" + elseif #i4.tabs == 6 then + return err(fmt("i4.new_tab: cannot add '%s' tab. Limit reached (6).", def.name)) end def.name = name - insert(i3.tabs, def) + insert(i4.tabs, def) end -i3.new_tab("inventory", { +i4.new_tab("inventory", { description = S"Inventory", formspec = get_inventory_fs, slots = true, }) -function i3.remove_tab(name) +function i4.remove_tab(name) if not true_str(name) then - return err "i3.remove_tab: tab name missing" + return err "i4.remove_tab: tab name missing" end - for i = #i3.tabs, 2, -1 do - local def = i3.tabs[i] + for i = #i4.tabs, 2, -1 do + local def = i4.tabs[i] if def and name == def.name then - remove(i3.tabs, i) + remove(i4.tabs, i) end end end -function i3.get_current_tab(player) +function i4.get_current_tab(player) local name = player:get_player_name() - local data = i3.data[name] + local data = i4.data[name] return data.tab end -function i3.set_tab(player, tabname) +function i4.set_tab(player, tabname) local name = player:get_player_name() - local data = i3.data[name] + local data = i4.data[name] if not tabname or tabname == "" then data.tab = 0 return end - for i, tab in ipairs(i3.tabs) do + for i, tab in ipairs(i4.tabs) do if tab.name == tabname then data.tab = i return end end - err(fmt("i3.set_tab: tab name '%s' does not exist", tabname)) + err(fmt("i4.set_tab: tab name '%s' does not exist", tabname)) end -function i3.override_tab(name, newdef) +function i4.override_tab(name, newdef) if not true_str(name) then - return err "i3.override_tab: tab name missing" + return err "i4.override_tab: tab name missing" elseif not true_table(newdef) then - return err "i3.override_tab: tab definition missing" + return err "i4.override_tab: tab definition missing" elseif not true_str(newdef.description) then - return err "i3.override_tab: description missing" + return err "i4.override_tab: description missing" end newdef.name = name - for i, def in ipairs(i3.tabs) do + for i, def in ipairs(i4.tabs) do if def.name == name then - i3.tabs[i] = newdef + i4.tabs[i] = newdef break end end end -function i3.new_footer_button(name, def) +function i4.new_footer_button(name, def) if not true_str(name) then - return err "i3.new_footer_button: button name missing" + return err "i4.new_footer_button: button name missing" elseif not true_table(def) then - return err "i3.new_footer_button: button definition missing" + return err "i4.new_footer_button: button definition missing" elseif not true_str(def.description) then - return err "i3.new_footer_button: description missing" + return err "i4.new_footer_button: description missing" elseif not def.image then - return err "i3.new_footer_button: image missing" - elseif #i3.footer_buttons == 16 then - return err(fmt("i3.new_footer_button: cannot add '%s' button. Limit reached (16).", def.name)) + return err "i4.new_footer_button: image missing" + elseif #i4.footer_buttons == 16 then + return err(fmt("i4.new_footer_button: cannot add '%s' button. Limit reached (16).", def.name)) end def.name = name - insert(i3.footer_buttons, def) + insert(i4.footer_buttons, def) end -function i3.remove_footer_button(name) +function i4.remove_footer_button(name) if not true_str(name) then - return err "i3.remove_footer_button: tab name missing" + return err "i4.remove_footer_button: tab name missing" end - for i = #i3.footer_buttons, 2, -1 do - local def = i3.footer_buttons[i] + for i = #i4.footer_buttons, 2, -1 do + local def = i4.footer_buttons[i] if def and name == def.name then - remove(i3.footer_buttons, i) + remove(i4.footer_buttons, i) end end end -function i3.override_footer_button(name, newdef) +function i4.override_footer_button(name, newdef) if not true_str(name) then - return err "i3.override_footer_button: button name missing" + return err "i4.override_footer_button: button name missing" elseif not true_table(newdef) then - return err "i3.override_footer_button: button definition missing" + return err "i4.override_footer_button: button definition missing" elseif not true_str(newdef.description) then - return err "i3.override_footer_button: description missing" + return err "i4.override_footer_button: description missing" elseif not true_str(newdef.image) then - return err "i3.override_footer_button: image missing" + return err "i4.override_footer_button: image missing" end newdef.name = name - for i, def in ipairs(i3.footer_buttons) do + for i, def in ipairs(i4.footer_buttons) do if def.name == name then - i3.footer_buttons[i] = newdef + i4.footer_buttons[i] = newdef break end end end -i3.new_footer_button("trash", { +i4.new_footer_button("trash", { description = S"Clear inventory", image = "i3_trash.png", formspec = confirm_trash_footer_fs, @@ -317,7 +317,7 @@ i3.new_footer_button("trash", { }) -i3.new_footer_button("sort", { +i4.new_footer_button("sort", { description = S"Sort inventory", image = "i3_sort.png", fields = function(player, data, fields) @@ -327,30 +327,30 @@ i3.new_footer_button("sort", { end, }) -i3.new_footer_button("settings", { +i4.new_footer_button("settings", { description = S"Settings", image = "i3_settings.png", formspec = settings_footer_fs, fields = settings_footer_fields, }) -i3.new_footer_button("home", { +i4.new_footer_button("home", { description = S"Go home", image = "i3_home.png", fields = home_footer_fields, }) -i3.register_craft_type("digging", { +i4.register_craft_type("digging", { description = S"Digging", icon = "i3_steelpick.png", }) -i3.register_craft_type("digging_chance", { +i4.register_craft_type("digging_chance", { description = S"Digging (by chance)", icon = "i3_mesepick.png", }) -i3.add_search_filter("groups", function(item, groups) +i4.add_search_filter("groups", function(item, groups) local def = reg_items[item] local has_groups = true @@ -364,42 +364,42 @@ i3.add_search_filter("groups", function(item, groups) return has_groups end) -function i3.compress(item, def) +function i4.compress(item, def) if not true_str(item) then - return err "i3.compress: item name missing" + return err "i4.compress: item name missing" elseif not true_table(def) then - return err "i3.compress: replace definition missing" + return err "i4.compress: replace definition missing" elseif not true_str(def.replace) then - return err "i3.compress: replace string missing" + return err "i4.compress: replace string missing" elseif not is_table(def.by) then - return err "i3.compress: replace substrings missing" - elseif i3.compressed[item] then - return err(fmt("i3.compress: item '%s' is already compressed", item)) + return err "i4.compress: replace substrings missing" + elseif i4.compressed[item] then + return err(fmt("i4.compress: item '%s' is already compressed", item)) end local t = {} - i3.compress_groups[item] = i3.compress_groups[item] or {} + i4.compress_groups[item] = i4.compress_groups[item] or {} for _, str in ipairs(def.by) do local it = item:gsub(def.replace, str) insert(t, it) - insert(i3.compress_groups[item], it) + insert(i4.compress_groups[item], it) - i3.compressed[it] = true + i4.compressed[it] = true end end -function i3.hud_notif(name, msg, img) +function i4.hud_notif(name, msg, img) if not true_str(name) then - return err "i3.hud_notif: player name missing" + return err "i4.hud_notif: player name missing" elseif not true_str(msg) then - return err "i3.hud_notif: message missing" + return err "i4.hud_notif: message missing" end - local data = i3.data[name] + local data = i4.data[name] if not data then - return err "i3.hud_notif: no player data initialized" + return err "i4.hud_notif: no player data initialized" end local player = get_player_by_name(name) @@ -429,20 +429,20 @@ function i3.hud_notif(name, msg, img) end end -function i3.add_sorting_method(name, def) +function i4.add_sorting_method(name, def) if not true_str(name) then - return err "i3.add_sorting_method: name missing" + return err "i4.add_sorting_method: name missing" elseif not true_table(def) then - return err "i3.add_sorting_method: definition missing" + return err "i4.add_sorting_method: definition missing" elseif not is_func(def.func) then - return err "i3.add_sorting_method: function missing" + return err "i4.add_sorting_method: function missing" end def.name = name - insert(i3.sorting_methods, def) + insert(i4.sorting_methods, def) end -i3.add_sorting_method("alphabetical", { +i4.add_sorting_method("alphabetical", { description = S"Sort items by name (A-Z)", func = function(list, data) sorter(list, data, 1) @@ -450,7 +450,7 @@ i3.add_sorting_method("alphabetical", { end }) -i3.add_sorting_method("numerical", { +i4.add_sorting_method("numerical", { description = S"Sort items by number of items per stack", func = function(list, data) sorter(list, data, 2) @@ -458,18 +458,18 @@ i3.add_sorting_method("numerical", { end, }) -function i3.add_waypoint(name, def) +function i4.add_waypoint(name, def) if not true_str(name) then - return err "i3.add_waypoint: name missing" + return err "i4.add_waypoint: name missing" elseif not true_table(def) then - return err "i3.add_waypoint: definition missing" + return err "i4.add_waypoint: definition missing" elseif not true_str(def.player) then - return err "i3.add_waypoint: player name missing" + return err "i4.add_waypoint: player name missing" end - local data = i3.data[def.player] + local data = i4.data[def.player] if not data then - return err "i3.add_waypoint: no player data initialized" + return err "i4.add_waypoint: no player data initialized" end local player = get_player_by_name(def.player) @@ -487,19 +487,19 @@ function i3.add_waypoint(name, def) data.scrbar_inv += 1000 end - i3.set_fs(player) + i4.set_fs(player) end -function i3.remove_waypoint(player_name, name) +function i4.remove_waypoint(player_name, name) if not true_str(player_name) then - return err "i3.remove_waypoint: player name missing" + return err "i4.remove_waypoint: player name missing" elseif not true_str(name) then - return err "i3.remove_waypoint: waypoint name missing" + return err "i4.remove_waypoint: waypoint name missing" end - local data = i3.data[player_name] + local data = i4.data[player_name] if not data then - return err "i3.remove_waypoint: no player data initialized" + return err "i4.remove_waypoint: no player data initialized" end local player = get_player_by_name(player_name) @@ -515,49 +515,49 @@ function i3.remove_waypoint(player_name, name) end end - i3.set_fs(player) + i4.set_fs(player) end -function i3.get_waypoints(player_name) +function i4.get_waypoints(player_name) if not true_str(player_name) then - return err "i3.get_waypoints: player name missing" + return err "i4.get_waypoints: player name missing" end - local data = i3.data[player_name] + local data = i4.data[player_name] if not data then - return err "i3.get_waypoints: no player data initialized" + return err "i4.get_waypoints: no player data initialized" end return data.waypoints end -function i3.new_minitab(name, def) - if #i3.minitabs == 6 then - return err "i3.new_minitab: limit reached (6)" +function i4.new_minitab(name, def) + if #i4.minitabs == 6 then + return err "i4.new_minitab: limit reached (6)" elseif not true_str(name) then - return err "i3.new_minitab: name missing" + return err "i4.new_minitab: name missing" elseif not true_table(def) then - return err "i3.new_minitab: definition missing" + return err "i4.new_minitab: definition missing" end def.name = name - insert(i3.minitabs, def) + insert(i4.minitabs, def) end -function i3.remove_minitab(name) +function i4.remove_minitab(name) if not true_str(name) then - return err "i3.remove_minitab: name missing" + return err "i4.remove_minitab: name missing" end - for i = #i3.minitabs, 2, -1 do - local v = i3.minitabs[i] + for i = #i4.minitabs, 2, -1 do + local v = i4.minitabs[i] if v and v.name == name then - remove(i3.minitabs, i) + remove(i4.minitabs, i) end end end -i3.new_minitab("all", { +i4.new_minitab("all", { description = "All", sorter = function() @@ -565,7 +565,7 @@ i3.new_minitab("all", { end }) -i3.new_minitab("nodes", { +i4.new_minitab("nodes", { description = "Nodes", sorter = function(item) @@ -573,7 +573,7 @@ i3.new_minitab("nodes", { end }) -i3.new_minitab("items", { +i4.new_minitab("items", { description = "Items", sorter = function(item) diff --git a/src/bags.lua b/src/bags.lua index 5b3deca..c840d84 100644 --- a/src/bags.lua +++ b/src/bags.lua @@ -1,4 +1,4 @@ -local set_fs = i3.set_fs +local set_fs = i4.set_fs IMPORT("get_bag_description", "ItemStack") IMPORT("S", "ES", "fmt", "msg", "slz", "dslz") @@ -16,7 +16,7 @@ end local function init_bags(player) local name = player:get_player_name() - local data = i3.data[name] + local data = i4.data[name] local bag = create_inventory(fmt("i3_bag_%s", name), { allow_put = function(inv, _, _, stack) @@ -142,22 +142,22 @@ local bag_recipes = { }, medium = { rcp = { - {"farming:string", "i3:bag_small", "farming:string"}, - {"farming:string", "i3:bag_small", "farming:string"}, + {"farming:string", "i4:bag_small", "farming:string"}, + {"farming:string", "i4:bag_small", "farming:string"}, }, size = 3, }, large = { rcp = { - {"farming:string", "i3:bag_medium", "farming:string"}, - {"farming:string", "i3:bag_medium", "farming:string"}, + {"farming:string", "i4:bag_medium", "farming:string"}, + {"farming:string", "i4:bag_medium", "farming:string"}, }, size = 4, }, } for size, item in pairs(bag_recipes) do - local bagname = fmt("i3:bag_%s", size) + local bagname = fmt("i4:bag_%s", size) core.register_craftitem(bagname, { description = fmt("%s Backpack", size:gsub("^%l", string.upper)), @@ -165,6 +165,7 @@ for size, item in pairs(bag_recipes) do groups = {bag = item.size}, stack_max = 1, }) + minetest.register_alias_force(fmt("i3:bag_%s", size), bagname) core.register_craft{output = bagname, recipe = item.rcp} core.register_craft{type = "fuel", recipe = bagname, burntime = 3} diff --git a/src/caches.lua b/src/caches.lua index efa3303..b698580 100644 --- a/src/caches.lua +++ b/src/caches.lua @@ -13,7 +13,7 @@ end local function cache_fuel(item) local burntime = get_burntime(item) if burntime > 0 then - i3.fuel_cache[item] = { + i4.fuel_cache[item] = { type = "fuel", items = {item}, burntime = burntime, @@ -23,15 +23,15 @@ local function cache_fuel(item) end local function cache_groups(group, groups) - i3.groups[group] = {} - i3.groups[group].groups = groups - i3.groups[group].items = groups_to_items(groups) + i4.groups[group] = {} + i4.groups[group].groups = groups + i4.groups[group].items = groups_to_items(groups) if #groups == 1 then - i3.groups[group].stereotype = get_group_stereotype(groups[1]) + i4.groups[group].stereotype = get_group_stereotype(groups[1]) end - local items = i3.groups[group].items + local items = i4.groups[group].items if #items <= 1 then return end local px, lim, c = 64, 10, 0 @@ -56,15 +56,15 @@ local function cache_groups(group, groups) if c > 1 then sprite = sprite:gsub("WxH", px .. "x" .. px * c) - i3.groups[group].sprite = sprite - i3.groups[group].count = c + i4.groups[group].sprite = sprite + i4.groups[group].count = c end end local function get_item_usages(item, recipe, added) if is_group(item) then local group = item:sub(7) - local group_cache = i3.groups[group] + local group_cache = i4.groups[group] local groups = group_cache and group_cache.groups or extract_groups(item) if not group_cache then @@ -76,15 +76,15 @@ local function get_item_usages(item, recipe, added) local usage = copy(recipe) table_replace(usage.items, item, name) - i3.usages_cache[name] = i3.usages_cache[name] or {} - insert(i3.usages_cache[name], 1, usage) + i4.usages_cache[name] = i4.usages_cache[name] or {} + insert(i4.usages_cache[name], 1, usage) added[name] = true end end elseif valid_item(reg_items[item]) then - i3.usages_cache[item] = i3.usages_cache[item] or {} - insert(i3.usages_cache[item], 1, recipe) + i4.usages_cache[item] = i4.usages_cache[item] or {} + insert(i4.usages_cache[item], 1, recipe) end end @@ -102,14 +102,14 @@ local function get_usages(recipe) end local function cache_usages(item) - local recipes = i3.recipes_cache[item] or {} + local recipes = i4.recipes_cache[item] or {} for i = 1, #recipes do get_usages(recipes[i]) end - if i3.fuel_cache[item] then - i3.usages_cache[item] = table_merge(i3.usages_cache[item] or {}, {i3.fuel_cache[item]}) + if i4.fuel_cache[item] then + i4.usages_cache[item] = table_merge(i4.usages_cache[item] or {}, {i4.fuel_cache[item]}) end end @@ -133,7 +133,7 @@ local function drop_table(name, drop) if not empty and (dname ~= name or (dname == name and dcount > 1)) then local rarity = valid_rarity and di.rarity - i3.register_craft { + i4.register_craft { type = rarity and "digging_chance" or "digging", items = {name}, output = fmt("%s %u", dname, dcount), @@ -157,7 +157,7 @@ local function cache_drops(name, drop) local empty = dstack:is_empty() if not empty and dname ~= name then - i3.register_craft { + i4.register_craft { type = "digging", items = {name}, output = drop, @@ -188,7 +188,7 @@ local function cache_recipes(item) end if recipes then - i3.recipes_cache[item] = table_merge(recipes, i3.recipes_cache[item] or {}) + i4.recipes_cache[item] = table_merge(recipes, i4.recipes_cache[item] or {}) end end @@ -204,7 +204,7 @@ core.register_craft = function(def) old_register_craft(def) if def.type == "toolrepair" then - i3.toolrepair = def.additional_wear * -100 + i4.toolrepair = def.additional_wear * -100 end local output = def.output or (true_str(def.recipe) and def.recipe) or nil @@ -244,20 +244,20 @@ end local function resolve_aliases(hash) for oldname, newname in pairs(reg_aliases) do cache_recipes(oldname) - local recipes = i3.recipes_cache[oldname] + local recipes = i4.recipes_cache[oldname] if recipes then - if not i3.recipes_cache[newname] then - i3.recipes_cache[newname] = {} + if not i4.recipes_cache[newname] then + i4.recipes_cache[newname] = {} end local similar - for i = 1, #i3.recipes_cache[oldname] do - local rcp_old = i3.recipes_cache[oldname][i] + for i = 1, #i4.recipes_cache[oldname] do + local rcp_old = i4.recipes_cache[oldname][i] - for j = 1, #i3.recipes_cache[newname] do - local rcp_new = copy(i3.recipes_cache[newname][j]) + for j = 1, #i4.recipes_cache[newname] do + local rcp_new = copy(i4.recipes_cache[newname][j]) rcp_new.output = oldname if table_eq(rcp_old, rcp_new) then @@ -267,13 +267,13 @@ local function resolve_aliases(hash) end if not similar then - insert(i3.recipes_cache[newname], rcp_old) + insert(i4.recipes_cache[newname], rcp_old) end end end - if newname ~= "" and i3.recipes_cache[oldname] and reg_items[newname] and not hash[newname] then - insert(i3.init_items, newname) + if newname ~= "" and i4.recipes_cache[oldname] and reg_items[newname] and not hash[newname] then + insert(i4.init_items, newname) end end end @@ -294,21 +294,21 @@ local function init_recipes() for name in pairs(_preselect) do cache_usages(name) - insert(i3.init_items, name) + insert(i4.init_items, name) _select[name] = true end resolve_aliases(_select) - sort(i3.init_items) + sort(i4.init_items) - if http and true_str(i3.export_url) then + if http and true_str(i4.export_url) then local post_data = { - recipes = i3.recipes_cache, - usages = i3.usages_cache, + recipes = i4.recipes_cache, + usages = i4.usages_cache, } http.fetch_async { - url = i3.export_url, + url = i4.export_url, post_data = core.write_json(post_data), } end @@ -321,12 +321,12 @@ local function init_cubes() local tiles = def.tiles or def.tile_images if is_cube(def.drawtype) then - i3.cubes[id] = get_cube(tiles) + i4.cubes[id] = get_cube(tiles) elseif sub(def.drawtype, 1, 9) == "plantlike" or sub(def.drawtype, 1, 8) == "firelike" then local texture = true_str(def.inventory_image) and def.inventory_image or tiles[1] if texture then - i3.plants[id] = texture + i4.plants[id] = texture end end end diff --git a/src/callbacks.lua b/src/callbacks.lua index f72bae4..7bbdc66 100644 --- a/src/callbacks.lua +++ b/src/callbacks.lua @@ -1,15 +1,15 @@ local http, storage = ... -local init_bags = i3.files.bags() -local fill_caches = i3.files.caches(http) -local init_hud = i3.files.hud() -local set_fs = i3.set_fs +local init_bags = i4.files.bags() +local fill_caches = i4.files.caches(http) +local init_hud = i4.files.hud() +local set_fs = i4.set_fs IMPORT("slz", "min", "insert", "copy", "ItemStack") IMPORT("spawn_item", "reset_data", "get_detached_inv", "play_sound", "update_inv_size") core.register_on_player_hpchange(function(player, hpchange) local name = player:get_player_name() - local data = i3.data[name] + local data = i4.data[name] if not data then return end local hp_max = player:get_properties().hp_max @@ -20,10 +20,10 @@ end) core.register_on_dieplayer(function(player) local name = player:get_player_name() - local data = i3.data[name] + local data = i4.data[name] if not data then return end - if i3.settings.drop_bag_on_die then + if i4.settings.drop_bag_on_die then local bagstack = ItemStack(data.bag) spawn_item(player, bagstack) end @@ -45,7 +45,7 @@ end) core.register_on_priv_grant(function(name, _, priv) if priv == "creative" or priv == "all" then - local data = i3.data[name] + local data = i4.data[name] reset_data(data) data.favs = {} @@ -66,7 +66,7 @@ core.register_on_player_inventory_action(function(player, _, _, info) end) if core.global_exists"armor" then - i3.modules.armor = true + i4.modules.armor = true local group_indexes = { {"armor_head", "i3_heavy_helmet"}, @@ -84,7 +84,7 @@ if core.global_exists"armor" then local _, armor_inv = armor:get_valid_player(player, "3d_armor") local def = stack:get_definition() local name = player:get_player_name() - local data = i3.data[name] + local data = i4.data[name] for i, v in ipairs(group_indexes) do local group, sound = unpack(v) @@ -128,7 +128,7 @@ if core.global_exists"armor" then core.register_on_player_inventory_action(function(player, action, _, info) if action ~= "take" then return end local name = player:get_player_name() - local data = i3.data[name] + local data = i4.data[name] if data.armor_disallow then local inv = player:get_inventory() @@ -144,11 +144,11 @@ if core.global_exists"armor" then end if core.global_exists"skins" then - i3.modules.skins = true + i4.modules.skins = true end if core.global_exists"awards" then - i3.modules.awards = true + i4.modules.awards = true core.register_on_craft(function(_, player) set_fs(player) @@ -193,17 +193,17 @@ local function get_formspec_version(info) end local function outdated(name) - core.show_formspec(name, "i3_outdated", + core.show_formspec(name, "i4_outdated", ("size[6.5,1.3]image[0,0;1,1;i3_book.png]label[1,0;%s]button_exit[2.6,0.8;1,1;;OK]"):format( "Your Minetest client is outdated.\nGet the latest version on minetest.net to play the game.")) end local function init_data(player, info) local name = player:get_player_name() - i3.data[name] = i3.data[name] or {} - local data = i3.data[name] + i4.data[name] = i4.data[name] or {} + local data = i4.data[name] - for k, v in pairs(i3.default_data) do + for k, v in pairs(i4.default_data) do local val = data[k] if val == nil then val = v @@ -216,8 +216,8 @@ local function init_data(player, info) data.filter = "" data.pagenum = 1 data.skin_pagenum = 1 - data.items = i3.init_items - data.items_raw = i3.init_items + data.items = i4.init_items + data.items_raw = i4.init_items data.favs = {} data.show_setting = "home" data.crafting_counts = {} @@ -234,28 +234,31 @@ local function init_data(player, info) end local function save_data(player_name) - local _data = copy(i3.data) + local _data = copy(i4.data) for name, v in pairs(_data) do for dat in pairs(v) do - if not i3.saves[dat] then + if not i4.saves[dat] then _data[name][dat] = nil - if player_name and i3.data[player_name] then - i3.data[player_name][dat] = nil -- To free up some memory + if player_name and i4.data[player_name] then + i4.data[player_name][dat] = nil -- To free up some memory end end end end storage:set_string("data", slz(_data)) + if i4.imported then + storage:set_int("imported_from_i3", 1) + end end insert(core.registered_on_joinplayers, 1, function(player) local name = player:get_player_name() local info = core.get_player_information and core.get_player_information(name) - if not info or get_formspec_version(info) < i3.settings.min_fs_version then + if not info or get_formspec_version(info) < i4.settings.min_fs_version then return outdated(name) end @@ -273,7 +276,7 @@ core.register_on_shutdown(save_data) local function routine() save_data() - core.after(i3.settings.save_interval, routine) + core.after(i4.settings.save_interval, routine) end -core.after(i3.settings.save_interval, routine) +core.after(i4.settings.save_interval, routine) diff --git a/src/common.lua b/src/common.lua index 4cdc3f0..34602fa 100644 --- a/src/common.lua +++ b/src/common.lua @@ -27,7 +27,7 @@ function core.is_creative_enabled(name) return core.check_player_privs(name, {creative = true}) or old_is_creative_enabled(name) end -local S = core.get_translator"i3" +local S = core.get_translator"i4" local ES = function(...) return core.formspec_escape(S(...)) end local function is_num(x) @@ -60,7 +60,7 @@ local function reset_compression(data) end local function msg(name, str) - local prefix = "[i3]" + local prefix = "[i4]" return core.chat_send_player(name, fmt("%s %s", core.colorize("#ff0", prefix), str)) end @@ -100,14 +100,14 @@ local function search(data) local filter = data.filter local opt = "^(.-)%+([%w_]+)=([%w_,]+)" - local search_filter = next(i3.search_filters) and match(filter, opt) + local search_filter = next(i4.search_filters) and match(filter, opt) local filters = {} if search_filter then search_filter = search_filter:trim() for filter_name, values in gmatch(filter, sub(opt, 6)) do - if i3.search_filters[filter_name] then + if i4.search_filters[filter_name] then values = split(values, ",") filters[filter_name] = values end @@ -126,7 +126,7 @@ local function search(data) if search_filter then for filter_name, values in pairs(filters) do if values then - local func = i3.search_filters[filter_name] + local func = i4.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)) @@ -268,7 +268,7 @@ local function valid_item(def) end local function get_group_stereotype(group) - local stereotype = i3.group_stereotypes[group] + local stereotype = i4.group_stereotypes[group] local def = reg_items[stereotype] if valid_item(def) then @@ -320,7 +320,7 @@ local function get_cube(tiles) end local function apply_recipe_filters(recipes, player) - for _, filter in pairs(i3.recipe_filters) do + for _, filter in pairs(i4.recipe_filters) do recipes = filter(recipes, player) end @@ -328,7 +328,7 @@ local function apply_recipe_filters(recipes, player) end local function recipe_filter_set() - return next(i3.recipe_filters) + return next(i4.recipe_filters) end local function compression_active(data) @@ -336,7 +336,7 @@ local function compression_active(data) end local function compressible(item, data) - return compression_active(data) and i3.compress_groups[item] + return compression_active(data) and i4.compress_groups[item] end local function is_fav(data) @@ -360,7 +360,7 @@ local function sort_by_category(data) for i = 1, #items do local item = items[i] - local tab = i3.minitabs[data.itab] + local tab = i4.minitabs[data.itab] local to_add = tab.sorter(item, data) if to_add then @@ -382,8 +382,8 @@ end local function get_recipes(player, item) item = core.registered_aliases[item] or item - local recipes = i3.recipes_cache[item] - local usages = i3.usages_cache[item] + local recipes = i4.recipes_cache[item] + local usages = i4.usages_cache[item] if recipes then recipes = apply_recipe_filters(recipes, player) @@ -410,7 +410,7 @@ end local function get_group_items(name) local groups = extract_groups(name) - return i3.groups[name:sub(7)].items or groups_to_items(groups) + return i4.groups[name:sub(7)].items or groups_to_items(groups) end local function craft_stack(player, data, craft_rcp) @@ -579,7 +579,7 @@ local function sort_inventory(player, data) list = data.inv_compress and compress_items(list, start_i) or pre_sorting(list, start_i) - local new_inv = i3.sorting_methods[data.sort].func(list, data) + local new_inv = i4.sorting_methods[data.sort].func(list, data) if not new_inv then return end if not data.ignore_hotbar then @@ -830,7 +830,7 @@ local _ = { vec_round = vector.round, } -function i3.get(...) +function i4.get(...) local t = {} for i, var in ipairs{...} do diff --git a/src/compression.lua b/src/compression.lua index fd06624..20ae782 100644 --- a/src/compression.lua +++ b/src/compression.lua @@ -310,4 +310,4 @@ for _, v2 in ipairs(v) do end end -i3.compress_groups, i3.compressed = compressed, _compressed +i4.compress_groups, i4.compressed = compressed, _compressed diff --git a/src/detached_inv.lua b/src/detached_inv.lua index 9fd750d..994e3b8 100644 --- a/src/detached_inv.lua +++ b/src/detached_inv.lua @@ -1,4 +1,4 @@ -local set_fs = i3.set_fs +local set_fs = i4.set_fs IMPORT("play_sound", "create_inventory") local trash = create_inventory("i3_trash", { @@ -10,7 +10,7 @@ local trash = create_inventory("i3_trash", { inv:set_list(listname, {}) local name = player:get_player_name() - local data = i3.data[name] + local data = i4.data[name] data.armor_allow = nil play_sound(name, "i3_trash", 1.0) diff --git a/src/fields.lua b/src/fields.lua index 5f5c46c..4532873 100644 --- a/src/fields.lua +++ b/src/fields.lua @@ -1,4 +1,4 @@ -local set_fs = i3.set_fs +local set_fs = i4.set_fs IMPORT("min", "max", "vec_round") IMPORT("reg_items", "reg_aliases") @@ -20,7 +20,7 @@ local function inv_fields(player, data, fields) data.font_size = tonumber(fields.sb_font_size:match"-?%d+$") end - local footer = data.footer_button and i3.footer_buttons[data.footer_button] + local footer = data.footer_button and i4.footer_buttons[data.footer_button] if footer and footer.fields then if not footer.fields(player, data, fields) then data.footer_button = nil @@ -29,7 +29,7 @@ local function inv_fields(player, data, fields) for field in pairs(fields) do if sub(field, 1, 4) == "btn_" then - data.subcat = indexof(i3.categories, sub(field, 5)) + data.subcat = indexof(i4.categories, sub(field, 5)) break elseif sub(field, 1, 3) == "cb_" then @@ -117,7 +117,7 @@ local function inv_fields(player, data, fields) data.bag_rename = true elseif fields.confirm_rename then - local bag = get_detached_inv("bag", name) + local bag = get_detached_inv("i3:bag", name) local bagstack = bag:get_stack("main", 1) local meta = bagstack:get_meta() local desc = translate(data.lang_code, bagstack:get_description()) @@ -136,7 +136,7 @@ local function inv_fields(player, data, fields) data.bag_rename = nil elseif fields.waypoint_add then - local max_waypoints = i3.settings.max_waypoints + local max_waypoints = i4.settings.max_waypoints if #data.waypoints >= max_waypoints then play_sound(name, "i3_cannot", 0.8) @@ -211,8 +211,8 @@ local function select_item(player, data, fields) data.alt_items = copy(data.items) data.expand = item - if i3.compress_groups[item] then - local items = copy(i3.compress_groups[item]) + if i4.compress_groups[item] then + local items = copy(i4.compress_groups[item]) insert(items, fmt("_%s", item)) sort(items, function(a, b) @@ -347,7 +347,7 @@ local function rcp_fields(player, data, fields) elseif fields.fav then local fav = is_fav(data) - if #data.favs < i3.settings.max_favs and not fav then + if #data.favs < i4.settings.max_favs and not fav then insert(data.favs, data.query_item) elseif fav then remove(data.favs, fav) @@ -398,7 +398,7 @@ local function home_footer_fields(player, data, fields) else msg(name, S"Welcome back home!") end - -- Otherwise, use i3’s home. + -- Otherwise, use i4’s home. elseif not data.home then msg(name, "No home set") elseif name then @@ -424,11 +424,12 @@ end -- Fields input-handler for the settings footer-dialogue. local function settings_footer_fields(player, data, fields) + local name = player:get_player_name() if fields.set_home and not check_privs(name, {home = true}) then return msg(name, "'home' privilege missing") elseif fields.set_home then if sethome then - sethome.set(player:get_player_name(), player:get_pos()) + sethome.set(name, player:get_pos()) else data.home = pos_to_str(player:get_pos(), 1) end @@ -439,7 +440,7 @@ end core.register_on_player_receive_fields(function(player, formname, fields) local name = player:get_player_name() - if formname == "i3_outdated" then + if formname == "i4_outdated" then return false, core.kick_player(name, S"Your Minetest client needs updating (www.minetest.net)") elseif formname ~= "" then return false @@ -453,7 +454,7 @@ core.register_on_player_receive_fields(function(player, formname, fields) end -- print(dump(fields)) - local data = i3.data[name] + local data = i4.data[name] if not data then return end local sb_inv = fields.scrbar_inv @@ -465,7 +466,7 @@ core.register_on_player_receive_fields(function(player, formname, fields) for f in pairs(fields) do if sub(f, 1, 4) == "tab_" then local tabname = sub(f, 5) - i3.set_tab(player, tabname) + i4.set_tab(player, tabname) break elseif sub(f, 1, 5) == "itab_" then data.pagenum = 1 @@ -474,7 +475,7 @@ core.register_on_player_receive_fields(function(player, formname, fields) break elseif sub(f, 1, 7) == "footer_" then local footer_name = sub(f, 8) - for i, footer in ipairs(i3.footer_buttons) do + for i, footer in ipairs(i4.footer_buttons) do if footer.name == footer_name then data.footer_button = i break @@ -485,7 +486,7 @@ core.register_on_player_receive_fields(function(player, formname, fields) rcp_fields(player, data, fields) - local tab = i3.tabs[data.tab] + local tab = i4.tabs[data.tab] if tab then if tab.slots then inv_fields(player, data, fields) diff --git a/src/groups.lua b/src/groups.lua index 6db4cd0..80b18dc 100644 --- a/src/groups.lua +++ b/src/groups.lua @@ -1,6 +1,6 @@ IMPORT("S") -i3.group_stereotypes = { +i4.group_stereotypes = { dye = "dye:white", wool = "wool:white", wood = "default:wood", @@ -18,7 +18,7 @@ i3.group_stereotypes = { mesecon_conductor_craftable = "mesecons:wire_00000000_off", } -i3.group_names = { +i4.group_names = { dye = S"Any dye", coal = S"Any coal", sand = S"Any sand", diff --git a/src/gui.lua b/src/gui.lua index 84de221..5b9bf2c 100644 --- a/src/gui.lua +++ b/src/gui.lua @@ -1,8 +1,8 @@ -local damage_enabled = i3.settings.damage_enabled -local debug_mode = i3.settings.debug_mode +local damage_enabled = i4.settings.damage_enabled +local debug_mode = i4.settings.debug_mode -local model_aliases = i3.files.model_alias() -local PNG, styles, fs_elements, colors = i3.files.styles() +local model_aliases = i4.files.model_alias() +local PNG, styles, fs_elements, colors = i4.files.styles() local sprintf = string.format local VoxelArea, VoxelManip = VoxelArea, VoxelManip @@ -29,7 +29,7 @@ end local function repairable(tool) local def = reg_tools[tool] - return i3.toolrepair and def and def.groups and def.groups.disable_repair ~= 1 + return i4.toolrepair and def and def.groups and def.groups.disable_repair ~= 1 end local function weird_desc(str) @@ -96,7 +96,7 @@ local function get_stack_max(inv, data, is_recipe, rcp) local def = reg_items[item] if def then - local group_cache = i3.groups[name:sub(7)] + local group_cache = i4.groups[name:sub(7)] local groups = group_cache and group_cache.groups or extract_groups(name) if item_has_groups(def.groups, groups) then @@ -229,8 +229,8 @@ local function get_isometric_view(fs, pos, X, Y, t, cubes, depth, high) local data = vm:get_data() for idx in area:iterp(pos1, pos2) do - local cube = i3.cubes[data[idx]] - local plant = i3.plants[data[idx]] + local cube = i4.cubes[data[idx]] + local plant = i4.plants[data[idx]] local img = cube or plant if img then @@ -526,7 +526,7 @@ local function get_container(fs, data, player, yoffset, ctn_len, award_list, awa local yextra = .2 - for i, title in ipairs(i3.categories) do + for i, title in ipairs(i4.categories) do local btn_name = fmt("btn_%s", title) fs("style[btn_%s;fgimg=%s;fgimg_hovered=%s;content_offset=0]", title, data.subcat == i and PNG[fmt("%s_hover", title)] or PNG[title], @@ -548,7 +548,7 @@ local function get_container(fs, data, player, yoffset, ctn_len, award_list, awa get_crafting_tab_fs(fs, data, player, esc_name, yextra, ctn_len) elseif data.subcat == 2 then - if not i3.modules.armor then + if not i4.modules.armor then return not_installed "3d_armor" end @@ -558,7 +558,7 @@ local function get_container(fs, data, player, yoffset, ctn_len, award_list, awa get_bag_fs(fs, data, bag_size, yextra) elseif data.subcat == 4 then - if not i3.modules.skins then + if not i4.modules.skins then return not_installed "skinsdb" end @@ -612,7 +612,7 @@ local function settings_footer_fs(player, data, fs) local home = data.home if sethome then - -- i3 stores home coordinates with one decimal of precision, as the blow fmt() statement + -- i4 stores home coordinates with one decimal of precision, as the blow fmt() statement -- assumes. So we need to trim sethome’s coordinates to one decimal, likewise. local home_pos = sethome.get(player:get_player_name()) home = string.format("(%.1f,%.1f,%.1f)", home_pos.x, home_pos.y, home_pos.z) @@ -665,7 +665,7 @@ local function settings_footer_fs(player, data, fs) local methods = {} - for _, v in ipairs(i3.sorting_methods) do + for _, v in ipairs(i4.sorting_methods) do local name = toupper(v.name) insert(methods, name) end @@ -673,7 +673,7 @@ local function settings_footer_fs(player, data, fs) label(5.3, 10.4, ES"Sorting method:") fs("dropdown[%f,%f;2.6,0.5;dd_sorting_method;%s;%u;true]", 5.3, 10.6, concat(methods, ","), data.sort) - local desc = i3.sorting_methods[data.sort].description + local desc = i4.sorting_methods[data.sort].description if desc then tooltip(5.3, 10.6, 2.4, 0.5, ESC(desc)) end @@ -694,8 +694,8 @@ end -- Get the inventory-footer’s formspec. local function get_footer(fs, data, player) -- Render individual footer buttons. - local starting_x = 3.43 - (.3 * (#i3.footer_buttons - 4)) -- Center the icons - for i, btn in ipairs(i3.footer_buttons) do + local starting_x = 3.43 - (.3 * (#i4.footer_buttons - 4)) -- Center the icons + for i, btn in ipairs(i4.footer_buttons) do local btn_name = "footer_" .. btn.name fs("style[%s;fgimg=%s;fgimg_hovered=%s^\\[brighten^\\[colorize:#fff:100;content_offset=0]", btn_name, btn.image, btn.image) @@ -704,7 +704,7 @@ local function get_footer(fs, data, player) end -- Render the current-selected button’s formspec, if one is active. - footer = i3.footer_buttons[data.footer_button] + footer = i4.footer_buttons[data.footer_button] if footer then if footer.formspec then footer.formspec(player, data, fs) @@ -748,7 +748,7 @@ local function get_inventory_fs(player, data, fs) if props.mesh ~= "" then local anim = player:get_local_animation() - local armor_skin = i3.modules.armor or i3.modules.skins + local armor_skin = i4.modules.armor or i4.modules.skins local t = {} for _, v in ipairs(props.textures) do @@ -774,7 +774,7 @@ local function get_inventory_fs(player, data, fs) if data.subcat == 3 and bag_size > 0 then max_val += min(0, ((bag_size - 1) * 10)) - elseif i3.modules.skins and data.subcat == 4 then + elseif i4.modules.skins and data.subcat == 4 then local spp = 24 local _skins = skins.get_skinlist_for_player(data.player_name) local nb = #_skins @@ -782,7 +782,7 @@ local function get_inventory_fs(player, data, fs) max_val += ceil(num / 3.5) * (nb > spp and 34 or 31) - elseif i3.modules.awards and data.subcat == 6 then + elseif i4.modules.awards and data.subcat == 6 then award_list = awards.get_award_states(data.player_name) award_list_nb = #award_list @@ -818,7 +818,7 @@ local function get_tooltip(item, info, lang_code) if info.groups then sort(info.groups) - tooltip = i3.group_names[concat(info.groups, ",")] + tooltip = i4.group_names[concat(info.groups, ",")] if not tooltip then local groupstr = {} @@ -862,7 +862,7 @@ local function get_tooltip(item, info, lang_code) end if info.repair then - tooltip = add(S("Repairable by step of @1", clr("#ff0", i3.toolrepair .. "%"))) + tooltip = add(S("Repairable by step of @1", clr("#ff0", i4.toolrepair .. "%"))) end if info.rarity then @@ -906,7 +906,7 @@ local function get_true_count(data, count, is_recipe, is_usage) end local function get_output_fs(fs, data, rcp, is_recipe, is_usage, shapeless, right, btn_size, btn_size2) - local custom_recipe = i3.craft_types[rcp.type] + local custom_recipe = i4.craft_types[rcp.type] local cooking = rcp.type == "cooking" local fuel = rcp.type == "fuel" @@ -941,7 +941,7 @@ local function get_output_fs(fs, data, rcp, is_recipe, is_usage, shapeless, righ end end - local BTN_SIZE = i3.settings.item_btn_size + local BTN_SIZE = i4.settings.item_btn_size local arrow_X = right + 0.2 + (btn_size2 or BTN_SIZE) local X = arrow_X + 1.2 local Y = data.yoffset + 1.4 @@ -978,7 +978,7 @@ local function get_output_fs(fs, data, rcp, is_recipe, is_usage, shapeless, righ 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 = i3.fuel_cache[name] and i3.fuel_cache[name].burntime + local burntime = i4.fuel_cache[name] and i4.fuel_cache[name].burntime local short_desc = meta:get_string"short_description" local long_desc = meta:get_string"description" @@ -1002,7 +1002,7 @@ end local function get_grid_fs(fs, data, rcp, is_recipe, is_usage) local width = rcp.width or 1 local right = 0 - local btn_size, _btn_size = i3.settings.item_btn_size + local btn_size, _btn_size = i4.settings.item_btn_size local cooktime, shapeless if rcp.type == "cooking" then @@ -1061,7 +1061,7 @@ local function get_grid_fs(fs, data, rcp, is_recipe, is_usage) end local groups - local group_cache = i3.groups[name:sub(7)] + local group_cache = i4.groups[name:sub(7)] if is_group(name) then groups = group_cache and group_cache.groups or extract_groups(name) @@ -1132,7 +1132,7 @@ local function get_grid_fs(fs, data, rcp, is_recipe, is_usage) 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 = i3.fuel_cache[name] and i3.fuel_cache[name].burntime + local burntime = i4.fuel_cache[name] and i4.fuel_cache[name].burntime local short_desc = meta:get_string"short_description" local long_desc = meta:get_string"description" @@ -1162,7 +1162,7 @@ local function get_rcp_lbl(fs, data, panel, rn, is_recipe, is_usage) local rcp = is_recipe and panel.rcp[data.rnum] or panel.rcp[data.unum] if rcp.custom then - local craft_type = i3.craft_types[rcp.type] + local craft_type = i4.craft_types[rcp.type] if craft_type then local desc = craft_type.description hypertext(data.inv_width + 4.8, data.yoffset + 0.12, 3, 1, "custom_rcp", @@ -1245,7 +1245,7 @@ end local function get_header(fs, data) local fav = is_fav(data) local nfavs = #data.favs - local max_favs = i3.settings.max_favs + local max_favs = i4.settings.max_favs local star_x, star_y, size = data.inv_width + 0.3, data.yoffset + 0.2, 0.4 if nfavs < max_favs or (nfavs == max_favs and fav) then @@ -1374,7 +1374,7 @@ local function get_rcp_extra(fs, data, player, panel, is_recipe, is_usage) for item, count in pairs(missing) do if count > 0 then - local name = is_group(item) and (i3.group_names[item:sub(7)] or item) or + local name = is_group(item) and (i4.group_names[item:sub(7)] or item) or get_desc(item, data.lang_code) str = fmt("%s\n%s %s", str, clr("#ff0", count .. '×'), name) end @@ -1399,7 +1399,7 @@ local function hide_items(player, data) for i = 1, #data.items do local item = data.items[i] - if not i3.compressed[item] then + if not i4.compressed[item] then insert(new, item) end end @@ -1446,7 +1446,7 @@ local function get_header_items_fs(fs, data) local cat = {{"all", "all items"}} if not recipe_filter_set() then - for _, v in ipairs(i3.minitabs) do + for _, v in ipairs(i4.minitabs) do if v.name == "nodes" then insert(cat, {"node", "nodes only"}) end @@ -1519,7 +1519,7 @@ end local function get_minitabs(fs, data, player, full_height) local minitabs = {} - for i, v in ipairs(i3.minitabs) do + for i, v in ipairs(i4.minitabs) do local access = v.access if access == nil or access(player, data) then minitabs[i] = v.description @@ -1567,7 +1567,7 @@ local function get_items_fs(fs, data, player, full_height) local lbl = ES"No item to show" local icon, width, offset = PNG.no_result, 4, 2 - if recipe_filter_set() and #i3.init_items > 0 and data.filter == "" then + if recipe_filter_set() and #i4.init_items > 0 and data.filter == "" then lbl = ES"Collect items to reveal more recipes" -- Progressive mode, etc. icon, width, offset = PNG.find_more, 2.5, 2.75 end @@ -1627,7 +1627,7 @@ local function get_items_fs(fs, data, player, full_height) end local function get_favs(fs, data) - local btn_size = i3.settings.item_btn_size + local btn_size = i4.settings.item_btn_size label(data.inv_width + 0.4, data.yoffset + 0.4, ES"Bookmarks") for i, item in ipairs(data.favs) do @@ -1665,9 +1665,9 @@ end local function get_tabs_fs(fs, player, data, full_height) local tab_len, tab_hgh, c, over = 3, 0.5, 0 - local _tabs = copy(i3.tabs) + local _tabs = copy(i4.tabs) - for i, def in ipairs(i3.tabs) do + for i, def in ipairs(i4.tabs) do if def.access and not def.access(player, data) then remove(_tabs, i) end @@ -1768,13 +1768,13 @@ local function make_fs(player, data) local full_height = 12 fs("formspec_version[%u]size[%f,%f]no_prepend[]bgcolor[#0000]", - i3.settings.min_fs_version, data.inv_width + 8, full_height) + i4.settings.min_fs_version, data.inv_width + 8, full_height) fs(styles) bg9(0, 0, data.inv_width, full_height, PNG.bg_full) - local tab = i3.tabs[data.tab] + local tab = i4.tabs[data.tab] if tab then if tab.formspec then @@ -1796,9 +1796,9 @@ local function make_fs(player, data) end end - local visible_tabs = #i3.tabs + local visible_tabs = #i4.tabs - for _, def in ipairs(i3.tabs) do + for _, def in ipairs(i4.tabs) do if def.access and not def.access(player, data) then visible_tabs -= 1 end diff --git a/src/hud.lua b/src/hud.lua index 1ada388..bc13b8d 100644 --- a/src/hud.lua +++ b/src/hud.lua @@ -3,12 +3,12 @@ IMPORT("get_connected_players", "add_hud_waypoint") local function init_hud(player) local name = player:get_player_name() - local data = i3.data[name] + local data = i4.data[name] local wdesc_y = -90 if core.global_exists"hb" then wdesc_y -= ceil(hb.hudbars_count / 2) * 5 - elseif not i3.settings.damage_enabled then + elseif not i4.settings.damage_enabled then wdesc_y += 15 end @@ -49,7 +49,7 @@ local function show_hud(player, data, notif, idx, dt) end if notif.show then - local speed = i3.settings.hud_speed * (100 * get_progress(offset.y, notif.max.y)) * dt + local speed = i4.settings.hud_speed * (100 * get_progress(offset.y, notif.max.y)) * dt for _, def in pairs(notif.elems) do local hud_info = player:hud_get(def) @@ -59,8 +59,8 @@ local function show_hud(player, data, notif, idx, dt) y = hud_info.offset.y - (speed * max(1, (#data.hud.notifs - idx + 1) / 1.45)) }) end - elseif notif.show == false and notif.hud_timer >= i3.settings.hud_timer_max then - local speed = (i3.settings.hud_speed * 2.6) * (100 * get_progress(offset.x, notif.max.x)) * dt + elseif notif.show == false and notif.hud_timer >= i4.settings.hud_timer_max then + local speed = (i4.settings.hud_speed * 2.6) * (100 * get_progress(offset.x, notif.max.x)) * dt for _, def in pairs(notif.elems) do local hud_info = player:hud_get(def) @@ -85,7 +85,7 @@ core.register_globalstep(function(dt) for i = 1, players[0] do local player = players[i] local name = player:get_player_name() - local data = i3.data[name] + local data = i4.data[name] if not data then return end for idx, notif in ipairs(data.hud.notifs) do @@ -107,7 +107,7 @@ core.register_globalstep(function(dt) local wieldidx = player:get_wield_index() if wieldidx == data.old_wieldidx then - if data.timer >= i3.settings.wielditem_fade_after and has_text then + if data.timer >= i4.settings.wielditem_fade_after and has_text then player:hud_change(data.hud.wielditem, "text", "") end return @@ -130,7 +130,7 @@ end) local function init_waypoints(player) local name = player:get_player_name() - local data = i3.data[name] + local data = i4.data[name] data.waypoints = data.waypoints or {} for _, v in ipairs(data.waypoints) do diff --git a/src/preprocessor.lua b/src/preprocessor.lua index 138842e..11ed1be 100644 --- a/src/preprocessor.lua +++ b/src/preprocessor.lua @@ -3,7 +3,7 @@ local fmt, split = string.format, string.split local var = "[%w%.%[%]\"\'_]" -local modpath = core.get_modpath"i3" +local modpath = core.get_modpath"i4" local _,_, fs_elements = dofile(modpath .. "/src/styles.lua") local operators = { @@ -42,7 +42,7 @@ local operators = { local function compile(data) data = data:gsub("IMPORT%((.-)%)", function(a) - return "local " .. a:gsub("\"", "") .. " = i3.get(" .. a .. ")" + return "local " .. a:gsub("\"", "") .. " = i4.get(" .. a .. ")" end) data = data:gsub("([%w_]+)%(", function(a) diff --git a/src/progressive.lua b/src/progressive.lua index 3b15292..b66b921 100644 --- a/src/progressive.lua +++ b/src/progressive.lua @@ -1,14 +1,14 @@ -local set_fs = i3.set_fs -local hud_notif = i3.hud_notif +local set_fs = i4.set_fs +local hud_notif = i4.hud_notif local POLL_FREQ = 0.25 IMPORT("reg_items", "reg_nodes", "fmt", "table_merge", "array_diff") IMPORT("is_group", "extract_groups", "item_has_groups", "apply_recipe_filters", "sort_by_category") -i3.remove_minitab"nodes" -i3.remove_minitab"items" +i4.remove_minitab"nodes" +i4.remove_minitab"items" -i3.new_minitab("unlocked", { +i4.new_minitab("unlocked", { description = "Unlocked", sorter = function(item, data) @@ -19,10 +19,10 @@ i3.new_minitab("unlocked", { local function get_filtered_items(player, data) local items, known = {}, 0 - for i = 1, #i3.init_items do - local item = i3.init_items[i] - local recipes = i3.recipes_cache[item] - local usages = i3.usages_cache[item] + for i = 1, #i4.init_items do + local item = i4.init_items[i] + local recipes = i4.recipes_cache[item] + local usages = i4.usages_cache[item] recipes = #apply_recipe_filters(recipes or {}, player) usages = #apply_recipe_filters(usages or {}, player) @@ -43,7 +43,7 @@ local function item_in_inv(item, inv_items) if is_group(item) then local groupname = item:sub(7) - local group_cache = i3.groups[groupname] + local group_cache = i4.groups[groupname] local groups = group_cache and group_cache.groups or extract_groups(item) for i = 1, inv_items_size do @@ -78,7 +78,7 @@ local function progressive_filter(recipes, player) end local name = player:get_player_name() - local data = i3.data[name] + local data = i4.data[name] if #data.inv_items == 0 then return {} @@ -148,7 +148,7 @@ local function poll_new_items(player, data, join) if reg_nodes[last_discovered] then local id = core.get_content_id(last_discovered) - img = i3.cubes[id] or img + img = i4.cubes[id] or img end hud_notif(data.player_name, msg, img) @@ -163,11 +163,11 @@ local function poll_new_items(player, data, join) core.after(POLL_FREQ, poll_new_items, player, data) end -i3.add_recipe_filter("Default progressive filter", progressive_filter) +i4.add_recipe_filter("Default progressive filter", progressive_filter) core.register_on_joinplayer(function(player) local name = player:get_player_name() - local data = i3.data[name] + local data = i4.data[name] if not data then return end data.inv_items = data.inv_items or {} diff --git a/src/styles.lua b/src/styles.lua index 4318c21..31fb6c0 100644 --- a/src/styles.lua +++ b/src/styles.lua @@ -25,7 +25,7 @@ local PNG = { book = "i3_book.png", sign = "i3_sign.png", cancel = "i3_cancel.png", - crafting = "i3_crafting.png", + crafting = "i4_crafting.png", slot = "i3_slot.png^\\[resize:128x128", pagenum_hover = "i3_slot.png^\\[resize:128x128^\\[opacity:130", tab = "i3_tab.png", @@ -59,7 +59,7 @@ local PNG = { cancel_hover = "i3_cancel.png^\\[brighten", search_hover = "i3_search.png^\\[brighten", - crafting_hover = "i3_crafting.png^\\[brighten", + crafting_hover = "i4_crafting.png^\\[brighten", trash_hover = "i3_trash.png^\\[brighten^\\[colorize:#f00:100", compress_hover = "i3_compress.png^\\[brighten", sort_hover = "i3_sort.png^\\[brighten", diff --git a/textures/i3_crafting.png b/textures/i4_crafting.png similarity index 100% rename from textures/i3_crafting.png rename to textures/i4_crafting.png