diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index be6ed38..179c084 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,6 +6,7 @@ variables: before_script: - rm Gemfile.lock + - bundle install test: stage: test @@ -21,10 +22,8 @@ test: pages: stage: deploy interruptible: true - before_script: - - rm Gemfile.lock script: - - bundle exec jekyll build -d public + - bundle exec jekyll build -d public --baseurl /minetest_modding_book artifacts: paths: - public diff --git a/Gemfile b/Gemfile index 1f50fb4..c26845e 100644 --- a/Gemfile +++ b/Gemfile @@ -6,4 +6,5 @@ gem "webrick" group :jekyll_plugins do gem "jekyll-sitemap" gem "jekyll-redirect-from" + gem "jekyll-sass-converter", "~> 2.0" end diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7d4f96c --- /dev/null +++ b/LICENSE @@ -0,0 +1,427 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + including for purposes of Section 3(b); and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/_en/basics/getting_started.md b/_en/basics/getting_started.md index aa814a7..73ddd31 100644 --- a/_en/basics/getting_started.md +++ b/_en/basics/getting_started.md @@ -12,18 +12,18 @@ redirect_from: ## Introduction Understanding the basic structure of a mod's folder is an essential skill when -creating mods. +creating mods. In this chapter, you'll learn about how modding in Minetest works +and create your first mod. - [What are Games and Mods?](#what-are-games-and-mods) - [Where are mods stored?](#where-are-mods-stored) -- [Mod Directory](#mod-directory) -- [mod.conf](#modconf) - - [Dependencies](#dependencies) +- [Creating your first mod](#creating-your-first-mod) + - [Mod directory](#mod-directory) + - [mod.conf](#modconf) + - [init.lua](#initlua) + - [Summary](#summary) +- [Dependencies](#dependencies) - [Mod Packs](#mod-packs) -- [Example](#example) - - [Mod Folder](#mod-folder) - - [init.lua](#initlua) - - [mod.conf](#modconf-1) ## What are Games and Mods? @@ -53,7 +53,7 @@ and is applicable for both game developers and modders. Each mod has its own directory where its Lua code, textures, models, and -sounds are placed. Minetest checks in a number of different locations for +sounds are placed. Minetest checks in several different locations for mods. These locations are commonly called *mod load paths*. For a given world/save game, three mod locations are checked. @@ -78,54 +78,91 @@ mod will be loaded in place of the earlier mod. This means that you can override game mods by placing a mod with the same name in the global mod location. -## Mod Directory +## Creating your first mod -![Find the mod's directory]({{ page.root }}/static/folder_modfolder.jpg) +### Mod directory -A *mod name* is used to refer to a mod. Each mod should have a unique name. -Mod names can include letters, numbers, and underscores. A good name should -describe what the mod does, and the directory which contains the components of a mod -must have the same name as the mod name. -To find out if a mod name is available, try searching for it on +Go to the global mods directory (About > Open user data directory > mods) and +create a new folder called "mymod". `mymod` is the mod name. + +Each mod should have a unique *mod name*, a technical identifier (id) used to +refer to the mod. Mod names can include letters, numbers, and underscores. A +good name should describe what the mod does, and the directory that contains +the components of a mod must have the same name as the mod name. To find out if +a mod name is available, try searching for it on [content.minetest.net](https://content.minetest.net). mymod - ├── init.lua (required) - Runs when the game loads. - ├── mod.conf (recommended) - Contains description and dependencies. - ├── textures (optional) - │   └── ... any textures or images - ├── sounds (optional) - │   └── ... any sounds - └── ... any other files or directories + ├── textures + │   └── mymod_node.png files + ├── init.lua + └── mod.conf -Only the init.lua file is required in a mod for it to run on game load; +Mods only require an init.lua file; however, mod.conf is recommended and other components may be needed depending on the mod's functionality. -## mod.conf +### mod.conf + +Create a mod.conf file with the following content: + +``` +name = mymod +description = Adds foo, bar, and bo. +depends = default +``` This file is used for mod metadata including the mod's name, description, and other information. -For example: +### init.lua - name = mymod - description = Adds foo, bar, and bo. - depends = modone, modtwo +Create an init.lua file with the following content: -### Dependencies +```lua +print("This file will be run at load time!") + +minetest.register_node("mymod:node", { + description = "This is a node", + tiles = {"mymod_node.png"}, + groups = {cracky = 1} +}) + +minetest.register_craft({ + type = "shapeless", + output = "mymod:node 3", + recipe = { "default:dirt", "default:stone" }, +}) +``` + +The init.lua file is the entrypoint to a mod, and runs when the mod is loaded. + + +### Summary + + +This mod has the name "mymod". It has two text files: init.lua and mod.conf. The +script prints a message and then registers a node and a craft recipe – these +will be explained later on. There's a single dependency, the +[default mod](https://content.minetest.net/metapackages/default/), which is +usually found in Minetest Game. There is also a texture in textures/ for the +node. + + +## Dependencies A dependency occurs when a mod requires another mod to be loaded before itself. -One mod may require another mod's code, items, or other resources to be available -for it to use. +One mod may require another mod's code, items, or other resources to be +available for it to use. There are two types of dependencies: hard and optional dependencies. Both require the mod to be loaded first. If the mod being depended on isn't available, a hard dependency will cause the mod to fail to load, while an optional dependency might lead to fewer features being enabled. -An optional dependency is useful if you want to optionally support another mod; it can -enable extra content if the user wishes to use both the mods at the same time. +An optional dependency is useful if you want to optionally support another mod; +it can enable extra content if the user wishes to use both the mods at the same +time. Dependencies are specified in a comma-separated list in mod.conf. @@ -148,43 +185,3 @@ a player, but don't want to make them download each one individually. Please note that a modpack is not a *game*. Games have their own organisational structure which will be explained in the Games chapter. - -## Example - -Here is an example which puts all of this together: - -### Mod Folder - mymod - ├── textures - │   └── mymod_node.png files - ├── init.lua - └── mod.conf - -### init.lua -```lua -print("This file will be run at load time!") - -minetest.register_node("mymod:node", { - description = "This is a node", - tiles = {"mymod_node.png"}, - groups = {cracky = 1} -}) - -minetest.register_craft({ - type = "shapeless", - output = "mymod:node 3", - recipe = { "default:dirt", "default:stone" }, -}) -``` - -### mod.conf - name = mymod - descriptions = Adds a node - depends = default - -This mod has the name "mymod". It has two text files: init.lua and mod.conf. The -script prints a message and then registers a node and craft recipe – these will -be explained later on. There's a single dependency, the -[default mod](https://content.minetest.net/metapackages/default/), -which is usually found in Minetest Game. There is also a texture in textures/ -for the node. diff --git a/_en/basics/lua.md b/_en/basics/lua.md index e0188b6..71f17c4 100644 --- a/_en/basics/lua.md +++ b/_en/basics/lua.md @@ -9,21 +9,39 @@ redirect_from: /en/chapters/lua.html ## Introduction -In this chapter we'll talk about scripting in Lua, the tools required -to assist with this, and some techniques which you may find useful. +In this chapter, you'll learn about scripting in Lua, the tools required +to help with this, and some techniques that you may find useful. -- [Code Editors](#code-editors) -- [Coding in Lua](#coding-in-lua) - - [Program Flow](#program-flow) - - [Variable Types](#variable-types) - - [Arithmetic Operators](#arithmetic-operators) - - [Selection](#selection) - - [Logical Operators](#logical-operators) - [Programming](#programming) + - [Coding in Lua](#coding-in-lua) +- [Code Editors](#code-editors) - [Local and Global Scope](#local-and-global-scope) - [Locals should be used as much as possible](#locals-should-be-used-as-much-as-possible) - [Including other Lua Scripts](#including-other-lua-scripts) + +## Programming + +Programming is the action of taking a problem, such as sorting a list +of items, and turning it into steps that a computer can understand. +Teaching you the logical process of programming is beyond the scope of this book; +however, the following websites are quite useful in developing this: + +* [Codecademy](http://www.codecademy.com/) is one of the best resources for + learning to write code. It provides an interactive tutorial experience. +* [Scratch](https://scratch.mit.edu) is a good resource for starting from + absolute basics, and learning the problem-solving techniques required to program. + It's great for children and teenagers. +* [Programming with Mosh](https://www.youtube.com/user/programmingwithmosh) is + a good YouTube series to learn programming. + +### Coding in Lua + +It's also beyond the scope of this book to teach Lua coding. +The [Programming in Lua (PiL)](https://www.lua.org/pil/contents.html) book is an +excellent introduction to Lua programming. + + ## Code Editors A code editor with code highlighting is sufficient for writing scripts in Lua. @@ -54,156 +72,36 @@ Functions which come with Lua by default, such as `table.insert`, are also highl Commonly used editors which are well-suited for Lua include: -* [VSCode](https://code.visualstudio.com/) - +* [VSCode](https://code.visualstudio.com/): open source (as Code-OSS or VSCodium), popular, and has - [plugins for Minetest modding](https://marketplace.visualstudio.com/items?itemName=GreenXenith.minetest-tools). -* [Notepad++](http://notepad-plus-plus.org/) - Windows-only -* [Atom](http://atom.io/) + [plugins for Minetest](https://marketplace.visualstudio.com/items?itemName=GreenXenith.minetest-tools). +* [Notepad++](http://notepad-plus-plus.org/): simple, Windows-only Other suitable editors are also available. -## Coding in Lua - -### Program Flow - -Programs are a series of commands that run one after another. We call these -commands "statements." Program flow is how these statements are executed, and -different types of flow allow you to skip or jump over sets of commands. - -There are three main types of flow: - -* Sequence: runs one statement after another, with no skipping. -* Selection: skips over sequences depending on conditions. -* Iteration: repeats the same statements until a condition is met. - -So, what do statements in Lua look like? - -```lua -local a = 2 -- Set 'a' to 2 -local b = 2 -- Set 'b' to 2 -local result = a + b -- Set 'result' to a + b, which is 4 -a = a + 10 -print("Sum is "..result) -``` - -In this example, `a`, `b`, and `result` are *variables*. Local variables are -declared by using the `local` keyword, and then given an initial value. Local -will be discussed later, because it's part of a very important concept called -*scope*. - -The `=` sign means *assignment*, so `result = a + b` means set the value of -`result` to the value of `a + b`. Variable names can be longer than one -character, as seen with the `result` variable. It's also worth noting that, like -most languages, Lua is *case-sensitive*; `A` is a different variable to `a`. - - -### Variable Types - -A variable will be only one of the following types and can change type after an -assignment. -It's good practice to make sure a variable is only ever nil or a single non-nil type. - -| Type | Description | Example | -|----------|---------------------------------|----------------| -| Nil | Not initialised. The variable is empty, it has no value | `local A`, `D = nil` | -| Number | A whole or decimal number. | `local A = 4` | -| String | A piece of text. | `local D = "one two three"` | -| Boolean | True or False. | `local is_true = false`, `local E = (1 == 1)` | -| Table | Lists. | Explained below. | -| Function | Can run. May require inputs and may return a value. | `local result = func(1, 2, 3)` | - -### Arithmetic Operators - -Operators in Lua include: - -| Symbol | Purpose | Example | -|--------|----------------|---------------------------| -| A + B | Addition | 2 + 2 = 4 | -| A - B | Subtraction | 2 - 10 = -8 | -| A * B | Multiplication | 2 * 2 = 4 | -| A / B | Division | 100 / 50 = 2 | -| A ^ B | Powers | 2 ^ 2 = 22 = 4 | -| A .. B | Join strings | "foo" .. "bar" = "foobar" | - -Please note that this is not an exhaustive list; it doesn't contain every -possible operator. - -### Selection - -The most basic method of selection is the if statement. For example: - -```lua -local random_number = math.random(1, 100) -- Between 1 and 100. -if random_number > 50 then - print("Woohoo!") -else - print("No!") -end -``` - -This generates a random number between 1 and 100. It then prints "Woohoo!" if -that number is bigger than 50, and otherwise prints "No!". - - -### Logical Operators - -Logical operators in Lua include: - -| Symbol | Purpose | Example | -|---------|--------------------------------------|-------------------------------------------------------------| -| A == B | Equals | 1 == 1 (true), 1 == 2 (false) | -| A ~= B | Doesn't equal | 1 ~= 1 (false), 1 ~= 2 (true) | -| A > B | Greater than | 5 > 2 (true), 1 > 2 (false), 1 > 1 (false) | -| A < B | Less than | 1 < 3 (true), 3 < 1 (false), 1 < 1 (false) | -| A >= B | Greater than or equals | 5 >= 5 (true), 5 >= 3 (true), 5 >= 6 (false) | -| A <= B | Less than or equals | 3 <= 6 (true), 3 <= 3 (true) | -| A and B | And (both must be correct) | (2 > 1) and (1 == 1) (true), (2 > 3) and (1 == 1) (false) | -| A or B | either or. One or both must be true. | (2 > 1) or (1 == 2) (true), (2 > 4) or (1 == 3) (false) | -| not A | not true | not (1 == 2) (true), not (1 == 1) (false) | - -Please note that this doesn't contain every possible operator. - -It is also possible to combine operators. For example: - -```lua -if not A and B then - print("Yay!") -end -``` - -This prints "Yay!" if A is false and B is true. - -Logical and arithmetic operators work the same way; they both accept inputs and -return a value which can be stored. For example: - -```lua -local A = 5 -local is_equal = (A == 5) -if is_equal then - print("Is equal!") -end -``` - -## Programming - -Programming is the action of taking a problem, such as sorting a list -of items, and turning it into steps that a computer can understand. - -Teaching you the logical process of programming is beyond the scope of this book; -however, the following websites are quite useful in developing this: - -* [Codecademy](http://www.codecademy.com/) is one of the best resources for - learning to write code. It provides an interactive tutorial experience. -* [Scratch](https://scratch.mit.edu) is a good resource for starting from - absolute basics, and learning the problem-solving techniques required to program.\\ - Scratch is *designed to teach children* how to program and isn't a serious - programming language. ## Local and Global Scope Whether a variable is local or global determines where it can be written to or -read from. A local variable is only accessible from where it is defined. Here -are some examples: +read from. Global variables can be accessed from anywhere in the script file, +and from any other mod: + +```lua +function one() + foo = "bar" +end + +function two() + print(dump(foo)) -- Output: "bar" +end + +one() +two() +``` + +In constrast, a local variable is only accessible from where it is defined. +Lua defaults to variables being global, so you need to explicitly use the +`local` keyword: ```lua -- Accessible from within this script file @@ -220,21 +118,8 @@ function myfunc() end ``` -In contrast, global variables can be accessed from anywhere in the script file, and -from any other mod. -```lua -function one() - foo = "bar" -end - -function two() - print(dump(foo)) -- Output: "bar" -end - -one() -two() -``` +### Locals should be used as much as possible Local variables should be used whenever possible. Mods should only create one global at most, with the same name as the mod. Creating other globals is sloppy @@ -284,6 +169,9 @@ end mymod.foo("foobar") ``` +`function mymod.foo()` is equivalent to `mymod.foo = function()`, it's just a +nicer way to write it. + ## Including other Lua Scripts The recommended way to include other Lua scripts in a mod is to use *dofile*. @@ -296,11 +184,13 @@ A script can return a value, which is useful for sharing private locals: ```lua -- script.lua -return "Hello world!" +local module = {} +module.message = "Hello World!" +return module -- init.lua local ret = dofile(minetest.get_modpath("modname") .. "/script.lua") -print(ret) -- Hello world! +print(ret.message) -- Hello world! ``` [Later chapters](../quality/clean_arch.html) will discuss how best to split up diff --git a/_en/items/callbacks.md b/_en/items/callbacks.md new file mode 100644 index 0000000..2c03bce --- /dev/null +++ b/_en/items/callbacks.md @@ -0,0 +1,206 @@ +--- +title: Node and Item Callbacks +layout: default +root: ../.. +idx: 2.15 +description: Learn about callbacks, actions, and events, including on_use, on_punch, on_place, on_rightclick +--- + +## Introduction + +Minetest heavily uses a callback-based modding design. A callback is a function +that you give to an API and is called when an event happens. For example, you +can provide an `on_punch` function in a node definition to be called when a player +punches a node. There are also global callbacks like +`minetest.register_on_punchnode` to receive events for all nodes. + +- [Item Callbacks](#item-callbacks) + - [on_use](#on_use) + - [on_place and on_secondary_use](#on_place-and-on_secondary_use) + - [on_drop](#on_drop) + - [after_use](#after_use) +- [item_place vs place_item](#item_place-vs-place_item) +- [Node Callbacks](#node-callbacks) + - [Right-clicking and placing a node](#right-clicking-and-placing-a-node) + - [Punching and digging](#punching-and-digging) + - [...and more!](#and-more) + + +## Item Callbacks + +When a player has a node, craftitem, or tool in their inventory, they may trigger +certain events: + +| Callback | Default binding | Default value | +|------------------|---------------------------|----------------------------------------------| +| on_use | left-click | nil | +| on_place | right-click on a node | `minetest.item_place` | +| on_secondary_use | right-click not on a node | `minetest.item_secondary_use` (does nothing) | +| on_drop | Q | `minetest.item_drop` | +| after_use | digging a node | nil | + + +### on_use + +Having a use callback prevents the item from being used to dig nodes. One common +use of the use callback is for food: + +```lua +minetest.register_craftitem("mymod:mudpie", { + description = "Alien Mud Pie", + inventory_image = "myfood_mudpie.png", + on_use = minetest.item_eat(20), +}) +``` + +The number supplied to the minetest.item_eat function is the number of hit +points healed when this food is consumed. Each heart icon the player has is +worth two hitpoints. A player can usually have up to 10 hearts, which is equal +to 20 hitpoints. + +minetest.item_eat() is a function that returns a function, setting it as the +on_use callback. This means the code above is equivalent to this: + +```lua +minetest.register_craftitem("mymod:mudpie", { + description = "Alien Mud Pie", + inventory_image = "myfood_mudpie.png", + on_use = function(...) + return minetest.do_item_eat(20, nil, ...) + end, +}) +``` + +By understanding how item_eat works by simply returning a function, it's +possible to modify it to do more complex behaviour like playing a custom sound. + + +### on_place and on_secondary_use + +The difference between `on_place` and `on_secondary_use` is that `on_place` is +called when the player is pointing at a node and `on_secondary_use` when the +player isn't. + +Both callbacks are called for all types of items. `on_place` defaults to the +`minetest.item_place` function, which handles calling the `on_rightclick` +callback of the pointed node or placing the wielded item if it is a node. + + +### on_drop + +on_drop is called when the player requests to drop an item, for example using +the drop key (Q) or dragging it outside of the inventory. It defaults to the +`minetest.item_drop` function, which will handle dropping the item. + + +### after_use + +`after_use` is called when digging a node and allows you to customise how wear +is applied to a tool. If after_use doesn't exist, then it is the same as: + +```lua +after_use = function(itemstack, user, node, digparams) + itemstack:add_wear(digparams.wear) + return itemstack +end +``` + + +## item_place vs place_item + +Minetest's API includes many different built-in callback implementations for you +to use. These callbacks are named with the item type first, for example, +`minetest.item_place` and `minetest.node_dig`. Some callback implementations are +used directly whereas some are functions that return the callback: + +```lua +minetest.register_item("mymod:example", { + on_place = minetest.item_place, + on_use = minetest.item_eat(10), +}) +``` + +Minetest's API also includes built-in functions that _do_ something. These are +often named in a confusingly similar way to built-in callback implementations +but have the verb first. Examples include `minetest.place_item` and +`minetest.dig_node` - these functions allow you to dig and place nodes with a +similar effect to players. + + +## Node Callbacks + +When a node is in an inventory, it uses Item Callbacks, as discussed above. When +a node is placed in the world, it uses Node Callbacks. There are quite a lot of +node callbacks, too many to discuss in this book. However, quite a few of them +will be talked about later in the book. + +Several of the callbacks are related to node operations such as placing and +removing from the world. It's important to note that node operation callbacks +like these aren't called from bulk changes - those that set a large number of +nodes at once - for performance reasons. Therefore, you can't rely on these +callbacks to always be called. + + +### Right-clicking and placing a node + +When the user right-clicks with an item whilst pointing at a node, the item's +`on_place` callback is called. By default, this is set to `minetest.item_place`. +If the pointed node has an `on_rightclick` callback and sneak (shift) is held, +then the `on_rightclick` callback is called. Otherwise, `minetest.item_place` +will place the node. + +Placing a node will call both `on_construct` and `after_place_node`. +`on_construct` is called by any node set event that wasn't in bulk and is just +given the node's position and value .`after_place_node` is only called by node +place, and so has more information - such as the placer and itemstack. + +It's important to note that players aren't the only objects that can place +nodes; it's common for mobs and mods to place nodes. To account for this, +`placer` could be a player, entity, or nil. + +```lua +minetest.register_node("mymod:mynode", { + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + if clicker:is_player() then + minetest.chat_send_player(clicker:get_player_name(), "Hello world!") + end + end, + on_construct = function(pos, node) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "My node!") + end, + after_place_node = function(pos, placer, itemstack, pointed_thing) + -- Make sure to check placer + if placer and placer:is_player() then + local meta = minetest.get_meta(pos) + meta:set_string("owner", placer:get_player_name()) + end + end, +}) +``` + +### Punching and digging + +Punching is when the player left-clicks for a short period. If the wielded item +has an `on_use` callback, this will be called. Otherwise, the `on_punch` +callback on the pointed node will be called. + +When the player attempts to dig a node, the `on_dig` callback on the node will be called. +This defaults to `minetest.node_dig`, which will check for area protection, wear +out the tool, remove the node, and run the `after_dig_node` callback. + + +```lua +minetest.register_node("mymod:mynode", { + on_punch = function(pos, node, puncher, pointed_thing) + if puncher:is_player() then + minetest.chat_send_player(puncher:get_player_name(), "Ow!") + end + end, +}) +``` + +### ...and more! + +Check out Minetest's Lua API reference for a list of all node callbacks, and +more information on the callbacks above. diff --git a/_en/items/creating_textures.md b/_en/items/creating_textures.md index dd5cc95..190a89b 100644 --- a/_en/items/creating_textures.md +++ b/_en/items/creating_textures.md @@ -25,8 +25,10 @@ available, which cover pixel art in much more detail. - [Using the Pencil](#using-the-pencil) - [Tiling](#tiling) - [Transparency](#transparency) + - [Color Palettes](#color-palettes) - [Editors](#editors) - [MS Paint](#ms-paint) + - [Aseprite / LibreSprite](#aseprite--libresprite) - [GIMP](#gimp) ## Techniques @@ -59,6 +61,13 @@ and some nodes, such as glass. Not all editors support transparency, so make sure you choose an editor which is suitable for the textures you wish to create. +### Color Palettes + +Using a consistent color palette is an easy way to make your art look a lot +better. It's a good idea to use one with a limited number of colors, perhaps 32 +at most. Premade palettes can be found at +[lospec.com](https://lospec.com/palette-list). + ## Editors ### MS Paint @@ -69,16 +78,21 @@ This usually won't matter when making textures for the sides of nodes, but if you need transparency in your textures you should choose a different editor. +### Aseprite / LibreSprite + +[Aseprite](https://www.aseprite.org/) is a proprietary pixel art editor. +It contains a lot of useful features by default such as color palettes and +animation tools. + +[LibreSprite](https://libresprite.github.io/) is an open-source fork of Aseprite +from before it went proprietary. + ### GIMP GIMP is commonly used in the Minetest community. It has quite a high learning curve because many of its features are not immediately obvious. -When using GIMP, the pencil tool can be selected from the Toolbox: - -
- Pencil in GIMP -
- -It's also advisable to select the Hard edge checkbox for the eraser tool. +When using GIMP, make sure to use the Pencil tool with the Pixel brush and a +size of 1. It's also advisable to select the "Hard edge" checkbox for the Eraser +tool. diff --git a/_en/items/inventories.md b/_en/items/inventories.md index 198c58e..d903e57 100644 --- a/_en/items/inventories.md +++ b/_en/items/inventories.md @@ -19,6 +19,9 @@ that be a player inventory, a node inventory, or a detached inventory. - [What are ItemStacks and Inventories?](#what-are-itemstacks-and-inventories) - [ItemStacks](#itemstacks) - [Inventory Locations](#inventory-locations) + - [Node Inventories](#node-inventories) + - [Player Inventories](#player-inventories) + - [Detached Inventories](#detached-inventories) - [Lists](#lists) - [Size and Width](#size-and-width) - [Checking Contents](#checking-contents) @@ -33,21 +36,21 @@ that be a player inventory, a node inventory, or a detached inventory. An ItemStack is the data behind a single cell in an inventory. -An *inventory* is a collection of *inventory lists*, each of which -is a 2D grid of ItemStacks. -Inventory lists are simply called *lists* in the context -of inventories. -The point of an inventory is to allow multiple grids when Players -and Nodes only have at most one inventory in them. +An *inventory* is a collection of *inventory lists*, each of which is a 2D grid +of ItemStacks. Inventory lists are referred to as *lists* in the context of +inventories. + +Players and nodes only have a single inventory; lists enable you to have +multiple grids within that inventory. By default, the player has the "main" list +for the bulk of its inventory and a few lists for the crafting system. ## ItemStacks -ItemStacks have four components to them: name, count, wear and metadata. +ItemStacks have four components to them: `name`, `count`, `wear`, and metadata. The item name may be the item name of a registered item, an alias, or an unknown -item name. -Unknown items are common when users uninstall mods, or when mods remove items without -precautions, such as registering aliases. +item name. Unknown items are common when users uninstall mods, or when mods +remove items without precautions, such as registering aliases. ```lua print(stack:get_name()) @@ -58,19 +61,14 @@ if not stack:is_known() then end ``` -The count will always be 0 or greater. -Through normal gameplay, the count should be no more than the maximum stack size -of the item - `stack_max`. -However, admin commands and buggy mods may result in stacks exceeding the maximum -size. +The count will always be 0 or greater. Through normal gameplay, the count should +be no more than the maximum stack size of the item - `stack_max`. However, admin +commands and buggy mods may result in stacks exceeding the maximum size. ```lua print(stack:get_stack_max()) ``` - - - An ItemStack can be empty, in which case the count will be 0. ```lua @@ -78,7 +76,7 @@ print(stack:get_count()) stack:set_count(10) ``` -ItemStacks can be constructed in multiple ways using the ItemStack function. +ItemStacks can be constructed in multiple ways using the ItemStack function: ```lua ItemStack() -- name="", count=0 @@ -87,24 +85,30 @@ ItemStack("default:stone 30") ItemStack({ name = "default:wood", count = 10 }) ``` -Item metadata is an unlimited key-value store for data about the item. -Key-value means that you use a name (called the key) to access the data (called the value). -Some keys have special meaning, such as `description` which is used to have a per-stack -item description. -This will be covered in more detail in the Metadata and Storage chapter. +Item metadata is an unlimited key-value store for data about the item. Key-value +means that you use a name (called the key) to access the data (called the +value). Some keys have special meaning, such as `description` which is used to +have a per-stack item description. This will be covered in more detail in the +[Storage and Metadata](../map/storage.html) chapter. ## Inventory Locations -An Inventory Location is where and how the inventory is stored. -There are three types of inventory location: player, node, and detached. -An inventory is directly tied to one and only one location - updating the inventory -will cause it to update immediately. +An Inventory Location is where and how the inventory is stored. There are three +types of inventory location: player, node, and detached. An inventory is +directly tied to one and only one location - updating the inventory will cause +it to update immediately. -Node inventories are related to the position of a specific node, such as a chest. -The node must be loaded because it is stored in [node metadata](../map/storage.html#metadata). +### Node Inventories + +Node inventories are related to the position of a specific node, such as a +chest. The node must be loaded because it is stored in +[node metadata](../map/storage.html#metadata). ```lua -local inv = minetest.get_inventory({ type="node", pos={x=1, y=2, z=3} }) +on_punch = function(pos, node) + local inv = minetest.get_inventory({ type="node", pos=pos }) + -- now use the inventory +end, ``` The above obtains an *inventory reference*, commonly referred to as *InvRef*. @@ -118,6 +122,8 @@ The location of an inventory reference can be found like so: local location = inv:get_location() ``` +### Player Inventories + Player inventories can be obtained similarly or using a player reference. The player must be online to access their inventory. @@ -127,8 +133,10 @@ local inv = minetest.get_inventory({ type="player", name="player1" }) local inv = player:get_inventory() ``` -A detached inventory is one which is independent of players or nodes. -Detached inventories also don't save over a restart. +### Detached Inventories + +A detached inventory is one that is independent of players or nodes. Detached +inventories also don't save over a restart. ```lua local inv = minetest.get_inventory({ @@ -142,10 +150,9 @@ before accessing it: minetest.create_detached_inventory("inventory_name") ``` -The create_detached_inventory function accepts 3 arguments, where only the first - the inventory name - -is required. -The second argument takes a table of callbacks, which can be used to control how -players interact with the inventory: +The `create_detached_inventory` function accepts 3 arguments, where only the +first - the inventory name - is required. The second argument takes a table of +callbacks, which can be used to control how players interact with the inventory: ```lua -- Input only detached inventory @@ -177,9 +184,10 @@ On the contrary, action callbacks - starting with `on_` - don't have a return va ## Lists -Inventory Lists are a concept used to allow multiple grids to be stored inside a single location. -This is especially useful for the player as there are a number of common lists -which all games have, such as the *main* inventory and *craft* slots. +Inventory Lists are a concept used to allow multiple grids to be stored inside a +single location. This is especially useful for the player as there are several +common lists that all games have, such as the *main* inventory and *craft* +slots. ### Size and Width diff --git a/_en/items/nodes_items_crafting.md b/_en/items/nodes_items_crafting.md index 1955783..452fc53 100644 --- a/_en/items/nodes_items_crafting.md +++ b/_en/items/nodes_items_crafting.md @@ -18,8 +18,6 @@ basic requirements for many mods. - [Item Aliases](#item-aliases) - [Textures](#textures) - [Registering a basic node](#registering-a-basic-node) -- [Actions and Callbacks](#actions-and-callbacks) - - [on_use](#onuse) - [Crafting](#crafting) - [Shaped](#shaped) - [Shapeless](#shapeless) @@ -29,25 +27,24 @@ basic requirements for many mods. ## What are Nodes and Items? -Nodes, craftitems, and tools are all Items. -An item is something that could be found in an inventory - -even though it may not be possible through normal gameplay. +Nodes, craftitems, and tools are all Items. An item is something that could be +found in an inventory - even if it isn't possible through normal gameplay. -A node is an item which can be placed or be found in the world. -Every position in the world must be occupied with one and only one node - -seemingly blank positions are usually air nodes. +A node is an item that can be placed or be found in the world. Every position +in the world must be occupied with one and only one node - seemingly blank +positions are usually air nodes. A craftitem can't be placed and is only found in inventories or as a dropped item in the world. -A tool has the ability to wear and typically has non-default digging capabilities. -In the future, it's likely that craftitems and tools will merge into one type of -item, as the distinction between them is rather artificial. +A tool has the ability to wear and typically has non-default digging +capabilities. In the future, it's likely that craftitems and tools will merge +into one type of item, as the distinction between them is rather artificial. ## Registering Items Item definitions consist of an *item name* and a *definition table*. -The definition table contains attributes which affect the behaviour of the item. +The definition table contains attributes that affect the behaviour of the item. ```lua minetest.register_craftitem("modname:itemname", { @@ -63,25 +60,26 @@ following format: modname:itemname -The modname is the name of the mod in which the item is registered, and the -item name is the name of the item itself. -The item name should be relevant to what the item is and can't already be registered. +The modname is the name of the mod in which the item is registered, and the item +name is the name of the item itself. The item name should be relevant to what +the item is and can't already be registered. + +Both `modname` and `itemname` should only contain lowercase letters, numbers, +and underscores. ### Item Aliases -Items can also have *aliases* pointing to their name. -An *alias* is a pseudo-item name which results in the engine treating any -occurrences of the alias as if it were the item name. -There are two main common uses of this: +Items can also have *aliases* pointing to their name. An *alias* is a +pseudo-item name that results in the engine treating any occurrences of the +alias as if it were the item name. There are two main common uses of this: * Renaming removed items to something else. There may be unknown nodes in the world and in inventories if an item is removed from a mod without any corrective code. * Adding a shortcut. `/giveme dirt` is easier than `/giveme default:dirt`. -Registering an alias is pretty simple. -A good way to remember the order of the arguments is `from → to` where -*from* is the alias and *to* is the target. +Registering an alias is pretty simple. A good way to remember the order of the +arguments is `from → to` where *from* is the alias and *to* is the target. ```lua minetest.register_alias("dirt", "default:dirt") @@ -103,14 +101,16 @@ JPEG textures are supported, but they do not support transparency and are genera bad quality at low resolutions. It is often better to use the PNG format. -Textures in Minetest are usually 16 by 16 pixels. -They can be any resolution, but it is recommended that they are in the order of 2, -for example, 16, 32, 64, or 128. -This is because other resolutions may not be supported correctly on older devices, -resulting in decreased performance. +Textures in Minetest are usually 16 by 16 pixels. They can be any resolution, +but it is recommended that they are in the order of 2, for example, 16, 32, 64, +or 128. This is because other resolutions may not be supported correctly on +older devices, especially phones, resulting in degraded performance. ## Registering a basic node +Registering nodes is similar to registering items, just with a different +function: + ```lua minetest.register_node("mymod:diamond", { description = "Alien Diamond", @@ -120,6 +120,9 @@ minetest.register_node("mymod:diamond", { }) ``` +Node definitions can contain any property in an item definition, and also +contain additional properties specific to nodes. + The `tiles` property is a table of texture names the node will use. When there is only one texture, this texture is used on every side. To give a different texture per-side, supply the names of 6 textures in this order: @@ -128,7 +131,7 @@ To give a different texture per-side, supply the names of 6 textures in this ord (+Y, -Y, +X, -X, +Z, -Z) Remember that +Y is upwards in Minetest, as is the convention with -3D computer graphics. +most 3D computer games. ```lua minetest.register_node("mymod:diamond", { @@ -152,49 +155,6 @@ The `is_ground_content` attribute allows caves to be generated over the stone. This is essential for any node which may be placed during map generation underground. Caves are cut out of the world after all the other nodes in an area have generated. -## Actions and Callbacks - -Minetest heavily uses a callback-based modding design. -Callbacks can be placed in the item definition table to allow response to various -different user events. - -### on_use - -By default, the use callback is triggered when a player left-clicks with an item. -Having a use callback prevents the item being used to dig nodes. -One common use of the use callback is for food: - -```lua -minetest.register_craftitem("mymod:mudpie", { - description = "Alien Mud Pie", - inventory_image = "myfood_mudpie.png", - on_use = minetest.item_eat(20), -}) -``` - -The number supplied to the minetest.item_eat function is the number of hit points -healed when this food is consumed. -Each heart icon the player has is worth two hitpoints. -A player can usually have up to 10 hearts, which is equal to 20 hitpoints. -Hitpoints don't have to be integers (whole numbers); they can be decimals. - -minetest.item_eat() is a function which returns a function, setting it -as the on_use callback. -This means the code above is roughly similar to this: - -```lua -minetest.register_craftitem("mymod:mudpie", { - description = "Alien Mud Pie", - inventory_image = "myfood_mudpie.png", - on_use = function(...) - return minetest.do_item_eat(20, nil, ...) - end, -}) -``` - -By understanding how item_eat works by simply returning a function, it's -possible to modify it to do more complex behaviour such as play a custom sound. - ## Crafting There are several types of crafting recipe available, indicated by the `type` @@ -327,6 +287,7 @@ minetest.register_craft({ }) ``` + ## Tools, Capabilities, and Dig Types Dig types are groups which are used to define how strong a node is when dug diff --git a/_en/map/environment.md b/_en/map/environment.md index fe7cf6d..cd26fc2 100644 --- a/_en/map/environment.md +++ b/_en/map/environment.md @@ -202,7 +202,7 @@ local function emerge_callback(pos, action, -- Send progress message if context.total_blocks == context.loaded_blocks then minetest.chat_send_all("Finished loading blocks!") - end + else local perc = 100 * context.loaded_blocks / context.total_blocks local msg = string.format("Loading blocks %d/%d (%.2f%%)", context.loaded_blocks, context.total_blocks, perc) diff --git a/_en/map/timers.md b/_en/map/timers.md index c0b3143..3f2e1f4 100644 --- a/_en/map/timers.md +++ b/_en/map/timers.md @@ -80,7 +80,7 @@ minetest.register_abm({ nodenames = {"default:dirt_with_grass"}, neighbors = {"default:water_source", "default:water_flowing"}, interval = 10.0, -- Run every 10 seconds - chance = 50, -- Select every 1 in 50 nodes + chance = 50, -- One node has a chance of 1 in 50 to get selected action = function(pos, node, active_object_count, active_object_count_wider) local pos = {x = pos.x, y = pos.y + 1, z = pos.z} diff --git a/_en/players/chat.md b/_en/players/chat.md index 2f2f9e6..21f8175 100644 --- a/_en/players/chat.md +++ b/_en/players/chat.md @@ -8,19 +8,21 @@ redirect_from: /en/chapters/chat.html cmd_online: level: warning title: Offline players can run commands - message:

A player name is passed instead of a player object because mods - can run commands on behalf of offline players. For example, the IRC - bridge allows players to run commands without joining the game.

+ message: | + A player name is passed instead of a player object because mods + can run commands on behalf of offline players. For example, the IRC + bridge allows players to run commands without joining the game. -

So make sure that you don't assume that the player is online. - You can check by seeing if

minetest.get_player_by_name
returns a player.

+ So make sure that you don't assume that the player is online. + You can check by seeing if `minetest.get_player_by_name` returns a player. cb_cmdsprivs: level: warning title: Privileges and Chat Commands - message: The shout privilege isn't needed for a player to trigger this callback. - This is because chat commands are implemented in Lua, and are just - chat messages that begin with a /. + message: | + The shout privilege isn't needed for a player to trigger this callback. + This is because chat commands are implemented in Lua, and are just + chat messages that begin with a /. --- @@ -29,15 +31,20 @@ cb_cmdsprivs: Mods can interact with player chat, including sending messages, intercepting messages, and registering chat commands. -- [Sending Messages to All Players](#sending-messages-to-all-players) -- [Sending Messages to Specific Players](#sending-messages-to-specific-players) +- [Sending Messages](#sending-messages) + - [To All Players](#to-all-players) + - [To Specific Players](#to-specific-players) - [Chat Commands](#chat-commands) -- [Complex Subcommands](#complex-subcommands) + - [Accepting Multiple Arguments](#accepting-multiple-arguments) + - [Using string.split](#using-stringsplit) + - [Using Lua patterns](#using-lua-patterns) - [Intercepting Messages](#intercepting-messages) -## Sending Messages to All Players +## Sending Messages -To send a message to every player in the game, call the chat_send_all function. +### To All Players + +To send a message to every player in the game, call the `chat_send_all` function. ```lua minetest.chat_send_all("This is a chat message to all players") @@ -51,9 +58,9 @@ Here is an example of how this appears in-game: The message appears on a separate line to distinguish it from in-game player chat. -## Sending Messages to Specific Players +### To Specific Players -To send a message to a specific player, call the chat_send_player function: +To send a message to a specific player, call the `chat_send_player` function: ```lua minetest.chat_send_player("player1", "This is a chat message for player1") @@ -80,26 +87,57 @@ minetest.register_chatcommand("foo", { In the above snippet, `interact` is listed as a required [privilege](privileges.html) meaning that only players with the `interact` privilege can run the command. +`param` is a string containing everything a player writes after the chatcommand +name. For example, if a user types `/grantme one,two,three` then `param` will be +`one,two,three`. + Chat commands can return up to two values, the first being a Boolean indicating success, and the second being a message to send to the user. {% include notice.html notice=page.cmd_online %} -## Complex Subcommands +### Accepting Multiple Arguments -It is often required to make complex chat commands, such as: + -* `/msg ` -* `/team join ` -* `/team leave ` -* `/team list` +`param` gives you all the arguments to a chat command in a single string. It's +common for chat commands to need to extract multiple arguments. There are two +ways of doing this, either using Minetest's string split or Lua patterns. -This is usually done using [Lua patterns](https://www.lua.org/pil/20.2.html). -Patterns are a way of extracting stuff from text using rules. +#### Using string.split + +A string can be split up into words using `string.split(" ")`: ```lua -local to, msg = string.match(param, "^([%a%d_-]+) (*+)$") +local parts = param:split(" ") +local cmd = parts[1] + +if cmd == "join" then + local team_name = parts[2] + team.join(name, team_name) + return true, "Joined team!" +elseif cmd == "max_users" then + local team_name = parts[2] + local max_users = tonumber(parts[3]) + if team_name and max_users then + return true, "Set max users of team " .. team_name .. " to " .. max_users + else + return false, "Usage: /team max_users " + end +else + return false, "Command needed" +end +``` + +#### Using Lua patterns + +[Lua patterns](https://www.lua.org/pil/20.2.html) are a way of extracting stuff +from text using rules. They're best suited for when there are arguments that can +contain spaces or more control is needed on how parameters are captured. + +```lua +local to, msg = param:match("^([%a%d_-]+) (*+)$") ``` The above code implements `/msg `. Let's go through left to right: @@ -123,13 +161,6 @@ Patterns would probably be the [lua-users.org tutorial](http://lua-users.org/wiki/PatternsTutorial) or the [PIL documentation](https://www.lua.org/pil/20.2.html). -

- There is also a library written by the author of this book which can be used - to make complex chat commands without patterns called - Chat Command Builder. -

- - ## Intercepting Messages To intercept a message, use register_on_chat_message: diff --git a/_en/players/formspecs.md b/_en/players/formspecs.md index a3061d8..c581a23 100644 --- a/_en/players/formspecs.md +++ b/_en/players/formspecs.md @@ -35,16 +35,16 @@ unexpected windows tend to disrupt gameplay. - [Real or Legacy Coordinates](#real-or-legacy-coordinates) - [Anatomy of a Formspec](#anatomy-of-a-formspec) - - [Elements](#elements) - - [Header](#header) + - [Elements](#elements) + - [Header](#header) - [Guessing Game](#guessing-game) - - [Padding and Spacing](#padding-and-spacing) - - [Receiving Formspec Submissions](#receiving-formspec-submissions) - - [Contexts](#contexts) + - [Padding and Spacing](#padding-and-spacing) + - [Receiving Formspec Submissions](#receiving-formspec-submissions) + - [Contexts](#contexts) - [Formspec Sources](#formspec-sources) - - [Node Meta Formspecs](#node-meta-formspecs) - - [Player Inventory Formspecs](#player-inventory-formspecs) - - [Your Turn](#your-turn) + - [Node Meta Formspecs](#node-meta-formspecs) + - [Player Inventory Formspecs](#player-inventory-formspecs) + - [Your Turn](#your-turn) ## Real or Legacy Coordinates @@ -100,7 +100,7 @@ settings of the client. Here's a formspec which is `2,2` in size: Notice how we explicitly defined the formspec language version. Without this, the legacy system will instead be used instead - which will -prevent the use of consistent element positioning and other new feautures. +prevent the use of consistent element positioning and other new features. The position and anchor elements are used to place the formspec on the screen. The position sets where on the screen the formspec will be, and defaults to @@ -225,9 +225,9 @@ may need to work on multiple forms, or on all forms. The `fields` parameter to the function is a table of the values submitted by the user, indexed by strings. Named elements will appear in the field under their own -name, but only if they are relevent for the event that caused the submission. -For example, a button element will only appear in fields if that particular button -was pressed. +name, depending on the event. Some elements will only be submitted if they caused +the event, such as buttons, and some elements will always appear in submissions, +such as fields. {% include notice.html notice=page.submit_vuln %} @@ -365,9 +365,9 @@ The player inventory formspec is the one shown when the player presses i. The global callback is used to receive events from this formspec, and the formname is `""`. -There are a number of different mods which allow multiple mods to customise -the player inventory. The officially recommended mod is -[Simple Fast Inventory (sfinv)](sfinv.html), and is included in Minetest Game. +There are a number of different mods which allow multiple mods to customise the +player inventory. Minetest Game uses +[SFINV](https://github.com/rubenwardy/sfinv/blob/master/Tutorial.md). ### Your Turn diff --git a/_en/players/sfinv.md b/_en/players/sfinv.md index 19c908e..6671103 100644 --- a/_en/players/sfinv.md +++ b/_en/players/sfinv.md @@ -1,242 +1,5 @@ --- -title: "SFINV: Inventory Formspec" -layout: default -root: ../.. -idx: 4.7 +sitemap: false redirect_from: /en/chapters/sfinv.html +redirect_to: "https://github.com/rubenwardy/sfinv/blob/master/Tutorial.md" --- - -## Introduction - -Simple Fast Inventory (SFINV) is a mod found in Minetest Game that is used to -create the player's inventory [formspec](formspecs.html). SFINV comes with -an API that allows you to add and otherwise manage the pages shown. - -Whilst SFINV by default shows pages as tabs, pages are called pages -because it is entirely possible that a mod or game decides to show them in -some other format instead. -For example, multiple pages could be shown in one formspec. - -- [Registering a Page](#registering-a-page) -- [Receiving events](#receiving-events) -- [Conditionally showing to players](#conditionally-showing-to-players) -- [on_enter and on_leave callbacks](#onenter-and-onleave-callbacks) -- [Adding to an existing page](#adding-to-an-existing-page) - -## Registering a Page - -SFINV provides the aptly named `sfinv.register_page` function to create pages. -Simply call the function with the page's name and its definition: - -```lua -sfinv.register_page("mymod:hello", { - title = "Hello!", - get = function(self, player, context) - return sfinv.make_formspec(player, context, - "label[0.1,0.1;Hello world!]", true) - end -}) -``` - -The `make_formspec` function surrounds your formspec with SFINV's formspec code. -The fourth parameter, currently set as `true`, determines whether the -player's inventory is shown. - -Let's make things more exciting; here is the code for the formspec generation -part of a player admin tab. This tab will allow admins to kick or ban players by -selecting them in a list and clicking a button. - -```lua -sfinv.register_page("myadmin:myadmin", { - title = "Tab", - get = function(self, player, context) - local players = {} - context.myadmin_players = players - - -- Using an array to build a formspec is considerably faster - local formspec = { - "textlist[0.1,0.1;7.8,3;playerlist;" - } - - -- Add all players to the text list, and to the players list - local is_first = true - for _ , player in pairs(minetest.get_connected_players()) do - local player_name = player:get_player_name() - players[#players + 1] = player_name - if not is_first then - formspec[#formspec + 1] = "," - end - formspec[#formspec + 1] = - minetest.formspec_escape(player_name) - is_first = false - end - formspec[#formspec + 1] = "]" - - -- Add buttons - formspec[#formspec + 1] = "button[0.1,3.3;2,1;kick;Kick]" - formspec[#formspec + 1] = "button[2.1,3.3;2,1;ban;Kick + Ban]" - - -- Wrap the formspec in sfinv's layout - -- (ie: adds the tabs and background) - return sfinv.make_formspec(player, context, - table.concat(formspec, ""), false) - end, -}) -``` - -There's nothing new about the above code; all the concepts are -covered above and in previous chapters. - -
- Player Admin Page -
- -## Receiving events - -You can receive formspec events by adding a `on_player_receive_fields` function -to a sfinv definition. - -```lua -on_player_receive_fields = function(self, player, context, fields) - -- TODO: implement this -end, -``` - -`on_player_receive_fields` works the same as -`minetest.register_on_player_receive_fields`, except that `context` is -given instead of `formname`. -Please note that SFINV will consume events relevant to itself, such as -navigation tab events, so you won't receive them in this callback. - -Now let's implement the `on_player_receive_fields` for our admin mod: - -```lua -on_player_receive_fields = function(self, player, context, fields) - -- text list event, check event type and set index if selection changed - if fields.playerlist then - local event = minetest.explode_textlist_event(fields.playerlist) - if event.type == "CHG" then - context.myadmin_selected_idx = event.index - end - - -- Kick button was pressed - elseif fields.kick then - local player_name = - context.myadmin_players[context.myadmin_selected_idx] - if player_name then - minetest.chat_send_player(player:get_player_name(), - "Kicked " .. player_name) - minetest.kick_player(player_name) - end - - -- Ban button was pressed - elseif fields.ban then - local player_name = - context.myadmin_players[context.myadmin_selected_idx] - if player_name then - minetest.chat_send_player(player:get_player_name(), - "Banned " .. player_name) - minetest.ban_player(player_name) - minetest.kick_player(player_name, "Banned") - end - end -end, -``` - -There's a rather large problem with this, however. Anyone can kick or ban players! You -need a way to only show this to players with the kick or ban privileges. -Luckily SFINV allows you to do this! - -## Conditionally showing to players - -You can add an `is_in_nav` function to your page's definition if you'd like to -control when the page is shown: - -```lua -is_in_nav = function(self, player, context) - local privs = minetest.get_player_privs(player:get_player_name()) - return privs.kick or privs.ban -end, -``` - -If you only need to check one priv or want to perform an 'and', you should use -`minetest.check_player_privs()` instead of `get_player_privs`. - -Note that the `is_in_nav` is only called when the player's inventory formspec is -generated. This happens when a player joins the game, switches tabs, or a mod -requests for SFINV to regenerate. - -This means that you need to manually request that SFINV regenerates the inventory -formspec on any events that may change `is_in_nav`'s result. In our case, -we need to do that whenever kick or ban is granted or revoked to a player: - -```lua -local function on_grant_revoke(grantee, granter, priv) - if priv ~= "kick" and priv ~= "ban" then - return - end - - local player = minetest.get_player_by_name(grantee) - if not player then - return - end - - local context = sfinv.get_or_create_context(player) - if context.page ~= "myadmin:myadmin" then - return - end - - sfinv.set_player_inventory_formspec(player, context) -end - -minetest.register_on_priv_grant(on_grant_revoke) -minetest.register_on_priv_revoke(on_grant_revoke) -``` - -## on_enter and on_leave callbacks - -A player *enters* a tab when the tab is selected and *leaves* a -tab when another tab is about to be selected. -It's possible for multiple pages to be selected if a custom theme is -used. - -Note that these events may not be triggered by the player. -The player may not even have the formspec open at that time. -For example, on_enter is called for the home page when a player -joins the game even before they open their inventory. - -It's not possible to cancel a page change, as that would potentially -confuse the player. - -```lua -on_enter = function(self, player, context) - -end, - -on_leave = function(self, player, context) - -end, -``` - -## Adding to an existing page - -To add content to an existing page, you will need to override the page -and modify the returned formspec. - -```lua -local old_func = sfinv.registered_pages["sfinv:crafting"].get -sfinv.override_page("sfinv:crafting", { - get = function(self, player, context, ...) - local ret = old_func(self, player, context, ...) - - if type(ret) == "table" then - ret.formspec = ret.formspec .. "label[0,0;Hello]" - else - -- Backwards compatibility - ret = ret .. "label[0,0;Hello]" - end - - return ret - end -}) -``` diff --git a/_en/quality/common_mistakes.md b/_en/quality/common_mistakes.md index e45ef8d..9c81501 100644 --- a/_en/quality/common_mistakes.md +++ b/_en/quality/common_mistakes.md @@ -24,7 +24,7 @@ The methods of ObjectRefs will always return nil when invalid, since Minetest 5. Any call will essentially be ignored. You should avoid storing ObjectRefs where possible. If you do to store an -ObjectRef, you should make you check it before use, like so: +ObjectRef, you should make sure you check it before use, like so: ```lua -- This only works in Minetest 5.2+ diff --git a/_en/quality/luacheck.md b/_en/quality/luacheck.md index d9b4410..963196f 100644 --- a/_en/quality/luacheck.md +++ b/_en/quality/luacheck.md @@ -20,7 +20,6 @@ editor to provide alerts to any mistakes. - [Configuring LuaCheck](#configuring-luacheck) - [Troubleshooting](#troubleshooting) - [Using with editor](#using-with-editor) -- [Checking Commits with Travis](#checking-commits-with-travis) ## Installing LuaCheck @@ -102,51 +101,7 @@ It is highly recommended that you find and install a plugin for your editor of c to show you errors without running a command. Most editors will likely have a plugin available. -* **Atom** - `linter-luacheck`. * **VSCode** - Ctrl+P, then paste: `ext install dwenegar.vscode-luacheck` * **Sublime** - Install using package-control: [SublimeLinter](https://github.com/SublimeLinter/SublimeLinter), [SublimeLinter-luacheck](https://github.com/SublimeLinter/SublimeLinter-luacheck). - -## Checking Commits with Travis - -If your project is public and is on Github, you can use TravisCI - a free service -to run jobs on commits to check them. This means that every commit you push will -be checked against LuaCheck, and a green tick or red cross will be displayed next to them -depending on whether LuaCheck finds any mistakes. This is especially helpful for -when your project receives a pull request - you'll be able to see the LuaCheck output -without downloading the code. - -First, you should visit [travis-ci.org](https://travis-ci.org/) and sign in with -your Github account. Then find your project's repo in your Travis profile, -and enable Travis by flipping the switch. - -Next, create a file called .travis.yml with the following content: - -```yml -language: generic -sudo: false -addons: - apt: - packages: - - luarocks -before_install: - - luarocks install --local luacheck -script: -- $HOME/.luarocks/bin/luacheck . -notifications: - email: false -``` - -If your project is a game rather than a mod or mod pack, -change the line after `script:` to: - -```yml -- $HOME/.luarocks/bin/luacheck mods/ -``` - -Now commit and push to Github. Go to your project's page on Github, and click -'commits'. You should see an orange disc next to the commit you just made. -After awhile it should change either into a green tick or a red cross depending on the -outcome of LuaCheck. In either case, you can click the icon to see the build logs -and the output of LuaCheck. diff --git a/_en/quality/readmore.md b/_en/quality/readmore.md index 7ce3dc9..c140c08 100644 --- a/_en/quality/readmore.md +++ b/_en/quality/readmore.md @@ -14,7 +14,6 @@ After you've read this book, take a look at the following. * Minetest's Lua API Reference - [HTML version](https://minetest.gitlab.io/minetest/class-reference/#player-only-no-op-for-other-objects) | [Text version](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt). -* Explore the [Developer Wiki](http://dev.minetest.net/Main_Page). * Look at [existing mods](https://forum.minetest.net/viewforum.php?f=11). ### Lua Programming diff --git a/_en/quality/translations.md b/_en/quality/translations.md new file mode 100644 index 0000000..ab183ea --- /dev/null +++ b/_en/quality/translations.md @@ -0,0 +1,198 @@ +--- +title: Translation (i18n / l10n) +layout: default +root: ../.. +idx: 8.05 +marked_text_encoding: + level: info + title: Marked Text Encoding + message: | + You don't need to know the exact format of marked text, but it might help + you understand. + + ``` + "\27(T@mymod)Hello everyone!\27E" + ``` + + * `\27` is the escape character - it's used to tell Minetest to pay attention as + something special is coming up. This is used for both translations and text + colorisation. + * `(T@mymod)` says that the following text is translatable using the `mymod` + textdomain. + * `Hello everyone!` is the translatable text in English, as passed to the + translator function. + * `\27E` is the escape character again and `E`, used to signal that the end has + been reached. +--- + +## Introduction + +Adding support for translation to your mods and games allows more people to +enjoy them. According to Google Play, 64% of Minetest Android users don't have +English as their primary language. Minetest doesn't track stats for user +languages across all platforms, but there's likely to be a high proportion of +non-English speaking users. + +Minetest allows you to translate your mods and games into different languages by +writing your text in English, and using translation files to map into other +languages. Translation is done on each player's client, allowing each player to +see a different language. + + +- [How does client-side translation work?](#how-does-client-side-translation-work) + - [Marked up text](#marked-up-text) + - [Translation files](#translation-files) +- [Format strings](#format-strings) +- [Best practices and Common Falsehoods about Translation](#best-practices-and-common-falsehoods-about-translation) +- [Server-side translations](#server-side-translations) +- [Conclusion](#conclusion) + + +## How does client-side translation work? + +### Marked up text + +The server needs to tell clients how to translate text. This is done by placing +control characters in text, telling Minetest where and how to translate +text. This is referred to as marked up text, and will be discussed more later. + +To mark text as translatable, use a translator function (`S()`), obtained using +`minetest.get_translator(textdomain)`: + +```lua +local S = minetest.get_translator("mymod") + +minetest.register_craftitem("mymod:item", { + description = S("My Item"), +}) +``` + +The first argument of `get_translator` is the `textdomain`, which acts as a +namespace. Rather than having all translations for a language stored in the same +file, translations are separated into textdomains, with a file per textdomain +per language. The textdomain should be the same as the mod name, as it helps +avoid mod conflicts. + +Marked up text can be used in most places where human-readable text is accepted, +including formspecs, item def fields, infotext, and more. When including marked +text in formspecs, you need to escape the text using `minetest.formspec_escape`. + +When the client encounters translatable text, such as that passed to +`description`, it looks it up in the player's language's translation file. If a +translation cannot be found, it falls back to the English translation. + +Translatable marked up text contains the English source text, the textdomain, +and any additional arguments passed to `S()`. It's essentially a text encoding +of the `S` call, containing all the required information. + +Another type of marked up text is that returned by `minetest.colorize`. + +{% include notice.html notice=page.marked_text_encoding %} + + +### Translation files + +Translation files are media files that can be found in the `locale` folder for +each mod. Currently, the only supported format is `.tr`, but support for more +common formats is likely in the future. Translation files must be named +in the following way: `[textdomain].[lang].tr`. + +Files in the `.tr` start with a comment specifying the textdomain, and then +further lines mapping from the English source text to the translation. + +For example, `mymod.fr.tr`: + +``` +# textdomain: mymod +Hello everyone!=Bonjour à tous ! +I like grapefruit=J'aime le pamplemousse +``` + +You should create translation files based on your mod/game's source code, +using a tool like +[update_translations](https://github.com/minetest-tools/update_translations). +This tool will look for `S(` in your Lua code, and automatically create a +template that translators can use to translate into their language. +It also handles updating the translation files when your source changes. + +You should always put literal text (`"`) inside S rather than using a variable, +as it helps tools find translations. + + +## Format strings + +It's common to need to include variable information within a translation +string. It's important that text isn't just concatenated, as that prevents +translators from changing the order of variables within a sentence. Instead, +you should use the translation system's format/arguments system: + +```lua +minetest.register_on_joinplayer(function(player) + minetest.chat_send_all(S("Everyone, say hi to @1!", player:get_player_name())) +end) +``` + +If you want to include a literal `@` in your translation, you'll need to escape +by writing `@@`. + +You should avoid concatenation *within* a sentence, but it's recommended that +you join multiple sentences using concatenation. This helps translators by +keeping strings smaller. + +```lua +S("Hello @1!", player_name) .. " " .. S("You have @1 new messages.", #msgs) +``` + + +## Best practices and Common Falsehoods about Translation + +* Avoid concatenating text and use format arguments instead. This gives + translators full control over changing the order of things. +* Create translation files automatically, using + [update_translations](https://github.com/minetest-tools/update_translations). +* It's common for variables to change the surrounding text, for example, with + gender and pluralisation. This is often hard to deal with, so is + frequently glossed over or worked around with gender neutral phrasings. +* Translations may be much longer or much smaller than the English text. Make + sure to leave plenty of space. +* Other languages may write numbers in a different way, for example, with commas + as decimal points. `1.000,23`, `1'000'000,32` +* Don't assume that other languages use capitalisation in the same way. + + +## Server-side translations + +Sometimes you need to know the translation of text on the server, for example, +to sort or search text. You can use `get_player_information` to get a player's +language and `get_translated_string` to translate marked text. + +```lua +local list = { + S("Hello world!"), + S("Potato") +} + +minetest.register_chatcommand("find", { + func = function(name, param) + local info = minetest.get_player_information(name) + local language = info and info.language or "en" + + for _, line in ipairs(list) do + local trans = minetest.get_translated_string(language, line) + if trans:contains(query) then + return line + end + end + end, +}) +``` + +## Conclusion + +The translation API allows making mods and games more accessible, but care is +needed in order to use it correctly. + +Minetest is continuously improving, and the translation API is likely to be +extended in the future. For example, support for gettext translation files will +allow common translator tools and platforms (like weblate) to be used, and +there's likely to be support for pluralisation and gender added. diff --git a/_en/quality/unit_testing.md b/_en/quality/unit_testing.md index 643380a..830f99e 100644 --- a/_en/quality/unit_testing.md +++ b/_en/quality/unit_testing.md @@ -17,9 +17,8 @@ we discussed how to structure your code avoid this. - [Your First Test](#your-first-test) - [init.lua](#initlua) - [api.lua](#apilua) - - [tests/api_spec.lua](#testsapispeclua) + - [tests/api_spec.lua](#testsapi_speclua) - [Mocking: Using External Functions](#mocking-using-external-functions) -- [Checking Commits with Travis](#checking-commits-with-travis) - [Conclusion](#conclusion) ## Installing Busted @@ -164,28 +163,6 @@ end) ``` -## Checking Commits with Travis - -The Travis script from the [Automatic Error Checking](luacheck.html) -chapter can be modified to also run Busted. - -```yml -language: generic -sudo: false -addons: - apt: - packages: - - luarocks -before_install: - - luarocks install --local luacheck && luarocks install --local busted -script: -- $HOME/.luarocks/bin/luacheck . -- $HOME/.luarocks/bin/busted . -notifications: - email: false -``` - - ## Conclusion Unit tests will greatly increase the quality and reliability of your project if used diff --git a/_it/basics/lua.md b/_it/basics/lua.md index ebe3bd4..475fd78 100644 --- a/_it/basics/lua.md +++ b/_it/basics/lua.md @@ -11,17 +11,32 @@ redirect_from: /it/chapters/lua.html In questo capitolo parleremo della programmazione in Lua, degli strumenti necessari, e tratteremo alcune tecniche che troverai probabilmente utili. -- [Editor di codice](#editor-di-codice) -- [Programmare in Lua](#programmare-in-lua) - - [Flusso del programma](#flusso-del-programma) - - [Tipi di variabili](#tipi-di-variabili) - - [Operatori matematici](#operatori-matematici) - - [Selezione](#selezione) - - [Operatori logici](#operatori-logici) - [Programmare](#programmare) + - [Programmare in Lua](#programmare-in-lua) +- [Editor di codice](#editor-di-codice) - [Portata locale e globale](#portata-locale-e-globale) + - [Precedenza alla portata locale](#precedenza-alla-portata-locale) - [Inclusione di altri script Lua](#inclusione-di-altri-script-lua) + +## Programmare + +Programmare è l'azione di prendere un problema, come ordinare una lista di oggetti, e tramutarlo in dei passaggi che il computer può comprendere. + +Insegnarti i processi logici della programmazione non rientra nell'ambito di questo libro; tuttavia, i seguenti siti sono alquanto utili per approfondire l'argomento: + +* [Codecademy](http://www.codecademy.com/) è una delle migliori risorse per imparare come scrivere codice; offre un'esperienza guidata interattiva. +* [Scratch](https://scratch.mit.edu) è una buona risorsa quando si comincia dalle basi assolute, imparando le tecniche di problem solving necessarie per la programmazione.\\ + Scratch è *ideato per insegnare ai bambini* e non è un linguaggio serio di programmazione. +* [Programming with Mosh](https://www.youtube.com/user/programmingwithmosh) is + a good YouTube series to learn programming. + +### Programmare in Lua + +Neanche insegnarti come programmare in lua rientra nell'ambito di questo libro. +Tuttavia, se mastichi l'inglese puoi rifarti a quest'altro libro, ["Programming in Lua"](https://www.lua.org/pil/contents.html), per un'eccellente infarinatura sull'argomento. Se invece l'inglese non è il tuo forte, troverai comunque svariate guide in italiano in giro per la rete. + + ## Editor di codice Un editor di codice con evidenziamento delle parole chiave è sufficiente per scrivere script in Lua. @@ -56,126 +71,6 @@ Tra gli editor più famosi che ben si prestano a lavorare in Lua, troviamo: (ne esistono ovviamente anche altri) -## Programmare in Lua - -### Flusso del programma - -I programmi sono una serie di comandi che vengono eseguiti uno dopo l'altro. -Chiamiamo questi comandi "istruzioni". -Il flusso del programma è il come queste istruzioni vengono eseguite, e a differenti tipi di flusso corrispondono comportamenti diversi. -Essi possono essere: - -* Sequenziali: eseguono un'istruzione dopo l'altra, senza salti. -* Selettivi: saltano alcune sequenze a seconda delle condizioni. -* Iteranti: continuano a eseguire le stesse istruzioni finché una condizione non è soddisfatta. - -Quindi, come vengono rappresentate le istruzioni in Lua? - -```lua -local a = 2 -- Imposta 'a' a 2 -local b = 2 -- Imposta 'b' a 2 -local risultato = a + b -- Imposta 'risultato' ad a + b, cioè 4 -a = a + 10 -print("La somma è ".. risultato) -``` - -In quest'esempio, `a`, `b`, e `risultato` sono *variabili*. Le variabili locali si dichiarano tramite l'uso della parola chiave `local` (che vedremo tra poco), e assegnando eventualmente loro un valore iniziale. - -Il simbolo `=` significa *assegnazione*, quindi `risultato = a + b` significa impostare "risultato" ad a + b. -Per quanto riguarda i nomi delle variabili, essi possono essere più lunghi di un carattere - al contrario che in matematica - come visto in `risultato`, e vale anche la pena notare che Lua è *case-sensitive* (differenzia maiuscole da minuscole); `A` è una variabile diversa da `a`. - -### Tipi di variabili - -Una variabile può equivalere solo a uno dei seguenti tipi e può cambiare tipo dopo l'assegnazione. -È buona pratica assicurarsi che sia sempre solo o nil o diversa da nil. - -| Tipo | Descrizione | Esempio | -|----------|---------------------------------|----------------| -| Nil | Non inizializzata. La variabile è vuota, non ha valore | `local A`, `D = nil` | -| Numero | Un numero intero o decimale | `local A = 4` | -| Stringa | Una porzione di testo | `local D = "one two three"` | -| Booleano | Vero o falso (true, false) | `local is_true = false`, `local E = (1 == 1)` | -| Tabella | Liste | Spiegate sotto | -| Funzione | Può essere eseguita. Può richiedere input e ritornare un valore | `local result = func(1, 2, 3)` | - -### Operatori matematici - -Tra gli operatori di Lua ci sono: - -| Simbolo | Scopo | Esempio | -|---------|--------------------|---------------------------| -| A + B | Addizione | 2 + 2 = 4 | -| A - B | Sottrazione | 2 - 10 = -8 | -| A * B | Moltiplicazione | 2 * 2 = 4 | -| A / B | Divisione | 100 / 50 = 2 | -| A ^ B | Potenze | 2 ^ 2 = 22 = 4 | -| A .. B | Concatena stringhe | "foo" .. "bar" = "foobar" | - -Si tenga presente che questa non è comunque una lista esaustiva. - -### Selezione - -Il metodo di selezione più basico è il costrutto if. -Si presenta così: - -```lua -local random_number = math.random(1, 100) -- Tra 1 e 100. -if random_number > 50 then - print("Woohoo!") -else - print("No!") -end -``` - -Questo esempio genera un numero casuale tra 1 e 100. -Stampa poi "Woohoo!" se il numero è superiore a 50, altrimenti stampa "No!". - -### Operatori logici - -Tra gli operatori logici di Lua ci sono: - -| Simbolo | Scopo | Esempio | -|---------|--------------------------------------|-------------------------------------------------------------| -| A == B | Uguale a | 1 == 1 (true), 1 == 2 (false) | -| A ~= B | Non uguale a (diverso da) | 1 ~= 1 (false), 1 ~= 2 (true) | -| A > B | Maggiore di | 5 > 2 (true), 1 > 2 (false), 1 > 1 (false) | -| A < B | Minore di | 1 < 3 (true), 3 < 1 (false), 1 < 1 (false) | -| A >= B | Maggiore o uguale a | 5 >= 5 (true), 5 >= 3 (true), 5 >= 6 (false) | -| A <= B | Minore o uguale a | 3 <= 6 (true), 3 <= 3 (true) | -| A and B | E (entrambi devono essere veri) | (2 > 1) and (1 == 1) (true), (2 > 3) and (1 == 1) (false) | -| A or B | O (almeno uno dei due vero) | (2 > 1) or (1 == 2) (true), (2 > 4) or (1 == 3) (false) | -| not A | non vero | not (1 == 2) (true), not (1 == 1) (false) | - -La lista non è esaustiva, e gli operatori possono essere combinati, come da esempio: - -```lua -if not A and B then - print("Yay!") -end -``` - -Che stampa "Yay!" se `A` è falso e `B` vero. - -Gli operatori logici e matematici funzionano esattamente allo stesso modo; entrambi accettano input e ritornano un valore che può essere immagazzinato. Per esempio: - -```lua -local A = 5 -local is_equal = (A == 5) -if is_equal then - print("È equivalente!") -end -``` - -## Programmare - -Programmare è l'azione di prendere un problema, come ordinare una lista di oggetti, e tramutarlo in dei passaggi che il computer può comprendere. - -Insegnarti i processi logici della programmazione non rientra nell'ambito di questo libro; tuttavia, i seguenti siti sono alquanto utili per approfondire l'argomento: - -* [Codecademy](http://www.codecademy.com/) è una delle migliori risorse per imparare come scrivere codice; offre un'esperienza guidata interattiva. -* [Scratch](https://scratch.mit.edu) è una buona risorsa quando si comincia dalle basi assolute, imparando le tecniche di problem solving necessarie per la programmazione.\\ - Scratch è *ideato per insegnare ai bambini* e non è un linguaggio serio di programmazione. - ## Portata locale e globale L'essere locale o globale di una variabile determina da dove è possibile accederci. @@ -211,6 +106,9 @@ one() two() ``` + +### Precedenza alla portata locale + Le variabili locali dovrebbero venire usate il più possibile, con le mod che creano al massimo una globale corrispondente al nome della mod. Crearne di ulteriori è considerato cattiva programmazione, e Minetest ci avviserà di ciò: diff --git a/_it/index.md b/_it/index.md index b0524ca..5074fbd 100644 --- a/_it/index.md +++ b/_it/index.md @@ -8,7 +8,7 @@ idx: 0.1 ---
-

Minetest: Libro del Modding

+

Minetest: Libro del Moddaggio

di con modifiche di Shara @@ -17,20 +17,20 @@ idx: 0.1 ## Introduzione -Il modding su Minetest è supportato grazie a script in Lua. +Il moddaggio su Minetest è supportato grazie a script in Lua. Questo libro mira a insegnarti come creare le tue mod, iniziando dalle basi. -Ogni capitolo si concentra su un punto specifico dell'API, e ti porterà presto +Ogni capitolo si concentra su un punto specifico dell'API, portandoti in breve tempo a fare le tue mod. Oltre che [leggere questo libro online](https://rubenwardy.com/minetest_modding_book), puoi anche [scaricarlo in HTML](https://github.com/rubenwardy/minetest_modding_book/releases). -### Feedback e Contributi +### Riscontri e Contributi -Hai notato un errore o vuoi dare un feedback? Assicurati di farmelo presente. +Hai notato un errore o vuoi darmi il tuo parere? Assicurati di farmelo presente. -* Crea una [Issue su GitLab](https://gitlab.com/rubenwardy/minetest_modding_book/-/issues). -* Posta nel [Topic sul Forum](https://forum.minetest.net/viewtopic.php?f=14&t=10729). +* Apri una [Segnalazione su GitLab](https://gitlab.com/rubenwardy/minetest_modding_book/-/issues). +* Rispondi alla [Discussione sul Forum](https://forum.minetest.net/viewtopic.php?f=14&t=10729). * [Contattami (in inglese)](https://rubenwardy.com/contact/). * Voglia di contribuire? [Leggi il README](https://gitlab.com/rubenwardy/minetest_modding_book/-/blob/master/README.md). diff --git a/_it/items/callbacks.md b/_it/items/callbacks.md new file mode 100644 index 0000000..7933883 --- /dev/null +++ b/_it/items/callbacks.md @@ -0,0 +1,184 @@ +--- +title: Richiami dei nodi e degli oggetti +layout: default +root: ../.. +idx: 2.15 +description: Scopri i richiami, le azioni e gli eventi, come on_use, on_punch, on_place e on_rightclick +--- + +## Introduction + +Minetest usa una struttura di moddaggio estensivamente incentrata sui richiami. Un richiamo è una funzione che si dà a un'API e che viene chiamata quando l'evento registrato si verifica. +Per esempio, puoi aggiungere una funzione `on_punch` nella definizione di un nodo, che verrà chiamata quando questo viene colpito. +Ci sono poi anche dei richiami globali, come `minetest.register_on_punchnode`, che in questo caso verrà invocato al colpire qualsiasi nodo. + +- [Richiami degli oggetti](#richiami-degli-oggetti) + - [on_use](#on_use) + - [on_place e on_secondary_use](#on_place-e-on_secondary_use) + - [on_drop](#on_drop) + - [after_use](#after_use) +- [item_place contro place_item](#item_place-contro-place_item) +- [Richiami dei nodi](#richiami-dei-nodi) + - [Tasto destro e nodi piazzati](#tasto-destro-e-nodi-piazzati) + - [Colpire e scavare](#colpire-e-scavare) + - [...e altro!](#e-altro) + + +## Richiami degli oggetti + +Quando un giocatore ha un nodo, un oggetto fabbricabile o uno strumento nel proprio inventario, questi potrebbero innescare degli eventi: + +| Richiamo | Assegnazione base | Valore base | +|------------------|---------------------------|----------------------------------------------| +| on_use | clic sinistro | nil | +| on_place | clic destro su un nodo | `minetest.item_place` | +| on_secondary_use | clic destro a vuoto | `minetest.item_secondary_use` (non fa nulla) | +| on_drop | Q | `minetest.item_drop` | +| after_use | allo scavare un nodo | nil | + + +### on_use + +Sovrascrivere l'uso dell'oggetto impedisce che quest'ultimo possa essere usato per scavare nodi. +Un impiego comune di questo richiamo lo si trova nel cibo: + +```lua +minetest.register_craftitem("miamod:fangotorta", { + description = "Torta aliena di fango", + inventory_image = "miamod_fangotorta.png", + on_use = minetest.item_eat(20), +}) +``` + +Il numero fornito alla funzione minetest.item_eat è il numero di punti salute ripristinati al consumare il cibo. +In gioco ogni cuore equivale a due punti. +Un giocatore ha solitamente un massimo di 10 cuori, ovvero 20 punti salute, e quest'ultimi non devono per forza essere interi - bensì anche decimali. + +`minetest.item_eat()` è una funzione che ritorna un'altra funzione, in questo caso quindi impostandola come richiamo di on_use. +Ciò significa che il codice in alto è alquanto simile al seguente: + +```lua +minetest.register_craftitem("miamod:fangotorta", { + description = "Torta aliena di fango", + inventory_image = "miamod_fangotorta.png", + on_use = function(...) + return minetest.do_item_eat(20, nil, ...) + end, +}) +``` + +Capendo come funziona item_eat, è possibile modificarlo per operazioni più complesse +come per esempio riprodurre un suono personalizzato. + + +### on_place e on_secondary_use + +La differenza tra `on_place` e `on_secondary_use` consiste nel fatto che `on_place` viene chiamato quando il giocatore sta puntando un nodo, mentre `on_secondary_use` quando non ne punta uno. + +Entrambi i richiami sono invocati per tutti i tipi di oggetti. +`on_place` risponde alla funzione `minetest.item_place`, la quale o gestisce la chiamata a `on_rightclick` del nodo puntato, o piazza l'oggetto in mano se questo è un nodo. + + +### on_drop + +`on_drop` viene chiamato quando il giocatore fa richiesta per buttare un oggetto, per esempio usando il tasto apposito (Q) o trascinando l'oggetto fuori dall'inventario. +Risponde alla funzione `minetest.item_drop`, la quale gestisce il buttare l'oggetto. + +### after_use + +`after_use` viene chiamato quando si scava un nodo, e permette di personalizzare come viene applicata l'usura a uno strumento. +Se `after_use` non esiste, è come se ci fosse scritto: + +```lua +after_use = function(itemstack, user, node, digparams) + itemstack:add_wear(digparams.wear) + return itemstack +end +``` + + +## item_place contro place_item + +L'API di Minetest include varie implementazioni già pronte di richiami. +Queste seguono la nomenclatura "tipodioggetto_azione", per esempio `minetest.item_place` e `minetest.node_dig`. +Alcune sono usate direttamente, mentre altre sono funzioni che ritornano il richiamo vero e proprio: + +```lua +minetest.register_item("miamod:esempio", { + on_place = minetest.item_place, + on_use = minetest.item_eat(10), +}) +``` + +Inoltre, l'API di Minetest include funzioni già pronte che _fanno_ qualcosa. +Queste sono spesso chiamate con nomi che rischiano di farle confondere con le implementazioni dei richiami, tuttavia hanno un verbo all'inizio (per esempio `minetest.place_item` e `minetest.dig_node`, che permettono rispettivamente di scavare e piazzare nodi come se lo stesse facendo un giocatore). + + +## Richiami dei nodi + +Quando un nodo si trova in un inventario, vengono invocati i richiami degli oggetti discussi poc'anzi. +Al contrario, quando un nodo è situato nel mondo, vengono invocati i richiami dei nodi. +Ce ne sono di svariati tipi, troppi per essere discussi in questo libro, tuttavia alcuni di questi verranno trattati nei capitoli successivi. + +Molti richiami dei nodi sono collegati alle operazioni effettuate - appunto - sui nodi, come piazzarli e rimuoverli dal mondo. +È importante però sottolineare che, per motivi di prestazioni, operazioni come queste non vengono chiamate da modifiche in blocco (quelle che cambiano un grande numero di nodi in un colpo solo). +È meglio quindi non fare affidamento su un'esecuzione sicura al 100%. + + +### Tasto destro e nodi piazzati + +Quando un utente preme col tasto destro un nodo mentre ha un oggetto in mano, viene invocato il richiamo `on_place` dell'oggetto. +Di base, questo è impostato a `minetest.item_place`. +Se il nodo puntato ha un richiamo `on_rightclick` e il tasto accovacciati (shift) è tenuto premuto, allora verrà chiamato `on_rightclick`. +Diversamente, `minetest.item_place` piazzerà il nodo. + +Piazzare un nodo invocherà simultaneamente `on_construct` e `after_place_node`: il primo è chiamato da ogni evento che cambia i singoli nodi (quindi non in blocco) e ritorna la posizione e il valore del nodo. +`after_place_node` viene invece chiamato solamente al piazzare un nodo, contenendo di conseguenza più informazioni - come chi l'ha piazzato e l'ItemStack. + +È importante notare che i giocatori non sono le uniche realtà che possono piazzare nodi; anche le entità e le mod possono farlo. +Per via di ciò, `place` potrebbe essere un giocatore, ma anche un'entità o `nil`. + +```lua +minetest.register_node("miamod:mionodo", { + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + if clicker:is_player() then + minetest.chat_send_player(clicker:get_player_name(), "Ciao mondo!") + end + end, + on_construct = function(pos, node) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "Il mio nodo!") + end, + after_place_node = function(pos, placer, itemstack, pointed_thing) + -- controlla chi sta piazzando + if placer and placer:is_player() then + local meta = minetest.get_meta(pos) + meta:set_string("proprietario", placer:get_player_name()) + end + end, +}) +``` + +### Colpire e scavare + +Si ha un colpo quando un giocatore preme col tasto sinistro per un breve periodo. +Se l'oggetto in mano possiede un richiamo `on_use`, questo verrà chiamato. +Diversamente, verrà chiamato il richiamo `on_punch` sul nodo selezionato. + +Quando il giocatore tenta di scavare un nodo, viene eseguito il richiamo `on_dig` del nodo. +Di base, ciò equivale a `minetest.node_dig`, che controlla eventuali protezioni dell'area, usura l'oggetto, rimuove il nodo, e ne esegue il richiamo `after_dig_node`. + + +```lua +minetest.register_node("miamod:mionodo", { + on_punch = function(pos, node, puncher, pointed_thing) + if puncher:is_player() then + minetest.chat_send_player(puncher:get_player_name(), "Ahia!") + end + end, +}) +``` + +### ...e altro! + +Dài un occhio alla API Lua di Minetest per una lista di tutti i richiami, e per avere più informazioni riguardo quelli vista qui sopra. diff --git a/_it/items/nodes_items_crafting.md b/_it/items/nodes_items_crafting.md index 25c87ec..9088635 100644 --- a/_it/items/nodes_items_crafting.md +++ b/_it/items/nodes_items_crafting.md @@ -17,8 +17,6 @@ Saper registrare nuovi nodi, oggetti fabbricabili e conseguenti ricette, è un r - [Alias](#alias) - [Texture](#texture) - [Registrare un nodo base](#registrare-un-nodo-base) -- [Azioni e callback](#azioni-e-callback) - - [on_use](#onuse) - [Fabbricazione](#fabbricazione) - [Fisse (shaped)](#fisse-shaped) - [Informi (shapeless)](#informi-shapeless) @@ -138,46 +136,6 @@ minetest.register_node("miamod:diamante", { L'attributo is_ground_content è essenziale per ogni nodo che si vuole far apparire sottoterra durante la generazione della mappa. Le caverne vengono scavate nel mondo dopo che tutti gli altri nodi nell'area sono stati generati. -## Azioni e callback - -Minetest usa ampiamente una struttura per il modding incentrata sui callback (richiami). -I callback possono essere inseriti nella tabella di definizioni dell'oggetto per permettere una risposta a vari tipi di eventi generati dall'utente. -Vediamone un esempio. - -### on_use - -Di base, il callback on_use scatta quando un giocatore clicca col tasto sinistro con l'oggetto in mano. -Avere un callback sull'uso previene che l'oggetto venga utilizzato per scavare nodi. -Un utilizzo comune di questo callback è per il cibo: - -```lua -minetest.register_craftitem("miamod:fangotorta", { - description = "Torta aliena di fango", - inventory_image = "miamod_fangotorta.png", - on_use = minetest.item_eat(20), -}) -``` - -Il numero fornito alla funzione minetest.item_eat è il numero di punti salute ripristinati al consumare il cibo. -In gioco ogni cuore equivale a due punti. -Un giocatore ha solitamente un massimo di 10 cuori, ovvero 20 punti salute, e quest'ultimi non devono per forza essere interi - ovvero decimali. - -`minetest.item_eat()` è una funzione che ritorna un'altra funzione, in questo caso quindi impostandola come callback di on_use. -Ciò significa che il codice in alto è alquanto simile al seguente: - -```lua -minetest.register_craftitem("miamod:fangotorta", { - description = "Torta aliena di fango", - inventory_image = "miamod_fangotorta.png", - on_use = function(...) - return minetest.do_item_eat(20, nil, ...) - end, -}) -``` - -Capendo come funziona item_eat, è possibile modificarlo per operazioni più complesse -come per esempio riprodurre un suono personalizzato. - ## Fabbricazione Ci sono diversi tipi di ricette di fabbricazione disponibili, indicate dalla proprietà `type`. diff --git a/_it/map/environment.md b/_it/map/environment.md index 7852a54..9e46c3d 100644 --- a/_it/map/environment.md +++ b/_it/map/environment.md @@ -184,7 +184,7 @@ local function mio_callback(pos, action, -- Invia messaggio indicante il progresso if param.blocchi_totali == param.blocchi_caricati then minetest.chat_send_all("Ho finito di caricare blocchi!") - end + else local percentuale = 100 * param.blocchi_caricati / param.blocchi_totali local msg = string.format("Caricamento blocchi %d/%d (%.2f%%)", param.blocchi_caricati, param.blocchi_totali, percentuale) diff --git a/_it/map/objects.md b/_it/map/objects.md index d9ea231..767682b 100644 --- a/_it/map/objects.md +++ b/_it/map/objects.md @@ -1,5 +1,5 @@ --- -title: Oggetti, giocatori e entità +title: Oggetti, giocatori ed entità layout: default root: ../.. idx: 3.4 diff --git a/_it/players/chat.md b/_it/players/chat.md index 053995e..f099932 100755 --- a/_it/players/chat.md +++ b/_it/players/chat.md @@ -8,17 +8,19 @@ redirect_from: /it/chapters/chat.html cmd_online: level: warning title: I giocatori offline possono eseguire comandi - message:

Viene passato il nome del giocatore al posto del giocatore in sé perché le mod possono eseguire comandi in vece di un giocatore offline. - Per esempio, il bridge IRC permette ai giocatori di eseguire comandi senza dover entrare in gioco.

+ message: | + Viene passato il nome del giocatore al posto del giocatore in sé perché le mod possono eseguire comandi in vece di un giocatore offline. + Per esempio, il ponte IRC permette ai giocatori di eseguire comandi senza dover entrare in gioco. -

Assicurati quindi di non dar per scontato che un giocatore sia connesso. - Puoi controllare ciò tramite

minetest.get_player_by_name
, per vedere se ritorna qualcosa o meno.

+ Assicurati quindi di non dar per scontato che un giocatore sia connesso. + Puoi controllare ciò tramite `minetest.get_player_by_name`, per vedere se ritorna qualcosa o meno. cb_cmdsprivs: level: warning title: Privilegi e comandi - message: Il privilegio shout non è necessario per far sì che un giocatore attivi questo callback. - Questo perché i comandi sono implementati in Lua, e sono semplicemente dei messaggi in chat che iniziano con /. + message: | + Il privilegio shout non è necessario per far sì che un giocatore attivi questo richiamo. + Questo perché i comandi sono implementati in Lua, e sono semplicemente dei messaggi in chat che iniziano con /. --- @@ -26,13 +28,18 @@ cb_cmdsprivs: Le mod possono interagire con la chat del giocatore, tra l'inviare messaggi, intercettarli e registrare dei comandi. -- [Inviare messaggi a tutti i giocatori](#inviare-messaggi-a-tutti-i-giocatori) -- [Inviare messaggi a giocatori specifici](#inviare-messaggi-a-giocatori-specifici) +- [Inviare messaggi](#inviare-messaggi) + - [A tutti i giocatori](#a-tutti-i-giocatori) + - [A giocatori specifici](#a-giocatori-specifici) - [Comandi](#comandi) -- [Complex Subcommands](#complex-subcommands) -- [Intercettare i messaggi](#interecettare-i-messaggi) + - [Accettare più argomenti](#accettare-più-argomenti) + - [Usare string.split](#usare-stringsplit) + - [Usare i pattern Lua](#usare-i-pattern-lua) +- [Intercettare i messaggi](#intercettare-i-messaggi) -## Inviare messaggi a tutti i giocatori +## Inviare messaggi + +### A tutti i giocatori Per inviare un messaggio a tutti i giocatori connessi in gioco, si usa la funzione `chat_send_all`: @@ -48,7 +55,7 @@ Segue un esempio di come apparirerebbe in gioco: Il messaggio appare su una nuova riga, per distinguerlo dai messaggi dei giocatori. -## Inviare messaggi a giocatori specifici +### A giocatori specifici Per inviare un messaggio a un giocatore in particolare, si usa invece la funzione `chat_send_player`: @@ -75,21 +82,50 @@ minetest.register_chatcommand("foo", { Nel codice qui in alto, `interact` è elencato come [privilegio](privileges.html) necessario; in altre parole, solo i giocatori che hanno quel privilegio possono usare il comando. +`param` è una stringa contenente tutto ciò che un giocatore scrive dopo il nome del comando. +Per esempio, in `/grantme uno,due,tre`, `param` equivarrà a `uno,due,tre`. + I comandi ritornano un massimo di due valori, dove il primo è un booleano che indica l'eventuale successo, mentre il secondo è un messaggio da inviare all'utente. {% include notice.html notice=page.cmd_online %} -## Sottocomandi complessi -È spesso necessario creare dei comandi complessi, come per esempio: +### Accettare più argomenti -* `/msg ` -* `/team entra ` -* `/team esci ` -* `/team elenco` +Non è raro che i comandi richiedano più argomenti, come per esempio `/squadra entra `. +Ci sono due modi per implementare ciò: usare `string.split` o i pattern Lua. -Questo viene solitamente reso possibile dai [pattern di Lua](https://www.lua.org/pil/20.2.html). -I pattern sono un modo di estrapolare "cose" dal testo usando delle regole ben precise. + +#### Usare string.split + +Una stringa può essere spezzettata in più parti tramite `string.split(" ")`: + +```lua +local parti = param:split(" ") +local cmd = parti[1] + +if cmd == "entra" then + local nome_squadra = parti[2] + squadra.entra(name, nome_squadra) + return true, "Sei dentro la squadra!" +elseif cmd == "gioc_max" then + local nome_squadra = parti[2] + local gioc_max = tonumber(parti[3]) + if nome_squadra and gioc_max then + return true, "Giocatori massimi della squadra " .. nome_squadra .. " impostati a " .. gioc_max + else + return false, "Uso: /squadra gioc_max " + end +else + return false, "È necessario un comando" +end +``` + + +#### Usare i pattern Lua + +[I pattern Lua](https://www.lua.org/pil/20.2.html) sono un modo per estrapolare pezzi di testo seguendo delle regole. +Sono perfetti in caso di argomenti che contengono spazi, o comunque quando è richiesto un controllo più meticoloso dei parametri catturati. ```lua local a, msg = string.match(param, "^([%a%d_-]+) (*+)$") @@ -130,7 +166,7 @@ minetest.register_on_chat_message(function(name, message) end) ``` -Ritornando false, si permette al messaggio di essere inviato. +Ritornando `false`, si permette al messaggio di essere inviato. In verità `return false` può anche essere omesso in quanto `nil` verrebbe ritornato implicitamente, e nil è trattato come false. {% include notice.html notice=page.cb_cmdsprivs %} diff --git a/_it/players/formspecs.md b/_it/players/formspecs.md index a8d42dc..46a9201 100644 --- a/_it/players/formspecs.md +++ b/_it/players/formspecs.md @@ -29,16 +29,16 @@ Tieni presente che se non si ha bisogno di ricevere input dal giocatore, per ese - [Coordinate reali o datate](#coordinate-reali-o-datate) - [Anatomia di un formspec](#anatomia-di-un-formspec) - - [Elementi](#elementi) - - [Intestazione](#intestazione) + - [Elementi](#elementi) + - [Intestazione](#intestazione) - [Esempio: indovina un numero](#esempio-indovina-un-numero) - - [Imbottitura e spaziatura](#imbottitura-e-spaziatura) - - [Ricevere i moduli di compilazione](#ricevere-i-moduli-di-compilazione) - - [Contesti](#contesti) + - [Imbottitura e spaziatura](#imbottitura-e-spaziatura) + - [Ricevere i moduli di compilazione](#ricevere-i-moduli-di-compilazione) + - [Contesti](#contesti) - [Ricavare un formspec](#ricavare-un-formspec) - - [Formspec nei nodi](#formspec-nei-nodi) - - [Inventario del giocatore](#inventario-del-giocatore) - - [Il tuo turno](#il-tuo-turno) + - [Formspec nei nodi](#formspec-nei-nodi) + - [Inventario del giocatore](#inventario-del-giocatore) + - [Il tuo turno](#il-tuo-turno) ## Coordinate reali o datate @@ -321,7 +321,7 @@ L'inventario del giocatore è un formspec, che viene mostrato al premere "I". Il callback globale viene usato per ricevere eventi dall'inventario, e il suo nome è `""`. Ci sono svariate mod che permettono ad altrettante mod di personalizzare l'inventario del giocatore. -La mod ufficialmente raccomandata è [Simple Fast Inventory (sfinv)](sfinv.html), ed è inclusa in Minetest Game. +La mod ufficialmente raccomandata è [SFINV](https://github.com/rubenwardy/sfinv/blob/master/Tutorial.md), ed è inclusa in Minetest Game. ### Il tuo turno diff --git a/_it/players/player_physics.md b/_it/players/player_physics.md index 22aa9b4..a59d3df 100644 --- a/_it/players/player_physics.md +++ b/_it/players/player_physics.md @@ -61,4 +61,4 @@ Quando si imposta una sovrascrittura, sovrascriverà qualsiasi altro suo simile * **Sonic**: Imposta il moltiplicatore di velocità a un valore elevato (almeno 6) quando un giocatore entra in gioco; * **Super rimbalzo**: Aumenta il valore del salto in modo che il giocatore possa saltare 20 metri (1 cubo = 1 metro); -* **Space**: Fai in modo che la gravità diminuisca man mano che si sale di altitudine. +* **Spazio**: Fai in modo che la gravità diminuisca man mano che si sale di altitudine. diff --git a/_it/players/sfinv.md b/_it/players/sfinv.md index a5dcb3b..1fd6fdd 100644 --- a/_it/players/sfinv.md +++ b/_it/players/sfinv.md @@ -1,223 +1,4 @@ --- -title: "SFINV" -layout: default -root: ../.. -idx: 4.7 -description: una mod per rendere più semplice la creazione di un inventario complesso -redirect_from: /it/chapters/sfinv.html +sitemap: false +redirect_to: "https://github.com/rubenwardy/sfinv/blob/master/Tutorial.md" --- - -## Introduzione - -Simple Fast Inventory (SFINV) è una mod presente in Minetest Game, usata per creare il [formspec](formspecs.html) del giocatore. -SFINV ha un'API che permette di aggiungere e altresì gestire le pagine mostrate. - -Mentre SFINV di base mostra le pagine come finestre, le pagine sono chiamate tali in quanto è assolutamente possibile che una mod o un gioco decidano di mostrarle in un altro formato. -Per esempio, più pagine possono essere mostrate nel medesimo formspec. - -- [Registrare una pagina](#registrare-una-pagina) -- [Ricevere eventi](#ricevere-eventi) -- [Condizioni per la visualizzazione](#condizioni-per-la-visualizzazione) -- [Callback on_enter e on_leave](#callback-onenter-e-onleave) -- [Aggiungere a una pagina esistente](#aggiungere-a-una-pagina-esistente) - -## Registrare una pagina - -SFINV fornisce la funzione chiamata `sfinv.register_page` per creare nuove pagine. -Basta chiamare la funzione con il nome che si vuole assegnare alla pagina e la sua definizione: - -```lua -sfinv.register_page("miamod:ciao", { - title = "Ciao!", - get = function(self, player, context) - return sfinv.make_formspec(player, context, - "label[0.1,0.1;Ciao mondo!]", true) - end -}) -``` - -La funzione `make_formspec` circonda il formspec con il codice di SFINV. -Il quarto parametro, attualmente impostato a `true`, determina se l'inventario del giocatore è mostrato. - -Rendiamo le cose più eccitanti; segue il codice della generazione di un formspec per gli admin. -Questa finestra permetterà agli admin di cacciare o bannare i giocatori selezionandoli da una lista e premendo un pulsante. - -```lua -sfinv.register_page("mioadmin:mioadmin", { - title = "Finestra", - get = function(self, player, context) - local giocatori = {} - context.mioadmin_giocatori = giocatori - - -- Usare un array per costruire un formspec è decisamente più veloce - local formspec = { - "textlist[0.1,0.1;7.8,3;lista_giocatori;" - } - - -- Aggiunge tutti i giocatori sia alla lista testuale che a quella - appunto - dei giocatori - local primo = true - for _ , giocatore in pairs(minetest.get_connected_players()) do - local nome_giocatore = giocatore:get_player_name() - giocatori[#giocatori + 1] = nome_giocatore - if not primo then - formspec[#formspec + 1] = "," - end - formspec[#formspec + 1] = - minetest.formspec_escape(nome_giocatore) - primo = false - end - formspec[#formspec + 1] = "]" - - -- Aggiunge i pulsanti - formspec[#formspec + 1] = "button[0.1,3.3;2,1;caccia;Caccia]" - formspec[#formspec + 1] = "button[2.1,3.3;2,1;banna;Caccia e Banna]" - - -- Avvolge il formspec nella disposizione di SFINV - -- (es: aggiunge le linguette delle finestre e lo sfondo) - return sfinv.make_formspec(player, context, - table.concat(formspec, ""), false) - end, -}) -``` - -Non c'è niente di nuovo in questa parte di codice; tutti i concetti sono già stati trattati o qui in alto o nei precedenti capitoli. - -
- Pagina per gli amministratori -
- -## Ricevere eventi - -Puoi ricevere eventi formspec tramite l'aggiunta della funzione `on_player_receive_fields` nella definizione SFINV. - -```lua -on_player_receive_fields = function(self, player, context, fields) - -- TODO: implementarlo -end, -``` - -`on_player_receive_fields` funziona alla stessa maniera di `minetest.register_on_player_receive_fields`, con la differenza che viene richiesto il contesto al posto del nome del form. -Tieni a mente che gli eventi interni di SFINV, come la navigazione tra le varie finestre, vengono gestiti dentro la mod stessa, e che quindi non verranno ricevuti in questo callback. - -Implementiamo ora `on_player_receive_fields` nella mod: - -```lua -on_player_receive_fields = function(self, player, context, fields) - -- evento della lista testuale: controlla il tipo di evento e imposta il nuovo indice se è cambiata la selezione - if fields.lista_giocatori then - local event = minetest.explode_textlist_event(fields.lista_giocatori) - if event.type == "CHG" then - context.mioadmin_sel_id = event.index - end - - -- Al premere "Caccia" - elseif fields.caccia then - local nome_giocatore = - context.myadmin_players[context.mioadmin_sel_id] - if player_name then - minetest.chat_send_player(player:get_player_name(), - "Cacciato " .. nome_giocatore) - minetest.kick_player(nome_giocatore) - end - - -- Al premere "Caccia e Banna" - elseif fields.banna then - local nome_giocatore = - context.myadmin_players[context.mioadmin_sel_id] - if player_name then - minetest.chat_send_player(player:get_player_name(), - "Banned " .. player_name) - minetest.ban_player(nome_giocatore) - minetest.kick_player(nome_giocatore, "Banned") - end - end -end, -``` - -C'è, tuttavia, un problema abbastanza grande a riguardo: chiunque può cacciare o bannare i giocatori! -C'è bisogno di un modo per mostrare questa finestra solo a chi ha i privilegi `kick` e `ban`. -Fortunatamente, SFINV ci permette di farlo! - -## Condizioni per la visualizzazione - -Si può aggiungere una funzione `is_in_nav` nella definizione della pagina se si desidera gestire quando la pagina deve essere mostrata: - -```lua -is_in_nav = function(self, player, context) - local privs = minetest.get_player_privs(player:get_player_name()) - return privs.kick or privs.ban -end, -``` - -Se si ha bisogno di controllare un solo privilegio o si vuole eseguire un `and`, si bisognerebbe usare `minetest.check_player_privs()` al posto di `get_player_privs`. - -Tieni a mente che `is_in_nav` viene chiamato soltanto alla generazione dell'inventario del giocatore. -Questo succede quando un giocatore entra in gioco, si muove tra le finestre, o una mod richiede a SFINV di rigenerare l'inventario. - -Ciò significa che hai bisogno di richiedere manualmente la rigenerazione del formspec dell'inventario per ogni evento che potrebbe cambiare il risultato ti `is_in_nav`. -Nel nostro caso, abbiamo bisogno di farlo ogni volta che i permessi `kick` o `ban` vengono assegnati/revocati a un giocatore: - -```lua -local function al_cambio_privilegi(nome_target, nome_garante, priv) - if priv ~= "kick" and priv ~= "ban" then - return - end - - local giocatore = minetest.get_player_by_name(nome_target) - if not giocatore then - return - end - - local contesto = sfinv.get_or_create_context(giocatore) - if contesto.page ~= "mioadmin:mioadmin" then - return - end - - sfinv.set_player_inventory_formspec(giocatore, contesto) -end - -minetest.register_on_priv_grant(al_cambio_privilegi) -minetest.register_on_priv_revoke(al_cambio_privilegi) -``` - -## Callback on_enter e on_leave - -Un giocatore *entra* in una finestra quando la finestra è selezionata e *esce* dalla finestra quando un'altra finestra è prossima a essere selezionata. -Attenzione che è possibile selezionare più pagine alla volta se viene usata un tema personalizzato. - -Si tenga conto, poi, che questi eventi potrebbero non essere innescati dal giocatore, in quanto potrebbe addirittura non avere un formspec aperto in quel momento. -Per esempio, `on_enter` viene chiamato dalla pagina principale anche quando un giocatore entra in gioco, ancor prima che apri l'inventario. - -Infine, non è possibile annullare il cambio pagina, in quanto potrebbe potenzialmente confondere il giocatore. - -```lua -on_enter = function(self, player, context) - -end, - -on_leave = function(self, player, context) - -end, -``` - -## Aggiungere a una pagina esistente - -Per aggiungere contenuti a una pagina che già esiste, avrai bisogno di sovrascrivere la pagina e modificare il formspec che viene ritornato: - -```lua -local vecchia_funzione = sfinv.registered_pages["sfinv:crafting"].get -sfinv.override_page("sfinv:crafting", { - get = function(self, player, context, ...) - local ret = vecchia_funzione(self, player, context, ...) - - if type(ret) == "table" then - ret.formspec = ret.formspec .. "label[0,0;Ciao]" - else - -- Retrocompatibilità - ret = ret .. "label[0,0;Ciao]" - end - - return ret - end -}) -``` diff --git a/_it/quality/luacheck.md b/_it/quality/luacheck.md index 33f9de9..931b0db 100644 --- a/_it/quality/luacheck.md +++ b/_it/quality/luacheck.md @@ -13,19 +13,18 @@ In questo capitolo, imparerai come usare uno strumento chiamato LuaCheck per sca LuaCheck può essere usato in combinazione con l'editor per fornire avvertimenti vari. - [Installare LuaCheck](#installare-luacheck) - - [Windows](#windows) - - [Linux](#linux) + - [Windows](#windows) + - [Linux](#linux) - [Eseguire LuaCheck](#eseguire-luacheck) - [Configurare LuaCheck](#configurare-luacheck) - - [Risoluzione problemi](#risoluzione-problemi) + - [Risoluzione problemi](#risoluzione-problemi) - [Uso nell'editor](#uso-nelleditor) -- [Controllare i commit con Travis](#controllare-i-commit-con-travis) ## Installare LuaCheck ### Windows -Basta scaricare luacheck.exe dall'apposita [pagina delle release su Github](https://github.com/mpeterv/luacheck/releases). +Basta scaricare luacheck.exe dall'apposita [pagina delle versioni su Github](https://github.com/mpeterv/luacheck/releases). ### Linux @@ -53,7 +52,7 @@ Su Linux, esegui `luacheck .` nella cartella principale del progetto. ## Configurare LuaCheck Crea un file chiamato .luacheckrc nella cartella principale del tuo progetto. -Questa può essere quella di un gioco, di una modpack o di una mod. +Questa può essere quella di un gioco, di un pacchetto mod o di una mod singola. Inserisci il seguente codice all'interno: @@ -80,7 +79,7 @@ read_globals = { Poi, avrai bisogno di assicurarti che funzioni eseguendo LuaCheck: dovresti ottenere molti meno errori questa volta. Partendo dal primo errore, modifica il codice per risolvere il problema, o modifica la configurazione di LuaCheck se il codice è corretto. -Dai un occhio alla lista sottostante. +Dài un occhio alla lista sottostante. ### Risoluzione problemi @@ -96,46 +95,7 @@ Dai un occhio alla lista sottostante. È caldamente consigliato installare un'estensione per il tuo editor di fiducia che ti mostri gli errori senza eseguire alcun comando. Queste sono disponibili nella maggior parte degli editor, come: -* **Atom** - `linter-luacheck`; * **VSCode** - Ctrl+P, poi incolla: `ext install dwenegar.vscode-luacheck`; * **Sublime** - Installala usando package-control: [SublimeLinter](https://github.com/SublimeLinter/SublimeLinter), [SublimeLinter-luacheck](https://github.com/SublimeLinter/SublimeLinter-luacheck). - -## Controllare i commit con Travis - -Se il tuo progetto è pubblico ed è su Github, puoi usare TravisCI - un servizio gratuito per eseguire controlli sui commit. -Questo significa che ogni commit pushato verrà controllato secondo le impostazioni di LuaCheck, e una spunta verde o una X rossa appariranno al suo fianco per segnalare se sono stati trovati errori o meno. -Ciò è utile soprattutto per quando il tuo progetto riceve una richiesta di modifica (*pull request*) per verificare se il codice è scritto bene senza doverlo scaricare. - -Prima di tutto, vai su [travis-ci.org](https://travis-ci.org/) ed esegui l'accesso con il tuo account Github. -Dopodiché cerca la repo del tuo progetto nel tuo profilo Travis, e abilita Travis cliccando sull'apposito bottone. - -Poi, crea un file chiamato `.travis.yml` con il seguente contenuto: - -```yml -language: generic -sudo: false -addons: - apt: - packages: - - luarocks -before_install: - - luarocks install --local luacheck -script: -- $HOME/.luarocks/bin/luacheck . -notifications: - email: false -``` - -Se il tuo progetto è un gioco piuttosto che una mod o un pacchetto di mod, cambia la riga dopo `script:` con: - -```yml -- $HOME/.luarocks/bin/luacheck mods/ -``` - -Ora esegui il commit e il push su Github. -Vai alla pagina del tuo progetto e clicca su "commits". -Dovresti vedere un cerchietto arancione di fianco al commit che hai appena fatto. -Dopo un po' di tempo il cerchietto dovrebbe cambiare in una spunta verde o in una X rossa (a seconda dell'esito, come detto prima). -In entrambi i casi, puoi cliccare l'icona per vedere il resoconto dell'operazione e l'output di LuaCheck. diff --git a/_it/quality/readmore.md b/_it/quality/readmore.md index add445b..512bcd9 100644 --- a/_it/quality/readmore.md +++ b/_it/quality/readmore.md @@ -14,7 +14,6 @@ Dopo aver letto questo libro, se mastichi l'inglese dai un occhio a ciò che seg * Riferimento alla API Lua di Minetest - [versione HTML](https://minetest.gitlab.io/minetest/) | [versione solo testo](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt). -* Esplora la [Wiki Sviluppatore](http://dev.minetest.net/Main_Page). * Spulcia le [mod esistenti](https://forum.minetest.net/viewforum.php?f=11). ### Programmazione in Lua diff --git a/_it/quality/translations.md b/_it/quality/translations.md new file mode 100644 index 0000000..657a2e4 --- /dev/null +++ b/_it/quality/translations.md @@ -0,0 +1,153 @@ +--- +title: Traduzione +layout: default +root: ../.. +idx: 8.05 +marked_text_encoding: + level: info + title: Marked Text Encoding + message: | + Non hai davvero bisogno di capire come funziona il testo formattato, ma potrebbe aiutarti a capire meglio. + + ``` + "\27(T@miamod)Hello everyone!\27E" + ``` + + * `\27` è il carattere di escape - è usato per dire a Minetest di far attenzione, in quanto sta per seguire qualcosa di speciale. È usato sia per le traduzioni che per la colorazione del testo. + * `(T@miamod)` dice che il testo a seguire è traducibile usando il dominio testuale di `miamod`. + * `Hello everyone!` è il testo in inglese da tradurre, passato alla funzione di traduzione. + * `\27E` è di nuovo il carattere di escape, dove `E` è usato per segnalare che si è arrivati alla fine. +--- + +## Introduzione + +Aggiungere il supporto per le traduzioni nelle tue mod e giochi dà la possibilità a più persone di gustarsele. +Stando a Google Play, il 64% dei giocatori di Minetest Android non usano l'inglese come prima lingua. +Per quanto Minetest non tenga traccia di questo parametro nelle altre piattaforme, vien comunque da sé pensare che una buona parte di giocatrici e giocatori non siano madrelingua inglesi. + +Minetest ti permette di localizzare i tuoi contenuti in tante lingue diverse, chiedendoti il testo base in inglese, seguito da dei file di traduzione che mappano le parole/frasi equivalenti nelle altre lingue. Questo processo di traduzione è fatto a lato client, cosicché ogni giocatore possa vedere il contenuto nella propria lingua (se disponibile). + + +- [Come funziona la traduzione lato client?](#come-funziona-la-traduzione-lato-client) + - [Testo formattato](#testo-formattato) + - [File di traduzione](#file-di-traduzione) +- [Formattare una stringa](#formattare-una-stringa) +- [Buona prassi per una buona traduzione](#buona-prassi-per-una-buona-traduzione) +- [Traduzioni lato server](#traduzioni-lato-server) +- [Per concludere](#per-concludere) + + +## Come funziona la traduzione lato client? + +### Testo formattato + +Il server ha bisogno di dire ai client come tradurre il testo. +Questo accade grazie alla funzione `minetest.get_translator(dominiotestuale)`, che abbrevieremo con `S()`: + +```lua +local S = minetest.get_translator("miamod") + +minetest.register_craftitem("miamod:oggetto", { + description = S("My Item"), +}) +``` + +Il primo parametro di `get_translator` è il dominio testuale, che funge da [spazio dei nomi](https://it.wikipedia.org/wiki/Namespace). +Piuttosto che avere tutte le traduzioni di una lingua salvate nello stesso file, queste possono essere suddivise in domini testuali (un file per dominio per lingua). +È buona norma assegnare al dominio testuale lo stesso nome della mod, onde evitare conflitti tra mod diverse. + +Il testo formattato può essere usato nella maggior parte dei casi dove è richiesto un testo fatto per gli esseri umani - come i formspec, i campi di definizioni di un oggetto, `infotext` ecc. +Nel caso dei formspec, tieni presente che dovrai usare la funzione di escape `minetest.formspec_escape` per una corretta visualizzazione. + +Quando il client incontra del testo formattato, come quello passato in `description`, ne andrà a cercare il corrispettivo nel file di traduzione della lingua del giocatore. Se la ricerca non avrà avuto esito positivo, ritornerà quello in inglese. + +Nel testo formattato sono presenti il testo sorgente in inglese, il dominio testuale, e qualsivoglia altro parametro passato a `S()`. +Essenzialmente, è una codifica testuale della chiamata a `S` che contiene tutte le informazioni necessarie. + +{% include notice.html notice=page.marked_text_encoding %} + + +### File di traduzione + +I file di traduzione sono file che possono essere trovati nella cartella `locale` di ogni mod. +Al momento, l'unico formato supportato è `.tr`, ma è probabile che altri formati più tipici saranno aggiunti in futuro. +I file di traduzione devono essere nominati nel seguente modo: `[dominiotestuale].[codicelingua].tr`. + +I file `.tr` iniziano con un commento che ne specifica il dominio, seguito poi da righe che mappano il testo originale in inglese nella lingua del file. + +Per esempio, `miamod.it.tr`: + +``` +# textdomain: miamod +Hello everyone!=Ciao a tutti! +I like grapefruit=Mi piace il pompelmo +``` + +Dovresti creare dei file di traduzione basati sul codice sorgente delle tue mod/giochi, usando uno strumento come [update_translations](https://github.com/minetest-tools/update_translations). +Questo cercherà tutte le occorrenze di `S(` nel tuo codice Lua, creando in automatico un modello che traduttrici e traduttori potranno usare per tradurre nella loro lingua. +Inoltre, si prenderà cura di aggiornare i file di traduzione ogniqualvolta verranno effettuate modifiche al codice. + + +## Formattare una stringa + +Non è raro dover inserire una variabile dentro una stringa da tradurre. +È importante che il testo non sia semplicemente concatenato, in quanto impedirebbe a chi traduce di cambiare l'ordine delle variabili all'interno della frase. +Al contrario, dovresti usare il seguente sistema di formattazione: + +```lua +minetest.register_on_joinplayer(function(player) + minetest.chat_send_all(S("Everyone, say hi to @1!", player:get_player_name())) +end) +``` + +Se vuoi scrivere letteralmente `@` nella tua frase, dovrai usare una sequenza di escape scrivendo `@@`. + +Dovresti evitare di concatenare stringhe *all'interno* di una frase; piuttosto, sarebbe meglio concatenare più frasi come da esempio, in quanto permette a chi traduce di lavorare su stringhe più piccole: + +```lua +S("Hello @1!", player_name) .. " " .. S("You have @1 new messages.", #msgs) +``` + + +## Buona prassi per una buona traduzione + +* Evita di concatenare il testo, optando invece per formattare le stringhe. Questo permette a chi traduce di avere pieno controllo sull'ordine degli elementi; +* Crea i file di traduzione in automatico usando [update_translations](https://github.com/minetest-tools/update_translations); +* È cosa comune che le variabili cambino il testo circostante, per esempio tramite genere e numero. Risulta spesso difficile trovare qualcosa che si sposi bene in tutti i casi, perciò si tende a cambiare la struttura della frase in modo che risulti sempre corretta ("Hai ottenuto 3 mele" -> "Hai ottenuto mela (x3)"); +* Le traduzioni potrebbero essere molto più lunghe o molto più corte rispetto all'originale. Assicurati di lasciare sempre un po' di respiro; +* Non tutte le lingue scrivono i numeri nella stessa maniera, come per esempio `1.000` e `1'000`; +* Non dar per scontato che le altre lingue usino le maiscuole nella stessa maniera della tua. + + +## Traduzioni lato server + +Certe volte ti capiterà di voler sapere quale traduzione di una tal stringa stia venendo visualizzata dal giocatore. +Puoi usare `get_player_information` per ottenere la lingua utilizzata e `get_translated_string` per tradurne il testo formattato. + +```lua +local list = { + S("Hello world!"), + S("Potato") +} + +minetest.register_chatcommand("find", { + func = function(name, param) + local info = minetest.get_player_information(name) + local lingua = info and info.language or "en" + + for _, riga in ipairs(lista) do + local trad = minetest.get_translated_string(language, riga) + if trad:contains(query) then + return riga + end + end + end, +}) +``` + +## Per concludere + +Se ben gestita, l'API per le traduzioni permette di rendere mod e giochi più accessibili. + +Si tenga comunque conto che Minetest è in continua evoluzione e che l'API verrà probabilmente ampliata in futuro. +Per esempio, il supporto per i file di traduzione *gettext* permetterà l'utilizzo di piattaforme e strumenti consolidati come Weblate, mentre nel frattempo si sta lavorando al supporto per il genere e il numero. diff --git a/_it/quality/unit_testing.md b/_it/quality/unit_testing.md index 7071d97..99ec1db 100644 --- a/_it/quality/unit_testing.md +++ b/_it/quality/unit_testing.md @@ -13,11 +13,10 @@ Scrivere i testing d'unità per le funzioni dove vengono chiamate quelle di Mine - [Installare Busted](#installare-busted) - [Il tuo primo test](#il-tuo-primo-test) - - [init.lua](#initlua) - - [api.lua](#apilua) - - [tests/api_spec.lua](#testsapispeclua) + - [init.lua](#initlua) + - [api.lua](#apilua) + - [tests/api_spec.lua](#testsapi_speclua) - [Simulare: usare funzioni esterne](#simulare-usare-funzioni-esterne) -- [Controllare commit con Travis](#controllare-commit-con-travis) - [Conclusione](#conclusione) ## Installare Busted @@ -154,29 +153,8 @@ end) ``` -## Controllare commit con Travis - -Lo script di Travis usato nel capitolo [Controllo automatico degli errori](luacheck.html) può essere modificato per eseguire (anche) Busted - -```yml -language: generic -sudo: false -addons: - apt: - packages: - - luarocks -before_install: - - luarocks install --local luacheck && luarocks install --local busted -script: -- $HOME/.luarocks/bin/luacheck . -- $HOME/.luarocks/bin/busted . -notifications: - email: false -``` - - ## Conclusione I testing d'unità aumenteranno notevolmente la qualità e l'affidabilità di un progetto se usati adeguatamente, ma ti richiederanno di strutturare il codice in maniera diversa dal solito. -Per un esempio di mod con molti testing d'unità, vedere la [crafting di rubenwardy](https://github.com/rubenwardy/crafting). +Per un esempio di mod con molti testing d'unità, vedere la mod [*crafting* di rubenwardy](https://github.com/rubenwardy/crafting). diff --git a/_layouts/base.html b/_layouts/base.html index 6e02eee..2458bad 100644 --- a/_layouts/base.html +++ b/_layouts/base.html @@ -3,7 +3,15 @@ layout: compress --- + +{% assign pathsplit = page.url | split: '/' %} +{% assign language = pathsplit[1] %} +{% assign language_info = site.data.languages | where: "code", language %} +{% if language_info %} + +{% else %} +{% endif %} {% if page.homepage %}{% else %}{{ page.title }} - {% endif %}Minetest Modding Book @@ -13,8 +21,20 @@ layout: compress + + + {% assign oldSegment = "/" | append: language | append: "/" %} + {% for other_lang in site.data.languages %} + {% unless other_lang.code == language %} + {% assign newSegment = "/" | append: other_lang.code | append: "/" %} + + + {% endunless %} + {% endfor %} + - +
diff --git a/_layouts/default.html b/_layouts/default.html index 5965ff0..28a9734 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -8,14 +8,15 @@ layout: base {% if language == "_it" %} {% assign language = "it" %} - {% assign links = site.it | sort: "idx" %} -{% elseif language == "_de" %} + {% assign links = site.it %} +{% elsif language == "_de" %} {% assign language = "de" %} - {% assign links = site.de | sort: "idx" %} + {% assign links = site.de %} {% else %} {% assign language = "en" %} - {% assign links = site.en | sort: "idx" %} + {% assign links = site.en %} {% endif %} +{% assign links = links | where_exp: "item", "item.sitemap != false" | sort: "idx" %} {% assign num = 0 %} @@ -66,7 +67,7 @@ layout: base