pax_global_header00006660000000000000000000000064140103163300014501gustar00rootroot0000000000000052 comment=f3799989435d1f7a6e4307572dafd93a79410f15 craftguide-2.0/000077500000000000000000000000001401031633000134575ustar00rootroot00000000000000craftguide-2.0/.gitignore000066400000000000000000000004101401031633000154420ustar00rootroot00000000000000## Files related to minetest development cycle /*.patch # GNU Patch reject file *.rej ## Editors and Development environments *~ *.swp *.bak* *.orig # Vim *.vim # Kate .*.kate-swp .swp.* # Eclipse (LDT) .project .settings/ .buildpath .metadata # Idea IDE .idea/* craftguide-2.0/.luacheckrc000066400000000000000000000002331401031633000155620ustar00rootroot00000000000000allow_defined_top = true read_globals = { "minetest", "default", "vector", "string", "table", "ItemStack", } globals = { "craftguide", "core", } craftguide-2.0/API.md000066400000000000000000000122731401031633000144170ustar00rootroot00000000000000## API ### Custom recipes Custom recipes are nonconventional crafts outside the main crafting grid. They can be registered in-game dynamically and have a size beyond 3x3 items. **Note:** the registration format differs from the default registration format in everything. The width is automatically calculated depending where you place the commas. Look at the examples attentively. #### Registering a custom crafting type (example) ```Lua craftguide.register_craft_type("digging", { description = "Digging", icon = "default_tool_steelpick.png", }) ``` #### Registering a custom crafting recipe (examples) ```Lua craftguide.register_craft({ type = "digging", result = "default:cobble 2", items = {"default:stone"}, }) ``` ```Lua craftguide.register_craft({ result = "default:cobble 16", items = { "default:stone, default:stone, default:stone", "default:stone, , default:stone", "default:stone, default:stone, default:stone", } }) ``` Recipes can be registered in a Minecraft-like way: ```Lua craftguide.register_craft({ grid = { "X #", " ## ", "X#X#", "X X", }, key = { ['#'] = "default:wood", ['X'] = "default:glass", }, result = "default:mese 3", }) ``` Multiples recipes can also be registered: ```Lua craftguide.register_craft({ { result = "default:mese", items = { "default:mese_crystal, default:mese_crystal", "default:mese_crystal, default:mese_crystal", } }, big = { result = "default:mese 4", items = { "default:mese_crystal, default:mese_crystal", "default:mese_crystal, default:mese_crystal", "default:mese_crystal, default:mese_crystal", "default:mese_crystal, default:mese_crystal", } }, }) ``` Recipes can be registered from a given URL containing a JSON file (HTTP support is required¹): ```Lua craftguide.register_craft({ url = "https://raw.githubusercontent.com/minetest-mods/craftguide/master/test.json" }) ``` --- ### Recipe filters Recipe filters can be used to filter the recipes shown to players. Progressive mode is implemented as a recipe filter. #### `craftguide.add_recipe_filter(name, function(recipes, player))` Adds a recipe filter with the given `name`. The filter function returns the recipes to be displayed, given the available recipes and an `ObjectRef` to the user. Each recipe is a table of the form returned by `minetest.get_craft_recipe`. Example function to hide recipes for items from a mod called "secretstuff": ```lua craftguide.add_recipe_filter("Hide secretstuff", function(recipes) local filtered = {} for _, recipe in ipairs(recipes) do if recipe.output:sub(1,12) ~= "secretstuff:" then filtered[#filtered + 1] = recipe end end return filtered end) ``` #### `craftguide.set_recipe_filter(name, function(recipe, player))` Removes all recipe filters and adds a new one. #### `craftguide.remove_recipe_filter(name)` Removes the recipe filter with the given `name`. #### `craftguide.get_recipe_filters()` Returns a map of recipe filters, indexed by name. --- ### Search filters Search filters are used to perform specific searches inside the search field. You can cumulate several filters to perform a specific search. They can be used like so: ` +=,,<...>` Example usages: - `+groups=cracky,crumbly`: search for groups `cracky` and `crumbly` in all items. - `wood +groups=flammable +type=node`: search for group `flammable` amongst items which contain `wood` in their names AND have a `node` drawtype. Notes: - If `optional_name` is omitted, the search filter will apply to all items, without pre-filtering. - The `groups` and `type` filters are currently implemented by default. #### `craftguide.add_search_filter(name, function(item, values))` Adds a search filter with the given `name`. The search function must return a boolean value (whether the given item should be listed or not). Example function sorting items by drawtype: ```lua craftguide.add_search_filter("type", function(item, drawtype) if drawtype == "node" then return reg_nodes[item] elseif drawtype == "item" then return reg_craftitems[item] elseif drawtype == "tool" then return reg_tools[item] end end) ``` #### `craftguide.remove_search_filter(name)` Removes the search filter with the given `name`. #### `craftguide.get_search_filters()` Returns a map of search filters, indexed by name. --- ### Miscellaneous #### `craftguide.show(player_name, item, show_usages)` Opens the Crafting Guide with the current filter applied. * `player_name`: string param. * `item`: optional, string param. If set, this item is pre-selected. If the item does not exist or has no recipe, use the player's previous selection. By default, player's previous selection is used * `show_usages`: optional, boolean param. If true, show item usages. #### `craftguide.group_stereotypes` This is the table indexing the item groups by stereotypes. You can add a stereotype like so: ```Lua craftguide.group_stereotypes.radioactive = "mod:item" ``` #### `craftguide.export_url` If set, the mod will export all the cached recipes and usages in a JSON format to the given URL (HTTP support is required¹). --- **¹** Add `craftguide` to the `secure.http_mods` or `secure.trusted_mods` setting in `minetest.conf`. craftguide-2.0/README.md000066400000000000000000000022601401031633000147360ustar00rootroot00000000000000# ![Preview1](http://i.imgur.com/fIPNYkb.png) Crafting Guide [![ContentDB](https://content.minetest.net/packages/jp/craftguide/shields/title/)](https://content.minetest.net/packages/jp/craftguide/) [![ContentDB](https://content.minetest.net/packages/jp/craftguide/shields/downloads/)](https://content.minetest.net/packages/jp/craftguide/) #### `craftguide` is the most comprehensive crafting guide on Minetest. #### Consult the [Minetest Wiki](http://wiki.minetest.net/Crafting_guide) for more details. This crafting guide is a blue book named *"Crafting Guide"* or a wooden sign. This crafting guide features a **progressive mode**. This mode is a Terraria-like system that shows recipes you can craft from items you ever had in your inventory. To enable it: `craftguide_progressive_mode = true` in `minetest.conf`. Use the command `/craft` to show the recipe(s) of the pointed node. For developers, `craftguide` also has a [modding API](https://github.com/minetest-mods/craftguide/blob/master/API.md). Love this mod? Donations are appreciated: https://www.paypal.me/jpg84240 ![Preview2](https://user-images.githubusercontent.com/7883281/103141399-113b5e00-46f4-11eb-96cd-0167972ca1cf.png) craftguide-2.0/init.lua000066400000000000000000001677121401031633000151430ustar00rootroot00000000000000craftguide = {} -- Caches local pdata = {} local init_items = {} local searches = {} local recipes_cache = {} local usages_cache = {} local fuel_cache = {} local replacements = {fuel = {}} local toolrepair local progressive_mode = core.settings:get_bool "craftguide_progressive_mode" local http = core.request_http_api() local singleplayer = core.is_singleplayer() local reg_items = core.registered_items local reg_nodes = core.registered_nodes local reg_craftitems = core.registered_craftitems local reg_tools = core.registered_tools local reg_entities = core.registered_entities local reg_aliases = core.registered_aliases local log = core.log local after = core.after local clr = core.colorize local parse_json = core.parse_json local write_json = core.write_json local chat_send = core.chat_send_player local show_formspec = core.show_formspec local 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 register_command = core.register_chatcommand local get_player_by_name = core.get_player_by_name local slz, dslz = core.serialize, core.deserialize local on_mods_loaded = core.register_on_mods_loaded local on_leaveplayer = core.register_on_leaveplayer local get_player_info = core.get_player_information local on_receive_fields = core.register_on_player_receive_fields local ESC = core.formspec_escape local S = core.get_translator "craftguide" local ES = function(...) return ESC(S(...)) end local maxn, sort, concat, copy, insert, remove = table.maxn, table.sort, table.concat, table.copy, table.insert, table.remove local sprintf, find, gmatch, match, sub, split, upper, lower = string.format, string.find, string.gmatch, string.match, string.sub, string.split, string.upper, string.lower local min, max, floor, ceil, abs = math.min, math.max, math.floor, math.ceil, math.abs local pairs, ipairs, next, type, setmetatable, tonum = pairs, ipairs, next, type, setmetatable, tonumber local vec_add, vec_mul = vector.add, vector.multiply local ROWS = 9 local LINES = 10 local IPP = ROWS * LINES local MAX_FAVS = 6 local ITEM_BTN_SIZE = 1.1 -- Progressive mode local POLL_FREQ = 0.25 local HUD_TIMER_MAX = 1.5 local MIN_FORMSPEC_VERSION = 4 local PNG = { bg = "craftguide_bg.png", bg_full = "craftguide_bg_full.png", search = "craftguide_search.png", prev = "craftguide_next.png^\\[transformFX", next = "craftguide_next.png", arrow = "craftguide_arrow.png", trash = "craftguide_trash.png", sort_az = "craftguide_sort.png", sort_za = "craftguide_sort2.png", compress = "craftguide_compress.png", fire = "craftguide_fire.png", fire_anim = "craftguide_fire_anim.png", book = "craftguide_book.png", sign = "craftguide_sign.png", cancel = "craftguide_cancel.png", export = "craftguide_export.png", slot = "craftguide_slot.png", tab = "craftguide_tab.png", furnace_anim = "craftguide_furnace_anim.png", cancel_hover = "craftguide_cancel.png^\\[brighten", search_hover = "craftguide_search.png^\\[brighten", export_hover = "craftguide_export.png^\\[brighten", trash_hover = "craftguide_trash.png^\\[brighten", compress_hover = "craftguide_compress.png^\\[brighten", sort_az_hover = "craftguide_sort.png^\\[brighten", sort_za_hover = "craftguide_sort2.png^\\[brighten", prev_hover = "craftguide_next_hover.png^\\[transformFX", next_hover = "craftguide_next_hover.png", tab_hover = "craftguide_tab_hover.png", } local fs_elements = { box = "box[%f,%f;%f,%f;%s]", label = "label[%f,%f;%s]", image = "image[%f,%f;%f,%f;%s]", button = "button[%f,%f;%f,%f;%s;%s]", tooltip = "tooltip[%f,%f;%f,%f;%s]", item_image = "item_image[%f,%f;%f,%f;%s]", bg9 = "background9[%f,%f;%f,%f;%s;false;%u]", model = "model[%f,%f;%f,%f;%s;%s;%s;%s;%s;true;%s]", image_button = "image_button[%f,%f;%f,%f;%s;%s;%s]", animated_image = "animated_image[%f,%f;%f,%f;;%s;%u;%u]", scrollbar = "scrollbar[%f,%f;%f,%f;horizontal;%s;%u]", item_image_button = "item_image_button[%f,%f;%f,%f;%s;%s;%s]", } local styles = sprintf([[ style_type[label,field;font_size=16] style_type[image_button;border=false;sound=craftguide_click] style_type[item_image_button;border=false;bgimg_hovered=%s;sound=craftguide_click] style[filter;border=false] style[cancel;fgimg=%s;fgimg_hovered=%s;content_offset=0] style[search;fgimg=%s;fgimg_hovered=%s;content_offset=0] style[trash;fgimg=%s;fgimg_hovered=%s;content_offset=0;sound=craftguide_delete] style[sort_az;fgimg=%s;fgimg_hovered=%s;content_offset=0] style[sort_za;fgimg=%s;fgimg_hovered=%s;content_offset=0] style[compress;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[guide_mode,inv_mode;fgimg_hovered=%s;noclip=true;content_offset=0;sound=craftguide_tab] style[pagenum,no_item,no_rcp;border=false;font=bold;font_size=18;content_offset=0] style[craft_rcp,craft_usg;border=false;noclip=true;font_size=16;sound=craftguide_craft; bgimg=craftguide_btn9.png;bgimg_hovered=craftguide_btn9_hovered.png; bgimg_pressed=craftguide_btn9_pressed.png;bgimg_middle=4,6] ]], PNG.slot, PNG.cancel, PNG.cancel_hover, PNG.search, PNG.search_hover, PNG.trash, PNG.trash_hover, PNG.sort_az, PNG.sort_az_hover, PNG.sort_za, PNG.sort_za_hover, PNG.compress, PNG.compress_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.tab_hover) local function get_lang_code(info) return info and info.lang_code end local function get_formspec_version(info) return info and info.formspec_version or 1 end local function outdated(name) local fs = sprintf([[ size[7.1,1.3] image[0,0;1,1;%s] label[1,0;%s] button_exit[3.1,0.8;1,1;;OK] ]], PNG.book, "Your Minetest client is outdated.\n" .. "Get the latest version on minetest.net to use the Crafting Guide.") return show_formspec(name, "craftguide", fs) end craftguide.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", } craftguide.model_alias = { ["boats:boat"] = {name = "boats:boat", drawtype = "entity"}, ["carts:cart"] = {name = "carts:cart", drawtype = "entity", frames = "0,0"}, ["default:chest"] = {name = "default:chest_open"}, ["default:chest_locked"] = {name = "default:chest_locked_open"}, ["doors:door_wood"] = {name = "doors:door_wood_a"}, ["doors:door_glass"] = {name = "doors:door_glass_a"}, ["doors:door_obsidian_glass"] = {name = "doors:door_obsidian_glass_a"}, ["doors:door_steel"] = {name = "doors:door_steel_a"}, ["xpanes:door_steel_bar"] = {name = "xpanes:door_steel_bar_a"}, } local function err(str) return log("error", str) end local function msg(name, str) return chat_send(name, sprintf("[craftguide] %s", str)) end local function is_num(x) return type(x) == "number" end local function is_str(x) return type(x) == "string" end local function true_str(str) return is_str(str) and str ~= "" end local function is_table(x) return type(x) == "table" end local function is_func(x) return type(x) == "function" end local function is_group(item) return sub(item, 1, 6) == "group:" end local function fmt(elem, ...) return sprintf(fs_elements[elem], ...) end local function clean_name(item) if sub(item, 1, 1) == ":" then item = sub(item, 2) end return item end local function array_diff(t1, t2) local hash = {} for i = 1, #t1 do local v = t1[i] hash[v] = true end for i = 1, #t2 do local v = t2[i] hash[v] = nil end local diff, c = {}, 0 for i = 1, #t1 do local v = t1[i] if hash[v] then c = c + 1 diff[c] = v end end return diff end local function table_eq(T1, T2) local avoid_loops = {} local function recurse(t1, t2) if type(t1) ~= type(t2) then return end if not is_table(t1) then return t1 == t2 end if avoid_loops[t1] then return avoid_loops[t1] == t2 end avoid_loops[t1] = t2 local t2k, t2kv = {}, {} for k in pairs(t2) do if is_table(k) then insert(t2kv, k) end t2k[k] = true end for k1, v1 in pairs(t1) do local v2 = t2[k1] if type(k1) == "table" then local ok for i = 1, #t2kv do local tk = t2kv[i] if table_eq(k1, tk) and recurse(v1, t2[tk]) then remove(t2kv, i) t2k[tk] = nil ok = true break end end if not ok then return end else if v2 == nil then return end t2k[k1] = nil if not recurse(v1, v2) then return end end end if next(t2k) then return end return true end return recurse(T1, T2) end local function table_merge(t1, t2, hash) t1 = t1 or {} t2 = t2 or {} if hash then for k, v in pairs(t2) do t1[k] = v end else local c = #t1 for i = 1, #t2 do c = c + 1 t1[c] = t2[i] end end return t1 end local function table_replace(t, val, new) for k, v in pairs(t) do if v == val then t[k] = new end end end local craft_types = {} function craftguide.register_craft_type(name, def) if not true_str(name) then return err "craftguide.register_craft_type(): name missing" end if not is_str(def.description) then def.description = "" end if not is_str(def.icon) then def.icon = "" end craft_types[name] = def end function craftguide.register_craft(def) local width, c = 0, 0 if true_str(def.url) then if not http then return err(sprintf([[craftguide.register_craft(): Unable to reach %s. No HTTP support for this mod: add it to the `secure.http_mods` or `secure.trusted_mods` setting.]], def.url)) end http.fetch({url = def.url}, function(result) if result.succeeded then local t = parse_json(result.data) if is_table(t) then return craftguide.register_craft(t) end end end) return end if not is_table(def) or not next(def) then return err "craftguide.register_craft(): craft definition missing" end if #def > 1 then for _, v in pairs(def) do craftguide.register_craft(v) end return end if def.result then def.output = def.result -- Backward compatibility def.result = nil end if not true_str(def.output) then return err "craftguide.register_craft(): output missing" end if not is_table(def.items) then def.items = {} end if def.grid then if not is_table(def.grid) then def.grid = {} end if not is_table(def.key) then def.key = {} end local cp = copy(def.grid) sort(cp, function(a, b) return #a > #b end) width = #cp[1] for i = 1, #def.grid do while #def.grid[i] < width do def.grid[i] = def.grid[i] .. " " end end for symbol in gmatch(concat(def.grid), ".") do c = c + 1 def.items[c] = def.key[symbol] end else local items, len = def.items, #def.items def.items = {} for i = 1, len do items[i] = items[i]:gsub(",", ", ") local rlen = #split(items[i], ",") if rlen > width then width = rlen end end for i = 1, len do while #split(items[i], ",") < width do items[i] = items[i] .. ", " end end for name in gmatch(concat(items, ","), "[%s%w_:]+") do c = c + 1 def.items[c] = match(name, "%S+") end end local item = match(def.output, "%S+") recipes_cache[item] = recipes_cache[item] or {} def.custom = true def.width = width insert(recipes_cache[item], def) end local recipe_filters = {} function craftguide.add_recipe_filter(name, f) if not true_str(name) then return err "craftguide.add_recipe_filter(): name missing" elseif not is_func(f) then return err "craftguide.add_recipe_filter(): function missing" end recipe_filters[name] = f end function craftguide.set_recipe_filter(name, f) if not is_str(name) then return err "craftguide.set_recipe_filter(): name missing" elseif not is_func(f) then return err "craftguide.set_recipe_filter(): function missing" end recipe_filters = {[name] = f} end function craftguide.remove_recipe_filter(name) recipe_filters[name] = nil end function craftguide.get_recipe_filters() return recipe_filters end local function apply_recipe_filters(recipes, player) for _, filter in pairs(recipe_filters) do recipes = filter(recipes, player) end return recipes end local search_filters = {} function craftguide.add_search_filter(name, f) if not true_str(name) then return err "craftguide.add_search_filter(): name missing" elseif not is_func(f) then return err "craftguide.add_search_filter(): function missing" end search_filters[name] = f end function craftguide.remove_search_filter(name) search_filters[name] = nil end function craftguide.get_search_filters() return search_filters end local function weird_desc(str) return not true_str(str) or find(str, "\n") or not find(str, "%u") end local function toupper(str) return str:gsub("%f[%w]%l", upper):gsub("_", " ") end local function snip(str, limit) return #str > limit and sprintf("%s...", sub(str, 1, limit - 3)) or str end local function get_desc(item) if sub(item, 1, 1) == "_" then item = sub(item, 2) end local def = reg_items[item] if def then local desc = ItemStack(item):get_short_description() if true_str(desc) then desc = desc:trim() if not find(desc, "%u") then desc = toupper(desc) end return desc elseif true_str(item) then return toupper(match(item, ":(.*)")) end end return S("Unknown Item (@1)", item) end local function item_has_groups(item_groups, groups) for i = 1, #groups do local group = groups[i] if (item_groups[group] or 0) == 0 then return end end return true end local function extract_groups(str) if sub(str, 1, 6) == "group:" then return split(sub(str, 7), ",") end end local function get_filtered_items(player, data) local items, known, c = {}, 0, 0 for i = 1, #init_items do local item = init_items[i] local recipes = recipes_cache[item] local usages = usages_cache[item] recipes = #apply_recipe_filters(recipes or {}, player) usages = #apply_recipe_filters(usages or {}, player) if recipes > 0 or usages > 0 then c = c + 1 items[c] = item if data then known = known + recipes + usages end end end if data then data.known_recipes = known end return items end local function get_burntime(item) return get_craft_result{method = "fuel", items = {item}}.time end local function cache_fuel(item) local burntime = get_burntime(item) if burntime > 0 then fuel_cache[item] = { type = "fuel", items = {item}, burntime = burntime, replacements = replacements.fuel[item], } end end local function show_item(def) return def and not (def.groups.not_in_craft_guide == 1 or def.groups.not_in_creative_inventory == 1) and def.description and def.description ~= "" end local function get_usages(recipe) local added = {} for _, item in pairs(recipe.items) do item = reg_aliases[item] or item if not added[item] then local groups = extract_groups(item) if groups then for name, def in pairs(reg_items) do if not added[name] and show_item(def) and item_has_groups(def.groups, groups) then local usage = copy(recipe) table_replace(usage.items, item, name) usages_cache[name] = usages_cache[name] or {} insert(usages_cache[name], 1, usage) added[name] = true end end elseif show_item(reg_items[item]) then usages_cache[item] = usages_cache[item] or {} insert(usages_cache[item], 1, recipe) end added[item] = true end end end local function cache_usages(item) local recipes = recipes_cache[item] or {} for i = 1, #recipes do get_usages(recipes[i]) end if fuel_cache[item] then usages_cache[item] = table_merge(usages_cache[item] or {}, {fuel_cache[item]}) end end local function drop_table(name, drop) local count_sure = 0 local drop_items = drop.items or {} local max_items = drop.max_items for i = 1, #drop_items do local di = drop_items[i] local valid_rarity = di.rarity and di.rarity > 1 if di.rarity or not max_items or (max_items and not di.rarity and count_sure < max_items) then for j = 1, #di.items do local dstack = ItemStack(di.items[j]) local dname = dstack:get_name() local dcount = dstack:get_count() local empty = dstack:is_empty() if not empty and (dname ~= name or (dname == name and dcount > 1)) then local rarity = valid_rarity and di.rarity craftguide.register_craft{ type = rarity and "digging_chance" or "digging", items = {name}, output = sprintf("%s %u", dname, dcount), rarity = rarity, tools = di.tools, } end end end if not di.rarity then count_sure = count_sure + 1 end end end local function cache_drops(name, drop) if true_str(drop) then local dstack = ItemStack(drop) local dname = dstack:get_name() local empty = dstack:is_empty() if not empty and dname ~= name then craftguide.register_craft{ type = "digging", items = {name}, output = drop, } end elseif is_table(drop) then drop_table(name, drop) end end local function cache_recipes(item) local recipes = get_all_recipes(item) if replacements[item] then local _recipes = {} for k, v in ipairs(recipes or {}) do _recipes[#recipes + 1 - k] = v end local shift = 0 local size_rpl = maxn(replacements[item]) local size_rcp = #_recipes if size_rpl > size_rcp then shift = size_rcp - size_rpl end for k, v in pairs(replacements[item]) do k = k + shift if _recipes[k] then _recipes[k].replacements = v end end recipes = _recipes end if recipes then recipes_cache[item] = table_merge(recipes, recipes_cache[item] or {}) end end local function get_recipes(player, item) local clean_item = reg_aliases[item] or item local recipes = recipes_cache[clean_item] local usages = usages_cache[clean_item] if recipes then recipes = apply_recipe_filters(recipes, player) end local no_recipes = not recipes or #recipes == 0 if no_recipes and not usages then return end usages = apply_recipe_filters(usages, player) local no_usages = not usages or #usages == 0 return not no_recipes and recipes or nil, not no_usages and usages or nil end local function groups_to_items(groups, get_all) if not get_all and #groups == 1 then local group = groups[1] local stereotype = craftguide.group_stereotypes[group] local def = reg_items[stereotype] if def and show_item(def) then return stereotype end end local names = {} for name, def in pairs(reg_items) do if show_item(def) and item_has_groups(def.groups, groups) then if get_all then names[#names + 1] = name else return name end end end return get_all and names or "" end local function sort_itemlist(player, az) local inv = player:get_inventory() local list = inv:get_list("main") local size = inv:get_size("main") local new_inv, stack_meta = {}, {} for i = 1, size do local stack = list[i] local name = stack:get_name() local count = stack:get_count() local empty = stack:is_empty() local meta = stack:get_meta():to_table() if not empty then if next(meta.fields) then stack_meta[#stack_meta + 1] = stack else new_inv[#new_inv + 1] = sprintf("%s %u", name, count) end end end if az then sort(new_inv) else sort(new_inv, function(a, b) return a > b end) end inv:set_list("main", new_inv) for i = 1, #stack_meta do inv:set_stack("main", #new_inv + i, stack_meta[i]) end end local function compress_items(player) local inv = player:get_inventory() local list = inv:get_list("main") local size = inv:get_size("main") local new_inv, _new_inv, stack_meta = {}, {}, {} for i = 1, size do local stack = list[i] local name = stack:get_name() local count = stack:get_count() local empty = stack:is_empty() local meta = stack:get_meta():to_table() if not empty then if next(meta.fields) then stack_meta[#stack_meta + 1] = stack else new_inv[name] = new_inv[name] or 0 new_inv[name] = new_inv[name] + count end end end for name, count in pairs(new_inv) do local stackmax = ItemStack(name):get_stack_max() local iter = ceil(count / stackmax) local leftover = count for _ = 1, iter do _new_inv[#_new_inv + 1] = sprintf("%s %u", name, min(stackmax, leftover)) leftover = leftover - stackmax end end sort(_new_inv) inv:set_list("main", _new_inv) for i = 1, #stack_meta do inv:set_stack("main", #_new_inv + i, stack_meta[i]) end end local function get_stack_max(inv, data, is_recipe, rcp) local list = inv:get_list("main") local size = inv:get_size("main") local counts_inv, counts_rcp, counts = {}, {}, {} local rcp_usg = is_recipe and "recipe" or "usage" for _, it in pairs(rcp.items) do counts_rcp[it] = (counts_rcp[it] or 0) + 1 end data.export_counts[rcp_usg] = {} data.export_counts[rcp_usg].rcp = counts_rcp for i = 1, size do local stack = list[i] if not stack:is_empty() then local item = stack:get_name() local count = stack:get_count() for name in pairs(counts_rcp) do if is_group(name) then local def = reg_items[item] if def then local groups = extract_groups(name) if item_has_groups(def.groups, groups) then counts_inv[name] = (counts_inv[name] or 0) + count end end end end counts_inv[item] = (counts_inv[item] or 0) + count end end data.export_counts[rcp_usg].inv = counts_inv for name in pairs(counts_rcp) do counts[name] = floor((counts_inv[name] or 0) / (counts_rcp[name] or 0)) end local max_stacks = math.huge for _, count in pairs(counts) do if count < max_stacks then max_stacks = count end end return max_stacks end local function get_stack(player, pname, stack, message) local inv = player:get_inventory() if inv:room_for_item("main", stack) then inv:add_item("main", stack) msg(pname, sprintf("%s added in your inventory", message)) else local dir = player:get_look_dir() local ppos = player:get_pos() ppos.y = ppos.y + 1.625 local look_at = vec_add(ppos, vec_mul(dir, 1)) core.add_item(look_at, stack) msg(pname, sprintf("%s spawned", message)) end end local function craft_stack(player, pname, data, craft_rcp) local inv = player:get_inventory() local rcp_usg = craft_rcp and "recipe" or "usage" local output = craft_rcp and data.recipes[data.rnum].output or data.usages[data.unum].output output = ItemStack(output) local stackname, stackcount, stackmax = output:get_name(), output:get_count(), output:get_stack_max() local scrbar_val = data[sprintf("scrbar_%s", craft_rcp and "rcp" or "usg")] or 1 for name, count in pairs(data.export_counts[rcp_usg].rcp) do local items = {[name] = count} if is_group(name) then items = {} local groups = extract_groups(name) local item_groups = groups_to_items(groups, true) local remaining = count for _, item in ipairs(item_groups) do for _name, _count in pairs(data.export_counts[rcp_usg].inv) do if item == _name and remaining > 0 then local c = min(remaining, _count) items[item] = c remaining = remaining - c end if remaining == 0 then break end end end end for k, v in pairs(items) do inv:remove_item("main", sprintf("%s %s", k, v * scrbar_val)) end end local count = stackcount * scrbar_val local desc = get_desc(stackname) local iter = ceil(count / stackmax) local leftover = count for _ = 1, iter do local c = min(stackmax, leftover) local message if c > 1 then message = clr("#ff0", sprintf("%s x %s", c, desc)) else message = clr("#ff0", sprintf("%s", desc)) end local stack = ItemStack(sprintf("%s %s", stackname, c)) get_stack(player, pname, stack, message) leftover = leftover - stackmax end end local function select_item(player, data, _f) local item for field in pairs(_f) do if find(field, ":") then item = field break end end if not item then return elseif sub(item, -4) == "_inv" then item = sub(item, 1, -5) elseif sub(item, 1, 1) == "_" then item = sub(item, 2) elseif sub(item, 1, 6) == "group|" then item = match(item, "([%w:_]+)$") end item = reg_aliases[item] or item if item == data.query_item then return end local recipes, usages = get_recipes(player, item) if not recipes and not usages then return end data.query_item = item data.recipes = recipes data.usages = usages data.rnum = 1 data.unum = 1 data.scrbar_rcp = 1 data.scrbar_usg = 1 data.export_rcp = nil data.export_usg = nil end local function repairable(tool) local def = reg_tools[tool] return toolrepair and def and def.groups and def.groups.disable_repair ~= 1 end local function is_fav(favs, query_item) local fav, i for j = 1, #favs do if favs[j] == query_item then fav = true i = j break end end return fav, i end local function get_tooltip(item, info) local tooltip if info.groups then sort(info.groups) tooltip = group_names[concat(info.groups, ",")] if not tooltip then local groupstr = {} for i = 1, #info.groups do insert(groupstr, clr("#ff0", info.groups[i])) end groupstr = concat(groupstr, ", ") tooltip = S("Any item belonging to the group(s): @1", groupstr) end else tooltip = get_desc(item) end local function add(str) return sprintf("%s\n%s", tooltip, str) end if info.cooktime then tooltip = add(S("Cooking time: @1", clr("#ff0", info.cooktime))) end if info.burntime then tooltip = add(S("Burning time: @1", clr("#ff0", info.burntime))) end if info.replace then for i = 1, #info.replace.items do local rpl = match(info.replace.items[i], "%S+") local desc = clr("#ff0", get_desc(rpl)) if info.replace.type == "cooking" then tooltip = add(S("Replaced by @1 on smelting", desc)) elseif info.replace.type == "fuel" then tooltip = add(S("Replaced by @1 on burning", desc)) else tooltip = add(S("Replaced by @1 on crafting", desc)) end end end if info.repair then tooltip = add(S("Repairable by step of @1", clr("#ff0", toolrepair .. "%"))) end if info.rarity then local chance = (1 / max(1, info.rarity)) * 100 tooltip = add(S("@1 of chance to drop", clr("#ff0", chance .. "%"))) end if info.tools then local several = #info.tools > 1 local names = several and "\n" or "" if several then for i = 1, #info.tools do names = sprintf("%s\t\t- %s\n", names, clr("#ff0", get_desc(info.tools[i]))) end tooltip = add(S("Only drop if using one of these tools: @1", sub(names, 1, -2))) else tooltip = add(S("Only drop if using this tool: @1", clr("#ff0", get_desc(info.tools[1])))) end end return sprintf("tooltip[%s;%s]", item, ESC(tooltip)) end local function get_output_fs(fs, data, rcp, is_recipe, shapeless, right, btn_size, _btn_size) local custom_recipe = craft_types[rcp.type] if custom_recipe or shapeless or rcp.type == "cooking" then local icon = custom_recipe and custom_recipe.icon or shapeless and "shapeless" or "furnace" if not custom_recipe then icon = sprintf("craftguide_%s.png^[resize:16x16", icon) end local pos_x = right + btn_size + 0.42 local pos_y = data.yoffset + 0.9 if sub(icon, 1, 18) == "craftguide_furnace" then fs(fmt("animated_image", pos_x, pos_y, 0.5, 0.5, PNG.furnace_anim, 8, 180)) else fs(fmt("image", pos_x, pos_y, 0.5, 0.5, icon)) end local tooltip = custom_recipe and custom_recipe.description or shapeless and S"Shapeless" or S"Cooking" fs(fmt("tooltip", pos_x, pos_y, 0.5, 0.5, ESC(tooltip))) end local arrow_X = right + 0.2 + (_btn_size or ITEM_BTN_SIZE) local X = arrow_X + 1.2 local Y = data.yoffset + 1.4 fs(fmt("image", arrow_X, Y + 0.06, 1, 1, PNG.arrow)) if rcp.type == "fuel" then fs(fmt("animated_image", X + 0.05, Y, ITEM_BTN_SIZE, ITEM_BTN_SIZE, PNG.fire_anim, 8, 180)) else local item = rcp.output item = ItemStack(clean_name(item)) local name = item:get_name() local count = item:get_count() local bt_s = ITEM_BTN_SIZE * 1.2 fs(fmt("image", X, Y - 0.11, bt_s, bt_s, PNG.slot)) local _name = sprintf("_%s", name) fs(fmt("item_image_button", X + 0.11, Y, ITEM_BTN_SIZE, ITEM_BTN_SIZE, sprintf("%s %u", name, count * (is_recipe and data.scrbar_rcp or data.scrbar_usg or 1)), _name, "")) local def = reg_items[name] local unknown = not def or nil local desc = def and def.description local weird = name ~= "" and desc and weird_desc(desc) or nil local burntime = fuel_cache[name] and fuel_cache[name].burntime local infos = { unknown = unknown, weird = weird, burntime = burntime, repair = repairable(name), rarity = rcp.rarity, tools = rcp.tools, } if next(infos) then fs(get_tooltip(_name, infos)) end end end local function get_grid_fs(fs, data, rcp, is_recipe) local width = rcp.width or 1 local right, btn_size, _btn_size = 0, ITEM_BTN_SIZE local cooktime, shapeless if rcp.type == "cooking" then cooktime, width = width, 1 elseif width == 0 and not rcp.custom then shapeless = true local n = #rcp.items width = (n < 5 and n > 1) and 2 or min(3, max(1, n)) end local rows = ceil(maxn(rcp.items) / width) local large_recipe = width > 3 or rows > 3 if large_recipe then fs("style_type[item_image_button;border=true]") end for i = 1, width * rows do local item = rcp.items[i] or "" item = clean_name(item) local name = match(item, "%S*") local X = ceil((i - 1) % width - width) X = X + (X * 0.2) + data.xoffset + 3.9 local Y = ceil(i / width) - min(2, rows) Y = Y + (Y * 0.15) + data.yoffset + 1.4 if large_recipe then btn_size = (3 / width) * (3 / rows) + 0.3 _btn_size = btn_size local xi = (i - 1) % width local yi = floor((i - 1) / width) X = btn_size * xi + data.xoffset + 0.3 + (xi * 0.05) Y = btn_size * yi + data.yoffset + 0.2 + (yi * 0.05) end if X > right then right = X end local groups if is_group(name) then groups = extract_groups(name) item = groups_to_items(groups) end local label = groups and "\nG" or "" local replace for j = 1, #(rcp.replacements or {}) do local replacement = rcp.replacements[j] if replacement[1] == name then replace = replace or {type = rcp.type, items = {}} local added for _, v in ipairs(replace.items) do if replacement[2] == v then added = true break end end if not added then label = sprintf("%s%s\nR", label ~= "" and "\n" or "", label) replace.items[#replace.items + 1] = replacement[2] end end end if not large_recipe then fs(fmt("image", X, Y, btn_size, btn_size, PNG.slot)) end local btn_name = groups and sprintf("group|%s|%s", groups[1], item) or item fs(fmt("item_image_button", X, Y, btn_size, btn_size, sprintf("%s %u", item, is_recipe and data.scrbar_rcp or data.scrbar_usg or 1), btn_name, label)) local def = reg_items[name] local unknown = not def or nil unknown = not groups and unknown or nil local desc = def and def.description local weird = name ~= "" and desc and weird_desc(desc) or nil local burntime = fuel_cache[name] and fuel_cache[name].burntime local infos = { unknown = unknown, weird = weird, groups = groups, burntime = burntime, cooktime = cooktime, replace = replace, } if next(infos) then fs(get_tooltip(btn_name, infos)) end end if large_recipe then fs("style_type[item_image_button;border=false]") end get_output_fs(fs, data, rcp, is_recipe, shapeless, right, btn_size, _btn_size) end local function get_rcp_lbl(fs, data, panel, rn, is_recipe) local lbl = ES("Usage @1 of @2", data.unum, rn) if is_recipe then lbl = ES("Recipe @1 of @2", data.rnum, rn) end local _lbl = translate(data.lang_code, lbl) local lbl_len = #_lbl:gsub("[\128-\191]", "") -- Count chars, not bytes in UTF-8 strings local shift = min(0.9, abs(12 - max(12, lbl_len)) * 0.15) fs(fmt("label", data.xoffset + 5.65 - shift, data.yoffset + 3.37, lbl)) if rn > 1 then local btn_suffix = is_recipe and "recipe" or "usage" local prev_name = sprintf("prev_%s", btn_suffix) local next_name = sprintf("next_%s", btn_suffix) local x_arrow = data.xoffset + 5.09 local y_arrow = data.yoffset + 3.2 fs(fmt("image_button", x_arrow - shift, y_arrow, 0.3, 0.3, "", prev_name, ""), fmt("image_button", x_arrow + 2.3, y_arrow, 0.3, 0.3, "", next_name, "")) end local rcp = is_recipe and panel.rcp[data.rnum] or panel.rcp[data.unum] get_grid_fs(fs, data, rcp, is_recipe) end local function get_model_fs(fs, data, def, model_alias) if model_alias then if model_alias.drawtype == "entity" then def = reg_entities[model_alias.name] local init_props = def.initial_properties def.textures = init_props and init_props.textures or def.textures def.mesh = init_props and init_props.mesh or def.mesh else def = reg_items[model_alias.name] end end local tiles = def.tiles or def.textures or {} local t = {} for _, v in ipairs(tiles) do local _name if v.color then if is_num(v.color) then local hex = sprintf("%02x", v.color) while #hex < 8 do hex = "0" .. hex end _name = sprintf("%s^[multiply:%s", v.name, sprintf("#%s%s", sub(hex, 3), sub(hex, 1, 2))) else _name = sprintf("%s^[multiply:%s", v.name, v.color) end elseif v.animation then _name = sprintf("%s^[verticalframe:%u:0", v.name, v.animation.aspect_h) end t[#t + 1] = _name or v.name or v end while #t < 6 do t[#t + 1] = t[#t] end fs(fmt("model", data.xoffset + 6.6, data.yoffset + 0.05, 1.3, 1.3, "", def.mesh, concat(t, ","), "0,0", "true", model_alias and model_alias.frames or "")) end local function get_header(fs, data) local fav = is_fav(data.favs, data.query_item) local nfavs = #data.favs local star_x, star_y, star_size = data.xoffset + 0.4, data.yoffset + 0.5, 0.4 if nfavs < MAX_FAVS or (nfavs == MAX_FAVS and fav) then local fav_marked = sprintf("craftguide_fav%s.png", fav and "_off" or "") fs(sprintf("style[fav;fgimg=%s;fgimg_hovered=%s;fgimg_pressed=%s]", sprintf("craftguide_fav%s.png", fav and "" or "_off"), fav_marked, fav_marked), fmt("image_button", star_x, star_y, star_size, star_size, "", "fav", ""), sprintf("tooltip[fav;%s]", fav and ES"Unmark this item" or ES"Mark this item")) else fs(sprintf("style[nofav;fgimg=%s;fgimg_hovered=%s;fgimg_pressed=%s]", "craftguide_fav_off.png", PNG.cancel, PNG.cancel), fmt("image_button", star_x, star_y, star_size, star_size, "", "nofav", ""), sprintf("tooltip[nofav;%s]", ES"Cannot mark this item. Bookmark limit reached.")) end local desc_lim, name_lim = 32, 34 local desc = ESC(get_desc(data.query_item)) local tech_name = data.query_item local X = data.xoffset + 1.05 local Y1 = data.yoffset + 0.47 local Y2 = Y1 + 0.5 if #desc > desc_lim then fs(fmt("tooltip", X, Y1 - 0.1, 5.7, 0.24, desc)) desc = snip(desc, desc_lim) end if #tech_name > name_lim then fs(fmt("tooltip", X, Y2 - 0.1, 5.7, 0.24, tech_name)) tech_name = snip(tech_name, name_lim) end fs("style_type[label;font=bold;font_size=22]", fmt("label", X, Y1, desc), "style_type[label;font=mono;font_size=16]", fmt("label", X, Y2, clr("#7bf", tech_name)), "style_type[label;font=normal;font_size=16]") local def = reg_items[data.query_item] local model_alias = craftguide.model_alias[data.query_item] if def.drawtype == "mesh" or model_alias then get_model_fs(fs, data, def, model_alias) else fs(fmt("item_image", data.xoffset + 6.8, data.yoffset + 0.17, 1.1, 1.1, data.query_item)) end end local function get_export_fs(fs, data, is_recipe, is_usage, max_stacks_rcp, max_stacks_usg) local name = is_recipe and "rcp" or "usg" local show_export = (is_recipe and data.export_rcp) or (is_usage and data.export_usg) fs(sprintf("style[export_%s;fgimg=%s;fgimg_hovered=%s]", name, sprintf("%s", show_export and PNG.export_hover or PNG.export), PNG.export_hover), fmt("image_button", data.xoffset + 7.35, data.yoffset + 0.2, 0.45, 0.45, "", sprintf("export_%s", name), ""), sprintf("tooltip[export_%s;%s]", name, ES"Quick crafting")) if not show_export then return end local craft_max = is_recipe and max_stacks_rcp or max_stacks_usg local stack_fs = (is_recipe and data.scrbar_rcp) or (is_usage and data.scrbar_usg) or 1 if stack_fs > craft_max then stack_fs = craft_max if is_recipe then data.scrbar_rcp = craft_max elseif is_usage then data.scrbar_usg = craft_max end end fs(sprintf("style[scrbar_%s;noclip=true]", name), sprintf("scrollbaroptions[min=1;max=%u;smallstep=1]", craft_max), fmt("scrollbar", data.xoffset + 8.1, data.yoffset, 3, 0.35, sprintf("scrbar_%s", name), stack_fs), fmt("button", data.xoffset + 8.1, data.yoffset + 0.4, 3, 0.7, sprintf("craft_%s", name), ES("Craft (x@1)", stack_fs))) end local function get_rcp_extra(player, fs, data, panel, is_recipe, is_usage) local rn = panel.rcp and #panel.rcp if rn then local rcp_normal = is_recipe and panel.rcp[data.rnum].type == "normal" local usg_normal = is_usage and panel.rcp[data.unum].type == "normal" local max_stacks_rcp, max_stacks_usg = 0, 0 local inv = player:get_inventory() if rcp_normal then max_stacks_rcp = get_stack_max(inv, data, is_recipe, panel.rcp[data.rnum]) end if usg_normal then max_stacks_usg = get_stack_max(inv, data, is_recipe, panel.rcp[data.unum]) end if is_recipe and max_stacks_rcp == 0 then data.export_rcp = nil data.scrbar_rcp = 1 elseif is_usage and max_stacks_usg == 0 then data.export_usg = nil data.scrbar_usg = 1 end if max_stacks_rcp > 0 or max_stacks_usg > 0 then get_export_fs(fs, data, is_recipe, is_usage, max_stacks_rcp, max_stacks_usg) end get_rcp_lbl(fs, data, panel, rn, is_recipe) else local lbl = is_recipe and ES"No recipes" or ES"No usages" fs(fmt("button", data.xoffset + 0.1, data.yoffset + (panel.height / 2) - 0.5, 7.8, 1, "no_rcp", lbl)) end end local function get_favs(fs, data) fs(fmt("label", data.xoffset + 0.4, data.yoffset + 0.4, ES"Bookmarks")) for i = 1, #data.favs do local item = data.favs[i] local X = data.xoffset - 0.7 + (i * 1.2) local Y = data.yoffset + 0.8 if data.query_item == item then fs(fmt("image", X, Y, ITEM_BTN_SIZE, ITEM_BTN_SIZE, PNG.slot)) end fs(fmt("item_image_button", X, Y, ITEM_BTN_SIZE, ITEM_BTN_SIZE, item, item, "")) end end local function get_panels(player, fs, data) local _title = {name = "title", height = 1.4} local _favs = {name = "favs", height = 2.23} local _recipes = {name = "recipes", rcp = data.recipes, height = 3.9} local _usages = {name = "usages", rcp = data.usages, height = 3.9} local panels = {_title, _recipes, _usages, _favs} for idx = 1, #panels do local panel = panels[idx] data.yoffset = 0 if idx > 1 then for _idx = idx - 1, 1, -1 do data.yoffset = data.yoffset + panels[_idx].height + 0.1 end end fs(fmt("bg9", data.xoffset + 0.1, data.yoffset, 7.9, panel.height, PNG.bg_full, 10)) local is_recipe, is_usage = panel.name == "recipes", panel.name == "usages" if is_recipe or is_usage then get_rcp_extra(player, fs, data, panel, is_recipe, is_usage) elseif panel.name == "title" then get_header(fs, data) elseif panel.name == "favs" then get_favs(fs, data) end end end local function get_item_list(fs, data, full_height) fs(fmt("bg9", 0, 0, data.xoffset, full_height, PNG.bg_full, 10)) local filtered = data.filter ~= "" fs("box[0.2,0.2;4.55,0.6;#bababa25]", "set_focus[filter]") fs(sprintf("field[0.3,0.2;%f,0.6;filter;;%s]", filtered and 3.45 or 3.9, ESC(data.filter))) fs("field_close_on_enter[filter;false]") if filtered then fs(fmt("image_button", 3.75, 0.35, 0.3, 0.3, "", "cancel", "")) end fs(fmt("image_button", 4.25, 0.32, 0.35, 0.35, "", "search", "")) fs(fmt("image_button", data.xoffset - 2.73, 0.3, 0.35, 0.35, "", "prev_page", ""), fmt("image_button", data.xoffset - 0.55, 0.3, 0.35, 0.35, "", "next_page", "")) data.pagemax = max(1, ceil(#data.items / IPP)) fs(fmt("button", data.xoffset - 2.4, 0.14, 1.88, 0.7, "pagenum", sprintf("%s / %u", clr("#ff0", data.pagenum), data.pagemax))) if #data.items == 0 then local lbl = ES"No item to show" if next(recipe_filters) and #init_items > 0 and data.filter == "" then lbl = ES"Collect items to reveal more recipes" end fs(fmt("button", 0, 3, data.xoffset, 1, "no_item", lbl)) end local first_item = (data.pagenum - 1) * IPP for i = first_item, first_item + IPP - 1 do local item = data.items[i + 1] if not item then break end local X = i % ROWS X = X + (X * 0.1) + 0.2 local Y = floor((i % IPP - X) / ROWS + 1) Y = Y + (Y * 0.06) + 1 if data.query_item == item then fs(fmt("image", X, Y, 1, 1, PNG.slot)) end fs[#fs + 1] = fmt("item_image_button", X, Y, 1, 1, item, sprintf("%s_inv", item), "") end end local function make_fs(player, data) local fs = setmetatable({}, { __call = function(t, ...) t[#t + 1] = concat({...}) end }) data.xoffset = ROWS + 1.2 local full_height = LINES + 1.73 fs(sprintf("formspec_version[%u]size[%f,%f]no_prepend[]bgcolor[#0000]", MIN_FORMSPEC_VERSION, data.xoffset + (data.query_item and 8 or 0), full_height), styles) get_item_list(fs, data, full_height) if data.query_item then get_panels(player, fs, data) end return concat(fs) end local function show_fs(player, name) local data = pdata[name] local fs = make_fs(player, data) show_formspec(name, "craftguide", fs) end craftguide.register_craft_type("digging", { description = ES"Digging", icon = "craftguide_steelpick.png", }) craftguide.register_craft_type("digging_chance", { description = ES"Digging (by chance)", icon = "craftguide_mesepick.png", }) local function search(data) local filter = data.filter if searches[filter] then data.items = searches[filter] return end local opt = "^(.-)%+([%w_]+)=([%w_,]+)" local search_filter = next(search_filters) and match(filter, opt) local filters = {} if search_filter then for filter_name, values in gmatch(filter, sub(opt, 6)) do if search_filters[filter_name] then values = split(values, ",") filters[filter_name] = values end end end local filtered_list, c = {}, 0 for i = 1, #data.items_raw do local item = data.items_raw[i] local def = reg_items[item] local desc = lower(translate(data.lang_code, def and def.description)) or "" local search_in = sprintf("%s %s", item, desc) local temp, j, to_add = {}, 1 if search_filter then for filter_name, values in pairs(filters) do if values then local func = search_filters[filter_name] to_add = (j > 1 and temp[item] or j == 1) and func(item, values) and (search_filter == "" or find(search_in, search_filter, 1, true)) if to_add then temp[item] = true end j = j + 1 end end else to_add = find(search_in, filter, 1, true) end if to_add then c = c + 1 filtered_list[c] = item end end if not next(recipe_filters) then -- Cache the results only if searched 2 times if searches[filter] == nil then searches[filter] = false else searches[filter] = filtered_list end end data.items = filtered_list end craftguide.add_search_filter("groups", function(item, groups) local def = reg_items[item] local has_groups = true for i = 1, #groups do local group = groups[i] if not def.groups[group] then has_groups = nil break end end return has_groups end) craftguide.add_search_filter("type", function(item, drawtype) if drawtype == "node" then return reg_nodes[item] elseif drawtype == "item" then return reg_craftitems[item] elseif drawtype == "tool" then return reg_tools[item] end end) --[[ As `core.get_craft_recipe` and `core.get_all_craft_recipes` do not return the fuel, replacements and toolrepair recipes, we have to override `core.register_craft` and do some reverse engineering. See engine's issues #4901, #5745 and #8920. ]] local old_register_craft = core.register_craft local rcp_num = {} core.register_craft = function(def) old_register_craft(def) if def.type == "toolrepair" then toolrepair = def.additional_wear * -100 end local output = def.output or (true_str(def.recipe) and def.recipe) or nil if not output then return end output = {match(output, "%S+")} local groups if is_group(output[1]) then groups = extract_groups(output[1]) output = groups_to_items(groups, true) end for i = 1, #output do local item = output[i] rcp_num[item] = (rcp_num[item] or 0) + 1 if def.replacements then if def.type == "fuel" then replacements.fuel[item] = def.replacements else replacements[item] = replacements[item] or {} replacements[item][rcp_num[item]] = def.replacements end end end end local old_clear_craft = core.clear_craft core.clear_craft = function(def) old_clear_craft(def) if true_str(def) then return -- TODO elseif is_table(def) then return -- TODO end end local function resolve_aliases(hash) for oldname, newname in pairs(reg_aliases) do cache_recipes(oldname) local recipes = recipes_cache[oldname] if recipes then if not recipes_cache[newname] then recipes_cache[newname] = {} end local similar for i = 1, #recipes_cache[oldname] do local rcp_old = recipes_cache[oldname][i] for j = 1, #recipes_cache[newname] do local rcp_new = copy(recipes_cache[newname][j]) rcp_new.output = oldname if table_eq(rcp_old, rcp_new) then similar = true break end end if not similar then insert(recipes_cache[newname], rcp_old) end end end if newname ~= "" and recipes_cache[oldname] and not hash[newname] then init_items[#init_items + 1] = newname end end end local function get_init_items() local _select, _preselect = {}, {} for name, def in pairs(reg_items) do if name ~= "" and show_item(def) then cache_drops(name, def.drop) cache_fuel(name) cache_recipes(name) _preselect[name] = true end end for name in pairs(_preselect) do cache_usages(name) if recipes_cache[name] or usages_cache[name] then init_items[#init_items + 1] = name _select[name] = true end end resolve_aliases(_select) sort(init_items) if http and true_str(craftguide.export_url) then local post_data = { recipes = recipes_cache, usages = usages_cache, } http.fetch_async{ url = craftguide.export_url, post_data = write_json(post_data), } end end local function init_data(name) local info = get_player_info(name) pdata[name] = { filter = "", pagenum = 1, items = init_items, items_raw = init_items, favs = {}, export_counts = {}, lang_code = get_lang_code(info), fs_version = get_formspec_version(info), } end local function reset_data(data) data.filter = "" data.pagenum = 1 data.rnum = 1 data.unum = 1 data.scrbar_rcp = 1 data.scrbar_usg = 1 data.query_item = nil data.recipes = nil data.usages = nil data.export_rcp = nil data.export_usg = nil data.items = data.items_raw end on_mods_loaded(get_init_items) on_joinplayer(function(player) local name = player:get_player_name() init_data(name) local data = pdata[name] if data.fs_version < MIN_FORMSPEC_VERSION then outdated(name) end end) on_receive_fields(function(player, formname, _f) if formname ~= "craftguide" then return false end local name = player:get_player_name() local data = pdata[name] local sb_rcp, sb_usg = _f.scrbar_rcp, _f.scrbar_usg if _f.quit then -- Neither the vignette nor hud_flags are available when /craft is used if data.vignette then player:hud_change(data.vignette, "text", "") data.vignette = nil end if data.hud_flags then data.hud_flags.crosshair = true player:hud_set_flags(data.hud_flags) data.hud_flags = nil end return false elseif _f.cancel then reset_data(data) elseif _f.prev_recipe or _f.next_recipe then local num = data.rnum + (_f.prev_recipe and -1 or 1) data.rnum = data.recipes[num] and num or (_f.prev_recipe and #data.recipes or 1) data.export_rcp = nil data.scrbar_rcp = 1 elseif _f.prev_usage or _f.next_usage then local num = data.unum + (_f.prev_usage and -1 or 1) data.unum = data.usages[num] and num or (_f.prev_usage and #data.usages or 1) data.export_usg = nil data.scrbar_usg = 1 elseif _f.key_enter_field == "filter" or _f.search then if _f.filter == "" then reset_data(data) return true, show_fs(player, name) end local str = lower(_f.filter) if data.filter == str then return end data.filter = str data.pagenum = 1 search(data) elseif _f.prev_page or _f.next_page then if data.pagemax == 1 then return end data.pagenum = data.pagenum - (_f.prev_page and 1 or -1) if data.pagenum > data.pagemax then data.pagenum = 1 elseif data.pagenum == 0 then data.pagenum = data.pagemax end elseif _f.fav then local fav, i = is_fav(data.favs, data.query_item) local total = #data.favs if total < MAX_FAVS and not fav then data.favs[total + 1] = data.query_item elseif fav then remove(data.favs, i) end elseif _f.export_rcp or _f.export_usg then if _f.export_rcp then data.export_rcp = not data.export_rcp if not data.export_rcp then data.scrbar_rcp = 1 end else data.export_usg = not data.export_usg if not data.export_usg then data.scrbar_usg = 1 end end elseif _f.trash then local inv = player:get_inventory() if not inv:is_empty("main") then inv:set_list("main", {}) end elseif _f.compress then compress_items(player) elseif _f.sort_az or _f.sort_za then sort_itemlist(player, _f.sort_az) elseif _f.scrbar_inv then data.scrbar_inv = tonumber(match(_f.scrbar_inv, "%d+")) return true elseif (sb_rcp and sub(sb_rcp, 1, 3) == "CHG") or (sb_usg and sub(sb_usg, 1, 3) == "CHG") then data.scrbar_rcp = sb_rcp and tonum(match(sb_rcp, "%d+")) data.scrbar_usg = sb_usg and tonum(match(sb_usg, "%d+")) elseif _f.craft_rcp or _f.craft_usg then craft_stack(player, name, data, _f.craft_rcp) else select_item(player, data, _f) end return true, show_fs(player, name) end) local function on_use(user) local name = user:get_player_name() local data = pdata[name] if not data then return end if data.fs_version < MIN_FORMSPEC_VERSION then return outdated(name) end if next(recipe_filters) then data.items_raw = get_filtered_items(user) search(data) end show_fs(user, name) data.vignette = user:hud_add({ hud_elem_type = "image", position = {x = 0.5, y = 0.5}, scale = {x = -100, y = -100}, text = "craftguide_vignette.png", z_index = -0xB00B, }) data.hud_flags = user:hud_get_flags() data.hud_flags.crosshair = false user:hud_set_flags(data.hud_flags) end core.register_craftitem("craftguide:book", { description = S"Crafting Guide", inventory_image = PNG.book, wield_image = PNG.book, stack_max = 1, groups = {book = 1}, on_use = function(_, user) on_use(user) end }) core.register_node("craftguide:sign", { description = S"Crafting Guide Sign", drawtype = "nodebox", tiles = {PNG.sign}, inventory_image = PNG.sign, wield_image = PNG.sign, paramtype = "light", paramtype2 = "wallmounted", sunlight_propagates = true, groups = { choppy = 1, attached_node = 1, oddly_breakable_by_hand = 1, flammable = 3, }, node_box = { type = "wallmounted", wall_top = {-0.5, 0.4375, -0.5, 0.5, 0.5, 0.5}, wall_bottom = {-0.5, -0.5, -0.5, 0.5, -0.4375, 0.5}, wall_side = {-0.5, -0.5, -0.5, -0.4375, 0.5, 0.5} }, on_construct = function(pos) local meta = core.get_meta(pos) meta:set_string("infotext", "Crafting Guide Sign") end, on_rightclick = function(_, _, user) on_use(user) end }) core.register_craft{ output = "craftguide:book", type = "shapeless", recipe = {"default:book"} } core.register_craft{ type = "fuel", recipe = "craftguide:book", burntime = 3 } core.register_craft{ output = "craftguide:sign", type = "shapeless", recipe = {"default:sign_wall_wood"} } core.register_craft{ type = "fuel", recipe = "craftguide:sign", burntime = 10 } if progressive_mode then local function item_in_inv(item, inv_items) local inv_items_size = #inv_items if is_group(item) then local groups = extract_groups(item) for i = 1, inv_items_size do local def = reg_items[inv_items[i]] if def then if item_has_groups(def.groups, groups) then return true end end end else for i = 1, inv_items_size do if inv_items[i] == item then return true end end end end local function recipe_in_inv(rcp, inv_items) for _, item in pairs(rcp.items) do if not item_in_inv(item, inv_items) then return end end return true end local function progressive_filter(recipes, player) if not recipes then return {} end local name = player:get_player_name() local data = pdata[name] if #data.inv_items == 0 then return {} end local filtered, c = {}, 0 for i = 1, #recipes do local recipe = recipes[i] if recipe_in_inv(recipe, data.inv_items) then c = c + 1 filtered[c] = recipe end end return filtered end local item_lists = {"main", "craft", "craftpreview"} local function get_inv_items(player) local inv = player:get_inventory() local stacks = {} for i = 1, #item_lists do local list = inv:get_list(item_lists[i]) table_merge(stacks, list) end local inv_items, c = {}, 0 for i = 1, #stacks do local stack = stacks[i] if not stack:is_empty() then local name = stack:get_name() if reg_items[name] then c = c + 1 inv_items[c] = name end end end return inv_items end local function init_hud(player, data) data.hud = { bg = player:hud_add{ hud_elem_type = "image", position = {x = 0.78, y = 1}, alignment = {x = 1, y = 1}, scale = {x = 370, y = 112}, text = PNG.bg, z_index = 0xDEAD, }, book = player:hud_add{ hud_elem_type = "image", position = {x = 0.79, y = 1.02}, alignment = {x = 1, y = 1}, scale = {x = 4, y = 4}, text = PNG.book, z_index = 0xDEAD, }, text = player:hud_add{ hud_elem_type = "text", position = {x = 0.84, y = 1.04}, alignment = {x = 1, y = 1}, number = 0xffffff, text = "", z_index = 0xDEAD, }, } end local function show_hud_success(player, data) -- It'd better to have an engine function `hud_move` to only need -- 2 calls for the notification's back and forth. local hud_info_bg = player:hud_get(data.hud.bg) local dt = 0.016 if hud_info_bg.position.y <= 0.9 then data.show_hud = false data.hud_timer = (data.hud_timer or 0) + dt end if data.show_hud then for _, def in pairs(data.hud) do local hud_info = player:hud_get(def) player:hud_change(def, "position", { x = hud_info.position.x, y = hud_info.position.y - (dt / 5) }) end player:hud_change(data.hud.text, "text", S("@1 new recipe(s) discovered!", data.discovered)) elseif data.show_hud == false then if data.hud_timer >= HUD_TIMER_MAX then for _, def in pairs(data.hud) do local hud_info = player:hud_get(def) player:hud_change(def, "position", { x = hud_info.position.x, y = hud_info.position.y + (dt / 5) }) end if hud_info_bg.position.y >= 1 then data.show_hud = nil data.hud_timer = nil end end end end -- Workaround. Need an engine call to detect when the contents of -- the player inventory changed, instead. local function poll_new_items() local players = get_players() for i = 1, #players do local player = players[i] local name = player:get_player_name() local data = pdata[name] local inv_items = get_inv_items(player) local diff = array_diff(inv_items, data.inv_items) if #diff > 0 then data.inv_items = table_merge(diff, data.inv_items) local oldknown = data.known_recipes or 0 get_filtered_items(player, data) data.discovered = data.known_recipes - oldknown if data.show_hud == nil and data.discovered > 0 then data.show_hud = true end end end after(POLL_FREQ, poll_new_items) end poll_new_items() globalstep(function() local players = get_players() for i = 1, #players do local player = players[i] local name = player:get_player_name() local data = pdata[name] if data.show_hud ~= nil and singleplayer then show_hud_success(player, data) end end end) craftguide.add_recipe_filter("Default progressive filter", progressive_filter) on_joinplayer(function(player) local name = player:get_player_name() local data = pdata[name] local meta = player:get_meta() data.inv_items = dslz(meta:get_string "inv_items") or {} data.known_recipes = dslz(meta:get_string "known_recipes") or 0 if singleplayer then init_hud(player, data) end end) local to_save = {"inv_items", "known_recipes"} local function save_meta(player) local meta = player:get_meta() local name = player:get_player_name() local data = pdata[name] for i = 1, #to_save do local meta_name = to_save[i] meta:set_string(meta_name, slz(data[meta_name])) end end on_leaveplayer(save_meta) on_shutdown(function() local players = get_players() for i = 1, #players do local player = players[i] save_meta(player) end end) end on_leaveplayer(function(player) local name = player:get_player_name() pdata[name] = nil end) function craftguide.show(name, item) if not true_str(name) then return err "craftguide.show(): player name missing" end local data = pdata[name] local player = get_player_by_name(name) local query_item = data.query_item reset_data(data) item = reg_items[item] and item or query_item local recipes, usages = get_recipes(item, data, player) if not recipes and not usages then if not recipes_cache[item] and not usages_cache[item] then return false, msg(name, sprintf("%s: %s", S"No recipe or usage for this node", clr("#ff0", get_desc(item)))) end return false, msg(name, sprintf("%s: %s", S"You don't know a recipe or usage for this item", get_desc(item))) end data.query_item = item data.recipes = recipes data.usages = usages show_fs(player, name) end register_command("craft", { description = S"Show recipe(s) of the pointed node", func = function(name) local player = get_player_by_name(name) local dir = player:get_look_dir() local ppos = player:get_pos() ppos.y = ppos.y + 1.625 local node_name for i = 1, 10 do local look_at = vec_add(ppos, vec_mul(dir, i)) local node = core.get_node(look_at) if node.name ~= "air" then local def = reg_items[node.name] if def then node_name = node.name break end end end if not node_name then return false, msg(name, S"No node pointed") end return true, craftguide.show(name, node_name) end, }) craftguide-2.0/license.txt000066400000000000000000000050561401031633000156500ustar00rootroot00000000000000License of source code ---------------------- The MIT License (MIT) Copyright (c) 2015-present Jean-Patrick Guerrero and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Licenses of media (textures) ---------------------------- paramat (CC BY-SA 3.0): craftguide_arrow.png - derived from a texture by BlockMen (CC BY-SA 3.0) You are free to: Share — copy and redistribute the material in any medium or format. Adapt — remix, transform, and build upon the material for any purpose, even commercially. The licensor cannot revoke these freedoms as long as you follow the license terms. Under the following terms: Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. Notices: You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation. No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material. For more details: http://creativecommons.org/licenses/by-sa/3.0/ craftguide-2.0/locale/000077500000000000000000000000001401031633000147165ustar00rootroot00000000000000craftguide-2.0/locale/craftguide.de.tr000066400000000000000000000046661401031633000200050ustar00rootroot00000000000000# textdomain: craftguide Craft Guide=Rezeptbuch Crafting Guide=Rezeptbuch Crafting Guide Sign=Rezepttafel Bookmarks=Lesezeichen Usage @1 of @2=Verwendung @1 von @2 Recipe @1 of @2=Rezept @1 von @2 No recipes=Keine Rezepte No usages=Keine Verwendungen Burning time: @1=Brennzeit: @1 Cooking time: @1=Kochzeit: @1 Replaced by @1 on smelting=Ersetzt durch @1 beim Schmelzen Replaced by @1 on burning=Ersetzt durch @1 beim Brennen Replaced by @1 on crafting=Ersetzt durch @1 beim Fertigen Repairable by step of @1=Reparierbar um @1 Any item belonging to the group(s): @1=Beliebiger Gegenstand aus Gruppe(n): @1 Any black dye=Beliebiger schwarzer Farbstoff Any black flower=Beliebige schwarze Blume Any blue dye=Beliebiger blauer Farbstoff Any blue flower=Beliebige blaue Blume Any brown dye=Beliebiger brauner Farbstoff Any coal=Beliebige Kohle Any cyan dye=Beliebiger türkiser Farbstoff Any dark green dye=Beliebiger dunkelgrüner Farbstoff Any dark grey dye=Beliebiger dunkelgrauer Farbstoff Any green dye=Beliebiger grüner Farbstoff Any green flower=Beliebige grüne Blume Any grey dye=Beliebiger grauer Farbstoff Any kind of stone block=Beliebiger Steinblock Any magenta dye=Beliebiger magenta Farbstoff Any orange dye=Beliebiger orange Farbstoff Any orange flower=Beliebige orange Blume Any pink dye=Beliebiger rosa Farbstoff Any red dye=Beliebiger roter Farbstoff Any red flower=Beliebige rote Blume Any sand=Beliebiger Sand Any stick=Beliebiger Stock Any tree=Beliebiger Baum Any vessel=Beliebiger Behälter Any violet dye=Beliebiger violetter Farbstoff Any violet flower=Beliebige violette Blume Any white dye=Beliebiger weißer Farbstoff Any white flower=Beliebige weiße Blume Any wood planks=Beliebige Holzplanken Any wool=Beliebige Wolle Any yellow dye=Beliebiger gelber Farbstoff Any yellow flower=Beliebige gelbe Blume Recipe's too big to be displayed (@1x@2)=Rezept ist zu groß für die Anzeige (@1×@2) Shapeless=Formlos Cooking=Kochen No item to show=Nichts anzuzeigen Collect items to reveal more recipes=Gegenstände aufsammeln, um mehr Rezepte aufzudecken Show recipe(s) of the pointed node=Rezept(e) des gezeigten Blocks anzeigen No node pointed=Auf keinem Block gezeigt You don't know a recipe or usage for this item=Sie kennen kein Rezept und keine Verwendung für diesen Gegenstand No recipe or usage for this item=Kein Rezept und keine Verwendung für diesen Gegenstand Digging=Graben Digging Chance=Grabechance @1 of chance to drop=@1 Abwurfwahrscheinlichkeit craftguide-2.0/locale/craftguide.fr.tr000066400000000000000000000056271401031633000200220ustar00rootroot00000000000000# textdomain: craftguide Craft Guide=Guide de recettes Crafting Guide=Guide de recettes Crafting Guide Sign=Guide de recettes Bookmarks=Favoris Usage @1 of @2=Usage @1 sur @2 Recipe @1 of @2=Recette @1 sur @2 No recipes=Pas de recettes No usages=Pas d'usages Burning time: @1=Temps de combustion : @1 Cooking time: @1=Temps de cuisson : @1 Replaced by @1 on smelting=Remplacé par @1 lors de la cuisson Replaced by @1 on burning=Remplacé par @1 lors de la combustion Replaced by @1 on crafting=Remplacé par @1 lors de la fabrication Repairable by step of @1=Réparable par étape de @1 Any item belonging to the group(s): @1=Tout item appartenant au(x) groupe(s) : @1 Any black dye=Quelconque colorant noir Any black flower=Quelconque fleur noire Any blue dye=Quelconque colorant bleu Any blue flower=Quelconque fleur bleue Any brown dye=Quelconque colorant marron Any coal=Quelconque charbon Any cyan dye=Quelconque colorant bleu ciel Any dark green dye=Quelconque colorant vert foncé Any dark grey dye=Quelconque colorant gris foncé Any green dye=Quelconque colorant vert Any green flower=Quelconque fleur verte Any grey dye=Quelconque colorant gris Any kind of stone block=Quelconque roche Any magenta dye=Quelconque colorant magenta Any orange dye=Quelconque colorant orange Any orange flower=Quelconque fleur orange Any pink dye=Quelconque colorant rose Any red dye=Quelconque colorant rouge Any red flower=Quelconque fleur rouge Any sand=Quelconque sable Any stick=Quelconque bâton Any tree=Quelconque tronc d'arbre Any vessel=Quelconque couvert Any violet dye=Quelconque colorant violet Any violet flower=Quelconque fleur violette Any white dye=Quelconque colorant blanc Any white flower=Quelconque fleur blanche Any wood planks=Quelconques planches de bois Any wool=Quelconque laine Any yellow dye=Quelconque colorant jaune Any yellow flower=Quelconque fleur jaune Shapeless=Sans forme Cooking=Cuisson No item to show=Aucun item à afficher Collect items to reveal more recipes=Collecte des items pour révéler plus de recettes Show recipe(s) of the pointed node=Affiche les recettes du bloc visé No node pointed=Aucun bloc visé You don't know a recipe or usage for this item=Vous ne connaissez aucune recette pour ce bloc No recipe or usage for this node=Aucune recette pour ce bloc Digging=Destruction Digging (by chance)=Destruction (par chance) @1 of chance to drop=@1 de chance de tomber Mark this item=Mettre en favori. Unmark this item=Enlever des favoris. Cannot mark this item. Limit of bookmarks reached.=Impossible de mettre cet item en favori. Limite des favoris atteinte. Only drop if using one of these tools: @1=Tombe seulement si détruit avec un de ces outils : @1 Only drop if using this tool: @1=Tombe seulement si détruit avec cet outil : @1 Craft @1 stack=Fabriquer @1 objet Craft @1 stacks=Fabriquer @1 objets @1 added in your inventory=@1 mis dans votre inventaire @1 crafted=@1 fabriqué(s) Quick crafting=Fabrication rapide craftguide-2.0/locale/craftguide.it.tr000066400000000000000000000047371401031633000200300ustar00rootroot00000000000000# textdomain: craftguide Craft Guide=Guida di assemblaggio Crafting Guide=Guida d'assemblaggio Crafting Guide Sign=Cartello della guida di assemblaggio Bookmarks=Segnalibri Usage @1 of @2=Utilizzo @1 di @2 Recipe @1 of @2=Ricetta @1 di @2 No recipes=Nessuna ricetta No usages=Nessun utilizzo Burning time: @1=Tempo di combustione: @1 Cooking time: @1=Tempo di cottura: @1 Replaced by @1 on smelting=Sostituito da @1 alla fusione Replaced by @1 on burning=Sostituito da @1 alla combustione Replaced by @1 on crafting=Sostituito da @1 all'assemblaggio Repairable by step of @1=Riparabile per passo di @1 Any item belonging to the group(s): @1=Qualunque oggetto appartenente al/ai gruppo/i: @1 Any black dye=Qualunque tintura nera Any black flower=Qualunque fiore nero Any blue dye=Qualunque tintura blu Any blue flower=Qualunque fiore blu Any brown dye=Qualunque tintura marrone Any coal=Qualunque carbone Any cyan dye=Qualunque tintura ciano Any dark green dye=Qualunque tintura verde scura Any dark grey dye=Qualunque tintura grigio scura Any green dye=Qualunque tintura verde Any green flower=Qualunque fiore verde Any grey dye=Qualunque tintura grigia Any kind of stone block=Qualunque tipo di blocco di pietra Any magenta dye=Qualunque tintura magenta Any orange dye=Qualunque tintura arancione Any orange flower=Qualunque fiore arancione Any pink dye=Qualunque tintura rosa Any red dye=Qualunque tintura rossa Any red flower=Qualunque fiore rosso Any sand=Qualunque sabbia Any stick=Qualunque bastone Any tree=Qualunque albero Any vessel=Qualunque contenitore Any violet dye=Qualunque tintura viola Any violet flower=Qualunque fiore viola Any white dye=Qualunque tintura bianca Any white flower=Qualunque fiore bianco Any wood planks=Qualunque asse di legno Any wool=Qualunque lana Any yellow dye=Qualunque tintura gialla Any yellow flower=Qualunque fiore giallo Recipe's too big to be displayed (@1x@2)=La ricetta è troppo grande per essere mostrata (@1x@2) Shapeless=Senza forma Cooking=Cottura No item to show=Nessun oggetto da mostrare Collect items to reveal more recipes=Raccogli oggetti per svelare più ricette Show recipe(s) of the pointed node=Mostra la/le ricetta/e del nodo puntato No node pointed=Nessun nodo puntato You don't know a recipe or usage for this item=Non conosci una ricetta o un utilizzo per questo oggetto No recipe or usage for this item=Nessuna ricetta o utilizzo per questo oggetto Digging=Scavando Digging Chance=Probabilità di scavare @1 of chance to drop=@1 di probabilità di rilascio craftguide-2.0/locale/craftguide.ru.tr000066400000000000000000000022241401031633000200270ustar00rootroot00000000000000# textdomain: craftguide Craft Guide=книга рецептов крафта Crafting Guide=книга рецептов крафта Crafting Guide Sign=Знак с книгой рецептов Usage @1 of @2=использование @1 из @2 Recipe @1 of @2=Рецепт @1 из @2 Burning time: @1=Время горения: @1 Cooking time: @1=Время преготовления: @1 Any item belonging to the group(s): @1=Любой элемент из группы: @1 Recipe's too big to be displayed (@1x@2)=Рецепт слишком большой для показа (@1x@2) Shapeless=Бесформенный Cooking=Приготовление No item to show=Нет элемента для показа Collect items to reveal more recipes=Собирайте предметы, чтобы раскрыть больше рецептов Show recipe(s) of the pointed node=Показать рецепт(ы) выбранной ноды No node pointed=Не указана нода You don't know a recipe for this node=Вы не знаете рецепт для этой ноды No recipe for this node=Нет рецептов для этой ноды craftguide-2.0/locale/craftguide.zh_CN.tr000066400000000000000000000023661401031633000204110ustar00rootroot00000000000000# textdomain: craftguide Any black dye=任何黑染料 Any black flower=任何黑花 Any blue dye=任何蓝染料 Any blue flower=任何蓝花 Any brown dye=任何棕染料 Any coal=任何煤炭 Any cyan dye=任何青染料 Any dark green dye=任何暗绿染料 Any dark grey dye=任何暗灰染料 Any green dye=任何绿染料 Any green flower=任何绿花 Any grey dye=任何灰染料 Any item belonging to the group(s): @1=任何属于以下组别的物品:@1 Any kind of stone block=任何石方块 Any magenta dye=任何品红染料 Any orange dye=任何橙染料 Any orange flower=任何橙花 Any pink dye=任何粉红染料 Any red dye=任何红染料 Any red flower=任何红花 Any sand=任何沙 Any stick=任何棒 Any vessel=任何容器 Any violet dye=任何紫染料 Any violet flower=任何紫花 Any white dye=任何白染料 Any white flower=任何白花 Any wood planks=任何木板 Any wool=任何羊毛 Any yellow dye=任何黄染料 Any yellow flower=任何黄花 Cooking time: @1=熔炼时间为:@1 No items to show=没有可显示的项目。 Recipe @1 of @2=第@1个配方,共@2个 Usage @1 of @2=第@1个用法,共@2个 Recipe is too big to be displayed=配方太大,无法显示。 Shapeless=无序配方 Unknown Item=不明物品 craftguide-2.0/locale/template000066400000000000000000000024361401031633000164610ustar00rootroot00000000000000# textdomain: craftguide Craft Guide= Crafting Guide= Crafting Guide Sign= Bookmarks= Usage @1 of @2= Recipe @1 of @2= No recipes= No usages= Burning time: @1= Cooking time: @1= Replaced by @1 on smelting= Replaced by @1 on burning= Replaced by @1 on crafting= Repairable by step of @1= Any item belonging to the group(s): @1= Any black dye= Any black flower= Any blue dye= Any blue flower= Any brown dye= Any coal= Any cyan dye= Any dark green dye= Any dark grey dye= Any green dye= Any green flower= Any grey dye= Any stone= Any magenta dye= Any orange dye= Any orange flower= Any pink dye= Any red dye= Any red flower= Any sand= Any stick= Any tree= Any vessel= Any violet dye= Any violet flower= Any white dye= Any white flower= Any wood planks= Any wool= Any yellow dye= Any yellow flower= Shapeless= Cooking= No item to show= Collect items to reveal more recipes= Show recipe(s) of the pointed node= No node pointed= You don't know a recipe or usage for this item= No recipe or usage for this node= Digging= Digging (by chance)= @1 of chance to drop= Mark this item= Unmark this item= Cannot mark this item. Limit of bookmarks reached.= Only drop if using one of these tools: @1= Only drop if using this tool: @1= Craft @1 stack= Craft @1 stacks= @1 added in your inventory= @1 crafted= Quick crafting= craftguide-2.0/mod.conf000066400000000000000000000001221401031633000151000ustar00rootroot00000000000000name = craftguide description = The most comprehensive Crafting Guide on Minetest craftguide-2.0/settingtypes.txt000066400000000000000000000002621401031633000167620ustar00rootroot00000000000000# The progressive mode shows recipes you can craft from items you ever had in your inventory. craftguide_progressive_mode (Learn crafting recipes progressively) bool false craftguide-2.0/sounds/000077500000000000000000000000001401031633000147725ustar00rootroot00000000000000craftguide-2.0/sounds/craftguide_click.ogg000077500000000000000000000141141401031633000207560ustar00rootroot00000000000000OggS+vorbisDqOggS+vorbis Lavf58.29.100encoder=Lavc58.54.100 libvorbisencoded_by=REAPERdate=2020-04-17time_reference=126242vorbis)BCV1L ŀАU`$)fI)(yHI)0c1c1c 4d( Ij9g'r9iN8 Q9 &cnkn)% Y@H!RH!b!b!r!r * 2 L2餓N:騣:(B -JL1Vc]|s9s9s BCV BdB!R)r 2ȀАU GI˱$O,Q53ESTMUUUUu]Wvevuv}Y[}Y[؅]aaaa}}} 4d #9)"9d ")Ifjihm˲,˲ iiiiiiifYeYeYeYeYeYeYeYeYeYeYeYeY@h*@@qq$ER$r, Y@R,r4Gs4s@BDFHJLNP@OggS+`6X\]PWWj`ILDϗ2R@01=kl-9lWT+,KUժ3' QTESgbQqLl,]+qfMmUuߥbA @a4^"c3δ_50֢- C*rWTX9M*ӗ[8Z.xYg1K{ PVyZ,"x~Y4@?aP(}Ӹ=ٴK&?:tbU:/rQ^e*V^ؑEWTCs k[e{ݻ% U|2__MVg#Ga}Gg|>YYe],kʒgr%˲,GqF2iKYőL888S/JE_WNeqzڲ(||sss2c*&YX77 LlVYM`j*?_~lnnnnLMMMX{8,?S෿LMDQ{QU@|P ˟_e4gmZmw]m֩jg9V}Z견V}A["ew0 .cs>~m*}JwP\_%Sٜ_?kWfoZ磝tx!?Y #|rTZ [_pU7g@̓_OԎ/ae^TmC.Ui@`;Yǟ Q>̿ˉk}?i땽X}t#+5_s_oe?ΩJP^g&O<ٖN}Ld#r.}>.7yyzLקdv2MMEJiC'k7gLC{ٸ-Co;3|0u,Ƚ j&{{4q=kSfzJq<$˚ sB bi?5IhwzڐM%+mje9ƲS ;V^È;7p7uUhNMTq,'@BDFHJLNP@OggSVͧQDDVgUW|GDEGGGI[LZUS\J`blec]`Y^jX^eNbZ_ٖ6+-o G6V =5 cۮ{uuӹ?WDu]QH?VU۷֟FuQ;C6<`+x˙9Wrz=+FXV+YwXT+RUx"b R0uRDZYgy|Wĥj4NwKpi7Vw4˺$Qݭ't@\+`w.45Hz ذjl7O& ;umUXvy1T'X<sxjWXdhM_LmJ `\0KY&D=2f }rJ9Lz 6Ly@m [{J@p3""2Y CdK;3t#C;dz5}4KMΔYxQCiL XKu%iΈSHa J)ɊjM]ni|8"#!y,ӾBLg02* FxV7ΈE& P+'Γ `0mY'0VT+5KPf>á1 0ճ ]â \? Q(67QS#Pdqb ݟ!NFfLů" dT%,X-XȂP`]J´SwY'!VfRPN l c?׉VO09-d.e>VZQpB}ԗ/sR`+VD: ¸ǻj8iAN w־QujUDEn3eEz梮EPUQ4*`Rv>?Tw*wG6 RbqKo2#$FZ".jԪg} b85],tbldJyQ B$Y=ҵTPlP <|)? lEr^LCNoQy5+Dlg]Z+z\ X!vUaŊ*܅~_63Q0AnqT+m @gl[puv)*铊 h"kXv+ E,h bź8$5α1G\#֯p,U~Yvj~5}eqkwu王1A=vqV \KK*?w(F vM Wh#XTŊNTZMAA1, bᆒJp@i#* ŲO`)U(m2Xfgn>8/ \Q^H6Q>;4cF-N]mMn0 ҙtqAz)G߻2%?_5 a73Tq c -,+"J JF=`FozДs Z` f\<9ͨV%u Ӡpɹ0SY s4ygW:` `Y eFFŴQg!Nt XƊ;+Abi0j*%%80N &đ!X mY e# du )`,EHMD/ 5 B`edO`"D2:tDT,|SoZ]|h.OZ/$bL3ìj*2P|=5*?hy9-Eȡ{_??_EvW3Йw|k $2k~]A-qa`kiMȲT&s3c~Sar_ro1rRDWeEЦi\Tta\H6(K̲`iY`lSjZn!X,]+ۥm,zD1uQj-jHB/-8ۀؤ0 u ~V.v!H`p$}BJNM 7SUOkԮPZ*(itVr:5b޸6{1{vYv/̒l Z>MЎrM9mC53']?M4U?^Qt|ݷҧ#3AHy=֩<! Xb:;8Wesk= gmAE̕Sfը#xuEѱP eVR(kq^ XM4bJ-HV@ Vf!`q YaWp_P@j'28,,,G0@Ifl|,N]/B$XɞK ^Ϧ3jV :%@쎕˷1u_w׾+rd[n}м11ם}z=td1TJq$ٰJc^Q,tt2>d)R6./9`ޞhT!p{ѾP1>Hjs+T5uc[ # ( @0$ Y y*8|HbMQe -YRE3Z0ȺqeC.QQɱ &"cQXZ=KɹPW`a [,! ]&qE$U Ȓ*eTX\J"eƎD.>ƔxON?%e`kY4d$DC:x78UB뛮3ۙ6텷6l/\䯉mw$f.i,=8%#tl_*}8-3顭d;5H 8)9_w0k\ ^15=Г,+S4 ,W+-rT3sZfٗ82Td˫H T(lH'0e9ZX-2t昪`aTlI1GgE4]\hPy' $%'@YH'48elhJ[˃22Nb٥E97f476NXѪ(V% iv{2Xb3 ѓF҆9]9 2UD S3"`#0zuH.g+lp&-GXr}}پ !PDeOWiER=4"+M![+K(|t*kʑ;ʸ\,Dn 5V³y#kW2 +*;Y@ˈ @g]WF2s ĮDF 7v 2`^6&e. +rᦌ*0 =ȇYgr0_Ө&%ݚ̘ɵ'6Ξ]]6n,|710՗!Ѳ+uϏ$_0t=~_9[t&%r["w\0U%0k߰tˁܪ[Q>}Q.N'VHL9YyY&):щz#}9{C.ɦ ~"Ŵ 0YI!&)p;1:Gufp+ @/ zu@.GTE7p`UDQ7enU )M pL( $RE7(@;C>eL G|2Y۰!UUHf;qf>|;ZkV|X9]=u/>>FEv̯?{ +1]`lm:tU:p۝G.Wy <%gn]R$%{ vdȢԬ䝈^f%e&7k0-IF]N4z`)kq\[3 ) 8R22`@ ග@kҽ38:o+8R'ؖ0(bRj #!k;E 0@5󗄓_Tg18U3EEq% zWt$BU%JĄ>{n^ m]7?5JO-o?$!lRz^ugMJOGlAΗ,ިy5M2gH~gC6LZ&7',p??,rݻ?t=ai]o/:>쁤:=3um~zzqBd5L2f;w(41$J2a:-UkDD{^|GmbS۩yf*:U=PWMeQ}mLע>tս31cӞϬf~ 9vd? F&5PR*P puhJ^]:aeyLz/n0U>Qߣ79|T2+4=eJ}_U}}`l`f`hznA[ՇS_ g)'鹿'gVt:s>F_y7~> AC_Y@Fk&VZ1y0 [h7;Ϫ,!87#w{&%?a d_g6Ms$f=nĺ- ΦfSr.1\./ҧR^6hP Ut`eEN, v2)V ;1,ϼhVUդ$VȷËqnܧt|^boN[!x.'_ dJ?=H>a'? 3x!["-6E BlT,9i(.˳uGc$<~}n?swFºl?=/NMyEy.j:{;8y~C|3Ix.5##v, kd6lG\ȟWRps㙳oOvޝesm/FO2e6\H"rCf՝'stu).=tsO?sϔ x)rL֋_=Xt;VnrVV~0E}v]H _~['.Θm ;23EsPJdrH vZS @H4e\ Oe^  ,\hYQ f!vλcn2N9p71_PQ/C)vkaƓ=d\zf=OG0ϦT9۩3{΂=Ϻ~39 9}S?t`ȿ9+ ugLiQ؍ucMWi%')u`ɞf)SԒu%tws>jc?hMLÕ4Чþ?l|l0N` S%Ev:"e|n jV ȖyYߩz3n@s;~đ1* !g h{0GH2q}'splu;7z_)lyw~7%MHS ϭUm| YL&/z}}woNE]jλ>jtkߑ@ݻN"+k:,vgz ӓEbSˬg5}IWr6gîi$=ߕLi<Â1uFdÑZ.7#=:^ TۚHHcraftguide-2.0/test.json000066400000000000000000000003041401031633000153260ustar00rootroot00000000000000{ "items": [ "default:stone, default:stone, default:stone", "default:stone, , default:stone", "default:stone, default:stone, default:stone" ], "result": "default:cobble 16" }craftguide-2.0/textures/000077500000000000000000000000001401031633000153425ustar00rootroot00000000000000craftguide-2.0/textures/craftguide_arrow.png000066400000000000000000000007251401031633000214030ustar00rootroot00000000000000PNG  IHDRddpTIDATxm@?e>B!C?.B\i!vYF쌄l &133333333333w=.,NNW1ɔdqGi(`QzAC" E 0A`(PC" E 0A`(PC" E 0A`(?JZwlJU^=ɚd} _ cE2YC!} 0A`(e({5P}ǹɚdqz(CoGYw>iuO׳@j;6U0?Lp! C 0 A`0a! C 0 A`0a! C 0 A`0za$`Uv%=eWLGݽo 2,1IENDB`craftguide-2.0/textures/craftguide_bg.png000066400000000000000000000024211401031633000206340ustar00rootroot00000000000000PNG  IHDRĉzTXtRaw profile type exifx]& YE$r0?\]]ӓL2ǀƟf!y.9G\UvM1:^OwbFOh_a׏5xF/Gˡ$|)]8;=zٶkgOsh=gC2 k?#} H2"АܟA^>nc|U>}eOZZzkO #"~0]}!>8 EQ1lgIkp+KAXc[y?G7gnXSy8JB|NyvsoWSZQ1lgIcp+ڶKAXc[-5mYVZܥ[^zNy?G7gnXSy8JB|Na8NPB#(dO "zD*v));M:<|o/ZW^uTm?{h& wMul_[X>v~խx0vjj. Guj^8, sԊs`ϓ{@j140`_rhHq2d -3_               F                                                    F         ?dc h3T/^jmh=TO՛Yiգyju_= <5ld0o3݀VWN"̜6d! 5??&C1Zz匊$ثnFCk>=<>ƠnGt;^?7G$y?JTIϡʹ.yiH@ 䘻#gSvnsޠ`wE@^whzwO{2Pavw;"~æ{&c=#GQ lr>zZmV[pURb2 yjiP_e=wN(+&.ru'?/UfRָƫ$LɗY?uBDVnO]#| 8u~ U0̂ f[-xv ("q0v@0E9GAc!p7޹&$Z8lFn)udyB!1AC.b)$K>SJ4eq%H$"*YYr`ШIEUsf1P _B%)Zr|ƚTqs iŖ4mcO]<ađ fqƋinjR\L'arO'gV{Mά2E`&7d N=%L_⍿cL ^S`Y nkGղ*VΙqW5KLASzYꪅV!Ṉmuh'=Xy2i*HC\س']j}c{wz\KGt\w6gǚWS& #5_D:}:lhȧGm6]2]Fj}v+~$wpuW65+iT9S#Ƞf\.@ >1S&tyX4rH#F ' %@ ʲ ao]=Pi~g}<\ś=]$Rl^$cREry€XH;8Z0̚/a+#4ǵ&j PNgBoH2.&Gقr.^Y l6o |^q%y(^?4еq y?G7gnXSy8JB|N>pm5#AבmM65y~痧ͦh00C>m/ƽIENDB`craftguide-2.0/textures/craftguide_cancel.png000066400000000000000000000103231401031633000214710ustar00rootroot00000000000000PNG  IHDR@@`U zTXtRaw profile type exifxڭYY SIpsD"vuwQ!hᅮ] bqO!?y}-[O]? m W;>7nqџ7~c'~sVygsGP o;;ǯlfNF8; m{^ѿ_/X#\\pr iaȟo_sӜc?] CQ lw+XG+l m`e mahoϾ5>nz̝k~ {qkݼk.a0;LfnCs6mM؆1gBE/n"-،c0E{u8&TsW0D|&}`'okuku7 n2@?4T%HDD!JQ9AE&Z$Ť)J%Ǭ9KTP\ z\CTs- iIM[j;ws/рaȈCGy MaʌSgy;k|~;X)wK)11A{lK.og[0 ؤ7ԝ1 p^s`+HWȎQ/#P+ousm1[a1S!ij..Z*ŀP[}XזH'o#|qF`KSF XcVV͡n-vurD+ @hd{0ˬ؃8!Qg<sH%;]<9l+ ZU>-̻ZND%1RÎ&d< H"4صkiB9cĦܚ=pM08G(Sq-qٺНEzƂuP14P5n#)AsBv `X2L\8[ϳ }ApzbH9̓ޡ(Z hap 4K=j&zp900ώ"FXe^Kw/r;wb`'`i)|| O BlȇٴBZϬR2N0~iT82#/ 5TK^_K/`b=0:׎?m1ўvV0[~>D; ( (b)S$m0n u H9Qy$pu+LDZAcx1k撵l,CJbn$1zHbQ ~fQ yOK=fJ3ݑ R-h;QC"(G7z \*{Y2jLU}W}軲sUGߕu[Yyޖg~VZݍ`G6s誈7E=GN.LpݦXRS6BtPY~u~9:wHPq=hZAl ò϶Rtu6ET\Qaϊ DVE[uav4lNt)dW(K(tuJ}^JqS~uK_? ζt LmBoJeSj)JSNBޓ6- 4W&꺏e}Y},˺cK~Yѧẘ呏rzcOIffo'^v5,RМFu G(3̰?P~mlY2zxd𧿾Yĥy]iFHue]IeV~E~X?Q7r?z KOz\~V=hDY $kM;o ]L] 6O#iCCPICC profilexJPTQ:8Cq+ɥ*BNib17$>LA |gq8p{`2rx宾ѤE]aTd=?|X}X?J<."BFY^s ̲ 8?ؤIز L2~j|B._ 1k/wܷ$Ypa|Ɍ5u} ,( \s=%KDfD! @jߟ~s9TVS/GDEլ}]rr'4x⑭DC&1O {oUΪB@iiZ -̇LC[oICW^8'7 VhYa%ɒiȍBN'aOE#)_= Z*aKABXUK}i2Z~ˮx S8v/6oz#p UQwFqώ TՓj͆O@œ9[ VT]Qw]2(0XrU5oh \X!&WHŮs|VBCjƝi{vo IDcVl=v; o;::9w@OU0=>pLhJLľ/PHlyCA.#E*t.㎤B$ҰWc Q _yx/TI̺p|7E"KvӢjT jM{߬r5 ðX_UME4_pI5mY,URVTuzvM;9OOr|x}oG+_Ϛ/tޞ/tI7p oQulyyF?k0,aHmM eImIENDB`craftguide-2.0/textures/craftguide_export.png000066400000000000000000000145371401031633000216000ustar00rootroot00000000000000PNG  IHDR@@iq zTXtRaw profile type exifxY E /AA.pNȡ*?gdDxC￶1RZZ):'W:/=iܽ] E*3xϕP 8d<ĵuV壩x\~?cXwq ^b+xz[wg$b p[O{룃>9u~?<>|?,, /xyTO 1QB `<;Hlq r#,#mg++Oϭ3/r{sꛯ+X\c!gܕ"rx=_P|\`Bxpܗ9)g\c|} @CLAA&Fc N)<#r(&ȱD;`I9s.Y r/K)RDIH&ƚjJBp`nIރ,ԙsgDFMhզ}>#<ʐQG}'41˔Yg}y`V^eɪwy-j_^P )OPcԉF'0`fW $E 7!iornw9@W~ڴ:7bwOHCzvwT@Kt],"9d_Kru=nM 41R3kU&Fڊ%l$IHm+Ǭ TAQu/|TM΄Z='V5R& t]f'rٺ]Gͅڲ,zKͮe2鐞x%C]n'&/6&̄ƔFY ɱ BeπMS`=rmm% Imʫu?Zkε v$|;}ۓ]f'#X$EϜZ1Vl]pdXN,{՚(Lvm 64R"A*P@XLf7#3]|X%("8!:R 7@`'k$Q/8gccʉ̯ehOB֑? 8ۂ7 ,qHEFLOd4XjF־`]i׮Μu-oTN趵qYBGq54V$ODdPi&qgjrhV(r'sɛ>v*L("npCFj[PԻ{IB#~#T`b3s"1FLF櫴;{Tps9 a_{l;\$yO[ ~-eͫu\8[Ut_nXnX^1m8=?*0jM壊 ^ b ٘nzOjsn Vcx(Өg[M >O< #E0;уĴkz6-Ķ -P.H!VF;ez='9;WR;Lu\)#J_;ā,cL+@ /U*p2&GRoCbxTNǭ0 u: upAsY7uGC8#StjEȴIVѩ[Tq؏>24-rPVo~V1Ifzc#LCoIm[]1'2zv"-&Dd}kմ$&hK Y] (Ouq4 ،4Cy ' pZtcK !)>RHbp Ji֯>uY'b 7/z#|Eȿ7E=Vr{c)#^L?J6gbwQGn-d0 #%bTKp3Fjr~1Ekh~ݍW;_!v;mM$RRDK5ʣ%a{.iMӥăutR|Oi,I=r=hEG2~DiUn}M:Gr(kN͕l*k%:iCCPICC profilex}=H@_SEC;tP,q*BZu0 4$).kŪ "%/)=B4gt̤b.*"(ˆ!!3˘4|=|,sA`1 2ô77m>qexܤ ?r]segFlf8B,XbV65qLtr8k:kߓ0TWN3 "QA6X~?%r)䪀c5h]?*NMzI$8@.j8N3pw&0IzŎm⺣){0dȦJAB7-07!0Vuwwv?قrw.bKGD pHYs:tIME 0QU LIDATxݛSXw f36I:3=f&b;C:bueUՓ$/B;iG(Ӑ9{ϻkeYJ)y%yAX%XضM"T*988km!ׯ_o @v θdYCeY8C.VAӉOEdrqq)Z\.$؏ ޶m$sssEDրE k`%'''<I[dbh^cW/e! JhIA=p'.K` + _`6J~rMS@g > tNp%:^&A=hg}q1i悼$äwS!}?aqqEc;#Ǒ|.02΁&h4X^^f~~F`0Xu0 C BI8::ZR*8;;c0q6^vp8<\uI8`- v&^ݻwZ-t}<q3 yf#~Zg1ɩ %J83fZM:%I뵭 2'''q]7-E#j ȉE5cA CmgϞs˂OD"NS,)4 v ;6[c$<#$aJN!(jpj-ˬ8di2v .8cC !a NFYrL^t:MTP(Pt:mEXaz}TrU!5Hi899&@WJX^^M׻1X&`%34>'̮:R1bx>pg2󜟟' p̍FhT92 {{{z7Hrrٵ-uu*qq%c(< $h'LrxxhvWLgEjd"J AqG9%iZfe^?::$H~AKM/{e6bK"\t:.ѝ?b#1A-jueVD|#S֟!ꟊiq>r~I}C%oЖ lmm?'kk|Έ:DGYj2zo߾57%~MD?+jޫMv8_IKRf'~e^5iii7&Vd%7NE[QO<&&xl(# %8$/HWJx3@8*Ƈ]}]SO0EF\w;? 7.q9=2 &x}Cɚ?q3v^}LhL ,` |imT*%J|E0IBmޥZ-#+T|k> IN<"SQFO/3 "VG+JБC%DU04xAܸwW ^= *}{Lƚsy( (sk"m|[G1.]RD& $1J_`\@_HmݔdLJ;#`L*_NsQDȾ~ffbޝjۍDP~$k=D9g^pO;;"KKKlooUNOiIT%ۼaF˲FFeF[o,eY~.auu5͏1oؿˉ7%M!t5hstNƴX0f1,o5޲055Zxs7nZ3w~̺vߋ ̈́mΙ#zR#\ޙXo$G-ijVD_*&(m!m&6D$R$ 4Ld2 @j\Lw&FJnf<{{g5&xȮG7ed&n@rjjB@ףfLMMl65YLG dT*$7"F[ \)E* $"VVe8n6R4:]G,8N)˴ZpNyDs=I,oDBJ(3|> xM?==MVc{{6GGGa^I3s/^`oodo@+arIENDB`craftguide-2.0/textures/craftguide_fav.png000066400000000000000000000156131401031633000210270ustar00rootroot00000000000000PNG  IHDR@@iq zTXtRaw profile type exifxY8Dy9H\լo0ǟJKU=QRhHP߿?RBrQsI5Xc r?W<O|Nqv"pH5q} ?w93s5ؓ;$9ISގהoNϬQx f4IK\og oHEDUԱILȧ彶1@sFmob,ӱ _ÃmF_|g{ݫk10ۿNܖd~|>O5|^|>v>~)ưBfrɡ!7N|YL)`8r`%ş OKg0q{ [W0^3 Cr'?Pv{[rpSw y3!dO!dc̃A$C$5ˁܓ6hHM$MX1*ɱFEUM8ڒ4\˒c֜r%܊XK)ZS͵Z[ XGzcמzqH#2h3LL32l˻RtWYu ׶uwu7TjA-G]ί!ɉf l@`] 9쪁$հqb@vnNo~39/PVAB%dWi.fE}'ӣ:m֎\rmeU9S},eWסK}gj])7ka{! է|=0yYyN#=x"1SZ\#S_V|jum }rzk[l[+Ok9`q͜cjs# 6iJ3wl $l$2jTaVj)QZ, ţ\Mc쮙dCT։; `^*{ERԼU4jP3b~6ndRa4A>K];((a%;Ӛi]#@@b_]r=hCU|eKڢKۭޛJ}CX>h 5N<\"34ILڈ 'iGE6sFw7+kzhSlLGG"/} ɐv{7<%Ya"vo19F$q1:ZBJ(5tӤzR?SJVwSC5wa4dl*a1dG׸I_ڬT#eJ@}Eeu0%Cqff`HKY#1ʪ6b䝐\qWAMLQS#Qr|G6mQ~8Cv\x»UBa1C_)ؘg2؇ lByFȟزzY1*eq0-+;PF|2&P5KThWEAJFZgBuruSg͓L+]W%eMx] %XaS9FLQ5b]x=b@_'C(Porn ..Vb,+~>va [߬t<@(ED&=A*i=0dQK\%u dkl1hRKHz7RV&GLBr(M-F?-!?F\.Y"n(ͣ!"WJb,V)ߋ, EJR{i1,R$-'A2gQlѠl& l,P ]uT?=7,Ir9;; 1Mz;߯i'q>6O0 @!O;X-Y8w^2ir<rmzĭOL z( -V 5`V=.뛺EͿ{\)]q?&1y\^v &Hb(l͉+5lz~FQ#T>- iQd.RiCCPICC profilex}=H@_["-fqP,_8jP! :\MGbYWWAqrtRtZxp܏ww`4k tL'b6*"0 3˘|=|,sgaӛyX`%Y%>'5ď\W<~\t933'b+JFzh}@7}~wYM̓'ֆ^f(i9U`a=]H(X'e?e %R~|ga&+Pșw*'@A Jfjs 3sgWUS@C;Eh̜ǹLMQ)wJ^sX|gZoWGhf9PK]ɘGK> ;S~yhKs Qdi`X*@Z[,ˎ^_XW4jr޼ߧx"EoHQHxM>'N1 тr}^{ xtA41q~HsSА @ @{"7sLICuŅӊ9RUIt+rQ-x;t`4ak~P8AA9a0ML)ee8=xm.81_\8-σFM9llV:]j9Bn)(fMHe H :b9q9(%868TL96}V@fF LD㣟{\3jKԬa}Sjk6`s CD >qJ1Ujɤż\A('S hzTJ.Փ4:ܳ,:K'|ǴYijViiZjrf /yU(fkRkWeFZ0 D ¨$m傢\sïV4k> 8-=òE _n樓Q& 3 30H?gr/i!`^N 1!٤s*ƬLՂ3{%!QC8g}yVVSs/i[g-HoP =.1AEU)*#͉gU*Z#K (:FW-?#աzVF"yQ$DV.KH>f/~H L wj|G'jT_}YXOpljl BBݑ]Uō9O\LV`v2SLPBB5![WsXB>|a-M 3`IRX%j~)5Q+B5Vn|Ͼ~ 4 фItU%wX-Kג^XJ^7qC>Fs!<}Akh ِZŴ%&f;v-!_fx1j~א_y` ^86qn\*es!ދ0=*gp"4g$lli W`-Q;b_¨[ w7E#mLYnlW=ՖYHoM~'|!":߆ `ٴaLi~Sٔ2Ǡqeq+ufaP%IENDB`craftguide-2.0/textures/craftguide_fav_off.png000066400000000000000000000117471401031633000216650ustar00rootroot00000000000000PNG  IHDR@@`U `zTXtRaw profile type exifx[$)EY,A`9t|:?&~j>Nq|euŌEQ/r˼O^ϻ.W6W@e;ou1\CbcE40@ wP2ˁY?ֳgi3ؿ|{U^m^5 9f_;UA03خ~ɿuB|`"N(%/ AǎO)L Q$aoQֆrar@$ɢ`SV X$ŔRNK5,9slIhԤYUVmEJ,䢥ZZ Uȁ檵Z[ QCVc}c.=s^zm>#4QFm)41Yfmy+UV]mk[vi筻+j?5RN_QcԩNab!zWCU|3̮P26nzC !mrͥ-¯sݟ@ϸ}ڴ:7bwM/![(u5R7 icnYL1:sy>v)]g.T:ck/!;OxpL,{,ԼfݯN?ʡc5{ϲF QTB%aɨl:'_"JKVY}9tmb6S;dIuByGw-дTUXT3}0pY8&@t쵚egk2x`xudu`:a?.ړٔ[:3wP͘A]Jڈ fC{Spe64y4#11́'A^)KXdk#Elق^:fNjʰ݈=C۩S[԰l[{{TZ&H&%Y&1:&bn,43 y[ѰyVIfL>؂:lLp_xKiWǬigy";"eG旜al3ÂNXvokS%v5)x!`15 fFRPjѪD;YIuFM2Fb_\!CovK0T;^r&cL'13;/:inq{9rQgʙr] [cѮr^-`}_N6KL׾HJplSݦJ'\?+cĜȺw('(v@ąEDj'̖H>d:V="1yJRKV^ٺ/{[`?w,p.%I񄃟  -%WO W  ХlDDZ23M"i` n8~e,*Nzi$.o~5Ut! < m(`Dn#rdB][G$1 zSkuXTj#[N g~?]V9 ٹvu瘥scIL: )3fbo)m@?6Y5y2 Fi,OϤ0bS˕CZvB٠gk>bވR5O*,$A?-ȹ!Yv}f GfճfJ&#Akd~]}1;Y{~ @F@x)=fusv7S] Qo}b7D0bPA ee !Cr-O<2(|iQDk&&yܸbjg:1OOӡyK)!4ȷf}[s\y|%<.EfǙ"pya0C|H]JY]kNEiQj#jIbQ!75:}Zp$[k+$六QzKGIb8ڔdh]d1&P_Զ><WbBЛN#xDI +&.G# thfkM[:|+Щ7.kOl0wK-wXr0-O#x{>Qn[-yx-`-z@{jjZ:#z3vҤ) 0iRL'inna׍T#iCCPICC profilexJPTQ)uH 3T (X&)&0' otp0BN´\ޅ4 .GWztie>+їjDqJ,̋ }3r*CP 4O( ]?MfᏧM'.M_S-qWUZ+rP`)~(ig+5\Olpmѥa`r-$V&3H13d1,Eґ{(t[G`" ɤ`1)b4,,z29,ZW ɈUXG&K~ Ӭ1M:iY-t31- <||=YSz"3,_!K#[5tlJ,>uaCpIG#0LL,ҌoZvOa33 khХG.>>yl,& ;S(QaRdeƒ2ا&)yUZСC."ҥ"K 4id&VE֧YAYآB7f` 6uj46MڈnjQ"$ Ѐ|0 D/ArpR׸B/f>d*TFBAZHf8]F2O]l,e&RfyC>1 p񁀠o;aA:::{,I+&Gl #"'1mbc"4d9reݛI%gjJ}mz*I2~ HT`"Ey cf0fXd*T934#2z')0 &KȑƉI%%LLҎujTriRt+L2E }66#!}d$GKOe6-sF*9+XHA WW9rQqľܧD Sٚu/ mL \t҈^nzu>IJO0 /h߸:rl @c+ްΆ`jHc|3$|ay;JzYuzhq8tN`IרP+rƻHeR߈BU:?'I]3|K#~9ئ"xB0qpw(ooն]D_RaKu?-dJrra =ڴt\y?-/xIFp q\~u^IH"ChF#C^s&`OC~ =Z1SHod9[`Ў?#{bXxhQ #O1k:|5WfS&:#!*8o||zJ5~fS<:8]y%QKj\ -IÎ@x4. ј}\!҈ZN=<O[h tl':|]SgO;VtErbG-.d]bSw+h2HK 8jo wPVsdEAG`b'Gڝph%SQq \]9F`B b ٍR|.]<\Ą8sԘAD?MT5dFB#@"OF!!9A!5aӠGsjȫ"]MeX CGh P%G O, R"  \"  8` 6L$+cSQчSnԛ* hxts6H&=x1GslO:4@R!7 , 9U||rlM>pJ.ɳkOYeR$>AZX6fL ӟlUiۼWsL.nK(L_h/g,+T*P9.-|Q|Q}|0eS_3< KLCυa2t2qiRF=~lEo<4=Si]jItipGq@*?_9z>& ʰoҠI)ͨJ..BmYqjɊ ~^_w3#F I USz^G2H=|;~<{B78>ߟԿ'?O~e]Yϯy>ۯK]R?>*BTs?+ȟO8dl@pVG>i~Բ8Q/D2 {api;ߟ{O?RE[4(y~Vn=aMOa㈉'x7wKN}FY}*7<)Zϵގ+ȡ)r[z~*Q S68 ,Co!~fo|~P_o3?O;P"m,&f:jk -#u4h7fTri'],դ&aћ'*O+ MV̬ZذYs-jJf˭4kh^[}9h:c93\'Ϭ*V]m5gm{tA&N=38Jͫ7>|^v-n;ٵEk픎k?ƳOk?N%'ѱT"oNY豔Ωga$HaEz󜨎1ٍ?{W=׹GCokG>ߎ}X lӺ֝eK>fѰOjňN2>{<9y2ĭ[cSR=ܾjW&+\eڎgAFQVC<{gzY(8zxw[؜%CXw[dZn>u;:k{|?Z޹}WgSfvWw9o5.2:z{J뜸J*[}]{/M^SwĒFn^V +3f&3anZy NO4JL@m,3>Nw؝uoljƉg޹xG{n=)꼥=l|Epop]Jk\{nk#ysq*UVnc6-\N?] 0:jq&gC1:m*< 79r#Et:DPi# 6iE“CDT&u)S]"iC>T"ru?Y :R ]XwoFztk!6GiAϦn ꜥ¥7P#&]V`v  ea]VlÌ g9KH,W ==n;4["es8N.w[nbY_#dv r:KDLn/ăce`'?ud1u3A*B֢5PZ4e/C }KCa~7O/^ƙu)Z" :bCp<*W>wf&-5́%%7@S#RX` >h^KH5ۥ3r>kl64 @C<ZPL6DD\i%,FzƈN'?-jYf4Jp;}`-\eʠ^Z*G 4&l&GJuh_&&5ր'Vta6ؼrv~A,j5KYkf'aXTØNQzr Ssa#1e`,5sqp%I׮ڜD" xq_:= >R=k*u<1m~bi$]}szÒМ U@\00>xs8p#]x#2ʋ!p@`phyHxb#i;03L')rVD8\-;G\[7bW*zFdAϺn #y;BUFSBCNK!m"ٕyݥB, ?:<>Y.bP" 'ز[R\lʸ*k%O JuyO2Y:ntؠ>jm<`+F-1%tV7I ;S~& q(6ɐ$Rs *qp2 !z`;P82T:Q̩S9`(DL氖z恣DS F$17rלb00 FKYв0 \q(¨㐅$Br,byB^HX[_V@1UFSl{ B80zol:edrSQHgm>ؤxv@A"Bb|meOSs,Hri$<#V w'- : Fx]MbZ_;?DFeh }t &n֌187&~(_& 27Ǒ`ٍPLFЈ101jp  rŔǩfD`#CG.HgL b&U tf$&[ܣFpH GNf^o;S' 1f'ĖMjlMHyr ߔrhdT" j\[iә_ E.2wP/~hGa, `( 3t;E({A (Y> PhTb Ƃn,4)IhRd3#<J70C1ShITQ! xɸGp1=+?KK*.ե=Է(jH'=b37(#FP&ǚP=t+L'06lb$u"d{aF":`\#Y4!g:D"sJ*g4qa?oQBD ѵE揉JG^aCs-w-o4U?KXExA9tx*U=E/ntE(c&.(+։mx xJ$ lYW)0-$)A=`e&C B׊EjYUi?@PBܾH08ҞA0 Eez!:70`&#2BG}5e1Uk^^\waS 8;4 öZk ZK/;0$4;\` 0N.`)^ ȶ!h"^o2J4NH'x!^MQIƞ)dTкEiDl21:qM 37A[Mљ23j86C32.FT8x"8`fPf'F7r60`ȓ"IidqF^Bh>bIͻѺ ZVI2mҸJw"~]bhTtS6EDj T>S[%ͬA2ID,e][a4 G@S`;hCjI@y>h}J˄IYk\xa5XJ $^t%x{b !}QQ/JnGH#aт~nL'(#Qtj8!]1:#: l$,϶' Vۦ| ØSE1k( ܃(\9cRԯQ#)1;$NoM%K^>O~l-^%k}=HFz@S1Kw0ay}*@.EWj7P7}>JO<=(*J (b$taKUT&¬6`QQ<dž%,S3%:4($pu::3& 2n#RZ1W'eZi ZBdFH;#,"2 '>b9a++<8-$cG'LE uݠNFQ&‹$9e,-FU}N;>;yW qzH[pb( 8a4s ݨVXx &ӥy00ZIsNǦϸi1KA2$٪?ae /A1,HE l 5!}2xtRFϋ iCCPICC profilex}=H@_?EvqPVBЪɥB$Qp-8XupqU?@]%1ݽ;_/3 fdBV+ϊb >y?G7gnXSy8JB|Nn`-'4a"A0rxGOk] F]UェޫRDy孍0"$z5<6&+ 4rK]Sg=suc攥c~a֡/MՌJi1[G:g)0~E29il*盥ܞsHd7H1p Sz9Cv Ƒ&c|`_aY:m8sFywbn{\!P38l` to 6iPD-M\=8[AѳAX%tIhePS2>4>𱄒!*vN>h`܏T nX}la"3^oܡM@SE2%R$@#nCzJ{$Y;&K=aE8m B0Xca£q?R:cc}~Yn~_7uH #-b~mbJ;6"G4Ɔ7_v EShg5;EDDDԢ4(&Eфه pGcx8'Avi}+Gn^/$Ogsuiz:,}?ތsFNP1{5s8"q AqLGxB6qd>K9 ȞxdGg 9eEWyY% Nu)Klq3/CgG3t84gD]+ƶcɀ2(l:#&6SDu`0b1f֢!PVhȱ$ȱ1C`V:0?NO4?}>(k?<'٧\ Fڏu9`(j\ 6tʍ{I8~^ `(G@Ŋk+IgnPF,+p84r_7_96Ƞ#ɶ`4L6yB6ٚEݾ,^) `0@CgP{R'M<1kUcZپc?bCXX Ts\Qmn"A!?D 7k̍Y;'3;10(Ohuhτ/AʢHP;j &rfNg2&#KH8[سi_V]6M"'P۱c=s;f;1-Nܱh&=8oㆣ^>c(_ |E5B ? Nv B>hx'W"e_UJYͬ]LEv]p.`,#ţ?w~N9*}0PoDkeCMׂeRbJ8 ߘ]}]3*|oD$h -~mR%Ktnhd);IaoO0bK@0_h{`fv@ޒvly]3ʦڿ űgc3à7)E 3x ]~Ygcs{$V}/*GX?ݍ' YXgڝ8 (L[ÉPYx/a'66[OL!ꧬ>IENDB`craftguide-2.0/textures/craftguide_furnace_anim.png000066400000000000000000000122241401031633000226750ustar00rootroot00000000000000PNG  IHDR" zTXtRaw profile type exifxڭir;Fs CdD-%]ٖ-9UTLO\^'CO3>r)x?^y0^O~\$s=@}?joq5zxz B0Qq۞w}!U>o9`G A~-O??S,@/Bzz&~8>9.E~9FD*-Bv$>Vy r_W/R~k"Y9.a}_awc\1k-Iq%SW8QROJcZq;K]k w~[1n߼9B|{o˰_"!Vn^CrbOm}B D]XLHdאJKql$h6&De1Tآ32D" hr Y9GrFI%Rj\eTs-VFrCd)REIR˭ڤ'8ڥ &5peƙfe)>Ǣ|V^e%FM MhUѦ]n;vPk'|ʩGN;l~E+kfqՉ N匌ȸX(h9--s3#(Eˍ`#yXNx?V\i[*sRodΑ_Itn݌=(zd t&[c K~'7mʽv= ޡ@jM+ze3a{ Q=;yռw`Uڧ!:1֖VQ1\ʃlIOi;+H]ӑDw$3bs)cnJ#kV%uh0'ĠH0B{,Q;dc` 'aގ*SyeޡTkZGW쥇3w39`;22o$BfC ښKΪMeS i?:5R)"8Hi4h0`,IJ:GeMM!X۔hWkߢŽԲ!yf6ZAU#s8@YE6nxJAiI.+Tru1&ۣwG&jca_|&.Ͻl%[Fa. t(6$` ""iYllE&Hr)B֭)*ZBgڲ7J+S qlԪt>0ǽDk|>4(](N*=l-]4B)p>*=SnJ`$ix!ΙPp32o)<^>~Zb!TtŒG6EO;Ğja j䵉65EtJAw)}G) PDWY+ڛd0 a.[@pM]5 5=˧P;m\ܩT܋=Y4Y] 9T6F4#3%o(U"^VD5R#KE!4AL|  h56pÑE-17b1kPĒ`Ʊ(ǍzrXwU"Á<^BnZJ\e"Y(5î0 1Eȋ$9V .-zEP-$:l tv,k}}2~ձ@Zn}Օc@QiѶ7V#o_y-v|Ўv+gYs' '1*XOpQH=HR;O.XZPa#BFX ;oz|TTϨ3̀ FZ|iiVO#Tn8'q;!bN:%BSH/viջJQke- [hcQ$ÚS:cJu0F5yhgd Tf,b]LlnoXMLњbUdKc$rIObjͥF Q1c%Y\C<|[F= B $anXhJRŐロڭfjW:9UZsb3xT^GgVj14fܨ]i>?X25JCrŐv ]&؉$%W#S  EjkUEE*Ԝ$ZIKf 3&!S;v6f 9bi)8 F ɘ2GR7f0$N54 o@i\P]Ω٪xL.H4iCCPICC profilex}=H@_?EvqPVBЪɥB$Qp-8XupqU?@]%1ݽ;_/3 fdBV+ϊb >y?G7gnXSy8JB|N,"+e?XItZ0UYe/@\H802׷P( @.W{8dU&uiBA\v:'TXrBIq]n&e.N|ىw#Oݔ2AG(.kUF0䒍<Гm`$'rF[m

