Verified Commit 66c332f7 authored by Dirk's avatar Dirk
Browse files

make compatible with Minetest 5.1.0

New style of formspec handling/creation and compatibility with formspec
version 2 coordinates.
parent ccb1f00b
......@@ -14,6 +14,8 @@ In both cases the player needs at least one free inventory slot where the node c
The selector can be accessed via the sfinv page as shown above but there are also a custom *Unified Inventory* page as well as a node. The node is crafted by red, blue, green, and yellow wool in a square.
If the node formspec looks super weird (massive spaces, large buttons, pixels overlapping or have large gaps, etc.) make sure you’re running at least Minetest 5.1.0 because the mod uses the new formspec version for the node. The sfinv page and the Unified Inventory page are not affected.
## Built-in Palettes
The built-in palettes all use *Silver Sandstone* as base node (`default:silver_sandstone`) when not in creative mode. All palettes are carefully created using original sources.
......@@ -91,7 +93,7 @@ Palette customization files are returning a table structured as shown below.
```lua
return {
base_node = 'default:cobble',
name = 'Modifiziert',
name = 'Modified',
default_opacity = 100,
pixels = {
['#00ff00'] = {
......@@ -110,3 +112,7 @@ Except the `pixels` entry this is similar to the palette definition. Values can
The `pixels` entry is a table with named entries. The names for the entries are the color names you want to modify. The named entries are tables that set the values that can be set in a PixelSpec except the color itself.
The colors of existing palettes can’t be changed with this functionality. This is intended because palettes are artistic works. If you want to have different colors in a palette you need to create a custom palette.
# Node Formspec Looks Weird
If the node formspec looks super weird (massive spaces, large buttons, pixels overlapping or have large gaps, etc.) you’re running an outdated version of Minetest. You need at least 5.1.0 to pproperly run the mod because it uses the new formspec version for the node. The sfinv page and the Unified Inventory page are not affected. They use the “version 1” code.
......@@ -9,14 +9,36 @@ local pixelart_texture = '(('..table.concat({
}, ':')..')^[resize:8x8)'
local open_node_formspec = function (player, formname)
local name = player:get_player_name()
local player_name = player:get_player_name()
local formspec = table.concat({
'size[8,9]',
pixelart.build_formspec(player, 'selector_node')
}, ' ')
local formspec = pixelart.build_formspec('selector_node', {
formspec = {
header = true,
version = 2,
width = 13,
height = 9,
for_player = player_name
},
buttons = {
width = 2.5,
height = 0.5,
spacing = 0.125,
position = { top = 0.25, left = 0.25 }
},
palette = {
title = {
show = true,
position = { top = 0.125+0.25, left = 2.5+0.25+0.25 }
}
},
pixels = {
size = 0.7,
spacing = { left = 0.05, bottom = 0.05 },
position = { top = 0.25+0.25+0.125, left = 3 }
}
})
minetest.show_formspec(name, 'pixelart:selector', formspec)
minetest.show_formspec(player_name, 'pixelart:selector', formspec)
end
pixelart.palette_actions['selector_node'] = open_node_formspec
......
......@@ -8,7 +8,30 @@ pixelart.palette_actions['sfinv_page'] = sfinv.set_page
sfinv.register_page(pixelart.modname..':selector', {
title = pixelart.S('Pixelart'),
get = function(self, player, context)
local formspec = pixelart.build_formspec(player, 'sfinv_page')
local formspec = pixelart.build_formspec('sfinv_page', {
formspec = {
header = false,
for_player = player:get_player_name()
},
buttons = {
width = 1.85,
height = 1,
spacing = -1+0.8,
position = { top = -0.125, left = 0 }
},
palette = {
title = {
show = true,
position = { top = 0.3-0.45, left = 1.85 }
}
},
pixels = {
size = 0.75,
spacing = { left = -0.2, bottom = -0.125 },
position = { top = 0.4125, left = 1.83 },
force_per_row = 11
}
})
return sfinv.make_formspec(player, context, formspec, false)
end
})
......
......@@ -14,7 +14,30 @@ local pixelart_texture = '(('..table.concat({
unified_inventory.register_page('pixelart:selector', {
get_formspec = function(player)
return {
formspec = pixelart.build_formspec(player, 'ui_page'),
formspec = pixelart.build_formspec('ui_page', {
formspec = {
header = false,
for_player = player:get_player_name()
},
buttons = {
width = 1.85,
height = 1,
spacing = -1+0.8,
position = { top = -0.125, left = 0 }
},
palette = {
title = {
show = true,
position = { top = 0.3-0.45, left = 1.85 }
}
},
pixels = {
size = 0.75,
spacing = { left = -0.2, bottom = -0.125 },
position = { top = 0.4125, left = 1.83 },
force_per_row = 11
}
}),
draw_inventory = false
}
end,
......
......@@ -7,4 +7,3 @@ Current Palette: @1=Aktuelle Palette: @1
# Messages
Not enough room for this pixel!=Für diesen Pixel ist kein Platz im Inventar!
You need @1 for this pixel!=Für diesen Pixel wird @1 benötigt!
No palette selected=Keine Palette ausgewählt
-- Build the formspec content for the player
-- # vim: nowrap
--
-- This function gets called when the fsinv page is opened and generates the
-- page’s formspec.
--
-- First are the palette buttons are built. The buttons are sorted by palette
-- ID and are shown independently from the palettes. Second the pixels (node
-- buttons) are loaded. For deciding the palette the user meta data value is
-- used. The value is set from the corresponding `field` handling function.
-- Set Vim to no-wrapping mode because of some lines not fitting within the 80
-- characters width limit due to overall readability of the code.
-- Build palette buttons
--
-- When a valid palette was found the buttons and the pallette’s name label are
-- generated and added to the formspec.
-- {
-- width = 2.5,
-- height = 0.5,
-- spacing = 0.125
-- position = { top = 0.25, left = 0.25 }
-- }
--
-- When everything is done the function returns the final formspec for the
-- sfinv page as single string.
-- `width` and `height` define the button size and `spacing` the spacing
-- between two buttons. The spacing is actualy the position of the “next”
-- button in the list. The `position` table is not used in THIS function but
-- for positioning the buttons container in `pixelart.build_formspec`
--
-- @param player The player object accessing the sfinv page
-- @return string The sfinv page’s formspec
local formspec_content = function (player, action)
local meta = player:get_meta()
local current_palette = meta:get(pixelart.palette_meta)
local palettes = {}
local palette_name = pixelart.S('No palette selected')
local pixels = { buttons = '', row = 0, column = 0 }
local button_position = -0.125
-- @see pixelart.build_formspec
-- @param parameters The table as described
-- @return string The parsed button list as formspec string
local get_palette_buttons = function (buttons)
local buttons_table = {}
local buttons_string = ''
local button_position = 0
local button_spacing = buttons.height + buttons.spacing
-- Build button list for palettes
for palette_id in pixelart.sorted_pairs(pixelart.palettes) do
local button = ('button[0,+pos;1.85,1;+name;+label]'):gsub('%+%a+', {
['+pos'] = button_position,
['+name'] = 'palette_'..palette_id,
['+label'] = pixelart.palettes[palette_id].name
local button = ('button[0,+p;+w,+h;+i;+l]'):gsub('%+%a+', {
['+p'] = button_position,
['+w'] = buttons.width,
['+h'] = buttons.height,
['+i'] = 'palette_'..palette_id,
['+l'] = pixelart.palettes[palette_id].name
})
table.insert(palettes, button)
button_position = button_position + 0.8
table.insert(buttons_table, button)
button_position = button_position + button_spacing
end
-- Build pixels list
if pixelart.palettes[current_palette] then
palette_name = pixelart.palettes[current_palette].name
pixels.buttons = pixelart.get_pixels(current_palette)
end
buttons_string = table.concat(buttons_table, ' ')
return 'container[+bleft,+btop]'..buttons_string..'container_end[]'
end
-- Build formspec
local r = {
'field[-1,-1;0,0;action_'..action..';;]',
'container[0,0]',
table.concat(palettes, ' '),
'container_end[]',
'container[1.85,0.3]',
'label[0,-0.45;'..pixelart.S('Current Palette: @1',palette_name)..']',
pixels.buttons,
'container_end[]'
}
return table.concat(r, ' ')
-- Validate the parameters table
--
-- This function takes the input and merges in all missing values in the input
-- table using the validation table as source. The source basically resembles
-- the node formspec.
--
-- @param input A parameters table to validate
-- @return table The validated and completed table
local validate_parameters_table = function (input)
local valid = {
formspec = { header = true, version = 2, width = 13, height = 9, for_player = '' },
buttons = { width = 2.5, height = 0.5, spacing = 0.125, position = { top = 0.25, left = 0.25 } },
palette = { title = { show = true, position = { top = 0.125+0.25, left = 2.5+0.25+0.25 } } },
pixels = { size = 0.7, spacing = { left = 0.05, bottom = 0.05 }, position = { top = 0.25+0.25+0.125, left = 3 }, force_per_row = false }
}
return pixelart.merge_tables(valid, input)
end
-- Function that returns the buil formspec.
-- Build the formspec based on the given parameters
--
-- The first parameter is the action defined in the interoperability file (see
-- provided files for examples). Basically those actions are references to
-- functions that will be executed when pressing any buttons in the formspec
-- to bring up the formspec again.
--
-- This function allows optional adjustments if needed later. Right now it just
-- returns the built formspec.
-- The second parameter is a parameters table as described here:
--
-- @param player The player object to build the formspec to
-- @param action The desired after palette selection action (those actions are
-- set from within the individual mod’s interoperability files).
pixelart.build_formspec = function (player, action)
return formspec_content(player, action)
-- parameters_in = {
-- formspec = {
-- header = true, -- Use formspec header (version & size)
-- version = 2, -- Formspec version to use
-- width = 13, -- Formspec width
-- height = 9m -- Formspec height
-- for_player = 'Name' -- Generate formspec for this player
-- },
-- buttons = {
-- width = 2.5, -- Palette selection buttons width
-- height = 0.5, -- Palette selection buttons height
-- spacing = 0.125, -- Spacing between palette selection buttons
-- position = {} -- Palette selection buttons position
-- },
-- palette = {
-- title = {
-- show = true, -- Whether to show the palette title or not
-- position = {} -- Position for the palette title
-- }
-- },
-- pixels = {
-- size = 0.7, -- Size of the pixel buttons
-- position = {} -- Position of the pixel buttons
-- spacing = {
-- left = 0.05 -- Left spacing of pixel buttons
-- bottom = 0.05 -- Bottom spacing of pixel buttons
-- }
-- }
-- }
--
-- All `position` entries are tables of the same style.
--
-- position = {
-- top = N, -- Position of the element related to the top of the formspec
-- left = N -- Position of the element related to the left of the formspec
-- }
--
-- Where `N` is a formspec units value.
--
-- @param action The formspec action to perform when clicking something
-- @param paramneters_in The formspec parameters as described
-- @return string The parsed formspec based on the parameters
pixelart.build_formspec = function (action, parameters_in)
if not parameters_in.formspec then return '' end
if not parameters_in.formspec.for_player then return '' end
local parameters = validate_parameters_table(parameters_in)
-- Check header usage
local header = ''
if parameters.formspec.header == true then
header = 'formspec_version[+version] size[+width,+height]'
end
-- Generate formspec pattern
local formspec_pattern = table.concat({
header,
'field[-1,-1;0,0;action_'..action..';;]',
get_palette_buttons(parameters.buttons),
pixelart.get_palette_pixels(parameters),
}, ' ')
-- Replace placeholder variables in pattern
local parsed_formspec = (formspec_pattern):gsub('%+%a+', {
['+version'] = parameters.formspec.version,
['+width'] = parameters.formspec.width,
['+height'] = parameters.formspec.height,
['+tleft'] = parameters.palette.title.position.left,
['+ttop'] = parameters.palette.title.position.top,
['+bleft'] = parameters.buttons.position.left,
['+btop'] = parameters.buttons.position.top,
['+pleft'] = parameters.pixels.position.left,
['+ptop'] = parameters.pixels.position.top,
})
return parsed_formspec
end
-- Get pixels and title for requested palette
--
-- This function loads all pixels from the given palette ID and returns them as
-- a string being the clickable buttons for the formspec. The colors are taken
-- from the palette definition as set there and are not sorted any further.
--
-- If the palette does not exist an empty string is returned.
--
-- If requested and available the title of the palette is shown, too.
--
-- @see pixelart.build_formspec
-- @param parameters a parameters table as per `pixelart.build_formspec`
-- @return string The “button string” for the palette formspec
pixelart.get_palette_pixels = function (parameters)
-- Shorthand for parameter entries
local formspec = parameters.formspec
local buttons = parameters.buttons
local pixels = parameters.pixels
-- Player data and current palette
local player_name = formspec.for_player
local player = minetest.get_player_by_name(player_name)
local meta = player:get_meta()
local current_palette = meta:get(pixelart.palette_meta)
-- Empty string return or preparing palette processing
if not pixelart.palettes[current_palette] then return '' end
local palette = pixelart.palettes[current_palette]
local pixels_table = {}
local pixels_string = ''
-- Palette title Processing
local title_string = ''
if parameters.palette.title.show == true then
title_string = ('+c'):gsub('%+%a+', {
['+c'] = pixelart.S('Current Palette: @1', palette.name)
})
end
-- Pixel button base positions
local button_x = 0 -- from left
local button_y = 0 -- from top
local buttons_in_row = 1
local buttons_per_row = pixels.force_per_row
-- Calculate buttons per row
if pixels.force_per_row == false then
local available_width = formspec.width - pixels.position.left
local button_size = pixels.size + pixels.spacing.left
buttons_per_row = math.floor(available_width / button_size)
end
for _,PixelSpec in pairs(palette.pixels) do
local color,opacity = pixelart.color_values(palette, PixelSpec)
local nodename = pixelart.generate_nodename(current_palette, color)
local pixel = ('item_image_button[+x,+y;+s,+s;+i;+n;]'):gsub('%+%a+', {
['+x'] = button_x,
['+y'] = button_y,
['+s'] = pixels.size,
['+i'] = nodename,
['+n'] = 'pixel_'..nodename
})
if buttons_in_row < buttons_per_row then
button_x = button_x + pixels.size + pixels.spacing.left
else
button_x = 0
button_y = button_y + pixels.size + pixels.spacing.bottom
buttons_in_row = 0
end
pixels_table[#pixels_table+1] = pixel
buttons_in_row = buttons_in_row + 1
if #pixels_table == 128 then break end
end
return table.concat({
'label[+tleft,+ttop;'..title_string..']',
'container[+pleft,+ptop]',
table.concat(pixels_table, ' '),
'container_end[]'
}, ' ')
end
-- Get pixels for requested palette
--
-- This function loads all pixels from the given palette ID and returns them as
-- a string being the clickable buttons for the formspec. The colors are taken
-- from the palette definition as set there and are not sorted any further.
--
-- If the palette does not exist an empty string is returned.
--
-- @param palette_id The ID of the palette to load.
-- @return string The “button string” for the palette formspec
pixelart.get_pixels = function (palette_id)
local palette = pixelart.palettes[palette_id]
local pixels = {}
local button_x = 0 -- from left
local button_y = 0 -- from top
local buttons_in_row = 0
if not palette then return '' end
for _,PixelSpec in pairs(palette.pixels) do
local color,opacity = pixelart.color_values(palette, PixelSpec)
local nodename = pixelart.generate_nodename(palette_id, color)
local b = ('item_image_button[+x,+y;0.75,0.75;+i;+n;]'):gsub('%+%a+', {
['+x'] = button_x,
['+y'] = button_y,
['+i'] = nodename,
['+n'] = 'pixel_'..nodename
})
pixels[#pixels+1] = b
buttons_in_row = buttons_in_row + 1
if buttons_in_row <= 9 then
button_x = button_x + 0.6
else
button_x = 0
button_y = button_y + 0.65
buttons_in_row = 0
end
if #pixels == 128 then break end
end
return table.concat(pixels, ' ')
end
......@@ -21,6 +21,30 @@ pixelart.sorted_pairs = function (t)
end
-- Merge two tables
--
-- This function merges all values from `merge_from` into `merge_to`
-- overwriting existing values in `merge_to` with values from `merge_from`
--
-- @param merge_to The table to merge to
-- @param merge_from The table to merge from
-- @return table the merged table as described
pixelart.merge_tables = function (merge_to, merge_from)
for k,v in pairs(merge_from) do
if type(v) == 'table' then
if type(merge_to[k] or false) == 'table' then
pixelart.merge_tables(merge_to[k] or {}, merge_from[k] or {})
else
merge_to[k] = v
end
else
merge_to[k] = v
end
end
return merge_to
end
-- Return opacity and color
--
-- When a palette and a PixelSpec are provided this function returns the
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment