diff --git a/init.lua b/init.lua index cc0ad91..52c72e6 100644 --- a/init.lua +++ b/init.lua @@ -58,7 +58,9 @@ local sprintf, find, gmatch, match, sub, split, upper, lower = 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 = pairs, ipairs, next, type, setmetatable +local pairs, ipairs, next, type, setmetatable, tonum = + pairs, ipairs, next, type, setmetatable, tonumber + local vec_add, vec_mul = vector.add, vector.multiply local ROWS, _ROWS = 9 @@ -66,6 +68,7 @@ local LINES = 10 local IPP = ROWS * LINES local MAX_FAVS = 6 local ITEM_BTN_SIZE = 1.1 +local SPACING = 0 -- Progressive mode local POLL_FREQ = 0.25 @@ -106,6 +109,7 @@ local fs_elements = { model = "model[%f,%f;%f,%f;%s;%s;%s;0,0;true]", 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]", arrow = "image_button[%f,%f;0.7,0.7;%s;%s;;;false;%s]", } @@ -113,7 +117,6 @@ local fs_elements = { local styles = sprintf([[ style_type[label,field;font_size=+0] style_type[image_button;border=false;sound=craftguide_click] - style_type[button;border=false;font=bold;font_size=+2;content_offset=0] style_type[item_image_button;border=false;bgimg_hovered=%s;bgimg_pressed=%s;sound=craftguide_click] style[filter;border=false] @@ -125,6 +128,9 @@ local styles = sprintf([[ style[next_recipe;fgimg=%s;fgimg_hovered=%s;fgimg_pressed=%s] style[prev_usage;fgimg=%s;fgimg_hovered=%s;fgimg_pressed=%s] style[next_usage;fgimg=%s;fgimg_hovered=%s;fgimg_pressed=%s] + style[pagenum,no_item,no_rcp;border=false;font=bold;font_size=+2;content_offset=0] + style[craft_rcp,craft_usg;border=false;noclip=true;font_size=+0;sound=craftguide_click;bgimg=craftguide_btn9.png; + bgimg_hovered=craftguide_btn9_hovered.png;bgimg_pressed=craftguide_btn9_pressed.png;bgimg_middle=4,6] ]], PNG.selected, PNG.selected, PNG.search, PNG.search_hover, @@ -791,6 +797,142 @@ local function groups_to_items(groups, get_all) return get_all and names or "" end +local function get_stack_max(data, is_recipe, rcp) + local inv = data.player:get_inventory() + 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] + 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 + + 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 craft_stack(player, pname, data, _f) + local inv = player:get_inventory() + local rcp_usg = _f.craft_rcp and "recipe" or "usage" + local output = _f.craft_rcp and + data.recipes[data.rnum].output or data.usages[data.unum].output + output = ItemStack(output) + local stackname, stackcount = output:get_name(), output:get_count() + local scrbar_val = data[sprintf("scrbar_%s", _f.craft_rcp and "rcp" or "usg")] + + for name, count in pairs(data.export_counts[rcp_usg].rcp) do + if is_group(name) then + local groups = extract_groups(name) + local items = groups_to_items(groups, true) + + for _, item in ipairs(items) do + for _name, _count in pairs(data.export_counts[rcp_usg].inv) do + if item == _name and _count >= count then + name = _name + break + end + end + end + end + + inv:remove_item("main", sprintf("%s %s", name, count * scrbar_val)) + end + + local count = stackcount * scrbar_val + local stack = ItemStack(sprintf("%s %s", stackname, count)) + local message = clr("#ff0", sprintf("%s x %s", count, stackname)) + + if inv:room_for_item("main", stack) then + inv:add_item("main", stack) + msg(pname, sprintf("%s added to 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 crafted!", message)) + end +end + +local function show_recipes(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(item, data, player) + if not recipes and not usages then return end + if data.show_usages 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 @@ -927,7 +1069,7 @@ local function get_tooltip(item, info) return sprintf("tooltip[%s;%s]", item, ESC(tooltip)) end -local function get_output_fs(fs, rcp, shapeless, right, btn_size, _btn_size, spacing) +local function get_output_fs(fs, rcp, shapeless, right, btn_size, _btn_size) local custom_recipe = craft_types[rcp.type] if custom_recipe or shapeless or rcp.type == "cooking" then @@ -939,7 +1081,7 @@ local function get_output_fs(fs, rcp, shapeless, right, btn_size, _btn_size, spa end local pos_x = right + btn_size + 0.42 - local pos_y = spacing + 0.9 + local pos_y = SPACING + 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)) @@ -955,7 +1097,7 @@ local function get_output_fs(fs, rcp, shapeless, right, btn_size, _btn_size, spa local arrow_X = right + 0.2 + (_btn_size or ITEM_BTN_SIZE) local X = arrow_X + 1.2 - local Y = spacing + 1.4 + local Y = SPACING + 1.4 fs(fmt("image", arrow_X, Y + 0.06, 1, 1, PNG.arrow)) @@ -994,7 +1136,7 @@ local function get_output_fs(fs, rcp, shapeless, right, btn_size, _btn_size, spa end end -local function get_grid_fs(fs, rcp, spacing) +local function get_grid_fs(fs, rcp) local width = rcp.width or 1 local right, btn_size, _btn_size = 0, ITEM_BTN_SIZE local cooktime, shapeless @@ -1023,7 +1165,7 @@ local function get_grid_fs(fs, rcp, spacing) X = X + (X * 0.2) + _ROWS + 3.9 local Y = ceil(i / width) - min(2, rows) - Y = Y + (Y * 0.15) + spacing + 1.4 + Y = Y + (Y * 0.15) + SPACING + 1.4 if large_recipe then btn_size = (3 / width) * (3 / rows) + 0.3 @@ -1033,7 +1175,7 @@ local function get_grid_fs(fs, rcp, spacing) local yi = floor((i - 1) / width) X = btn_size * xi + _ROWS + 0.3 + (xi * 0.05) - Y = btn_size * yi + spacing + 0.2 + (yi * 0.05) + Y = btn_size * yi + SPACING + 0.2 + (yi * 0.05) end if X > right then @@ -1104,10 +1246,10 @@ local function get_grid_fs(fs, rcp, spacing) fs("style_type[item_image_button;border=false]") end - get_output_fs(fs, rcp, shapeless, right, btn_size, _btn_size, spacing) + get_output_fs(fs, rcp, shapeless, right, btn_size, _btn_size) end -local function get_rcp_lbl(fs, data, panel, spacing, rn, is_recipe) +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 @@ -1118,27 +1260,69 @@ local function get_rcp_lbl(fs, data, panel, spacing, rn, is_recipe) 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", _ROWS + 5.65 - shift, spacing + 3.37, lbl)) + fs(fmt("label", _ROWS + 5.65 - shift, SPACING + 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 = _ROWS + 4.9 - local y_arrow = spacing + 3 + local y_arrow = SPACING + 3 fs(fmt("arrow", x_arrow - shift, y_arrow, PNG.prev, prev_name, ""), fmt("arrow", x_arrow + 2.3, y_arrow, PNG.next, next_name, "")) end local rcp = is_recipe and panel.rcp[data.rnum] or panel.rcp[data.unum] - get_grid_fs(fs, rcp, spacing) + get_grid_fs(fs, rcp) end -local function get_title_fs(query_item, favs, lang_code, fs, spacing) +local function get_model_fs(fs, 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 + 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))) + + 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", _ROWS + 6.6, SPACING + 0.05, 1.3, 1.3, "", def.mesh, concat(t, ","))) +end + +local function get_title_fs(fs, query_item, favs, lang_code) local fav = is_fav(favs, query_item) local nfavs = #favs - local star_x, star_y, star_size = _ROWS + 0.4, spacing + 0.5, 0.4 + local star_x, star_y, star_size = _ROWS + 0.4, SPACING + 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 "") @@ -1156,62 +1340,97 @@ local function get_title_fs(query_item, favs, lang_code, fs, spacing) end fs("style_type[label;font=bold;font_size=+6]", - fmt("label", _ROWS + 1.05, spacing + 0.47, snip(ESC(get_desc(query_item, lang_code)), 32)), + fmt("label", _ROWS + 1.05, SPACING + 0.47, snip(ESC(get_desc(query_item, lang_code)), 32)), "style_type[label;font=mono;font_size=+0]", - fmt("label", _ROWS + 1.05, spacing + 0.97, clr("#7bf", snip(query_item, 34))), + fmt("label", _ROWS + 1.05, SPACING + 0.97, clr("#7bf", snip(query_item, 34))), "style_type[label;font=normal]") local def = reg_items[query_item] local model_alias = craftguide.model_alias[query_item] if def.drawtype == "mesh" or model_alias then - 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 - 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))) - - 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("style_type[model;bgcolor=black]") - fs(fmt("model", _ROWS + 6.6, spacing + 0.05, 1.3, 1.3, "", def.mesh, concat(t, ","))) + get_model_fs(fs, def, model_alias) else - fs(fmt("item_image", _ROWS + 6.8, spacing + 0.17, 1.1, 1.1, query_item)) + fs(fmt("item_image", _ROWS + 6.8, SPACING + 0.17, 1.1, 1.1, query_item)) end end -local function get_panels(data, fs) +local function get_export_fs(fs, data, panel, 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;fgimg_pressed=%s]", + name, sprintf("craftguide_export%s.png", show_export and "" or "_off"), + "craftguide_export.png", "craftguide_export.png"), + fmt("image_button", + _ROWS + 7.35, SPACING + 0.2, 0.45, 0.45, "", sprintf("export_%s", name), ""), + sprintf("tooltip[export_%s;%s]", name, ES"Craft this item")) + + if not show_export then return end + + local item = (is_recipe and panel.rcp[data.rnum].output) or + (is_usage and panel.rcp[data.unum].output) + item = clean_name(item) + local _name = match(item, "%S*") + local stack = ItemStack(_name) + local stack_max = stack:get_stack_max() + local stack_fs = (is_recipe and data.scrbar_rcp) or (is_usage and data.scrbar_usg) or 1 + + fs(sprintf("style[scrbar_%s;noclip=true]", name), + sprintf("scrollbaroptions[min=1;max=%u;smallstep=1]", + min((is_recipe and max_stacks_rcp or max_stacks_usg), stack_max)), + fmt("scrollbar", _ROWS + 8.1, SPACING, 3, 0.35, sprintf("scrbar_%s", name), stack_fs), + fmt("button", _ROWS + 8.1, SPACING + 0.4, 3, 0.7, sprintf("craft_%s", name), + sprintf("%s", stack_fs == 1 and ES"Craft stack" or + sprintf(ES"Craft %u stacks", stack_fs)))) +end + +local function get_rcp_extra(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 + + if rcp_normal then + max_stacks_rcp = get_stack_max(data, is_recipe, panel.rcp[data.rnum]) + end + + if usg_normal then + max_stacks_usg = get_stack_max(data, is_recipe, panel.rcp[data.unum]) + end + + if max_stacks_rcp > 0 or max_stacks_usg > 0 then + get_export_fs(fs, data, panel, 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", + _ROWS + 0.1, SPACING + (panel.height / 2) - 0.5, 7.8, 1, "no_rcp", lbl)) + end +end + +local function get_favs(fs, data) + fs(fmt("label", _ROWS + 0.4, SPACING + 0.4, ES"Bookmarks")) + + for i = 1, #data.favs do + local item = data.favs[i] + local X = _ROWS - 0.7 + (i * 1.2) + local Y = SPACING + 0.8 + + if data.query_item == item then + fs(fmt("image", X, Y, ITEM_BTN_SIZE, ITEM_BTN_SIZE, PNG.selected)) + end + + fs(fmt("item_image_button", X, Y, ITEM_BTN_SIZE, ITEM_BTN_SIZE, item, item, "")) + end +end + +local function get_panels(fs, data) local _title = {name = "title", height = 1.4} local _favs = {name = "favs", height = 2.2} local _recipes = {name = "recipes", rcp = data.recipes, height = 3.9} @@ -1219,48 +1438,25 @@ local function get_panels(data, fs) local panels = {_title, _recipes, _usages, _favs} for idx = 1, #panels do - local panel, spacing = panels[idx], 0 + local panel = panels[idx] + SPACING = 0 if idx > 1 then for _idx = idx - 1, 1, -1 do - spacing = spacing + panels[_idx].height + 0.1 + SPACING = SPACING + panels[_idx].height + 0.1 end end - local rn = panel.rcp and #panel.rcp - local is_recipe = panel.name == "recipes" - local recipe_or_usage = panel.name == "recipes" or panel.name == "usages" + fs(fmt("bg9", _ROWS + 0.1, SPACING, 7.9, panel.height, PNG.bg_full, 10)) - if rn then - get_rcp_lbl(fs, data, panel, spacing, rn, is_recipe) - end - - fs(fmt("bg9", _ROWS + 0.1, spacing, 7.9, panel.height, PNG.bg_full, 10)) - - if recipe_or_usage and not rn then - local lbl = is_recipe and ES"No recipes" or ES"No usages" - fs(fmt("button", - _ROWS + 0.1, spacing + (panel.height / 2) - 0.5, 7.8, 1, "", lbl)) + local is_recipe, is_usage = panel.name == "recipes", panel.name == "usages" + if is_recipe or is_usage then + get_rcp_extra(fs, data, panel, is_recipe, is_usage) elseif panel.name == "title" then - get_title_fs(data.query_item, data.favs, data.lang_code, fs, spacing) - + get_title_fs(fs, data.query_item, data.favs, data.lang_code) elseif panel.name == "favs" then - fs(fmt("label", _ROWS + 0.4, spacing + 0.4, ES"Bookmarks")) - - for i = 1, #data.favs do - local item = data.favs[i] - local X = _ROWS - 0.7 + (i * 1.2) - local Y = spacing + 0.8 - - if data.query_item == item then - fs(fmt("image", - X, Y, ITEM_BTN_SIZE, ITEM_BTN_SIZE, PNG.selected)) - end - - fs(fmt("item_image_button", - X, Y, ITEM_BTN_SIZE, ITEM_BTN_SIZE, item, item, "")) - end + get_favs(fs, data) end end end @@ -1309,7 +1505,7 @@ local function make_fs(data) lbl = ES"Collect items to reveal more recipes" end - fs(fmt("button", 0, 3, _ROWS, 1, "", lbl)) + fs(fmt("button", 0, 3, _ROWS, 1, "no_item", lbl)) end local first_item = (data.pagenum - 1) * IPP @@ -1332,7 +1528,7 @@ local function make_fs(data) end if (data.recipes and #data.recipes > 0) or (data.usages and #data.usages > 0) then - get_panels(data, fs) + get_panels(fs, data) end return concat(fs) @@ -1562,13 +1758,15 @@ local function init_data(name) local info = get_player_info(name) pdata[name] = { - filter = "", - pagenum = 1, - items = init_items, - items_raw = init_items, - favs = {}, - lang_code = get_lang_code(info), - fs_version = get_formspec_version(info), + 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), + player = get_player_by_name(name), } end @@ -1577,10 +1775,14 @@ local function reset_data(data) 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.show_usages = nil + data.export_rcp = nil + data.export_usg = nil data.items = data.items_raw end @@ -1601,16 +1803,23 @@ local function fields(player, _f) local name = player:get_player_name() local data = pdata[name] + local scrbar_rcp_CHG = _f.scrbar_rcp and sub(_f.scrbar_rcp, 1, 3) == "CHG" + local scrbar_usg_CHG = _f.scrbar_usg and sub(_f.scrbar_usg, 1, 3) == "CHG" + if _f.clear 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 @@ -1623,6 +1832,7 @@ local function fields(player, _f) data.filter = str data.pagenum = 1 + search(data) elseif _f.prev_page or _f.next_page then @@ -1644,38 +1854,22 @@ local function fields(player, _f) 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 + else + data.export_usg = not data.export_usg + end + + elseif scrbar_rcp_CHG or scrbar_usg_CHG then + data.scrbar_rcp = _f.scrbar_rcp and tonum(match(_f.scrbar_rcp, "%d+")) + data.scrbar_usg = _f.scrbar_usg and tonum(match(_f.scrbar_usg, "%d+")) + + elseif _f.craft_rcp or _f.craft_usg then + craft_stack(player, name, data, _f) else - 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(item, data, player) - if not recipes and not usages then return end - if data.show_usages and not usages then return end - - data.query_item = item - data.recipes = recipes - data.usages = usages - data.rnum = 1 - data.unum = 1 + show_recipes(player, data, _f) end return true, show_fs(player, name) @@ -1780,8 +1974,7 @@ if progressive_mode then local def = reg_items[inv_items[i]] if def then - local item_groups = def.groups - if item_has_groups(item_groups, groups) then + if item_has_groups(def.groups, groups) then return true end end @@ -1795,8 +1988,8 @@ if progressive_mode then end end - local function recipe_in_inv(recipe, inv_items) - for _, item in pairs(recipe.items) do + 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 diff --git a/textures/craftguide_btn9.png b/textures/craftguide_btn9.png new file mode 100644 index 0000000..34433ac Binary files /dev/null and b/textures/craftguide_btn9.png differ diff --git a/textures/craftguide_btn9_hovered.png b/textures/craftguide_btn9_hovered.png new file mode 100644 index 0000000..01c2dc7 Binary files /dev/null and b/textures/craftguide_btn9_hovered.png differ diff --git a/textures/craftguide_btn9_pressed.png b/textures/craftguide_btn9_pressed.png new file mode 100644 index 0000000..0cbac75 Binary files /dev/null and b/textures/craftguide_btn9_pressed.png differ diff --git a/textures/craftguide_export.png b/textures/craftguide_export.png new file mode 100644 index 0000000..a1dc545 Binary files /dev/null and b/textures/craftguide_export.png differ diff --git a/textures/craftguide_export_off.png b/textures/craftguide_export_off.png new file mode 100644 index 0000000..75708b7 Binary files /dev/null and b/textures/craftguide_export_off.png differ diff --git a/textures/craftguide_fav_off.png b/textures/craftguide_fav_off.png index b597b7a..c8d53b1 100644 Binary files a/textures/craftguide_fav_off.png and b/textures/craftguide_fav_off.png differ