<#|H4lӂd[X}-z8.51I]jrҎ<8|C5-x5!'izaˍUn5NBNG*LR~t +zۭ3{$`# y᝷*΂S5` sy >53߳ܿ`#L>pEo3h]'MQlw ~-`sG!ʻmh.;+Ӹ+n\ 415[&)S󋧰7} o4̢vלrwXJ^0z'-+E+bn] u(!`0n[WnͳǺ3U g69]r9( sBmT0b' 0 -x^K-1z&7BDpP>cK)Rⴚ\aρ#'f\K(IXDL٣Ɯ2g9BPJPcM\KCbKJ;DOpfS0H&:ȑ˅ȷjҌɦt_}zCډ2Mk~y/~_Kx y?G7gnXSy8JB|N5#RgV^}%@*!k{إ<՚s, 5U\fmujMGrv) Liԏu~{Y-RVEۥ,༥o"Uz3g\v)ij9cl`EIx^ &oBq8F&!}%f&!NJ4K"͒=Jm^)51y3*<}BU ǯ7}NC./r?CoSIENDB`craftguide-2.0/textures/craftguide_next.png000066400000000000000000000141011401031633000212200ustar00rootroot00000000000000PNG  IHDR@@iq zTXtRaw profile type exifxڭi:sqys9|$]eޔÖZ Ln3e_H1%_(I>ο? -rvYbp GZ`d}З]N|gnk{޲63<,)o_櫛ya+vX5O.6?W͜`=9D÷\x520sGc=h=9;f751"]>9#.;Ϻp@DMBc1(1XbM>)ɥ$II Qd)R!ǜKƒ\JՙD*W4| -ԤVZO=ԥ^znM4dQFLbg2,.|mV\iʫ@B/Pjn#5{t3s(8S̎lCpbvGPD"bcUĀ0L>n&C(t ' չ;Pmzx2Gm[P`Wc#1y2sMy MYs1ͣnOjYdUjc+Yv6e7ޯ)E 6f>|hpߴլfyί5/{lL)Wy˷e:˲:TM(cӳ3cl[{kF)~ gc6Gmџ>9fXisL}A|~۪'?#n 㛯!_A_nv;QX^+c6NLppJE]:kLa8Gp )} JS@Ssxdhl/D\֭ЊY C7!C; L#?}S 71=8%yH%Y=FyI`CCcNI#I=Uөrx*NbU+(9 tGŸ8Uq$:*fkm~ZN chh`<ᱼF2s{  ^OQgL`b2 Zff@dKv(P>70nX¬<> 1ei3D S5vdRQCѭJ4ﰿeZ,Ң)=ATvΐ)"(1 G#3k5ߤh(y"jFZmc2 KQ,x,Yf /7ϻQuD2vS`)dqjIR)Bz!|2 EIq,Bin,nj֓~2rza~DDPԬ<Kٵy>O)0ZL&|JNfÐQh2CU YЏh"Oz|Jq Q{b1ݞ j++*=IY&^#/`%g0? ?!y/4Xie2je|H_>C nk)P N 4$K: >tR'vc _RZ܇`|@f#K oxwVɭ wL]y] z6-lϖcK1wg2lK>>b"g"{7S=$!U%i6wZo |E,¬3#HKf3J|RzN)o||[;%(uv{e膒5d0JK$+XT=Swn c3 S<MP"{mu0I]S[ۡ"6~wT{lvV쮞uBE뤺^=Sc^~W9Wوw v8ڕK1/Trl:6iURn!u͊R$؈AaDr_E%[SdgFo;eV]EO15#;ʰh3ƳלVgO;-~367mLthX1!N:ӋHotӥZ9.iTH:x4(:]KDߟJٴfWrdD5B'U?;%Uu&ws?y|NMqοyl^b7q_ld> |JJrMkTZ4ϖRf&a%1$'˥BO0da!Eq2DQmcConS/QC'!Vؓ!Yr N} 57g1QE7[UdShg 3U _8n9/48:iCCPICC profilex}=H@_["A;8dNt*BZu0 4$).kŪ "%/)=FfW P5H'B6*_0C\b>')x{zY}JdO eaolZ:}0+I 9A$~~62y0P`YP#Q?yZ= Cyme4G" BʨBViOxGH.\e0r, ݚI7)_lc ͺmv<WZ_mOm-romM.w']2$G  }SzZ8}2U88Ƌ=`r/7zbKGD pHYs^{tIME 4opIDATxOoLK[9R*V'FQ/ o4?oS/LaYuW 'rRBK;L~3N)iI|}yC\X.˅$I|>jkk =z7np~ 8 /?:Ϟ=޽{155N.^Koo/W\k#ǀNAWVȻ >X,F2$ɐf4y2[ZZŋk$lY@I(X WR [ $mJX ^@ OX^^6%hF.ۺm ʒ5%dMuuu|%O#D"a~wpIX5Bۖi.$YXX HN4mXEUt2PU"4(O>(K0DB' *~^SO&|\-fYյֺ: \zP(`5Fz~XY4x y3¶ 0 +466 L!mm_> w Ǚ&W:u7oShNEy2; |@$###LLLӡ躾uW D~}q ) s]uTD5bZ,q3۷y9߿7![N[n1::۷o JΝ;crDډHǏ311|AM (GC^~f2dK !cS^^^###|!w,|Mee%@rΝ;^,^;ۉ?*歬& ;١BȐ}31l86lb?Fau `m;,V3bf]6X-6v I.,̎=3ĉsp!HP K(Irt"Cc9䠁BSJ9[(%W|XRr UjfkRPRt߹.=׆~CFij;_^fvDJ 5фQ9a Yo( U̶hb%` #Yf1@軱< ;roF;B@WܾAzĔ!+wQK oASU}=&A2[\o2Fuߨ@`F1,zP[*:P;)sa~ gGݏՑk` Xg8 An[F{H49,T܃ #g@7T[ΝspN/Ph11{`QO9FFj 9#wA}R=quĀGlJ1í]WY1Z>%ȗ+t۫c(vZip BQ*04'`*:XFoL(՚0>]38'Mr +$VJ!J hP2j- _6m^5Zɬ6!bmZC f&5=!Fbcw?8ԃHБh9B 2 B: *mJ h^&q4 3BCz pv*@f>EZoY'P l\I (͠J2LSEe(ox,.5:%Ȳ5<1ci(A4Lhs<"ޕ kK7p-]f(f)X ne&E)4XuY~=iEh mڝ52@ W{`d*^Jjuybm7(rfȏRL?k"o1~ȝ xα;=f}D_p.\ȩ%zMA/6;躀,vuYAd1 ȺuYܠڢ@d b]. JdJdDd b]. !t]@C躀+] dGyՌfs &snX4,Ĵ#_ aNmk(ͫ/JWAW)MҚ4[䐵w  БI.`b#b4>i|?mrJgK0~ra< @&1@ic0$u$B1܌Wfp[CP0ELxPD6ŚAJ+.e@:23OB1!OhC1.|X?9N (P< 0bpu] KF2Yl$a$ok.^3ϛYޮ\4o*Ww+'|Dsҁ=s>@J@^{@O=3/7OK?{;Jo& ݌/OMkOK #:`uykyS ;E{@hlZ ѾDޗѥwt}y] 1;@-B:ns $SfѶ! k瓋{vuέ=uZgZXhٓ{̉i8Y*AEhg,sA5lzR}ւncw+Jt|1wl,a 5#䐌҄3HxwKZ` E( >ٙ! N_ry-ӻj* yutriCCPICC profilex}=H@_SEъT2T' RE EjVL.& IZpc⬫ ~89:)HK -b<8ǻ{wP/346Sɮ]aa+3˘$|=|,s>5g1 2ô77m>qexܤ ?r]segtj8L,XicV45)∪/dF]QscigJo+u`ZKuKS`ɐMٕ4|x?o@Ϛ[s@JX}ۿggr ̤bKGD pHYs^{tIME 2dIDATx[O@YrUD Qꃊ&Do|ѨKeMACϰcYS-~ߜΙcpX 03

