Done! export & craft recipes
							
								
								
									
										465
									
								
								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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								textures/craftguide_btn9.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 146 B  | 
							
								
								
									
										
											BIN
										
									
								
								textures/craftguide_btn9_hovered.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 146 B  | 
							
								
								
									
										
											BIN
										
									
								
								textures/craftguide_btn9_pressed.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 146 B  | 
							
								
								
									
										
											BIN
										
									
								
								textures/craftguide_export.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 7.9 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								textures/craftguide_export_off.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.9 KiB  | 
| 
		 Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.1 KiB  |