diff --git a/formspec.lua b/formspec.lua index 162f3ec..ee24b31 100644 --- a/formspec.lua +++ b/formspec.lua @@ -1,15 +1,32 @@ local gui = {}; +function gui.formspec(args) + local data = string.format("formspec_version[%d] size[%f,%f]", args.version or 3, args.w, args.h); + + if args.bg then + data = data .. gui.bg9 { + skin = args.bg, + fullsize = true, + }; + end + + return data; +end + function gui.bg9(args) return string.format("background9[%f,%f;%f,%f;%s;%s;%s]", - args.x, args.y, - args.w, args.h, + args.x or 0, args.y or 0, + args.w or 1 * (args.size or 1), args.h or 1 * (args.size or 1), args.skin.texture .. ".png", args.fullsize or false, tostring(args.skin.radius)); end function gui.button(args) + if args.disabled then + return string.format("button[%f,%f;%f,%f;disabled_button;%s]", args.x, args.y, args.w, args.h, args.text); + end + local data = string.format("button[%f,%f;%f,%f;%s;%s]", args.x, args.y, args.w, args.h, args.id, args.text); if args.tooltip then @@ -22,6 +39,19 @@ function gui.button(args) return data; end +function gui.container(args) + local data = string.format("container[%f,%f]", args.x, args.y); + for _,element in ipairs(args) do + data = data .. element; + end + + return data .. "container_end[]"; +end + +function gui.image(args) + return string.format("image[%f,%f;%f,%f;%s]", args.x, args.y, args.w, args.h, args.image); +end + function gui.image_button(args) local data = string.format("image_button[%f,%f;%f,%f;%s;%s;%s]", args.x, args.y, @@ -40,7 +70,46 @@ function gui.image_button(args) return data; end +function gui.inventory(args) + local data = ""; + + if args.bg then + for i = 0,args.w - 1 do + for j = 0,args.h - 1 do + data = data .. gui.bg9 { + x = args.x + (i * 1.25), + y = args.y + (j * 1.25), + + skin = args.bg, + }; + end + end + end + + data = data .. string.format("list[%s;%s;%f,%f;%f,%f;]", args.location, args.id, args.x, args.y, args.w, args.h); + + if args.tooltip then + data = data .. gui.tooltip { + x = args.x, + y = args.y, + w = args.w, + h = args.h, + text = args.tooltip, + }; + end + + return data; +end + function gui.label(args) + if args.textcolor then + return string.format("label[%f,%f;%s%s]", + args.x, + args.y, + minetest.get_color_escape_sequence(args.textcolor), + args.text); + end + return string.format("label[%f,%f;%s]", args.x, args.y, args.text); end @@ -61,7 +130,11 @@ function gui.style_type(args) end function gui.tooltip(args) - return string.format("tooltip[%s;%s]", args.id, args.text); + if args.id then + return string.format("tooltip[%s;%s]", args.id, args.text); + else + return string.format("tooltip[%f,%f;%f,%f;%s]", args.x, args.y, args.w, args.h, args.text); + end end return gui; diff --git a/init.lua b/init.lua index c8d32e8..bdb9de4 100644 --- a/init.lua +++ b/init.lua @@ -58,4 +58,4 @@ loadfile(modpath .. "/items.lua") (); _cartographer.generate_marker_formspec = loadfile(modpath .. "/marker_formspec.lua") (_cartographer.marker_lookup, cartographer.gui); loadfile(modpath .. "/map_formspec.lua") (map_data); loadfile(modpath .. "/commands.lua") (); -loadfile(modpath .. "/table.lua") (_cartographer.materials_by_name, _cartographer.materials_by_group, cartographer.skin); +loadfile(modpath .. "/table.lua") (_cartographer.materials_by_name, _cartographer.materials_by_group, cartographer.gui, cartographer.skin); diff --git a/table.lua b/table.lua index 30aad72..7a14995 100644 --- a/table.lua +++ b/table.lua @@ -1,4 +1,4 @@ -local materials_by_name, materials_by_group, gui_skin = ...; +local materials_by_name, materials_by_group, gui, gui_skin = ...; local MAP_SIZE = 40; local SCALE_SMALL = 1; @@ -6,28 +6,6 @@ local SCALE_MEDIUM = 2; local SCALE_LARGE = 4; local SCALE_HUGE = 8; --- Draw background elements in the same arrangement as inventory slots --- x: The x position of the inventory --- y: The y position of the inventory --- cols: The width of the inventory, in columns --- rows: The height of the inventory, in rows --- skin: A 9-slice background skin table --- --- Returns a formspec string -local function inventory_bg(x, y, cols, rows, skin) - local data = ""; - for i = 0,cols - 1 do - for j = 0,rows - 1 do - data = data .. string.format("background9[%f,%f;1,1;%s.png;false;%s]", - x + (i * 1.25), y + (j * 1.25), - skin.texture, - tostring(skin.radius)); - end - end - - return data; -end - -- Get the material cost for the given map scale and detail level -- scale: The map scale -- detail: The detail level @@ -63,7 +41,8 @@ local function get_craft_material_cost(meta) if stack:get_name() == "cartographer:map" then local smeta = stack:get_meta(); - local sub_cost = get_material_cost(smeta:get_int("cartographer:scale") or SCALE_SMALL, (smeta:get_int("cartographer:detail") or 1) - 1); + local sub_cost = get_material_cost(smeta:get_int("cartographer:scale") or SCALE_SMALL, + (smeta:get_int("cartographer:detail") or 1) - 1); is_positive = cost.paper >= sub_cost.paper and cost.pigment >= sub_cost.pigment; cost.paper = math.max(cost.paper - sub_cost.paper, 0); cost.pigment = math.max(cost.pigment - sub_cost.pigment, 0); @@ -142,31 +121,20 @@ end local fs = {}; --- Draw a button, with support for enabled/disabled states --- x: The x position of the button --- y: The y position of the button --- w: The width of the button --- h: The height of the button --- id: The element id --- text: The text to display in the button --- enabled: Whether or not the button is enabled --- --- Returns a formspec string -function fs.button(x, y, w, h, id, text, enabled) - if enabled then - return string.format("button[%f,%f;%f,%f;%s;%s]", x, y, w, h, id, text); - end - - return string.format("button[%f,%f;%f,%f;disabled_button;%s]", x, y, w, h, text); -end - -- Draw a 1px thick horizontal separator formspec element -- y: The y position of the separator -- skin: A 9-slice background skin table -- -- Returns a formspec string function fs.separator(y, skin) - return string.format("background9[0.1,%f;10.05,0.01;%s.png;false;%s]", y, skin.texture, tostring(skin.radius)) + return gui.bg9 { + x = 0.1, + y = y, + w = 10.05, + h = 0.01, + + skin = skin, + }; end -- Draw all the essential formspec data (size, background, styles, tabs) @@ -178,21 +146,106 @@ end -- -- Returns a formspec string function fs.header(w, h, rank, tab, skin) - local data = "formspec_version[3]" - .. string.format("size[%f,%f]", w, h) - .. string.format("background9[-0.1,0;1,1;%s.png;true;%s]", skin.background.texture, tostring(skin.background.radius)) - .. string.format("background9[0.0625,0.125;%f,%f;%s.png;false;%s]", w - 0.125, h - 0.25, skin.inner_background.texture, tostring(skin.inner_background.radius)) - .. string.format("style_type[button;noclip=true;border=false;bgimg=%s.png;bgimg_hovered=%s.png;bgimg_pressed=%s.png;bgimg_middle=%s;textcolor=%s]", skin.tab.texture, skin.tab.hovered_texture, skin.tab.pressed_texture, tostring(skin.tab.radius), skin.tab.font_color) - .. string.format("style[tab%d;noclip=true;border=false;bgimg=%s.png;bgimg_hovered=%s.png;bgimg_pressed=%s.png;bgimg_middle=%s;textcolor=%s]", tab, skin.tab.selected_texture, skin.tab.selected_texture, skin.tab.selected_texture, tostring(skin.tab.radius), skin.tab.font_color) - .. string.format("button[0.25,-0.425;1.5,0.55;tab1;Materials]", tab) - .. string.format("button[1.75,-0.425;1.5,0.55;tab2;Create Map]", tab); + local data = { + gui.formspec { + w = w, + h = h, + + bg = skin.background, + }, + gui.bg9 { + x = 0.0625, + y = 0.125, + + w = w - 0.125, + h = h - 0.25, + + skin = skin.inner_background, + }, + + gui.style_type { + selector = "button", + properties = { + noclip = true, + border = false, + + bgimg = skin.tab.texture .. ".png", + bgimg_hovered = skin.tab.hovered_texture .. ".png", + bgimg_pressed = skin.tab.pressed_texture .. ".png", + bgimg_middle = skin.tab.radius, + textcolor = skin.tab.font_color, + } + }, + gui.style { + selector = "tab" .. tostring(tab), + properties = { + bgimg = skin.tab.selected_texture .. ".png", + bgimg_hovered = skin.tab.selected_texture .. ".png", + bgimg_pressed = skin.tab.selected_texture .. ".png", + } + }, + + gui.button { + x = 0.25, + y = -0.425, + + w = 1.5, + h = 0.55, + + id = "tab1", + + text = "Materials" + }, + gui.button { + x = 1.75, + y = -0.425, + + w = 1.5, + h = 0.55, + + id = "tab2", + + text = "Create Map" + }, + }; if rank >= 2 then - data = data .. string.format("button[3.25,-0.425;1.5,0.55;tab3;Copy Map]", tab); + table.insert(data, gui.button { + x = 3.25, + y = -0.425, + + w = 1.5, + h = 0.55, + + id = "tab3", + + text = "Copy Map" + }); end - return data .. string.format("style_type[button;border=false;bgimg=%s.png;bgimg_hovered=%s.png;bgimg_pressed=%s.png;bgimg_middle=%s;textcolor=%s]", skin.button.texture, skin.button.hovered_texture, skin.button.pressed_texture, tostring(skin.button.radius), skin.button.font_color) - .. string.format("style[disabled_button;bgimg=;bgimg_hovered=;bgimg_pressed=;textcolor=%s]", skin.button.disabled_font_color); + table.insert(data, gui.style_type { + selector = "button", + properties = { + bgimg = skin.button.texture .. ".png", + bgimg_hovered = skin.button.hovered_texture .. ".png", + bgimg_pressed = skin.button.pressed_texture .. ".png", + bgimg_middle = skin.button.radius, + + textcolor = skin.button.font_color, + }, + }); + table.insert(data, gui.style { + selector = "disabled_button", + properties = { + bgimg = "", + bgimg_hovered = "", + bgimg_pressed = "", + + textcolor = skin.button.disabled_font_color, + }, + }); + + return table.concat(data); end -- Draw material counters from a table's metadata @@ -203,41 +256,116 @@ end -- -- Returns a formspec string function fs.materials(x, y, meta, skin) - return string.format("container[%f,%f]", x, y) - .. "formspec_version[3]" - .. string.format("background9[0,0.125;1,0.25;%s.png;false;%s]", skin.label.texture, tostring(skin.label.radius)) - .. string.format("image[0.125,0.125;0.25,0.25;%s.png]", skin.paper_texture) - .. string.format("label[0.375,0.25;%sx %d]", minetest.get_color_escape_sequence(skin.label.font_color), meta:get_int("paper")) - .. string.format("background9[1.25,0.125;1,0.25;%s.png;false;%s]", skin.label.texture, tostring(skin.label.radius)) - .. string.format("image[1.375,0.125;0.25,0.25;%s.png]", skin.pigment_texture) - .. string.format("label[1.625,0.25;%sx %d]", minetest.get_color_escape_sequence(skin.label.font_color), meta:get_int("pigment")) - .. "container_end[]"; + return gui.container { + x = x, + y = y, + + gui.bg9 { + x = 0, + y = 0.125, + + w = 1.0, + h = 0.25, + + skin = skin.label, + }, + gui.image { + x = 0.125, + y = 0.125, + + w = 0.25, + h = 0.25, + + image = skin.paper_texture .. ".png", + }, + gui.label { + x = 0.375, + y = 0.25, + + textcolor = skin.label.font_color, + text = string.format("x %d", meta:get_int("paper")), + }, + + gui.bg9 { + x = 1.25, + y = 0.125, + + w = 1.0, + h = 0.25, + + skin = skin.label, + }, + gui.image { + x = 1.375, + y = 0.125, + + w = 0.25, + h = 0.25, + + image = skin.pigment_texture .. ".png", + }, + gui.label { + x = 1.625, + y = 0.25, + + textcolor = skin.label.font_color, + text = string.format("x %d", meta:get_int("pigment")), + }, + }; end -- Draw a label with material costs from a table -- x: The x position of the interface -- y: The y position of the interface -- cost: A table of material costs, with string keys for the material --- names and iteger values +-- names and integer values -- skin: A formspec skin table -- -- Returns a formspec string function fs.cost(x, y, cost, skin) - local data = string.format("background9[%f,%f;1,0.5;%s.png;false;%s]", x, y - 0.125, skin.label.texture, tostring(skin.label.radius)); - local i = 0; + local data = { + gui.bg9 { + x = x, + y = y - 0.125, + w = 1, + h = 0.5, + skin = skin.label, + }, + } + + local i = 0; for name,value in pairs(cost) do + local texture = ""; if name == "paper" then - data = data .. string.format("image[%f,%f;0.25,0.25;%s.png]", x + 0.125, y + (i * 0.25) - 0.125, skin.paper_texture) + texture = skin.paper_texture .. ".png"; elseif name == "pigment" then - data = data .. string.format("image[%f,%f;0.25,0.25;%s.png]", x + 0.125, y + (i * 0.25) - 0.125, skin.pigment_texture) + texture = skin.pigment_texture .. ".png"; end - data = data .. string.format("label[%f,%f;%sx %d]", x + 0.375, y + (i * 0.25), minetest.get_color_escape_sequence(skin.label.font_color), value); + table.insert(data, gui.image { + x = x + 0.125, + y = y + (i * 0.25) - 0.125, + w = 0.25, + h = 0.25, + + image = texture, + }); + + table.insert(data, gui.label { + x = x + 0.375, + y = y + (i * 0.25); + w = 0.25, + h = 0.25, + + textcolor = skin.label.font_color, + text = string.format("x %d", value); + }); + i = i + 1; end - return data; + return table.concat(data); end -- Draw the material conversion tab UI @@ -251,14 +379,35 @@ function fs.convert(x, y, pos, skin) local meta = minetest.get_meta(pos); local value = cartographer.get_material_value(meta:get_inventory():get_stack("input", 1)); - return string.format("container[%f,%f]", x, y) - .. "formspec_version[3]" - .. inventory_bg(0, 0, 1, 1, skin.slot) - .. string.format("list[nodemeta:%d,%d,%d;input;0,0;1,1;]", pos.x, pos.y, pos.z) - .. "tooltip[0,0;1,1;Place items here to convert\nthem into mapmaking materials]" - .. fs.button(2.5, 0.25, 2, 0.5, "convert", "Convert Materials", value.paper + value.pigment > 0) - .. fs.cost(1.25, 0.375, value, skin) - .. "container_end[]"; + return gui.container { + x = x, + y = y, + + gui.inventory { + x = 0, + y = 0, + w = 1, + h = 1, + + location = string.format("nodemeta:%d,%d,%d", pos.x, pos.y, pos.z), + id = "input", + bg = skin.slot, + tooltip = "Place items here to convert\nthem into mapmaking materials", + }, + + gui.button { + x = 2.5, + y = 0.25, + w = 2, + h = 0.5, + + id = "convert", + text = "Convert Materials", + disabled = value.paper + value.pigment <= 0, + }, + + fs.cost(1.25, 0.375, value, skin), + }; end -- Draw the map crafting tab UI @@ -272,38 +421,135 @@ end -- Returns a formspec string function fs.craft(x, y, pos, rank, meta, skin) local cost, is_positive = get_craft_material_cost(meta); - local data = string.format("container[%f,%f]", x, y) - .. "formspec_version[3]" - .. inventory_bg(0, 1, 1, 1, skin.slot) - .. string.format("list[nodemeta:%d,%d,%d;output;0,1;1,1;]", pos.x, pos.y, pos.z) - .. "tooltip[0,1;1,1;Place a map here to upgrade it,\nor leave empty to craft]" - .. fs.button(2.5, 1.25, 2, 0.5, "craft", "Craft Map", is_positive and can_afford(cost, meta)) - .. fs.cost(1.25, 1.375, cost, skin); + + local data = { + x = x, + y = y, + + gui.inventory { + x = 0, + y = 1, + w = 1, + h = 1, + + location = string.format("nodemeta:%d,%d,%d", pos.x, pos.y, pos.z), + id = "output", + bg = skin.slot, + tooltip = "Place a map here to upgrade it,\nor leave empty to craft", + }, + gui.button { + x = 2.5, + y = 1.25, + w = 2, + h = 0.5, + + id = "craft", + text = "Craft Map", + disabled = not (is_positive and can_afford(cost, meta)), + }, + fs.cost(1.25, 1.375, cost, skin), + + gui.style { + selector = string.format("%dx,%d", meta:get_int("scale"), meta:get_int("detail") + 1), + properties = { + bgimg = skin.button.selected_texture .. ".png", + bgimg_hovered = skin.button.selected_texture .. ".png", + bgimg_pressed = skin.button.selected_texture .. ".png", + }, + }, + gui.label { + x = 0, + y = 0, + + text = "Detail Level", + textcolor = skin.label.font_color, + }, + }; if rank > 1 then - data = data .. string.format("style[%dx;bgimg=%s.png;bgimg_hovered=%s.png;bgimg_pressed=%s.png]", meta:get_int("scale"), skin.button.selected_texture, skin.button.selected_texture, skin.button.selected_texture) - .. string.format("label[2.5,0;%sMap Scale]", minetest.get_color_escape_sequence(skin.label.font_color)) - .. "button[2.5,0.25;0.5,0.5;1x;1x]" - .. "button[3.0,0.25;0.5,0.5;2x;2x]"; + table.insert(data, gui.button { + x = 2.5, + y = 0.25, + w = 0.5, + h = 0.5, + + id = "1x", + text = "1x", + }); + table.insert(data, gui.button { + x = 3.0, + y = 0.25, + w = 0.5, + h = 0.5, + + id = "2x", + text = "2x", + }); if rank > 2 then - data = data .. "button[3.5,0.25;0.5,0.5;4x;4x]" - .. "button[4.0,0.25;0.5,0.5;8x;8x]"; + table.insert(data, gui.button { + x = 3.5, + y = 0.25, + w = 0.5, + h = 0.5, + + id = "4x", + text = "4x", + }); + table.insert(data, gui.button { + x = 4.0, + y = 0.25, + w = 0.5, + h = 0.5, + + id = "8x", + text = "8x", + }); end end - data = data .. string.format("style[%d;bgimg=%s.png;bgimg_hovered=%s.png;bgimg_pressed=%s.png]", meta:get_int("detail") + 1, skin.button.selected_texture, skin.button.selected_texture, skin.button.selected_texture) - .. string.format("label[0,0;%sDetail Level]", minetest.get_color_escape_sequence(skin.label.font_color)) - .. "button[0.0,0.25;0.5,0.5;1;1]" - .. "button[0.5,0.25;0.5,0.5;2;2]"; + table.insert(data, gui.button { + x = 0, + y = 0.25, + w = 0.5, + h = 0.5, + + id = "1", + text = "1", + }); + table.insert(data, gui.button { + x = 0.5, + y = 0.25, + w = 0.5, + h = 0.5, + + id = "2", + text = "2", + }); if rank > 1 then - data = data .. "button[1.0,0.25;0.5,0.5;3;3]"; + table.insert(data, gui.button { + x = 1.0, + y = 0.25, + w = 0.5, + h = 0.5, + + id = "3", + text = "3", + }); if rank > 2 then - data = data .. "button[1.5,0.25;0.5,0.5;4;4]"; + table.insert(data, gui.button { + x = 1.5, + y = 0.25, + w = 0.5, + h = 0.5, + + id = "4", + text = "4", + }); end end - return data .. "container_end[]"; + return gui.container(data); end -- Draw the map copying tab UI @@ -317,15 +563,42 @@ function fs.copy(x, y, pos, skin) local meta = minetest.get_meta(pos); local costs = get_copy_material_cost(meta); - return string.format("container[%f,%f]", x, y) - .. "formspec_version[3]" - .. inventory_bg(0, 0, 1, 1, skin.slot) - .. string.format("list[nodemeta:%d,%d,%d;copy_input;0,0;1,1;]", pos.x, pos.y, pos.z) - .. fs.button(2.5, 0.25, 2, 0.5, "copy", "Copy Map", can_afford(costs, meta)) - .. fs.cost(1.25, 0.375, costs, skin) - .. inventory_bg(8.75, 0, 1, 1, skin.slot) - .. string.format("list[nodemeta:%d,%d,%d;copy_output;8.75,0;1,1;]", pos.x, pos.y, pos.z) - .. "container_end[]"; + return gui.container { + x = x, + y = y, + + gui.inventory { + x = 0, + y = 0, + w = 1, + h = 1, + + location = string.format("nodemeta:%d,%d,%d", pos.x, pos.y, pos.z), + id = "copy_input", + bg = skin.slot, + }, + gui.inventory { + x = 8.75, + y = 0, + w = 1, + h = 1, + + location = string.format("nodemeta:%d,%d,%d", pos.x, pos.y, pos.z), + id = "copy_output", + bg = skin.slot, + }, + gui.button { + x = 2.5, + y = 0.25, + w = 2, + h = 0.5, + + id = "copy", + text = "Copy Map", + disabled = not can_afford(costs, meta), + }, + fs.cost(1.25, 0.375, costs, skin), + }; end -- Draw the player's inventory @@ -335,11 +608,16 @@ end -- -- Returns a formspec string function fs.inv(x, y, skin) - return string.format("container[%f,%f]", x, y) - .. "formspec_version[3]" - .. inventory_bg(0, 0, 8, 4, skin.slot) - .. "list[current_player;main;0,0;8,4;]" - .. "container_end[]"; + return gui.inventory { + x = x, + y = y, + w = 8, + h = 4, + + location = "current_player", + id = "main", + bg = skin.slot, + }; end local player_tables = {};