_5K~8,DxUeEN`m$DZ߅&]).Zz%DG?N#^G%%D `n=Ii`%B.yT$t.K;x=W(`s0%]`J DV|HiH:@" O$!cҕ 7V #@$tyͩ4$ltZੰ1tmڞ@$DYo(u}v Q,!*AZ%DE(!*OiPmJIBiH(U ɗ H)Hhf"* nA/@J nѶ++$S84,O%^ݑmc-6Mwp82#",Fk"3rDá(i7YL{2r,Gg x&NȲ`^&i 8g|X^rEfF3nK'[KTF`_&B}?&Bnfi[؇djNMX8×. _-_m _ [v\Y@]3 |D̀[Q $4çL4w_‡vd"ETp0B8-hroWHLKMO-+&㽑{]V |P$jV؟W8J4|h%EBw26{7@y7uN(De x:tIENDB`craftguide-2.0/textures/craftguide_search.png000066400000000000000000000167771401031633000215340ustar00rootroot00000000000000PNG  IHDR@@iq vzTXtRaw profile type exifxڭir:sqp#s@v?/cEdJ"A H{J.I|ZjsQo}s?9<_/㸫ϝP Xd<5/r彩#2:wRKKX(|ۂx:#|l\_9N1^%8^zN~]߽vCȍϝ\nfQxlzﮧGl.Q^,D De;o<4!X%0)w(DlJfPGR on;M_Yyy ,_sͷ_W טaoJo';U"(͕ kS O#AxW3.bm}UB(J: ^$, )\ 5ڼSy6H&!1Bl)*%H"IcNYr%K*Rr)Vz5UZk@iV[kY3W#4dQFm |f2,6 +.hbUV]muNa M*hզ}wڲ.[Ԟ'jDʞ+oQcԕŌx`1O)X,fW $["F ۿW(nN-9gD퓨-sDBɾDvG*o]cglT.yY,҂::Θ9VF\ /-۳{70Zoذq+=6;ƞco[eFSysΕF{{Q_%,juqqyvaҔ֕V+fgY{8A 䢠Ԃk\_9Q5nͭ[Sb-\_*Qסc쇍;ȽeدıN NX-&*{F|FYI=!1˞2ťm7EYk/',IeuȉQ.iUSgNaW^`BszM$3^?BY$Fb]|h=Jxol{ץQ#Ʈ1RׯV֡lᳺJ볍<7,q EZ,'<[d}& 5V;S)8%xp1;+EH[XCd[`TmS^2&1AjUQ2\plHF/2 L樓9@@{ ؄0(g'c2lۈp8IVˀ~ MRv1: iv3L$ HIԦʸ#M?TZOfaX玸ip'$UfY=֭7 A> Gh-!l;Y}R+Jv"7plvK/!_Ɩ!4ZEYSUgٰiJLD, ,'9xhD^ͳ >8 [ęBnd'ȟ;Tۡ \:YP>zk-w}`,W`{*E]׈h-g񹔀pˍx 3;6XJИl\q* 9YȦ 8HX %ֳڶcHi 8X×n6_'6Ժ4c֗Y*Ҫy̠.7޻Xxn y&?Ok'=I}K  t6:z/mуB&2Y;k3$y22"=T /h(qvd4&.`7@׬z/la;(Li懫}HM6lr&2944 }_OJѝ=[v% ԭj}dҹ?^py'SsGZ`zNIBRՓ:vYh;“#I>0lZ*yJ2a<= /I޷CvAdj紆{L=얳)/Vha3=j=@<$ V}:it5(q"l uRUo+t i-HWN7^Fo}\<"-!;ۊg#wO#@C(˨hxBcl?xKqQ1I'9kM\rS ⌡yKq,FBttBa95XD鮌:^jV{,_SȺM5$H|K[H]qԻ2u(0q>و~T:c,ys1›6I7n^z:X }[WH|z ™jŊRldvUt ضDoS!&rhлҞA^kEm=]Kn<ڄOy&DS+u,.ESDR!%yV{9\e{`|V0OPXvNUj3ӏ4vgKC5hj x]oe5yu6zL9=ANg#ιQ$&b9u 5IjS)ea}(FD ~FLt\r}JAe|S#4^k)d)v|GEQ. P` -e-> g<C~v%r^8Pqpp~ ~ieDe<|EQ (x@ kNw 3^@dśCYa0`ccɓ'? FG(B Q juSI ƣP(&{C?VYȋ(,p8, x| Q^n(2} >l(*ԺQʂ hQW e(/8-z/;p/} X/+.`yw`AEȲ@0!c[x1 ~}jLLURKsrU8R lҢ,$Ӣ(<1$Iٳw”h~&K6? %𘇋C~{vCEUO!.R+!In߾x/_b8b{|E =!!.M(y¥Q .(+;!(ƒ1(6K*w慘 44?S.-!V+ˆی(;| U~O"I+!D4M!ܹhn$Iސ j.4o½cSj&TkֹD'# - ]w+xu a$IPհ8'J PoRO)¿5,fNJr#%"PnV,8qMt]cy~+W=4G=&ߧFK3&OC e +3-nmm! Ci$I@kpux>5M5{P>HBgBa*7H2[| InnOEY  ݻd-#FhM'Oq9HQ RlU IxBޑHES0:c^Slǁ\u]D'<@䴀+ ]p3 ]")ޣK^ y#MS,//caaij/@“,OPF= M .<σ#cdYc j*1$A$Ȳ  3>sHHtN,Ch8??,`ٺ ?׌_Ǚh4pxxn`0( @Alja8"MS kjk#@? ROQ4Ek%+Ǿ>d"c J(hR<8==>Cah'- g?|M$Ifp¿pMh4z3Q]9 H.8c &=z+YW@7p]UQ$%+hm嶹XDf c \-uTtU\_1hӧ8??2g;I4dt:0IvJMQyeZWV O2uJz.+u]T*gY6@_Zp}i`U$k%@YAgX ЍFuĥiU}.p \EE35?륭 IDQ4u@ʕ"NUz N_yK2(" uέA<aNT^$d󆒔Y#W57vGjr`)^%nirv./. ?/ռ$fdUhAu+}< 2B]pJQD媯(V5%ݙUֻt9+M(7ÿ1\RK0W /\RTDڌsf)9e[(J ޲@pYl 1W$SIײ^R繪N@4Zxafz\ͻ c $)AzdY\Y ,?{ȑָ8+uwFZ21zNlo]Űs u .[͇ue~֭ Ƙ) 77 4 t:2 ߢ%֔$K*OyךutId9WlBJYk@pt7(NdrYaee~h,>jZ11EfMMgWRoI3d H j]gQ')𪛢վe~+㸬Q@O?SWdXg{ZBEI |\\%2mncի!<hV 7]KXSz`QRZBV_.ѡjeY>r"{{j2$ф(`j'E\Poyާ`Se4i1U;2 )OE҂>ӹLukMT eOMݠ;VXVUVY__,Q̪ +`#ZC|zC6w\R.SaT[l:(%nPX/Kf09Lt2$r%\jNZ &It]|CR["LG7&pp9_x$=kq&F Z`0@~(AE 1Bc+e"q1zby"3|!/[0+:L@ ?~?Ɲ;wh4AMm DphP9Vyo_9Y]$NEJuY=!ohZK e=2.'T^MAqcggtctF2VIT>K9Y_1l̲v 2.`l6jJ{%ːY;W2c(,y[V \lr{qqxU?PZ,[U*_(u-bY)D\Nv (V}jȲ GGGN; ǙZVޒ$A)Uci:}$e o080dWV9gD\ߚe@8F899A^Gl6/u~EQ ~Qa0 I>x: V4 EUsM'Ih0 Ԗ="ӋILfJp4q0qttCכkXT+Npl^ 2 EȞOٞprTlll8* v(5ȓ =.Cc\lc/$ FwaA!3G,'f 5k# _^:;;;e2#q^_RnM`/eDQYyBjZoWא`%yO?HW{v_iVHm)EIENDB`craftguide-2.0/textures/craftguide_shapeless.png000066400000000000000000000076161401031633000222460ustar00rootroot00000000000000PNG  IHDRw= zTXtRaw profile type exifxYr<߹^a9z@JNĹ㲥2Rχ)_l$W~1|]=,GwMr=}1O{5b55pOɖHsk2I,97s׍۔Zaz'4w9kwޕwޟ;ԹtYCw~''ggou}e|ɗ&|wv@rZ#5ꪏx4^|.wkW8OUxe]u'CwxuS%*KoftLvnݾ]v'q2&W u;>l1e?Vk/o^j..2:?|]V9.b|wD0l7gXu;S`ްv'J1 &MLFF#&;0zUf+;{me@)*>3B!B 5c1($WK>SJ9T>s9\- %Tr)V*UroŖZn|ǞzaĈ#<ʨӨ SL?Ì3<ˬ - +*>vEQ3W쎔ܗQJ$VbJ$fX3AbB? !~~qPnłt+˨C Ja/Pݸuƈ;I4B ΦE7pȜB@)&b4[YD#A |y4r5gSmF эPz9OqzAF1q|V`Jɘt|[+5Oǵ%ܘ}ڼyA)&$ˑ_ R.2#OôfgU%s Dh6 nY2MVBW59N&zQ-P+z!zb IOMH\kD؈4 OHĝ(TBP@P2HDY/ߗ*ml? PUߛFSMI['J귱X|֭ڄ7 ^= j Ar*7)*]S!pW-<{&Rb̝ƥ9&Lw=,%!J@,Ԯ 1CB 5o]6'# ?0 m}fЭ2F@t$m$}Hsj[LcAuM0p}n&f`Y٢ܦn*m!Ϭ|VcU#@cXte 0TZݍڒ6xx)MC0MIHЅfJҳ3*I!6-٠LD40ZG}%zdy[CK#Q]diHl #yIy^jl[Fݤ6 >!h{nt$4>\"PVZɦl?~ <}}h_4-~- [A\ ]>$ZमT6 u{S,E6v`'0'^¹HL $I ZA H<,C(W^fR諭$ī)}gWLWISX%:vP |] 첚S;h^P [3BH$ 5-5j GǛ oWmt-/TGjKY*Seߨ2Q 8Gb Ph7N=St&LKVJo7:8fڊs7ebJb1oŧV)%L=\6t)Ǯ+7IJdڭ! _ % XvAMg7rkVqD;9Da+V q5e]z:~C}IrvM71+ˊ /7;)Y((*4;hk t2gs#GnM4 )(s:'da%ںD!ȉlRR4Y[T}MnM=˖KF=V;#>z7Aj\zO'{kn/!ޛBC~(Cwa%}|T7#{QZޫp !`7 N{{n!҆>} vZr3zX6 RKo _o~`mG`sK{-/AI1O\KZl7+Q:?Q'5/dqiCCPICC profilex}=H@_?EvqPVBЪɥB$Qp-8XupqU?@]%1ݽ;_/3 fdBV+ϊb >y?G7gnXSy8JB|NG%D{ϊ]EEGDi9J({U+Yݶoc2.f$IENDB`craftguide-2.0/textures/craftguide_sign.png000066400000000000000000000060051401031633000212060ustar00rootroot00000000000000PNG  IHDRazTXtRaw profile type exifxڭWk#' )rqxV9~A`nUIbrT?u6<`OC/]wSppc}C uAv&4.n,Jtc~}zgPQhx+_y~9ӕ }ȧe>[Q1Bmc-/F;˜c{WBQ#ʚΚgZĕese\bm f+#2 Ͻ J5OD_1ȕSHbJIRNEaD$Kɔ=4s)Kι2U0R5T*5>-4n&-ҩ豧.=2P8ҐGgJ!)6XwNjY]gl|^bf? Q j8wW&/ ny?G7gnXSy8JB|NtOf27Ȅ,3q&3 KA_$)L({,+`g^v^|~[rܟ]d{ȋ1mu%e6!=˴(qe%o_(?NRNR7a2Eȗe_/^I"!4XZ"aÜ9 u /!h9Iw h. ).V*vW!ى,_^` ?ӷ8w{+|w̳RWvƓIS)`76rr7 !U6>[o@{@E@008@0 gUуL* #Hs E!ik gNǖnUۏOdzX/TO{kvʓl/p0ATE*]D´EOe0LA<^UM(Z !=wP%&vP6ki9Q2SFǪ,f>Y|2POCxs>ٳD"a?];9jm1lFRNVDo| `nɵjhgq:ލbPQ,S$Inh -do=+(Q5hFIv)aY](¹72U~Z>\,eayL7/K!:IENDB`craftguide-2.0/textures/craftguide_slot.png000066400000000000000000000102151401031633000212250ustar00rootroot00000000000000PNG  IHDR6iCCPICC profile(}=HPOS"vqP] *U(BP+`?hҐ8 .κ: x_Rh8{B4ktL'b6*^@eIR uOTwq>˿S",3Lxxz68GYIVωL #8]̨IGb+JF-Qeb22(J0N#<"` ˟GhWc nHbŋ|&"#DeZ3۵ v9IEE#ÑFed1؃) 8 K>29dH7%&9|xH;Ƹ"">pic]4װIJ"WD\LbyB2YÑSzh 8JL&2ׇ_sjq[8.щ &Kr"l>ω.\& DUW&,n &7ޯ[z*7˿┕x|\DKZ'ryI@Uy}_3o!yU X]s;!ȳEg>a.|z=I cWId ~c,{|;"n5t[м<GDr;bئƓȦɑ,oO%`?'76Ak=Mi,C^)'.bqQxg<8 yy,8܈l=zphy 4XX^p!EĤDd뀌՟a  G{5ߨ^0w=B^58ƐL#&ּI|| oUp,r/d* `'>^CG#X]vҟ>6s ɽUSpEuq*="_?H}gƳ.8 ~jmXp؋Iyc]8{59 Whpv(o.loϊ{,               @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@} ?[g@D ϙ@f>"b6[` ˀBfF$Yn#&2 'x<iN H]kƴ2& KXǼ&څ9Lc%"xD,~dV~>bb5^X~> ڊmD\i,! {WWa,_tX\r{߅\( 7._--aL_MkYoNO񉫷ȼD$Og 8>ݶ{''ӟDD}㑯ya^'{GjD"!z,/[_W~\%-G۸{z`}Ѳ[fi$D]zG,_$ɍio Odۓj<2 ћDش.B]8_Iv% QF4kB""O(#?-HNZIH#&' $*ύ:^}jBRߘqU<՜˸?[^v{ҚRNbǦ1%&-:>9f^d8ӈ8Oq~o"W p Ѩ7}iWT5[}2 dgq In$8`D/TUqGr']z퇤k8m19oXukKDX|/njbñ/(oI i I.ob>$XhdʴT;K Fe{CG1S^0ncyjk倬 I2AQn 5?mZ/hLg0Ά#"kjIENDB`craftguide-2.0/textures/craftguide_steelpick.png000066400000000000000000000044051401031633000222330ustar00rootroot00000000000000PNG  IHDRaozTXtRaw profile type exifxڵW[v( g$y`?NMi 䪒Ma3>'8vҺ~xo.Gg|FxpA}lG'ϻ1nĤj9\9|6:(sw+uG.\0]ȻϻjgO{2~w/Ï #~Bʇ9M벏@4E-25-Pf[AyJ CeW]Eϝ5se 'ngk`]n@w|Bkݴ֫X2abzbƨ"zcxnL@[X_o`V|`e(޴Bdk8C H.P$+BexΈ(nps\sXyv#7hp\YGBC9C1HPRECQLrYx ED%IV^FUM'RL432le)\\%XhI%WȧjR7א&ZlҴ;L}=vSZnF2toX/8YCLL'ar͓3=O&g6g '4B߉à7ś -W̙Iݟ`΀}Z\](ZØjpYCǴE"_f>J 8Ay39͎AdMܻz1$ih_/^xlWgI,m 26wׂAUQ z. mēufD VԋSmՍumƃp`z~!WWqvNGt{٤ʗ%RkrcJW?B{ju.K7 q!Q;j=ENYff:HQބ=&6Sӱ&k6p @ߒu5 KGl6L3/͘{n [zUC<1|yUxgL%kQ5gDI;q=^6WH/ulѽ) P{%!S#=4cnW>֓*95|ǜo9;.G[g"%l2/݄si!oB!r%%|7b ؄](3i|/#L 3O6d i]Og#o{ԇ 1Mߏׯj%.C3Cg>!)OFiCCPICC profilex}=H@_?EvqPVBЪɥB$Qp-8XupqU?@]%1ݽ;_/3 fdBV+ϊb >y?G7gnXSy8JB|NGD8{{f|ϑQ#÷,-<ͫ++"c Y.T^gq)7owtT+nuykXg"67D]Py/"6uO=Uo<mBqH%%P\[c"e!ɠѠwR #j-*[Lck4 IENDB`craftguide-2.0/textures/craftguide_vignette.png000066400000000000000000000547751401031633000221140ustar00rootroot00000000000000PNG  IHDRXr5zTXtRaw profile type exifxm( s=|T r}t$TMb,z]Ʒg !y.9G\Ub}G>}()C VoIzxW}~7ʊshܓ\>zGTΝib^*uE aB +4'K2\E| /1~.K>kR|B2a/zyρY s=g9u5eT4Ŧ^(2Vm=xa4*Ġ2%TiزQC$sc:mSJkd"] 5?sx; e9*b1YHc[OXMwkj*knGo*佅(X?P"V$C1(elDPE,/ UH`cm[Vμ8B%M X))ǒ*XRլh͒S֜uUK̭Xu݋EpjŊRjPW}Kt/*Wmhܬy+vqLܭ{/ 'HCG6|Q'zmLSg6}Y/PC7egOj Zlj.f Ɖ@44/f)%^XBIb:-b@Nz S܂Oq#?A.~@#mb.\5݇5VW3;݈iCCPICC profilex}=H@_S";HqPVBЪɥ_Ф!Iqq\ ~,V\uup'G'E)IExwqY]〦f:U10FeIR y?Ggaӛy8JJ|N34 Hʄݮɶׇ6a۵Zs=m::'9[9 kݘlq1ƿV* p n]T*EhTV=Y:QRFjWr+\*PE Fgl 3Du{_NjLb ЙWVT,ˌW<,[`Y j,Z22C U V$]A3J=Lm Z FMbE_E@U`wيYdKr-$_ ^(XY V <ł-M0j5+~x^nawxiSUIźH*X V gNέ~Z_ ! bJꕢPUCQ?88J0 Z h U,Ub Q !UnZ:q}/2 8\`Bu X\M]wQNTTAkW:5RUUZjH%_Z* ) Zu*Pł(.@~VLxcLD$Z&X Z }wKYź`+fݡcE;"sv *P+X}&{X-'*Xd4:UZZsp綻VFMJ N*E9`5USסsajU7Dvnr*Z6>lln=SC}4*C:YRVxVɡn[['`bV* X,/pYh+sEPv-Bzྀԡ9YPUu, ZЕ_~;V+X9`BȮcx``1NP*2t9X`YSA+NgsKN #΂U`EzU>`Enꪗt]VfY#*DS+X@T|VJe=ήjij!,:P9F+XP!줢,5 RU0Z,RT*[YB`'*XbzUV@Rؠt'`̮C;[ֶyUʆ-p@*v( yЮſV΁h4%m+/}"o+fΩCg2\)֮56aw \*V]` QU>iLU-.k`*̲:}` b*Ve%i٠~*`Ua,EB`Y*+d (X(ڥLRRSZԬ*00drv " ]mJ+*X Nl?~%s22X~]B;X51l@()t)u3Bl=E <ЪS;\4ek5+3JU fUvm)U5 ڷ Gk7U p;%)>^ P T@aj3*XU*ZVx^%( ZƒUvP54X]m9VT F+X1a˺Ouhj  @lcMJj}3Xy |Ūz U(XMUB{c6DȆ}Æ,ZV*+YV B@U+XۢJtR(q!LurB`uJb,F{7V'ǂVS0"̄@BBЭ`iՎ H(R Vd 6<)mJiIتj V{,DE׮ [I@u YrҺt01ӆcg [J* viX=FdwjB b  \jߟUU ZVjV* NVxAjRur{'gAٕxU+XaV`5\ÆY-(+[|=FV'gUQ,jEmKEՊXhRUNIvgSv#!gnp;T,u؊T0UC{P!vWjV×dX;WLeV|a \JV&UOtT?PT\QPY;+B"hE$f\}~1pu 'jw:\UPV"+ZͪtlizWS fLL@’Xdծ (8k_VX u:p-*Kͯ)x;b/ XjUaA4|!gv6XqbV,2\leՎ-`KI`$,媒ze&u:EjT <NjjuC,RbYC;@+kR cfN8ZHʰc:k Rc3.>UժDAFC  \lUTLFѪ d(Y੦j懕 [U[R- S։pu>JٰZRWBT4|UӞX 3*9X;•B^Tժga T=P*Uj[ʉ*D-YF*Ur;`Wj`եqvP"CyYNj: "jpRi8e7MEhRWTU놈w%5 J%"ANpʷRTv9ئ:0f |4 !CUUk R 2YT9ȅfj]3 +Y!%"4|Ƅ.$<8sdլA* T„pT@z;k7ZB@pu-I5ՠAU(\j.(螑;-Ui6`+I Th% A]*ǿi5 #!DU`j@ dX;TP2--6hU*ϻW(BUZVY0 ʡroR. SUT,ֶBuz^yJYАexgs3 T` Uل *[@*YBVJ2j0J 2ƒ;U[2d(Qϻ۝n<VY0偅HE!;V P3 M`>{0ZѪp]0aCvrm lßIH\'nLu V;]Mx퍥^U] tr7`)%{agw1= T*k "j* kO}4\e}fVKABU4PuNRQpdUT 2-rRVsν҄+lUĀ,&lg!]wW%+ EFDnJ%pBUzSU!+B^93UB(Ve߰#heCɊ ^\,j MavceWy׽7VnnmY{+y]o9< [= }c_?m{fl V1`YbJ_RԬUH]ϨQP"+zU!ὲa Xʉω{`iՐ [LbB\*E|+Xrf-$m!QT<ۊ,e*3sZz)X(*ZB*YaCV\T`UUj֋xR*SzL:=[BVPR"լg{l~_cpj̶B'!\l!,j…vIxT,jV#)TjXz5ƸL f~2 !*jAEC+VWXe67ZBS=B:`&j *jn,VDŊRV:Ο:]=JW'_(jhK_2]1ΆBYL)by:DƢ+룶[ZڣWwU賘Vm<:ߦCk=&r»(T| a,\YߠT+t8U,Eveidw}i*Ww۩!™lހm> !˜+tX̳g8/T@)c=+/'a)_ *ѡU#JV*j]Ee_}Z Vܾ|e jJU5˳].AѶݠ RAdNw]vGSR+1r[+oCYuw"xbqZ^`/cmgmMz6+YPWK;mw'^DwH»5+"-AԹx eyS$#zT+;:\hU7"NrXKRrw_}!ٽb8nAmСA JU =TJ܎ؾڞ T&[5LtR2YHq#* *X,UӉUHnwq1к/sP5@qvV-$G 7K1jfrleXo1'wVvnYWl1gZT(P!Q"@xͶTKHbÅ ܞeUJ*A aQj:/:ƛ l? Tc!bDX)EU,ł+V,6X'[i=5"DY70gWwvZ63LE@U WH5KsjKd– hԬ1TQCԫnp$WVxqX7($+`Y,+B}e l`6jmi˂+TGR);*~jw\@Htg@Zns'iRei>$ÕZA XUhYys6K?ٶ ِ} O 3SwzDxCgP'`}fyeTeؽ깫[64da+wOzEBV=ff Tu{ٶaM+v)ğ67Ub* ШP B+T-7E{Ѹ~Q,gv;Fq4W#/z/>jك(*^WhW-5e|%u3-Lb(ˬD*9XrWeWZ [*sfG;hԬ}< W9qJ)#|N{V܇V+j1`J͞AMY~m{ f?R*^]r1W@tA5I6Pջ>;{xm@!,zc̳ ϫ4(O #L*p*nP IDAT<:[dY*ly^AD#H1iaHznJJ(]ߞfj~(~t.V +à 1qG]]zcFR:XR'0+VWT}SP650@Q0ʮspńeqvupW:G$h1?;b!E=T'P}$?"d?{ĆR7SzŸ3U9uZl5 ņEX߆螡j餞ESRouԑZzt>jOT4ha!J<t) A9U4EEew/Lu"Q=HVK ?Fh%c뺃yo؊}ʾ +O{/`fh¦a|Jݣ?QC.rF n(D@j$ࠋ~PT, _E W pAJu>9SP? dr;)j&!7^=Ge:W zuQxQe w6Xa w{+}_r W VZHIv*+Ut>wy>P زf)eDZn҃ K)TH#sQ<,ƽ1wM77|zu_š5Y&ŗ]Ǘڟ u+d{Q=;Z2db)(~/yXQCƳrrW7r"RgY9{`Uz+g,*,9r7`~ćzU0rxz{rTP d=p}Xᷬb w? POw͔ZL/,)#]_"R9*cw>=-vj)MԱ[yP fiU)85?ިsP ǡpjKpg]#*$w]헓~qhR|rr"jJpGl+v=p]/}>Ky7.#UǦMřxUp{z/F*fY/{U,}N̟gwW91DeepCM;u S_ك A,k]v|fw ޼=} kտQ;}Ŀ~_}Ez^zG_^=w_^륗Zm}-㗻^T~CŎ^z饗>XK/=5[z;^z饗Cz饗^z饗^z饗^z饗s/AV`uK/KY0`]S/K/B(}{0s~>z饗^[؀՝|C}K?}7[Ҿ}M7W/rg}}þ]"]3ֿ_^F}Կogr\En렛Co/]%ܗ66k@Qw3ί{k 9|p"}zA%Jצ{8)Nh Y Խ=}MO@AAzU7p?9U)bwv0By):E*H;M0uHtX"u ݐ?+|SARÃanȳ\ܳcQo"*o'QEeξl']f[\}=}w,UT|sG`?x9zd@.Wz~o]/z۷*DG 7%Qhk<(UWNYVyme_B #lwcpS+ae CRf n Zר\eW*}oll1wMp皛cf[3dWбQx}^fGU`5^a@Fwl~U.BQ\tхOLP+J\_^;H3UY 4v.굲Ud=XuB YغDu 2]DWT*NjUiT9`:kW/k_!<>W#wg~+B@1-*VP!p~_ȍ?WxԬ %;F(⚏:  RތzPY*VƛxNedrOdߵrZvx;+t9O)2d#Ly(XJoQc <+ g+1ժ_Q -hT1W--N㡕 aBK1Thfi픀^2JL2rѨsOu+p F3 4h~5nZ } 癛|@xtQ0SBF%GzWE*zK]/[$Rr3ʦBЃpnk`uQ0Ќ|,UjjRϢ30`:PLSR2uObtrATUbP #? *l|G(Y ^a`]C 9h^錤90E [zq|B\E%o 7G !AdQ{ ¨Pa6daKeCتer ~#XojBR0"< >XJfEV-z+U; vdK<@+ogVp:Whe:TEf1Vg?aӠ^eQ˽=FYdQdW)mlJ)A'/eQl[E !>T+( <4n3BJf <ʒgf}dNeu{Jll{#[A+ Rc`}أjuWźoVNVTG>zB_3vʃNE=jӰ{xbURK,(t~ِƸULFOZr|̉9H ȷcxgV:eaV4EXe,Eja}RD2..^X[Ā:f/p⾢ZΜEdYku]0a!gvx|SYͲE&G0BUP`ZU+K(:4רÄ?Ã|e&J lb gDm-=X(>AaUa@M~;[]:"x/zKaY5XUꐥ^:Fu%]h:4u_d D*Vfmy2;\442 ˛~GV$l=]+2TmebwhPHrds=(RUŊrr"Y՝+y(JQ10*Z{T0BR獗`:< aAeݞ*\=XYX2 jY6 hUpJ,cR{%jTemI5f Q+x—fSŮI*Jb*+ԺϝQ|OyAr* ,'(2? Z틵'Pyl{jpŇs nN ;`1rXFŊ*S 0:y`6\?Q jg,YjHrό^#6Y5 ZN|.ﱯVlJ2K0J )Š6ȉF ט#j ڲa}+ @v)om̀sʄ+!I F B/L`w1gZ;󓼱"g)0`nv9ك&{P2+*LQF0{!l~f2fR:ŲAbƨv={7 Xy@lKh4BzCWf B)WofgwusS+3A %ږ5b*VAB>g`2$~J\^1fXA5r1hp TןV+tT>۲A!+UdϠj2v0TUIf "_=XYpb U\@+$Ga9%=9@˪j)$w{ê>ZUJn0j&wz Z$aͨ^E'WVx0*=jV! >IB\oУ0$-hbAK!:IwkWp6~bz϶m~>3IO/^I@&{d$WH V^.4p3$ tbgڵk:L\U fCA2`Œ8VHxGO%GlZS;&3[vb{kJ_Qv+KN8T *l(7 fs e_a6wW8AxeEaNowT{_;|Y+TPѪA9 9spAZ+fagvPIŊ=^R+~; . sy.&{ f&"heasN(~0 3Ǒ}Haj W+uR9ԹAU-prDzhBQgdR1q]&MóuC[4598zy[aDA5 DA}׌3B6Y5"*]z1P)cW> `3=^+mfS[EjOFaq)+s0W3 -Od.bW7)ҮSŚG-p uxB!2KK&m?fm"n ,rAI!ke8v6+)Jթptg4's$1r,PFE5IDATπW6hY! [r(\TO{^a*Z( RFnr Y YcT-5 Yꕊr}FZ1Rqlτώ`ńL*fT& B_ lCBd!wPիWcYXZ+dxDEC 8R s݊M $Z,dԩ ef)WJX+hQ*JRP!Ӻ(h^ X V` Oa1lI \^e VUD{$l1@Y5,@fi$jՓ+aEF? ^R*k.DZH2U[A+ۄ\YݣƒahHЪ*+p,4x :x1 N+B5^0hG3P6M-DsAZ#\YAjz .U茁 V˿ Zի6Rղa׻r  *%Ze(T弎o9L*5&a%[.JU5L'|,jm٠ `v3+XEr"\QUAtrhG-4 lC[ʔ hYUhؚtxpǚW~lV0B*jS۲@J'lBBff(OhD LL3U]ܣj6\! !j4&*tlPj[$p`=T)\xŜs ZjХT7`EUfhGQU_1ę'GyaEU5"T,Ro,5lGDfζaA:0TN9bu;| G W+XeLf/JZ FYHBUDYpLrG&3CBV(+DeULEVU*\}'"zeU }ώ7FL8+Dwz yY^ժZJ:NT+u~W KCӌꅂ1+tE7,i>퇅ZD,զ}B(sxf"_fYkEb/Yπ6*\ KME*WPm*Q4P;4T,┡TeY誠`f>jپ Q^JlΕ| }Z5mP2ˢTE]y ZY} * 4N.lhj@MWm! | vkJ,FN֧}_Yπᇥ.8Y^*Hb+Kh('8FvU_U CV &}m+0U ` P-_wM0Y0"XFY k>:P**W9YO)RPd~GZQ6 UcgYƀ fzֳԫO=O* Φ d1):S5+KLdG Z^CZ Vj'xD;4P"; -0^!ÅYjUxd( vj u~ X(`U+KbYXSWP J W` cЅ0T*au|Tp՘iARQVQq2]ܫ*Ǣ^HkY2r, (Uoi8fE(X 梬P !` ^^=}w,e > H RfER F V&lY˪\LpZ Q;0gN*%3',,4DB"<"BnLBjr{dЫjjˆ-zHtT"έ`ٶ1LHڦb,Lo l7T"Bm-6Kh \UfR<KUi" d54p(O ɡ+ E ;`e`zۧ,YJ Á6TVVBLድͪ^UxK%SKngCUmD`GIZGfr^eQ@!G+X%,dw/$e6 U=mOUQ c {À YHph'BR'(Xnmy VL6'nC4 :L/i[+XsNvUj{ECvV3S56&nC4(XĂǰ wQ"W֪V$G zNp>E4DWlr[Hn_ `[E58[+V{5PZY@Sw+Va :jʭ( OSԓݭeQZ5\A[ұQ:6 V(bRbT4LyhWKٮ퍅.Ȩ(c*T)@*h)ԉT~WViDx a*X,ѻlBB@R )*^AZ+Q.>5B.**LyC{+V v lkU]C3VEкTPNRB@VzUEz 3``aj̀ v QPVUR A]YW߀偪 Yr)`) v LՊ q*V+X93=G0T* oKZ*s$0R 2kSmW{L:YTp59XTac+X(-`nא\RPleBa,KV6Lyg "uŜAf1*V+X:zRPa*l@,̰eeZ, je+XFD+#t ZQ*u9XB'(,hU/PW ZJ*V+Xq`1+[)U{S1yWՕUHgoXUuw_ٖ^ 2T)UK)kj.VJ O #'!4kL[%`0zJAP2ᤢ:S=:,tUzaKj NlXB~Vņ$`ʕ5F܌Eʪ^)^*ꆣch+XyI3:Nl}%*WՕ-(RI`3 ֺrX,VcdޜF<]JO.uTTPl`e_ezF[[3+XX'*X S1+kƒ+ ! ;XUUmQNTaX`a1aJ(TPP`YAƣjE*X]2r %rVAlK-T%T+L',څXBPQ@ rUQdN+FHubf5Xi)Xaf(XVa `* VUy/}ƫga( x` y*,<5 <3TN4 4hYT5 :kF(Wj2쫢`lժJ Vze24)DZ[jj ` -;gC`!HbCjQVfG edJzJ*&P2hW* Ֆi: Vjl+s`kЪ=T|gVFKYbB(:G hE+XVcѨep+&ԪVb4:(:I:2BlUKA DW, +;*XJgTԪ+IRԭl+N+ jE(XlJyXl+&`u SH+IE ^;[jW&`=&U4Te",Bb;SjA4)ݪZku+fTEkUZ0lYˢ`UQc+&aU PS,Zz>%ʛ9+dJq`Z(˓ԞQ9BV(Xՠuߑ0Sۙ$(\ W+Xv1TSvQX*&`ՠľ_o6fek{ˢjUT+E(X(}}tԹUP5+YbVQ+Xp`1fzP*TrQZ/ԩU(+j!@AKQ !a ^̀GzUEBC*D@PRV,ۓ1Ƹ?!5{IENDB`