Merge branch 'master' of https://gitlab.com/rubenwardy/minetest_modding_book
This commit is contained in:
commit
d584267768
@ -6,6 +6,7 @@ variables:
|
|||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- rm Gemfile.lock
|
- rm Gemfile.lock
|
||||||
|
- bundle install
|
||||||
|
|
||||||
test:
|
test:
|
||||||
stage: test
|
stage: test
|
||||||
@ -21,10 +22,8 @@ test:
|
|||||||
pages:
|
pages:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
interruptible: true
|
interruptible: true
|
||||||
before_script:
|
|
||||||
- rm Gemfile.lock
|
|
||||||
script:
|
script:
|
||||||
- bundle exec jekyll build -d public
|
- bundle exec jekyll build -d public --baseurl /minetest_modding_book
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- public
|
- public
|
||||||
|
1
Gemfile
1
Gemfile
@ -6,4 +6,5 @@ gem "webrick"
|
|||||||
group :jekyll_plugins do
|
group :jekyll_plugins do
|
||||||
gem "jekyll-sitemap"
|
gem "jekyll-sitemap"
|
||||||
gem "jekyll-redirect-from"
|
gem "jekyll-redirect-from"
|
||||||
|
gem "jekyll-sass-converter", "~> 2.0"
|
||||||
end
|
end
|
||||||
|
427
LICENSE
Normal file
427
LICENSE
Normal file
@ -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.
|
@ -12,18 +12,18 @@ redirect_from:
|
|||||||
## Introduction <!-- omit in toc -->
|
## Introduction <!-- omit in toc -->
|
||||||
|
|
||||||
Understanding the basic structure of a mod's folder is an essential skill when
|
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)
|
- [What are Games and Mods?](#what-are-games-and-mods)
|
||||||
- [Where are mods stored?](#where-are-mods-stored)
|
- [Where are mods stored?](#where-are-mods-stored)
|
||||||
- [Mod Directory](#mod-directory)
|
- [Creating your first mod](#creating-your-first-mod)
|
||||||
- [mod.conf](#modconf)
|
- [Mod directory](#mod-directory)
|
||||||
- [Dependencies](#dependencies)
|
- [mod.conf](#modconf)
|
||||||
|
- [init.lua](#initlua)
|
||||||
|
- [Summary](#summary)
|
||||||
|
- [Dependencies](#dependencies)
|
||||||
- [Mod Packs](#mod-packs)
|
- [Mod Packs](#mod-packs)
|
||||||
- [Example](#example)
|
|
||||||
- [Mod Folder](#mod-folder)
|
|
||||||
- [init.lua](#initlua)
|
|
||||||
- [mod.conf](#modconf-1)
|
|
||||||
|
|
||||||
|
|
||||||
## What are Games and Mods?
|
## What are Games and Mods?
|
||||||
@ -53,7 +53,7 @@ and is applicable for both game developers and modders.
|
|||||||
<a name="mod-locations"></a>
|
<a name="mod-locations"></a>
|
||||||
|
|
||||||
Each mod has its own directory where its Lua code, textures, models, and
|
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*.
|
mods. These locations are commonly called *mod load paths*.
|
||||||
|
|
||||||
For a given world/save game, three mod locations are checked.
|
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.
|
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.
|
Go to the global mods directory (About > Open user data directory > mods) and
|
||||||
Mod names can include letters, numbers, and underscores. A good name should
|
create a new folder called "mymod". `mymod` is the mod name.
|
||||||
describe what the mod does, and the directory which contains the components of a mod
|
|
||||||
must have the same name as the mod name.
|
Each mod should have a unique *mod name*, a technical identifier (id) used to
|
||||||
To find out if a mod name is available, try searching for it on
|
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).
|
[content.minetest.net](https://content.minetest.net).
|
||||||
|
|
||||||
mymod
|
mymod
|
||||||
├── init.lua (required) - Runs when the game loads.
|
├── textures
|
||||||
├── mod.conf (recommended) - Contains description and dependencies.
|
│ └── mymod_node.png files
|
||||||
├── textures (optional)
|
├── init.lua
|
||||||
│ └── ... any textures or images
|
└── mod.conf
|
||||||
├── sounds (optional)
|
|
||||||
│ └── ... any sounds
|
|
||||||
└── ... any other files or directories
|
|
||||||
|
|
||||||
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
|
however, mod.conf is recommended and other components may be needed
|
||||||
depending on the mod's functionality.
|
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
|
This file is used for mod metadata including the mod's name, description, and other
|
||||||
information.
|
information.
|
||||||
|
|
||||||
For example:
|
### init.lua
|
||||||
|
|
||||||
name = mymod
|
Create an init.lua file with the following content:
|
||||||
description = Adds foo, bar, and bo.
|
|
||||||
depends = modone, modtwo
|
|
||||||
|
|
||||||
### 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.
|
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
|
One mod may require another mod's code, items, or other resources to be
|
||||||
for it to use.
|
available for it to use.
|
||||||
|
|
||||||
There are two types of dependencies: hard and optional dependencies.
|
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
|
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
|
available, a hard dependency will cause the mod to fail to load, while an optional
|
||||||
dependency might lead to fewer features being enabled.
|
dependency might lead to fewer features being enabled.
|
||||||
|
|
||||||
An optional dependency is useful if you want to optionally support another mod; it can
|
An optional dependency is useful if you want to optionally support another mod;
|
||||||
enable extra content if the user wishes to use both the mods at the same time.
|
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.
|
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*.
|
Please note that a modpack is not a *game*.
|
||||||
Games have their own organisational structure which will be explained in the
|
Games have their own organisational structure which will be explained in the
|
||||||
Games chapter.
|
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.
|
|
||||||
|
@ -9,21 +9,39 @@ redirect_from: /en/chapters/lua.html
|
|||||||
|
|
||||||
## Introduction <!-- omit in toc -->
|
## Introduction <!-- omit in toc -->
|
||||||
|
|
||||||
In this chapter we'll talk about scripting in Lua, the tools required
|
In this chapter, you'll learn about scripting in Lua, the tools required
|
||||||
to assist with this, and some techniques which you may find useful.
|
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)
|
- [Programming](#programming)
|
||||||
|
- [Coding in Lua](#coding-in-lua)
|
||||||
|
- [Code Editors](#code-editors)
|
||||||
- [Local and Global Scope](#local-and-global-scope)
|
- [Local and Global Scope](#local-and-global-scope)
|
||||||
- [Locals should be used as much as possible](#locals-should-be-used-as-much-as-possible)
|
- [Locals should be used as much as possible](#locals-should-be-used-as-much-as-possible)
|
||||||
- [Including other Lua Scripts](#including-other-lua-scripts)
|
- [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
|
## Code Editors
|
||||||
|
|
||||||
A code editor with code highlighting is sufficient for writing scripts in Lua.
|
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:
|
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
|
open source (as Code-OSS or VSCodium), popular, and has
|
||||||
[plugins for Minetest modding](https://marketplace.visualstudio.com/items?itemName=GreenXenith.minetest-tools).
|
[plugins for Minetest](https://marketplace.visualstudio.com/items?itemName=GreenXenith.minetest-tools).
|
||||||
* [Notepad++](http://notepad-plus-plus.org/) - Windows-only
|
* [Notepad++](http://notepad-plus-plus.org/): simple, Windows-only
|
||||||
* [Atom](http://atom.io/)
|
|
||||||
|
|
||||||
Other suitable editors are also available.
|
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 = 2<sup>2</sup> = 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
|
## Local and Global Scope
|
||||||
|
|
||||||
Whether a variable is local or global determines where it can be written to or
|
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
|
read from. Global variables can be accessed from anywhere in the script file,
|
||||||
are some examples:
|
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
|
```lua
|
||||||
-- Accessible from within this script file
|
-- Accessible from within this script file
|
||||||
@ -220,21 +118,8 @@ function myfunc()
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
In contrast, global variables can be accessed from anywhere in the script file, and
|
|
||||||
from any other mod.
|
|
||||||
|
|
||||||
```lua
|
### Locals should be used as much as possible
|
||||||
function one()
|
|
||||||
foo = "bar"
|
|
||||||
end
|
|
||||||
|
|
||||||
function two()
|
|
||||||
print(dump(foo)) -- Output: "bar"
|
|
||||||
end
|
|
||||||
|
|
||||||
one()
|
|
||||||
two()
|
|
||||||
```
|
|
||||||
|
|
||||||
Local variables should be used whenever possible. Mods should only create one
|
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
|
global at most, with the same name as the mod. Creating other globals is sloppy
|
||||||
@ -284,6 +169,9 @@ end
|
|||||||
mymod.foo("foobar")
|
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
|
## Including other Lua Scripts
|
||||||
|
|
||||||
The recommended way to include other Lua scripts in a mod is to use *dofile*.
|
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
|
```lua
|
||||||
-- script.lua
|
-- script.lua
|
||||||
return "Hello world!"
|
local module = {}
|
||||||
|
module.message = "Hello World!"
|
||||||
|
return module
|
||||||
|
|
||||||
-- init.lua
|
-- init.lua
|
||||||
local ret = dofile(minetest.get_modpath("modname") .. "/script.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
|
[Later chapters](../quality/clean_arch.html) will discuss how best to split up
|
||||||
|
206
_en/items/callbacks.md
Normal file
206
_en/items/callbacks.md
Normal file
@ -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 <!-- omit in toc -->
|
||||||
|
|
||||||
|
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.
|
@ -25,8 +25,10 @@ available, which cover pixel art in much more detail.
|
|||||||
- [Using the Pencil](#using-the-pencil)
|
- [Using the Pencil](#using-the-pencil)
|
||||||
- [Tiling](#tiling)
|
- [Tiling](#tiling)
|
||||||
- [Transparency](#transparency)
|
- [Transparency](#transparency)
|
||||||
|
- [Color Palettes](#color-palettes)
|
||||||
- [Editors](#editors)
|
- [Editors](#editors)
|
||||||
- [MS Paint](#ms-paint)
|
- [MS Paint](#ms-paint)
|
||||||
|
- [Aseprite / LibreSprite](#aseprite--libresprite)
|
||||||
- [GIMP](#gimp)
|
- [GIMP](#gimp)
|
||||||
|
|
||||||
## Techniques
|
## Techniques
|
||||||
@ -59,6 +61,13 @@ and some nodes, such as glass.
|
|||||||
Not all editors support transparency, so make sure you choose an
|
Not all editors support transparency, so make sure you choose an
|
||||||
editor which is suitable for the textures you wish to create.
|
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
|
## Editors
|
||||||
|
|
||||||
### MS Paint
|
### 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
|
but if you need transparency in your textures you should choose a
|
||||||
different editor.
|
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
|
||||||
|
|
||||||
GIMP is commonly used in the Minetest community. It has quite a high
|
GIMP is commonly used in the Minetest community. It has quite a high
|
||||||
learning curve because many of its features are not immediately
|
learning curve because many of its features are not immediately
|
||||||
obvious.
|
obvious.
|
||||||
|
|
||||||
When using GIMP, the pencil tool can be selected from the Toolbox:
|
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
|
||||||
<figure>
|
tool.
|
||||||
<img src="{{ page.root }}//static/pixel_art_gimp_pencil.png" alt="Pencil in GIMP">
|
|
||||||
</figure>
|
|
||||||
|
|
||||||
It's also advisable to select the Hard edge checkbox for the eraser tool.
|
|
||||||
|
@ -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)
|
- [What are ItemStacks and Inventories?](#what-are-itemstacks-and-inventories)
|
||||||
- [ItemStacks](#itemstacks)
|
- [ItemStacks](#itemstacks)
|
||||||
- [Inventory Locations](#inventory-locations)
|
- [Inventory Locations](#inventory-locations)
|
||||||
|
- [Node Inventories](#node-inventories)
|
||||||
|
- [Player Inventories](#player-inventories)
|
||||||
|
- [Detached Inventories](#detached-inventories)
|
||||||
- [Lists](#lists)
|
- [Lists](#lists)
|
||||||
- [Size and Width](#size-and-width)
|
- [Size and Width](#size-and-width)
|
||||||
- [Checking Contents](#checking-contents)
|
- [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 ItemStack is the data behind a single cell in an inventory.
|
||||||
|
|
||||||
An *inventory* is a collection of *inventory lists*, each of which
|
An *inventory* is a collection of *inventory lists*, each of which is a 2D grid
|
||||||
is a 2D grid of ItemStacks.
|
of ItemStacks. Inventory lists are referred to as *lists* in the context of
|
||||||
Inventory lists are simply called *lists* in the context
|
inventories.
|
||||||
of inventories.
|
|
||||||
The point of an inventory is to allow multiple grids when Players
|
Players and nodes only have a single inventory; lists enable you to have
|
||||||
and Nodes only have at most one inventory in them.
|
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
|
||||||
|
|
||||||
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
|
The item name may be the item name of a registered item, an alias, or an unknown
|
||||||
item name.
|
item name. Unknown items are common when users uninstall mods, or when mods
|
||||||
Unknown items are common when users uninstall mods, or when mods remove items without
|
remove items without precautions, such as registering aliases.
|
||||||
precautions, such as registering aliases.
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
print(stack:get_name())
|
print(stack:get_name())
|
||||||
@ -58,19 +61,14 @@ if not stack:is_known() then
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
The count will always be 0 or greater.
|
The count will always be 0 or greater. Through normal gameplay, the count should
|
||||||
Through normal gameplay, the count should be no more than the maximum stack size
|
be no more than the maximum stack size of the item - `stack_max`. However, admin
|
||||||
of the item - `stack_max`.
|
commands and buggy mods may result in stacks exceeding the maximum size.
|
||||||
However, admin commands and buggy mods may result in stacks exceeding the maximum
|
|
||||||
size.
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
print(stack:get_stack_max())
|
print(stack:get_stack_max())
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
An ItemStack can be empty, in which case the count will be 0.
|
An ItemStack can be empty, in which case the count will be 0.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
@ -78,7 +76,7 @@ print(stack:get_count())
|
|||||||
stack:set_count(10)
|
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
|
```lua
|
||||||
ItemStack() -- name="", count=0
|
ItemStack() -- name="", count=0
|
||||||
@ -87,24 +85,30 @@ ItemStack("default:stone 30")
|
|||||||
ItemStack({ name = "default:wood", count = 10 })
|
ItemStack({ name = "default:wood", count = 10 })
|
||||||
```
|
```
|
||||||
|
|
||||||
Item metadata is an unlimited key-value store for data about the item.
|
Item metadata is an unlimited key-value store for data about the item. Key-value
|
||||||
Key-value means that you use a name (called the key) to access the data (called the value).
|
means that you use a name (called the key) to access the data (called the
|
||||||
Some keys have special meaning, such as `description` which is used to have a per-stack
|
value). Some keys have special meaning, such as `description` which is used to
|
||||||
item description.
|
have a per-stack item description. This will be covered in more detail in the
|
||||||
This will be covered in more detail in the Metadata and Storage chapter.
|
[Storage and Metadata](../map/storage.html) chapter.
|
||||||
|
|
||||||
## Inventory Locations
|
## Inventory Locations
|
||||||
|
|
||||||
An Inventory Location is where and how the inventory is stored.
|
An Inventory Location is where and how the inventory is stored. There are three
|
||||||
There are three types of inventory location: player, node, and detached.
|
types of inventory location: player, node, and detached. An inventory is
|
||||||
An inventory is directly tied to one and only one location - updating the inventory
|
directly tied to one and only one location - updating the inventory will cause
|
||||||
will cause it to update immediately.
|
it to update immediately.
|
||||||
|
|
||||||
Node inventories are related to the position of a specific node, such as a chest.
|
### Node Inventories
|
||||||
The node must be loaded because it is stored in [node metadata](../map/storage.html#metadata).
|
|
||||||
|
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
|
```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*.
|
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()
|
local location = inv:get_location()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Player Inventories
|
||||||
|
|
||||||
Player inventories can be obtained similarly or using a player reference.
|
Player inventories can be obtained similarly or using a player reference.
|
||||||
The player must be online to access their inventory.
|
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()
|
local inv = player:get_inventory()
|
||||||
```
|
```
|
||||||
|
|
||||||
A detached inventory is one which is independent of players or nodes.
|
### Detached Inventories
|
||||||
Detached inventories also don't save over a restart.
|
|
||||||
|
A detached inventory is one that is independent of players or nodes. Detached
|
||||||
|
inventories also don't save over a restart.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local inv = minetest.get_inventory({
|
local inv = minetest.get_inventory({
|
||||||
@ -142,10 +150,9 @@ before accessing it:
|
|||||||
minetest.create_detached_inventory("inventory_name")
|
minetest.create_detached_inventory("inventory_name")
|
||||||
```
|
```
|
||||||
|
|
||||||
The create_detached_inventory function accepts 3 arguments, where only the first - the inventory name -
|
The `create_detached_inventory` function accepts 3 arguments, where only the
|
||||||
is required.
|
first - the inventory name - is required. The second argument takes a table of
|
||||||
The second argument takes a table of callbacks, which can be used to control how
|
callbacks, which can be used to control how players interact with the inventory:
|
||||||
players interact with the inventory:
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- Input only detached inventory
|
-- Input only detached inventory
|
||||||
@ -177,9 +184,10 @@ On the contrary, action callbacks - starting with `on_` - don't have a return va
|
|||||||
|
|
||||||
## Lists
|
## Lists
|
||||||
|
|
||||||
Inventory Lists are a concept used to allow multiple grids to be stored inside a single location.
|
Inventory Lists are a concept used to allow multiple grids to be stored inside a
|
||||||
This is especially useful for the player as there are a number of common lists
|
single location. This is especially useful for the player as there are several
|
||||||
which all games have, such as the *main* inventory and *craft* slots.
|
common lists that all games have, such as the *main* inventory and *craft*
|
||||||
|
slots.
|
||||||
|
|
||||||
### Size and Width
|
### Size and Width
|
||||||
|
|
||||||
|
@ -18,8 +18,6 @@ basic requirements for many mods.
|
|||||||
- [Item Aliases](#item-aliases)
|
- [Item Aliases](#item-aliases)
|
||||||
- [Textures](#textures)
|
- [Textures](#textures)
|
||||||
- [Registering a basic node](#registering-a-basic-node)
|
- [Registering a basic node](#registering-a-basic-node)
|
||||||
- [Actions and Callbacks](#actions-and-callbacks)
|
|
||||||
- [on_use](#onuse)
|
|
||||||
- [Crafting](#crafting)
|
- [Crafting](#crafting)
|
||||||
- [Shaped](#shaped)
|
- [Shaped](#shaped)
|
||||||
- [Shapeless](#shapeless)
|
- [Shapeless](#shapeless)
|
||||||
@ -29,25 +27,24 @@ basic requirements for many mods.
|
|||||||
|
|
||||||
## What are Nodes and Items?
|
## What are Nodes and Items?
|
||||||
|
|
||||||
Nodes, craftitems, and tools are all Items.
|
Nodes, craftitems, and tools are all Items. An item is something that could be
|
||||||
An item is something that could be found in an inventory -
|
found in an inventory - even if it isn't possible through normal gameplay.
|
||||||
even though it may not be possible through normal gameplay.
|
|
||||||
|
|
||||||
A node is an item which can be placed or be found in the world.
|
A node is an item that can be placed or be found in the world. Every position
|
||||||
Every position in the world must be occupied with one and only one node -
|
in the world must be occupied with one and only one node - seemingly blank
|
||||||
seemingly blank positions are usually air nodes.
|
positions are usually air nodes.
|
||||||
|
|
||||||
A craftitem can't be placed and is only found in inventories or as a dropped item
|
A craftitem can't be placed and is only found in inventories or as a dropped item
|
||||||
in the world.
|
in the world.
|
||||||
|
|
||||||
A tool has the ability to wear and typically has non-default digging capabilities.
|
A tool has the ability to wear and typically has non-default digging
|
||||||
In the future, it's likely that craftitems and tools will merge into one type of
|
capabilities. In the future, it's likely that craftitems and tools will merge
|
||||||
item, as the distinction between them is rather artificial.
|
into one type of item, as the distinction between them is rather artificial.
|
||||||
|
|
||||||
## Registering Items
|
## Registering Items
|
||||||
|
|
||||||
Item definitions consist of an *item name* and a *definition table*.
|
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
|
```lua
|
||||||
minetest.register_craftitem("modname:itemname", {
|
minetest.register_craftitem("modname:itemname", {
|
||||||
@ -63,25 +60,26 @@ following format:
|
|||||||
|
|
||||||
modname:itemname
|
modname:itemname
|
||||||
|
|
||||||
The modname is the name of the mod in which the item is registered, and the
|
The modname is the name of the mod in which the item is registered, and the item
|
||||||
item name is the name of the item itself.
|
name is the name of the item itself. The item name should be relevant to what
|
||||||
The item name should be relevant to what the item is and can't already be registered.
|
the item is and can't already be registered.
|
||||||
|
|
||||||
|
Both `modname` and `itemname` should only contain lowercase letters, numbers,
|
||||||
|
and underscores.
|
||||||
|
|
||||||
### Item Aliases
|
### Item Aliases
|
||||||
|
|
||||||
Items can also have *aliases* pointing to their name.
|
Items can also have *aliases* pointing to their name. An *alias* is a
|
||||||
An *alias* is a pseudo-item name which results in the engine treating any
|
pseudo-item name that results in the engine treating any occurrences of the
|
||||||
occurrences of the alias as if it were the item name.
|
alias as if it were the item name. There are two main common uses of this:
|
||||||
There are two main common uses of this:
|
|
||||||
|
|
||||||
* Renaming removed items to something else.
|
* Renaming removed items to something else.
|
||||||
There may be unknown nodes in the world and in inventories if an item is
|
There may be unknown nodes in the world and in inventories if an item is
|
||||||
removed from a mod without any corrective code.
|
removed from a mod without any corrective code.
|
||||||
* Adding a shortcut. `/giveme dirt` is easier than `/giveme default:dirt`.
|
* Adding a shortcut. `/giveme dirt` is easier than `/giveme default:dirt`.
|
||||||
|
|
||||||
Registering an alias is pretty simple.
|
Registering an alias is pretty simple. A good way to remember the order of the
|
||||||
A good way to remember the order of the arguments is `from → to` where
|
arguments is `from → to` where *from* is the alias and *to* is the target.
|
||||||
*from* is the alias and *to* is the target.
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
minetest.register_alias("dirt", "default:dirt")
|
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.
|
bad quality at low resolutions.
|
||||||
It is often better to use the PNG format.
|
It is often better to use the PNG format.
|
||||||
|
|
||||||
Textures in Minetest are usually 16 by 16 pixels.
|
Textures in Minetest are usually 16 by 16 pixels. They can be any resolution,
|
||||||
They can be any resolution, but it is recommended that they are in the order of 2,
|
but it is recommended that they are in the order of 2, for example, 16, 32, 64,
|
||||||
for example, 16, 32, 64, or 128.
|
or 128. This is because other resolutions may not be supported correctly on
|
||||||
This is because other resolutions may not be supported correctly on older devices,
|
older devices, especially phones, resulting in degraded performance.
|
||||||
resulting in decreased performance.
|
|
||||||
|
|
||||||
## Registering a basic node
|
## Registering a basic node
|
||||||
|
|
||||||
|
Registering nodes is similar to registering items, just with a different
|
||||||
|
function:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
minetest.register_node("mymod:diamond", {
|
minetest.register_node("mymod:diamond", {
|
||||||
description = "Alien 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.
|
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.
|
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:
|
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)
|
(+Y, -Y, +X, -X, +Z, -Z)
|
||||||
|
|
||||||
Remember that +Y is upwards in Minetest, as is the convention with
|
Remember that +Y is upwards in Minetest, as is the convention with
|
||||||
3D computer graphics.
|
most 3D computer games.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
minetest.register_node("mymod:diamond", {
|
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.
|
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.
|
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
|
## Crafting
|
||||||
|
|
||||||
There are several types of crafting recipe available, indicated by the `type`
|
There are several types of crafting recipe available, indicated by the `type`
|
||||||
@ -327,6 +287,7 @@ minetest.register_craft({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Tools, Capabilities, and Dig Types
|
## Tools, Capabilities, and Dig Types
|
||||||
|
|
||||||
Dig types are groups which are used to define how strong a node is when dug
|
Dig types are groups which are used to define how strong a node is when dug
|
||||||
|
@ -202,7 +202,7 @@ local function emerge_callback(pos, action,
|
|||||||
-- Send progress message
|
-- Send progress message
|
||||||
if context.total_blocks == context.loaded_blocks then
|
if context.total_blocks == context.loaded_blocks then
|
||||||
minetest.chat_send_all("Finished loading blocks!")
|
minetest.chat_send_all("Finished loading blocks!")
|
||||||
end
|
else
|
||||||
local perc = 100 * context.loaded_blocks / context.total_blocks
|
local perc = 100 * context.loaded_blocks / context.total_blocks
|
||||||
local msg = string.format("Loading blocks %d/%d (%.2f%%)",
|
local msg = string.format("Loading blocks %d/%d (%.2f%%)",
|
||||||
context.loaded_blocks, context.total_blocks, perc)
|
context.loaded_blocks, context.total_blocks, perc)
|
||||||
|
@ -80,7 +80,7 @@ minetest.register_abm({
|
|||||||
nodenames = {"default:dirt_with_grass"},
|
nodenames = {"default:dirt_with_grass"},
|
||||||
neighbors = {"default:water_source", "default:water_flowing"},
|
neighbors = {"default:water_source", "default:water_flowing"},
|
||||||
interval = 10.0, -- Run every 10 seconds
|
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,
|
action = function(pos, node, active_object_count,
|
||||||
active_object_count_wider)
|
active_object_count_wider)
|
||||||
local pos = {x = pos.x, y = pos.y + 1, z = pos.z}
|
local pos = {x = pos.x, y = pos.y + 1, z = pos.z}
|
||||||
|
@ -8,19 +8,21 @@ redirect_from: /en/chapters/chat.html
|
|||||||
cmd_online:
|
cmd_online:
|
||||||
level: warning
|
level: warning
|
||||||
title: Offline players can run commands
|
title: Offline players can run commands
|
||||||
message: <p>A player name is passed instead of a player object because mods
|
message: |
|
||||||
can run commands on behalf of offline players. For example, the IRC
|
A player name is passed instead of a player object because mods
|
||||||
bridge allows players to run commands without joining the game.</p>
|
can run commands on behalf of offline players. For example, the IRC
|
||||||
|
bridge allows players to run commands without joining the game.
|
||||||
|
|
||||||
<p>So make sure that you don't assume that the player is online.
|
So make sure that you don't assume that the player is online.
|
||||||
You can check by seeing if <pre>minetest.get_player_by_name</pre> returns a player.</p>
|
You can check by seeing if `minetest.get_player_by_name` returns a player.
|
||||||
|
|
||||||
cb_cmdsprivs:
|
cb_cmdsprivs:
|
||||||
level: warning
|
level: warning
|
||||||
title: Privileges and Chat Commands
|
title: Privileges and Chat Commands
|
||||||
message: The shout privilege isn't needed for a player to trigger this callback.
|
message: |
|
||||||
This is because chat commands are implemented in Lua, and are just
|
The shout privilege isn't needed for a player to trigger this callback.
|
||||||
chat messages that begin with a /.
|
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
|
Mods can interact with player chat, including
|
||||||
sending messages, intercepting messages, and registering chat commands.
|
sending messages, intercepting messages, and registering chat commands.
|
||||||
|
|
||||||
- [Sending Messages to All Players](#sending-messages-to-all-players)
|
- [Sending Messages](#sending-messages)
|
||||||
- [Sending Messages to Specific Players](#sending-messages-to-specific-players)
|
- [To All Players](#to-all-players)
|
||||||
|
- [To Specific Players](#to-specific-players)
|
||||||
- [Chat Commands](#chat-commands)
|
- [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)
|
- [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
|
```lua
|
||||||
minetest.chat_send_all("This is a chat message to all players")
|
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.
|
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
|
```lua
|
||||||
minetest.chat_send_player("player1", "This is a chat message for player1")
|
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
|
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.
|
[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,
|
Chat commands can return up to two values,
|
||||||
the first being a Boolean indicating success, and the second being a
|
the first being a Boolean indicating success, and the second being a
|
||||||
message to send to the user.
|
message to send to the user.
|
||||||
|
|
||||||
{% include notice.html notice=page.cmd_online %}
|
{% include notice.html notice=page.cmd_online %}
|
||||||
|
|
||||||
## Complex Subcommands
|
### Accepting Multiple Arguments
|
||||||
|
|
||||||
It is often required to make complex chat commands, such as:
|
<a name="complex-subcommands"></a>
|
||||||
|
|
||||||
* `/msg <to> <message>`
|
`param` gives you all the arguments to a chat command in a single string. It's
|
||||||
* `/team join <teamname>`
|
common for chat commands to need to extract multiple arguments. There are two
|
||||||
* `/team leave <teamname>`
|
ways of doing this, either using Minetest's string split or Lua patterns.
|
||||||
* `/team list`
|
|
||||||
|
|
||||||
This is usually done using [Lua patterns](https://www.lua.org/pil/20.2.html).
|
#### Using string.split
|
||||||
Patterns are a way of extracting stuff from text using rules.
|
|
||||||
|
A string can be split up into words using `string.split(" ")`:
|
||||||
|
|
||||||
```lua
|
```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 <team_name> <number>"
|
||||||
|
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 <to> <message>`. Let's go through left to right:
|
The above code implements `/msg <to> <message>`. 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)
|
[lua-users.org tutorial](http://lua-users.org/wiki/PatternsTutorial)
|
||||||
or the [PIL documentation](https://www.lua.org/pil/20.2.html).
|
or the [PIL documentation](https://www.lua.org/pil/20.2.html).
|
||||||
|
|
||||||
<p class="book_hide">
|
|
||||||
There is also a library written by the author of this book which can be used
|
|
||||||
to make complex chat commands without patterns called
|
|
||||||
<a href="https://gitlab.com/rubenwardy/ChatCmdBuilder">Chat Command Builder</a>.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
## Intercepting Messages
|
## Intercepting Messages
|
||||||
|
|
||||||
To intercept a message, use register_on_chat_message:
|
To intercept a message, use register_on_chat_message:
|
||||||
|
@ -35,16 +35,16 @@ unexpected windows tend to disrupt gameplay.
|
|||||||
|
|
||||||
- [Real or Legacy Coordinates](#real-or-legacy-coordinates)
|
- [Real or Legacy Coordinates](#real-or-legacy-coordinates)
|
||||||
- [Anatomy of a Formspec](#anatomy-of-a-formspec)
|
- [Anatomy of a Formspec](#anatomy-of-a-formspec)
|
||||||
- [Elements](#elements)
|
- [Elements](#elements)
|
||||||
- [Header](#header)
|
- [Header](#header)
|
||||||
- [Guessing Game](#guessing-game)
|
- [Guessing Game](#guessing-game)
|
||||||
- [Padding and Spacing](#padding-and-spacing)
|
- [Padding and Spacing](#padding-and-spacing)
|
||||||
- [Receiving Formspec Submissions](#receiving-formspec-submissions)
|
- [Receiving Formspec Submissions](#receiving-formspec-submissions)
|
||||||
- [Contexts](#contexts)
|
- [Contexts](#contexts)
|
||||||
- [Formspec Sources](#formspec-sources)
|
- [Formspec Sources](#formspec-sources)
|
||||||
- [Node Meta Formspecs](#node-meta-formspecs)
|
- [Node Meta Formspecs](#node-meta-formspecs)
|
||||||
- [Player Inventory Formspecs](#player-inventory-formspecs)
|
- [Player Inventory Formspecs](#player-inventory-formspecs)
|
||||||
- [Your Turn](#your-turn)
|
- [Your Turn](#your-turn)
|
||||||
|
|
||||||
|
|
||||||
## Real or Legacy Coordinates
|
## 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.
|
Notice how we explicitly defined the formspec language version.
|
||||||
Without this, the legacy system will instead be used instead - which will
|
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 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
|
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
|
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
|
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.
|
name, depending on the event. Some elements will only be submitted if they caused
|
||||||
For example, a button element will only appear in fields if that particular button
|
the event, such as buttons, and some elements will always appear in submissions,
|
||||||
was pressed.
|
such as fields.
|
||||||
|
|
||||||
{% include notice.html notice=page.submit_vuln %}
|
{% 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
|
The global callback is used to receive events from this formspec, and the
|
||||||
formname is `""`.
|
formname is `""`.
|
||||||
|
|
||||||
There are a number of different mods which allow multiple mods to customise
|
There are a number of different mods which allow multiple mods to customise the
|
||||||
the player inventory. The officially recommended mod is
|
player inventory. Minetest Game uses
|
||||||
[Simple Fast Inventory (sfinv)](sfinv.html), and is included in Minetest Game.
|
[SFINV](https://github.com/rubenwardy/sfinv/blob/master/Tutorial.md).
|
||||||
|
|
||||||
|
|
||||||
### Your Turn
|
### Your Turn
|
||||||
|
@ -1,242 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: "SFINV: Inventory Formspec"
|
sitemap: false
|
||||||
layout: default
|
|
||||||
root: ../..
|
|
||||||
idx: 4.7
|
|
||||||
redirect_from: /en/chapters/sfinv.html
|
redirect_from: /en/chapters/sfinv.html
|
||||||
|
redirect_to: "https://github.com/rubenwardy/sfinv/blob/master/Tutorial.md"
|
||||||
---
|
---
|
||||||
|
|
||||||
## Introduction <!-- omit in toc -->
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
<figure>
|
|
||||||
<img src="{{ page.root }}//static/sfinv_admin_fs.png" alt="Player Admin Page">
|
|
||||||
</figure>
|
|
||||||
|
|
||||||
## 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
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
@ -24,7 +24,7 @@ The methods of ObjectRefs will always return nil when invalid, since Minetest 5.
|
|||||||
Any call will essentially be ignored.
|
Any call will essentially be ignored.
|
||||||
|
|
||||||
You should avoid storing ObjectRefs where possible. If you do to store an
|
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
|
```lua
|
||||||
-- This only works in Minetest 5.2+
|
-- This only works in Minetest 5.2+
|
||||||
|
@ -20,7 +20,6 @@ editor to provide alerts to any mistakes.
|
|||||||
- [Configuring LuaCheck](#configuring-luacheck)
|
- [Configuring LuaCheck](#configuring-luacheck)
|
||||||
- [Troubleshooting](#troubleshooting)
|
- [Troubleshooting](#troubleshooting)
|
||||||
- [Using with editor](#using-with-editor)
|
- [Using with editor](#using-with-editor)
|
||||||
- [Checking Commits with Travis](#checking-commits-with-travis)
|
|
||||||
|
|
||||||
## Installing LuaCheck
|
## 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
|
to show you errors without running a command. Most editors will likely have a plugin
|
||||||
available.
|
available.
|
||||||
|
|
||||||
* **Atom** - `linter-luacheck`.
|
|
||||||
* **VSCode** - Ctrl+P, then paste: `ext install dwenegar.vscode-luacheck`
|
* **VSCode** - Ctrl+P, then paste: `ext install dwenegar.vscode-luacheck`
|
||||||
* **Sublime** - Install using package-control:
|
* **Sublime** - Install using package-control:
|
||||||
[SublimeLinter](https://github.com/SublimeLinter/SublimeLinter),
|
[SublimeLinter](https://github.com/SublimeLinter/SublimeLinter),
|
||||||
[SublimeLinter-luacheck](https://github.com/SublimeLinter/SublimeLinter-luacheck).
|
[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.
|
|
||||||
|
@ -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) |
|
* 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).
|
[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).
|
* Look at [existing mods](https://forum.minetest.net/viewforum.php?f=11).
|
||||||
|
|
||||||
### Lua Programming
|
### Lua Programming
|
||||||
|
198
_en/quality/translations.md
Normal file
198
_en/quality/translations.md
Normal file
@ -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 <!-- omit in toc -->
|
||||||
|
|
||||||
|
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.
|
@ -17,9 +17,8 @@ we discussed how to structure your code avoid this.
|
|||||||
- [Your First Test](#your-first-test)
|
- [Your First Test](#your-first-test)
|
||||||
- [init.lua](#initlua)
|
- [init.lua](#initlua)
|
||||||
- [api.lua](#apilua)
|
- [api.lua](#apilua)
|
||||||
- [tests/api_spec.lua](#testsapispeclua)
|
- [tests/api_spec.lua](#testsapi_speclua)
|
||||||
- [Mocking: Using External Functions](#mocking-using-external-functions)
|
- [Mocking: Using External Functions](#mocking-using-external-functions)
|
||||||
- [Checking Commits with Travis](#checking-commits-with-travis)
|
|
||||||
- [Conclusion](#conclusion)
|
- [Conclusion](#conclusion)
|
||||||
|
|
||||||
## Installing Busted
|
## 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
|
## Conclusion
|
||||||
|
|
||||||
Unit tests will greatly increase the quality and reliability of your project if used
|
Unit tests will greatly increase the quality and reliability of your project if used
|
||||||
|
@ -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.
|
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](#programmare)
|
||||||
|
- [Programmare in Lua](#programmare-in-lua)
|
||||||
|
- [Editor di codice](#editor-di-codice)
|
||||||
- [Portata locale e globale](#portata-locale-e-globale)
|
- [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)
|
- [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
|
## Editor di codice
|
||||||
|
|
||||||
Un editor di codice con evidenziamento delle parole chiave è sufficiente per scrivere script in Lua.
|
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)
|
(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 = 2<sup>2</sup> = 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
|
## Portata locale e globale
|
||||||
|
|
||||||
L'essere locale o globale di una variabile determina da dove è possibile accederci.
|
L'essere locale o globale di una variabile determina da dove è possibile accederci.
|
||||||
@ -211,6 +106,9 @@ one()
|
|||||||
two()
|
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.
|
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ò:
|
Crearne di ulteriori è considerato cattiva programmazione, e Minetest ci avviserà di ciò:
|
||||||
|
|
||||||
|
14
_it/index.md
14
_it/index.md
@ -8,7 +8,7 @@ idx: 0.1
|
|||||||
---
|
---
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<h1>Minetest: Libro del Modding</h1>
|
<h1>Minetest: Libro del Moddaggio</h1>
|
||||||
|
|
||||||
<span>di <a href="https://rubenwardy.com" rel="author">rubenwardy</a></span>
|
<span>di <a href="https://rubenwardy.com" rel="author">rubenwardy</a></span>
|
||||||
<span>con modifiche di <a href="http://rc.minetest.tv/">Shara</a></span>
|
<span>con modifiche di <a href="http://rc.minetest.tv/">Shara</a></span>
|
||||||
@ -17,20 +17,20 @@ idx: 0.1
|
|||||||
|
|
||||||
## Introduzione
|
## 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.
|
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.
|
a fare le tue mod.
|
||||||
|
|
||||||
Oltre che [leggere questo libro online](https://rubenwardy.com/minetest_modding_book),
|
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).
|
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).
|
* Apri una [Segnalazione 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).
|
* Rispondi alla [Discussione sul Forum](https://forum.minetest.net/viewtopic.php?f=14&t=10729).
|
||||||
* [Contattami (in inglese)](https://rubenwardy.com/contact/).
|
* [Contattami (in inglese)](https://rubenwardy.com/contact/).
|
||||||
* Voglia di contribuire?
|
* Voglia di contribuire?
|
||||||
[Leggi il README](https://gitlab.com/rubenwardy/minetest_modding_book/-/blob/master/README.md).
|
[Leggi il README](https://gitlab.com/rubenwardy/minetest_modding_book/-/blob/master/README.md).
|
||||||
|
184
_it/items/callbacks.md
Normal file
184
_it/items/callbacks.md
Normal file
@ -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 <!-- omit in toc -->
|
||||||
|
|
||||||
|
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.
|
@ -17,8 +17,6 @@ Saper registrare nuovi nodi, oggetti fabbricabili e conseguenti ricette, è un r
|
|||||||
- [Alias](#alias)
|
- [Alias](#alias)
|
||||||
- [Texture](#texture)
|
- [Texture](#texture)
|
||||||
- [Registrare un nodo base](#registrare-un-nodo-base)
|
- [Registrare un nodo base](#registrare-un-nodo-base)
|
||||||
- [Azioni e callback](#azioni-e-callback)
|
|
||||||
- [on_use](#onuse)
|
|
||||||
- [Fabbricazione](#fabbricazione)
|
- [Fabbricazione](#fabbricazione)
|
||||||
- [Fisse (shaped)](#fisse-shaped)
|
- [Fisse (shaped)](#fisse-shaped)
|
||||||
- [Informi (shapeless)](#informi-shapeless)
|
- [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.
|
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.
|
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
|
## Fabbricazione
|
||||||
|
|
||||||
Ci sono diversi tipi di ricette di fabbricazione disponibili, indicate dalla proprietà `type`.
|
Ci sono diversi tipi di ricette di fabbricazione disponibili, indicate dalla proprietà `type`.
|
||||||
|
@ -184,7 +184,7 @@ local function mio_callback(pos, action,
|
|||||||
-- Invia messaggio indicante il progresso
|
-- Invia messaggio indicante il progresso
|
||||||
if param.blocchi_totali == param.blocchi_caricati then
|
if param.blocchi_totali == param.blocchi_caricati then
|
||||||
minetest.chat_send_all("Ho finito di caricare blocchi!")
|
minetest.chat_send_all("Ho finito di caricare blocchi!")
|
||||||
end
|
else
|
||||||
local percentuale = 100 * param.blocchi_caricati / param.blocchi_totali
|
local percentuale = 100 * param.blocchi_caricati / param.blocchi_totali
|
||||||
local msg = string.format("Caricamento blocchi %d/%d (%.2f%%)",
|
local msg = string.format("Caricamento blocchi %d/%d (%.2f%%)",
|
||||||
param.blocchi_caricati, param.blocchi_totali, percentuale)
|
param.blocchi_caricati, param.blocchi_totali, percentuale)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: Oggetti, giocatori e entità
|
title: Oggetti, giocatori ed entità
|
||||||
layout: default
|
layout: default
|
||||||
root: ../..
|
root: ../..
|
||||||
idx: 3.4
|
idx: 3.4
|
||||||
|
@ -8,17 +8,19 @@ redirect_from: /it/chapters/chat.html
|
|||||||
cmd_online:
|
cmd_online:
|
||||||
level: warning
|
level: warning
|
||||||
title: I giocatori offline possono eseguire comandi
|
title: I giocatori offline possono eseguire comandi
|
||||||
message: <p>Viene passato il nome del giocatore al posto del giocatore in sé perché le mod possono eseguire comandi in vece di un giocatore offline.
|
message: |
|
||||||
Per esempio, il bridge IRC permette ai giocatori di eseguire comandi senza dover entrare in gioco.</p>
|
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.
|
||||||
|
|
||||||
<p>Assicurati quindi di non dar per scontato che un giocatore sia connesso.
|
Assicurati quindi di non dar per scontato che un giocatore sia connesso.
|
||||||
Puoi controllare ciò tramite <pre>minetest.get_player_by_name</pre>, per vedere se ritorna qualcosa o meno.</p>
|
Puoi controllare ciò tramite `minetest.get_player_by_name`, per vedere se ritorna qualcosa o meno.
|
||||||
|
|
||||||
cb_cmdsprivs:
|
cb_cmdsprivs:
|
||||||
level: warning
|
level: warning
|
||||||
title: Privilegi e comandi
|
title: Privilegi e comandi
|
||||||
message: Il privilegio shout non è necessario per far sì che un giocatore attivi questo callback.
|
message: |
|
||||||
Questo perché i comandi sono implementati in Lua, e sono semplicemente dei messaggi in chat che iniziano con /.
|
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.
|
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](#inviare-messaggi)
|
||||||
- [Inviare messaggi a giocatori specifici](#inviare-messaggi-a-giocatori-specifici)
|
- [A tutti i giocatori](#a-tutti-i-giocatori)
|
||||||
|
- [A giocatori specifici](#a-giocatori-specifici)
|
||||||
- [Comandi](#comandi)
|
- [Comandi](#comandi)
|
||||||
- [Complex Subcommands](#complex-subcommands)
|
- [Accettare più argomenti](#accettare-più-argomenti)
|
||||||
- [Intercettare i messaggi](#interecettare-i-messaggi)
|
- [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`:
|
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.
|
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`:
|
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.
|
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.
|
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 %}
|
{% include notice.html notice=page.cmd_online %}
|
||||||
|
|
||||||
## Sottocomandi complessi
|
|
||||||
|
|
||||||
È spesso necessario creare dei comandi complessi, come per esempio:
|
### Accettare più argomenti
|
||||||
|
|
||||||
* `/msg <a> <messaggio>`
|
Non è raro che i comandi richiedano più argomenti, come per esempio `/squadra entra <nome_squadra>`.
|
||||||
* `/team entra <nometeam>`
|
Ci sono due modi per implementare ciò: usare `string.split` o i pattern Lua.
|
||||||
* `/team esci <nometeam>`
|
|
||||||
* `/team elenco`
|
|
||||||
|
|
||||||
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 <nome_squadra> <numero>"
|
||||||
|
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
|
```lua
|
||||||
local a, msg = string.match(param, "^([%a%d_-]+) (*+)$")
|
local a, msg = string.match(param, "^([%a%d_-]+) (*+)$")
|
||||||
@ -130,7 +166,7 @@ minetest.register_on_chat_message(function(name, message)
|
|||||||
end)
|
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.
|
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 %}
|
{% include notice.html notice=page.cb_cmdsprivs %}
|
||||||
|
@ -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)
|
- [Coordinate reali o datate](#coordinate-reali-o-datate)
|
||||||
- [Anatomia di un formspec](#anatomia-di-un-formspec)
|
- [Anatomia di un formspec](#anatomia-di-un-formspec)
|
||||||
- [Elementi](#elementi)
|
- [Elementi](#elementi)
|
||||||
- [Intestazione](#intestazione)
|
- [Intestazione](#intestazione)
|
||||||
- [Esempio: indovina un numero](#esempio-indovina-un-numero)
|
- [Esempio: indovina un numero](#esempio-indovina-un-numero)
|
||||||
- [Imbottitura e spaziatura](#imbottitura-e-spaziatura)
|
- [Imbottitura e spaziatura](#imbottitura-e-spaziatura)
|
||||||
- [Ricevere i moduli di compilazione](#ricevere-i-moduli-di-compilazione)
|
- [Ricevere i moduli di compilazione](#ricevere-i-moduli-di-compilazione)
|
||||||
- [Contesti](#contesti)
|
- [Contesti](#contesti)
|
||||||
- [Ricavare un formspec](#ricavare-un-formspec)
|
- [Ricavare un formspec](#ricavare-un-formspec)
|
||||||
- [Formspec nei nodi](#formspec-nei-nodi)
|
- [Formspec nei nodi](#formspec-nei-nodi)
|
||||||
- [Inventario del giocatore](#inventario-del-giocatore)
|
- [Inventario del giocatore](#inventario-del-giocatore)
|
||||||
- [Il tuo turno](#il-tuo-turno)
|
- [Il tuo turno](#il-tuo-turno)
|
||||||
|
|
||||||
|
|
||||||
## Coordinate reali o datate
|
## 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 è `""`.
|
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.
|
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
|
### Il tuo turno
|
||||||
|
|
||||||
|
@ -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;
|
* **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);
|
* **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.
|
||||||
|
@ -1,223 +1,4 @@
|
|||||||
---
|
---
|
||||||
title: "SFINV"
|
sitemap: false
|
||||||
layout: default
|
redirect_to: "https://github.com/rubenwardy/sfinv/blob/master/Tutorial.md"
|
||||||
root: ../..
|
|
||||||
idx: 4.7
|
|
||||||
description: una mod per rendere più semplice la creazione di un inventario complesso
|
|
||||||
redirect_from: /it/chapters/sfinv.html
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Introduzione <!-- omit in toc -->
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
<figure>
|
|
||||||
<img src="{{ page.root }}//static/sfinv_admin_fs.png" alt="Pagina per gli amministratori">
|
|
||||||
</figure>
|
|
||||||
|
|
||||||
## 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
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
@ -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.
|
LuaCheck può essere usato in combinazione con l'editor per fornire avvertimenti vari.
|
||||||
|
|
||||||
- [Installare LuaCheck](#installare-luacheck)
|
- [Installare LuaCheck](#installare-luacheck)
|
||||||
- [Windows](#windows)
|
- [Windows](#windows)
|
||||||
- [Linux](#linux)
|
- [Linux](#linux)
|
||||||
- [Eseguire LuaCheck](#eseguire-luacheck)
|
- [Eseguire LuaCheck](#eseguire-luacheck)
|
||||||
- [Configurare LuaCheck](#configurare-luacheck)
|
- [Configurare LuaCheck](#configurare-luacheck)
|
||||||
- [Risoluzione problemi](#risoluzione-problemi)
|
- [Risoluzione problemi](#risoluzione-problemi)
|
||||||
- [Uso nell'editor](#uso-nelleditor)
|
- [Uso nell'editor](#uso-nelleditor)
|
||||||
- [Controllare i commit con Travis](#controllare-i-commit-con-travis)
|
|
||||||
|
|
||||||
## Installare LuaCheck
|
## Installare LuaCheck
|
||||||
|
|
||||||
### Windows
|
### 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
|
### Linux
|
||||||
|
|
||||||
@ -53,7 +52,7 @@ Su Linux, esegui `luacheck .` nella cartella principale del progetto.
|
|||||||
## Configurare LuaCheck
|
## Configurare LuaCheck
|
||||||
|
|
||||||
Crea un file chiamato .luacheckrc nella cartella principale del tuo progetto.
|
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:
|
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.
|
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.
|
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
|
### 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.
|
È 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:
|
Queste sono disponibili nella maggior parte degli editor, come:
|
||||||
|
|
||||||
* **Atom** - `linter-luacheck`;
|
|
||||||
* **VSCode** - Ctrl+P, poi incolla: `ext install dwenegar.vscode-luacheck`;
|
* **VSCode** - Ctrl+P, poi incolla: `ext install dwenegar.vscode-luacheck`;
|
||||||
* **Sublime** - Installala usando package-control:
|
* **Sublime** - Installala usando package-control:
|
||||||
[SublimeLinter](https://github.com/SublimeLinter/SublimeLinter),
|
[SublimeLinter](https://github.com/SublimeLinter/SublimeLinter),
|
||||||
[SublimeLinter-luacheck](https://github.com/SublimeLinter/SublimeLinter-luacheck).
|
[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.
|
|
||||||
|
@ -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/) |
|
* 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).
|
[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).
|
* Spulcia le [mod esistenti](https://forum.minetest.net/viewforum.php?f=11).
|
||||||
|
|
||||||
### Programmazione in Lua
|
### Programmazione in Lua
|
||||||
|
153
_it/quality/translations.md
Normal file
153
_it/quality/translations.md
Normal file
@ -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 <!-- omit in toc -->
|
||||||
|
|
||||||
|
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.
|
@ -13,11 +13,10 @@ Scrivere i testing d'unità per le funzioni dove vengono chiamate quelle di Mine
|
|||||||
|
|
||||||
- [Installare Busted](#installare-busted)
|
- [Installare Busted](#installare-busted)
|
||||||
- [Il tuo primo test](#il-tuo-primo-test)
|
- [Il tuo primo test](#il-tuo-primo-test)
|
||||||
- [init.lua](#initlua)
|
- [init.lua](#initlua)
|
||||||
- [api.lua](#apilua)
|
- [api.lua](#apilua)
|
||||||
- [tests/api_spec.lua](#testsapispeclua)
|
- [tests/api_spec.lua](#testsapi_speclua)
|
||||||
- [Simulare: usare funzioni esterne](#simulare-usare-funzioni-esterne)
|
- [Simulare: usare funzioni esterne](#simulare-usare-funzioni-esterne)
|
||||||
- [Controllare commit con Travis](#controllare-commit-con-travis)
|
|
||||||
- [Conclusione](#conclusione)
|
- [Conclusione](#conclusione)
|
||||||
|
|
||||||
## Installare Busted
|
## 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
|
## 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.
|
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).
|
||||||
|
@ -3,7 +3,15 @@ layout: compress
|
|||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
|
|
||||||
|
{% assign pathsplit = page.url | split: '/' %}
|
||||||
|
{% assign language = pathsplit[1] %}
|
||||||
|
{% assign language_info = site.data.languages | where: "code", language %}
|
||||||
|
{% if language_info %}
|
||||||
|
<html lang="{{ language }}">
|
||||||
|
{% else %}
|
||||||
<html>
|
<html>
|
||||||
|
{% endif %}
|
||||||
<head>
|
<head>
|
||||||
<title>{% if page.homepage %}{% else %}{{ page.title }} - {% endif %}Minetest Modding Book</title>
|
<title>{% if page.homepage %}{% else %}{{ page.title }} - {% endif %}Minetest Modding Book</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@ -13,8 +21,20 @@ layout: compress
|
|||||||
<meta name="author" content="rubenwardy">
|
<meta name="author" content="rubenwardy">
|
||||||
<meta name="flattr:id" content="gl763e">
|
<meta name="flattr:id" content="gl763e">
|
||||||
|
|
||||||
|
<link rel="canonical" href="https://rubenwardy.com/minetest_modding_book{{ page.url }}" />
|
||||||
|
|
||||||
|
{% assign oldSegment = "/" | append: language | append: "/" %}
|
||||||
|
{% for other_lang in site.data.languages %}
|
||||||
|
{% unless other_lang.code == language %}
|
||||||
|
{% assign newSegment = "/" | append: other_lang.code | append: "/" %}
|
||||||
|
|
||||||
|
<link rel="alternate" hreflang="{{ other_lang.code }}"
|
||||||
|
href="{{ page.url | replace: oldSegment, newSegment | relative_url }}" />
|
||||||
|
{% endunless %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
<style>body,html,nav{background:#333}nav,nav li,nav li a{display:block}body,html,main,nav li{margin:0;padding:0}main,nav{position:absolute;top:0}body,html{font-size:17px;color:#000}#container{width:100%;max-width:1100px;margin:auto;position:relative}nav{left:0;width:280px;list-style:none;color:#fff}nav li a{padding:5px;color:#ccc;text-decoration:none}main{left:280px;right:0}article{background:#fff;padding:0 20px 20px}</style>
|
<style>body,html,nav{background:#333}nav,nav li,nav li a{display:block}body,html,main,nav li{margin:0;padding:0}main,nav{position:absolute;top:0}body,html{font-size:17px;color:#000}#container{width:100%;max-width:1100px;margin:auto;position:relative}nav{left:0;width:280px;list-style:none;color:#fff}nav li a{padding:5px;color:#ccc;text-decoration:none}main{left:280px;right:0}article{background:#fff;padding:0 20px 20px}</style>
|
||||||
<link rel="stylesheet" href="{{ page.root }}/static/style.css?v=2">
|
<link rel="stylesheet" href="{{ page.root }}/static/style.css?v=3">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
|
@ -8,14 +8,15 @@ layout: base
|
|||||||
|
|
||||||
{% if language == "_it" %}
|
{% if language == "_it" %}
|
||||||
{% assign language = "it" %}
|
{% assign language = "it" %}
|
||||||
{% assign links = site.it | sort: "idx" %}
|
{% assign links = site.it %}
|
||||||
{% elseif language == "_de" %}
|
{% elsif language == "_de" %}
|
||||||
{% assign language = "de" %}
|
{% assign language = "de" %}
|
||||||
{% assign links = site.de | sort: "idx" %}
|
{% assign links = site.de %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% assign language = "en" %}
|
{% assign language = "en" %}
|
||||||
{% assign links = site.en | sort: "idx" %}
|
{% assign links = site.en %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% assign links = links | where_exp: "item", "item.sitemap != false" | sort: "idx" %}
|
||||||
|
|
||||||
{% assign num = 0 %}
|
{% assign num = 0 %}
|
||||||
|
|
||||||
@ -66,7 +67,7 @@ layout: base
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
© 2014-21
|
© 2014-{{ site.time | date: '%Y' }}
|
||||||
{% if language == "en" %}
|
{% if language == "en" %}
|
||||||
| Helpful? Consider
|
| Helpful? Consider
|
||||||
<a href="https://rubenwardy.com/donate/">donating</a>
|
<a href="https://rubenwardy.com/donate/">donating</a>
|
||||||
|
@ -33,68 +33,6 @@ figure {
|
|||||||
padding: 0 0 0 6px;
|
padding: 0 0 0 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-info {
|
|
||||||
background: #ececec !important;
|
|
||||||
border: 1px solid #aaa !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-danger {
|
|
||||||
background: #fcc !important;
|
|
||||||
border: 1px solid #a66 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-warning {
|
|
||||||
background: #FED;
|
|
||||||
border: 1px solid #fc9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-tip {
|
|
||||||
background: #ccf;
|
|
||||||
border: 1px solid #66a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-green {
|
|
||||||
background: #161;
|
|
||||||
border: 1px solid #393;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice {
|
|
||||||
margin: 10px;
|
|
||||||
display: block;
|
|
||||||
padding: 5px;
|
|
||||||
border-radius: 5px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice p {
|
|
||||||
margin: 0 0 17px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice p:last-child {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice > span {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 40px;
|
|
||||||
font-size: 24px;
|
|
||||||
text-align: center;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice > div {
|
|
||||||
margin-left: 35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice h2 {
|
|
||||||
margin: 0 0 5px 0;
|
|
||||||
padding: 0 0 2px 0;
|
|
||||||
font-size: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-link, .anchor {
|
.header-link, .anchor {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #bbb;
|
color: #bbb;
|
||||||
|
61
_sass/_notice.scss
Normal file
61
_sass/_notice.scss
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
.notice {
|
||||||
|
margin: 2em 0;
|
||||||
|
display: block;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0 0 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p:last-child {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 40px;
|
||||||
|
font-size: 24px;
|
||||||
|
text-align: center;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
margin-left: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 0 0 5px 0;
|
||||||
|
padding: 0 0 2px 0;
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-info {
|
||||||
|
background: #ececec !important;
|
||||||
|
border: 1px solid #aaa !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-danger {
|
||||||
|
background: #fcc !important;
|
||||||
|
border: 1px solid #a66 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-warning {
|
||||||
|
background: #FED;
|
||||||
|
border: 1px solid #fc9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-tip {
|
||||||
|
background: #ccf;
|
||||||
|
border: 1px solid #66a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-green {
|
||||||
|
background: #161;
|
||||||
|
border: 1px solid #393;
|
||||||
|
}
|
17
_sass/_table.scss
Normal file
17
_sass/_table.scss
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
table td, table th {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
table tr:nth-child(even) {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
table th {
|
||||||
|
padding: 12px 8px;
|
||||||
|
text-align: left;
|
||||||
|
background-color: #04AA6D;
|
||||||
|
color: white;
|
||||||
|
}
|
53
index.html
53
index.html
@ -1,35 +1,36 @@
|
|||||||
---
|
---
|
||||||
layout: none
|
layout: none
|
||||||
---
|
---
|
||||||
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Redirecting...</title>
|
<title>Minetest Modding Book</title>
|
||||||
|
<meta name="description" content="An easy guide to learn how to create mods for Minetest">
|
||||||
|
<script>
|
||||||
|
var languages = {{ site.data.languages | jsonify }};
|
||||||
|
function getLanguage() {
|
||||||
|
var userLang = navigator.language || navigator.userLanguage;
|
||||||
|
for (var i = 0; i < languages.length; i++) {
|
||||||
|
var lang = languages[i];
|
||||||
|
if (userLang.indexOf(lang.code) == 0) {
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var language = getLanguage() || languages[0];
|
||||||
|
window.location.replace(language.code + "/index.html");
|
||||||
|
</script>
|
||||||
|
<meta http-equiv="refresh" content="0;URL='{{ 'en/index.html' | absolute_url }}'" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
Detecting and redirecting to the correct translation.<br><br>
|
<main>
|
||||||
|
<h1>Minetest Modding Book</h1>
|
||||||
<a href="en/index.html">View English Translation</a><br><br>
|
<p>Detecting and redirecting to the correct translation.</p>
|
||||||
|
<p>
|
||||||
<script>
|
<a href="en/index.html">View English Translation</a>
|
||||||
var languages = {{ site.data.languages | jsonify }};
|
</p>
|
||||||
function getLanguage() {
|
</main>
|
||||||
var userLang = navigator.language || navigator.userLanguage;
|
|
||||||
for (var i = 0; i < languages.length; i++) {
|
|
||||||
var lang = languages[i];
|
|
||||||
if (userLang.indexOf(lang.code) == 0) {
|
|
||||||
return lang;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var language = getLanguage() || languages[0];
|
|
||||||
var url = language.code + "/index.html";
|
|
||||||
document.write('<a href="' + language.code +
|
|
||||||
'/index.html">Redirecting to the ' + language.name +
|
|
||||||
' version...</a>');
|
|
||||||
|
|
||||||
window.location.replace(url);
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
---
|
---
|
||||||
|
|
||||||
{% assign pages = site.en | sort: "idx" %}
|
{% assign pages = site.en | where_exp: "item", "item.sitemap != false" | sort: "idx" %}
|
||||||
{% assign num = 0 %}
|
{% assign num = 0 %}
|
||||||
|
|
||||||
[
|
[
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 98 KiB |
Binary file not shown.
Before Width: | Height: | Size: 28 KiB |
@ -4,3 +4,5 @@
|
|||||||
@import "main";
|
@import "main";
|
||||||
@import "content";
|
@import "content";
|
||||||
@import "code";
|
@import "code";
|
||||||
|
@import "notice";
|
||||||
|
@import "table";
|
||||||
|
Loading…
Reference in New Issue
Block a user