From 6444963fb11279156e10fe9a25e6e22042f3d395 Mon Sep 17 00:00:00 2001 From: red-001 Date: Wed, 31 May 2017 19:07:33 +0100 Subject: [PATCH 001/107] Nametag: remove colour codes before calculating alignment. (#5862) --- src/camera.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/camera.cpp b/src/camera.cpp index 949494b0..52a42a3a 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -558,9 +558,10 @@ void Camera::drawNametags() f32 transformed_pos[4] = { pos.X, pos.Y, pos.Z, 1.0f }; trans.multiplyWith1x4Matrix(transformed_pos); if (transformed_pos[3] > 0) { + std::string nametag_colorless = unescape_enriched(nametag->nametag_text); core::dimension2d textsize = g_fontengine->getFont()->getDimension( - utf8_to_wide(nametag->nametag_text).c_str()); + utf8_to_wide(nametag_colorless).c_str()); f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f : core::reciprocal(transformed_pos[3]); v2u32 screensize = m_driver->getScreenSize(); From ed8215042c3fdf0f020b2870c8fb783ca839e30b Mon Sep 17 00:00:00 2001 From: red-001 Date: Wed, 31 May 2017 19:08:13 +0100 Subject: [PATCH 002/107] Add more files and file types to `.gitignore` (#5859) --- .gitignore | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2ef4fa6d..7b5ecab6 100644 --- a/.gitignore +++ b/.gitignore @@ -22,9 +22,12 @@ tags !tags/ gtags.files .idea/* +# Codelite +*.project ## Files related to minetest development cycle /*.patch +*.diff # GNU Patch reject file *.rej @@ -43,13 +46,17 @@ gtags.files !/mods/minetest/mods_here.txt /worlds /world/ -/clientmods/mods.conf +/clientmods/* +!/clientmods/preview/ /client/mod_storage/ ## Configuration/log files minetest.conf debug.txt +## Other files generated by minetest +screenshot_*.png + ## Doxygen files doc/Doxyfile doc/html/ From a7787bb9d27851b3e8ff9ff388e50793ee5120ed Mon Sep 17 00:00:00 2001 From: red-001 Date: Thu, 1 Jun 2017 07:00:26 +0100 Subject: [PATCH 003/107] Fix dropdown menu selection (#5847) This fixes a bug that occurred when the selection list of a drop down menu was changed but the name was still the same. --- src/guiFormSpecMenu.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 971d505f..22852b5e 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -2005,6 +2005,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_tooltips.clear(); m_inventory_rings.clear(); m_static_texts.clear(); + m_dropdowns.clear(); // Set default values (fits old formspec values) m_bgcolor = video::SColor(140,0,0,0); From 1c69476d9f10540088ead63c8bcae91f46bc84d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Thu, 1 Jun 2017 23:18:24 +0200 Subject: [PATCH 004/107] Show singlenode mapgen to menu (#5868) Fix #5867 --- src/mapgen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 6fd2ac58..dfe413a7 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -85,7 +85,7 @@ static MapgenDesc g_reg_mapgens[] = { {"flat", true}, {"fractal", true}, {"valleys", true}, - {"singlenode", false}, + {"singlenode", true}, }; STATIC_ASSERT( From 001de6ffbac83bcd41ecda075d79d265a69c7d42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Thu, 1 Jun 2017 23:18:55 +0200 Subject: [PATCH 005/107] Do not shade inventory items with textures (#5869) This commit restores the old behavior: if an inventory item has an own inventory texture, it will not be shaded. --- src/hud.cpp | 5 ++++- src/mesh.cpp | 18 ++++++++++-------- src/mesh.h | 9 +++++++++ src/wieldmesh.cpp | 5 +++++ src/wieldmesh.h | 7 ++++++- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/hud.cpp b/src/hud.cpp index 72145b4d..a2f031b4 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -698,7 +698,10 @@ void drawItemStack(video::IVideoDriver *driver, if (p->override_base) c = p->color; } - colorizeMeshBuffer(buf, &c); + if (imesh->needs_shading) + colorizeMeshBuffer(buf, &c); + else + setMeshBufferColor(buf, c); video::SMaterial &material = buf->getMaterial(); material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; material.Lighting = false; diff --git a/src/mesh.cpp b/src/mesh.cpp index 824d6891..3ab67510 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -175,6 +175,14 @@ void translateMesh(scene::IMesh *mesh, v3f vec) mesh->setBoundingBox(bbox); } +void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor &color) +{ + const u32 stride = getVertexPitchFromType(buf->getVertexType()); + u32 vertex_count = buf->getVertexCount(); + u8 *vertices = (u8 *) buf->getVertices(); + for (u32 i = 0; i < vertex_count; i++) + ((video::S3DVertex *) (vertices + i * stride))->Color = color; +} void setMeshColor(scene::IMesh *mesh, const video::SColor &color) { @@ -182,14 +190,8 @@ void setMeshColor(scene::IMesh *mesh, const video::SColor &color) return; u32 mc = mesh->getMeshBufferCount(); - for (u32 j = 0; j < mc; j++) { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - const u32 stride = getVertexPitchFromType(buf->getVertexType()); - u32 vertex_count = buf->getVertexCount(); - u8 *vertices = (u8 *)buf->getVertices(); - for (u32 i = 0; i < vertex_count; i++) - ((video::S3DVertex *)(vertices + i * stride))->Color = color; - } + for (u32 j = 0; j < mc; j++) + setMeshBufferColor(mesh->getMeshBuffer(j), color); } void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolor) diff --git a/src/mesh.h b/src/mesh.h index adaf0c83..423e43ae 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -49,11 +49,20 @@ void scaleMesh(scene::IMesh *mesh, v3f scale); */ void translateMesh(scene::IMesh *mesh, v3f vec); +/*! + * Sets a constant color for all vertices in the mesh buffer. + */ +void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor &color); + /* Set a constant color for all vertices in the mesh */ void setMeshColor(scene::IMesh *mesh, const video::SColor &color); +/*! + * Overwrites the color of a mesh buffer. + * The color is darkened based on the normal vector of the vertices. + */ void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolor); /* diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp index 8b1477bb..7736ec2a 100644 --- a/src/wieldmesh.cpp +++ b/src/wieldmesh.cpp @@ -440,10 +440,15 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) scene::SMesh *mesh = NULL; + // Shading is on by default + result->needs_shading = true; + // If inventory_image is defined, it overrides everything else if (def.inventory_image != "") { mesh = getExtrudedMesh(tsrc, def.inventory_image); result->buffer_colors.push_back(ItemPartColor()); + // Items with inventory images do not need shading + result->needs_shading = false; } else if (def.type == ITEM_NODE) { if (f.mesh_ptr[0]) { mesh = cloneMesh(f.mesh_ptr[0]); diff --git a/src/wieldmesh.h b/src/wieldmesh.h index ef164c11..faedce48 100644 --- a/src/wieldmesh.h +++ b/src/wieldmesh.h @@ -59,8 +59,13 @@ struct ItemMesh * Stores the color of each mesh buffer. */ std::vector buffer_colors; + /*! + * If false, all faces of the item should have the same brightness. + * Disables shading based on normal vectors. + */ + bool needs_shading; - ItemMesh() : mesh(NULL), buffer_colors() {} + ItemMesh() : mesh(NULL), buffer_colors(), needs_shading(true) {} }; /* From 30c51a1115539b76aa76091de0adbb083c680b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Fri, 2 Jun 2017 13:57:59 +0000 Subject: [PATCH 006/107] Document hardware coloring and soft node overlays (#5876) --- doc/lua_api.txt | 161 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 76244646..e6d85636 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -426,6 +426,167 @@ Result is more like what you'd expect if you put a color on top of another color. Meaning white surfaces get a lot of your new color while black parts don't change very much. +Hardware coloring +----------------- +The goal of hardware coloring is to simplify the creation of +colorful nodes. If your textures use the same pattern, and they only +differ in their color (like colored wool blocks), you can use hardware +coloring instead of creating and managing many texture files. +All of these methods use color multiplication (so a white-black texture +with red coloring will result in red-black color). + +### Static coloring +This method is useful if you wish to create nodes/items with +the same texture, in different colors, each in a new node/item definition. + +#### Global color +When you register an item or node, set its `color` field (which accepts a +`ColorSpec`) to the desired color. + +An `ItemStack`s static color can be overwritten by the `color` metadata +field. If you set that field to a `ColorString`, that color will be used. + +#### Tile color +Each tile may have an individual static color, which overwrites every +other coloring methods. To disable the coloring of a face, +set its color to white (because multiplying with white does nothing). +You can set the `color` property of the tiles in the node's definition +if the tile is in table format. + +### Palettes +For nodes and items which can have many colors, a palette is more +suitable. A palette is a texture, which can contain up to 256 pixels. +Each pixel is one possible color for the node/item. +You can register one node/item, which can have up to 256 colors. + +#### Palette indexing +When using palettes, you always provide a pixel index for the given +node or `ItemStack`. The palette is read from left to right and from +top to bottom. If the palette has less than 256 pixels, then it is +stretched to contain exactly 256 pixels (after arranging the pixels +to one line). The indexing starts from 0. + +Examples: +* 16x16 palette, index = 0: the top left corner +* 16x16 palette, index = 4: the fifth pixel in the first row +* 16x16 palette, index = 16: the pixel below the top left corner +* 16x16 palette, index = 255: the bottom right corner +* 2 (width)x4 (height) palette, index=31: the top left corner. + The palette has 8 pixels, so each pixel is stretched to 32 pixels, + to ensure the total 256 pixels. +* 2x4 palette, index=32: the top right corner +* 2x4 palette, index=63: the top right corner +* 2x4 palette, index=64: the pixel below the top left corner + +#### Using palettes with items +When registering an item, set the item definition's `palette` field to +a texture. You can also use texture modifiers. + +The `ItemStack`'s color depends on the `palette_index` field of the +stack's metadata. `palette_index` is an integer, which specifies the +index of the pixel to use. + +#### Linking palettes with nodes +When registering a node, set the item definition's `palette` field to +a texture. You can also use texture modifiers. +The node's color depends on its `param2`, so you also must set an +appropriate `drawtype`: +* `drawtype = "color"` for nodes which use their full `param2` for + palette indexing. These nodes can have 256 different colors. + The palette should contain 256 pixels. +* `drawtype = "colorwallmounted"` for nodes which use the first + five bits (most significant) of `param2` for palette indexing. + The remaining three bits are describing rotation, as in `wallmounted` + draw type. Division by 8 yields the palette index (without stretching the + palette). These nodes can have 32 different colors, and the palette + should contain 32 pixels. + Examples: + * `param2 = 17` is 2 * 8 + 1, so the rotation is 1 and the third (= 2 + 1) + pixel will be picked from the palette. + * `param2 = 35` is 4 * 8 + 3, so the rotation is 3 and the fifth (= 4 + 1) + pixel will be picked from the palette. +* `drawtype = "colorfacedir"` for nodes which use the first + three bits of `param2` for palette indexing. The remaining + five bits are describing rotation, as in `facedir` draw type. + Division by 32 yields the palette index (without stretching the + palette). These nodes can have 8 different colors, and the + palette should contain 8 pixels. + Examples: + * `param2 = 17` is 0 * 32 + 17, so the rotation is 17 and the + first (= 0 + 1) pixel will be picked from the palette. + * `param2 = 35` is 1 * 32 + 3, so the rotation is 3 and the + second (= 1 + 1) pixel will be picked from the palette. + +To colorize a node on the map, set its `param2` value (according +to the node's draw type). + +### Conversion between nodes in the inventory and the on the map +Static coloring is the same for both cases, there is no need +for conversion. + +If the `ItemStack`'s metadata contains the `color` field, it will be +lost on placement, because nodes on the map can only use palettes. + +If the `ItemStack`'s metadata contains the `palette_index` field, you +currently must manually convert between it and the node's `param2` with +custom `on_place` and `on_dig` callbacks. + +### Colored items in craft recipes +Craft recipes only support item strings, but fortunately item strings +can also contain metadata. Example craft recipe registration: + + local stack = ItemStack("wool:block") + dyed:get_meta():set_int("palette_index", 3) -- add index + minetest.register_craft({ + output = dyed:to_string(), -- convert to string + type = "shapeless", + recipe = { + "wool:block", + "dye:red", + }, + }) + +Metadata field filtering in the `recipe` field are not supported yet, +so the craft output is independent of the color of the ingredients. + +Soft texture overlay +-------------------- +Sometimes hardware coloring is not enough, because it affects the +whole tile. Soft texture overlays were added to Minetest to allow +the dynamic coloring of only specific parts of the node's texture. +For example a grass block may have colored grass, while keeping the +dirt brown. + +These overlays are 'soft', because unlike texture modifiers, the layers +are not merged in the memory, but they are simply drawn on top of each +other. This allows different hardware coloring, but also means that +tiles with overlays are drawn slower. Using too much overlays might +cause FPS loss. + +To define an overlay, simply set the `overlay_tiles` field of the node +definition. These tiles are defined in the same way as plain tiles: +they can have a texture name, color etc. +To skip one face, set that overlay tile to an empty string. + +Example (colored grass block): + + minetest.register_node("default:dirt_with_grass", { + description = "Dirt with Grass", + -- Regular tiles, as usual + -- The dirt tile disables palette coloring + tiles = {{name = "default_grass.png"}, + {name = "default_dirt.png", color = "white"}}, + -- Overlay tiles: define them in the same style + -- The top and bottom tile does not have overlay + overlay_tiles = {"", "", + {name = "default_grass_side.png", tileable_vertical = false}}, + -- Global color, used in inventory + color = "green", + -- Palette in the world + paramtype2 = "color", + palette = "default_foilage.png", + }) + Sounds ------ Only Ogg Vorbis files are supported. From 80fe516e4e9139503afec234d81555468803b118 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 2 Jun 2017 17:16:30 +0200 Subject: [PATCH 007/107] =?UTF-8?q?Remove=20=E2=80=9Cinf=E2=80=9D=20argume?= =?UTF-8?q?nt=20from=20shutdown=20command=20help=20(#5880)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builtin/game/chatcommands.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 3f5f044b..3dfc29ff 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -807,7 +807,7 @@ core.register_chatcommand("days", { core.register_chatcommand("shutdown", { description = "Shutdown server", - params = "[delay_in_seconds(0..inf) or -1 for cancel] [reconnect] [message]", + params = "[delay_in_seconds (non-negative number, or -1 to cancel)] [reconnect] [message]", privs = {server=true}, func = function(name, param) local delay, reconnect, message = param:match("([^ ][-]?[0-9]+)([^ ]+)(.*)") From 1b83b0acfde26e1689202bb99659934ed598d705 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 2 Jun 2017 14:15:49 +0200 Subject: [PATCH 008/107] Lua_api.txt: Various edits and Markdown syntax improvements Add minor bits of missing Lua API documentation. Remove L-system lighting bug warning. Clarify 2 lines in node timer documentation. Fix many Markdown syntax errors in lua_api.txt. --- doc/lua_api.txt | 253 ++++++++++++++++++++++++++---------------------- 1 file changed, 136 insertions(+), 117 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index e6d85636..e4ffa7bb 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -186,7 +186,11 @@ Naming convention for registered textual names ---------------------------------------------- Registered names should generally be in this format: - "modname:" ( can have characters a-zA-Z0-9_) + `modname:` + +`` can have these characters: + + a-zA-Z0-9_ This is to prevent conflicting names from corrupting maps and is enforced by the mod loader. @@ -209,7 +213,7 @@ The `:` prefix can also be used for maintaining backwards compatibility. ### Aliases Aliases can be added by using `minetest.register_alias(name, convert_to)` or -`minetest.register_alias_force(name, convert_to). +`minetest.register_alias_force(name, convert_to)`. This will make Minetest to convert things called name to things called `convert_to`. @@ -309,10 +313,10 @@ Example: default_sandstone.png^[resize:16x16 #### `[opacity:` - Makes the base image transparent according to the given ratio. - r must be between 0 and 255. - 0 means totally transparent. - 255 means totally opaque. +Makes the base image transparent according to the given ratio. + +`r` must be between 0 and 255. +0 means totally transparent. 255 means totally opaque. Example: @@ -676,7 +680,7 @@ the global `minetest.registered_*` tables. * `minetest.unregister_item(name)` * Unregisters the item name from engine, and deletes the entry with key * `name` from `minetest.registered_items` and from the associated item - * table according to its nature: minetest.registered_nodes[] etc + * table according to its nature: `minetest.registered_nodes[]` etc * `minetest.register_biome(biome definition)` * returns an integer uniquely identifying the registered biome @@ -754,9 +758,9 @@ They are represented by a table: {name="name", param1=num, param2=num} -`param1` and `param2` are 8-bit integers. The engine uses them for certain -automated functions. If you don't use these functions, you can use them to -store arbitrary values. +`param1` and `param2` are 8-bit integers ranging from 0 to 255. The engine uses +them for certain automated functions. If you don't use these functions, you can +use them to store arbitrary values. The functions of `param1` and `param2` are determined by certain fields in the node definition: @@ -825,15 +829,6 @@ node definition: ^ Only valid for "glasslike_framed" or "glasslike_framed_optional" drawtypes. param2 defines 64 levels of internal liquid. Liquid texture is defined using `special_tiles = {"modname_tilename.png"},` - collision_box = { - type = "fixed", - fixed = { - {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, - }, - }, - ^ defines list of collision boxes for the node. If empty, collision boxes - will be the same as nodeboxes, in case of any other nodes will be full cube - as in the example above. Nodes can also contain extra data. See "Node Metadata". @@ -984,6 +979,7 @@ If no flags are specified (or defaults is), 2D noise is eased and 3D noise is no Accumulates the absolute value of each noise gradient result. Noise parameters format example for 2D or 3D perlin noise or perlin noise maps: + np_terrain = { offset = 0, scale = 1, @@ -994,8 +990,8 @@ Noise parameters format example for 2D or 3D perlin noise or perlin noise maps: lacunarity = 2.0, flags = "defaults, absvalue" } - ^ A single noise parameter table can be used to get 2D or 3D noise, - when getting 2D noise spread.z is ignored. + ^ A single noise parameter table can be used to get 2D or 3D noise, + when getting 2D noise spread.z is ignored. Ore types @@ -1067,14 +1063,15 @@ to small changes. The following is a decent set of parameters to work from: }, noise_threshold = 1.6 -WARNING: Use this ore type *very* sparingly since it is ~200x more +**WARNING**: Use this ore type *very* sparingly since it is ~200x more computationally expensive than any other ore. Ore attributes -------------- See section "Flag Specifier Format". -Currently supported flags: `absheight` +Currently supported flags: +`absheight`, `puff_cliffs`, `puff_additive_composition`. ### `absheight` Also produce this same ore between the height range of `-y_max` and `-y_min`. @@ -1130,6 +1127,7 @@ in the form of a table. This table specifies the following fields: previous contents (default: false) About probability values: + * A probability value of `0` or `1` means that node will never appear (0% chance). * A probability value of `254` or `255` means the node will always appear (100% chance). * If the probability value `p` is greater than `1`, then there is a @@ -1284,16 +1282,32 @@ There are three kinds of items: nodes, tools and craftitems. things according to `tool_capabilities`. * Craftitem (`register_craftitem`): A miscellaneous item. +### Amount and wear +All item stacks have an amount between 0 to 65535. It is 1 by +default. Tool item stacks can not have an amount greater than 1. + +Tools use a wear (=damage) value ranging from 0 to 65535. The +value 0 is the default and used is for unworn tools. The values +1 to 65535 are used for worn tools, where a higher value stands for +a higher wear. Non-tools always have a wear value of 0. + ### Item formats Items and item stacks can exist in three formats: Serializes, table format and `ItemStack`. #### Serialized -This is called "stackstring" or "itemstring": +This is called "stackstring" or "itemstring". It is a simple string with +1-3 components: the full item identifier, an optional amount and an optional +wear value. Syntax: -* e.g. `'default:dirt 5'` -* e.g. `'default:pick_wood 21323'` -* e.g. `'default:apple'` + [[ ]] + +Examples: + +* `'default:apple'`: 1 apple +* `'default:dirt 5'`: 5 dirt +* `'default:pick_stone'`: a new stone pickaxe +* `'default:pick_wood 1 21323'`: a wooden pickaxe, ca. 1/3 worn out #### Table format Examples: @@ -1387,6 +1401,9 @@ Another example: Make red wool from white wool and red dye: ### Special groups * `immortal`: Disables the group damage system for an entity +* `punch_operable`: For entities; disables the regular damage mechanism for + players punching it by hand or a non-tool item, so that it can do something + else than take damage. * `level`: Can be used to give an additional sense of progression in the game. * A larger level will cause e.g. a weapon of a lower level make much less damage, and get worn out much faster, or not be able to get drops @@ -1425,6 +1442,7 @@ Another example: Make red wool from white wool and red dye: ### Examples of custom groups Item groups are often used for defining, well, _groups of items_. + * `meat`: any meat-kind of a thing (rating might define the size or healing ability or be irrelevant -- it is not defined as of yet) * `eatable`: anything that can be eaten. Rating might define HP gain in half @@ -1645,7 +1663,7 @@ Item metadata only contains a key-value store. Some of the values in the key-value store are handled specially: -* `description`: Set the itemstack's description. Defaults to idef.description +* `description`: Set the item stack's description. Defaults to `idef.description` * `color`: A `ColorString`, which sets the stack's color. * `palette_index`: If the item has a palette, this is used to get the current color from the palette. @@ -1707,7 +1725,7 @@ examples. #### `container[,]` * Start of a container block, moves all physical elements in the container by (X, Y) -* Must have matching container_end +* Must have matching `container_end` * Containers can be nested, in which case the offsets are added (child containers are relative to parent containers) @@ -1792,7 +1810,7 @@ examples. #### `field[,;,;;