diff --git a/API.md b/API.md index 8a54d9f..3541ee5 100644 --- a/API.md +++ b/API.md @@ -38,7 +38,7 @@ i3.new_tab { Updates the current formspec. `extra_formspec` adds an additional formspec string. -#### `i3.delete_tab(tabname)` +#### `i3.remove_tab(tabname)` Deletes a tab by name. @@ -141,7 +141,7 @@ Recipes can be registered from a given URL containing a JSON file (HTTP support ```Lua i3.register_craft({ - url = "https://raw.githubusercontent.com/minetest-mods/i3/main/test_online_recipe.json" + url = "https://raw.githubusercontent.com/minetest-mods/i3/main/tests/test_online_recipe.json" }) ``` @@ -178,7 +178,7 @@ end) Removes all recipe filters and adds a new one. -#### `i3.delete_recipe_filter(name)` +#### `i3.remove_recipe_filter(name)` Removes the recipe filter with the given `name`. @@ -197,12 +197,12 @@ 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. +- `wood +groups=flammable`: search for group `flammable` amongst items which contain + `wood` in their names. 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. +- The `+groups` filter is currently implemented by default. #### `i3.add_search_filter(name, function(item, values))` @@ -237,15 +237,6 @@ 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 diff --git a/README.md b/README.md index d1dd3be..9fdce53 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ This mod requires **Minetest 5.4+** - Inventory Sorting (alphabetical + item stack compression) - Item Bookmarks - Waypoints + - Item List Compression (**`moreblocks`** supported) **ยน** *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`.* @@ -48,4 +49,4 @@ Love this mod? Donations are appreciated: https://www.paypal.me/jpg84240 Demo video (outdated): https://www.youtube.com/watch?v=25nCAaqeacU -![Preview](https://user-images.githubusercontent.com/7883281/116791813-0edf7b00-aabd-11eb-90b3-11c604af34dc.png) +![Preview](https://user-images.githubusercontent.com/7883281/123561657-10ba7780-d7aa-11eb-8bbe-dcec348bb28c.png) diff --git a/etc/compress.lua b/etc/compress.lua new file mode 100644 index 0000000..a33b91f --- /dev/null +++ b/etc/compress.lua @@ -0,0 +1,319 @@ +local fmt, insert = string.format, table.insert + +local wood_types = { + "acacia_wood", "aspen_wood", "junglewood", "pine_wood", +} + +local material_tools = { + "bronze", "diamond", "mese", "stone", "wood", +} + +local material_stairs = { + "acacia_wood", "aspen_wood", "brick", "bronzeblock", "cobble", "copperblock", + "desert_cobble", "desert_sandstone", "desert_sandstone_block", "desert_sandstone_brick", + "desert_stone", "desert_stone_block", "desert_stonebrick", + "glass", "goldblock", "ice", "junglewood", "mossycobble", "obsidian", + "obsidian_block", "obsidian_glass", "obsidianbrick", "pine_wood", + "sandstone", "sandstone_block", "sandstonebrick", + "silver_sandstone", "silver_sandstone_block", "silver_sandstone_brick", + "snowblock", "steelblock", "stone", "stone_block", "stonebrick", + "straw", "tinblock", +} + +local colors = { + "black", "blue", "brown", "cyan", "dark_green", "dark_grey", "green", + "grey", "magenta", "orange", "pink", "red", "violet", "yellow", +} + +local to_compress = { + ["default:wood"] = { + replace = "wood", + by = wood_types, + }, + + ["default:fence_wood"] = { + replace = "wood", + by = wood_types, + }, + + ["default:fence_rail_wood"] = { + replace = "wood", + by = wood_types, + }, + + ["default:mese_post_light"] = { + replace = "mese_post_light", + by = { + "mese_post_light_acacia", + "mese_post_light_aspen_wood", + "mese_post_light_junglewood", + "mese_post_light_pine_wood", + } + }, + + ["doors:gate_wood_closed"] = { + replace = "wood", + by = wood_types, + }, + + ["wool:white"] = { + replace = "white", + by = colors + }, + + ["dye:white"] = { + replace = "white", + by = colors + }, + + ["default:axe_steel"] = { + replace = "steel", + by = material_tools + }, + + ["default:pick_steel"] = { + replace = "steel", + by = material_tools + }, + + ["default:shovel_steel"] = { + replace = "steel", + by = material_tools + }, + + ["default:sword_steel"] = { + replace = "steel", + by = material_tools + }, + + ["farming:hoe_steel"] = { + replace = "steel", + by = {"wood", "stone"} + }, + + ["stairs:slab_wood"] = { + replace = "wood", + by = material_stairs + }, + + ["stairs:stair_wood"] = { + replace = "wood", + by = material_stairs + }, + + ["stairs:stair_inner_wood"] = { + replace = "wood", + by = material_stairs + }, + + ["stairs:stair_outer_wood"] = { + replace = "wood", + by = material_stairs + }, + + ["walls:cobble"] = { + replace = "cobble", + by = {"desertcobble", "mossycobble"} + }, +} + +local circular_saw_names = { + {"micro", "_1"}, + {"panel", "_1"}, + {"micro", "_2"}, + {"panel", "_2"}, + {"micro", "_4"}, + {"panel", "_4"}, + {"micro", ""}, + {"panel", ""}, + + {"micro", "_12"}, + {"panel", "_12"}, + {"micro", "_14"}, + {"panel", "_14"}, + {"micro", "_15"}, + {"panel", "_15"}, + {"stair", "_outer"}, + {"stair", ""}, + + {"stair", "_inner"}, + {"slab", "_1"}, + {"slab", "_2"}, + {"slab", "_quarter"}, + {"slab", ""}, + {"slab", "_three_quarter"}, + {"slab", "_14"}, + {"slab", "_15"}, + + {"slab", "_two_sides"}, + {"slab", "_three_sides"}, + {"slab", "_three_sides_u"}, + {"stair", "_half"}, + {"stair", "_alt_1"}, + {"stair", "_alt_2"}, + {"stair", "_alt_4"}, + {"stair", "_alt"}, + {"stair", "_right_half"}, + + {"slope", ""}, + {"slope", "_half"}, + {"slope", "_half_raised"}, + {"slope", "_inner"}, + {"slope", "_inner_half"}, + {"slope", "_inner_half_raised"}, + {"slope", "_inner_cut"}, + {"slope", "_inner_cut_half"}, + + {"slope", "_inner_cut_half_raised"}, + {"slope", "_outer"}, + {"slope", "_outer_half"}, + {"slope", "_outer_half_raised"}, + {"slope", "_outer_cut"}, + {"slope", "_outer_cut_half"}, + {"slope", "_outer_cut_half_raised"}, + {"slope", "_cut"}, +} + +local moreblocks_nodes = { + "coal_stone", + "wood_tile", + "iron_checker", + "circle_stone_bricks", + "cobble_compressed", + "plankstone", + "clean_glass", + "split_stone_tile", + "all_faces_tree", + "dirt_compressed", + "coal_checker", + "clean_glow_glass", + "tar", + "clean_super_glow_glass", + "stone_tile", + "cactus_brick", + "super_glow_glass", + "desert_cobble_compressed", + "copperpatina", + "coal_stone_bricks", + "glow_glass", + "cactus_checker", + "all_faces_pine_tree", + "all_faces_aspen_tree", + "all_faces_acacia_tree", + "all_faces_jungle_tree", + "iron_stone", + "grey_bricks", + "wood_tile_left", + "wood_tile_down", + "wood_tile_center", + "wood_tile_right", + "wood_tile_full", + "checker_stone_tile", + "iron_glass", + "iron_stone_bricks", + "wood_tile_flipped", + "wood_tile_offset", + "coal_glass", + + "straw", + + "stone", + "stone_block", + "cobble", + "mossycobble", + "brick", + "sandstone", + "steelblock", + "goldblock", + "copperblock", + "bronzeblock", + "diamondblock", + "tinblock", + "desert_stone", + "desert_stone_block", + "desert_cobble", + "meselamp", + "glass", + "tree", + "wood", + "jungletree", + "junglewood", + "pine_tree", + "pine_wood", + "acacia_tree", + "acacia_wood", + "aspen_tree", + "aspen_wood", + "obsidian", + "obsidian_block", + "obsidianbrick", + "obsidian_glass", + "stonebrick", + "desert_stonebrick", + "sandstonebrick", + "silver_sandstone", + "silver_sandstone_brick", + "silver_sandstone_block", + "desert_sandstone", + "desert_sandstone_brick", + "desert_sandstone_block", + "sandstone_block", + "coral_skeleton", + "ice", +} + +local colors_moreblocks = table.copy(colors) +insert(colors_moreblocks, "white") + +local moreblocks_mods = { + wool = colors_moreblocks, + moreblocks = moreblocks_nodes, +} + +local t = {} + +for mod, v in pairs(moreblocks_mods) do +for _, nodename in ipairs(v) do + t[nodename] = {} + + for _, shape in ipairs(circular_saw_names) do + local to_add = true + + if shape[1] == "slope" and shape[2] == "" then + to_add = nil + end + + if to_add then + insert(t[nodename], fmt("%s_%s%s", shape[1], nodename, shape[2])) + end + end + + local slope_name = fmt("slope_%s", nodename) + + to_compress[fmt("%s:%s", mod, slope_name)] = { + replace = slope_name, + by = t[nodename] + } +end +end + +local compressed = {} + +for k, v in pairs(to_compress) do + compressed[k] = compressed[k] or {} + + for _, str in ipairs(v.by) do + local it = k:gsub(v.replace, str) + insert(compressed[k], it) + end +end + +local _compressed = {} + +for _, v in pairs(compressed) do +for _, v2 in ipairs(v) do + _compressed[v2] = true +end +end + +return compressed, _compressed diff --git a/etc/groups.lua b/etc/groups.lua new file mode 100644 index 0000000..ea126e9 --- /dev/null +++ b/etc/groups.lua @@ -0,0 +1,62 @@ +local S = core.get_translator "i3" + +local 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", +} + +return group_stereotypes, group_names diff --git a/etc/model_aliases.lua b/etc/model_aliases.lua new file mode 100644 index 0000000..30083e6 --- /dev/null +++ b/etc/model_aliases.lua @@ -0,0 +1,13 @@ +local 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"}, +} + +return model_alias diff --git a/etc/styles.lua b/etc/styles.lua new file mode 100644 index 0000000..da91599 --- /dev/null +++ b/etc/styles.lua @@ -0,0 +1,116 @@ +local fmt = string.format + +local PNG = { + bg = "i3_bg.png", + bg_full = "i3_bg_full.png", + bar = "i3_bar.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_small = "i3_tab_small.png", + tab_top = "i3_tab.png^\\[transformFY", + furnace_anim = "i3_furnace_anim.png", + bag = "i3_bag.png", + armor = "i3_armor.png", + awards = "i3_award.png", + skins = "i3_skin.png", + waypoints = "i3_waypoint.png", + teleport = "i3_teleport.png", + add = "i3_add.png", + refresh = "i3_refresh.png", + visible = "i3_visible.png^\\[brighten", + nonvisible = "i3_non_visible.png", + exit = "i3_exit.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_small_hover = "i3_tab_small_hover.png", + tab_hover_top = "i3_tab_hover.png^\\[transformFY", + bag_hover = "i3_bag_hover.png", + armor_hover = "i3_armor_hover.png", + awards_hover = "i3_award_hover.png", + skins_hover = "i3_skin_hover.png", + waypoints_hover = "i3_waypoint_hover.png", + teleport_hover = "i3_teleport.png^\\[brighten", + add_hover = "i3_add.png^\\[brighten", + refresh_hover = "i3_refresh.png^\\[brighten", + exit_hover = "i3_exit.png^\\[brighten", +} + +local styles = fmt([[ + style_type[field;border=false;bgcolor=transparent] + style_type[label,field;font_size=16] + style_type[button;border=false;content_offset=0] + style_type[image_button,item_image_button;border=false;sound=i3_click] + style_type[item_image_button;bgimg_hovered=%s] + + style[pagenum,no_item,no_rcp;font=bold;font_size=18] + style[exit;fgimg=%s;fgimg_hovered=%s;content_offset=0] + 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[waypoint_add;fgimg=%s;fgimg_hovered=%s;content_offset=0] + style[btn_bag,btn_armor,btn_skins;font=bold;font_size=18;content_offset=0;sound=i3_click] + style[craft_rcp,craft_usg;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.exit, PNG.exit_hover, +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, +PNG.add, PNG.add_hover) + +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]", +} + +return PNG, styles, fs_elements diff --git a/init.lua b/init.lua index 0e6af50..088248e 100644 --- a/init.lua +++ b/init.lua @@ -1,12 +1,12 @@ i3 = {} +local modpath = core.get_modpath "i3" local storage = core.get_mod_storage() local slz, dslz = core.serialize, core.deserialize local pdata = dslz(storage:get_string "pdata") or {} -- Caches local init_items = {} -local searches = {} local recipes_cache = {} local usages_cache = {} local fuel_cache = {} @@ -15,7 +15,13 @@ local toolrepair local tabs = {} +local model_aliases = loadfile(modpath .. "/etc/model_aliases.lua")() +local PNG, styles, fs_elements = loadfile(modpath .. "/etc/styles.lua")() +local compress_groups, compressed = loadfile(modpath .. "/etc/compress.lua")() +local group_stereotypes, group_names = loadfile(modpath .. "/etc/groups.lua")() + local progressive_mode = core.settings:get_bool "i3_progressive_mode" and not(core.is_creative_enabled()) +local item_compression = core.settings:get_bool "i3_item_compression" local damage_enabled = core.settings:get_bool "enable_damage" local __3darmor, __skinsdb, __awards @@ -25,6 +31,9 @@ local __unified_inventory, old_unified_inventory_fn local http = core.request_http_api() local singleplayer = core.is_singleplayer() +local after = core.after +local clr = core.colorize + local reg_items = core.registered_items local reg_nodes = core.registered_nodes local reg_craftitems = core.registered_craftitems @@ -32,29 +41,9 @@ 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 get_inv = core.get_inventory -local chat_send = core.chat_send_player -local show_formspec = core.show_formspec -local pos_to_string = core.pos_to_string 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 on_leaveplayer = core.register_on_leaveplayer -local on_mods_loaded = core.register_on_mods_loaded -local get_player_info = core.get_player_information +local translate = core.get_translated_string 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" @@ -109,117 +98,6 @@ local SUBCAT = { "waypoints", } -local PNG = { - bg = "i3_bg.png", - bg_full = "i3_bg_full.png", - bar = "i3_bar.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", - bag = "i3_bag.png", - armor = "i3_armor.png", - awards = "i3_award.png", - skins = "i3_skin.png", - waypoints = "i3_waypoint.png", - teleport = "i3_teleport.png", - add = "i3_add.png", - refresh = "i3_refresh.png", - visible = "i3_visible.png^\\[brighten", - nonvisible = "i3_non_visible.png", - exit = "i3_exit.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", - bag_hover = "i3_bag_hover.png", - armor_hover = "i3_armor_hover.png", - awards_hover = "i3_award_hover.png", - skins_hover = "i3_skin_hover.png", - waypoints_hover = "i3_waypoint_hover.png", - teleport_hover = "i3_teleport.png^\\[brighten", - add_hover = "i3_add.png^\\[brighten", - refresh_hover = "i3_refresh.png^\\[brighten", - exit_hover = "i3_exit.png^\\[brighten", -} - -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[field;border=false;bgcolor=transparent] - style_type[label,field;font_size=16] - style_type[button;border=false;content_offset=0] - style_type[image_button,item_image_button;border=false;sound=i3_click] - style_type[item_image_button;bgimg_hovered=%s] - - style[pagenum,no_item,no_rcp;font=bold;font_size=18] - style[exit;fgimg=%s;fgimg_hovered=%s;content_offset=0] - 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[waypoint_add;fgimg=%s;fgimg_hovered=%s;content_offset=0] - style[btn_bag,btn_armor,btn_skins;font=bold;font_size=18;content_offset=0;sound=i3_click] - style[craft_rcp,craft_usg;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.exit, PNG.exit_hover, -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, -PNG.add, PNG.add_hover) - local function get_lang_code(info) return info and info.lang_code end @@ -233,92 +111,25 @@ local function outdated(name) PNG.book, "Your Minetest client is outdated.\nGet the latest version on minetest.net to use i3") - show_formspec(name, "i3", fs) + core.show_formspec(name, "i3", fs) end local old_is_creative_enabled = core.is_creative_enabled function core.is_creative_enabled(name) + if name == "" then + return old_is_creative_enabled(name) + end + 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) + return core.log("error", str) end local function msg(name, str) - return chat_send(name, sprintf("[i3] %s", str)) + return core.chat_send_player(name, sprintf("[i3] %s", str)) end local function is_num(x) @@ -354,7 +165,7 @@ local function fmt(elem, ...) end local function clean_name(item) - if sub(item, 1, 1) == ":" or sub(item, 1, 1) == " " then + if sub(item, 1, 1) == ":" or sub(item, 1, 1) == " " or sub(item, 1, 1) == "_" then item = sub(item, 2) end @@ -500,7 +311,7 @@ function i3.register_craft(def) http.fetch({url = def.url}, function(result) if result.succeeded then - local t = parse_json(result.data) + local t = core.parse_json(result.data) if is_table(t) then return i3.register_craft(t) end @@ -615,7 +426,7 @@ function i3.set_recipe_filter(name, f) recipe_filters = {[name] = f} end -function i3.delete_recipe_filter(name) +function i3.remove_recipe_filter(name) recipe_filters[name] = nil end @@ -651,6 +462,14 @@ function i3.get_search_filters() return search_filters end +local function compression_active(data) + return item_compression and not next(recipe_filters) and data.filter == "" +end + +local function compressible(item, data) + return compression_active(data) and compress_groups[item] +end + local function weird_desc(str) return not true_str(str) or find(str, "\n") or not find(str, "%u") end @@ -734,7 +553,7 @@ local function get_filtered_items(player, data) end local function get_burntime(item) - return get_craft_result{method = "fuel", items = {item}}.time + return core.get_craft_result{method = "fuel", items = {item}}.time end local function cache_fuel(item) @@ -754,19 +573,22 @@ local function show_item(def) def.description and def.description ~= "" end +local function reset_compression(data) + data.alt_items = nil + data.expand = "" +end + local function search(data) + reset_compression(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 + search_filter = search_filter:trim() + for filter_name, values in gmatch(filter, sub(opt, 6)) do if search_filters[filter_name] then values = split(values, ",") @@ -800,7 +622,18 @@ local function search(data) end end else - to_add = find(search_in, filter, 1, true) + local ok = true + + for keyword in gmatch(filter, "%S+") do + if not find(search_in, keyword, 1, true) then + ok = nil + break + end + end + + if ok then + to_add = true + end end if to_add then @@ -809,16 +642,36 @@ local function search(data) 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 + data.items = filtered_list +end + +local function sort_by_category(data) + reset_compression(data) + local items = data.items_raw + + if data.filter ~= "" then + search(data) + items = data.items + end + + local new = {} + + for i = 1, #items do + local item = items[i] + local to_add = true + + if data.current_itab == 2 then + to_add = reg_nodes[item] + elseif data.current_itab == 3 then + to_add = reg_craftitems[item] or reg_tools[item] + end + + if to_add then + new[#new + 1] = item end end - data.items = filtered_list + data.items = new end local function get_item_usages(item, recipe, added) @@ -923,7 +776,7 @@ local function cache_drops(name, drop) end local function cache_recipes(item) - local recipes = get_all_recipes(item) + local recipes = core.get_all_craft_recipes(item) if replacements[item] then local _recipes = {} @@ -978,15 +831,16 @@ 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 stereotype = group_stereotypes[group] local def = reg_items[stereotype] - if def and show_item(def) then + if 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 @@ -1241,36 +1095,84 @@ local function select_item(player, name, data, _f) end end - if not item then - return - elseif sub(item, 1, 1) == "_" then - item = sub(item, 2) - elseif sub(item, 1, 6) == "group|" then - item = match(item, "([%w:_]+)$") + if not item then return end + + if compressible(item, data) then + local idx + + for i = 1, #data.items do + local it = data.items[i] + if it == item then + idx = i + break + end + end + + if data.expand ~= "" then + data.alt_items = nil + + if item == data.expand then + data.expand = nil + return + end + end + + if idx and item ~= data.expand then + data.alt_items = copy(data.items) + data.expand = item + + if compress_groups[item] then + local items = copy(compress_groups[item]) + insert(items, fmt("_%s", item)) + + sort(items, function(a, b) + if a:sub(1, 1) == "_" then + a = a:sub(2) + end + + return a < b + end) + + local i = 1 + + for _, v in ipairs(items) do + if show_item(reg_items[clean_name(v)]) then + insert(data.alt_items, idx + i, v) + i = i + 1 + end + end + end + end + else + if 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) + return get_stack(player, stack, clr("#ff0", fmt("%u x %s", stackmax, get_desc(item)))) + 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 - - 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) - return get_stack(player, stack, clr("#ff0", fmt("%u x %s", stackmax, get_desc(item)))) - 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) @@ -1421,7 +1323,11 @@ local function get_output_fs(fs, data, rcp, is_recipe, shapeless, right, btn_siz fs(fmt("list[detached:i3_output_%s;main;%f,%f;1,1;]", rcp_usg, X + 0.11, Y)) fs("button", X + 0.11, Y, ITEM_BTN_SIZE, ITEM_BTN_SIZE, _name, "") - local inv = get_inv {type = "detached", name = fmt("i3_output_%s", rcp_usg)} + local inv = core.get_inventory { + type = "detached", + name = fmt("i3_output_%s", rcp_usg) + } + inv:set_stack("main", 1, item) pos = {x = X + 0.11, y = Y} else @@ -1708,7 +1614,7 @@ local function get_header(fs, data) fs("style_type[label;font=normal;font_size=16]") local def = reg_items[data.query_item] - local model_alias = i3.model_alias[data.query_item] + local model_alias = model_aliases[data.query_item] if def.drawtype == "mesh" or model_alias then get_model_fs(fs, data, def, model_alias) @@ -1788,9 +1694,22 @@ local function get_rcp_extra(player, fs, data, panel, is_recipe, is_usage) fs("container_end[]") end -local function get_items_fs(fs, data, extend) - local rows = 8 - local lines = extend and 12 or 9 +local function get_items_fs(fs, data, full_height) + if compression_active(data) then + local new = {} + + for i = 1, #data.items do + local item = data.items[i] + if not compressed[item] then + new[#new + 1] = item + end + end + + data.items = new + end + + local items = data.alt_items or data.items + local rows, lines = 8, 12 local ipp = rows * lines local size = 0.85 @@ -1799,17 +1718,17 @@ local function get_items_fs(fs, data, extend) fmt("field[%f,0.2;2.95,0.6;filter;;%s]", data.inv_width + 0.35, ESC(data.filter)), "field_close_on_enter[filter;false]") - fs("image_button", data.inv_width + 3.35, 0.35, 0.3, 0.3, "", "cancel", "") + fs("image_button", data.inv_width + 3.35, 0.35, 0.3, 0.3, "", "cancel", "") fs("image_button", data.inv_width + 3.85, 0.32, 0.35, 0.35, "", "search", "") - fs("image_button", data.inv_width + 5.27, 0.3, 0.35, 0.35, "", "prev_page", "") - fs("image_button", data.inv_width + 7.45, 0.3, 0.35, 0.35, "", "next_page", "") + fs("image_button", data.inv_width + 5.27, 0.3, 0.35, 0.35, "", "prev_page", "") + fs("image_button", data.inv_width + 7.45, 0.3, 0.35, 0.35, "", "next_page", "") - data.pagemax = max(1, ceil(#data.items / ipp)) + data.pagemax = max(1, ceil(#items / ipp)) fs("button", data.inv_width + 5.6, 0.14, 1.88, 0.7, "pagenum", fmt("%s / %u", clr("#ff0", data.pagenum), data.pagemax)) - if #data.items == 0 then + if #items == 0 then local lbl = ES"No item to show" if next(recipe_filters) and #init_items > 0 and data.filter == "" then @@ -1817,21 +1736,49 @@ local function get_items_fs(fs, data, extend) end fs("button", data.inv_width + 0.1, 3, 8, 1, "no_item", lbl) + else + local first_item = (data.pagenum - 1) * ipp + + for i = first_item, first_item + ipp - 1 do + local item = items[i + 1] + if not item then break end + + local _compressed = item:sub(1, 1) == "_" + local name = _compressed and item:sub(2) or item + + local X = i % rows + X = X - (X * 0.045) + data.inv_width + 0.28 + + local Y = round((i % ipp - X) / rows + 1, 0) + Y = Y - (Y * 0.085) + 0.95 + + fs[#fs + 1] = fmt("item_image_button", X, Y, size, size, name, item, "") + + if compressible(item, data) then + local expand = data.expand == name + + fs(fmt("tooltip[%s;%s]", item, expand and ES"Click to hide" or ES"Click to expand")) + fs("style_type[label;font=bold;font_size=20]") + fs("label", X + 0.65, Y + 0.7, expand and "-" or "+") + fs("style_type[label;font=normal;font_size=16]") + end + end end - local first_item = (data.pagenum - 1) * ipp + local _tabs = {"All", "Nodes", "Items"} + local tab_len, tab_hgh = 1.8, 0.5 - for i = first_item, first_item + ipp - 1 do - local item = data.items[i + 1] - if not item then break end + for i, title in ipairs(_tabs) do + local selected = i == data.current_itab - local X = i % rows - X = X - (X * 0.045) + data.inv_width + 0.28 + 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 PNG.tab_small_hover or PNG.tab_small, + PNG.tab_small_hover, selected and "#fff" or "#ddd")) - local Y = round((i % ipp - X) / rows + 1, 0) - Y = Y - (Y * (extend and 0.085 or 0.035)) + 0.95 - - fs[#fs + 1] = fmt("item_image_button", X, Y, size, size, item, item, "") + fs("style_type[image_button:hovered;textcolor=#fff]") + fs("image_button", (data.inv_width - 0.65) + (i * (tab_len + 0.1)), + full_height, tab_len, tab_hgh, "", fmt("itab_%u", i), title) end end @@ -1854,21 +1801,15 @@ end local function get_panels(player, data, fs, full_height) local _title = {name = "title", height = 1.4} local _favs = {name = "favs", height = 2.23} - local _items = {name = "items", height = 9.69} + local _items = {name = "items", height = full_height} local _recipes = {name = "recipes", rcp = data.recipes, height = 4.045} local _usages = {name = "usages", rcp = data.usages, height = 4.045} - local panels, extend + local panels if data.query_item then panels = {_title, _recipes, _usages, _favs} else - panels = {_items, _favs} - - if #data.favs == 0 then - extend = true - remove(panels, 2) - _items.height = full_height - end + panels = {_items} end for idx = 1, #panels do @@ -1888,7 +1829,7 @@ local function get_panels(player, data, fs, full_height) if is_recipe or is_usage then get_rcp_extra(player, fs, data, panel, is_recipe, is_usage) elseif panel.name == "items" then - get_items_fs(fs, data, extend) + get_items_fs(fs, data, full_height) elseif panel.name == "title" then get_header(fs, data) elseif panel.name == "favs" then @@ -2008,7 +1949,7 @@ local function get_waypoint_fs(fs, data, player, yextra, ctn_len) fs("tooltip", 0, y, ctn_len - 2.5, 0.65, fmt("Name: %s\nPosition:%s", clr("#ff0", v.name), - pos_to_string(v.pos, 0):sub(2,-2):gsub("(%-*%d+)", clr("#ff0", " %1")))) + core.pos_to_string(v.pos, 0):sub(2,-2):gsub("(%-*%d+)", clr("#ff0", " %1")))) local del = fmt("waypoint_%u_delete", i) fs(fmt("style[%s;fgimg=%s;fgimg_hovered=%s;content_offset=0]", del, PNG.trash, PNG.trash_hover)) @@ -2287,9 +2228,9 @@ function i3.get_tabs() return tabs end -function i3.delete_tab(tabname) +function i3.remove_tab(tabname) if not true_str(tabname) then - return err "i3.delete_tab: tab name missing" + return err "i3.remove_tab: tab name missing" end for i, def in ipairs(tabs) do @@ -2356,15 +2297,18 @@ local function init_data(player, info) data.favs = {} data.export_counts = {} data.current_tab = 1 + data.current_itab = 1 data.subcat = 1 data.scrbar_inv = 0 data.lang_code = get_lang_code(info) + data.fs_version = info.formspec_version after(0, set_fs, player) end local function reset_data(data) data.filter = "" + data.expand = "" data.pagenum = 1 data.rnum = 1 data.unum = 1 @@ -2375,7 +2319,12 @@ local function reset_data(data) data.usages = nil data.export_rcp = nil data.export_usg = nil + data.alt_items = nil data.items = data.items_raw + + if data.current_itab > 1 then + sort_by_category(data) + end end local function rcp_fields(player, data, fields) @@ -2402,6 +2351,10 @@ local function rcp_fields(player, data, fields) search(data) + if data.current_itab > 1 then + sort_by_category(data) + end + 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) @@ -2515,9 +2468,9 @@ local function get_inventory_fs(player, data, fs) local textures = concat(t, ","):gsub("!", ",") --fs("style[player_model;bgcolor=black]") - fs("model", 0.2, 0.2, armor_skin and 4 or 3.4, armor_skin and ctn_hgt or 6.1, + fs("model", 0.2, 0.2, armor_skin and 4 or 3.4, ctn_hgt, "player_model", props.mesh, textures, "0,-150", "false", "false", - fmt("%u,%u", anim.x, anim.y)) + fmt("%u,%u%s", anim.x, anim.y, data.fs_version >= 5 and ";30" or "")) else local size = 2.5 fs("image", 0.7, 0.2, size, size * props.visual_size.y, props.textures[1]) @@ -2565,9 +2518,7 @@ local function get_inventory_fs(player, data, fs) (max_val * 4) / 12, max_val, 9.8, ctn_hgt, data.scrbar_inv)) fs(fmt("scroll_container[3.9,0.2;%f,%f;scrbar_inv;vertical]", ctn_len, ctn_hgt)) - - get_ctn_content(fs, data, player, yoffset, ctn_len, award_list, awards_unlocked, award_list_nb) - + get_ctn_content(fs, data, player, yoffset, ctn_len, award_list, awards_unlocked, award_list_nb) fs("scroll_container_end[]") local btn = { @@ -2812,18 +2763,6 @@ i3.add_search_filter("groups", function(item, groups) 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. @@ -2943,14 +2882,14 @@ local function get_init_items() usages = usages_cache, } - http.fetch_async{ + http.fetch_async { url = i3.export_url, - post_data = write_json(post_data), + post_data = core.write_json(post_data), } end end -on_mods_loaded(function() +core.register_on_mods_loaded(function() get_init_items() __sfinv = rawget(_G, "sfinv") @@ -2974,7 +2913,7 @@ local function init_backpack(player) local data = pdata[name] local inv = player:get_inventory() - inv:set_size("main", INV_SIZE) + inv:set_size("main", data.bag_size and BAG_SIZES[data.bag_size] or INV_SIZE) data.bag = create_inventory(fmt("%s_backpack", name), { allow_put = function(_inv, listname, _, stack) @@ -3015,7 +2954,6 @@ local function init_backpack(player) if data.bag_size then data.bag:set_stack("main", 1, fmt("i3:bag_%s", data.bag_size)) - inv:set_size("main", BAG_SIZES[data.bag_size]) end end @@ -3040,11 +2978,11 @@ local function init_waypoints(player) end end -on_joinplayer(function(player) +core.register_on_joinplayer(function(player) local name = player:get_player_name() - local info = get_player_info(name) + local info = core.get_player_information and core.get_player_information(name) - if get_formspec_version(info) < MIN_FORMSPEC_VERSION then + if not info or get_formspec_version(info) < MIN_FORMSPEC_VERSION then if __sfinv then sfinv.set_player_inventory_formspec = old_sfinv_fn sfinv.enabled = true @@ -3058,6 +2996,8 @@ on_joinplayer(function(player) end end + pdata[name] = nil + return outdated(name) end @@ -3111,12 +3051,12 @@ local function save_data(player_name) storage:set_string("pdata", slz(_pdata)) end -on_leaveplayer(function(player) +core.register_on_leaveplayer(function(player) local name = player:get_player_name() save_data(name) end) -on_shutdown(save_data) +core.register_on_shutdown(save_data) local function routine() save_data() @@ -3125,7 +3065,7 @@ end after(SAVE_INTERVAL, routine) -on_receive_fields(function(player, formname, fields) +core.register_on_player_receive_fields(function(player, formname, fields) if formname ~= "" then return false end @@ -3139,6 +3079,10 @@ on_receive_fields(function(player, formname, fields) local tabname = sub(f, 5) set_tab(player, tabname) break + elseif sub(f, 1, 5) == "itab_" then + data.pagenum = 1 + data.current_itab = tonum(f:sub(-1)) + sort_by_category(data) end end @@ -3170,6 +3114,7 @@ if progressive_mode then if is_group(item) then local groups = extract_groups(item) + for i = 1, inv_items_size do local def = reg_items[inv_items[i]] @@ -3236,6 +3181,7 @@ if progressive_mode then 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 @@ -3326,7 +3272,8 @@ if progressive_mode then -- 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() + local players = core.get_connected_players() + for i = 1, #players do local player = players[i] local name = player:get_player_name() @@ -3346,6 +3293,8 @@ if progressive_mode then end data.items_raw = items + data.current_itab = 1 + search(data) set_fs(player) end @@ -3356,8 +3305,9 @@ if progressive_mode then poll_new_items() - globalstep(function() - local players = get_players() + core.register_globalstep(function() + local players = core.get_connected_players() + for i = 1, #players do local player = players[i] local name = player:get_player_name() @@ -3371,7 +3321,7 @@ if progressive_mode then i3.add_recipe_filter("Default progressive filter", progressive_filter) - on_joinplayer(function(player) + core.register_on_joinplayer(function(player) local name = player:get_player_name() local data = pdata[name] @@ -3417,5 +3367,5 @@ for size, rcp in pairs(bag_recipes) do 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") +--dofile(modpath .. "/tests/test_tabs.lua") +--dofile(modpath .. "/tests/test_custom_recipes.lua") diff --git a/settingtypes.txt b/settingtypes.txt index a97f54a..42bc83e 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -1,2 +1,5 @@ # 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 + +# Regroup the items of the same type in the item list. +i3_item_compression (Regroup items of the same type) bool true diff --git a/test_custom_recipes.lua b/tests/test_custom_recipes.lua similarity index 100% rename from test_custom_recipes.lua rename to tests/test_custom_recipes.lua diff --git a/test_online_recipe.json b/tests/test_online_recipe.json similarity index 100% rename from test_online_recipe.json rename to tests/test_online_recipe.json diff --git a/test_tabs.lua b/tests/test_tabs.lua similarity index 100% rename from test_tabs.lua rename to tests/test_tabs.lua diff --git a/textures/i3_tab_small.png b/textures/i3_tab_small.png new file mode 100644 index 0000000..2a2fc0b Binary files /dev/null and b/textures/i3_tab_small.png differ diff --git a/textures/i3_tab_small_hover.png b/textures/i3_tab_small_hover.png new file mode 100644 index 0000000..102c027 Binary files /dev/null and b/textures/i3_tab_small_hover.png differ