mods that hopefully work for a TechAge-based pack

This commit is contained in:
tangent committed 2026-09-09 06:27:51 -06:00
1 parent 4e6b08d194
commit c27db2a537
6234 files changed
+375578

No files matched your search

@@ -0,0 +1,28 @@
The Signs Bot mod for Minetest is
Copyright (C) 2019 Joachim Stolberg
License of source code
----------------------
This mod is free software; you can redistribute and/or
modify it under the terms of the GNU General Public License version 3 or later
published by the Free Software Foundation.
This mod is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public
License along with this mod; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Boston, MA 02110-1301, USA.
License of media (textures, sounds and documentation)
-----------------------------------------------------
All textures, sounds and documentation files are licensed under the
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
http://creativecommons.org/licenses/by-sa/3.0/
+674
View File
@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
+64
View File
@@ -0,0 +1,64 @@
Signs Bot [signs_bot]
=====================
**A robot controlled by signs.**
Browse on: [GitHub](https://github.com/joe7575/signs_bot)
Download: [GitHub](https://github.com/joe7575/signs_bot/archive/master.zip)
![Signs Bot](https://github.com/joe7575/signs_bot/blob/master/screenshot.png)
### Introduction
See [manual](https://github.com/joe7575/signs_bot/blob/master/manual_EN.md)
The mod has an in-game help to all blocks and signs.
If 'techage' is installed, the techage ingame manual will be expanded.
If techage is not installed, it is recommended that you have installed the
mod doclib.
### License
Copyright (C) 2019-2025 Joachim Stolberg
Copyright (C) 2021 Michal 'Micu' Cieslakiewicz (soup commands)
Code: Licensed under the GNU GPL version 3 or later. See LICENSE.txt
### Dependencies
default, farming, basic_materials, tubelib2
optional: farming redo, node_io, doc, techage, minecart, xdecor, compost, doclib
### History
- 2019-03-23 v0.01 * first draft
- 2019-04-06 v0.02 * completely reworked
- 2019-04-08 v0.03 * 'plant_sapling', 'place_below', 'dig_below' added, many bugs fixed
- 2019-04-11 v0.04 * support for 'node_io' added, chest added, further commands added
- 2019-04-14 v0.05 * timer added, user signs added, bug fixes
- 2019-04-15 v0.06 * nodes remove bugfix, punch_cart command added, cart sensor added
- 2019-04-18 v0.07 * node_io is now optional, support for MTG chests and furnace added
- 2019-05-22 v0.08 * recipe bug fixes and prepared for techage
- 2019-05-25 v0.09 * in-game help added for the mod 'doc'
- 2019-07-05 v0.10 * Timer, sensor and cart handling improvements
- 2019-07-08 v0.11 * Delayer added
- 2019-08-09 v0.12 * bug fixes
- 2019-08-14 v0.13 * Signs Bot Chest recipe added, Minecart signs added
- 2020-01-02 v1.00 * bot inventory filter added, documentation enhanced
- 2020-03-27 v1.01 * flower command and sign added
- 2020-03-30 v1.02 * Program flow control commands added
- 2020-06-21 v1.03 * Interpreter bugfixes, node and crop sensors changed
- 2020-10-01 v1.04 * Many improvements and bugfixes (Thanks to Thomas-S)
- 2021-01-30 v1.05 * Many improvements and bugfixes
- 2021-03-14 v1.06 * Switch translation from intllib to minetest.translator
- 2021-04-24 v1.07 * Adapted to minecart v2.0
- 2021-05-04 v1.08 * Add print command, improve error msg
- 2021-08-22 v1.09 * Add soup commands and signs, add aspen sign
- 2021-09-18 v1.10 * Add techage command 'set <num>' to the Bot Control Unit
- 2022-03-19 V1.11 * Extend farming (and add ethereal) support (Thanks to nixnoxus)
- 2022-09-11 V1.12 * Add commands `jump_low_batt` , `jump_check_item`, and `fall_down`
- 2023-06-27 V1.13 * Add Beduino command support (see bep-006_sb_cmnd.md)
- 2023-07-28 V1.13 * Introduction/manual restructured, doclib dependency added
- 2025-02-16 V1.14 * Filling of slots changed. AND gate improved. Consider falling nodes.
+544
View File
@@ -0,0 +1,544 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signs Bot: Robot basis block
]]--
-- for lazy programmers
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
signs_bot.MAX_CAPA = 600
local CYCLE_TIME = 1
local CYCLE_TIME2 = 2 -- for charging phase
local function in_range(val, min, max)
if val < min then return min end
if val > max then return max end
return val
end
-- determine item name from the given Bot inventory slot
function signs_bot.bot_inv_item_name(pos, slot)
if slot == 0 then return nil end -- invalid num
local inv = M(pos):get_inventory()
local name = inv:get_stack("filter", slot):get_name()
if name ~= "" then return name end
end
-- put items into the bot inventory and return leftover
function signs_bot.bot_inv_put_item(pos, slot, items)
if not items then return end
local inv = M(pos):get_inventory()
if slot and slot > 0 then
local name = inv:get_stack("filter", slot):get_name()
if name == "" or name == items:get_name() then
local stack = inv:get_stack("main", slot)
items = stack:add_item(items)
inv:set_stack("main", slot, stack)
end
else
for idx = 1,8 do
local name = inv:get_stack("filter", idx):get_name()
if name == items:get_name() then
local stack = inv:get_stack("main", idx)
items = stack:add_item(items)
inv:set_stack("main", idx, stack)
if items:get_count() == 0 then return items end
end
end
for idx = 1,8 do
local name = inv:get_stack("filter", idx):get_name()
if name == "" then
local stack = inv:get_stack("main", idx)
items = stack:add_item(items)
inv:set_stack("main", idx, stack)
if items:get_count() == 0 then return items end
end
end
end
return items
end
local function take_items(inv, slot, num)
local stack = inv:get_stack("main", slot)
if stack:get_count() >= num then
local taken = stack:take_item(num)
inv:set_stack("main", slot, stack)
return taken
else
inv:set_stack("main", slot, nil)
local rest = num - stack:get_count()
local taken = inv:remove_item("main", ItemStack(stack:get_name().." "..rest))
stack:set_count(stack:get_count() + taken:get_count())
return stack
end
end
-- take items from the bot inventory
function signs_bot.bot_inv_take_item(pos, slot, num)
local inv = M(pos):get_inventory()
if slot and slot > 0 then
return take_items(inv, slot, num)
else
for idx = 1,8 do
if not inv:get_stack("main", idx):is_empty() then
return take_items(inv, idx, num)
end
end
end
end
local bot_inv_item_name = signs_bot.bot_inv_item_name
local function preassigned_slots(pos)
local inv = M(pos):get_inventory()
local tbl = {}
for idx = 1,8 do
local item_name = inv:get_stack("filter", idx):get_name()
if item_name ~= "" then
local x = ((idx - 1) % 4) + 5
local y = idx < 5 and 1 or 2
tbl[#tbl+1] = "item_image["..x..","..y..";1,1;"..item_name.."]"
end
end
return table.concat(tbl, "")
end
local function status(mem)
if mem.error then
if type(mem.error) == "string" then
return mem.error
else
return dump(mem.error)
end
end
if mem.running then
return S("running")
end
if mem.charging then
return S("charging")
end
return S("stopped")
end
local function formspec(pos, mem)
mem.running = mem.running or false
local cmnd = mem.running and "stop;"..S("Off") or "start;"..S("On")
local bot = not mem.running and "image[0.6,0;1,1;signs_bot_bot_inv.png]" or ""
local current_capa = mem.capa or (signs_bot.MAX_CAPA * 0.9)
return "size[9,8.2]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[2.1,0;"..S("Signs").."]label[5.3,0;"..S("Other items").."]"..
"image[0.6,0;1,1;signs_bot_form_mask.png]"..
bot..
preassigned_slots(pos)..
signs_bot.formspec_battery_capa(signs_bot.MAX_CAPA, current_capa)..
"label[2.1,0.5;1]label[3.1,0.5;2]label[4.1,0.5;3]"..
"list[context;sign;1.8,1;3,2;]"..
"label[2.1,3;4]label[3.1,3;5]label[4.1,3;6]"..
"label[5.3,0.5;1]label[6.3,0.5;2]label[7.3,0.5;3]label[8.3,0.5;4]"..
"list[context;main;5,1;4,2;]"..
"label[5.3,3;5]label[6.3,3;6]label[7.3,3;7]label[8.3,3;8]"..
"button[0.2,1;1.5,1;config;"..S("Config").."]"..
"button[0.2,2;1.5,1;"..cmnd.."]"..
"label[1,3.6;"..status(mem).."]"..
"list[current_player;main;0.5,4.4;8,4;]"..
"listring[context;main]"..
"listring[current_player;main]"
end
local function formspec_cfg()
return "size[9,8.2]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[5.3,0;"..S("Preassign slots items").."]"..
"label[5.3,0.5;1]label[6.3,0.5;2]label[7.3,0.5;3]label[8.3,0.5;4]"..
"list[context;filter;5,1;4,2;]"..
"label[5.3,3;5]label[6.3,3;6]label[7.3,3;7]label[8.3,3;8]"..
"button[0.2,1;1.5,1;back;"..S("Back").."]"..
"list[current_player;main;0.5,4.4;8,4;]"..
"listring[context;filter]"..
"listring[current_player;main]"
end
local function get_capa(itemstack)
local meta = itemstack:get_meta()
if meta then
return in_range(meta:get_int("capa") * (signs_bot.MAX_CAPA/100), 0, 3000)
end
return 0
end
local function set_capa(pos, oldnode, digger, capa)
local node = ItemStack(oldnode.name)
local meta = node:get_meta()
capa = techage.power.percent(signs_bot.MAX_CAPA, capa)
capa = (math.floor((capa or 0) / 5)) * 5
meta:set_int("capa", capa)
local text = S("Robot Box").." ("..capa.." %)"
meta:set_string("description", text)
local inv = minetest.get_inventory({type="player", name=digger:get_player_name()})
if inv then
local left_over = inv:add_item("main", node)
if left_over:get_count() > 0 then
minetest.add_item(pos, node)
end
end
end
function signs_bot.infotext(pos, state)
local meta = M(pos)
local number = meta:get_string("number")
state = state or "<unknown>"
meta:set_string("infotext", S("Robot Box").." "..number..": "..state)
end
local function free_start_pos(pos, mem)
local param2 = (minetest.get_node(pos).param2 + 1) % 4
local robot_pos = lib.next_pos(pos, param2, 1)
return signs_bot.lib.is_air_like(robot_pos)
end
local function reset_robot(pos, mem)
mem.robot_param2 = (minetest.get_node(pos).param2 + 1) % 4
mem.robot_pos = lib.next_pos(pos, mem.robot_param2, 1)
local pos_below = {x=mem.robot_pos.x, y=mem.robot_pos.y-1, z=mem.robot_pos.z}
signs_bot.place_robot(mem.robot_pos, pos_below, mem.robot_param2)
mem.error = false
end
function signs_bot.start_robot(base_pos)
local mem = tubelib2.get_mem(base_pos)
if free_start_pos(base_pos, mem) then
mem.steps = nil
mem.script = "cond_move"
local meta = M(base_pos)
signs_bot.reset(base_pos, mem)
mem.running = true
mem.charging = false
mem.error = false
mem.stored_node = nil
if minetest.global_exists("techage") then
mem.capa = mem.capa or 0 -- enable power consumption
else
mem.capa = nil
end
meta:set_string("formspec", formspec(base_pos, mem))
signs_bot.infotext(base_pos, S("running"))
reset_robot(base_pos, mem)
minetest.get_node_timer(base_pos):start(CYCLE_TIME)
return true
end
end
function signs_bot.stop_robot(base_pos, mem)
local meta = M(base_pos)
if mem.signal_request ~= true then
mem.running = false
mem.error = false
if minetest.global_exists("techage") then
minetest.get_node_timer(base_pos):start(CYCLE_TIME2)
mem.charging = true
mem.power_available = false
else
minetest.get_node_timer(base_pos):stop()
mem.charging = false
end
if mem.charging then
signs_bot.infotext(base_pos, S("charging"))
else
signs_bot.infotext(base_pos, S("stopped"))
end
meta:set_string("formspec", formspec(base_pos, mem))
signs_bot.remove_robot(mem)
else
mem.signal_request = false
signs_bot.start_robot(base_pos)
end
end
-- Used by the pairing tool
local function signs_bot_get_signal(pos, node)
local mem = tubelib2.get_mem(pos)
if mem.running then
return "on"
else
return "off"
end
end
-- To be called from sensors
local function signs_bot_on_signal(pos, node, signal)
local mem = tubelib2.get_mem(pos)
if signal == "on" and not mem.running then
signs_bot.start_robot(pos)
elseif signal == "off" and mem.running then
signs_bot.stop_robot(pos, mem)
-- else
-- mem.signal_request = (signal == "on")
end
end
local function node_timer(pos, elapsed)
local mem = tubelib2.get_mem(pos)
if mem.charging and signs_bot.while_charging then
return signs_bot.while_charging(pos, mem)
else
local res = false
--local t = minetest.get_us_time()
if mem.running then
res = signs_bot.run_next_command(pos, mem)
end
--t = minetest.get_us_time() - t
--print("node_timer", t)
return res and mem.running
end
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local mem = tubelib2.get_mem(pos)
local meta = minetest.get_meta(pos)
if fields.update then
meta:set_string("formspec", formspec(pos, mem))
elseif fields.config then
meta:set_string("formspec", formspec_cfg())
elseif fields.back then
meta:set_string("formspec", formspec(pos, mem))
elseif fields.start then
signs_bot.start_robot(pos)
elseif fields.stop then
signs_bot.stop_robot(pos, mem)
end
end
local function on_rightclick(pos)
local mem = tubelib2.get_mem(pos)
M(pos):set_string("formspec", formspec(pos, mem))
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
local mem = tubelib2.get_mem(pos)
if mem.running then
return 0
end
local name = stack:get_name()
if listname == "sign" and minetest.get_item_group(name, "sign_bot_sign") ~= 1 then
return 0
end
if listname == "main" and bot_inv_item_name(pos, index) and
name ~= bot_inv_item_name(pos, index) then
return 0
end
if listname == "filter" then
local inv = M(pos):get_inventory()
local list = inv:get_list(listname)
if list[index]:get_count() == 0 or stack:get_name() ~= list[index]:get_name() then
return 1
end
return 0
end
return stack:get_count()
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
local mem = tubelib2.get_mem(pos)
if mem.running then
return 0
end
return stack:get_count()
end
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
local mem = tubelib2.get_mem(pos)
if mem.running then
return 0
end
if from_list ~= to_list then
return 0
end
local inv = M(pos):get_inventory()
local name = inv:get_stack(from_list, from_index):get_name()
if to_list == "main" and bot_inv_item_name(pos, to_index) and
name ~= bot_inv_item_name(pos, to_index) then
return 0
end
if to_list == "filter" then
local list = inv:get_list(to_list)
if list[to_index]:get_count() == 0 or name ~= list[to_index]:get_name() then
return 1
end
return 0
end
return count
end
local drop = "signs_bot:box"
if minetest.global_exists("techage") then
drop = ""
end
minetest.register_node("signs_bot:box", {
description = S("Signs Bot Box"),
stack_max = 1,
tiles = {
-- up, down, right, left, back, front
'signs_bot_base_top.png',
'signs_bot_base_top.png',
'signs_bot_base_right.png',
'signs_bot_base_left.png',
'signs_bot_base_front.png',
'signs_bot_base_front.png',
},
on_construct = function(pos)
local meta = M(pos)
local inv = meta:get_inventory()
inv:set_size('main', 8)
inv:set_size('sign', 6)
inv:set_size('filter', 8)
end,
after_place_node = function(pos, placer, itemstack)
if not placer or not placer:is_player() then
minetest.remove_node(pos)
minetest.add_item(pos, itemstack)
return
end
local mem = tubelib2.init_mem(pos)
mem.running = false
mem.error = false
local meta = M(pos)
local number = ""
if minetest.global_exists("techage") then
number = techage.add_node(pos, "signs_bot:box")
end
meta:set_string("owner", placer:get_player_name())
meta:set_string("number", number)
meta:set_string("formspec", formspec(pos, mem))
meta:set_string("signs_bot_cmnd", "turn_off")
meta:set_int("err_code", 0)
signs_bot.infotext(pos, S("stopped"))
if minetest.global_exists("techage") then
techage.ElectricCable:after_place_node(pos)
mem.capa = get_capa(itemstack)
end
end,
signs_bot_get_signal = signs_bot_get_signal,
signs_bot_on_signal = signs_bot_on_signal,
on_receive_fields = on_receive_fields,
on_rightclick = on_rightclick,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_take = allow_metadata_inventory_take,
allow_metadata_inventory_move = allow_metadata_inventory_move,
can_dig = function(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local mem = tubelib2.get_mem(pos)
if mem.running then
return
end
local inv = M(pos):get_inventory()
return inv:is_empty("main") and inv:is_empty("sign")
end,
on_dig = function(pos, node, puncher, pointed_thing)
minetest.node_dig(pos, node, puncher, pointed_thing)
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
if minetest.global_exists("techage") then
techage.ElectricCable:after_dig_node(pos)
local mem = tubelib2.get_mem(pos)
set_capa(pos, oldnode, digger, mem.capa)
end
tubelib2.del_mem(pos)
end,
on_timer = node_timer,
on_rotate = screwdriver.disallow,
drop = drop,
paramtype2 = "facedir",
is_ground_content = false,
groups = {cracky = 1},
sounds = default.node_sound_metal_defaults(),
})
if minetest.global_exists("techage") then
minetest.register_craft({
output = "signs_bot:box",
recipe = {
{"default:steel_ingot", "group:wood", "default:steel_ingot"},
{"basic_materials:motor", "techage:ta4_wlanchip", "basic_materials:gear_steel"},
{"default:tin_ingot", "", "default:tin_ingot"}
}
})
else
minetest.register_craft({
output = "signs_bot:box",
recipe = {
{"default:steel_ingot", "group:wood", "default:steel_ingot"},
{"basic_materials:motor", "default:mese_crystal", "basic_materials:gear_steel"},
{"default:tin_ingot", "", "default:tin_ingot"}
}
})
end
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "box", {
name = S("Signs Bot Box"),
data = {
item = "signs_bot:box",
text = table.concat({
S("The Box is the housing of the bot."),
S("Place the box and start the bot by means of the 'On' button."),
S("If the mod techage is installed, the bot needs electrical power."),
"",
S("The bot leaves the box on the right side."),
S("It will not start, if this position is blocked."),
"",
S("To stop and remove the bot, press the 'Off' button."),
"",
S("The box inventory simulates the inventory of the bot."),
S("You will not be able to access the inventory, if the bot is running."),
S("The bot can carry up to 8 stacks and 6 signs with it."),
}, "\n")
},
})
end
@@ -0,0 +1,24 @@
| Author | Version | Status | Modified |
| ---------- | ------- | -------- | ----------- |
| J.Stolberg | 0.1 | Proposal | 27 Jun 2023 |
# BEP 006: Signs Bot Commands
### Send Command / Trigger Action
| Command | Topic (num) | Payload (array/string) | Remarks |
| ----------- | ----------- | ---------------------- | ------------------------------------------------------------ |
| Turn on/off | 1 | [num] | Turn bot on/off<br />*num* is the state: 0 = "off", 1 = "on" |
| | | | |
| | | | |
### Request Data
| Command | Topic (num) | Payload (array/string) | Response (array/string) | Remarks to the response |
| --------- | ----------- | ---------------------- | ----------------------- | ------------------------------------------------------------ |
| Bot State | 128 | - | [num] | RUNNING = 1, BLOCKED = 2,<br /> STANDBY = 3, NOPOWER = 4,<br />FAULT = 5, STOPPED = 6,<br />CHARCHING = 7 |
| | | | | |
| | | | | |
@@ -0,0 +1,76 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPLv3
See LICENSE.txt for more information
Signs Bot: Bot Flap
]]--
-- Load support for I18n.
local S = signs_bot.S
local function formspec(cmnd)
cmnd = minetest.formspec_escape(cmnd)
return "size[6,5]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[0.3,0.3;"..cmnd.."]"..
"button_exit[2.5,5.5;2,1;exit;"..S("Exit").."]"
end
local commands = [[dig_sign 6
move 2
place_sign_behind 6
]]
minetest.register_node("signs_bot:bot_flap", {
description = S("Bot Flap"),
paramtype2 = "facedir",
tiles = {
"signs_bot_bot_flap_top.png",
"signs_bot_bot_flap_top.png",
"signs_bot_bot_flap_top.png",
"signs_bot_bot_flap_top.png",
"signs_bot_bot_flap.png",
"signs_bot_bot_flap.png",
},
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_string("signs_bot_cmnd", commands)
meta:set_string("formspec", formspec(commands))
end,
on_rotate = screwdriver.disallow,
is_ground_content = false,
groups = {choppy = 2, oddly_breakable_by_hand = 2, wood = 1, sign_bot_sign = 1},
sounds = default.node_sound_wood_defaults(),
})
minetest.register_craft({
output = "signs_bot:bot_flap",
recipe = {
{"signs_bot:sign_cmnd", "group:wood", "default:steel_ingot"},
{"", "", ""},
{"", "", ""}
}
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "bot_flap", {
name = S("Bot Flap"),
data = {
item = "signs_bot:bot_flap",
text = table.concat({
S("The flap is a simple block used as door for the bot."),
S("Place the flap in any wall, and the bot will automatically open"),
S("and close the flap as it passes through it."),
}, "\n")
},
})
end
@@ -0,0 +1,130 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Bot Sensor
(passive node, the Bot detects the sensor and sends the signal)
]]--
-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S2P = minetest.string_to_pos
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local function update_infotext(pos, dest_pos, cmnd)
M(pos):set_string("infotext", S("Bot Sensor: Connected with").." "..P2S(dest_pos).." / "..cmnd)
end
minetest.register_node("signs_bot:bot_sensor", {
description = S("Bot Sensor"),
inventory_image = "signs_bot_sensor_bot_inv.png",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor2.png^signs_bot_sensor_bot.png",
"signs_bot_sensor2.png",
"signs_bot_sensor2.png",
"signs_bot_sensor2.png",
"signs_bot_sensor2.png",
"signs_bot_sensor2.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
meta:set_string("infotext", S("Bot Sensor: Not connected"))
end,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
use_texture_alpha = signs_bot.CLIP,
is_ground_content = false,
groups = {sign_bot_sensor = 1, cracky = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("signs_bot:bot_sensor_on", {
description = S("Bot Sensor"),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor2.png^signs_bot_sensor_bot_on.png",
"signs_bot_sensor2.png",
"signs_bot_sensor2.png",
"signs_bot_sensor2.png",
"signs_bot_sensor2.png",
"signs_bot_sensor2.png",
},
-- Called from the Bot
after_place_node = function(pos)
minetest.get_node_timer(pos):start(1)
signs_bot.send_signal(pos)
signs_bot.lib.activate_extender_nodes(pos, true)
end,
on_timer = function(pos)
local node = tubelib2.get_node_lvm(pos)
node.name = "signs_bot:bot_sensor"
minetest.swap_node(pos, node)
return false
end,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
use_texture_alpha = signs_bot.CLIP,
is_ground_content = false,
diggable = false,
groups = {sign_bot_sensor = 1, not_in_creative_inventory = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_craft({
output = "signs_bot:bot_sensor",
recipe = {
{"", "", ""},
{"dye:black", "group:wood", "dye:yellow"},
{"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"}
}
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "bot_sensor", {
name = S("Bot Sensor"),
data = {
item = "signs_bot:bot_sensor",
text = table.concat({
S("The Bot Sensor detects any bot and sends a signal, if a bot is nearby."),
S("the sensor range is one node/meter."),
S("The sensor direction does not care."),
}, "\n")
},
})
end
@@ -0,0 +1,160 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Cart Sensor
]]--
-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S2P = minetest.string_to_pos
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
local CYCLE_TIME = 2
local function update_infotext(pos, dest_pos, dest_idx)
M(pos):set_string("infotext", S("Cart Sensor: Connected with").." "..P2S(dest_pos).." / "..dest_idx)
end
local function swap_node(pos, name)
local node = minetest.get_node(pos)
if node.name == name then
return false
end
node.name = name
minetest.swap_node(pos, node)
return true
end
local function node_timer(pos)
if minecart.is_cart_available(pos, M(pos):get_int("param2"), 1) then
if swap_node(pos, "signs_bot:cart_sensor_on") then
signs_bot.send_signal(pos)
signs_bot.lib.activate_extender_nodes(pos, true)
end
else
swap_node(pos, "signs_bot:cart_sensor")
end
return true
end
minetest.register_node("signs_bot:cart_sensor", {
description = S("Cart Sensor"),
inventory_image = "signs_bot_sensor_cart_inv.png",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor1.png^signs_bot_sensor_cart.png",
"signs_bot_sensor1.png",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR180",
},
after_place_node = function(pos, placer)
local meta = M(pos)
meta:set_string("infotext", S("Cart Sensor: Not connected"))
minetest.get_node_timer(pos):start(CYCLE_TIME)
local node = minetest.get_node(pos)
meta:set_int("param2", (node.param2 + 2) % 4)
end,
on_timer = node_timer,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
is_ground_content = false,
groups = {sign_bot_sensor = 1, cracky = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("signs_bot:cart_sensor_on", {
description = S("Cart Sensor"),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor1.png^signs_bot_sensor_cart_on.png",
"signs_bot_sensor1.png",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR180",
},
on_timer = node_timer,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
is_ground_content = false,
diggable = false,
groups = {sign_bot_sensor = 1, not_in_creative_inventory = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_craft({
output = "signs_bot:cart_sensor",
recipe = {
{"", "", ""},
{"dye:black", "group:stone", "dye:yellow"},
{"default:copper_ingot", "default:mese_crystal_fragment", "default:steel_ingot"}
}
})
minetest.register_lbm({
label = "[signs_bot] Restart timer",
name = "signs_bot:crop_sensor_restart",
nodenames = {"signs_bot:cart_sensor", "signs_bot:cart_sensor_on"},
run_at_every_load = true,
action = function(pos, node)
minetest.get_node_timer(pos):start(CYCLE_TIME)
if node.name == "signs_bot:cart_sensor_on" then
signs_bot.send_signal(pos)
signs_bot.lib.activate_extender_nodes(pos, true)
end
end
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "cart_sensor", {
name = S("Cart Sensor"),
data = {
item = "signs_bot:cart_sensor",
text = table.concat({
S("The Cart Sensor detects and sends a signal, if a cart (Minecart) is nearby."),
S("the sensor range is one node/meter."),
S("The sensor has an active side (red) that must point to the rail/cart."),
}, "\n")
},
})
end
+208
View File
@@ -0,0 +1,208 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signs Changer/Control Unit for Bot Control
]]--
-- for lazy programmers
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
local INFO = [[Set the node state: command = 'set', payload = 1..4]]
local NodeIdx = {
["signs_bot:changer1"] = "1",
["signs_bot:changer2"] = "2",
["signs_bot:changer3"] = "3",
["signs_bot:changer4"] = "4",
}
local formspec = "size[8,7]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[1,1.3;"..S("Signs:").."]"..
"label[2.6,0.7;1]label[5.1,0.7;2]"..
"list[context;sign;3,0.5;2,2;]"..
"label[2.6,1.7;3]label[5.1,1.7;4]"..
"list[current_player;main;0,3;8,4;]"..
"listring[context;main]"..
"listring[current_player;main]"
-- Get one sign from the robot signs inventory
local function get_inv_sign(pos, slot)
local inv = minetest.get_inventory({type="node", pos=pos})
if inv then
local stack = inv:get_stack("sign", slot)
local taken = stack:take_item(1)
inv:set_stack("sign", slot, stack)
return taken
end
end
local function put_inv_sign(pos, slot, sign)
local inv = minetest.get_inventory({type="node", pos=pos})
if inv then
inv:set_stack("sign", slot, sign)
end
end
local function signs_bot_get_signal(pos, node)
return NodeIdx[node.name]
end
-- To be called from sensors
local function signs_bot_on_signal(pos, node, signal)
-- swap changer
local old_idx = NodeIdx[node.name]
local new_idx = tonumber(signal)
node.name = "signs_bot:changer"..signal
if old_idx and NodeIdx[node.name] then
local pos1 = lib.next_pos(pos, M(pos):get_int("param2"))
minetest.swap_node(pos, node)
-- swap sign
local param2 = minetest.get_node(pos1).param2
local sign = lib.dig_sign(pos1)
if sign then
M(pos):set_int("sign_param2_"..old_idx, param2)
put_inv_sign(pos, old_idx, sign)
end
sign = get_inv_sign(pos, new_idx)
if sign:get_count() == 1 then
lib.place_sign(pos1, sign, M(pos):get_int("sign_param2_"..signal))
end
end
end
local function swap_node(pos, node)
local slot = NodeIdx[node.name]
if slot then
local new_idx = (tonumber(slot) % 4) + 1
signs_bot_on_signal(pos, node, new_idx)
end
end
local function allow_metadata_inventory()
return 0
end
local function can_dig(pos)
local inv = minetest.get_inventory({type="node", pos=pos})
return inv:is_empty("sign")
end
for idx = 1,4 do
local not_in_inv = idx == 1 and 0 or 1
minetest.register_node("signs_bot:changer"..idx, {
description = S("Bot Control Unit"),
inventory_image = "signs_bot_ctrl_unit_inv.png",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor3.png^signs_bot_changer"..idx..".png",
"signs_bot_sensor3.png",
"signs_bot_sensor3.png^[transformFXR90",
"signs_bot_sensor3.png^[transformFXR90",
"signs_bot_sensor3.png^[transformFXR90",
"signs_bot_sensor3.png^[transformFXR180",
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size('sign', 4)
end,
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_string("formspec", formspec)
local node = minetest.get_node(pos)
meta:set_int("param2", (node.param2 + 2) % 4)
if minetest.get_modpath("techage") then
techage.logic.after_place_node(pos, placer, node.name, S("Bot Control Unit"))
techage.logic.infotext(meta, S("Bot Control Unit"))
end
end,
signs_bot_get_signal = signs_bot_get_signal,
signs_bot_on_signal = signs_bot_on_signal,
allow_metadata_inventory_put = allow_metadata_inventory,
allow_metadata_inventory_take = allow_metadata_inventory,
on_punch = swap_node,
can_dig = can_dig,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
is_ground_content = false,
groups = {cracky = 1, not_in_creative_inventory = not_in_inv},
drop = "signs_bot:changer1",
sounds = default.node_sound_metal_defaults(),
})
end
minetest.register_craft({
output = "signs_bot:changer1",
recipe = {
{"", "", ""},
{"dye:yellow", "group:wood", "dye:black"},
{"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"}
}
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "changer", {
name = S("Bot Control Unit"),
data = {
item = "signs_bot:changer1",
text = table.concat({
S("The Bot Control Unit is used to lead the bot by means of signs."),
S("The unit can be loaded with up to 4 different signs and can be programmed by means of sensors."),
"",
S("To load the unit, place a sign on the red side of the unit and click on the unit."),
S("The sign disappears / is moved to the inventory of the unit."),
S("This can be repeated 3 times."),
"",
S("Use the connection tool to connect up to 4 sensors with the Bot Control Unit."),
}, "\n")
},
})
end
if minetest.get_modpath("techage") then
techage.register_node({"signs_bot:changer1", "signs_bot:changer2", "signs_bot:changer3", "signs_bot:changer4"}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "set" then
local idx = tonumber(payload) or 1
if idx >= 1 and idx <= 4 then
local node = techage.get_node_lvm(pos)
signs_bot_on_signal(pos, node, idx)
return true
end
elseif topic == "info" then
return INFO
else
return "unsupported"
end
end,
})
end
+259
View File
@@ -0,0 +1,259 @@
--[[
Signs Bot
=========
Copyright (C) 2019-0221 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signs Bot Chest
]]--
-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S2P = minetest.string_to_pos
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local NODE_IO = minetest.global_exists("node_io")
local function get_inv_state(pos)
local inv = minetest.get_inventory({type="node", pos=pos})
if not inv then return "almost full" end
if inv:is_empty("main") then
return "empty"
else
local list = inv:get_list("main")
for _, item in ipairs(list) do
if item:is_empty() then
return "not empty"
end
end
end
return "almost full"
end
local function check_state(pos)
local state = M(pos):get_string("state")
if state == get_inv_state(pos) then
signs_bot.send_signal(pos)
signs_bot.lib.activate_extender_nodes(pos, true)
end
end
local function update_infotext(pos, dest_pos, cmnd)
local meta = M(pos)
local state = get_inv_state(pos)
meta:set_string("infotext", S("Bot Chest: Sends signal to").." "..P2S(dest_pos).." / "..cmnd..", if "..state)
meta:set_string("state", state)
end
local function formspec()
return "size[9,8]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"list[context;main;0.5,0;8,4;]"..
"list[current_player;main;0.5,4.3;8,4;]"..
"listring[context;main]"..
"listring[current_player;main]"
end
if NODE_IO then
minetest.register_node("signs_bot:chest", {
description = S("Signs Bot Chest"),
tiles = {
-- up, down, right, left, back, front
'signs_bot_chest_top.png',
'signs_bot_chest_top.png',
'signs_bot_chest_side.png',
'signs_bot_chest_side.png',
'signs_bot_chest_side.png',
'signs_bot_chest_front.png',
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size('main', 32)
end,
after_place_node = function(pos, placer)
local mem = tubelib2.init_mem(pos)
mem.running = false
local meta = minetest.get_meta(pos)
meta:set_string("owner", placer:get_player_name())
meta:set_string("formspec", formspec(pos, mem))
meta:set_string("infotext", S("Bot Chest: Not connected"))
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list)
check_state(pos)
end,
on_metadata_inventory_put = function(pos, listname)
check_state(pos)
end,
on_metadata_inventory_take = function(pos, listname)
check_state(pos)
end,
node_io_can_put_item = function(pos, node, side) return true end,
node_io_room_for_item = function(pos, node, side, itemstack, count)
local inv = minetest.get_meta(pos):get_inventory()
if not inv then return 0 end
return node_io.room_for_item_in_inventory(inv, "main", itemstack, count)
end,
node_io_put_item = function(pos, node, side, putter, itemstack)
local owner = M(pos):get_string("owner")
if owner == putter:get_player_name() then
local left_over = node_io.put_item_in_inventory(pos, node, "main", putter, itemstack)
check_state(pos)
return left_over
end
end,
node_io_can_take_item = function(pos, node, side) return true end,
node_io_get_item_size = function(pos, node, side)
return node_io.get_inventory_size(pos, "main")
end,
node_io_get_item_name = function(pos, node, side, index)
return node_io.get_inventory_name(pos, "main", index)
end,
node_io_get_item_stack = function(pos, node, side, index)
return node_io.get_inventory_stack(pos, "main", index)
end,
node_io_take_item = function(pos, node, side, taker, want_item, want_count)
local owner = M(pos):get_string("owner")
if owner == taker:get_player_name() then
local left_over = node_io.take_item_from_inventory(pos, node, "main", taker, want_item, want_count)
check_state(pos)
return left_over
end
end,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype2 = "facedir",
is_ground_content = false,
groups = {cracky = 1, sign_bot_sensor = 1},
sounds = default.node_sound_metal_defaults(),
})
else
minetest.register_node("signs_bot:chest", {
description = S("Signs Bot Chest"),
tiles = {
-- up, down, right, left, back, front
'signs_bot_chest_top.png',
'signs_bot_chest_top.png',
'signs_bot_chest_side.png',
'signs_bot_chest_side.png',
'signs_bot_chest_side.png',
'signs_bot_chest_front.png',
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size('main', 32)
end,
after_place_node = function(pos, placer)
local mem = tubelib2.init_mem(pos)
mem.running = false
local meta = minetest.get_meta(pos)
meta:set_string("owner", placer:get_player_name())
meta:set_string("formspec", formspec(pos, mem))
meta:set_string("infotext", S("Bot Chest: Not connected"))
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list)
check_state(pos)
end,
on_metadata_inventory_put = function(pos, listname)
check_state(pos)
end,
on_metadata_inventory_take = function(pos, listname)
check_state(pos)
end,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype2 = "facedir",
is_ground_content = false,
groups = {cracky = 1, sign_bot_sensor = 1},
sounds = default.node_sound_metal_defaults(),
})
end
signs_bot.register_inventory({"signs_bot:chest"}, {
put = {
allow_inventory_put = function(pos, stack, player_name)
local owner = M(pos):get_string("owner")
minetest.after(1, check_state, pos)
return owner == player_name
end,
listname = "main",
},
take = {
allow_inventory_take = function(pos, stack, player_name)
local owner = M(pos):get_string("owner")
minetest.after(1, check_state, pos)
return owner == player_name
end,
listname = "main",
},
})
minetest.register_craft({
output = "signs_bot:chest",
recipe = {
{"default:steel_ingot", "", "default:steel_ingot"},
{"", "default:chest", ""},
{"default:tin_ingot", "", "default:tin_ingot"}
}
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "chest", {
name = S("Signs Bot Chest"),
data = {
item = "signs_bot:chest",
text = table.concat({
S("The Signs Bot Chest is a special chest with sensor function."),
S("It sends a signal depending on the chest state."),
S("Possible states are 'empty', 'not empty', 'almost full'"),
"",
S("A typical use case is to turn off the bot, when the chest is almost full or empty."),
}, "\n")
},
})
end
@@ -0,0 +1,206 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Bot farming commands
]]--
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
local not_farming_redo = farming.mod ~= "redo"
local bot_inv_put_item = signs_bot.bot_inv_put_item
local bot_inv_take_item = signs_bot.bot_inv_take_item
local function soil_availabe(pos, trellis)
local node = minetest.get_node_or_nil(pos)
if node.name == (trellis or "air") then
node = minetest.get_node_or_nil({x=pos.x, y=pos.y-1, z=pos.z})
if node and minetest.get_item_group(node.name, "soil") >= 1 then
return true
end
end
return false
end
local function planting(base_pos, mem, slot)
local pos = mem.pos_tbl and mem.pos_tbl[mem.steps]
mem.steps = (mem.steps or 1) + 1
if pos and lib.not_protected(base_pos, pos) then
local stack = bot_inv_take_item(base_pos, slot, 1)
if stack and stack ~= "" then
local plant = stack:get_name()
if plant then
local item = signs_bot.FarmingSeed[plant]
if item and soil_availabe(pos, signs_bot.FarmingNeedTrellis[item]) then
if minetest.registered_nodes[item] then
local p2 = minetest.registered_nodes[item].place_param2 or 1
minetest.set_node(pos, {name = item, param2 = p2})
else
minetest.set_node(pos, {name = item})
end
minetest.sound_play("default_place_node", {pos = pos, gain = 1.0})
if not_farming_redo then
minetest.get_node_timer(pos):start(math.random(166, 286))
end
else
bot_inv_put_item(base_pos, 0, ItemStack(plant))
end
end
end
end
end
signs_bot.register_botcommand("sow_seed", {
mod = "farming",
params = "<slot>",
num_param = 1,
description = S("Sow farming seeds\nin front of the robot"),
check = function(slot)
slot = tonumber(slot)
return slot and slot > 0 and slot < 9
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot)
if not mem.steps then
mem.pos_tbl = signs_bot.lib.gen_position_table(mem.robot_pos, mem.robot_param2, 3, 3, 0)
mem.steps = 1
end
mem.pos_tbl = mem.pos_tbl or {}
planting(base_pos, mem, slot)
if mem.steps > #mem.pos_tbl then
mem.steps = nil
return signs_bot.DONE
end
return signs_bot.BUSY
end,
})
local function harvesting(base_pos, mem)
local pos = mem.pos_tbl and mem.pos_tbl[mem.steps]
mem.steps = (mem.steps or 1) + 1
if pos and lib.not_protected(base_pos, pos) then
local node = minetest.get_node_or_nil(pos)
if signs_bot.FarmingCrop[node.name] then
local trellis = signs_bot.FarmingKeepTrellis[node.name]
if trellis then
minetest.set_node(pos, {name = trellis})
elseif not trellis then
minetest.remove_node(pos)
end
-- Do not cache the result of get_node_drops; it is a probabilistic function!
local drops = minetest.get_node_drops(node.name)
for _,itemstring in ipairs(drops) do
if not trellis or trellis ~= itemstring then
local leftover = bot_inv_put_item(base_pos, 0, ItemStack(itemstring))
if leftover and leftover:get_count() > 0 then
signs_bot.lib.drop_items(mem.robot_pos, leftover)
end
end
end
end
end
end
signs_bot.register_botcommand("harvest", {
mod = "farming",
params = "",
num_param = 0,
description = S("Harvest farming products\nin front of the robot\non a 3x3 field."),
cmnd = function(base_pos, mem)
if not mem.steps then
mem.pos_tbl = signs_bot.lib.gen_position_table(mem.robot_pos, mem.robot_param2, 3, 3, 0)
mem.steps = 1
end
mem.pos_tbl = mem.pos_tbl or {}
harvesting(base_pos, mem)
if mem.steps > #mem.pos_tbl then
mem.steps = nil
return signs_bot.DONE
end
return signs_bot.BUSY
end,
})
local function plant_sapling(base_pos, mem, slot)
local pos = lib.dest_pos(mem.robot_pos, mem.robot_param2, {0})
if lib.not_protected(base_pos, pos) and soil_availabe(pos) then
local stack = bot_inv_take_item(base_pos, slot, 1)
local item = stack and signs_bot.TreeSaplings[stack:get_name()]
if item and item.sapling then
minetest.set_node(pos, {name = item.sapling, paramtype2 = "wallmounted", param2 = 1})
if item.t1 ~= nil then
-- We have to simulate "on_place" and start the timer by hand
-- because the after_place_node function checks player rights and can't therefore
-- be used.
minetest.get_node_timer(pos):start(math.random(item.t1, item.t2))
end
end
end
end
signs_bot.register_botcommand("plant_sapling", {
mod = "farming",
params = "<slot>",
num_param = 1,
description = S("Plant a sapling\nin front of the robot"),
check = function(slot)
slot = tonumber(slot)
return slot and slot > 0 and slot < 9
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot)
plant_sapling(base_pos, mem, slot)
return signs_bot.DONE
end,
})
local CMD = [[dig_sign 1
move
harvest
sow_seed 1
backward
place_sign 1
turn_around]]
signs_bot.register_sign({
name = "farming",
description = S('Sign "farming"'),
commands = CMD,
image = "signs_bot_sign_farming.png",
})
minetest.register_craft({
output = "signs_bot:farming 2",
recipe = {
{"group:wood", "default:stick", "group:wood"},
{"dye:black", "default:stick", "dye:yellow"},
{"dye:grey", "", ""}
}
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "farming", {
name = S("Sign 'farming'"),
data = {
item = "signs_bot:farming",
text = table.concat({
S("Used to harvest and seed a 3x3 field."),
S("Place the sign in front of the field."),
S("The seed to be placed has to be in the first inventory slot of the bot."),
S("When finished, the bot turns."),
}, "\n")
},
})
end
@@ -0,0 +1,155 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2020 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Bot flower cutting command
]]--
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
local bot_inv_put_item = signs_bot.bot_inv_put_item
local bot_inv_take_item = signs_bot.bot_inv_take_item
local Flowers = {}
-- Special drop handling is necessary because of waterlily.
function signs_bot.register_flower(name)
local drop = signs_bot.lib.is_simple_node({name = name})
if drop then
Flowers[name] = drop
end
end
signs_bot.register_flower("default:bush_stem")
signs_bot.register_flower("default:acacia_bush_stem")
signs_bot.register_flower("default:pine_bush_stem")
minetest.after(1, function()
local function add_flower(name)
local def = minetest.registered_nodes[name]
if def and (def.groups.mushroom == 1 or def.groups.flower == 1) then
signs_bot.register_flower(name)
end
end
for _,def in pairs(minetest.registered_decorations) do
local name = def.decoration
if type(name) == "string" then
add_flower(name)
elseif type(name) == "table" then
for _,sub_name in ipairs(name) do
add_flower(sub_name)
end
end
end
end)
local function is_tree(node)
if minetest.get_item_group(node.name, "tree") == 1 then
return signs_bot.handle_drop_like_a_player(node)
end
if minetest.get_item_group(node.name, "leaves") == 1 then
return signs_bot.handle_drop_like_a_player(node)
end
end
local function harvesting(base_pos, mem)
local pos = mem.pos_tbl and mem.pos_tbl[mem.steps]
mem.steps = (mem.steps or 1) + 1
if pos and lib.not_protected(base_pos, pos) then
local node = minetest.get_node_or_nil(pos)
if node.name ~= "default:papyrus" then
local drop = Flowers[node.name] or is_tree(node)
if drop then
minetest.remove_node(pos)
local leftover = bot_inv_put_item(base_pos, 0, ItemStack(drop))
if leftover and leftover:get_count() > 0 then
signs_bot.lib.drop_items(mem.robot_pos, leftover)
end
end
else
-- papyrus is a special plant that is collected upwards when cut
local count = 0
while node.name == "default:papyrus" and lib.not_protected(base_pos, pos) do
minetest.remove_node(pos)
pos = { x = pos.x, y = pos.y + 1, z = pos.z }
count = count + 1
node = minetest.get_node(pos)
end
if count > 0 then
local leftover = bot_inv_put_item(base_pos, 0, ItemStack("default:papyrus " .. count))
if leftover and leftover:get_count() > 0 then
signs_bot.lib.drop_items(mem.robot_pos, leftover)
end
end
end
end
end
signs_bot.register_botcommand("cutting", {
mod = "farming",
params = "",
num_param = 0,
description = S("Cutting flowers, papyrus,\nleaves and tree blocks\nin front of the robot\non a 3x3 field."),
cmnd = function(base_pos, mem)
if not mem.steps then
mem.pos_tbl = signs_bot.lib.gen_position_table(mem.robot_pos, mem.robot_param2, 3, 3, 0)
mem.steps = 1
end
mem.pos_tbl = mem.pos_tbl or {}
harvesting(base_pos, mem)
if mem.steps > #mem.pos_tbl then
mem.steps = nil
return signs_bot.DONE
end
return signs_bot.BUSY
end,
})
local CMD = [[dig_sign 1
move
cutting
backward
place_sign 1
turn_around]]
signs_bot.register_sign({
name = "flowers",
description = S('Sign "flowers"'),
commands = CMD,
image = "signs_bot_sign_flowers.png",
})
minetest.register_craft({
output = "signs_bot:flowers 2",
recipe = {
{"group:wood", "default:stick", "group:wood"},
{"dye:black", "default:stick", "dye:yellow"},
{"dye:red", "", ""}
}
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "flowers", {
name = S("Sign 'flowers'"),
data = {
item = "signs_bot:flowers",
text = table.concat({
S("Used to cut flowers on a 3x3 field."),
S("Place the sign in front of the field."),
S("When finished, the bot turns."),
}, "\n")
},
})
end
+328
View File
@@ -0,0 +1,328 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signs Bot: More commands
]]--
-- for lazy programmers
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
local RegisteredInventories = {}
--
-- Move from/to inventories
--
-- From chest to robot
function signs_bot.robot_take(base_pos, robot_pos, param2, want_count, slot)
local target_pos = lib.next_pos(robot_pos, param2)
local node = tubelib2.get_node_lvm(target_pos)
local def = RegisteredInventories[node.name]
local owner = M(base_pos):get_string("owner")
-- Is known type of inventory node?
if def and (not def.allow_take or def.allow_take(target_pos, nil, owner)) then
local src_inv = minetest.get_inventory({type="node", pos=target_pos})
-- take specified item_name from bot slot configuration OR any item from the chest
local item_name = signs_bot.bot_inv_item_name(base_pos, slot) or lib.peek_inv(src_inv, def.take_listname)
if item_name then
local taken = src_inv:remove_item(def.take_listname, ItemStack(item_name.." "..want_count))
local leftover = signs_bot.bot_inv_put_item(base_pos, slot, taken)
src_inv:add_item(def.take_listname, leftover)
end
end
end
-- From robot to chest
function signs_bot.robot_put(base_pos, robot_pos, param2, num, slot)
local target_pos = lib.next_pos(robot_pos, param2)
local node = tubelib2.get_node_lvm(target_pos)
local def = RegisteredInventories[node.name]
local owner = M(base_pos):get_string("owner")
local taken = signs_bot.bot_inv_take_item(base_pos, slot, num)
-- Is known type of inventory node?
if taken and def and (not def.allow_put or def.allow_put(target_pos, taken, owner)) then
local dst_inv = minetest.get_inventory({type="node", pos=target_pos})
local leftover = dst_inv and dst_inv:add_item(def.put_listname, taken)
if leftover and leftover:get_count() > 0 then
signs_bot.bot_inv_put_item(base_pos, slot, leftover)
end
elseif taken then
signs_bot.bot_inv_put_item(base_pos, slot, taken)
end
end
function signs_bot.robot_peek(base_pos, robot_pos, param2, want_count, slot)
local target_pos = lib.next_pos(robot_pos, param2)
local node = tubelib2.get_node_lvm(target_pos)
local def = RegisteredInventories[node.name]
local owner = M(base_pos):get_string("owner")
-- Is known type of inventory node?
if def and (not def.allow_take or def.allow_take(target_pos, nil, owner)) then
local src_inv = minetest.get_inventory({type="node", pos=target_pos})
-- take specified item_name from bot slot configuration OR any item from the chest
local item_name = signs_bot.bot_inv_item_name(base_pos, slot) or lib.peek_inv(src_inv, def.take_listname)
if item_name then
return src_inv:contains_item(def.take_listname, ItemStack(item_name.." "..want_count))
end
end
end
-- From robot to furnace
function signs_bot.robot_put_fuel(base_pos, robot_pos, param2, num, slot)
local target_pos = lib.next_pos(robot_pos, param2)
local node = tubelib2.get_node_lvm(target_pos)
local def = RegisteredInventories[node.name]
local owner = M(base_pos):get_string("owner")
local taken = signs_bot.bot_inv_take_item(base_pos, slot, num)
-- Is known type of inventory node?
if taken and def and (not def.allow_fuel or def.allow_fuel(target_pos, taken, owner)) then
local dst_inv = minetest.get_inventory({type="node", pos=target_pos})
local leftover = dst_inv and dst_inv:add_item(def.fuel_listname, taken)
if leftover and leftover:get_count() > 0 then
signs_bot.bot_inv_put_item(base_pos, slot, leftover)
end
end
end
signs_bot.register_botcommand("take_item", {
mod = "item",
params = "<num> <slot>",
num_param = 2,
description = S("Take <num> items from a chest like node\nand put it into the item inventory.\n"..
"<slot> is the bot inventory slot\n(1..8) or 0 for any one"),
check = function(num, slot)
num = tonumber(num) or 1
if num < 1 or num > 99 then
return false
end
slot = tonumber(slot) or 0
if slot < 0 or slot > 8 then
return false
end
return true
end,
cmnd = function(base_pos, mem, num, slot)
num = tonumber(num) or 1
slot = tonumber(slot) or 0
signs_bot.robot_take(base_pos, mem.robot_pos, mem.robot_param2, num, slot)
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("jump_check_item", {
mod = "item",
params = "<num> <slot> <label>",
num_param = 3,
description = S("Check if there are <num>\n"..
"items in the chest like node.\n"..
"If not, jump to <label>\n"..
"<slot> is the bot inventory slot\n"..
"(1..8) to specify the item, or 0 for any item"),
check = function(num, slot, lbl)
num = tonumber(num) or 1
if num < 1 or num > 99 then
return false
end
slot = tonumber(slot) or 0
if slot < 0 or slot > 8 then
return false
end
return signs_bot.check_label(lbl)
end,
cmnd = function(base_pos, mem, num, slot, addr)
num = tonumber(num) or 1
if not signs_bot.robot_peek(base_pos, mem.robot_pos, mem.robot_param2, num, slot) then
mem.pc = (addr or 4) - 4
end
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("add_item", {
mod = "item",
params = "<num> <slot>",
num_param = 2,
description = S("Add <num> items to a chest like node\ntaken from the item inventory.\n"..
"<slot> is the bot inventory slot (1..8) or 0 for any one"),
check = function(num, slot)
num = tonumber(num) or 1
if num < 1 or num > 99 then
return false
end
slot = tonumber(slot) or 0
if slot < 0 or slot > 8 then
return false
end
return true
end,
cmnd = function(base_pos, mem, num, slot)
num = tonumber(num) or 1
slot = tonumber(slot) or 0
signs_bot.robot_put(base_pos, mem.robot_pos, mem.robot_param2, num, slot)
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("add_fuel", {
mod = "item",
params = "<num> <slot>",
num_param = 2,
description = S("Add <num> fuel to a furnace like node\ntaken from the item inventory.\n"..
"<slot> is the bot inventory slot (1..8) or 0 for any one"),
check = function(num, slot)
num = tonumber(num) or 1
if num < 1 or num > 99 then
return false
end
slot = tonumber(slot) or 0
if slot < 0 or slot > 8 then
return false
end
return true
end,
cmnd = function(base_pos, mem, num, slot)
num = tonumber(num) or 1
slot = tonumber(slot) or 0
signs_bot.robot_put_fuel(base_pos, mem.robot_pos, mem.robot_param2, num, slot)
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("cond_take_item", {
mod = "item",
params = "<num> <slot>",
num_param = 2,
description = S("deprecated, use bot inventory configuration instead"),
check = function(num, slot)
return false
end,
cmnd = function(base_pos, mem, num, slot)
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("cond_add_item", {
mod = "item",
params = "<num> <slot>",
num_param = 2,
description = S("deprecated, use bot inventory configuration instead"),
check = function(num, slot)
return false
end,
cmnd = function(base_pos, mem, num, slot)
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("pickup_items", {
mod = "item",
params = "<slot>",
num_param = 1,
description = S("Pick up all objects\n"..
"in a 3x3 field.\n"..
"<slot> is the bot inventory slot (1..8) or 0 for any one"),
check = function(slot)
slot = tonumber(slot) or 0
return slot >= 0 and slot < 9
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot) or 0
local pos = lib.dest_pos(mem.robot_pos, mem.robot_param2, {0,0})
for _, object in pairs(minetest.get_objects_inside_radius(pos, 2.5)) do
local lua_entity = object:get_luaentity()
if not object:is_player() and lua_entity and lua_entity.name == "__builtin:item" then
local item = ItemStack(lua_entity.itemstring)
local leftover = signs_bot.bot_inv_put_item(base_pos, slot, item)
if leftover:get_count() == 0 then
object:remove()
end
end
end
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("drop_items", {
mod = "item",
params = "<num> <slot>",
num_param = 2,
description = S("Drop items in front of the bot.\n"..
"<slot> is the bot inventory slot (1..8) or 0 for any one"),
check = function(num, slot)
num = tonumber(num) or 1
if num < 1 or num > 99 then
return false
end
slot = tonumber(slot) or 0
if slot < 0 or slot > 8 then
return false
end
return true
end,
cmnd = function(base_pos, mem, num, slot)
num = tonumber(num) or 1
slot = tonumber(slot) or 0
local pos = lib.dest_pos(mem.robot_pos, mem.robot_param2, {0})
local items = signs_bot.bot_inv_take_item(base_pos, slot, num)
minetest.add_item(pos, items)
return signs_bot.DONE
end,
})
if minetest.global_exists("minecart") then
signs_bot.register_botcommand("punch_cart", {
mod = "item",
params = "",
num_param = 0,
description = S("Punch a rail cart to start it"),
cmnd = function(base_pos, mem)
local punch_dir = minetest.facedir_to_dir(mem.robot_param2)
minecart.punch_cart(mem.robot_pos, mem.robot_param2, 1, punch_dir)
return signs_bot.DONE
end,
})
end
-- def is a table with following data:
-- {
-- put = {
-- allow_inventory_put = func(pos, stack, player_name),
-- listname = "src",
-- },
-- take = {
-- allow_inventory_take = func(pos, stack, player_name),
-- listname = "dst",
-- fuel = {
-- allow_inventory_put = func(pos, stack, player_name),
-- listname = "fuel",
-- },
-- }
function signs_bot.register_inventory(node_names, def)
for _, name in ipairs(node_names) do
RegisteredInventories[name] = {
allow_put = def.put and def.put.allow_inventory_put,
put_listname = def.put and def.put.listname,
allow_take = def.take and def.take.allow_inventory_take,
take_listname = def.take and def.take.listname,
allow_fuel = def.fuel and def.fuel.allow_inventory_put,
fuel_listname = def.fuel and def.fuel.listname,
}
end
end
+360
View File
@@ -0,0 +1,360 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2024 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Bot move commands
]]--
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
local get_node_lvm = tubelib2.get_node_lvm
local function node_and_pos(pos)
return get_node_lvm(pos), pos
end
-- Positions to check:
-- 5 6
-- [R]1
-- 3 2
-- 4
function signs_bot.move_robot(mem)
local param2 = mem.robot_param2
local pos = mem.robot_pos
local node1, pos1 = node_and_pos(lib.next_pos(pos, param2))
local node2, pos2 = node_and_pos({x=pos1.x, y=pos1.y-1, z=pos1.z})
local node3, pos3 = node_and_pos({x=pos.x, y=pos.y-1, z=pos.z})
--
-- One step forward (pos1)
--
if lib.check_pos(pos1, node1, node2, param2) or
lib.check_pos(pos1, node1, node3, param2) then
if node3.name == "signs_bot:robot_foot" then
lib.swap_node(pos3, mem.stored_node or {name = "air"})
minetest.remove_node(pos)
elseif node3.name == "signs_bot:robot_leg" then
local node4, pos4 = node_and_pos({x=pos.x, y=pos.y-2, z=pos.z})
if node4.name == "signs_bot:robot_foot" then
lib.swap_node(pos4, mem.stored_node or {name = "air"})
end
minetest.swap_node(pos3, {name = "air"})
minetest.remove_node(pos)
else
lib.swap_node(pos, mem.stored_node or {name = "air"})
end
mem.stored_node = node1
minetest.set_node(pos1, {name="signs_bot:robot", param2=param2})
minetest.sound_play('signs_bot_step', {pos = pos1})
return pos1
end
--
-- One step up (pos5)
--
local node6, pos6 = node_and_pos({x=pos1.x, y=pos1.y+1, z=pos1.z})
if lib.check_pos(pos6, node6, node1, param2) then
local node5, pos5 = node_and_pos({x=pos.x, y=pos.y+1, z=pos.z})
if node5.name == "air" then
if node3.name == "signs_bot:robot_leg" then
return nil
elseif node3.name == "signs_bot:robot_foot" then
minetest.swap_node(pos, {name="signs_bot:robot_leg"})
else
minetest.swap_node(pos, {name="signs_bot:robot_foot"})
end
minetest.set_node(pos5, {name="signs_bot:robot", param2=param2})
minetest.sound_play('signs_bot_step', {pos = pos5})
return pos5
end
end
--
-- One step down I (pos3)
--
local node4, pos4 = node_and_pos({x=pos.x, y=pos.y-2, z=pos.z})
if lib.check_pos(pos3, node3, node4, param2) then --
minetest.remove_node(pos)
minetest.set_node(pos3, {name="signs_bot:robot", param2=param2})
minetest.sound_play('signs_bot_step', {pos = pos3})
mem.stored_node = node3
return pos3
end
--
-- One step down II (pos3)
--
if node3.name == "signs_bot:robot_foot" or node3.name == "signs_bot:robot_leg" then
minetest.remove_node(pos)
minetest.set_node(pos3, {name="signs_bot:robot", param2=param2})
minetest.sound_play('signs_bot_step', {pos = pos3})
return pos3
end
end
local function backward_robot(mem)
local param2 = mem.robot_param2
local pos = mem.robot_pos
local node1, pos1 = node_and_pos(lib.next_pos(pos, (param2 + 2) % 4))
local node2, pos2 = node_and_pos({x=pos1.x, y=pos1.y-1, z=pos1.z})
local node3, pos3 = node_and_pos({x=pos.x, y=pos.y-1, z=pos.z})
local node4, pos4 = node_and_pos({x=pos.x, y=pos.y-2, z=pos.z})
local new_pos = nil
if lib.check_pos(pos1, node1, node2, param2) then
if node3.name == "signs_bot:robot_foot" or node3.name == "signs_bot:robot_leg" then
minetest.remove_node(pos3)
if node4.name == "signs_bot:robot_foot" then
minetest.remove_node(pos4)
end
end
lib.swap_node(pos, mem.stored_node or {name = "air"})
minetest.set_node(pos1, {name="signs_bot:robot", param2=param2})
minetest.sound_play('signs_bot_step', {pos = pos1})
mem.stored_node = node1
return pos1
end
end
signs_bot.register_botcommand("backward", {
mod = "move",
params = "",
num_param = 0,
description = S("Move the robot one step back"),
cmnd = function(base_pos, mem)
local new_pos = backward_robot(mem)
if new_pos then -- not blocked?
mem.robot_pos = new_pos
end
return signs_bot.DONE
end,
})
local function turn_robot(pos, param2, dir)
if dir == "R" then
param2 = (param2 + 1) % 4
else
param2 = (param2 + 3) % 4
end
minetest.swap_node(pos, {name="signs_bot:robot", param2=param2})
minetest.sound_play('signs_bot_step', {pos = pos, gain = 0.6})
return param2
end
signs_bot.register_botcommand("turn_left", {
mod = "move",
params = "",
num_param = 0,
description = S("Turn the robot to the left"),
cmnd = function(base_pos, mem)
mem.robot_param2 = turn_robot(mem.robot_pos, mem.robot_param2, "L")
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("turn_right", {
mod = "move",
params = "",
num_param = 0,
description = S("Turn the robot to the right"),
cmnd = function(base_pos, mem)
mem.robot_param2 = turn_robot(mem.robot_pos, mem.robot_param2, "R")
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("turn_around", {
mod = "move",
params = "",
num_param = 0,
description = S("Turn the robot around"),
cmnd = function(base_pos, mem)
mem.robot_param2 = turn_robot(mem.robot_pos, mem.robot_param2, "R")
mem.robot_param2 = turn_robot(mem.robot_pos, mem.robot_param2, "R")
return signs_bot.DONE
end,
})
-- Positions to check:
-- 1
-- [R]
-- 2
local function robot_up(pos, param2)
local node1, pos1 = node_and_pos({x=pos.x, y=pos.y+1, z=pos.z})
local node2, pos2 = node_and_pos({x=pos.x, y=pos.y-1, z=pos.z})
if lib.check_pos(pos1, node1, node2, param2) then
if node2.name == "signs_bot:robot_leg" then
return nil
elseif node2.name == "signs_bot:robot_foot" then
minetest.swap_node(pos, {name="signs_bot:robot_leg"})
else
minetest.swap_node(pos, {name="signs_bot:robot_foot"})
end
minetest.set_node(pos1, {name="signs_bot:robot", param2=param2})
minetest.sound_play('signs_bot_step', {pos = pos1})
return pos1
end
return nil
end
signs_bot.register_botcommand("move_up", {
mod = "move",
params = "",
num_param = 0,
description = S("Move the robot upwards"),
cmnd = function(base_pos, mem)
local new_pos = robot_up(mem.robot_pos, mem.robot_param2)
if new_pos then -- not blocked?
mem.robot_pos = new_pos
end
return signs_bot.DONE
end,
})
-- Positions to check:
-- [R]
-- 1
-- 2
-- 3
local function robot_down(pos, param2)
local node1, pos1 = node_and_pos({x=pos.x, y=pos.y-1, z=pos.z})
local node2, pos2 = node_and_pos({x=pos.x, y=pos.y-2, z=pos.z})
local node3, pos3 = node_and_pos({x=pos.x, y=pos.y-3, z=pos.z})
if lib.check_pos(pos1, node1, node2, param2)
or (node1.name == "air" and lib.check_pos(pos2, node2, node3, param2))
or (node1.name == "signs_bot:robot_leg" or node1.name == "signs_bot:robot_foot") then
minetest.remove_node(pos)
minetest.set_node(pos1, {name="signs_bot:robot", param2=param2})
minetest.sound_play('signs_bot_step', {pos = pos1})
return pos1
end
return nil
end
signs_bot.register_botcommand("move_down", {
mod = "move",
params = "",
num_param = 0,
description = S("Move the robot down"),
cmnd = function(base_pos, mem)
local new_pos = robot_down(mem.robot_pos, mem.robot_param2)
if new_pos then -- not blocked?
mem.robot_pos = new_pos
end
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("fall_down", {
mod = "move",
params = "",
num_param = 0,
description = S("Fall into a hole/chasm (up to 10 blocks)"),
cmnd = function(base_pos, mem)
if not mem.bot_falling then
--Run a while loop that checks the 10 nodes below bot for a node with the walkable property, breaking the loop once it finds a walkable node.
local fallcounter = 0
local fallnode = {walkable = false}
while fallcounter <= 9 and fallnode.walkable == false do
fallcounter = fallcounter + 1
--Pulls the node name from the next position, then assigns the node definintion to the fallnode variable.
fallnode = minetest.get_node_or_nil({x=mem.robot_pos.x, y=mem.robot_pos.y-fallcounter, z=mem.robot_pos.z})
fallnode = minetest.registered_nodes[fallnode.name]
end
--If the first nine nodes below the bot are not walkable, then it should assign the definintion to the 10th node. If it too is not walkable, then a "Too Deep" error is returned.
if fallnode.walkable == false then
return signs_bot.ERROR, "Too deep"
end
--Designates the node above the walkable node as the new location for the bot.
local pos3 = {x=mem.robot_pos.x, y=mem.robot_pos.y-fallcounter+1, z=mem.robot_pos.z}
--Turns the bot into a falling node.
local sts, _ = minetest.spawn_falling_node(mem.robot_pos)
--Stores the data for the node the bot will land in and replaces it with air. This way if the node the bot lands in is occupied by an unwalkable node, such as a rail or sign,
--it will be repalced when the bot moves.
mem.stored_node = get_node_lvm(pos3)
minetest.swap_node(pos3, {name="air"})
if sts then
mem.bot_falling = 2
mem.robot_pos = {x=pos3.x, y=pos3.y, z=pos3.z}
return signs_bot.BUSY
end
else
mem.bot_falling = mem.bot_falling - 1
if mem.bot_falling <= 0 then
mem.bot_falling = nil
return signs_bot.DONE
end
return signs_bot.BUSY
end
end,
})
signs_bot.register_botcommand("pause", {
mod = "move",
params = "<sec>",
num_param = 1,
description = S("Stop the robot for <sec> seconds\n(1..9999)"),
check = function(sec)
sec = tonumber(sec) or 1
return sec and sec > 0 and sec < 10000
end,
cmnd = function(base_pos, mem, sec)
if not mem.steps then
mem.steps = tonumber(sec) or 1
end
mem.steps = mem.steps - 1
if mem.steps == 0 then
mem.steps = nil
return signs_bot.DONE
end
if mem.capa then
mem.capa = mem.capa + 1
end
return signs_bot.BUSY
end,
})
signs_bot.register_botcommand("stop", {
mod = "move",
params = "",
num_param = 0,
description = S("Stop the robot."),
cmnd = function(base_pos, mem, slot)
local param2 = mem.robot_param2
local pos = mem.robot_pos
local node1, pos1 = node_and_pos(lib.next_pos(pos, param2))
if mem.capa then
mem.capa = mem.capa + 2
end
if node1 and node1.name ~= "signs_bot:sign_stop" then
return signs_bot.DONE
end
return signs_bot.BUSY
end,
})
signs_bot.register_botcommand("turn_off", {
mod = "move",
params = "",
num_param = 0,
description = S("Turn the robot off\n"..
"and put it back in the box."),
cmnd = function(base_pos, mem)
signs_bot.stop_robot(base_pos, mem)
return signs_bot.TURN_OFF
end,
})
@@ -0,0 +1,246 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Bot cloning/pattern commands, signs, and nodes
]]--
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
local ValidSizes = {
["3x1"] = true,
["3x2"] = true,
["3x3"] = true,
["5x1"] = true,
["5x2"] = true,
["5x3"] = true,
}
--
-- Helper function to rotate nodes by the Y-axis
--
local Param2Matrix = {
{0,1,2,3}, -- y+
{6,15,8,17}, -- z+
{4,13,10,19}, -- z-
{5,14,11,16}, -- x+
{7,12,9,18}, -- x-
{22,21,20,23}, -- y-
}
local tWallmountedRot = {[0]=3,5,4,2}
local tFacedirRot = {}
for _,row in ipairs(Param2Matrix) do
for idx,elem in ipairs(row) do
local tbl = {}
for i = 0,3 do
tbl[i] = row[((i+idx-1) % 4) + 1]
end
tFacedirRot[elem] = tbl
end
end
local function param2_conversion(node, offs)
local ndef = minetest.registered_nodes[node.name]
if not ndef or not ndef.paramtype2 then return end
if ndef.paramtype2 == "facedir" then
node.param2 = tFacedirRot[node.param2][offs]
elseif ndef.paramtype2 == "wallmounted" and node.param2 > 1 then
node.param2 = tWallmountedRot[(node.param2 + offs - 2) % 4]
end
end
--
-- Inventory functions
--
local function inv_get_item(pos, name)
-- air can be copied for free
if name == "air" then
return "air"
end
-- try to get the item
local inv = minetest.get_inventory({type="node", pos=pos})
if not inv then return "signs_bot:missing" end
local taken = inv:remove_item("main", ItemStack(name))
if taken:get_count() == 0 then
return "signs_bot:missing"
end
return name
end
local function inv_put_item(pos, mem, name)
if name == "air" then
return
end
local inv = minetest.get_inventory({type="node", pos=pos})
if not inv then return end
local leftover = inv:add_item("main", ItemStack(name))
if leftover:get_count() > 0 then
lib.drop_items(mem.robot_pos, leftover)
end
end
local function pattern_copy(base_pos, mem)
local src_pos = mem.src_pos_tbl[mem.steps]
local dst_pos = mem.dst_pos_tbl[mem.steps]
mem.steps = mem.steps + 1
if lib.not_protected(base_pos, dst_pos) then
local src_node = tubelib2.get_node_lvm(src_pos)
src_node.name = inv_get_item(base_pos, src_node.name)
local dst_node = tubelib2.get_node_lvm(dst_pos)
inv_put_item(base_pos, mem, dst_node.name)
param2_conversion(src_node, mem.dir_offs)
minetest.set_node(dst_pos, src_node)
end
end
signs_bot.register_botcommand("pattern", {
mod = "copy",
params = "",
num_param = 0,
description = S("Store pattern to be cloned."),
cmnd = function(base_pos, mem)
mem.pttrn_pos = lib.next_pos(mem.robot_pos, mem.robot_param2)
mem.pttrn_param2 = mem.robot_param2
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("copy", {
mod = "copy",
params = "<size> <lvl>",
num_param = 2,
description = S("Copy the nodes from\n"..
"the stored pattern position\n"..
"<size> is: 3x1, 3x2, 3x3,\n"..
"5x1, 5x2, 5x3 (wide x deep)\n"..
"<lvl> pattern level offset (0..4)"),
check = function(size, lvl)
lvl = tonumber(lvl) or 0
if not lvl or lvl < 0 or lvl > 4 then
return false
end
return ValidSizes[size]
end,
cmnd = function(base_pos, mem, size, lvl)
if not mem.pttrn_pos then return true end
if not mem.steps then
local x,z = size:match('(%d)x(%d)')
lvl = tonumber(lvl) or 0
mem.x_size = tonumber(x)
mem.z_size = tonumber(z)
mem.src_pos_tbl = signs_bot.lib.gen_position_table(mem.pttrn_pos, mem.pttrn_param2, x, z, lvl)
mem.dst_pos_tbl = signs_bot.lib.gen_position_table(mem.robot_pos, mem.robot_param2, x, z, 0)
mem.dir_offs = mem.robot_param2 - mem.pttrn_param2
mem.steps = 1
end
pattern_copy(base_pos, mem)
if mem.steps > #mem.src_pos_tbl then
mem.steps = nil
return signs_bot.DONE
end
return signs_bot.BUSY
end,
})
minetest.register_node("signs_bot:missing", {
description = "Missing Node",
tiles = {"signs_bot_missing_node.png"},
drawtype = "glasslike",
paramtype = "light",
sunlight_propagates = true,
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "blend" or true,
is_ground_content = false,
groups = {snappy=3,cracky=3,oddly_breakable_by_hand=3, not_in_creative_inventory = 1},
drop = "",
sounds = default.node_sound_glass_defaults(),
})
signs_bot.register_sign({
name = "pattern",
description = S('Sign "pattern"'),
commands = "pattern\nturn_around",
image = "signs_bot_sign_pattern.png",
})
minetest.register_craft({
output = "signs_bot:pattern 2",
recipe = {
{"group:wood", "default:stick", "group:wood"},
{"dye:red", "default:stick", "dye:black"},
{"", "", ""}
}
})
local CMND = [[dig_sign 1
move
copy 3x3
move_up
copy 3x3 1
move_up
copy 3x3 2
move_down
move_down
backward
place_sign 1
turn_around]]
signs_bot.register_sign({
name = "copy3x3x3",
description = S('Sign "copy 3x3x3"'),
commands = CMND,
image = "signs_bot_sign_copy3x3x3.png",
})
minetest.register_craft({
output = "signs_bot:copy3x3x3 2",
recipe = {
{"group:wood", "default:stick", "group:wood"},
{"dye:black", "default:stick", "dye:red"},
{"", "", ""}
}
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "pattern", {
name = S("Sign 'pattern'"),
data = {
item = "signs_bot:pattern",
text = table.concat({
S("Used to make a copy of a 3x3x3 cube."),
S("Place the sign in front of the pattern to be copied."),
S("Use the copy sign to make the copy of this pattern on a different location."),
S("The bot must first reach the pattern sign, then the copy sign."),
}, "\n")
},
})
end
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "copy3x3x3", {
name = S("Sign 'copy3x3x3'"),
data = {
item = "signs_bot:copy3x3x3",
text = table.concat({
S("Used to make a copy of a 3x3x3 cube."),
S("Place the sign in front of the location, where the copy should be made."),
S("Use the pattern sign to mark the pattern."),
}, "\n")
},
})
end
@@ -0,0 +1,483 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Bot place/remove commands
]]--
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
local bot_inv_take_item = signs_bot.bot_inv_take_item
local function bot_inv_put_item(pos, slot, items)
local leftover = signs_bot.bot_inv_put_item(pos, slot, items)
return leftover:get_count() == 0
end
local tValidLevels = {[-1] = -1, [0] = 0, [1] = 1}
-- for items with paramtype2 = "facedir"
local tRotations = {}
local Rotations = {
{0,8,22,4},
{1,17,21,13},
{2,6,20,10},
{3,15,23,19},
}
for _,v in ipairs(Rotations) do
local t = table.copy(v)
for i = 1,4 do
table.insert(t, 1, table.remove(t))
tRotations[t[1]] = {t[2], t[3], t[4]}
end
end
--
-- Place/dig items
--
local function place_item(base_pos, robot_pos, param2, slot, route, level)
local pos1, p2 = lib.dest_pos(robot_pos, param2, route)
pos1.y = pos1.y + level
if not lib.not_protected(base_pos, pos1) then
return signs_bot.ERROR, S("Error: Position protected")
end
if lib.is_air_like(pos1) then
local taken = signs_bot.bot_inv_take_item(base_pos, slot, 1)
if taken then
local name = taken:get_name()
if name == "default:torch" then
name = "signs_bot:torch"
end
local def = minetest.registered_nodes[name]
if not def then return end
if def.paramtype2 == "wallmounted" then
local dir = minetest.facedir_to_dir(p2)
local wdir = minetest.dir_to_wallmounted(dir)
minetest.set_node(pos1, {name=name, param2=wdir})
else
minetest.set_node(pos1, {name=name, param2=p2})
minetest.check_single_for_falling(pos1)
end
end
end
return signs_bot.DONE
end
signs_bot.register_botcommand("place_front", {
mod = "place",
params = "<slot> <lvl>",
num_param = 2,
description = S("Place a block in front of the robot\n"..
"<slot> is the inventory slot (1..8)\n"..
"<lvl> is one of: -1 0 +1"),
check = function(slot, lvl)
slot = tonumber(slot) or 0
if not slot or slot < 0 or slot > 8 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, slot, lvl)
slot = tonumber(slot) or 0
local level = tValidLevels[lvl]
return place_item(base_pos, mem.robot_pos, mem.robot_param2, slot, {0}, level)
end,
})
signs_bot.register_botcommand("place_left", {
mod = "place",
params = "<slot> <lvl>",
num_param = 2,
description = S("Place a block on the left side\n"..
"<slot> is the inventory slot (1..8)\n"..
"<lvl> is one of: -1 0 +1"),
check = function(slot, lvl)
slot = tonumber(slot) or 0
if not slot or slot < 0 or slot > 8 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, slot, lvl)
slot = tonumber(slot) or 0
local level = tValidLevels[lvl]
return place_item(base_pos, mem.robot_pos, mem.robot_param2, slot, {3,0}, level)
end,
})
signs_bot.register_botcommand("place_right", {
mod = "place",
params = "<slot> <lvl>",
num_param = 2,
description = S("Place a block on the right side\n"..
"<slot> is the inventory slot (1..8)\n"..
"<lvl> is one of: -1 0 +1"),
check = function(slot, lvl)
slot = tonumber(slot) or 0
if not slot or slot < 0 or slot > 8 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, slot, lvl)
slot = tonumber(slot) or 0
local level = tValidLevels[lvl]
return place_item(base_pos, mem.robot_pos, mem.robot_param2, slot, {1,0}, level)
end,
})
local function place_item_below(base_pos, robot_pos, param2, slot)
local pos1 = {x=robot_pos.x,y=robot_pos.y-1,z=robot_pos.z}
if not lib.not_protected(base_pos, pos1) then
return signs_bot.ERROR, S("Error: Position protected")
end
local node = tubelib2.get_node_lvm(pos1)
if node.name == "signs_bot:robot_foot" then
local taken = bot_inv_take_item(base_pos, slot, 1)
if taken then
local name = taken:get_name()
local def = minetest.registered_nodes[name]
if not def then return end
minetest.set_node(pos1, {name=name, param2=param2})
end
end
return signs_bot.DONE
end
signs_bot.register_botcommand("place_below", {
mod = "place",
params = "<slot>",
num_param = 1,
description = S("Place a block under the robot.\n"..
"Hint: use 'move_up' first.\n"..
"<slot> is the inventory slot (1..8)"),
check = function(slot)
slot = tonumber(slot) or 0
return slot and slot >= 0 and slot < 9
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot) or 0
mem.stored_node = {name = "air"}
return place_item_below(base_pos, mem.robot_pos, mem.robot_param2, slot)
end,
})
local function place_item_above(base_pos, robot_pos, param2, slot)
local pos1 = {x=robot_pos.x,y=robot_pos.y+1,z=robot_pos.z}
if not lib.not_protected(base_pos, pos1) then
return signs_bot.ERROR, S("Error: Position protected")
end
if lib.is_air_like(pos1) then
local taken = bot_inv_take_item(base_pos, slot, 1)
if taken then
local name = taken:get_name()
local def = minetest.registered_nodes[name]
if not def then return end
minetest.set_node(pos1, {name=name, param2=param2})
end
end
return signs_bot.DONE
end
signs_bot.register_botcommand("place_above", {
mod = "place",
params = "<slot>",
num_param = 1,
description = S("Place a block above the robot.\n"..
"<slot> is the inventory slot (1..8)"),
check = function(slot)
slot = tonumber(slot) or 0
return slot and slot >= 0 and slot < 9
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot) or 0
return place_item_above(base_pos, mem.robot_pos, mem.robot_param2, slot)
end,
})
local function dig_item(base_pos, robot_pos, param2, slot, route, level)
local pos1 = lib.dest_pos(robot_pos, param2, route)
pos1.y = pos1.y + (level or 0)
local node = tubelib2.get_node_lvm(pos1)
local dug_name = lib.is_simple_node(node)
if not lib.not_protected(base_pos, pos1) then
return signs_bot.ERROR, S("Error: Position protected")
end
if dug_name then
if bot_inv_put_item(base_pos, slot, ItemStack(dug_name)) then
minetest.remove_node(pos1)
else
return signs_bot.ERROR, S("Error: No free inventory space")
end
end
return signs_bot.DONE
end
signs_bot.register_botcommand("dig_front", {
mod = "place",
params = "<slot> <lvl>",
num_param = 2,
description = S("Dig the block in front of the robot\n"..
"<slot> is the inventory slot (1..8)\n"..
"<lvl> is one of: -1 0 +1"),
check = function(slot, lvl)
slot = tonumber(slot) or 0
if not slot or slot < 0 or slot > 8 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, slot, lvl)
slot = tonumber(slot) or 0
local level = tValidLevels[lvl]
return dig_item(base_pos, mem.robot_pos, mem.robot_param2, slot, {0}, level)
end,
expensive = true,
})
signs_bot.register_botcommand("dig_left", {
mod = "place",
params = "<slot> <lvl>",
num_param = 2,
description = S("Dig the block on the left side\n"..
"<slot> is the inventory slot (1..8)\n"..
"<lvl> is one of: -1 0 +1"),
check = function(slot, lvl)
slot = tonumber(slot) or 0
if not slot or slot < 0 or slot > 8 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, slot, lvl)
slot = tonumber(slot) or 0
local level = tValidLevels[lvl]
return dig_item(base_pos, mem.robot_pos, mem.robot_param2, slot, {0,3}, level)
end,
expensive = true,
})
signs_bot.register_botcommand("dig_right", {
mod = "place",
params = "<slot> <lvl>",
num_param = 2,
description = S("Dig the block on the right side\n"..
"<slot> is the inventory slot (1..8)\n"..
"<lvl> is one of: -1 0 +1"),
check = function(slot, lvl)
slot = tonumber(slot) or 0
if not slot or slot < 0 or slot > 8 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, slot, lvl)
slot = tonumber(slot) or 0
local level = tValidLevels[lvl]
return dig_item(base_pos, mem.robot_pos, mem.robot_param2, slot, {0,1}, level)
end,
expensive = true,
})
local function dig_item_below(base_pos, robot_pos, param2, slot)
local pos1 = {x=robot_pos.x,y=robot_pos.y-1,z=robot_pos.z}
local node = tubelib2.get_node_lvm(pos1)
local dug_name = lib.is_simple_node(node)
if not lib.not_protected(base_pos, pos1) then
return signs_bot.ERROR, S("Error: Position protected")
end
if dug_name then
if bot_inv_put_item(base_pos, slot, ItemStack(dug_name)) then
minetest.set_node(pos1, {name="signs_bot:robot_foot"})
else
return signs_bot.ERROR, S("Error: No free inventory space")
end
end
return signs_bot.DONE
end
signs_bot.register_botcommand("dig_below", {
mod = "place",
params = "<slot>",
num_param = 1,
description = S("Dig the block under the robot.\n"..
"<slot> is the inventory slot (1..8)"),
check = function(slot)
slot = tonumber(slot) or 0
return slot and slot >= 0 and slot < 9
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot) or 0
return dig_item_below(base_pos, mem.robot_pos, mem.robot_param2, slot)
end,
expensive = true,
})
local function dig_item_above(base_pos, robot_pos, param2, slot)
local pos1 = {x=robot_pos.x,y=robot_pos.y+1,z=robot_pos.z}
local node = tubelib2.get_node_lvm(pos1)
local dug_name = lib.is_simple_node(node)
if not lib.not_protected(base_pos, pos1) then
return signs_bot.ERROR, S("Error: Position protected")
end
if dug_name then
if bot_inv_put_item(base_pos, slot, ItemStack(dug_name)) then
minetest.remove_node(pos1)
else
return signs_bot.ERROR, S("Error: No free inventory space")
end
end
return signs_bot.DONE
end
signs_bot.register_botcommand("dig_above", {
mod = "place",
params = "<slot>",
num_param = 1,
description = S("Dig the block above the robot.\n"..
"<slot> is the inventory slot (1..8)"),
check = function(slot)
slot = tonumber(slot) or 0
return slot and slot >= 0 and slot < 9
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot) or 0
return dig_item_above(base_pos, mem.robot_pos, mem.robot_param2, slot, 1)
end,
expensive = true,
})
local function rotate_item(base_pos, robot_pos, param2, route, level, steps)
local pos1 = lib.dest_pos(robot_pos, param2, route)
pos1.y = pos1.y + level
local node = tubelib2.get_node_lvm(pos1)
if not lib.not_protected(base_pos, pos1) then
return signs_bot.ERROR, S("Error: Position protected")
end
if lib.is_simple_node(node) then
local p2 = tRotations[node.param2] and tRotations[node.param2][steps]
if p2 then
minetest.swap_node(pos1, {name=node.name, param2=p2})
end
end
return signs_bot.DONE
end
signs_bot.register_botcommand("rotate_item", {
mod = "place",
params = "<lvl> <steps>",
num_param = 2,
description = S("Rotate the block in front of the robot\n"..
"<lvl> is one of: -1 0 +1\n"..
"<steps> is one of: 1 2 3"),
check = function(lvl, steps)
steps = tonumber(steps) or 1
if not steps or steps < 1 or steps > 4 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, lvl, steps)
local level = tValidLevels[lvl]
steps = tonumber(steps) or 1
return rotate_item(base_pos, mem.robot_pos, mem.robot_param2, {0}, level, steps)
end,
})
local function set_param2(base_pos, robot_pos, bot_param2, route, level, block_param2)
local pos1 = lib.dest_pos(robot_pos, bot_param2, route)
pos1.y = pos1.y + level
local node = tubelib2.get_node_lvm(pos1)
if not lib.not_protected(base_pos, pos1) then
return signs_bot.ERROR, S("Error: Position protected")
end
if lib.is_simple_node(node) then
if block_param2 then
minetest.swap_node(pos1, {name=node.name, param2=block_param2})
end
end
return signs_bot.DONE
end
signs_bot.register_botcommand("set_param2", {
mod = "place",
params = "<lvl> <param2>",
num_param = 2,
description = S("Set param2 of the block in front of the robot\n"..
"<lvl> is one of: -1 0 +1\n"..
"<param2> is: 0 - 23"),
check = function(lvl, param2)
param2 = tonumber(param2) or 0
if not param2 or param2 < 0 or param2 > 23 then
return false
end
return tValidLevels[lvl] ~= nil
end,
cmnd = function(base_pos, mem, lvl, param2)
local level = tValidLevels[lvl]
param2 = tonumber(param2) or 0
return set_param2(base_pos, mem.robot_pos, mem.robot_param2, {0}, level, param2)
end,
})
-- Simplified torch which can be placed w/o a fake player
minetest.register_node("signs_bot:torch", {
description = S("Bot torch"),
inventory_image = "default_torch_on_floor.png",
wield_image = "default_torch_on_floor.png",
drawtype = "nodebox",
node_box = {
type = "connected",
fixed = {
{-1/16, -3/16, -1/16, 1/16, 7/16, 1/16},
--{-2/16, 4/16, -2/16, 2/16, 8/16, 2/16},
},
connect_bottom = {{-1/16, -8/16, -1/16, 1/16, 1/16, 1/16}},
connect_front = {{-1/16, -1/16, -8/16, 1/16, 1/16, 1/16}},
connect_left = {{-8/16, -1/16, -1/16, 1/16, 1/16, 1/16}},
connect_back = {{-1/16, -1/16, -1/16, 1/16, 1/16, 8/16}},
connect_right = {{-1/16, -1/16, -1/16, 8/16, 1/16, 1/16}},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_torch_top.png",
"signs_bot_torch_bottom.png",
{
image = "signs_bot_torch_animated.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 4.0,
},
},
},
connects_to = {
"group:pane", "group:stone", "group:glass", "group:wood", "group:tree",
"group:bakedclay", "group:soil"},
paramtype = "light",
paramtype2 = "facedir",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
walkable = false,
liquids_pointable = false,
light_source = 12,
groups = {choppy=2, dig_immediate=3, flammable=1, attached_node=1, torch=1, not_in_creative_inventory=1},
drop = "default:torch",
sounds = default.node_sound_wood_defaults(),
})
+374
View File
@@ -0,0 +1,374 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Bot sign commands and nodes
]]--
-- for lazy programmers
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
local sCmnds = ""
local lCmnds = {}
local tCmndIdx = {}
minetest.after(2, function()
for idx,cmnd in ipairs(signs_bot.get_commands()) do
cmnd = minetest.formspec_escape(cmnd)
lCmnds[#lCmnds+1] = cmnd
tCmndIdx[cmnd] = idx
end
sCmnds = table.concat(lCmnds, ",")
end)
local function formspec1(meta)
local cmnd = meta:get_string("signs_bot_cmnd")
local name = meta:get_string("sign_name")
local err_msg = meta:get_string("err_msg")
cmnd = minetest.formspec_escape(cmnd)
name = minetest.formspec_escape(name)
return "size[9,8]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"style_type[textarea,table;font=mono]"..
"tabheader[0,0;tab;"..S("Commands,Help")..";1;;true]"..
"field[0.3,0.5;9,1;name;"..S("Sign name:")..";"..name.."]"..
"textarea[0.3,1.2;9,7.2;cmnd;;"..cmnd.."]"..
"label[0.3,7.5;"..err_msg.."]"..
"button_exit[5,7.5;2,1;cancel;"..S("Cancel").."]"..
"button[7,7.5;2,1;check;"..S("Check").."]"
end
local function formspec2(pos, text)
return "size[9,8]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"style_type[textarea,table;font=mono]"..
"tabheader[0,0;tab;"..S("Commands,Help")..";2;;true]"..
"table[0.1,0;8.6,4;command;"..sCmnds..";"..pos.."]"..
"textarea[0.3,4.5;9,3.5;help;"..S("Help")..":;"..text.."]"..
"button[3,7.5;3,1;copy;"..S("Copy Cmnd").."]"
end
local function add_arrow(text, line_num)
local tbl = {}
for idx,line in ipairs(string.split(text, "\n", true)) do
if idx == line_num and not string.find(line, '--<<== error') then
tbl[#tbl+1] = line.." --<<== error"
else
tbl[#tbl+1] = line
end
end
return table.concat(tbl, "\n")
end
local function check_syntax(pos, meta, text)
local res,err_msg, line_num = signs_bot.check_commands(pos, text)
meta:set_int("err_code", res and 0 or 1) -- zero means OK
meta:set_string("err_msg", err_msg)
if line_num > 0 then
meta:set_string("signs_bot_cmnd", add_arrow(text, line_num))
end
end
local function append_line(pos, meta, line)
line = line and line:trim() or ""
local text = meta:get_string("signs_bot_cmnd").."\n"..line
meta:set_string("signs_bot_cmnd", text)
meta:set_int("err_code", 1) -- zero means OK
meta:set_string("err_msg", S("please check the added line(s)"))
end
local function check_and_store(pos, meta, fields)
meta:set_string("sign_name", fields.name)
meta:set_string("signs_bot_cmnd", fields.cmnd)
check_syntax(pos, meta, fields.cmnd)
meta:set_string("formspec", formspec1(meta))
meta:set_string("infotext", meta:get_string("sign_name"))
end
minetest.register_node("signs_bot:sign_cmnd", {
description = S('Sign "command"'),
drawtype = "nodebox",
inventory_image = "signs_bot_sign_cmnd.png",
node_box = {
type = "fixed",
fixed = {
{ -1/16, -8/16, -1/16, 1/16, 4/16, 1/16},
{ -6/16, -5/16, -2/16, 6/16, 3/16, -1/16},
},
},
paramtype2 = "facedir",
tiles = {
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png^signs_bot_sign_cmnd.png",
},
after_place_node = function(pos, placer, itemstack)
local imeta = itemstack:get_meta()
local nmeta = minetest.get_meta(pos)
if imeta:get_string("description") ~= "" then
nmeta:set_string("signs_bot_cmnd", imeta:get_string("cmnd"))
nmeta:set_string("sign_name", imeta:get_string("description"))
nmeta:set_string("err_msg", imeta:get_string("err_msg"))
nmeta:set_int("err_code", imeta:get_int("err_code"))
else
nmeta:set_string("sign_name", S('Sign "command"'))
nmeta:set_string("signs_bot_cmnd", S("-- enter or copy commands from help page"))
nmeta:set_int("err_code", 0)
end
nmeta:set_string("infotext", nmeta:get_string("sign_name"))
nmeta:set_string("formspec", formspec1(nmeta))
end,
on_receive_fields = function(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = minetest.get_meta(pos)
if fields.check then
check_and_store(pos, meta, fields)
elseif fields.key_enter_field then
check_and_store(pos, meta, fields)
elseif fields.copy then
append_line(pos, meta, lCmnds[meta:get_int("help_pos")])
elseif fields.tab == "1" then
meta:set_string("formspec", formspec1(meta))
elseif fields.tab == "2" then
check_and_store(pos, meta, fields)
local pos = meta:get_int("help_pos")
local cmnd = lCmnds[pos] or ""
meta:set_string("formspec", formspec2(pos, signs_bot.get_help_text(cmnd)))
elseif fields.command then
local evt = minetest.explode_table_event(fields.command)
if evt.type == "DCL" then
append_line(pos, meta, lCmnds[tonumber(evt.row)])
elseif evt.type == "CHG" then
local pos = tonumber(evt.row)
meta:set_int("help_pos", pos)
local cmnd = lCmnds[pos] or ""
meta:set_string("formspec", formspec2(pos, signs_bot.get_help_text(cmnd)))
end
end
end,
after_dig_node = lib.after_dig_sign_node,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
is_ground_content = false,
drop = "",
groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2, sign_bot_sign = 1},
sounds = default.node_sound_wood_defaults(),
})
-- Get one sign from the robot signs inventory
local function get_inv_sign(base_pos, slot)
local inv = minetest.get_inventory({type="node", pos=base_pos})
local stack = inv:get_stack("sign", slot)
local taken = stack:take_item(1)
if taken:get_count() == 1 then
inv:set_stack("sign", slot, stack)
return taken
end
end
-- Put one sign into the robot signs inventory
local function put_inv_sign(base_pos, slot, item)
local inv = minetest.get_inventory({type="node", pos=base_pos})
local stack = inv:get_stack("sign", slot)
local leftovers = stack:add_item(item)
if leftovers:get_count() == 0 then
inv:set_stack("sign", slot, stack)
return true
end
return false
end
local function place_sign(base_pos, robot_pos, param2, slot)
local pos1 = lib.dest_pos(robot_pos, param2, {0})
if lib.not_protected(base_pos, pos1) then
if lib.is_air_like(pos1) then
local sign = get_inv_sign(base_pos, slot)
if sign then
lib.place_sign(pos1, sign, param2)
return signs_bot.DONE
else
return signs_bot.ERROR, S("Error: Signs inventory empty")
end
end
end
return signs_bot.ERROR, S("Error: Position protected or occupied")
end
signs_bot.register_botcommand("place_sign", {
mod = "sign",
params = "<slot>",
num_param = 1,
description = S("Place a sign in front of the robot\ntaken from the signs inventory\n"..
"<slot> is the inventory slot (1..6)"),
check = function(slot)
slot = tonumber(slot) or 1
return slot and slot > 0 and slot < 7
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot) or 1
return place_sign(base_pos, mem.robot_pos, mem.robot_param2, slot)
end,
})
local function place_sign_behind(base_pos, robot_pos, param2, slot)
local pos1 = lib.dest_pos(robot_pos, param2, {2})
if lib.not_protected(base_pos, pos1) then
if lib.is_air_like(pos1) then
local sign = get_inv_sign(base_pos, slot)
if sign then
lib.place_sign(pos1, sign, param2)
return signs_bot.DONE
else
return signs_bot.ERROR, S("Error: Signs inventory empty")
end
end
end
return signs_bot.ERROR, S("Error: Position protected or occupied")
end
signs_bot.register_botcommand("place_sign_behind", {
mod = "sign",
params = "<slot>",
num_param = 1,
description = S("Place a sign behind the robot\ntaken from the signs inventory\n"..
"<slot> is the inventory slot (1..6)"),
check = function(slot)
slot = tonumber(slot) or 1
return slot and slot > 0 and slot < 7
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot) or 1
return place_sign_behind(base_pos, mem.robot_pos, mem.robot_param2, slot)
end,
})
local function dig_sign(base_pos, robot_pos, param2, slot)
local pos1 = lib.dest_pos(robot_pos, param2, {0})
local meta = M(pos1)
local cmnd = meta:get_string("signs_bot_cmnd")
local err_code = meta:get_int("err_code")
local name = meta:get_string("sign_name")
if cmnd == "" then
return signs_bot.ERROR, S("Error: No sign available")
end
if lib.not_protected(base_pos, pos1) then
local node = tubelib2.get_node_lvm(pos1)
local sign = ItemStack(node.name)
local meta = sign:get_meta()
meta:set_string("description", name)
meta:set_string("cmnd", cmnd)
meta:set_int("err_code", err_code)
minetest.remove_node(pos1)
if not put_inv_sign(base_pos, slot, sign) then
signs_bot.lib.drop_items(robot_pos, sign)
return signs_bot.ERROR, S("Error: Signs inventory slot is occupied")
end
return signs_bot.DONE
end
return signs_bot.ERROR, S("Error: Position is protected")
end
signs_bot.register_botcommand("dig_sign", {
mod = "sign",
params = "<slot>",
num_param = 1,
description = S("Dig the sign in front of the robot\n"..
"and add it to the signs inventory.\n"..
"<slot> is the inventory slot (1..6)"),
check = function(slot)
slot = tonumber(slot) or 1
return slot and slot > 0 and slot < 7
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot) or 1
return dig_sign(base_pos, mem.robot_pos, mem.robot_param2, slot)
end,
})
local function trash_sign(base_pos, robot_pos, param2, slot)
local pos1 = lib.dest_pos(robot_pos, param2, {0})
local cmnd = M(pos1):get_string("signs_bot_cmnd")
if cmnd == "" then
return signs_bot.ERROR, S("Error: No sign available")
end
if lib.not_protected(base_pos, pos1) then
local node = tubelib2.get_node_lvm(pos1)
local sign = ItemStack("signs_bot:sign_cmnd")
minetest.remove_node(pos1)
local leftover = signs_bot.bot_inv_put_item(base_pos, slot, sign)
if leftover and leftover:get_count() > 0 then
signs_bot.lib.drop_items(robot_pos, leftover)
end
return signs_bot.DONE
end
return signs_bot.ERROR, S("Error: Position is protected")
end
signs_bot.register_botcommand("trash_sign", {
mod = "sign",
params = "<slot>",
num_param = 1,
description = S("Dig the sign in front of the robot\n"..
"and add the cleared sign to\nthe item iventory.\n"..
"<slot> is the inventory slot (1..8)"),
check = function(slot)
slot = tonumber(slot) or 1
return slot and slot > 0 and slot < 9
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot) or 1
return trash_sign(base_pos, mem.robot_pos, mem.robot_param2, slot)
end,
})
minetest.register_craft({
output = "signs_bot:sign_cmnd 4",
recipe = {
{"group:wood", "default:stick", "group:wood"},
{"dye:yellow", "default:stick", "dye:yellow"},
{"", "dye:black", ""}
}
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "sign_cmnd", {
name = S("Sign 'command'"),
data = {
item = "signs_bot:sign_cmnd",
text = table.concat({
S("The 'command' sign can be programmed by the player."),
S("Place the sign in front of you and use the node menu to program your sequence of bot commands."),
S("The menu has an edit field for your commands and a help page with all available commands."),
S("The help page has a copy button to simplify the programming."),
}, "\n")
},
})
end
+323
View File
@@ -0,0 +1,323 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
Copyright (C) 2021 Michal 'Micu' Cieslakiewicz
GPL v3
See LICENSE.txt for more information
Bot soup cooking commands
Allows bot to use pot (xdecor:cauldron) to cook a vegetable soup
]]--
if not minetest.global_exists("bucket") or not minetest.global_exists("fire")
or not minetest.global_exists("xdecor") then return end
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
local bucket_empty, bucket_water = ItemStack("bucket:bucket_empty 1"), ItemStack("bucket:bucket_water 1")
local bowl_empty_farming, bowl_empty_xdecor = ItemStack("farming:bowl 1"), ItemStack("xdecor:bowl 1")
local bowl_soup = ItemStack("xdecor:bowl_soup 1")
local function find_item_slot(base_pos, item)
local inv = M(base_pos):get_inventory()
local s = nil
for i = 1, 8 do
local t = inv:get_stack("main", i)
if t and t:get_name() == item:get_name() then
s = i
break
end
end
return s
end
signs_bot.register_botcommand("take_water", {
mod = "soup",
params = "<slot>",
num_param = 1,
description = S("Take water into empty bucket when standing on a shore\n(use specified slot number or 0 for auto selection)"),
check = function(slot)
slot = tonumber(slot)
return slot and slot >= 0 and slot <= 8
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot)
local pos = lib.dest_pos(mem.robot_pos, mem.robot_param2, { 0 })
pos.y = pos.y - 1
if not lib.not_protected(base_pos, pos) then
return signs_bot.ERROR, S("Error: Position protected")
end
local node = minetest.get_node_or_nil(pos)
if not node or node.name ~= "default:water_source" then
return signs_bot.ERROR, S("Error: No still water around")
end
local itemslot = slot
if slot == 0 then
itemslot = find_item_slot(base_pos, bucket_empty)
if not itemslot then
return signs_bot.ERROR, S("Error: No empty bucket in inventory")
end
end
local item = signs_bot.bot_inv_take_item(base_pos, itemslot, 1)
if not item or item:is_empty() or item:get_name() ~= bucket_empty:get_name() then
return signs_bot.ERROR, S("Error: No empty bucket in inventory slot " .. itemslot)
end
item = signs_bot.bot_inv_put_item(base_pos, slot, bucket_water)
if item:is_empty() then
minetest.remove_node(pos)
else
signs_bot.bot_inv_put_item(base_pos, itemslot, bucket_empty)
return signs_bot.ERROR, S("Error: No inventory space for full bucket")
end
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("fill_cauldron", {
mod = "soup",
params = "<slot>",
num_param = 1,
description = S("Pour water from bucket to empty cauldron in front of a robot\n(use specified slot number or 0 for auto selection)"),
check = function(slot)
slot = tonumber(slot)
return slot and slot >= 0 and slot <= 8
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot)
local pos = lib.dest_pos(mem.robot_pos, mem.robot_param2, { 0 })
if not lib.not_protected(base_pos, pos) then
return signs_bot.ERROR, S("Error: Position protected")
end
local node = minetest.get_node_or_nil(pos)
if not node or node.name ~= "xdecor:cauldron_empty" then
return signs_bot.ERROR, S("Error: No empty cauldron in front of a robot")
end
local itemslot = slot
if slot == 0 then
itemslot = find_item_slot(base_pos, bucket_water)
if not itemslot then
return signs_bot.ERROR, S("Error: No full bucket in inventory")
end
end
local item = signs_bot.bot_inv_take_item(base_pos, itemslot, 1)
if not item or item:is_empty() or item:get_name() ~= bucket_water:get_name() then
return signs_bot.ERROR, S("Error: No full bucket in inventory slot " .. itemslot)
end
item = signs_bot.bot_inv_put_item(base_pos, slot, bucket_empty)
if item:is_empty() then
minetest.set_node(pos, { name = "xdecor:cauldron_idle", param2 = node.param2 })
else
signs_bot.bot_inv_put_item(base_pos, itemslot, bucket_water)
return signs_bot.ERROR, S("Error: No inventory space for empty bucket")
end
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("flame_on", {
mod = "soup",
params = "",
num_param = 0,
description = S("Set fire under cauldron (requires flammable material)\n(command is ignored when fire is already burning)"),
cmnd = function(base_pos, mem)
local pos = lib.dest_pos(mem.robot_pos, mem.robot_param2, { 0 })
local fire_pos = { x = pos.x, y = pos.y - 1, z = pos.z }
local fuel_pos = { x = pos.x, y = pos.y - 2, z = pos.z }
if not (lib.not_protected(base_pos, pos) and lib.not_protected(base_pos, fire_pos)
and lib.not_protected(base_pos, fuel_pos)) then
return signs_bot.ERROR, S("Error: Position protected")
end
local node = minetest.get_node_or_nil(pos)
if not node or not node.name:find("xdecor:cauldron") then
return signs_bot.ERROR, S("Error: No cauldron in front of a robot")
end
local fire_node = minetest.get_node_or_nil(fire_pos)
if fire_node and fire_node.name:match("fire:[%w_]*flame") then
return signs_bot.DONE
elseif not fire_node or fire_node.name ~= "air" then
return signs_bot.ERROR, S("Error: No space for fire under cauldron")
end
local fuel_node = minetest.get_node_or_nil(fuel_pos)
if fuel_node and minetest.registered_nodes[fuel_node.name].on_ignite then
minetest.registered_nodes[fuel_node.name].on_ignite(fuel_pos)
elseif fuel_node and minetest.get_item_group(fuel_node.name, "flammable") >= 1 then
minetest.set_node(fire_pos, { name = "fire:basic_flame" })
else
return signs_bot.ERROR, S("Error: No flammable material under cauldron")
end
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("flame_off", {
mod = "soup",
params = "",
num_param = 0,
description = S("Put out (extinguish) fire under cauldron\n(command is ignored when there is no fire)"),
cmnd = function(base_pos, mem)
local pos = lib.dest_pos(mem.robot_pos, mem.robot_param2, { 0 })
local fire_pos = { x = pos.x, y = pos.y - 1, z = pos.z }
if not (lib.not_protected(base_pos, pos) and lib.not_protected(base_pos, fire_pos)) then
return signs_bot.ERROR, S("Error: Position protected")
end
local node = minetest.get_node_or_nil(pos)
if not node or not node.name:find("xdecor:cauldron") then
return signs_bot.ERROR, S("Error: No cauldron in front of a robot")
end
local node = minetest.get_node_or_nil(fire_pos)
if node and node.name:match("fire:[%w_]*flame") then
minetest.remove_node(fire_pos)
end
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("take_soup", {
mod = "soup",
params = "<slot>",
num_param = 1,
description = S("Take boiling soup into empty bowl from cauldron\nin front of a robot\n(use specified slot number or 0 for auto selection)"),
check = function(slot)
slot = tonumber(slot)
return slot and slot >= 0 and slot <= 8
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot)
local pos = lib.dest_pos(mem.robot_pos, mem.robot_param2, { 0 })
if not lib.not_protected(base_pos, pos) then
return signs_bot.ERROR, S("Error: Position protected")
end
local node = minetest.get_node_or_nil(pos)
if not node or node.name ~= "xdecor:cauldron_soup" then
return signs_bot.ERROR, S("Error: No cauldron with a soup in front of a robot")
end
local itemslot = slot
if slot == 0 then
itemslot = find_item_slot(base_pos, bowl_empty_xdecor) or find_item_slot(base_pos, bowl_empty_farming)
if not itemslot then
return signs_bot.ERROR, S("Error: No empty bowl in inventory")
end
end
local item = signs_bot.bot_inv_take_item(base_pos, itemslot, 1)
if not item or item:is_empty() or (item:get_name() ~= bowl_empty_farming:get_name()
and item:get_name() ~= bowl_empty_xdecor:get_name()) then
return signs_bot.ERROR, S("Error: No empty bowl in inventory slot " .. itemslot)
end
local item_full = signs_bot.bot_inv_put_item(base_pos, slot, bowl_soup)
if item_full:is_empty() then
minetest.set_node(pos, { name = "xdecor:cauldron_empty", param2 = node.param2 })
else
signs_bot.bot_inv_put_item(base_pos, itemslot, item)
return signs_bot.ERROR, S("Error: No inventory space for full bowl")
end
return signs_bot.DONE
end,
})
local CMD_WATER = [[
dig_sign 1
move
take_water 1
backward
place_sign 1
turn_around
]]
local CMD_SOUP = [[
dig_sign 1
move 2
fill_cauldron 1
flame_on
pause 11
move_up
drop_items 1 2
drop_items 1 3
move_down
pause 6
take_soup 4
flame_off
backward
backward
place_sign 1
turn_around
]]
signs_bot.register_sign({
name = "water",
description = S('Sign "take water"'),
commands = CMD_WATER,
image = "signs_bot_sign_water.png",
})
signs_bot.register_sign({
name = "soup",
description = S('Sign "cook soup"'),
commands = CMD_SOUP,
image = "signs_bot_sign_soup.png",
})
minetest.register_craft({
output = "signs_bot:water",
recipe = {
{"group:wood", "bucket:bucket_empty", "group:wood"},
{"dye:black", "default:stick", "dye:yellow"},
{"dye:blue", "", ""}
}
})
minetest.register_craft({
output = "signs_bot:soup",
recipe = {
{"group:wood", "xdecor:bowl", "group:wood"},
{"dye:black", "default:stick", "dye:yellow"},
{"dye:orange", "", ""}
}
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "water", {
name = S("Sign 'take water'"),
data = {
item = "signs_bot:water",
text = table.concat({
S("Used to take water into bucket."),
S("Place the sign on a shore, in front of the still water pool."),
S("Items in slots:"),
S(" 1 - empty bucket"),
S("The result is one bucket with water in selected inventory slot."),
S("When finished, the bot turns around."),
}, "\n")
},
})
doc.add_entry("signs_bot", "soup", {
name = S("Sign 'cook soup'"),
data = {
item = "signs_bot:soup",
text = table.concat({
S("Used to cook a vegetable soup in cauldron."),
S("Cauldon should be empty and located above flammable material."),
S("Place the sign in front of the cauldron with one field space,"),
S("to prevent wooden sign from catching fire."),
S("Items in slots:"),
S(" 1 - water bucket"),
S(" 2 - vegetable #1 (i.e. tomato)"),
S(" 3 - vegetable #2 (i.e. carrot)"),
S(" 4 - empty bowl (from farming or xdecor mods)"),
S("The result is one bowl with vegetable soup in selected inventory slot."),
S("When finished, the bot turns around."),
}, "\n")
},
})
end
@@ -0,0 +1,98 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Bot tree cutting signs
]]--
-- Load support for I18n.
local S = signs_bot.S
local CMNDS = [[-- Harvest pine/aspen trunks v1.0
-- Take dirt and saplings from chest
dig_sign 1
move 1
turn_right
take_item 99 0
take_item 99 0
-- Goto trunk
turn_left
dig_front 0 0
move 1
-- Climb up
repeat 10
dig_above 0
move_up
place_below 1
end
-- Climb down
repeat 10
dig_below 1
move_down
end
-- Pickup saplings
repeat 4
pickup_items 2
turn_left
end
-- Return dirt and saplings to chest
backward
plant_sapling 2
turn_right
add_item 99 1
add_item 99 2
-- Finish
turn_left
backward
place_sign 1
turn_around]]
local HELP = table.concat({
S("Used to harvest an aspen or pine tree trunk"),
S("- Place the sign in front of the tree."),
S("- Place a chest to the right of the sign."),
S("- Put a dirt stack (10 items min.) into the chest."),
S("- Preconfigure slot 1 of the bot inventory with dirt"),
S("- Preconfigure slot 2 of the bot inventory with saplings"),
}, "\n")
signs_bot.register_signXL({
name = "aspen",
description = S('Sign "aspen"'),
help_text = HELP,
commands = CMNDS,
image = "signs_bot_sign_aspen.png",
})
minetest.register_craft({
output = "signs_bot:aspen 2",
recipe = {
{"group:wood", "default:stick", "group:wood"},
{"dye:black", "default:stick", "dye:yellow"},
{"dye:grey", "default:aspen_sapling", ""}
}
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "aspen", {
name = S("Sign 'aspen'"),
data = {
item = "signs_bot:aspen",
text = HELP .. "\n",
},
})
end
+312
View File
@@ -0,0 +1,312 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signs Bot: Robot command interpreter
]]--
-- for lazy programmers
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local MP = minetest.get_modpath("signs_bot")
local ci = dofile(MP.."/interpreter.lua")
local lib = signs_bot.lib
-- Possible command results
signs_bot.BUSY = ci.BUSY
signs_bot.DONE = ci.DONE
signs_bot.NEW = ci.NEW
signs_bot.ERROR = ci.ERROR
signs_bot.TURN_OFF = ci.TURN_OFF
-- API functions
signs_bot.check_label = ci.check_label
local tCommands = {}
local SortedKeys = {}
local SortedMods = {}
local tMods = {}
local ExpensiveCmnds = {}
function signs_bot.steps(mem, first, last)
if not mem.steps then
mem.steps = first - 1
end
mem.steps = mem.steps + 1
if mem.steps >= last then
mem.steps = nil
return ci.DONE, last
end
return ci.BUSY, mem.steps
end
--
-- Command register API function
--
function signs_bot.register_botcommand(name, def)
tCommands[name] = def
tCommands[name].name = name
if def.expensive then
ExpensiveCmnds[name] = true
end
if not SortedKeys[def.mod] then
SortedKeys[def.mod] = {}
SortedMods[#SortedMods+1] = def.mod
tMods[#tMods+1] = def.mod
end
local idx = #SortedKeys[def.mod] + 1
SortedKeys[def.mod][idx] = name
if def.num_param and def.cmnd then
ci.register_command(name, def.num_param, def.cmnd, def.check)
end
end
function signs_bot.get_commands()
local tbl = {}
for _,mod in ipairs(SortedMods) do
tbl[#tbl+1] = mod.." "..S("commands:")
for _,cmnd in ipairs(SortedKeys[mod]) do
local item = tCommands[cmnd]
tbl[#tbl+1] = " "..item.name.." "..item.params
end
end
return tbl
end
function signs_bot.get_help_text(cmnd)
if cmnd then
cmnd = unpack(string.split(cmnd, " "))
local item = tCommands[cmnd]
if item then
return item.description
end
end
return S("unknown command")
end
function signs_bot.check_commands(pos, text)
return ci.check_script(text)
end
--
-- Bot Commands
--
local function check_sign(pos, mem)
local meta = M(pos)
local cmnd = meta:get_string("signs_bot_cmnd")
if cmnd ~= "" then -- command block?
if meta:get_int("err_code") ~= 0 then -- code not valid?
return false
end
local node = tubelib2.get_node_lvm(pos)
-- correct sign direction?
if mem.robot_param2 == node.param2 then
return true
end
-- special sign node?
if node.name == "signs_bot:bot_flap" or node.name == "signs_bot:box" then
return true
end
end
return false
end
-- Function returns 2 values:
-- - true if a sensor could be available, else false
-- - the sign pos or nil
local function scan_surrounding(mem)
local pos1 = lib.next_pos(mem.robot_pos, mem.robot_param2)
if check_sign(pos1, mem) then
return true, pos1
end
local pos2 = {x=pos1.x, y=pos1.y+1, z=pos1.z}
if check_sign(pos2, mem) then
return true, pos2
end
if minetest.find_node_near(mem.robot_pos, 1, {
"signs_bot:box", "signs_bot:bot_sensor"}) then -- something around?
return true
end
return false
end
local function activate_sensor(pos, param2)
local pos1 = lib.next_pos(pos, param2)
local node = tubelib2.get_node_lvm(pos1)
if node.name == "signs_bot:bot_sensor" then
node.name = "signs_bot:bot_sensor_on"
minetest.swap_node(pos1, node)
local ndef = minetest.registered_nodes[node.name]
if ndef and ndef.after_place_node then
ndef.after_place_node(pos1)
end
end
end
local function bot_error(base_pos, mem, err, cmd)
minetest.sound_play('signs_bot_error', {pos = base_pos})
minetest.sound_play('signs_bot_error', {pos = mem.robot_pos})
err = err or "unknown"
if cmd then
signs_bot.infotext(base_pos, err .. ":\n'" .. cmd .. "'")
mem.error = err .. ": '" .. cmd .. "'"
else
signs_bot.infotext(base_pos, err)
mem.error = err
end
return false
end
local function power_consumption(mem, cmnd)
if mem.capa then
if ExpensiveCmnds[cmnd] then
mem.capa = mem.capa - 2
else
mem.capa = mem.capa - 1
end
return mem.capa > 0
end
return true
end
function signs_bot.run_next_command(base_pos, mem)
local res, err, cmd = ci.run_script(base_pos, mem)
if res == ci.ERROR then
return bot_error(base_pos, mem, err, cmd)
elseif res == ci.EXIT then
signs_bot.stop_robot(base_pos, mem)
return false
end
if not power_consumption(mem) then
signs_bot.stop_robot(base_pos, mem)
mem.bot_state = "nopower"
return bot_error(base_pos, mem, "No power")
end
mem.error = false
return true
end
function signs_bot.reset(base_pos, mem)
ci.reset_script(base_pos, mem)
end
signs_bot.register_botcommand("repeat", {
mod = "core",
params = "<num>",
description = S("start of a 'repeat..end' block"),
})
signs_bot.register_botcommand("end", {
mod = "core",
params = "",
description = S("end command of a 'repeat..end' block"),
})
signs_bot.register_botcommand("call", {
mod = "core",
params = "<label>",
description = S("call a subroutine (with 'return' statement)"),
})
signs_bot.register_botcommand("return", {
mod = "core",
params = "",
description = S("return from a subroutine"),
})
signs_bot.register_botcommand("jump", {
mod = "core",
params = "<label>",
description = S("jump to a label"),
})
local function move(mem, any_sensor)
local new_pos = signs_bot.move_robot(mem)
if new_pos then -- not blocked?
mem.robot_pos = new_pos
if any_sensor then
activate_sensor(mem.robot_pos, (mem.robot_param2 + 1) % 4)
activate_sensor(mem.robot_pos, (mem.robot_param2 + 3) % 4)
end
elseif mem.capa then
mem.capa = mem.capa + 1
end
return new_pos ~= nil
end
signs_bot.register_botcommand("move", {
mod = "move",
params = "<steps>",
num_param = 1,
description = S([[Move the robot 1..999 steps forward
without paying attention to any signs.
Up and down movements also become
counted as steps.]]),
check = function(steps)
steps = tonumber(steps) or 1
return steps > 0 and steps < 1000
end,
cmnd = function(base_pos, mem, steps)
steps = tonumber(steps) or 1
local res, idx = signs_bot.steps(mem, 1, steps)
move(mem, scan_surrounding(mem))
return res
end,
})
signs_bot.register_botcommand("cond_move", {
mod = "move",
params = "",
num_param = 0,
description = S([[Walk until a sign or obstacle is
reached. Then continue with the next command.
When a sign has been reached,
the current program is ended
and the bot executes the
new program from the sign]]),
cmnd = function(base_pos, mem)
local any_sensor, sign_pos = scan_surrounding(mem)
if not sign_pos then
if move(mem, any_sensor) then
return ci.BUSY
end
return signs_bot.DONE
else
mem.script = M(sign_pos):get_string("signs_bot_cmnd").."\ncond_move"
return ci.NEW
end
end,
})
signs_bot.register_botcommand("print", {
mod = "debug",
params = "<text>",
num_param = 1,
description = S([[Print given text as chat message.
For two or more words, use the '*' character
instead of spaces, like "Hello*world"]]),
check = function(text)
return text ~= ""
end,
cmnd = function(base_pos, mem, text)
text = tostring(text):gsub("%*", " ")
local owner = M(base_pos):get_string("owner")
if owner ~= "" and text ~= "" then
minetest.chat_send_player(owner, "Bot: " .. text)
end
return signs_bot.DONE
end,
})
+101
View File
@@ -0,0 +1,101 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signs Bot: Commands for the compost mod
]]--
-- Load support for I18n.
local S = signs_bot.S
local NUM_LEAVES = 2
-- we reuse the minecart hopper API here
local function additem(mem, stack)
local pos = signs_bot.lib.next_pos(mem.robot_pos, mem.robot_param2)
local node = minetest.get_node(pos)
local ndef = minetest.registered_nodes[node.name]
if ndef.minecart_hopper_additem then
return ndef.minecart_hopper_additem(pos, stack)
end
pos = {x = pos.x, y = pos.y - 1, z = pos.z}
node = minetest.get_node(pos)
ndef = minetest.registered_nodes[node.name]
if ndef and ndef.minecart_hopper_additem then
return ndef.minecart_hopper_additem(pos, stack)
end
return stack
end
local function takeitem(mem)
local pos = signs_bot.lib.next_pos(mem.robot_pos, mem.robot_param2)
local node = minetest.get_node(pos)
local ndef = minetest.registered_nodes[node.name]
if ndef.minecart_hopper_takeitem then
return ndef.minecart_hopper_takeitem(pos, 1)
end
pos = {x = pos.x, y = pos.y - 1, z = pos.z}
node = minetest.get_node(pos)
ndef = minetest.registered_nodes[node.name]
if ndef and ndef.minecart_hopper_takeitem then
return ndef.minecart_hopper_takeitem(pos, 1)
end
end
if minetest.global_exists("compost") then
signs_bot.register_botcommand("add_compost", {
mod = "compost",
params = "<slot>",
num_param = 1,
description = S("Put 2 leaves into the compost barrel\n"..
"<slot> is the bot inventory slot (1..8)\n"..
"with the leaves."),
check = function(slot)
slot = tonumber(slot) or 0
return slot > 0 and slot < 9
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot) or 0
local taken = signs_bot.bot_inv_take_item(base_pos, slot, NUM_LEAVES)
local leftover = additem(mem, taken)
if leftover and leftover:get_count() > 0 then
signs_bot.bot_inv_put_item(base_pos, slot, leftover)
end
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("take_compost", {
mod = "compost",
params = "<slot>",
num_param = 1,
description = S("Take a compost item from the barrel.\n"..
"<slot> (1..8 or 0 for the first free slot) is the bot\n"..
"slot for the compost item."),
check = function(num, slot)
slot = tonumber(slot) or 0
return slot >= 0 and slot < 9
end,
cmnd = function(base_pos, mem, slot)
slot = tonumber(slot) or 0
local taken = takeitem(mem)
local leftover = signs_bot.bot_inv_put_item(base_pos, slot, taken)
if leftover and leftover:get_count() > 0 then
signs_bot.lib.drop_items(mem.robot_pos, leftover)
end
return signs_bot.DONE
end,
})
end
@@ -0,0 +1,164 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Crop Sensor
]]--
-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
local CYCLE_TIME = 4
local function update_infotext(pos, dest_pos, dest_idx)
M(pos):set_string("infotext", S("Crop Sensor: Connected with").." "..P2S(dest_pos).." / "..dest_idx)
end
local function swap_node(pos, name)
local node = minetest.get_node(pos)
if node.name == name then
return false
end
if string.sub(node.name, 1,21) == "signs_bot:crop_sensor" then
node.name = name
minetest.swap_node(pos, node)
return true
end
return false
end
local function node_timer(pos)
local pos1 = lib.next_pos(pos, M(pos):get_int("param2"))
local node = minetest.get_node_or_nil(pos1)
if node and signs_bot.FarmingCrop[node.name] then
if swap_node(pos, "signs_bot:crop_sensor_on") then
signs_bot.send_signal(pos)
signs_bot.lib.activate_extender_nodes(pos, true)
minetest.after(1, swap_node, pos, "signs_bot:crop_sensor")
end
end
return true
end
minetest.register_node("signs_bot:crop_sensor", {
description = S("Crop Sensor"),
inventory_image = "signs_bot_sensor_crop_inv.png",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor1.png^signs_bot_sensor_crop.png",
"signs_bot_sensor1.png",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR180",
},
after_place_node = function(pos, placer)
local meta = M(pos)
meta:set_string("infotext", S("Crop Sensor: Not connected"))
minetest.get_node_timer(pos):start(CYCLE_TIME)
local node = minetest.get_node(pos)
meta:set_int("param2", (node.param2 + 2) % 4)
end,
on_timer = node_timer,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
is_ground_content = false,
groups = {sign_bot_sensor = 1, cracky = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("signs_bot:crop_sensor_on", {
description = S("Crop Sensor"),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor1.png^signs_bot_sensor_crop_on.png",
"signs_bot_sensor1.png",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR180",
},
on_timer = node_timer,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
is_ground_content = false,
drop = "signs_bot:crop_sensor",
groups = {sign_bot_sensor = 1, cracky = 1, not_in_creative_inventory = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_craft({
output = "signs_bot:crop_sensor",
recipe = {
{"", "", ""},
{"dye:black", "group:stone", "dye:yellow"},
{"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"}
}
})
minetest.register_lbm({
label = "[signs_bot] Restart timer",
name = "signs_bot:crop_sensor_restart",
nodenames = {"signs_bot:crop_sensor", "signs_bot:crop_sensor_on"},
run_at_every_load = true,
action = function(pos, node)
minetest.get_node_timer(pos):start(CYCLE_TIME)
if node.name == "signs_bot:crop_sensor_on" then
signs_bot.send_signal(pos)
signs_bot.lib.activate_extender_nodes(pos, true)
end
end
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "crop_sensor", {
name = S("Crop Sensor"),
data = {
item = "signs_bot:crop_sensor",
text = table.concat({
S("The Crop Sensor sends cyclical signals when, for example, wheat is fully grown."),
S("The sensor range is one node/meter."),
S("The sensor has an active side (red) that must point to the crop/field."),
}, "\n")
},
})
end
@@ -0,0 +1,26 @@
#!/usr/bin/env python
import os
spath = "./"
n = len(spath)
for root, dirs, files in os.walk(spath):
offs = root[n:]
for fname in files:
name, ext = os.path.splitext(fname)
if ext == ".lua":
src = os.path.join(spath, offs, fname)
lOut = []
changed = False
for line in open(src, "rt").readlines():
line2 = line.rstrip()
if len(line2) != len(line) - 1:
changed = True
lOut.append(line2)
if changed:
print("%s changed" % src)
text = "\n".join(lOut) + "\n"
open(src, "wt").write(text)
+274
View File
@@ -0,0 +1,274 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signal Delayer.
Signals are forwarded delayed. Subsequent signals are queued.
]]--
-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S2P = minetest.string_to_pos
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local CYCLE_TIME = 2
local function update_infotext(pos, dest_pos, cmnd)
M(pos):set_string("infotext", S("Signal Delayer: Connected with").." "..P2S(dest_pos).." / "..cmnd)
end
local function infotext(pos)
local meta = M(pos)
local dest_pos = meta:get_string("signal_pos")
local signal = meta:get_string("signal_data")
if dest_pos ~= "" and signal ~= "" then
update_infotext(pos, S2P(dest_pos), signal)
end
end
local function formspec(meta)
local label = minetest.formspec_escape(S("Delay time [sec]:"))
local value = minetest.formspec_escape(meta:get_int("time"))
return "size[4,3]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"field[0.3,1;4,1;time;"..label..";"..value.."]"..
"button_exit[1,2.2;2,1;start;"..S("Start").."]"
end
-- Used by the pairing tool
local function signs_bot_get_signal(pos, node)
return "do"
end
-- switch to normal or loaded texture
local function turn_off(pos)
local mem = tubelib2.get_mem(pos)
local node = minetest.get_node(pos)
if mem.queue > 0 then
node.name = "signs_bot:delayer_loaded"
else
node.name = "signs_bot:delayer"
end
minetest.swap_node(pos, node)
end
-- switch to loaded texture
local function loaded(pos)
local node = minetest.get_node(pos)
if node.name == "signs_bot:delayer" or node.name == "signs_bot:delayer_on" then
node.name = "signs_bot:delayer_loaded"
minetest.swap_node(pos, node)
end
end
local function send_signal(pos)
local meta = M(pos)
local mem = tubelib2.get_mem(pos)
local node = minetest.get_node(pos)
if node.name == "signs_bot:delayer" or node.name == "signs_bot:delayer_loaded" then
node.name = "signs_bot:delayer_on"
minetest.swap_node(pos, node)
minetest.after(2, turn_off, pos)
end
signs_bot.send_signal(pos)
signs_bot.lib.activate_extender_nodes(pos, true)
mem.queue = (mem.queue or 1) - 1
if mem.queue > 0 then
mem.time = meta:get_int("time")
end
end
local function node_timer(pos)
local meta = M(pos)
local mem = tubelib2.get_mem(pos)
mem.time = (mem.time or 0) - CYCLE_TIME
--print("node_timer time="..mem.time..", queue="..mem.queue)
if mem.time <= 0 and mem.queue > 0 then
send_signal(pos)
end
return mem.queue > 0
end
-- To be called from sensors
local function signs_bot_on_signal(pos, node, signal)
local meta = M(pos)
local mem = tubelib2.get_mem(pos)
mem.queue = mem.queue or 0
--print("signs_bot_on_signal", signal, meta:get_int("time"))
if signal ~= "do" or meta:get_int("time") == 0 then return end
if mem.queue <= 0 then
mem.queue = 1
mem.time = meta:get_int("time")
loaded(pos)
else
mem.queue = mem.queue + 1
end
minetest.get_node_timer(pos):start(CYCLE_TIME)
end
local function on_receive_fields(pos, formname, fields, player)
local mem = tubelib2.get_mem(pos)
local meta = M(pos)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
if fields.key_enter_field == "time" or fields.start then
local time = tonumber(fields.time)
if time and time >= 0 and time < 999999 then
meta:set_int("time", time)
mem.time = 0
mem.queue = 0
end
end
meta:set_string("formspec", formspec(meta))
turn_off(pos)
end
minetest.register_node("signs_bot:delayer", {
description = S("Signal Delayer"),
inventory_image = "signs_bot_delayer_inv.png",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor2.png^signs_bot_delayer.png",
"signs_bot_sensor2.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
local mem = tubelib2.init_mem(pos)
mem.time = 0
mem.queue = 0
infotext(pos)
meta:set_string("formspec", formspec(meta))
end,
signs_bot_get_signal = signs_bot_get_signal,
signs_bot_on_signal = signs_bot_on_signal,
on_receive_fields = on_receive_fields,
on_timer = node_timer,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
is_ground_content = false,
groups = {sign_bot_sensor = 1, cracky = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("signs_bot:delayer_loaded", {
description = S("Signal Delayer"),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor2.png^signs_bot_delayer_loaded.png",
"signs_bot_sensor2.png",
},
signs_bot_get_signal = signs_bot_get_signal,
signs_bot_on_signal = signs_bot_on_signal,
on_receive_fields = on_receive_fields,
on_timer = node_timer,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
use_texture_alpha = signs_bot.CLIP,
is_ground_content = false,
diggable = false,
groups = {sign_bot_sensor = 1, not_in_creative_inventory = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("signs_bot:delayer_on", {
description = S("Signal Delayer"),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor2.png^signs_bot_delayer_on.png",
"signs_bot_sensor2.png",
},
on_timer = node_timer,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
use_texture_alpha = signs_bot.CLIP,
is_ground_content = false,
diggable = false,
groups = {sign_bot_sensor = 1, not_in_creative_inventory = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_craft({
output = "signs_bot:delayer",
recipe = {
{"group:wood", "dye:yellow", ""},
{"default:mese_crystal_fragment", "", ""},
{"default:steel_ingot", "", ""},
}
})
minetest.register_lbm({
label = "[signs_bot] Restart delayer",
name = "signs_bot:delayer_restart",
nodenames = {"signs_bot:delayer", "signs_bot:delayer_loaded", "signs_bot:delayer_on"},
run_at_every_load = true,
action = function(pos, node)
turn_off(pos)
local mem = tubelib2.get_mem(pos)
if mem.time and mem.time > 0 then
minetest.get_node_timer(pos):start(CYCLE_TIME)
end
end
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "delayer", {
name = S("Signal Delayer"),
data = {
item = "signs_bot:delayer",
text = table.concat({
S("Signals are forwarded delayed. Subsequent signals are queued."),
S("The delay time can be configured."),
}, "\n")
},
})
end
@@ -0,0 +1,7 @@
default
farming
basic_materials
tubelib2
node_io?
techage?
doc?
+193
View File
@@ -0,0 +1,193 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPLv3
See LICENSE.txt for more information
Signs Bot: Ingame Documentation
]]--
-- Load support for I18n.
local S = signs_bot.S
signs_bot.doc = {}
if not minetest.get_modpath("doc") then
return
end
local function formspec(data)
if data.image then
local image = "image["..(doc.FORMSPEC.ENTRY_WIDTH - 3)..",0;3,2;"..data.image.."]"
local formstring = doc.widgets.text(data.text, doc.FORMSPEC.ENTRY_START_X, doc.FORMSPEC.ENTRY_START_Y+1.6, doc.FORMSPEC.ENTRY_WIDTH, doc.FORMSPEC.ENTRY_HEIGHT - 1.6)
return image..formstring
elseif data.item then
local box = "box["..(doc.FORMSPEC.ENTRY_WIDTH - 1.6)..",0;1,1.1;#BBBBBB]"
local image = "item_image["..(doc.FORMSPEC.ENTRY_WIDTH - 1.5)..",0.1;1,1;"..data.item.."]"
local formstring = doc.widgets.text(data.text, doc.FORMSPEC.ENTRY_START_X, doc.FORMSPEC.ENTRY_START_Y+0.8, doc.FORMSPEC.ENTRY_WIDTH, doc.FORMSPEC.ENTRY_HEIGHT - 0.8)
return box..image..formstring
else
return doc.entry_builders.text(data.text)
end
end
local start_doc = table.concat({
S("After you have placed the Signs Bot Box, you can start the bot by means of the 'On' button in the box menu."),
S("If the bot returns to its box right away, you will likely need to charge it with electrical energy (techage) first."),
S("The bot then runs straight up until it reaches an obstacle (a step with two or more blocks up or down or a sign.)"),
S("If the bot first reaches a sign it will execute the commands on the sign."),
S("If the command(s) on the sign is e.g. 'turn_around', the bot turns and goes back."),
S("In this case, the bot reaches his box again and turns off."),
"",
S("The Signs Bot Box has an inventory with 6 stacks for signs and 8 stacks for other items (to be placed/dug by the bot)."),
S("This inventory simulates the bot internal inventory."),
S("That means you will only have access to the inventory if the bot is turned off ('sitting' in his box)."),
}, "\n")
local control_doc = table.concat({
S("You simply control the direction of the bot by means of the 'turn left' and 'turn right' signs (signs with the arrow)."),
S("The bot can run over steps (one block up/down). But there are also commands to move the bot up and down."),
"",
S("It is not necessary to mark a way back to the box."),
S("With the command 'turn_off' the bot will turn off and be back in his box from every position."),
S("The same applies if you turn off the bot by the box menu."),
S("If the bot reaches a sign from the wrong direction (from back or sides) the sign will be ignored."),
S("The bot will walk over."),
"",
S("All predefined signs have a menu with a list of the bot commands."),
S("These signs can't be changed, but you can craft and program your own signs."),
S("For this you have to use the 'command' sign."),
S("This sign has an edit field for your commands and a help page with all available commands."),
S("The help page has a copy button to simplify the programming."),
"",
S("Also for your own signs it is important to know:"),
S("After the execution of the last command of the sign, the bot falls back into its default behaviour and runs in its taken direction."),
"",
S("A standard job for the bot is to move items from one chest to another"),
S("(chest or node with a chest like inventory)."),
S("This can be done by means of the two signs 'take item' and 'add item'."),
S("These signs have to be placed on top of chest nodes."),
}, "\n")
local sensor_doc = table.concat({
S("In addition to the signs the bot can be controlled by means of sensors."),
S("Sensors like the Bot Sensor have two states: on and off."),
S("If the Bot Sensor detects a bot it will switch to the state 'on' and"),
S("sends a signal to a connected block, called an actuator."),
"",
S("Sensors are:"),
S("- Bot Sensor: Sends a signal when the robot passes by"),
S("- Node Sensor: Sends a signal when it detects any node"),
S("- Crop Sensor: Sends a signal when, for example wheat is fully grown"),
S("- Bot Chest: Sends a signal depending on the chest state (empty, full)"),
"",
S("Actuators are:"),
S("- Signs Bot Box: Can turn the bot off and on"),
S("- Control Unit: Can be used to exchange the sign to lead the bot"),
"",
S("Additional sensors and actuator can be added by other mods."),
}, "\n")
local tool_doc = table.concat({
S("To send a signal from a sensor to an actuator, the sensor has to be connected (paired) with actuator."),
S("To connect sensor and actuator, the Sensor Connection Tool has to be used."),
S("Simply click with the tool on both blocks and the sensor will be connected with the actuator."),
S("A successful connection is indicated by a ping/pong noise."),
"",
S("Before you connect sensor with actuator, take care that the actuator is in the requested state."),
S("For example: If you want to start the Bot with a sensor, connect the sensor with the Bot Box,"),
S("when the Bot is in the state 'on'. Otherwise the sensor signal will stop the Bot,"),
S("instead of starting it."),
}, "\n")
local inventory_doc = table.concat({
S("The following applies to all commands that are used to place items in the bot inventory, like:"),
"",
S("- take_item <num> <slot>"),
S("- pickup_items <slot>"),
S("- trash_sign <slot>"),
S("- harvest <slot>"),
S("- dig_front <slot> <lvl>"),
S("- dig_left <slot> <lvl>"),
S("- dig_right <slot> <lvl>"),
S("- dig_below <slot> <lvl>"),
S("- dig_above <slot> <lvl>"),
"",
S("If no slot or slot 0 was specified with the command (case A), all 8 slots of the bot inventory "),
S("are checked one after the other. If a slot was specified (case B), only this slot is checked."),
S("In both cases the following applies: If the slot is preconfigured and fits the item, "),
S("or if the slot is not configured and empty, or is only partially filled with the item type "),
S("(which should be added), then the items are added."),
S("If not all items can be added, the remaining slots will be tried out in case A."),
S("Anything that could not be added to your own inventory goes back."),
"",
S("The following applies to all commands that are used to take items from the bot inventory, like:"),
"",
S("- add_item <num> <slot>"),
"",
S("It doesn't matter whether a slot is configured or not. The bot takes the first stack that "),
S("it can find from its own inventory and tries to use it."),
S("If a slot is specified, it only takes this, if no slot has been specified, it checks all of "),
S("them one after the other, starting from slot 1 until it finds something."),
S("If the number found is smaller than requested, he tries to take the rest out of any slot."),
}, "\n")
local sorting_data = {"start", "control", "sensor_doc", "tool", "invent",
"box", "bot_flap", "duplicator",
"bot_sensor", "node_sensor", "crop_sensor", "chest", "timer",
"changer", "sensor_extender", "and", "delayer",
"farming", "pattern", "copy3x3x3", "flowers", "aspen",
"sign_cmnd", "sign_right", "sign_left", "sign_take", "sign_add", "sign_stop", "sign_blank"}
if minetest.global_exists("minecart") then
table.insert(sorting_data, "cart_sensor")
table.insert(sorting_data, "sign_add_cart")
table.insert(sorting_data, "sign_take_cart")
end
if minetest.global_exists("xdecor") then
table.insert(sorting_data, "water")
table.insert(sorting_data, "soup")
end
doc.add_category("signs_bot",
{
name = S("Signs Bot"),
description = S("A robot controlled by signs, used for automated work"),
sorting = "custom",
build_formspec = formspec,
sorting_data = sorting_data,
})
doc.add_entry("signs_bot", "start", {
name = S("Start the Bot"),
data = {text = start_doc, image = "signs_bot_doc_image.png"},
})
doc.add_entry("signs_bot", "control", {
name = S("Control the Bot"),
data = {text = control_doc, image = "signs_bot_doc_image.png"},
})
doc.add_entry("signs_bot", "sensor_doc", {
name = S("Sensors and Actuators"),
data = {text = sensor_doc, image = "signs_bot_doc_image.png"},
})
doc.add_entry("signs_bot", "tool", {
name = S("Connecting sensors and actuator"),
data = {text = tool_doc, image = "signs_bot_doc_image.png"},
})
doc.add_entry("signs_bot", "invent", {
name = S("Bot inventory behavior"),
data = {text = inventory_doc, image = "signs_bot_doc_image.png"},
})
Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

@@ -0,0 +1,256 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signs duplicator
]]--
-- for lazy programmers
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
local formspec = "size[8,7.3]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[0.1,0.2;"..S("Template:").."]"..
"list[context;temp;2,0;1,1;]"..
"label[0.1,1.2;"..S("Input:").."]"..
"list[context;inp;2,1;1,1;]"..
"label[0.1,2.2;"..S("Output:").."]"..
"list[context;outp;2,2;1,1;]"..
"label[3,0.2;"..S("1. Place one 'cmnd' sign").."]"..
"label[3,1.2;"..S("2. Add 'blank signs'").."]"..
"label[3,2.2;"..S("3. Take the copies").."]"..
"list[current_player;main;0,3.5;8,4;]"..
"listring[context;inp]"..
"listring[current_player;main]"..
"listring[current_player;main]"..
"listring[context;outp]"
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if listname == "outp" then
return 0
end
if minetest.get_item_group(stack:get_name(), "sign_bot_sign") ~= 1
and stack:get_name() ~= "default:book_written" then
return 0
end
if listname == "temp" then
return 1
end
return stack:get_count()
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local function get_template_data(stack)
local name = stack:get_name()
local data = stack:get_meta():to_table().fields
if name == "default:sign_user" or name == "signs_bot:sign_cmnd" then
return data.description, data.cmnd
end
if name == "default:book_written" then
return data.title, data.text
end
end
local function get_dest_item(stack)
local name = stack:get_name()
if name == "signs_bot:sign_blank" or name == "signs_bot:sign_user" then
return ItemStack("signs_bot:sign_user")
end
if name == "signs_bot:sign_cmnd" then
return ItemStack("signs_bot:sign_cmnd")
end
end
local function move_to_output(pos)
local inv = M(pos):get_inventory()
local inp_stack = inv:get_stack("inp", 1)
local temp_stack = inv:get_stack("temp", 1)
local outp_stack = inv:get_stack("outp", 1)
local dest_item = get_dest_item(inp_stack)
local descr, cmnd = get_template_data(temp_stack)
if dest_item and descr then
dest_item:set_count(inp_stack:get_count())
local meta = dest_item:get_meta()
meta:set_string("description", descr)
meta:set_string("cmnd", cmnd)
inp_stack:clear()
inv:set_stack("inp", 1, inp_stack)
inv:set_stack("outp", 1, dest_item)
end
end
local function on_metadata_inventory_put(pos, listname, index, stack, player)
if listname == "inp" then
minetest.after(0.5, move_to_output, pos)
end
end
minetest.register_node("signs_bot:duplicator", {
description = S("Signs Duplicator"),
stack_max = 1,
tiles = {
-- up, down, right, left, back, front
'signs_bot_base_top.png',
'signs_bot_base_top.png',
'signs_bot_duplicator.png',
'signs_bot_duplicator.png',
'signs_bot_duplicator.png',
'signs_bot_duplicator.png',
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size('inp', 1)
inv:set_size('temp', 1)
inv:set_size('outp', 1)
meta:set_string("formspec", formspec)
end,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_take = allow_metadata_inventory_take,
on_metadata_inventory_put = on_metadata_inventory_put,
can_dig = function(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:is_empty("inp") and inv:is_empty("temp") and inv:is_empty("outp")
end,
paramtype2 = "facedir",
is_ground_content = false,
groups = {cracky = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_craft({
output = "signs_bot:duplicator",
recipe = {
{"default:steel_ingot", "group:wood", "default:steel_ingot"},
{"", "basic_materials:gear_steel", ""},
{"default:tin_ingot", "", "default:tin_ingot"}
}
})
local function formspec_user(cmnd)
cmnd = minetest.formspec_escape(cmnd)
return "size[6,5]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[0.2,0;"..cmnd.."]"
end
minetest.register_node("signs_bot:sign_user", {
description = S('Sign "user"'),
drawtype = "nodebox",
inventory_image = "signs_bot_sign_user.png",
node_box = {
type = "fixed",
fixed = {
{ -1/16, -8/16, -1/16, 1/16, 4/16, 1/16},
{ -6/16, -5/16, -2/16, 6/16, 3/16, -1/16},
},
},
paramtype2 = "facedir",
tiles = {
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png^signs_bot_sign_user.png",
},
after_place_node = function(pos, placer, itemstack)
local imeta = itemstack:get_meta()
local nmeta = minetest.get_meta(pos)
if imeta:get_string("description") ~= "" then
nmeta:set_string("signs_bot_cmnd", imeta:get_string("cmnd"))
nmeta:set_string("sign_name", imeta:get_string("description"))
end
nmeta:set_string("infotext", nmeta:get_string("sign_name"))
local text = nmeta:get_string("sign_name").."\n"..imeta:get_string("cmnd")
nmeta:set_string("formspec", formspec_user(text))
end,
after_dig_node = lib.after_dig_sign_node,
drop = "",
on_rotate = screwdriver.disallow,
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2, sign_bot_sign = 1, not_in_creative_inventory = 1},
sounds = default.node_sound_wood_defaults(),
})
signs_bot.register_sign({
name = "sign_blank",
description = S('Sign "blank"'),
commands = "",
image = "signs_bot_sign_blank.png",
})
minetest.register_craft({
output = "signs_bot:sign_blank 6",
recipe = {
{"group:wood", "default:stick", "group:wood"},
{"dye:yellow", "default:stick", "dye:yellow"},
{"", "", ""}
}
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "duplicator", {
name = S("Signs Duplicator"),
data = {
item = "signs_bot:duplicator",
text = table.concat({
S("The Duplicator can be used to make copies of signs."),
S("1. Put one 'cmnd' sign to be used as template into the 'Template' inventory"),
S("2. Add one or several 'blank signs' to the 'Input' inventory."),
S("3. Take the copies from the 'Output' inventory."),
"",
S("Written books [default:book_written] can alternatively be used as template"),
S("Already written signs can be used as input, too."),
}, "\n")
},
})
end
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "sign_blank", {
name = S('Sign "blank"'),
data = {
item = "signs_bot:sign_blank",
text = S("Needed as input for the Duplicator.")
},
})
end
+139
View File
@@ -0,0 +1,139 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Sensor Extender
(passive node, the Sensor detects the extender)
]]--
-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local function update_infotext(pos, dest_pos, cmnd)
M(pos):set_string("infotext", S("Sensor Extender: Connected with").." "..P2S(dest_pos).." / "..cmnd)
end
minetest.register_node("signs_bot:sensor_extender", {
description = S("Sensor Extender"),
inventory_image = "signs_bot_extender_inv.png",
drawtype = "nodebox",
node_box = {
type = "connected",
fixed = {
{-6/32, -1/2, -6/32, 6/32, -5/16, 6/32},
},
connect_front = {{-1/16, -1/2, -12/16, 1/16, -6/16, 1/16}},
connect_left = {{-12/16, -1/2, -1/16, 1/16, -6/16, 1/16}},
connect_back = {{-1/16, -1/2, -1/16, 1/16, -6/16, 12/16}},
connect_right = {{-1/16, -1/2, -1/16, 12/16, -6/16, 1/16}},
},
connects_to = {"group:sign_bot_sensor"},
tiles = {
-- up, down, right, left, back, front
"signs_bot_extender.png",
"signs_bot_extender.png",
"signs_bot_extender_side.png",
"signs_bot_extender_side.png",
"signs_bot_extender_side.png",
"signs_bot_extender_side.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
meta:set_string("infotext", S("Sensor Extender: Not connected"))
end,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
is_ground_content = false,
groups = {sign_bot_sensor = 1, cracky = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("signs_bot:sensor_extender_on", {
description = S("Sensor Extender"),
drawtype = "nodebox",
node_box = {
type = "connected",
fixed = {
{-6/32, -1/2, -6/32, 6/32, -5/16, 6/32},
},
connect_front = {{-1/16, -1/2, -12/16, 1/16, -6/16, 1/16}},
connect_left = {{-12/16, -1/2, -1/16, 1/16, -6/16, 1/16}},
connect_back = {{-1/16, -1/2, -1/16, 1/16, -6/16, 12/16}},
connect_right = {{-1/16, -1/2, -1/16, 12/16, -6/16, 1/16}},
},
connects_to = {"group:sign_bot_sensor"},
tiles = {
-- up, down, right, left, back, front
"signs_bot_extender_on.png",
"signs_bot_extender.png",
"signs_bot_extender_side.png",
"signs_bot_extender_side.png",
"signs_bot_extender_side.png",
"signs_bot_extender_side.png",
},
-- Called from the Sensor beside
after_place_node = function(pos)
minetest.get_node_timer(pos):start(1)
signs_bot.send_signal(pos)
signs_bot.lib.activate_extender_nodes(pos)
end,
on_timer = function(pos)
local node = tubelib2.get_node_lvm(pos)
node.name = "signs_bot:sensor_extender"
minetest.swap_node(pos, node)
return false
end,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
is_ground_content = false,
diggable = false,
groups = {sign_bot_sensor = 1, not_in_creative_inventory = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_craft({
output = "signs_bot:sensor_extender",
recipe = {
{"group:wood", "dye:yellow"},
{"default:mese_crystal_fragment", "default:steel_ingot"}
}
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "sensor_extender", {
name = S("Sensor Extender"),
data = {
item = "signs_bot:sensor_extender",
text = table.concat({
S("With the Sensor Extender, sensor signals can be sent to more than one actuator."),
S("Place one or more extender nearby the sensor and connect each extender"),
S("with one further actuator by means of the Connection Tool."),
}, "\n")
},
})
end
+458
View File
@@ -0,0 +1,458 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Script to generate the template file and update the translation files.
# Copy the script into the mod or modpack root folder and run it there.
#
# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
# LGPLv2.1+
#
# See https://github.com/minetest-tools/update_translations for
# potential future updates to this script.
from __future__ import print_function
import os, fnmatch, re, shutil, errno
from sys import argv as _argv
from sys import stderr as _stderr
# Running params
params = {"recursive": False,
"help": False,
"mods": False,
"verbose": False,
"folders": [],
"no-old-file": False,
"break-long-lines": False,
"sort": False
}
# Available CLI options
options = {"recursive": ['--recursive', '-r'],
"help": ['--help', '-h'],
"mods": ['--installed-mods', '-m'],
"verbose": ['--verbose', '-v'],
"no-old-file": ['--no-old-file', '-O'],
"break-long-lines": ['--break-long-lines', '-b'],
"sort": ['--sort', '-s']
}
# Strings longer than this will have extra space added between
# them in the translation files to make it easier to distinguish their
# beginnings and endings at a glance
doublespace_threshold = 80
def set_params_folders(tab: list):
'''Initialize params["folders"] from CLI arguments.'''
# Discarding argument 0 (tool name)
for param in tab[1:]:
stop_param = False
for option in options:
if param in options[option]:
stop_param = True
break
if not stop_param:
params["folders"].append(os.path.abspath(param))
def set_params(tab: list):
'''Initialize params from CLI arguments.'''
for option in options:
for option_name in options[option]:
if option_name in tab:
params[option] = True
break
def print_help(name):
'''Prints some help message.'''
print(f'''SYNOPSIS
{name} [OPTIONS] [PATHS...]
DESCRIPTION
{', '.join(options["help"])}
prints this help message
{', '.join(options["recursive"])}
run on all subfolders of paths given
{', '.join(options["mods"])}
run on locally installed modules
{', '.join(options["no-old-file"])}
do not create *.old files
{', '.join(options["sort"])}
sort output strings alphabetically
{', '.join(options["break-long-lines"])}
add extra line breaks before and after long strings
{', '.join(options["verbose"])}
add output information
''')
def main():
'''Main function'''
set_params(_argv)
set_params_folders(_argv)
if params["help"]:
print_help(_argv[0])
elif params["recursive"] and params["mods"]:
print("Option --installed-mods is incompatible with --recursive")
else:
# Add recursivity message
print("Running ", end='')
if params["recursive"]:
print("recursively ", end='')
# Running
if params["mods"]:
print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}")
run_all_subfolders("~/.minetest/mods")
elif len(params["folders"]) >= 2:
print("on folder list:", params["folders"])
for f in params["folders"]:
if params["recursive"]:
run_all_subfolders(f)
else:
update_folder(f)
elif len(params["folders"]) == 1:
print("on folder", params["folders"][0])
if params["recursive"]:
run_all_subfolders(params["folders"][0])
else:
update_folder(params["folders"][0])
else:
print("on folder", os.path.abspath("./"))
if params["recursive"]:
run_all_subfolders(os.path.abspath("./"))
else:
update_folder(os.path.abspath("./"))
#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
pattern_lua_s = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
pattern_lua_fs = re.compile(r'[\.=^\t,{\(\s]N?FS\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
pattern_lua_bracketed_s = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
pattern_lua_bracketed_fs = re.compile(r'[\.=^\t,{\(\s]N?FS\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
# Handles "concatenation" .. " of strings"
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
pattern_tr = re.compile(r'(.*?[^@])=(.*)')
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
pattern_tr_filename = re.compile(r'\.tr$')
pattern_po_language_code = re.compile(r'(.*)\.po$')
#attempt to read the mod's name from the mod.conf file. Returns None on failure
def get_modname(folder):
try:
with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
for line in mod_conf:
match = pattern_name.match(line)
if match:
return match.group(1)
except FileNotFoundError:
pass
return None
#If there are already .tr files in /locale, returns a list of their names
def get_existing_tr_files(folder):
out = []
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
if pattern_tr_filename.search(name):
out.append(name)
return out
# A series of search and replaces that massage a .po file's contents into
# a .tr file's equivalent
def process_po_file(text):
# The first three items are for unused matches
text = re.sub(r'#~ msgid "', "", text)
text = re.sub(r'"\n#~ msgstr ""\n"', "=", text)
text = re.sub(r'"\n#~ msgstr "', "=", text)
# comment lines
text = re.sub(r'#.*\n', "", text)
# converting msg pairs into "=" pairs
text = re.sub(r'msgid "', "", text)
text = re.sub(r'"\nmsgstr ""\n"', "=", text)
text = re.sub(r'"\nmsgstr "', "=", text)
# various line breaks and escape codes
text = re.sub(r'"\n"', "", text)
text = re.sub(r'"\n', "\n", text)
text = re.sub(r'\\"', '"', text)
text = re.sub(r'\\n', '@n', text)
# remove header text
text = re.sub(r'=Project-Id-Version:.*\n', "", text)
# remove double-spaced lines
text = re.sub(r'\n\n', '\n', text)
return text
# Go through existing .po files and, if a .tr file for that language
# *doesn't* exist, convert it and create it.
# The .tr file that results will subsequently be reprocessed so
# any "no longer used" strings will be preserved.
# Note that "fuzzy" tags will be lost in this process.
def process_po_files(folder, modname):
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
code_match = pattern_po_language_code.match(name)
if code_match == None:
continue
language_code = code_match.group(1)
tr_name = modname + "." + language_code + ".tr"
tr_file = os.path.join(root, tr_name)
if os.path.exists(tr_file):
if params["verbose"]:
print(f"{tr_name} already exists, ignoring {name}")
continue
fname = os.path.join(root, name)
with open(fname, "r", encoding='utf-8') as po_file:
if params["verbose"]:
print(f"Importing translations from {name}")
text = process_po_file(po_file.read())
with open(tr_file, "wt", encoding='utf-8') as tr_out:
tr_out.write(text)
# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
# Creates a directory if it doesn't exist, silently does
# nothing if it already exists
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise
# Converts the template dictionary to a text to be written as a file
# dKeyStrings is a dictionary of localized string to source file sets
# dOld is a dictionary of existing translations and comments from
# the previous version of this text
def strings_to_text(dkeyStrings, dOld, mod_name, header_comments):
lOut = [f"# textdomain: {mod_name}\n"]
if header_comments is not None:
lOut.append(header_comments)
dGroupedBySource = {}
for key in dkeyStrings:
sourceList = list(dkeyStrings[key])
if params["sort"]:
sourceList.sort()
sourceString = "\n".join(sourceList)
listForSource = dGroupedBySource.get(sourceString, [])
listForSource.append(key)
dGroupedBySource[sourceString] = listForSource
lSourceKeys = list(dGroupedBySource.keys())
lSourceKeys.sort()
for source in lSourceKeys:
localizedStrings = dGroupedBySource[source]
if params["sort"]:
localizedStrings.sort()
lOut.append("")
lOut.append(source)
lOut.append("")
for localizedString in localizedStrings:
val = dOld.get(localizedString, {})
translation = val.get("translation", "")
comment = val.get("comment")
if params["break-long-lines"] and len(localizedString) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{localizedString}={translation}")
if params["break-long-lines"] and len(localizedString) > doublespace_threshold:
lOut.append("")
unusedExist = False
for key in dOld:
if key not in dkeyStrings:
val = dOld[key]
translation = val.get("translation")
comment = val.get("comment")
# only keep an unused translation if there was translated
# text or a comment associated with it
if translation != None and (translation != "" or comment):
if not unusedExist:
unusedExist = True
lOut.append("\n\n##### not used anymore #####\n")
if params["break-long-lines"] and len(key) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{key}={translation}")
if params["break-long-lines"] and len(key) > doublespace_threshold:
lOut.append("")
return "\n".join(lOut) + '\n'
# Writes a template.txt file
# dkeyStrings is the dictionary returned by generate_template
def write_template(templ_file, dkeyStrings, mod_name):
# read existing template file to preserve comments
existing_template = import_tr_file(templ_file)
text = strings_to_text(dkeyStrings, existing_template[0], mod_name, existing_template[2])
mkdir_p(os.path.dirname(templ_file))
with open(templ_file, "wt", encoding='utf-8') as template_file:
template_file.write(text)
# Gets all translatable strings from a lua file
def read_lua_file_strings(lua_file):
lOut = []
with open(lua_file, encoding='utf-8') as text_file:
text = text_file.read()
#TODO remove comments here
text = re.sub(pattern_concat, "", text)
strings = []
for s in pattern_lua_s.findall(text):
strings.append(s[1])
for s in pattern_lua_bracketed_s.findall(text):
strings.append(s)
for s in pattern_lua_fs.findall(text):
strings.append(s[1])
for s in pattern_lua_bracketed_fs.findall(text):
strings.append(s)
for s in strings:
s = re.sub(r'"\.\.\s+"', "", s)
s = re.sub("@[^@=0-9]", "@@", s)
s = s.replace('\\"', '"')
s = s.replace("\\'", "'")
s = s.replace("\n", "@n")
s = s.replace("\\n", "@n")
s = s.replace("=", "@=")
lOut.append(s)
return lOut
# Gets strings from an existing translation file
# returns both a dictionary of translations
# and the full original source text so that the new text
# can be compared to it for changes.
# Returns also header comments in the third return value.
def import_tr_file(tr_file):
dOut = {}
text = None
header_comment = None
if os.path.exists(tr_file):
with open(tr_file, "r", encoding='utf-8') as existing_file :
# save the full text to allow for comparison
# of the old version with the new output
text = existing_file.read()
existing_file.seek(0)
# a running record of the current comment block
# we're inside, to allow preceeding multi-line comments
# to be retained for a translation line
latest_comment_block = None
for line in existing_file.readlines():
line = line.rstrip('\n')
if line[:3] == "###":
if header_comment is None:
# Save header comments
header_comment = latest_comment_block
# Stip textdomain line
tmp_h_c = ""
for l in header_comment.split('\n'):
if not l.startswith("# textdomain:"):
tmp_h_c += l + '\n'
header_comment = tmp_h_c
# Reset comment block if we hit a header
latest_comment_block = None
continue
if line[:1] == "#":
# Save the comment we're inside
if not latest_comment_block:
latest_comment_block = line
else:
latest_comment_block = latest_comment_block + "\n" + line
continue
match = pattern_tr.match(line)
if match:
# this line is a translated line
outval = {}
outval["translation"] = match.group(2)
if latest_comment_block:
# if there was a comment, record that.
outval["comment"] = latest_comment_block
latest_comment_block = None
dOut[match.group(1)] = outval
return (dOut, text, header_comment)
# Walks all lua files in the mod folder, collects translatable strings,
# and writes it to a template.txt file
# Returns a dictionary of localized strings to source file sets
# that can be used with the strings_to_text function.
def generate_template(folder, mod_name):
dOut = {}
for root, dirs, files in os.walk(folder):
for name in files:
if fnmatch.fnmatch(name, "*.lua"):
fname = os.path.join(root, name)
found = read_lua_file_strings(fname)
if params["verbose"]:
print(f"{fname}: {str(len(found))} translatable strings")
for s in found:
sources = dOut.get(s, set())
sources.add(f"### {os.path.basename(fname)} ###")
dOut[s] = sources
if len(dOut) == 0:
return None
templ_file = os.path.join(folder, "locale/template.txt")
write_template(templ_file, dOut, mod_name)
return dOut
# Updates an existing .tr file, copying the old one to a ".old" file
# if any changes have happened
# dNew is the data used to generate the template, it has all the
# currently-existing localized strings
def update_tr_file(dNew, mod_name, tr_file):
if params["verbose"]:
print(f"updating {tr_file}")
tr_import = import_tr_file(tr_file)
dOld = tr_import[0]
textOld = tr_import[1]
textNew = strings_to_text(dNew, dOld, mod_name, tr_import[2])
if textOld and textOld != textNew:
print(f"{tr_file} has changed.")
if not params["no-old-file"]:
shutil.copyfile(tr_file, f"{tr_file}.old")
with open(tr_file, "w", encoding='utf-8') as new_tr_file:
new_tr_file.write(textNew)
# Updates translation files for the mod in the given folder
def update_mod(folder):
modname = get_modname(folder)
if modname is not None:
process_po_files(folder, modname)
print(f"Updating translations for {modname}")
data = generate_template(folder, modname)
if data == None:
print(f"No translatable strings found in {modname}")
else:
for tr_file in get_existing_tr_files(folder):
update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
else:
print(f"\033[31mUnable to find modname in folder {folder}.\033[0m", file=_stderr)
exit(1)
# Determines if the folder being pointed to is a mod or a mod pack
# and then runs update_mod accordingly
def update_folder(folder):
is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
if is_modpack:
subfolders = [f.path for f in os.scandir(folder) if f.is_dir()]
for subfolder in subfolders:
update_mod(subfolder + "/")
else:
update_mod(folder)
print("Done.")
def run_all_subfolders(folder):
for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]:
update_folder(modfolder + "/")
main()
+80
View File
@@ -0,0 +1,80 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2023 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
A robot controlled by signs
]]--
signs_bot = {}
-- Version for compatibility checks, see readme.md/history
signs_bot.version = 1.14
-- Test for MT 5.4 new string mode
signs_bot.CLIP = minetest.features.use_texture_alpha_string_modes and "clip" or true
if minetest.global_exists("techage") and techage.version < 1.0 then
error("[signs_bot] Signs Bot requires techage version 1.0 or newer!")
end
if tubelib2.version < 1.9 then
error("[signs_bot] Signs Bot requires tubelib2 version 1.9 or newer!")
end
if minetest.global_exists("minecart") and minecart.version < 2.0 then
error("[signs_bot] Signs Bot requires minecart version 2.0 or newer!")
end
-- Load support for I18n.
signs_bot.S = minetest.get_translator("signs_bot")
local MP = minetest.get_modpath("signs_bot")
dofile(MP.."/doc.lua")
dofile(MP.."/random.lua")
dofile(MP.."/lib.lua")
dofile(MP.."/basis.lua")
dofile(MP.."/robot.lua")
dofile(MP.."/signs.lua")
dofile(MP.."/commands.lua")
dofile(MP.."/cmd_move.lua")
dofile(MP.."/cmd_item.lua")
dofile(MP.."/cmd_place.lua")
dofile(MP.."/cmd_sign.lua")
dofile(MP.."/cmd_pattern.lua")
dofile(MP.."/cmd_farming.lua")
dofile(MP.."/cmd_flowers.lua")
dofile(MP.."/cmd_soup.lua")
dofile(MP.."/cmd_trees.lua")
dofile(MP.."/signal.lua")
dofile(MP.."/extender.lua")
dofile(MP.."/changer.lua")
dofile(MP.."/bot_flap.lua")
dofile(MP.."/duplicator.lua")
dofile(MP.."/nodes.lua")
dofile(MP.."/bot_sensor.lua")
dofile(MP.."/node_sensor.lua")
dofile(MP.."/crop_sensor.lua")
if minetest.global_exists("minecart") then
dofile(MP.."/cart_sensor.lua")
end
dofile(MP.."/chest.lua")
dofile(MP.."/legacy.lua")
dofile(MP.."/techage.lua")
dofile(MP.."/timer.lua")
dofile(MP.."/delayer.lua")
dofile(MP.."/logic_and.lua")
dofile(MP.."/compost.lua")
dofile(MP.."/manual.lua")
dofile(MP.."/tool.lua")
@@ -0,0 +1,317 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signs Bot: Command interpreter
]]--
-- Load support for I18n.
local S = signs_bot.S
local MAX_SIZE = 1000 -- max number of tokens
local tCmdDef = {}
local lCmdLookup = {}
local tSymbolTbl = {}
local CodeCache = {}
local api = {}
-- Possible command results
api.BUSY = 1 -- execute the same command again
api.DONE = 2 -- next command
api.NEW = 3 -- switch to a new script, provided as second value
api.ERROR = 4 -- stop execution with error, error message provided as second value
api.EXIT = 5 -- stop execution
-------------------------------------------------------------------------------
-- Compiler
-------------------------------------------------------------------------------
local function trim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
local function get_line_tokens(script)
local idx = 0
script = script or ""
script = script:gsub("\r\n", "\n")
script = script:gsub("\r", "\n")
local lines = string.split(script, "\n", true)
return function()
while idx < #lines do
idx = idx + 1
-- remove comments
local line = string.split(lines[idx], "--", true, 1)[1] or ""
-- remove blanks
line = trim(line)
if #line > 0 then
-- split into tokens
return idx, unpack(string.split(line, " "))
end
end
end
end
local function dbg_out(opcode, num_param, code, pc)
if num_param == 0 then
print(">>"..lCmdLookup[opcode][3])
elseif num_param == 1 then
print(">>"..lCmdLookup[opcode][3].." "..code[pc+1])
elseif num_param == 2 then
print(">>"..lCmdLookup[opcode][3].." "..code[pc+1].." "..code[pc+2])
else
print(">>"..lCmdLookup[opcode][3].." "..code[pc+1].." "..code[pc+2].." "..code[pc+3])
end
end
local function tokenizer(script)
local tokens = {}
for _, cmnd, param1, param2, param3 in get_line_tokens(script) do
if tCmdDef[cmnd] then
local num_param = tCmdDef[cmnd].num_param
tokens[#tokens + 1] = cmnd
if num_param >= 1 then
tokens[#tokens + 1] = param1 or "nil"
end
if num_param >= 2 then
tokens[#tokens + 1] = param2 or "nil"
end
if num_param >= 3 then
tokens[#tokens + 1] = param3 or "nil"
end
elseif cmnd:find("%w+:$") then
tokens[#tokens + 1] = cmnd
end
end
tokens[#tokens + 1] = "exit"
return tokens
end
local function pass1(tokens)
local pc = 1
tSymbolTbl = {}
for _, token in ipairs(tokens) do
if token:find("%w+:$") then
tSymbolTbl[token] = pc
else
pc = pc + 1
end
end
end
local function pass2(tokens)
local code = {}
local num_param = 0
for _, token in ipairs(tokens) do
if num_param > 0 then
code[#code + 1] = tonumber(token) or tSymbolTbl[token..":"] or token
num_param = num_param - 1
elseif tCmdDef[token] then
num_param = tCmdDef[token].num_param
code[#code + 1] = tCmdDef[token].opcode
end
end
return code
end
local function compile(script)
local tokens = tokenizer(script)
pass1(tokens)
return pass2(tokens)
end
local function gen_string_cmnd(code, pc, num_param, script)
local tokens = tokenizer(script)
pc = math.min(pc, #tokens)
if num_param == 0 then
return tokens[pc]
elseif num_param == 1 then
return tokens[pc] .. " " .. (tokens[pc+1] or "")
elseif num_param == 2 then
return tokens[pc] .. " " .. (tokens[pc+1] or "") .. " " .. (tokens[pc+2] or "")
else
return tokens[pc] .. " " .. (tokens[pc+1] or "") .. " " .. (tokens[pc+2] or "") .. " " .. (tokens[pc+3] or "")
end
end
-------------------------------------------------------------------------------
-- Commands
-------------------------------------------------------------------------------
local function register_command(cmnd_name, num_param, cmnd_func, check_func)
assert(num_param, cmnd_name..": num_param = "..dump(num_param))
assert(cmnd_func, cmnd_name..": cmnd_func = "..dump(cmnd_func))
assert(check_func or num_param == 0, cmnd_name..": check_func = "..dump(check_func))
lCmdLookup[#lCmdLookup + 1] = {num_param, cmnd_func, cmnd_name}
tCmdDef[cmnd_name] = {
num_param = num_param,
cmnd = cmnd_func,
name = cmnd_name,
check = check_func,
opcode = #lCmdLookup,
}
end
register_command("repeat", 1,
function(base_pos, mem, cnt)
mem.Stack[#mem.Stack + 1] = tonumber(cnt)
mem.Stack[#mem.Stack + 1] = mem.pc + 1
return api.DONE
end,
function(cnt)
cnt = tonumber(cnt) or 0
return cnt > 0 and cnt < 1000
end
)
register_command("end", 0,
function(base_pos, mem)
if #mem.Stack < 2 then
return api.ERROR
end
mem.Stack[#mem.Stack - 1] = mem.Stack[#mem.Stack - 1] - 1
if mem.Stack[#mem.Stack - 1] > 0 then
mem.pc = mem.Stack[#mem.Stack]
else
mem.Stack[#mem.Stack] = nil
mem.Stack[#mem.Stack] = nil
end
return api.DONE
end
)
register_command("call", 1,
function(base_pos, mem, addr)
if #mem.Stack > 99 then
return api.ERROR, "call stack overrun"
end
mem.Stack[#mem.Stack + 1] = mem.pc + 2
mem.pc = addr - 2
return api.DONE
end,
function(addr)
return addr and tSymbolTbl[addr..":"]
end
)
register_command("return", 0,
function(base_pos, mem)
if #mem.Stack < 1 then
return api.ERROR, "no return address"
end
mem.pc = (mem.Stack[#mem.Stack] or 1) - 1
mem.Stack[#mem.Stack] = nil
return api.DONE
end
)
register_command("jump", 1,
function(base_pos, mem, addr)
mem.pc = addr - 2
return api.DONE
end,
function(addr)
return addr and tSymbolTbl[addr..":"]
end
)
register_command("exit", 0,
function(base_pos, mem)
return api.EXIT
end
)
-------------------------------------------------------------------------------
-- API functions
-------------------------------------------------------------------------------
function api.register_command(cmnd_name, num_param, cmnd_func, check_func)
register_command(cmnd_name, num_param, cmnd_func, check_func)
end
function api.check_label(label)
return label and tSymbolTbl[label..":"] ~= nil
end
-- function returns: true/false, error_string, line-num
function api.check_script(script)
local tbl = {}
local num_token = 0
-- to fill the symbol table
local tokens = tokenizer(script)
pass1(tokens)
for idx, cmnd, param1, param2, param3 in get_line_tokens(script) do
if tCmdDef[cmnd] then
num_token = num_token + 1 + tCmdDef[cmnd].num_param
if num_token > MAX_SIZE then
return false, S("Maximum programm size exceeded"), idx
end
param1 = tonumber(param1) or param1
param2 = tonumber(param2) or param2
param3 = tonumber(param3) or param3
local num_param = (param1 and 1 or 0) + (param2 and 1 or 0) + (param3 and 1 or 0)
if tCmdDef[cmnd].num_param < num_param then
return false, S("Too many parameters"), idx
end
if tCmdDef[cmnd].num_param > 0 and not tCmdDef[cmnd].check(param1, param2, param3) then
return false, S("Parameter error"), idx
end
elseif not cmnd:find("%w+:$") then
return false, S("Command error"), idx
end
tbl[cmnd] = (tbl[cmnd] or 0) + 1
end
if (tbl["end"] or 0) > (tbl["repeat"] or 0) then
return false, S("'repeat' missing"), 0
elseif (tbl["end"] or 0) < (tbl["repeat"] or 0) then
return false, S("'end' missing"), 0
end
return true, S("Checked and approved"), 0
end
-- function returns: true/false, error-string
-- default_cmnd is used for the 'cond_move'
function api.run_script(base_pos, mem)
local hash = minetest.hash_node_position(base_pos)
CodeCache[hash] = CodeCache[hash] or compile(mem.script)
local code = CodeCache[hash]
mem.pc = mem.pc or 1
mem.Stack = mem.Stack or {}
local opcode = code[mem.pc]
if opcode then
local num_param, func = unpack(lCmdLookup[opcode])
--dbg_out(opcode, num_param, code, mem.pc)
local res, err = func(base_pos, mem, code[mem.pc+1], code[mem.pc+2], code[mem.pc+3])
if res == api.DONE then
mem.pc = mem.pc + 1 + num_param
elseif res == api.NEW then
CodeCache[hash] = compile(mem.script)
mem.pc = 1
mem.Stack = {}
elseif res == api.ERROR then
return res, err, gen_string_cmnd(code, mem.pc, num_param, mem.script)
end
return res, err, ""
end
return api.EXIT
end
function api.reset_script(base_pos, mem)
local hash = minetest.hash_node_position(base_pos)
CodeCache[hash] = nil
mem.pc = 1
mem.Stack = {}
mem.bot_falling = nil
end
return api
@@ -0,0 +1,4 @@
#!/bin/bash
../intllib/tools/xgettext.sh ./basis.lua ./bot_flap.lua ./bot_sensor.lua ./cart_sensor.lua ./changer.lua ./chest.lua ./cmd_farming.lua ./cmd_flowers.lua ./cmd_item.lua ./cmd_move.lua ./cmd_pattern.lua ./cmd_place.lua ./cmd_sign.lua ./commands.lua ./crop_sensor.lua ./doc.lua ./duplicator.lua ./extender.lua ./init.lua ./lib.lua ./node_sensor.lua ./nodes.lua ./robot.lua ./signal.lua ./signs.lua ./tool.lua ./timer.lua ./delayer.lua ./logic_and.lua ./interpreter.lua ./compost.lua
+98
View File
@@ -0,0 +1,98 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Registation of standard chests and furnace
]]--
-- for lazy programmers
local M = minetest.get_meta
signs_bot.register_inventory({"default:chest", "default:chest_open"}, {
put = {
listname = "main",
},
take = {
listname = "main",
},
})
signs_bot.register_inventory({"default:chest_locked", "default:chest_locked_open"}, {
put = {
allow_inventory_put = function(pos, stack, player_name)
local owner = M(pos):get_string("owner")
return owner == player_name
end,
listname = "main",
},
take = {
allow_inventory_take = function(pos, stack, player_name)
local owner = M(pos):get_string("owner")
return owner == player_name
end,
listname = "main",
},
})
signs_bot.register_inventory({"default:furnace", "default:furnace_active"}, {
put = {
allow_inventory_put = function(pos, stack, player_name)
minetest.get_node_timer(pos):start(1.0)
return true
end,
listname = "src",
},
take = {
listname = "dst",
},
fuel = {
allow_inventory_put = function(pos, stack, player_name)
minetest.get_node_timer(pos):start(1.0)
return true
end,
listname = "fuel",
},
})
signs_bot.register_inventory({"mobs:beehive"}, {
put = {
listname = "beehive",
},
take = {
listname = "beehive",
},
})
signs_bot.register_inventory({"xdecor:hive"}, {
put = {
listname = "honey",
},
take = {
listname = "honey",
},
})
signs_bot.register_inventory({"shop:shop"}, {
put = {
allow_inventory_put = function(pos, stack, player_name)
local owner = M(pos):get_string("owner")
return owner == player_name
end,
listname = "stock",
},
take = {
allow_inventory_take = function(pos, stack, player_name)
local owner = M(pos):get_string("owner")
return owner == player_name
end,
listname = "register",
},
})
+319
View File
@@ -0,0 +1,319 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signs Bot: Library with helper functions
]]--
-- for lazy programmers
local M = minetest.get_meta
signs_bot.lib = {}
local Face2Dir = {[0]=
{x=0, y=0, z=1},
{x=1, y=0, z=0},
{x=0, y=0, z=-1},
{x=-1, y=0, z=0},
{x=0, y=-1, z=0},
{x=0, y=1, z=0}
}
-- allowed for digging
local NotSoSimpleNodes = {}
function signs_bot.lib.register_node_to_be_dug(name)
NotSoSimpleNodes[name] = true
end
-- Determine the next robot position based on the robot position,
-- the robot param2.
function signs_bot.lib.next_pos(pos, param2)
return vector.add(pos, Face2Dir[param2])
end
-- Determine the destination position based on the robot position,
-- the robot param2, and a route table like : {0,0,3}
-- 0 = forward, 1 = right, 2 = backward, 3 = left
function signs_bot.lib.dest_pos(pos, param2, route)
local p2 = param2
for _,dir in ipairs(route) do
p2 = (param2 + dir) % 4
pos = vector.add(pos, Face2Dir[p2])
end
return pos, p2
end
local next_pos = signs_bot.lib.next_pos
local dest_pos = signs_bot.lib.dest_pos
local get_node_lvm = tubelib2.get_node_lvm
local function poke_objects(pos, param2, objects)
minetest.sound_play('signs_bot_go_away', {pos = pos})
for _,obj in ipairs(objects) do
local pos1 = obj:get_pos()
pos1 = vector.add(pos1, vector.multiply(Face2Dir[param2], 0.2))
obj:move_to(pos1)
end
end
-- check if nodeA on posA == air-like and nodeB == solid and no player around
function signs_bot.lib.check_pos(posA, nodeA, nodeB, param2)
local ndefA = minetest.registered_nodes[nodeA.name]
local ndefB = minetest.registered_nodes[nodeB.name]
if ndefA and not ndefA.walkable and ndefB and ndefB.walkable then
return true
end
return false
end
local function handle_drop(drop)
-- To keep it simple, return only the item with the lowest rarity
if drop.items then
local rarity = 9999
local name
for idx,item in ipairs(drop.items) do
if item.rarity and item.rarity < rarity then
rarity = item.rarity
name = item.items[1] -- take always the first item
else
return item.items[1] -- take always the first item
end
end
return name
end
end
function signs_bot.handle_drop_like_a_player(node)
local drops = minetest.get_node_drops(node)
if #drops >= 1 then
return drops[1]
end
return false
end
-- Has to be checked before a node is placed
function signs_bot.lib.is_air_like(pos)
local node = get_node_lvm(pos)
local ndef = minetest.registered_nodes[node.name]
if ndef and ndef.buildable_to then
return true
end
return false
end
-- Has to be checked before a node is dug
function signs_bot.lib.is_simple_node(node)
if node.name == "signs_bot:box" then return false end
-- don't remove nodes with some intelligence or undiggable nodes
local ndef = minetest.registered_nodes[node.name]
if not NotSoSimpleNodes[node.name] then
if not ndef or node.name == "air" then return false end
if ndef.drop == "" then return false end
if ndef.diggable == false then return false end
-- Allow digging nodes with after_dig_node only when everness is loaded
if ndef.after_dig_node and not minetest.get_modpath("everness") then return false end
end
if type(ndef.drop) == "table" then
return handle_drop(ndef.drop)
end
return ndef.drop or node.name
end
-- Check rights before node is dug or inventory is used
function signs_bot.lib.not_protected(base_pos, pos)
local me = M(base_pos):get_string("owner")
minetest.load_area(pos)
if minetest.is_protected(pos, me) then
return false
end
local you = M(pos):get_string("owner")
if you ~= "" and me ~= you then
return false
end
return true
end
--
-- Access functions for chest like nodes
-- (bot inventory releated function are in basis.lua)
-- Search for items in the inventory and return the item_name or nil.
function signs_bot.lib.peek_inv(inv, listname)
if inv:is_empty(listname) then return nil end
local inv_size = inv:get_size(listname)
for idx in signs_bot.random(inv_size) do
local stack = inv:get_stack(listname, idx)
if stack:get_count() > 0 then
return stack:get_name()
end
end
end
-- In the case an inventory is full
function signs_bot.lib.drop_items(robot_pos, items)
local pos1 = {x=robot_pos.x-1, y=robot_pos.y, z=robot_pos.z-1}
local pos2 = {x=robot_pos.x+1, y=robot_pos.y, z=robot_pos.z+1}
for _,pos in ipairs(minetest.find_nodes_in_area(pos1, pos2, {"air"})) do
minetest.add_item(pos, items)
return
end
end
--
-- Place/dig signs
--
function signs_bot.lib.place_sign(pos, sign, param2)
if sign:get_name() then
minetest.set_node(pos, {name=sign:get_name(), param2=param2})
local ndef = minetest.registered_nodes[sign:get_name()]
if ndef and ndef.after_place_node then
ndef.after_place_node(pos, nil, sign)
end
end
end
function signs_bot.lib.dig_sign(pos, node)
node = node or get_node_lvm(pos)
local nmeta = minetest.get_meta(pos)
local cmnd = nmeta:get_string("signs_bot_cmnd")
local sign
if cmnd ~= "" then
if node.name == "signs_bot:sign_cmnd" then
local err_code = nmeta:get_int("err_code")
local err_msg = nmeta:get_string("err_msg")
local name = nmeta:get_string("sign_name")
sign = ItemStack("signs_bot:sign_cmnd")
local smeta = sign:get_meta()
smeta:set_string("cmnd", cmnd)
smeta:set_int("err_code", err_code)
smeta:set_string("err_msg", err_msg)
smeta:set_string("description", name)
else
sign = ItemStack(node.name)
end
minetest.remove_node(pos)
return sign
end
end
function signs_bot.lib.after_dig_sign_node(pos, oldnode, oldmetadata, digger)
local sign = ItemStack(oldnode.name)
local smeta = sign:get_meta()
smeta:set_string("cmnd", oldmetadata.fields.signs_bot_cmnd)
smeta:set_string("description", oldmetadata.fields.sign_name)
if oldmetadata.fields.err_code then
smeta:set_int("err_code", tonumber(oldmetadata.fields.err_code))
smeta:set_string("err_msg", oldmetadata.fields.err_msg or "")
end
local player_name = digger:get_player_name()
if player_name == "" then
minetest.add_item(pos, sign)
else
local inv = minetest.get_inventory({type="player", name=digger:get_player_name()})
local left_over = inv:add_item("main", sign)
if left_over:get_count() > 0 then
minetest.add_item(pos, sign)
end
end
end
local function activate_extender_node(pos)
local node = get_node_lvm(pos)
if node.name == "signs_bot:sensor_extender" then
node.name = "signs_bot:sensor_extender_on"
minetest.swap_node(pos, node)
minetest.registered_nodes["signs_bot:sensor_extender_on"].after_place_node(pos)
end
end
local NestedCounter = 0
function signs_bot.lib.activate_extender_nodes(pos, is_sensor)
if is_sensor then
NestedCounter = 0
else
NestedCounter = NestedCounter + 1
if NestedCounter >= 5 then
return
end
end
activate_extender_node({x=pos.x-1, y=pos.y, z=pos.z})
activate_extender_node({x=pos.x+1, y=pos.y, z=pos.z})
activate_extender_node({x=pos.x, y=pos.y, z=pos.z-1})
activate_extender_node({x=pos.x, y=pos.y, z=pos.z+1})
end
--
-- Determine the field positions
--
local function start_pos(robot_pos, robot_param2, x_size, lvl_offs)
local pos = next_pos(robot_pos, robot_param2)
pos = {x=pos.x, y=pos.y+lvl_offs, z=pos.z}
if tonumber(x_size) == 5 then
return dest_pos(pos, robot_param2, {3,3})
else
return dest_pos(pos, robot_param2, {3})
end
end
--
-- Return a table with all positions to copy
--
function signs_bot.lib.gen_position_table(robot_pos, robot_param2, x_size, z_size, lvl_offs)
local tbl = {}
if robot_pos and robot_param2 and x_size and z_size and lvl_offs then
local pos = start_pos(robot_pos, robot_param2, x_size, lvl_offs)
tbl[#tbl+1] = pos
z_size = math.min(z_size, 5)
for z = 1,z_size do
for x = 1,x_size-1 do
local dir = (z % 2) == 0 and 3 or 1
pos = dest_pos(pos, robot_param2, {dir})
tbl[#tbl+1] = pos
end
if z < z_size then
pos = dest_pos(pos, robot_param2, {0})
tbl[#tbl+1] = pos
end
end
end
return tbl
end
function signs_bot.lib.trim_text(text)
local tbl = {}
for idx,line in ipairs(string.split(text, "[\r\n]+", true, -1, true)) do
tbl[#tbl+1] = line:trim()
end
return table.concat(tbl, "\n")
end
function signs_bot.lib.fake_player(name)
return {
get_player_name = function() return name end,
is_player = function() return false end,
}
end
function signs_bot.lib.swap_node(pos, node_name)
minetest.swap_node(pos, node_name)
local mod = node_name.name:match("(.*):")
if mod == "farming" and farming.start_seed_timer then
farming.start_seed_timer(pos)
end
end
signs_bot.lib.register_node_to_be_dug("default:cactus")
signs_bot.lib.register_node_to_be_dug("default:papyrus")
@@ -0,0 +1,477 @@
# textdomain: signs_bot
### basis.lua ###
running=läuft
stopped=gestoppt
Off=Aus
On=An
Signs=Zeichen
Other items=Andere Gegenstände
Config=Konfig.
Preassign slots items=Vorbelegungen
Back=Zurück
Robot Box=Roboterbox
Signs Bot Box=Roboter Box
The Box is the housing of the bot.=Die Box ist das Gehäuse des Roboters.
Place the box and start the bot by means of the 'On' button.=Platziere die Box und starte den Roboter über den "An" Button.
If the mod techage is installed, the bot needs electrical power.=Wenn die Mod techage installiert ist, benötigt der Roboter elektrischen Strom.
The bot leaves the box on the right side.=Der Roboter verlässt die Box auf der rechten Seite.
It will not start, if this position is blocked.=Er startet nicht, wenn diese Position belegt ist.
To stop and remove the bot, press the 'Off' button.=Um den Roboter zu stoppen bzw. zu entfernen, drücke den "Aus" Button.
The box inventory simulates the inventory of the bot.=Das Inventar der Box simuliert das Roboter Inventar.
You will not be able to access the inventory, if the bot is running.=Du hast keinen Zugriff auf das Inventar, sofern der Roboter unterwegs ist.
The bot can carry up to 8 stacks and 6 signs with it.=Der Roboter kann 8 Stapel von Blöcken und 6 Zeichen transportieren.
### basis.lua ###
### techage.lua ###
charging=aufladen
### bot_flap.lua ###
Exit=Beenden
Bot Flap=Roboterklappe
The flap is a simple block used as door for the bot.=Die Klappe ist ein einfacher Block, welcher vom Roboter als Tür genutzt wird.
Place the flap in any wall, and the bot will automatically open=Platziere die Klappe in eine Mauer und der Roboter wird die Klappe automatisch öffnen
and close the flap as it passes through it.=und wieder schließen, wenn er durchgegangen ist.
### bot_sensor.lua ###
Bot Sensor: Connected with=Bot Sensor: Verbunden mit
Bot Sensor=Bot Sensor
Bot Sensor: Not connected=Bot Sensor: Nicht verbunden
The Bot Sensor detects any bot and sends a signal, if a bot is nearby.=Der Roboter Sensor entdeckt jeden Roboter und sendet ein Signal, sofern ein Roboter in der Nähe ist.
The sensor direction does not care.=Die Ausrichtung des Sensor spielt keine Rolle.
### cart_sensor.lua ###
Cart Sensor: Connected with=Wagen Sensor: Verbunden mit
Cart Sensor=Wagen Sensor
Cart Sensor: Not connected=Wagen Sensor: Nicht verbunden
The Cart Sensor detects and sends a signal, if a cart (Minecart) is nearby.=Der Wagen Sensor sendet ein Signal, sofern ein Wagen in der Nähe ist.
The sensor has an active side (red) that must point to the rail/cart.=Der Sensor hat eine aktive Seite (rot), welche zu den Schienen zeigen muss.
### cart_sensor.lua ###
### bot_sensor.lua ###
the sensor range is one node/meter.=Der Sensorbereich ist einen Block/Meter groß.
### changer.lua ###
Signs:=Zeichen:
Bot Control Unit=Roboter Steuerungseinheit
The Bot Control Unit is used to lead the bot by means of signs.=Die Roboter Steuerungseinheit dient zur Steuerung des Roboters über Zeichen.
The unit can be loaded with up to 4 different signs and can be programmed by means of sensors.=Die Einheit kann mit bis zu 4 verschiedenen Zeichen geladen und über Sensoren programmiert werden.
To load the unit, place a sign on the red side of the unit and click on the unit.=Um die Steuerungseinheit zu laden, platziere ein Zeichen auf die rote Seite der Einheit und klicke auf die Einheit.
The sign disappears / is moved to the inventory of the unit.=Das Zeichen verschwindet/ist in die Steuerungseinheit verschoben.
This can be repeated 3 times.=Dies kann bis zu 3 mal wiederholt werden.
Use the connection tool to connect up to 4 sensors with the Bot Control Unit.=Benutze das Verbinde-Werkzeug um die bis zu 4 Sensoren mit der Steuerungseinheit zu verbinden.
### chest.lua ###
Bot Chest: Sends signal to=Bot Kiste: Sende Signal zu
Signs Bot Chest=Signs Bot Kiste
Bot Chest: Not connected=Bot Kiste: nicht verbunden
The Signs Bot Chest is a special chest with sensor function.=Die Roboter Kiste ist eine spezielle Kiste mit zusätzlicher Sensor-Funktion.
It sends a signal depending on the chest state.=Sie sendet ein Signal abhängig vom Zustand der Kiste.
Possible states are 'empty', 'not empty', 'almost full'=Mögliche Zustände sind "leer", "nicht leer" und "fast voll"
A typical use case is to turn off the bot, when the chest is almost full or empty.=Ein typischer Anwendungsfall ist den Roboter zu stoppen, wenn die Kiste fast voll oder leer ist.
### cmd_farming.lua ###
Sow farming seeds@nin front of the robot=Sähe Saatgut@nvor dem Roboter
Harvest farming products@nin front of the robot@non a 3x3 field.=Ernte Früchte/Getreide@nin einem 3x3 großem Feld@nvor dem Roboter.
Plant a sapling@nin front of the robot=Pflanze einen Setzling@nvor den Roboter
Sign "farming"=Zeichen "Farming"
Sign 'farming'=Zeichen 'Farming'
Used to harvest and seed a 3x3 field.=Benötigt um ein 3x3 Feld zu ernten und wieder zu sähen.
The seed to be placed has to be in the first inventory slot of the bot.=Das Saatgut, dass gesät werden soll, muss sich an der 1. Position im Inventar befinden.
### cmd_farming.lua ###
### cmd_flowers.lua ###
Place the sign in front of the field.=Platziere das Zeichen vor das Feld.
When finished, the bot turns.=Der Roboter dreht um, wenn er fertig ist.
### cmd_flowers.lua ###
Cutting flowers, papyrus,@nleaves and tree blocks@nin front of the robot@non a 3x3 field.=Schneide Blumen, Papyrus,@nBlätter und Baumblöcke@nin einem 3x3 großem Feld@nvor dem Roboter.
Sign "flowers"=Zeichen "Blumen"
Sign 'flowers'=Zeichen 'Blumen'
Used to cut flowers on a 3x3 field.=Benötigt um ein 3x3 Blumenfeld zu ernten.
### cmd_item.lua ###
Take <num> items from a chest like node@nand put it into the item inventory.@n<slot> is the bot inventory slot@n(1..8) or 0 for any one=Nehme <num> Gegenstände aus der@nKiste oder dem Kisten-ähnlichen Block@nund tue diese in das eigene Inventar@nan der Position <slot>. Slot @= (1..8)@noder 0 für irgend eine Position
Check if there are <num>@nitems in the chest like node.@nIf not, jump to <label>@n<slot> is the bot inventory slot@n(1..8) to specify the item, or 0 for any item=Prüfe, ob sich <num> Gegenstände@nin dem Kisten-ähnlichen Block befinden.@nWenn nicht, springe zu <label>@n<slot> ist der Bot-Inventar-Slot@n(1..8) um einen Gegenstand auszuwählen, oder 0 für irgendeinen Gegenstand
Add <num> items to a chest like node@ntaken from the item inventory.@n<slot> is the bot inventory slot (1..8) or 0 for any one=Lege <num> Gegenstände aus dem@neigenen Inventar in die andere Kiste.@n<slot> ist die Position im@neigenen Inventar (1--8).@noder 0 für irgend eine Position
Add <num> fuel to a furnace like node@ntaken from the item inventory.@n<slot> is the bot inventory slot (1..8) or 0 for any one=Lege <num> Brennstoffe aus dem@neigenen Inventar in den anderen Block.@n<slot> ist die Position im@neigenen Inventar (1--8).@noder 0 für irgend eine Position
deprecated, use bot inventory configuration instead=veraltet, benutze stattdessen die Inventar Konfigurationsmöglichkeit
Pick up all objects@nin a 3x3 field.@n<slot> is the bot inventory slot (1..8) or 0 for any one=Hebe alle Objekte in einem@n3x3 Blöcke großen Feld auf@nund lege diese in das eigene@nInventar an Position <slot> (1-8)@noder 0 für irgend eine Position
Drop items in front of the bot.@n<slot> is the bot inventory slot (1..8) or 0 for any one=Lasse ein Objekt aus dem eigenen@nInventar vor dem Roboter fallen.@n<slot> ist die Position im@neigenen Inventar (1--8).@noder 0 für irgend eine Position
Punch a rail cart to start it=Schlage den Wagen um ihn zu starten
### cmd_move.lua ###
Move the robot one step back=Bewege den Roboter@neinen Schritt zurück
Turn the robot to the left=Drehe den Roboter@nnach links
Turn the robot to the right=Drehe den Roboter@nnach rechts
Turn the robot around=Drehe den Roboter@num (180 Grad)
Move the robot upwards=Bewege den Roboter@nnach oben
Move the robot down=Bewege den Roboter@nnach unten
Fall into a hole/chasm (up to 10 blocks)=Falle in ein Loch/Abgrund (bis zu 10 Blöcke)
Stop the robot for <sec> seconds@n(1..9999)=Stoppe den Roboter@nfür <sec> Sekunden (1.9999)
Stop the robot.=Stoppe den Roboter.
Turn the robot off@nand put it back in the box.=Schalte den Roboter aus und@nsetze ihn damit zurück in@nseine Box.
### cmd_pattern.lua ###
Store pattern to be cloned.=Speichere die Vorlage@ndie kopiert werden soll.
Copy the nodes from@nthe stored pattern position@n<size> is: 3x1, 3x2, 3x3,@n5x1, 5x2, 5x3 (wide x deep)@n<lvl> pattern level offset (0..4)=Kopiere die Blöcke von der@n"Vorlage" Position.@n<size> ist: 3x1, 3x2, 3x3,@n5x1, 5x2, 5x3 (Breite x Tiefe)@n<lvl> Vorlagenebene (0..4)
Sign "pattern"=Zeichen "Vorlage"
Sign "copy 3x3x3"=Zeichen "kopiere 3x3x3"
Sign 'pattern'=Zeichen 'Vorlage'
Used to make a copy of a 3x3x3 cube.=Benötigt um eine Kopie eines 3x3x3 Quadrats zu machen.
Place the sign in front of the pattern to be copied.=Platziere das Zeichen vor die Vorlage, die kopiert werden soll.
Use the copy sign to make the copy of this pattern on a different location.=Benutze das Kopier-Zeichen, um eine Kopie dieser Vorlage an einer anderen Stelle zu machen.
The bot must first reach the pattern sign, then the copy sign.=Der Roboter muss zuerst das Vorlage-Zeichen und dann das Kopier-Zeichen erreichen.
Sign 'copy3x3x3'=Zeichen 'kopiere 3x3x3'
Place the sign in front of the location, where the copy should be made.=Platziere das Zeichen vor die Stelle, wo die Kopie hergestellt werden soll.
Use the pattern sign to mark the pattern.=Benutze das Vorlage-Zeichen und die Vorlage zu markieren.
### cmd_place.lua ###
Place a block in front of the robot@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=Setze einen Block vor den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--8).@nFür <lvl> ist zulässig: -1 0 +1
Place a block on the left side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=Setze einen Block links vorne.@n<slot> ist die Position im@neigenen Inventar (1--8).@nFür <lvl> ist zulässig: -1 0 +1
Place a block on the right side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=Setze einen Block rechts vorne.@n<slot> ist die Position im@neigenen Inventar (1--8).@nFür <lvl> ist zulässig: -1 0 +1
Place a block under the robot.@nHint: use 'move_up' first.@n<slot> is the inventory slot (1..8)=Setze einen Block unter den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--8).
Place a block above the robot.@n<slot> is the inventory slot (1..8)=Setze einen Block über den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--8).
Error: No free inventory space=Fehler: Kein freier Inventarplatz
Dig the block in front of the robot@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=Entferne einen Block vor den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--8).@nFür <lvl> ist zulässig: -1 0 +1
Dig the block on the left side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=Entferne einen links vorne.@n<slot> ist die Position im@neigenen Inventar (1--8).@nFür <lvl> ist zulässig: -1 0 +1
Dig the block on the right side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=Entferne einen rechts vorne.@n<slot> ist die Position im@neigenen Inventar (1--8).@nFür <lvl> ist zulässig: -1 0 +1
Dig the block under the robot.@n<slot> is the inventory slot (1..8)=Entferne einen Block unter dem@nRoboter.@n<slot> ist die Position im@neigenen Inventar (1--8).
Dig the block above the robot.@n<slot> is the inventory slot (1..8)=Entferne einen Block über dem@nRoboter.@n<slot> ist die Position im@neigenen Inventar (1--8).
Rotate the block in front of the robot@n<lvl> is one of: -1 0 +1@n<steps> is one of: 1 2 3=Rotiere den Block vor dem Roboter.@nFür <lvl> ist zulässig: -1 0 +1@nFür <steps> ist zulässig: 1 2 3
Bot torch=Bot Fackel
### cmd_place.lua ###
### cmd_soup.lua ###
Error: Position protected=Fehler: Position geschützt
### cmd_sign.lua ###
Commands,Help=Kommandos,Hilfe
Sign name:=Zeichenname:
Cancel=Abbruch
Check=Prüfen
Help=Hilfe
Copy Cmnd=Kopiere Kommando
please check the added line(s)=bitte prüfe die neue(n) Zeile(n)
Sign "command"=Zeichen "Kommando"
-- enter or copy commands from help page=-- Kommandos eingeben oder von der Hilfeseite kopieren
Error: Signs inventory empty=Fehler: Zeichen Inventar ist leer
Error: Position protected or occupied=Fehler: Position ist geschützt oder belegt
Place a sign in front of the robot@ntaken from the signs inventory@n<slot> is the inventory slot (1..6)=Setze ein Zeichen vor den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--6).
Place a sign behind the robot@ntaken from the signs inventory@n<slot> is the inventory slot (1..6)=Setze ein Zeichen hinter den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--6).
Error: No sign available=Fehler: Kein Zeichen verfügar
Error: Signs inventory slot is occupied=Fehler: Die Zeicheninventar Position ist belegt
Error: Position is protected=Fehler: Die Position ist geschützt
Dig the sign in front of the robot@nand add it to the signs inventory.@n<slot> is the inventory slot (1..6)=Entferne das Zeichen vor den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--6).
Dig the sign in front of the robot@nand add the cleared sign to@nthe item iventory.@n<slot> is the inventory slot (1..8)=Entferne das Zeichen vor den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--6).
Sign 'command'=Zeichen 'Kommando'
The 'command' sign can be programmed by the player.=Das 'Kommando' Zeichen kann vom Spieler programmiert werden.
Place the sign in front of you and use the node menu to program your sequence of bot commands.=Platziere das Zeichen vor dir und nutze das Zeichen-Menü, um die Kommando-Sequenz zu programmieren.
The menu has an edit field for your commands and a help page with all available commands.=Das Menü hat ein Eingabefeld für deine Kommandos und eine Hilfeseite zu allen Kommandos.
### cmd_soup.lua ###
Take water into empty bucket when standing on a shore@n(use specified slot number or 0 for auto selection)=Schöpfe Wasser mit einen leeren Eimer.@nDer Bot muss dazu am Wasser stehen. (verwende die Slot-Nummer@ndes Eimers oder 0 für eine automatische Auswahl)
Error: No still water around=Fehler: Kein stilles Wasser in der Nähe
Error: No empty bucket in inventory=Fehler: Kein leerer Eimer im Inventar
Error: No empty bucket in inventory slot =Fehler: Kein leerer Eimer in der Inventar Position
Error: No inventory space for full bucket=Fehler: Kein freier Inventarplatz für den vollen Eimer
Pour water from bucket to empty cauldron in front of a robot@n(use specified slot number or 0 for auto selection)=Gieße das Wasser aus dem Eimer in einen leeren Kessel (xdecor) vor dem Roboter@n(verwende die Slot-Nummer des Eimers oder 0 für eine automatische Auswahl)
Error: No empty cauldron in front of a robot=Fehler: Kein leerer Kessel vor dem Roboter
Error: No full bucket in inventory=Fehler: Kein voller Eimer im Inventar
Error: No full bucket in inventory slot =Fehler: Kein voller Eimer an der Inventarposition
Error: No inventory space for empty bucket=Fehler: Kein Platz im Inventar für den leeren Eimer
Set fire under cauldron (requires flammable material)@n(command is ignored when fire is already burning)=Mache Feuer unter dem Kessel (benötigt brennbares Material)@n(Befehl wird ignoriert, wenn Feuer bereits brennt)
Error: No cauldron in front of a robot=Fehler: Kein Kessel vor dem Roboter
Error: No space for fire under cauldron=Fehler: Kein Platz für das Feuer unter dem Kessel
Error: No flammable material under cauldron=Fehler: Kein brennbares Material unter dem Kessel
Put out (extinguish) fire under cauldron@n(command is ignored when there is no fire)=Das Feuer löschen unter dem Kessel@n(Befehl wird ignoriert, wenn kein Feuer vorhanden ist)
Take boiling soup into empty bowl from cauldron@nin front of a robot@n(use specified slot number or 0 for auto selection)=Die kochende Suppe aus im Kessel in eine leere Schüssel tun.@n(verwende die Slot-Nummer der Schüssel oder 0 für eine automatische Auswahl)
Error: No cauldron with a soup in front of a robot=Fehler: Kein Kessel mit Suppe vor dem Roboter
Error: No empty bowl in inventory=Fehler: Keine leere Schüssel im Inventar
Error: No empty bowl in inventory slot =Fehler: Keine leere Schüssel in der angegebenen Inventarposition
Error: No inventory space for full bowl=Fehler: Kein Platz im Inventar für die Schüssel
Sign "take water"=Zeichen "Schöpfe Wasser"
Sign "cook soup"=Zeichen "Koche Suppe"
Sign 'take water'=Zeichen 'Schöpfe Wasser'
Used to take water into bucket.=Wird verwendet, um Wasser in den Eimer zu schöpfen.
Place the sign on a shore, in front of the still water pool.=Platziere das Schild am Ufer des Wassers
Items in slots:=Gegenstände im Inventar:
1 - empty bucket= 1 - leerer Eimer
The result is one bucket with water in selected inventory slot.=Das Ergebnis ist ein Eimer mit Wasser an der ausgewählten Inventarposition.
When finished, the bot turns around.=Wenn er fertig ist, dreht sich der Bot um.
Sign 'cook soup'=Zeichen 'Koche Suppe'
Used to cook a vegetable soup in cauldron.=Wird verwendet, um eine vegetarische Suppe im Kessel zu kochen.
Cauldon should be empty and located above flammable material.=Der Kessel sollte leer sein und sich über brennbarem Material befinden.
Place the sign in front of the cauldron with one field space,=Platziere das Schild vor dem Kessel mit einem Feld Abstand,
to prevent wooden sign from catching fire.=um zu verhindern, dass das Holzschild Feuer fängt.
1 - water bucket= 2 - Wassereimer
2 - vegetable #1 (i.e. tomato)= 2 - Gemüse #1 (bspw. Tomate)
3 - vegetable #2 (i.e. carrot)= 3 - Gemüse #2 (bspw. Zwiebel)
4 - empty bowl (from farming or xdecor mods)= 4 - Leere Schüssel (Von der Mod farming oder xdecor)
The result is one bowl with vegetable soup in selected inventory slot.=Das Ergebnis ist eine Schüssel mit Gemüsesuppe an der ausgewählten Inventarposition.
### cmd_trees.lua ###
Used to harvest an aspen or pine tree trunk=Wird verwendet, um einen Espen- oder Kiefernstamm zu ernten
- Place the sign in front of the tree.=- Setze das Zeichen vor den Baum.
- Place a chest to the right of the sign.=- Setze eine Kiste rechts neben das Zeichen.
- Put a dirt stack (10 items min.) into the chest.=- Lege einen Stapel (>10) Erde (dirt) in die Kiste.
- Preconfigure slot 1 of the bot inventory with dirt=Pos. 1 des Bot-Inventars mit Erde (dirt) vorkonfigurieren
- Preconfigure slot 2 of the bot inventory with saplings=Pos. 2 des Bot-Inventars mit einem Setzling vorkonfigurieren
Sign "aspen"=Zeichen "Espe"
Sign 'aspen'=Zeichen 'Espe'
### commands.lua ###
commands:=Kommandos:
unknown command=unbekanntes Kommando
start of a 'repeat..end' block=Anfang eines 'repeat..end' Blocks
end command of a 'repeat..end' block=Ende Kommando eines 'repeat..end' Blocks
call a subroutine (with 'return' statement)=Aufruf einer Unterfunktion (mit 'return' Anweisung)
return from a subroutine=Rückkehr von einer Unterfunktion
jump to a label=Sprung zu einer Marke
Move the robot 1..999 steps forward@nwithout paying attention to any signs.@nUp and down movements also become@ncounted as steps.=Bewege den Roboter 1..999 Schritte@nvorwärts ohne auf Zeichen zu achten.@nAuf- und Ab-Bewegungen werden auch@nals Schritte gezählt.
Walk until a sign or obstacle is@nreached. Then continue with the next command.@nWhen a sign has been reached,@nthe current program is ended@nand the bot executes the@nnew program from the sign=Gehe bis ein Zeichen oder Hindernis@nerreicht wurde. Führe dann das nächste@nKommando aus. @nWurde ein Zeichen erreicht, so arbeite@ndie Kommandos des Zeichens als@nUnter-Prozess ab
Print given text as chat message.@nFor two or more words, use the '*' character@ninstead of spaces, like "Hello*world"=Gebe den angegebenen Text als Chat-Nachricht aus.@nFür zwei oder mehr Wörter verwende das Zeichen '*' @nanstelle von Leerzeichen, z. B. "Hallo*Welt".
### compost.lua ###
Put 2 leaves into the compost barrel@n<slot> is the bot inventory slot (1..8)@nwith the leaves.=Lege 2 Blätter in den Kompostbehälter@n<slot> ist die Position im@nBot Inventar (1..8)@nmit den Blättern.
Take a compost item from the barrel.@n<slot> (1..8 or 0 for the first free slot) is the bot@nslot for the compost item.=Nimm einen Kompostblock aus dem@nKompostbehälter. <slot> ist die Position im@nBot Inventar für den Block.@nWerte für <slot>: 0..8, oder 0 für die erste@nfreie Inventarposition.
### crop_sensor.lua ###
Crop Sensor: Connected with=Ernte Sensor: Verbunden mit
Crop Sensor=Ernte Sensor
Crop Sensor: Not connected=Ernte Sensor: Nicht verbunden
The Crop Sensor sends cyclical signals when, for example, wheat is fully grown.=Der Ernte Sensor sendet zyklisch ein Signal, wenn bspw. der Weizen voll ausgewachsen ist.
The sensor range is one node/meter.=Der Sensorbereich beträgt einen Block/Meter.
The sensor has an active side (red) that must point to the crop/field.=Der Sensor hat eine aktive Seite (rot), welche zur der Pflanze zeigen muss.
### delayer.lua ###
Signal Delayer: Connected with=Signal Verzögerer: Verbunden mit
Delay time [sec]:=Verzögerungszeit [s]:
Signal Delayer=Signal Verzögerer
Signals are forwarded delayed. Subsequent signals are queued.=Signale werden verzögert weitergeleitet. Nachfolgende Signale werden in die Warteschlange gestellt.
The delay time can be configured.=Die Verzögerungszeit kann eingestellt werden.
### delayer.lua ###
### timer.lua ###
Start=Start
### doc.lua ###
After you have placed the Signs Bot Box, you can start the bot by means of the 'On' button in the box menu.=Nachdem du die Roboter-Kiste platziert hast, kannst du den Roboter über den "An" Button im Kistenmenü starten.
If the bot returns to its box right away, you will likely need to charge it with electrical energy (techage) first.=Wenn der Bot sofort in seine Box zurückkehrt, musst du ihn wahrscheinlich zuerst mit elektrischer Energie (Techage) aufladen.
The bot then runs straight up until it reaches an obstacle (a step with two or more blocks up or down or a sign.)=Der Roboter läuft dann geradeaus, bis er ein Hindernis erreicht (eine Stufe mit zwei oder mehr Blöcken, auf oder ab).
If the bot first reaches a sign it will execute the commands on the sign.=Falls der Roboter zuerst ein Zeichen erreicht, wird er die Kommandos auf dem Zeichen ausführen.
If the command(s) on the sign is e.g. 'turn_around', the bot turns and goes back.=Falls das Kommandos auf dem Zeichen bspw. ein 'turn_around' ist, dreht der Roboter um und geht zurück.
In this case, the bot reaches his box again and turns off.=In diesem Fall erreicht der Roboter wieder seine Box und schaltet sich ab.
The Signs Bot Box has an inventory with 6 stacks for signs and 8 stacks for other items (to be placed/dug by the bot).=Die Roboter Box hat ein Inventar für 6 Stapel Zeichen und 8 Stapel für andere Gegenstände (welche platziert oder eingesammelt werden können).
This inventory simulates the bot internal inventory.=Das Inventar der Box simuliert das Roboter Inventar.
That means you will only have access to the inventory if the bot is turned off ('sitting' in his box).=Das bedeutet, du hast nur Zugriff auf das Inventar, sofern der Roboter ausgeschaltet ist.
You simply control the direction of the bot by means of the 'turn left' and 'turn right' signs (signs with the arrow).=Du kannst die Richtung des Roboters ganz einfach über 'turn_left' (drehe nach links) oder 'turn_right' (drehe nach rechts) Zeichen verändern.
The bot can run over steps (one block up/down). But there are also commands to move the bot up and down.=Der Roboter kann auch Stufen überwinden (eine Block hoch oder runter). Aber es gibt auch Kommandos, um den Roboter nach oben oder unten zu bewegen.
It is not necessary to mark a way back to the box.=Es ist nicht notwendig, dem Roboter einen Weg zurück zu seiner Box zu markieren.
With the command 'turn_off' the bot will turn off and be back in his box from every position.=Mit dem Kommando "turn_off" wird der Roboter ausgeschaltet und befindet sich danach wieder in seiner Box, egal wo er war.
The same applies if you turn off the bot by the box menu.=Das gleiche gilt, wenn du den Roboter über den 'Aus' Button ausschaltest.
If the bot reaches a sign from the wrong direction (from back or sides) the sign will be ignored.=Wenn der Roboter ein Zeichen von der falschen Richtung erreicht (von der Seite oder von hinten), wird das Zeichen ignoriert.
The bot will walk over.=Der Roboter läuft einfach hinüber.
All predefined signs have a menu with a list of the bot commands.=Alle vordefinierten Zeichen haben ein Menü mit der Liste der Kommandos.
These signs can't be changed, but you can craft and program your own signs.=Diese Zeichen können nicht geändert werden, aber du kannst eigene Zeichen herstellen und programmieren.
For this you have to use the 'command' sign.=Dafür kannst du das 'command' Zeichen verwenden.
This sign has an edit field for your commands and a help page with all available commands.=Das Zeichen hat ein Eingabefeld für deine Kommandos und eine Hilfeseite zu allen Kommandos.
Also for your own signs it is important to know:=Es ist auch für eigene Zeichen wichtig zu wissen:
After the execution of the last command of the sign, the bot falls back into its default behaviour and runs in its taken direction.=Nach der Ausführung des letzten Kommandos eines Zeichens fällt der Roboter immer zurück in sein Standardverhalten und läuft in die eingeschlagene Richtung weiter.
A standard job for the bot is to move items from one chest to another=Eine Standardaufgabe für den Roboter ist, Gegenstände von einer Kiste in eine andere zu tun
(chest or node with a chest like inventory).=(Kiste oder Block mit Kisten-ähnlichem Inventar).
This can be done by means of the two signs 'take item' and 'add item'.=Das kann mit Hilfe von zwei Zeichen realisiert werden: 'take_item' und 'add_item'.
These signs have to be placed on top of chest nodes.=Diese Zeichen müssen auf den Kisten platziert werden.
In addition to the signs the bot can be controlled by means of sensors.=Zusätzlich zu den Zeichen kann der Roboter auch über Sensoren gesteuert werden.
Sensors like the Bot Sensor have two states: on and off.=Sensoren wie der Roboter-Sensor haben zwei Zustande: on' (an) und 'off' (aus).
If the Bot Sensor detects a bot it will switch to the state 'on' and=Wenn der Roboter-Sensor einen Roboter erkennt geht er in den Zustand 'on' und
sends a signal to a connected block, called an actuator.=sendet ein Signal zu einem verbundenen Block, einem Aktor.
Sensors are:=Sensoren sind:
- Bot Sensor: Sends a signal when the robot passes by=- Roboter Sensor: Sendet ein Signal wenn ein Roboter vorbei geht
- Node Sensor: Sends a signal when it detects any node=- Block Sensor: Sendet ein Signal bei einer Veränderung von Blocken vor sich
- Crop Sensor: Sends a signal when, for example wheat is fully grown=- Ernte Sensor: Sendet ein Signal, wenn bspw. der Weizen voll ausgewachsen ist
- Bot Chest: Sends a signal depending on the chest state (empty, full)=- Roboter-Kiste: Sendet ein Signal abhängig vom Zustand der Kiste (voll, leer)
Actuators are:=Aktoren sind:
- Signs Bot Box: Can turn the bot off and on=- Roboter-Box: Kann den Roboter ein- und ausschalten
- Control Unit: Can be used to exchange the sign to lead the bot=- Steuerungseinheit: Wird genutzt um Zeichen auszutauschen und damit den Roboter zu steuern
Additional sensors and actuator can be added by other mods.=Weitere Sensoren und Aktoren können von weiteren Mods hinzugefügt werden.
To send a signal from a sensor to an actuator, the sensor has to be connected (paired) with actuator.=Um ein Signal von einem Sensor zu einem Aktor senden zu können, muss der Sensor mit dem Aktor verbunden werden (Pairing).
To connect sensor and actuator, the Sensor Connection Tool has to be used.=Das Sensor Verbinde-Werkzeug wir genutzt, um Sensoren mit Aktoren zu verbinden.
Simply click with the tool on both blocks and the sensor will be connected with the actuator.=Klicke einfach mit dem Werkzeug nacheinander auf beide Blöcke und der Sensor wird mit dem Aktor verbunden.
A successful connection is indicated by a ping/pong noise.=Eine erfolgreiche Verbindung wird über ein Ping/Pong-Geräusch angezeigt.
Before you connect sensor with actuator, take care that the actuator is in the requested state.=Bevor du einen Sensor mit einem Aktor verbindest, achte darauf, dass sich der Aktor im richtigen Zustand befindet.
For example: If you want to start the Bot with a sensor, connect the sensor with the Bot Box,=Zum Beispiel: Wenn du den Roboter über einen Sensor starten willst, verbinde Sensor und Roboter-Kiste nur,
when the Bot is in the state 'on'. Otherwise the sensor signal will stop the Bot,=wenn der Roboter an ist (im Zustand 'on'). Anderenfalls würde der Sensor den Roboter ausschalten,
instead of starting it.=anstatt ihn zu starten.
The following applies to all commands that are used to place items in the bot inventory, like:=Das folgende gilt für alle Kommandos, um Items in das Roboter Inventar zu legen, wie:
- take_item <num> <slot>=- take_item <num> <slot>
- pickup_items <slot>=- pickup_items <slot>
- trash_sign <slot>=- trash_sign <slot>
- harvest <slot>=- harvest <slot>
- dig_front <slot> <lvl>=- dig_front <slot> <lvl>
- dig_left <slot> <lvl>=- dig_left <slot> <lvl>
- dig_right <slot> <lvl>=- dig_right <slot> <lvl>
- dig_below <slot> <lvl>=- dig_below <slot> <lvl>
- dig_above <slot> <lvl>=- dig_above <slot> <lvl>
If no slot or slot 0 was specified with the command (case A), all 8 slots of the bot inventory =Wurde beim Kommando kein Slot oder Slot 0 angegeben (Fall A), werden nacheinander alle
are checked one after the other. If a slot was specified (case B), only this slot is checked.=8 Slots des Bot Inventars geprüft, Wurde ein Slot angegeben (Fall B), wird nur dieser geprüft.
In both cases the following applies: If the slot is preconfigured and fits the item, =In beiden Fällen gilt: Ist der Slot vorkonfiguriert und passt das Item dazu, oder ist der Slot
or if the slot is not configured and empty, or is only partially filled with the item type =nicht konfiguriert und leer, oder mit dem Item-Typ (das hinzu gefügt werden soll) nur
(which should be added), then the items are added.=teilweise gefüllt, dann werden die Items hinzugefügt.
If not all items can be added, the remaining slots will be tried out in case A.=Können nicht alle Items hinzugefügt werden, werden im Falle A die restlichen Slots weiter durchprobiert.
Anything that could not be added to your own inventory goes back.=Was nicht dem eigenen Inventar hinzugefügt werden konnte, geht zurück.
The following applies to all commands that are used to take items from the bot inventory, like:=Das folgende gilt für alle Kommandos, um Items aus dem Roboter Inventar zu nehmen, wie:
- add_item <num> <slot>=- add_item <num> <slot>
It doesn't matter whether a slot is configured or not. The bot takes the first stack that =Hier ist es egal, ob ein Slot konfiguriert ist, oder nicht. Der Bot nimmt den ersten Stack, den
it can find from its own inventory and tries to use it.=er finden kann, aus dem eigenen Inventar und versucht diesen zu nutzen.
If a slot is specified, it only takes this, if no slot has been specified, it checks all of =Ist ein Slot angegeben, nimmt er nur diesen, wurde kein Slot angegeben, prüft er alle
them one after the other, starting from slot 1 until it finds something.=nacheinander, von Slot 1 beginnend, bis er etwas findet. Ist die gefundene Anzahl kleiner als
If the number found is smaller than requested, he tries to take the rest out of any slot.=gefordert, versucht er den Rest aus irgend einem Slot zu nehmen.
Signs Bot=Signs Bot
A robot controlled by signs, used for automated work=Ein Roboter, gesteuert über Zeichen, für Automatisierungsaufgaben
Start the Bot=Starte den Roboter
Control the Bot=Steuere den Roboter
Sensors and Actuators=Sensoren und Aktoren
Connecting sensors and actuator=Verbinde Sensor mit Aktor
Bot inventory behavior=Verhalten beim Roboter Inventar
### doc.lua ###
### cmd_sign.lua ###
The help page has a copy button to simplify the programming.=Die Hilfeseite hat einen Kopier-Button um die Programmierung zu erleichtern.
### duplicator.lua ###
Template:=Vorlage:
Input:=Eingabe:
Output:=Ausgabe:
1. Place one 'cmnd' sign= 1. Kommando Zeichen einlegen
2. Add 'blank signs'=2. Füge 'leere Zeichen' hinzu
3. Take the copies=3. Entnehme die Kopien
Signs Duplicator=Zeichen Kopierer
Sign "user"=Zeichen "Benutzer"
Sign "blank"="Leeres" Zeichen
The Duplicator can be used to make copies of signs.=Der Zeichen Kopierer kann zur Herstellung von Kopien eines Zeichens genutzt werden.
1. Put one 'cmnd' sign to be used as template into the 'Template' inventory=1. Lege ein Kommando-Zeichen als Vorlage in das 'Vorlage' Inventar
2. Add one or several 'blank signs' to the 'Input' inventory.=2. Füge ein oder mehrere 'leere Zeichen' als Eingabe hinzu.
3. Take the copies from the 'Output' inventory.=3. Entnehme die Kopieren aus der Ausgabe.
Written books [default:book_written] can alternatively be used as template=Alternativ können auch beschriebene Bücher [default:book_written] als Vorlage verwendet werden
Already written signs can be used as input, too.=Bereits beschriebene Zeichen können auch als Eingabe-Zeichen genutzt werden.
Needed as input for the Duplicator.=Wird als Eingabe für den Zeichen Kopierer benötigt.
### extender.lua ###
Sensor Extender: Connected with=Sensor Erweiterung: Verbunden mit
Sensor Extender=Sensor Erweiterung
Sensor Extender: Not connected=Sensor Erweiterung: Nicht verbunden
With the Sensor Extender, sensor signals can be sent to more than one actuator.=Mit Hilfe der Sensor-Erweiterung können weitere Aktoren mit dem Sensor verbunden werden.
Place one or more extender nearby the sensor and connect each extender=Platziere ein oder mehrere Sensor-Erweiterungen neben einen Sensor und verbinde diese
with one further actuator by means of the Connection Tool.=mit weiteren Aktoren mit Hilfe des Verbinde-Werkzeuges.
### interpreter.lua ###
Maximum programm size exceeded=Maximale Programmlänge überschritten
Too many parameters=Zu viele Parameter
Parameter error=Parameter Fehler
Command error=Kommandozeilen Fehler
'repeat' missing=Es fehlt ein 'repeat'
'end' missing=Es fehlt ein 'end'
Checked and approved=Geprüft und genehmigt
### logic_and.lua ###
Signal AND with=Signal UND mit
inputs=Eingängen
Signal AND=Signal UND
Signal is sent, if all input signals are received.=Signal wird gesendet, wenn all Eingangssignale empfangen wurden.
### node_sensor.lua ###
Node Sensor: Connected with =Block Sensor: Verbunden mit
added=hinzukommen
removed=fehlen
added or removed=hinzukommen oder fehlen
Send signal if nodes have been:=Sende ein Signal wenn Blöcke:
accept=übernehmen
Node Sensor=Block Sensor
Node Sensor: Not connected=Block Sensor: Nicht verbunden
The node sensor sends cyclical signals when it detects that nodes have appeared or disappeared,=Der Block Sensor sendet zyklisch ein Signal, wenn er eine Veränderung von Blöcken vor sich entdeckt (ein Block erscheint oder verschwindet),
but has to be configured accordingly.=aber muss entsprechend konfiguriert werden.
Valid nodes are all kind of blocks and plants.=Gültig sind alle Arten von Blöcken oder Pflanzen.
The sensor range is 3 nodes/meters in one direction.=Die Sensor-Reichweite beträgt 3 Blöcke/Meter in eine Richtung.
The sensor has an active side (red) that must point to the observed area.=Der Sensor hat eine aktive Seite (rot) welche zu dem zu überwachenden Bereich zeigen muss.
### signs.lua ###
Instructions:=Anweisungen:
Code=Code
Sign "turn right"=Zeichen "Rechts drehen"
Sign "turn left"=Zeichen "Links drehen"
Sign "take item"=Zeichen "Nehme Gegenstand"
Sign "add item"=Zeichen "Lege Gegenstand"
Sign "stop"=Zeichen "Stopp"
Sign "add to cart"=Zeichen "Lege in den Wagen"
Sign "take from cart"=Zeichen "Nehme aus dem Wagen"
The Bot turns right when it detects this sign in front of it.=Der Roboter dreht nach rechts, wenn er dieses Zeichen vor sich hat.
The Bot turns left when it detects this sign in front of it.=Der Roboter dreht nach links, wenn er dieses Zeichen vor sich hat.
The Bot takes items out of a chest in front of it and then turns around.=Der Roboter nimmt Gegenstände aus einer Kiste vor sich und dreht dann um.
This sign has to be placed on top of the chest.=Das Zeichen muss auf der Kiste platziert werden.
The Bot puts items into a chest in front of it and then turns around.=Der Roboter legt Gegenstände in eine Kiste vor sich und dreht dann um.
The Bot will stop in front of this sign until the sign is removed or the bot is turned off.=Der Roboter stoppt vor diesem Zeichen, bis das Zeichen entfernt, oder der Roboter ausgeschaltet wird.
The Bot puts items into a minecart in front of it, pushes the cart and then turns around.=Der Roboter legt Gegenstände in den Wagen vor sich, startet den Wagen und dreht dann um.
This sign has to be placed on top of the rail at the cart end position.=Das Zeichen muss auf dem Gleis und damit über dem Wagen platziert werden.
The Bot takes items out of a minecart in front of it, pushes the cart and then turns around.=Der Roboter nimmt Gegenstände aus dem Wagen vor sich, startet den Wagen und dreht dann um.
### techage.lua ###
no power=kein Strom
Ignite the techage charcoal lighter=Zünde den Holzkohle-Anzünder an
Turns the bot off if the@nbattery power is below the@ngiven value in percent (1..99)=Schalte den Bot aus,@nwenn die Batterieladung kleiner@nist als der angegebene Wert@nin Prozent (1.99)
Jump to <label> if the@nbattery power is below the@ngiven value in percent (1..99)=Springe zu <label> wenn die@nBatterieladung unter dem@ngegebenen Wert in Prozent (1..99) liegt
fully charged=voll geladen
Sends a techage command@nto a given node.@nReceiver is addressed by@nthe techage node number.@nFor commands with two or more@nwords, use the '*' character@ninstead of spaces, e.g.:@nsend_cmnd 3465 pull*default:dirt*2=Sende ein techage Kommando@nan einen Block mit der@nangegebenen Blocknummer.@nFür Kommandos mit zwei oder mehr @nWörtern verwende das Zeichen '*' @nanstelle des Leerzeichens, wie bspw.:@nsend_cmnd 3465 pull*default:dirt*2
### timer.lua ###
Bot Timer=Roboter Timer
Cycle time [min]:=Zykluszeit [min]:
Bot Timer: Not connected=Roboter Timer: Nicht verbunden
Special kind of sensor.=Spezielle Form eines Sensors.
Can be programmed with a time in seconds, e.g. to start the bot cyclically.=Kann mit einer Zeit in Sekunden programmiert werden, um bspw. den Roboter zyklisch zu starten.
### timer.lua ###
### logic_and.lua ###
Connected with=Verbunden mit
### tool.lua ###
Sensor Connection Tool=Sensor Verbindungswerkzeug
@@ -0,0 +1,476 @@
# textdomain: signs_bot
### basis.lua ###
running=
stopped=
Off=
On=
Signs=
Other items=
Config=
Preassign slots items=
Back=
Robot Box=
Signs Bot Box=
The Box is the housing of the bot.=
Place the box and start the bot by means of the 'On' button.=
If the mod techage is installed, the bot needs electrical power.=
The bot leaves the box on the right side.=
It will not start, if this position is blocked.=
To stop and remove the bot, press the 'Off' button.=
The box inventory simulates the inventory of the bot.=
You will not be able to access the inventory, if the bot is running.=
The bot can carry up to 8 stacks and 6 signs with it.=
### basis.lua ###
### techage.lua ###
charging=
### bot_flap.lua ###
Exit=
Bot Flap=
The flap is a simple block used as door for the bot.=
Place the flap in any wall, and the bot will automatically open=
and close the flap as it passes through it.=
### bot_sensor.lua ###
Bot Sensor: Connected with=
Bot Sensor=
Bot Sensor: Not connected=
The Bot Sensor detects any bot and sends a signal, if a bot is nearby.=
The sensor direction does not care.=
### cart_sensor.lua ###
Cart Sensor: Connected with=
Cart Sensor=
Cart Sensor: Not connected=
The Cart Sensor detects and sends a signal, if a cart (Minecart) is nearby.=
The sensor has an active side (red) that must point to the rail/cart.=
### cart_sensor.lua ###
### bot_sensor.lua ###
the sensor range is one node/meter.=
### changer.lua ###
Signs:=
Bot Control Unit=
The Bot Control Unit is used to lead the bot by means of signs.=
The unit can be loaded with up to 4 different signs and can be programmed by means of sensors.=
To load the unit, place a sign on the red side of the unit and click on the unit.=
The sign disappears / is moved to the inventory of the unit.=
This can be repeated 3 times.=
Use the connection tool to connect up to 4 sensors with the Bot Control Unit.=
### chest.lua ###
Bot Chest: Sends signal to=
Signs Bot Chest=
Bot Chest: Not connected=
The Signs Bot Chest is a special chest with sensor function.=
It sends a signal depending on the chest state.=
Possible states are 'empty', 'not empty', 'almost full'=
A typical use case is to turn off the bot, when the chest is almost full or empty.=
### cmd_farming.lua ###
Sow farming seeds@nin front of the robot=
Harvest farming products@nin front of the robot@non a 3x3 field.=
Plant a sapling@nin front of the robot=
Sign "farming"=
Sign 'farming'=
Used to harvest and seed a 3x3 field.=
The seed to be placed has to be in the first inventory slot of the bot.=
### cmd_farming.lua ###
### cmd_flowers.lua ###
Place the sign in front of the field.=
When finished, the bot turns.=
### cmd_flowers.lua ###
Cutting flowers, papyrus,@nleaves and tree blocks@nin front of the robot@non a 3x3 field.=
Sign "flowers"=
Sign 'flowers'=
Used to cut flowers on a 3x3 field.=
### cmd_item.lua ###
Take <num> items from a chest like node@nand put it into the item inventory.@n<slot> is the bot inventory slot@n(1..8) or 0 for any one=
Check if there are <num>@nitems in the chest like node.@nIf not, jump to <label>@n<slot> is the bot inventory slot@n(1..8) to specify the item, or 0 for any item=
Add <num> items to a chest like node@ntaken from the item inventory.@n<slot> is the bot inventory slot (1..8) or 0 for any one=
Add <num> fuel to a furnace like node@ntaken from the item inventory.@n<slot> is the bot inventory slot (1..8) or 0 for any one=
deprecated, use bot inventory configuration instead=
Pick up all objects@nin a 3x3 field.@n<slot> is the bot inventory slot (1..8) or 0 for any one=
Drop items in front of the bot.@n<slot> is the bot inventory slot (1..8) or 0 for any one=
Punch a rail cart to start it=
### cmd_move.lua ###
Move the robot one step back=
Turn the robot to the left=
Turn the robot to the right=
Turn the robot around=
Move the robot upwards=
Move the robot down=
Fall into a hole/chasm (up to 10 blocks)=
Stop the robot for <sec> seconds@n(1..9999)=
Stop the robot.=
Turn the robot off@nand put it back in the box.=
### cmd_pattern.lua ###
Store pattern to be cloned.=
Copy the nodes from@nthe stored pattern position@n<size> is: 3x1, 3x2, 3x3,@n5x1, 5x2, 5x3 (wide x deep)@n<lvl> pattern level offset (0..4)=
Sign "pattern"=
Sign "copy 3x3x3"=
Sign 'pattern'=
Used to make a copy of a 3x3x3 cube.=
Place the sign in front of the pattern to be copied.=
Use the copy sign to make the copy of this pattern on a different location.=
The bot must first reach the pattern sign, then the copy sign.=
Sign 'copy3x3x3'=
Place the sign in front of the location, where the copy should be made.=
Use the pattern sign to mark the pattern.=
### cmd_place.lua ###
Place a block in front of the robot@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=
Place a block on the left side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=
Place a block on the right side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=
Place a block under the robot.@nHint: use 'move_up' first.@n<slot> is the inventory slot (1..8)=
Place a block above the robot.@n<slot> is the inventory slot (1..8)=
Error: No free inventory space=
Dig the block in front of the robot@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=
Dig the block on the left side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=
Dig the block on the right side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=
Dig the block under the robot.@n<slot> is the inventory slot (1..8)=
Dig the block above the robot.@n<slot> is the inventory slot (1..8)=
Rotate the block in front of the robot@n<lvl> is one of: -1 0 +1@n<steps> is one of: 1 2 3=
Bot torch=
### cmd_place.lua ###
### cmd_soup.lua ###
Error: Position protected=
### cmd_sign.lua ###
Commands,Help=
Sign name:=
Cancel=
Check=
Help=
Copy Cmnd=
please check the added line(s)=
Sign "command"=
-- enter or copy commands from help page=
Error: Signs inventory empty=
Error: Position protected or occupied=
Place a sign in front of the robot@ntaken from the signs inventory@n<slot> is the inventory slot (1..6)=
Place a sign behind the robot@ntaken from the signs inventory@n<slot> is the inventory slot (1..6)=
Error: No sign available=
Error: Signs inventory slot is occupied=
Error: Position is protected=
Dig the sign in front of the robot@nand add it to the signs inventory.@n<slot> is the inventory slot (1..6)=
Dig the sign in front of the robot@nand add the cleared sign to@nthe item iventory.@n<slot> is the inventory slot (1..8)=
Sign 'command'=
The 'command' sign can be programmed by the player.=
Place the sign in front of you and use the node menu to program your sequence of bot commands.=
The menu has an edit field for your commands and a help page with all available commands.=
### cmd_soup.lua ###
Take water into empty bucket when standing on a shore@n(use specified slot number or 0 for auto selection)=
Error: No still water around=
Error: No empty bucket in inventory=
Error: No empty bucket in inventory slot =
Error: No inventory space for full bucket=
Pour water from bucket to empty cauldron in front of a robot@n(use specified slot number or 0 for auto selection)=
Error: No empty cauldron in front of a robot=
Error: No full bucket in inventory=
Error: No full bucket in inventory slot =
Error: No inventory space for empty bucket=
Set fire under cauldron (requires flammable material)@n(command is ignored when fire is already burning)=
Error: No cauldron in front of a robot=
Error: No space for fire under cauldron=
Error: No flammable material under cauldron=
Put out (extinguish) fire under cauldron@n(command is ignored when there is no fire)=
Take boiling soup into empty bowl from cauldron@nin front of a robot@n(use specified slot number or 0 for auto selection)=
Error: No cauldron with a soup in front of a robot=
Error: No empty bowl in inventory=
Error: No empty bowl in inventory slot =
Error: No inventory space for full bowl=
Sign "take water"=
Sign "cook soup"=
Sign 'take water'=
Used to take water into bucket.=
Place the sign on a shore, in front of the still water pool.=
Items in slots:=
1 - empty bucket=
The result is one bucket with water in selected inventory slot.=
When finished, the bot turns around.=
Sign 'cook soup'=
Used to cook a vegetable soup in cauldron.=
Cauldon should be empty and located above flammable material.=
Place the sign in front of the cauldron with one field space,=
to prevent wooden sign from catching fire.=
1 - water bucket=
2 - vegetable #1 (i.e. tomato)=
3 - vegetable #2 (i.e. carrot)=
4 - empty bowl (from farming or xdecor mods)=
The result is one bowl with vegetable soup in selected inventory slot.=
### cmd_trees.lua ###
Used to harvest an aspen or pine tree trunk=
- Place the sign in front of the tree.=
- Place a chest to the right of the sign.=
- Put a dirt stack (10 items min.) into the chest.=
- Preconfigure slot 1 of the bot inventory with dirt=
- Preconfigure slot 2 of the bot inventory with saplings=
Sign "aspen"=
Sign 'aspen'=
### commands.lua ###
commands:=
unknown command=
start of a 'repeat..end' block=
end command of a 'repeat..end' block=
call a subroutine (with 'return' statement)=
return from a subroutine=
jump to a label=
Move the robot 1..999 steps forward@nwithout paying attention to any signs.@nUp and down movements also become@ncounted as steps.=
Walk until a sign or obstacle is@nreached. Then continue with the next command.@nWhen a sign has been reached,@nthe current program is ended@nand the bot executes the@nnew program from the sign=
Print given text as chat message.@nFor two or more words, use the '*' character@ninstead of spaces, like "Hello*world"=
### compost.lua ###
Put 2 leaves into the compost barrel@n<slot> is the bot inventory slot (1..8)@nwith the leaves.=
Take a compost item from the barrel.@n<slot> (1..8 or 0 for the first free slot) is the bot@nslot for the compost item.=
### crop_sensor.lua ###
Crop Sensor: Connected with=
Crop Sensor=
Crop Sensor: Not connected=
The Crop Sensor sends cyclical signals when, for example, wheat is fully grown.=
The sensor range is one node/meter.=
The sensor has an active side (red) that must point to the crop/field.=
### delayer.lua ###
Signal Delayer: Connected with=
Delay time [sec]:=
Signal Delayer=
Signals are forwarded delayed. Subsequent signals are queued.=
The delay time can be configured.=
### delayer.lua ###
### timer.lua ###
Start=
### doc.lua ###
After you have placed the Signs Bot Box, you can start the bot by means of the 'On' button in the box menu.=
If the bot returns to its box right away, you will likely need to charge it with electrical energy (techage) first.=
The bot then runs straight up until it reaches an obstacle (a step with two or more blocks up or down or a sign.)=
If the bot first reaches a sign it will execute the commands on the sign.=
If the command(s) on the sign is e.g. 'turn_around', the bot turns and goes back.=
In this case, the bot reaches his box again and turns off.=
The Signs Bot Box has an inventory with 6 stacks for signs and 8 stacks for other items (to be placed/dug by the bot).=
This inventory simulates the bot internal inventory.=
That means you will only have access to the inventory if the bot is turned off ('sitting' in his box).=
You simply control the direction of the bot by means of the 'turn left' and 'turn right' signs (signs with the arrow).=
The bot can run over steps (one block up/down). But there are also commands to move the bot up and down.=
It is not necessary to mark a way back to the box.=
With the command 'turn_off' the bot will turn off and be back in his box from every position.=
The same applies if you turn off the bot by the box menu.=
If the bot reaches a sign from the wrong direction (from back or sides) the sign will be ignored.=
The bot will walk over.=
All predefined signs have a menu with a list of the bot commands.=
These signs can't be changed, but you can craft and program your own signs.=
For this you have to use the 'command' sign.=
This sign has an edit field for your commands and a help page with all available commands.=
Also for your own signs it is important to know:=
After the execution of the last command of the sign, the bot falls back into its default behaviour and runs in its taken direction.=
A standard job for the bot is to move items from one chest to another=
(chest or node with a chest like inventory).=
This can be done by means of the two signs 'take item' and 'add item'.=
These signs have to be placed on top of chest nodes.=
In addition to the signs the bot can be controlled by means of sensors.=
Sensors like the Bot Sensor have two states: on and off.=
If the Bot Sensor detects a bot it will switch to the state 'on' and=
sends a signal to a connected block, called an actuator.=
Sensors are:=
- Bot Sensor: Sends a signal when the robot passes by=
- Node Sensor: Sends a signal when it detects any node=
- Crop Sensor: Sends a signal when, for example wheat is fully grown=
- Bot Chest: Sends a signal depending on the chest state (empty, full)=
Actuators are:=
- Signs Bot Box: Can turn the bot off and on=
- Control Unit: Can be used to exchange the sign to lead the bot=
Additional sensors and actuator can be added by other mods.=
To send a signal from a sensor to an actuator, the sensor has to be connected (paired) with actuator.=
To connect sensor and actuator, the Sensor Connection Tool has to be used.=
Simply click with the tool on both blocks and the sensor will be connected with the actuator.=
A successful connection is indicated by a ping/pong noise.=
Before you connect sensor with actuator, take care that the actuator is in the requested state.=
For example: If you want to start the Bot with a sensor, connect the sensor with the Bot Box,=
when the Bot is in the state 'on'. Otherwise the sensor signal will stop the Bot,=
instead of starting it.=
The following applies to all commands that are used to place items in the bot inventory, like:=
- take_item <num> <slot>=
- pickup_items <slot>=
- trash_sign <slot>=
- harvest <slot>=
- dig_front <slot> <lvl>=
- dig_left <slot> <lvl>=
- dig_right <slot> <lvl>=
- dig_below <slot> <lvl>=
- dig_above <slot> <lvl>=
If no slot or slot 0 was specified with the command (case A), all 8 slots of the bot inventory =
are checked one after the other. If a slot was specified (case B), only this slot is checked.=
In both cases the following applies: If the slot is preconfigured and fits the item, =
or if the slot is not configured and empty, or is only partially filled with the item type =
(which should be added), then the items are added.=
If not all items can be added, the remaining slots will be tried out in case A.=
Anything that could not be added to your own inventory goes back.=
The following applies to all commands that are used to take items from the bot inventory, like:=
- add_item <num> <slot>=
It doesn't matter whether a slot is configured or not. The bot takes the first stack that =
it can find from its own inventory and tries to use it.=
If a slot is specified, it only takes this, if no slot has been specified, it checks all of =
them one after the other, starting from slot 1 until it finds something.=
If the number found is smaller than requested, he tries to take the rest out of any slot.=
Signs Bot=
A robot controlled by signs, used for automated work=
Start the Bot=
Control the Bot=
Sensors and Actuators=
Connecting sensors and actuator=
Bot inventory behavior=
### doc.lua ###
### cmd_sign.lua ###
The help page has a copy button to simplify the programming.=
### duplicator.lua ###
Template:=
Input:=
Output:=
1. Place one 'cmnd' sign=
2. Add 'blank signs'=
3. Take the copies=
Signs Duplicator=
Sign "user"=
Sign "blank"=
The Duplicator can be used to make copies of signs.=
1. Put one 'cmnd' sign to be used as template into the 'Template' inventory=
2. Add one or several 'blank signs' to the 'Input' inventory.=
3. Take the copies from the 'Output' inventory.=
Written books [default:book_written] can alternatively be used as template=
Already written signs can be used as input, too.=
Needed as input for the Duplicator.=
### extender.lua ###
Sensor Extender: Connected with=
Sensor Extender=
Sensor Extender: Not connected=
With the Sensor Extender, sensor signals can be sent to more than one actuator.=
Place one or more extender nearby the sensor and connect each extender=
with one further actuator by means of the Connection Tool.=
### interpreter.lua ###
Maximum programm size exceeded=
Too many parameters=
Parameter error=
Command error=
'repeat' missing=
'end' missing=
Checked and approved=
### logic_and.lua ###
Signal AND with=
inputs=
Signal AND=
Signal is sent, if all input signals are received.=
### node_sensor.lua ###
Node Sensor: Connected with =
added=
removed=
added or removed=
Send signal if nodes have been:=
accept=
Node Sensor=
Node Sensor: Not connected=
The node sensor sends cyclical signals when it detects that nodes have appeared or disappeared,=
but has to be configured accordingly.=
Valid nodes are all kind of blocks and plants.=
The sensor range is 3 nodes/meters in one direction.=
The sensor has an active side (red) that must point to the observed area.=
### signs.lua ###
Instructions:=
Code=
Sign "turn right"=
Sign "turn left"=
Sign "take item"=
Sign "add item"=
Sign "stop"=
Sign "add to cart"=
Sign "take from cart"=
The Bot turns right when it detects this sign in front of it.=
The Bot turns left when it detects this sign in front of it.=
The Bot takes items out of a chest in front of it and then turns around.=
This sign has to be placed on top of the chest.=
The Bot puts items into a chest in front of it and then turns around.=
The Bot will stop in front of this sign until the sign is removed or the bot is turned off.=
The Bot puts items into a minecart in front of it, pushes the cart and then turns around.=
This sign has to be placed on top of the rail at the cart end position.=
The Bot takes items out of a minecart in front of it, pushes the cart and then turns around.=
### techage.lua ###
no power=
Ignite the techage charcoal lighter=
Turns the bot off if the@nbattery power is below the@ngiven value in percent (1..99)=
Jump to <label> if the@nbattery power is below the@ngiven value in percent (1..99)=
fully charged=
Sends a techage command@nto a given node.@nReceiver is addressed by@nthe techage node number.@nFor commands with two or more@nwords, use the '*' character@ninstead of spaces, e.g.:@nsend_cmnd 3465 pull*default:dirt*2=
### timer.lua ###
Bot Timer=
Cycle time [min]:=
Bot Timer: Not connected=
Special kind of sensor.=
Can be programmed with a time in seconds, e.g. to start the bot cyclically.=
### timer.lua ###
### logic_and.lua ###
Connected with=
### tool.lua ###
Sensor Connection Tool=
@@ -0,0 +1,275 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signal AND.
Signal is sent, if all input signals are received.
]]--
-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S2P = minetest.string_to_pos
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
local function inputs(tbl)
local out = {}
for _,v in ipairs(tbl) do
if v then
out[#out + 1] = "1"
else
out[#out + 1] = "0"
end
end
return table.concat(out, " ")
end
local function update_infotext(pos, dest_pos, cmnd)
local mem = tubelib2.get_mem(pos)
local text = table.concat({
S("Signal AND with"),
#mem.inputs or 0,
S("inputs"),
":",
S("Connected with"),
P2S(dest_pos),
"/",
cmnd,
":",
inputs(mem.inputs or {})
}, " ")
M(pos):set_string("infotext", text)
end
local function all_inputs(mem)
mem.inputs = mem.inputs or {}
for _,v in ipairs(mem.inputs) do
if not v then return false end
end
return true
end
local function zero_inputs(mem)
mem.inputs = mem.inputs or {}
for _,v in ipairs(mem.inputs) do
if v then return true end
end
return false
end
local function clear_inputs(mem)
mem.inputs = mem.inputs or {}
for i,_ in ipairs(mem.inputs) do
mem.inputs[i] = false
end
end
local function infotext(pos)
local meta = M(pos)
local dest_pos = meta:get_string("signal_pos")
local signal = meta:get_string("signal_data")
if dest_pos ~= "" and signal ~= "" then
update_infotext(pos, S2P(dest_pos), signal)
end
end
-- Used by the pairing tool
local function signs_bot_get_signal(pos, node)
local mem = tubelib2.get_mem(pos)
mem.known_sensors = mem.known_sensors or {}
if not mem.known_sensors[node.hash] then
mem.known_sensors[node.hash] = true
mem.inputs = mem.inputs or {}
mem.inputs[#mem.inputs + 1] = false
clear_inputs(mem)
infotext(pos)
end
return #mem.inputs
end
-- switch to normal texture
local function turn_off(pos)
local node = minetest.get_node(pos)
if node.name == "signs_bot:and2" or node.name == "signs_bot:and3" then
node.name = "signs_bot:and1"
minetest.swap_node(pos, node)
end
end
-- switch to not zero texture
local function not_zero(pos)
local node = minetest.get_node(pos)
if node.name == "signs_bot:and1" or node.name == "signs_bot:and3" then
node.name = "signs_bot:and2"
minetest.swap_node(pos, node)
end
infotext(pos)
end
local function send_signal(pos)
local meta = M(pos)
local mem = tubelib2.get_mem(pos)
local node = minetest.get_node(pos)
if node.name == "signs_bot:and1" or node.name == "signs_bot:and2" then
node.name = "signs_bot:and3"
minetest.swap_node(pos, node)
minetest.after(2, turn_off, pos)
end
signs_bot.send_signal(pos)
signs_bot.lib.activate_extender_nodes(pos, true)
clear_inputs(mem)
infotext(pos)
end
local function valid_state(pos)
local signal = M(pos):get_string("signal_data")
local state = signs_bot.get_state(pos)
-- Signs box must be in the opposite state
return state == nil or (signal == "on" and state == false) or (signal == "off" and state == true)
end
-- To be called from sensors
local function signs_bot_on_signal(pos, node, signal)
local mem = tubelib2.get_mem(pos)
signal = tonumber(signal) or 1
if valid_state(pos) then
mem.inputs = mem.inputs or {}
mem.inputs[signal] = true
if all_inputs(mem) then
send_signal(pos)
else
not_zero(pos)
end
end
end
minetest.register_node("signs_bot:and1", {
description = S("Signal AND"),
inventory_image = "signs_bot_and_inv.png",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor2.png^signs_bot_and1.png",
"signs_bot_sensor2.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
local mem = tubelib2.init_mem(pos)
mem.inputs = {}
infotext(pos)
end,
signs_bot_get_signal = signs_bot_get_signal,
signs_bot_on_signal = signs_bot_on_signal,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
is_ground_content = false,
groups = {sign_bot_sensor = 1, cracky = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("signs_bot:and2", {
description = S("Signal AND"),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor2.png^signs_bot_and2.png",
"signs_bot_sensor2.png",
},
on_punch = function(pos, node, puncher, pointed_thing)
local mem = tubelib2.get_mem(pos)
clear_inputs(mem)
turn_off(pos)
infotext(pos)
end,
signs_bot_get_signal = signs_bot_get_signal,
signs_bot_on_signal = signs_bot_on_signal,
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
is_ground_content = false,
drop = "signs_bot:and1",
groups = {sign_bot_sensor = 1, cracky = 1, not_in_creative_inventory = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("signs_bot:and3", {
description = S("Signal AND"),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor2.png^signs_bot_and3.png",
"signs_bot_sensor2.png",
},
update_infotext = update_infotext,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
is_ground_content = false,
diggable = false,
groups = {sign_bot_sensor = 1, not_in_creative_inventory = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_craft({
output = "signs_bot:and1",
recipe = {
{"group:wood", "dye:yellow", ""},
{"", "", ""},
{"default:steel_ingot", "", "default:mese_crystal_fragment"},
}
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "and", {
name = S("Signal AND"),
data = {
item = "signs_bot:and1",
text = table.concat({
S("Signal is sent, if all input signals are received."),
}, "\n")
},
})
end
+143
View File
@@ -0,0 +1,143 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
InGame Documentation for techage or doclib
]]--
local MP = minetest.get_modpath("signs_bot")
if minetest.global_exists("techage") then
-- Use the Techage Construction Board
local content = dofile(MP.."/manual_EN.lua")
doclib.add_to_manual("techage", "EN", content)
local content = dofile(MP.."/manual_DE.lua")
doclib.add_to_manual("techage", "DE", content)
elseif minetest.global_exists("doclib") then
-- Create own manual book
local settings = {
symbol_item = "signs_bot_bot_inv.png",
}
doclib.create_manual("signs_bot", "EN", settings)
local content = dofile(MP.."/manual_EN.lua")
doclib.add_to_manual("signs_bot", "EN", content)
doclib.create_manual("signs_bot", "DE", settings)
local content = dofile(MP.."/manual_DE.lua")
doclib.add_to_manual("signs_bot", "DE", content)
minetest.register_node("signs_bot:manual", {
description = "Signs Bot Manual (EN)",
inventory_image = "doclib_book_inv.png",
tiles = {
-- up, down, right, left, back, front
"doclib_book.png",
"doclib_book.png",
"doclib_book.png^[transformR270",
"doclib_book.png^[transformR90",
"doclib_book.png^[transformR180",
"doclib_book.png"
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/32, -16/32, -12/32, 8/32, -12/32, 12/32},
},
},
after_place_node = function(pos, placer, itemstack)
minetest.get_meta(pos):set_string("infotext", "Signs Bot Manual (EN)")
minetest.get_meta(pos):set_string("formspec", doclib.formspec(pos, "signs_bot", "EN"))
end,
on_receive_fields = function(pos, formname, fields, player)
local player_name = player:get_player_name()
if minetest.is_protected(pos, player_name) then
return
end
minetest.get_meta(pos):set_string("formspec", doclib.formspec(pos, "signs_bot", "EN", fields))
end,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
use_texture_alpha = "clip",
is_ground_content = false,
groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2},
})
minetest.register_node("signs_bot:handbuch", {
description = "Signs Bot Handbuch (DE)",
inventory_image = "doclib_book_inv.png",
tiles = {
-- up, down, right, left, back, front
"doclib_book.png",
"doclib_book.png",
"doclib_book.png^[transformR270",
"doclib_book.png^[transformR90",
"doclib_book.png^[transformR180",
"doclib_book.png"
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/32, -16/32, -12/32, 8/32, -12/32, 12/32},
},
},
after_place_node = function(pos, placer, itemstack)
minetest.get_meta(pos):set_string("infotext", "Signs Bot Handbuch (DE)")
minetest.get_meta(pos):set_string("formspec", doclib.formspec(pos, "signs_bot", "DE"))
end,
on_receive_fields = function(pos, formname, fields, player)
local player_name = player:get_player_name()
if minetest.is_protected(pos, player_name) then
return
end
minetest.get_meta(pos):set_string("formspec", doclib.formspec(pos, "signs_bot", "DE", fields))
end,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
use_texture_alpha = "clip",
is_ground_content = false,
groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2},
})
minetest.register_craft({
output = "signs_bot:manual",
recipe = {
{"dye:green", "default:paper", "default:paper"},
{"dye:black", "default:paper", "default:paper"},
{"dye:green", "default:paper", "default:paper"},
},
})
minetest.register_craft({
type = "shapeless",
output = "signs_bot:handbuch",
recipe = {"signs_bot:manual"},
})
minetest.register_craft({
type = "shapeless",
output = "signs_bot:manual",
recipe = {"signs_bot:handbuch"},
})
end
@@ -0,0 +1,638 @@
return {
titles = {
"1,Signs Bot",
"2,Erste Schritte",
"2,Schilder",
"2,Sensors and Actuators",
"2,Sensor Verbindungswerkzeug",
"2,Inventar",
"2,Blöcke",
"3,Signs Bot Box",
"3,Bot Klappe",
"3,Zeichen Kopierer",
"3,Bot Sensor",
"3,Block Sensor",
"3,Ernte Sensor",
"3,Signs Bot Kiste",
"3,Bot Timer",
"3,Roboter Steuerungseinheit",
"3,Sensor Erweiterung",
"3,Signal AND",
"3,Signal Verzögerer",
"3,Zeichen 'Farming'",
"3,Zeichen 'Vorlage'",
"3,Zeichen 'kopiere 3x3x3'",
"3,Zeichen 'Blumen'",
"3,Zeichen 'Espe'",
"3,Zeichen 'Kommando'",
"3,Zeichen \"Rechts drehen\"",
"3,Zeichen \"Links drehen\"",
"3,Zeichen \"Nehme Gegenstand\"",
"3,Zeichen \"Lege Gegenstand\"",
"3,Zeichen \"Stopp\"",
"3,Zeichen \"Lege in den Wagen\" (minecart)",
"3,Zeichen \"Nehme aus dem Wagen\" (minecart)",
"3,Zeichen 'Schöpfe Wasser' (xdecor)",
"3,Zeichen 'Koche Suppe' (xdecor)",
"2,Bot Kommandos",
"3,Techage spezifische Kommandos",
"3,Flow Control Kommandos",
"3,Weitere Sprungkommands",
"3,Flow Control Beispiele",
"4,Beispiel mit einer Funktion am Anfang:",
"4,Beispiel mit einer Funktion am Ende:",
},
texts = {
"Ein durch Zeichen/Schilder gesteuerter Roboter.\n"..
"\n"..
"Web Doku: https://github.com/joe7575/signs_bot/blob/master/manual_DE.md\n"..
"\n"..
"\n"..
"\n",
"Nachdem du die Signs Bot Box platziert hast\\, kannst du den Bot über die\n"..
"Schaltfläche „An“ im Boxmenü starten. Wenn der Bot sofort in seine Box zurückkehrt\\,\n"..
"muss der Bot zuerst mit elektrischer Energie (Techage) aufgeladen werden.\n"..
"Anschließend läuft der Bot geradeaus\\, bis er auf ein Hindernis trifft\n"..
"(eine Stufe mit zwei oder mehr Blöcken nach oben oder unten\\, oder ein Schild.)\n"..
"\n"..
"Der Bot kann nur durch Schilder gesteuert werden\\, die ihm in den Weg gestellt werden.\n"..
"\n"..
"Falls der Bot ein Schild erreicht\\, führt er die Befehle auf dem Schild aus.\n"..
"Falls der erste Befehl auf dem Schild z.B. lautet: „turn_around“\\, dreht sich der Bot \n"..
"um und geht zurück. In diesem Fall erreicht der Bot seine Box erneut und\n"..
"schaltet sich ab.\n"..
"\n"..
"Falls der Bot ein Hindernis erreicht\\, stoppt er\\, oder führt\\, falls verfügbar\\,\n"..
"den nächsten Befehle vom letzten Schild aus.\n"..
"\n"..
"Die Signs Bot Box verfügt über ein Inventar mit 6 Slots (Speicherplätze) für Schilder\n"..
"und 8 Slots für andere Gegenstände (die vom Bot platziert/abgebaut werden).\n"..
"Dieses Inventar simuliert das interne Inventar des Bots. Das bedeutet\\, dass du\n"..
"nur dann Zugriff auf das Inventar hast\\, wenn der Bot ausgeschaltet ist\n"..
"(in seiner Box „sitzt“).\n"..
"\n"..
"Außerdem gibt es folgende Blöcke:\n"..
"\n"..
" - Sensoren: Diese können ein Signal an einen Aktor senden\\, wenn sie mit dem\nAktor verbunden sind.\n"..
" - Aktoren: Diese führen eine Aktion aus\\, wenn sie ein Signal von einem Sensor empfangen.\n"..
"\n"..
"\n"..
"\n",
"Du steuerst die Richtung des Bots über die „links drehen“ und\n"..
"„rechts drehen“-Schilder (Schilder mit dem Pfeil). Der Bot kann Stufen\n"..
"überwinden (einen Block hoch/runter).\n"..
"Es gibt aber auch Befehle\\, um den Bot nach oben und unten zu bewegen.\n"..
"\n"..
"Es ist nicht notwendig\\, einen Weg zurück zur Box zu markieren. Mit dem\n"..
"Befehl „turn_off“ schaltet sich der Bot ab und ist von jeder Position aus wieder\n"..
"in seiner Box. Das Gleiche gilt\\, wenn du den Bot über das Box-Menü ausschaltest.\n"..
"Wenn der Bot ein Schild aus der falschen Richtung (von hinten oder von der Seite)\n"..
"erreicht\\, wird das Schild ignoriert.\n"..
"Der Bot wird einfach über das Schild steigen.\n"..
"\n"..
"Alle vordefinierten Schilder verfügen über ein Menü mit einer Liste der Bot-Befehle.\n"..
"Diese Schilder können nicht geändert werden\\, aber du kannst deine eigenen Schilder\n"..
"erstellen und programmieren. Hierzu musst du das „command“-Schild verwenden.\n"..
"Dieses Schild verfügt über ein Bearbeitungsfeld für Befehle und eine Hilfeseite mit\n"..
"allen verfügbaren Befehlen. Die Hilfeseite verfügt über eine Kopierschaltfläche\\,\n"..
"um die Programmierung zu vereinfachen.\n"..
"\n"..
"Auch für die eigenen Schilder ist es wichtig zu wissen: Nach der Ausführung des\n"..
"letzten Befehls des Schildes verfällt der Bot wieder in sein Standardverhalten\n"..
"und läuft in die eingeschlagene Richtung.\n"..
"\n"..
"Eine Standardaufgabe des Bots besteht darin\\, Gegenstände von einer Truhe zu\n"..
"einer anderen Truhe (oder einem Block mit einem truhenähnlichen Inventar) zu\n"..
"verschieben. Dies kann über die beiden Zeichen „Nehme Gegenstand“ und\n"..
"„Lege Gegenstand“ erfolgen.\n"..
"Diese Schilder müssen auf dem Truhenblock platziert werden.\n"..
"\n"..
"\n"..
"\n",
"Zusätzlich zu den Schildern kann der Bot mittels Sensoren gesteuert werden. Sensoren wie der Bot-Sensor haben zwei Zustände: ein und aus. Wenn der Bot-Sensor einen Bot erkennt\\, wechselt er in den Zustand „Ein“ und sendet ein Signal an einen angeschlossenen Block\\, einen sogenannten Aktor.\n"..
"\n"..
"Sensoren sind:\n"..
"\n"..
" - Bot-Sensor: Sendet ein Signal\\, wenn ein Roboter am Sensor vorbeikommt\n"..
" - Block Sensor: Sendet ein Signal\\, wenn der Sensor einen (neuen) Block erkennt\n"..
" - Ernte Sensor: Sendet ein Signal\\, wenn beispielsweise Weizen ausgewachsen ist\n"..
" - Signs Bot Kiste: Sendet je nach Truhenzustand (leer\\, voll) ein Signal\n"..
"\n"..
"Aktuatoren sind:\n"..
"\n"..
" - Roboter Box: Kann den Bot ein- und ausschalten\n"..
" - Roboter Steuerungseinheit: Kann verwendet werden\\, um ein Schild auszutauschen\\,\num dadurch den Bot zu lenken\n"..
"\n"..
"Sensoren müssen mit Aktoren verbunden (gepaart) werden. Dafür kann das\n"..
"„Sensor Verbindungswerkzeug“ genutzt werden.\n"..
"\n"..
"\n"..
"\n",
"Um ein Signal von einem Sensor an einen Aktor zu senden\\, muss der Sensor mit dem\n"..
"Aktor verbunden (gepaart) werden. Zur Verbindung von Sensor und Aktor muss das\n"..
"Sensor Connection Tool verwendet werden. Klicke einfach mit dem Werkzeug\n"..
"auf beide Blöcke und der Sensor wird mit dem Aktor verbunden. Eine erfolgreiche\n"..
"Verbindung wird durch ein Ping/Pong-Geräusch angezeigt.\n"..
"\n"..
"Bevor du den Sensor mit dem Aktor verbindest\\, stelle sicher\\, dass sich der Aktor im\n"..
"gewünschten Zustand befindet.\n"..
"Beispiel: Wenn du den Bot mit einem Sensor starten möchtest\\, verbinde den Sensor\n"..
"mit der Bot Box\\, wenn sich der Bot im Status „An“ befindet. Andernfalls stoppt das\n"..
"Sensorsignal den Bot\\, anstatt ihn zu starten.\n"..
"\n"..
"\n"..
"\n",
"Das Folgende gilt für alle Befehle\\, die Gegenstände/Artikel in das Bot-Inventar\n"..
"legen\\, wie:\n"..
"\n"..
" - 'take_item <num> <slot>'\n"..
" - 'pickup_items <slot>'\n"..
" - 'trash_sign <slot>'\n"..
" - 'harvest <slot>'\n"..
" - 'dig_front <slot> <lvl>'\n"..
"\n"..
"Wenn beim Befehl kein Slot oder aber Slot 0 angegeben wurde (Fall A)\\, werden\n"..
"nacheinander alle 8 Slots des Bot-Inventars überprüft. \n"..
"Wenn ein Slot angegeben wurde (Fall B)\\, wird nur dieser Slot überprüft.\n"..
"In beiden Fällen gilt: \n"..
"\n"..
"Wenn der Slot vorkonfiguriert ist und zum Artikel passt\\, oder wenn der Slot\n"..
"nicht konfiguriert und leer ist\\, oder nur teilweise mit dem Artikeltyp gefüllt ist\\,\n"..
"der hinzugefügt werden soll\\, dann werden die oder der Artikel hinzugefügt.\n"..
"Dabei werden vorkonfigurierte Slots zuerst gefüllt\\, bevor leere Slots verwendet werden.\n"..
"\n"..
"Können nicht alle Artikel hinzugefügt werden\\, werden im Fall A die verbleibenden\n"..
"Slots durchprobiert. Alles\\, was nicht zum eigenen Inventar hinzugefügt werden\n"..
"konnte\\, geht zurück oder wird fallen gelassen.\n"..
"\n"..
"Das Folgende gilt für alle Befehle\\, die verwendet werden\\, um Gegenstände aus dem\n"..
"Bot-Inventar zu entnehmen\\, wie zum Beispiel:\n"..
"\n"..
" - 'add_item <num> <slot>'\n"..
"\n"..
"Hier spielt es keine Rolle\\, ob ein Slot vorkonfiguriert ist oder nicht. Der Bot nimmt\n"..
"den ersten Stapel\\, den er aus seinem eigenen Inventar finden kann\\, und versucht\\,\n"..
"ihn zu verwenden. Wenn ein Slot angegeben ist\\, nimmt er Artikel nur aus diesem\n"..
"Slot. Ist kein Slot angegeben\\, prüft der Bot nacheinander alle Positiionen\\,\n"..
"beginnend bei Slot 1\\, bis es etwas findet. Ist die gefundene Anzahl kleiner als\n"..
"gefordert\\, versucht er\\, den Rest aus einem beliebigen anderen Slot zu entnehmen.\n"..
"\n"..
"\n"..
"\n",
"",
"Die Box ist das Gehäuse des Bots. Platzieren Sie die Box und starten Sie den Bot über\n"..
"die Schaltfläche „An“. Wenn die Mod Techage installiert ist\\, benötigt der Bot auch Strom.\n"..
"Der Bot verlässt die Box auf der rechten Seite. Es startet nicht\\, wenn diese Position\n"..
"blockiert ist.\n"..
"\n"..
"Um den Bot anzuhalten und zu entfernen\\, drücken Sie die „Aus“-Taste.\n"..
"Das Box-Inventar simuliert das Inventar des Bots.\n"..
"Sie können nicht auf das Inventar zugreifen\\, wenn der Bot aktiv ist..\n"..
"Der Bot kann bis zu 8 Stapel mit Gengeständen und 6 Schilder mit sich führen.\n"..
"\n"..
"\n"..
"\n",
"Die Klappe ist ein einfacher Block\\, der als Tür für den Bot dient. Platziere die\n"..
"Klappe in einer beliebigen Wand und der Bot öffnet und schließt die Klappe \n"..
"automatisch\\, wenn er an dieser Stelle durch die Wand geht.\n"..
"\n"..
"\n"..
"\n",
"Mit dem Kopierer können Schilderkopien erstellt werden:\n"..
"\n"..
" - Fügen Sie ein „cmnd“-Schild\\, das als Vorlage verwendet werden soll\\, \n in das Inventar „Vorlage“ ein\n"..
" - Fügen Sie ein oder mehrere „Leerzeichen“ zum Inventar „Eingabe“ hinzu.\n"..
" - Nehmen Sie die Kopien aus dem Inventar „Ausgabe“.\n"..
"\n"..
"Alternativ können auch geschriebene Bücher \\[default:book_written\\] als\n"..
"Vorlage verwendet werden.\n"..
"Auch bereits geschriebene Schilder können als Input verwendet werden.\n"..
"\n"..
"\n"..
"\n",
"Der Bot-Sensor erkennt jeden Bot und sendet ein Signal\\, wenn sich ein Bot\n"..
"in der Nähe befindet.\n"..
"Der Sensorbereich beträgt einen Block/Meter. \n"..
"Die Sensorrichtung spielt keine Rolle.\n"..
"\n"..
"\n"..
"\n",
"Der Block Sensor sendet zyklisch Signale\\, wenn er das Auftauchen oder\n"..
"Verschwinden von Blöcken erkennt\\, muss aber entsprechend konfiguriert\n"..
"werden. Die Sensorreichweite beträgt 3 Blöcke/Meter in eine Richtung.\n"..
"Der Sensor hat eine aktive Seite (rot)\\, die auf den beobachteten Bereich\n"..
"zeigen muss.\n"..
"\n"..
"\n"..
"\n",
"Der Ernte Sensor sendet zyklische Signale\\, wenn beispielsweise Weizen\n"..
"ausgewachsen ist. Der Sensorbereich beträgt einen Block/Meter.#\n"..
"Der Sensor hat eine aktive Seite (rot)\\, die auf die Ernte/das Feld\n"..
"zeigen muss.\n"..
"\n"..
"\n"..
"\n",
"Die Signs Bot Kiste ist eine spezielle Truhe mit Sensorfunktion. Sie sendet\n"..
"je nach Zustand ein Signal.\n"..
"Mögliche Zustände sind „empty“\\, „not empty“\\, „almost full“.\n"..
"\n"..
"Ein typischer Anwendungsfall ist das Ausschalten des Bots\\, wenn die Truhe\n"..
"fast voll oder aber leer ist.\n"..
"\n"..
"\n"..
"\n",
"Dies ist eine besondere Typ von Sensor. Er ist programmierbar mit einer Zeit\n"..
"in Sekunden\\, z.B. um den Bot zyklisch zu starten.\n"..
"\n"..
"\n"..
"\n",
"Die Roboter Steuerungseinheit dient der Steuerung des Bots mittels Zeichen.\n"..
"Das Gerät kann mit bis zu 4 verschiedenen Schildern bestückt und mittels\n"..
"Sensoren programmiert werden.\n"..
"\n"..
"Um die Steuerungseinheit zu laden\\, platzieren Sie ein Schild auf der roten Seite\n"..
"der Steuerungseinheit und klicken Sie auf die Steuerungseinheit.\n"..
"Das Schild verschwindet / wird in das Inventar der Steuerungseinheit verschoben.\n"..
"Dies kann dreimal wiederholt werden.\n"..
"\n"..
"Verwenden Sie das Verbindungstool\\, um bis zu 4 Sensoren mit der\n"..
"Steuerungseinheit zu verbinden.\n"..
"\n"..
"\n"..
"\n",
"Mit der Sensor Erweiterung können Sensorsignale an mehr als einen Aktor\n"..
"gesendet werden.\n"..
"Platzieren Sie eine oder mehrere Sensor Erweiterungen in der Nähe des\n"..
"Sensors und verbinden Sie jede Sensor Erweiterung mithilfe des\n"..
"Verbindungswerkzeug mit einem weiteren Aktor.\n"..
"\n"..
"\n"..
"\n",
"Um mehrere Signale zu kombinieren\\, verwende den Signal AND Block.\n"..
"Dieser sendet erst ein Signal\\, wenn alle Eingangssignale empfangen wurden.\n"..
"(logisches UND). Der Block hat beliebig viele Eingänge und 1 Ausgang.\n"..
"Der Block hat drei Zustände\\, die er farblich anzeigt:\n"..
"\n"..
" - Schwarzes &-Symbol: Es liegt kein Eingangssignal an\n"..
" - Blaues &-Symbol: Es liegt mindestens ein Eingangssignal an\\, aber noch nicht alle\n"..
" - Rotes &-Symbol: Alle Eingangssignale liegen an\\, der Block sendet ein Signal\nund löscht die Eingangssignale\\, so dass er wieder ein schwarzes Symbol anzeigt.\n"..
"\n"..
"Der Block arbeitet nur\\, wenn der Ausgangsaktor nicht in dem Zustand ist\\,\n"..
"in den er durch das Signal gesetzt werden soll.\n"..
"Ist also bspw. ein Bot bereits unterwegs und der AND-Block ist so progammiert\\,\n"..
"dass der Bot starten soll\\, werden Eingangsssignale nicht angenommen.\n"..
"\n"..
"Durch einen Schlag auf den Block werden ggf. anliegende Eingangssignale\n"..
"gelöscht\\, so dass der Block wieder auf den Ausgangszustand zurückfällt.\n"..
"\n"..
"\n"..
"\n",
"Signale werden verzögert weitergeleitet. Nachfolgende Signale werden\n"..
"in die Warteschlange gestellt.\n"..
"Die Verzögerungszeit ist konfigurierbar.\n"..
"\n"..
"\n"..
"\n",
"Wird zum Ernten und Säen eines 3x3-Feldes verwendet. Platziere das Schild\n"..
"vor dem Feld.\n"..
"Der verwendete Samen muss sich im ersten Slot des Bot Inventars\n"..
"befinden. Wenn der Bot fertig ist\\, dreht sich der Bot und läuft zurück.\n"..
"\n"..
"\n"..
"\n",
"Wird verwendet\\, um eine Kopie eines 3x3x3-Würfels zu erstellen. Platziere das\n"..
"Schild vor die zu kopierenden Blöcke. Verwende das Kopierzeichen\\, um die Kopie\n"..
"dieser Blöcke an einem anderen Ort anzufertigen. \n"..
"Der Bot muss zuerst das \"Vorlage\" Zeichen abarbeiten\\, erst dann kann der Bot zum\n"..
"Kopierzeichen geleitet werden.\n"..
"\n"..
"\n"..
"\n",
"Wird verwendet\\, um eine Kopie eines 3x3x3-Würfels zu erstellen. Platziere das Schild\n"..
"vor der Stelle\\, an der die Kopie angefertigt werden soll. Siehe auch \"Vorlage\" Zeichen.\n"..
"\n"..
"\n"..
"\n",
"Wird zum Schneiden von Blumen auf einem 3x3-Feld verwendet. Platziere das SWenn der Bot fertig ist\\, dreht sich der Bot und geht zurück.child\n"..
"vor dem Feld.\n"..
"Wenn der Bot fertig ist\\, dreht er sich um.\n"..
"\n"..
"\n"..
"\n",
"Wird zum Ernten eines Espen- oder Kiefernstamms verwendet:\n"..
"\n"..
" - Platziere das Schild vor dem Baum.\n"..
" - Platziere eine Truhe rechts neben dem Schild.\n"..
" - Legen Sie einen Erdstapel (mindestens 10 Blöcke) in die Truhe.\n"..
" - Slot 1 des Bot-Inventars für Erde vorkonfigurieren\n"..
" - Slot 2 des Bot-Inventars für Setzlingen vorkonfigurieren\n"..
"\n"..
"\n"..
"\n",
"Das „Kommando“-Zeichen kann vom Spieler programmiert werden. Platziere\n"..
"das Schild und verwende das Blockmenü\\, um die Abfolge von Bot-Befehlen zu\n"..
"programmieren.\n"..
"Das Menü verfügt über ein Bearbeitungsfeld für Ihre Befehle und eine Hilfeseite\n"..
"mit allen verfügbaren Befehle. Die Hilfeseite verfügt über eine Kopierschaltfläche\\,\n"..
"um die Programmierung zu vereinfachen.\n"..
"\n"..
"\n"..
"\n",
"Der Bot dreht sich nach rechts\\, wenn er dieses Schild vor sich erkennt.\n"..
"\n"..
"\n"..
"\n",
"Der Bot dreht sich nach links\\, wenn er dieses Schild vor sich erkennt.\n"..
"\n"..
"\n"..
"\n",
"Der Bot nimmt Gegenstände aus einer Truhe/Kiste vor sich und dreht sich dann um.\n"..
"Dieses Schild muss oben auf der Truhe angebracht werden.\n"..
"\n"..
"\n"..
"\n",
"Der Bot legt Gegenstände in eine Truhe /Kistevor sich und dreht sich dann um.\n"..
"Dieses Schild muss oben auf der Truhe angebracht werden.\n"..
"\n"..
"\n"..
"\n",
"Der Bot bleibt vor diesem Schild stehen\\, bis das Schild entfernt oder der\n"..
"Bot ausgeschaltet wird.\n"..
"\n"..
"\n"..
"\n",
"Der Bot legt Gegenstände in einen Grubenwagen (minecart) vor sich\\,\n"..
"schiebt den Wagen an und dreht sich dann um. Dieses Schild muss an\n"..
"der Endposition des Wagens über der Schiene angebracht werden.\n"..
"\n"..
"\n"..
"\n",
"Der Bot nimmt Gegenstände aus einem Grubenwagen (minecart) vor sich\\,\n"..
"schiebt den Wagen an und dreht sich dann um. Dieses Schild muss an der\n"..
"Endposition des Wagens über der Schiene angebracht werden.\n"..
"\n"..
"\n"..
"\n",
"Wird verwendet\\, um Wasser in einen Eimer zu füllen. Platzieren Sie das Schild\n"..
"am Ufer vor dem stillen Wasserbecken.\n"..
"\n"..
"Gegenstände in den Slots:\n"..
"\n"..
" 1 - leerer Eimer\n"..
"\n"..
"Das Ergebnis ist ein Eimer mit Wasser im ausgewählten Inventarplatz.\n"..
"Wenn der Bot fertig ist\\, dreht er sich um.\n"..
"\n"..
"\n"..
"\n",
"Wird zum Kochen einer Gemüsesuppe im Kessel verwendet. \n"..
"Der Kessel sollte leer und über brennbarem Material platziert sein. \n"..
"Im zu verhindern\\, dass das Holzschild Feuer fängt\\, stelle das Schild\n"..
"ein Feld vor den Kessel.\n"..
"\n"..
"Gegenstände in den Slots:\n"..
"\n"..
" 1 - Wassereimer\"\n"..
" 2 – Gemüse Nr. 1 (z. B. Tomate)\n"..
" 3 – Gemüse Nr. 2 (z. B. Karotte)\n"..
" 4 – leere Schüssel (von Farming- oder Xdecor-Mods)\n"..
"\n"..
"Das Ergebnis ist eine Schüssel mit Gemüsesuppe im ausgewählten Inventarplatz.\n"..
"Wenn der Bot fertig ist\\, dreht er sich um.\n"..
"\n"..
"\n"..
"\n",
"Die Befehle sind auch alle als Hilfeseite im Zeichen „Kommandos“ beschrieben.\n"..
"Alle gesetzten Blöcke oder Schilder werden aus dem Bot-Inventar übernommen.\n"..
"Alle entfernten Blöcke oder Schilder werden wieder dem Bot-Inventar hinzugefügt.\n"..
"„<slot>“ ist immer der interne Inventarstapel des Bots (1..8).\n"..
"\n"..
" move <steps> - gehe einen oder mehrere Schritte vorwärts\n"..
" cond_move - gehe bis zum nächsten Hindernis oder Schild\n"..
" turn_left - drehe links\n"..
" turn_right - drehe rechts\n"..
" turn_around - drehe um\n"..
" backward - gehe ein Schitt zurück\n"..
" turn_off - schalte den Bot aus / zurück in die Box\n"..
" pause <sec> - warte eine oder mehrere Sekunden\n"..
" move_up - nach oben bewegen (maximal 2 Mal)\n"..
" move_down - nach unten bewegen\n"..
" fall_down - in ein Loch/Abgrund fallen lassen (bis zu 10 Blöcke)\n"..
" take_item <num> <slot> - nehme einen oder mehrere Gegenstände aus einer Kiste\n"..
" add_item <num> <slot> - lege einen oder mehrere Gegenstände in eine Kiste\n"..
" add_fuel <num> <slot> - fülle Brennstoff in einen Ofen\n"..
" place_front <slot> <lvl> - setze den Block vor den Roboter\n"..
" place_left <slot> <lvl> - setze den Block links vom Roboter\n"..
" place_right <slot> <lvl> - setze den Block rechts vom Roboter\n"..
" place_below <slot> - hebe den Roboter an und setze den Block unter den Roboter\n"..
" place_above <slot> - setze den Block über den Roboter\n"..
" dig_front <slot> <lvl> - entferne den Block vor dem Roboter\n"..
" dig_left <slot> <lvl> - entferne den Block links vom Roboter\n"..
" dig_right <slot> <lvl> - entferne den Block rechts vom Roboter\n"..
" dig_below <slot> - entferne den Block unter dem Roboter\n"..
" dig_above <slot> - entferne den Block über dem Roboter\n"..
" rotate_item <lvl> <steps> - drehe einen Block vor dem Roboter\n"..
" set_param2 <lvl> <param2> - setze param2 des Blocks vor dem Roboter\n"..
" place_sign <slot> - setze das Schild vor den Roboter\n"..
" place_sign_behind <slot> - setze das Schild hinter den Roboter\n"..
" dig_sign <slot> - entferne das Schild vor den Roboter\n"..
" trash_sign <slot> - Entferne das Schild\\, lösche die Daten und fügen es dem Inventar hinzu\n"..
" stop - stoppe den Bot\\, bis das Schild entfernt wird\n"..
" pickup_items <slot> - hebe Gegenstände auf (in einem 3x3 Feld)\n"..
" drop_items <num> <slot> - lasse Gegenstände fallen\n"..
" harvest - ernte ein 3x3 Feld ab (farming)\n"..
" cutting - schneide Blumen in einem 3x3 Feld ab\n"..
" sow_seed <slot> - sähe/pflanze ein 3x3 Feld an\n"..
" plant_sapling <slot> - pflanze einen Setzling vor dem Roboter\n"..
" pattern - speichere die Blockeigenschaften hinter dem Schild (3x3x3 Würfel) als Vorlage\n"..
" copy <size> - erstelle eine 3x3x3-Kopie der gespeicherten Vorlage\n"..
" punch_cart - stoße einen Grubenwagen an\n"..
" add_compost <slot> - gebe 2 Blätter in das Kompostfass\n"..
" take_compost <slot> - nehme Kompost aus dem Kompostfass\n"..
" print <text> - gebe eine Chat-Nachricht für Debug-Zwecke aus\n"..
" take_water <slot> - schöpfe Wasser mit einem leeren Eimer\n"..
" fill_cauldron <slot> - fülle den xdecor Kessel für eine Suppe\n"..
" take_soup <slot> - fülle die kochende Suppe aus dem Kessel in eine leere Schüssel\n"..
" flame_on - mache Feuer an\n"..
" flame_off - lösche das Feuer\n"..
"\n"..
"\n"..
"\n",
" ignite - Zünde den Techage-Kohleanzünder an\n"..
" low_batt <percent> - Schalte den Bot aus\\, wenn die Batterieleistung\n"..
" unter dem angegebenen Wert in Prozent (1..99) liegt.\n"..
" jump_low_batt <percent> <label> - Springe zu <label>\\, wenn die Batterieleistung\n"..
" unter dem angegebenen Wert in Prozent (1..99) liegt.\n"..
" (siehe \"Flow Control Kommandos\")\n"..
" send_cmnd <receiver> <command> - Sende ein Techage-Befehl an einen bestimmten Knoten.\n"..
" Der Empfänger wird über die Techage-Knotennummer angesprochen.\n"..
" Für Befehle mit zwei oder mehr Wörtern:\n"..
" Verwenden Sie das Zeichen „*“ statt Leerzeichen\\, z.B.:\n"..
" send_cmnd 3465 pull*default:dirt*2\n"..
"\n"..
"\n"..
"\n",
" -- Sprungbefehl\\, <label> ist ein Wort aus den Zeichen a-z oder A-Z\n"..
" jump <label>\n"..
" \n"..
" -- Sprungmarke / Beginn einer Funktion\n"..
" <label>:\n"..
" \n"..
" -- Rückkehr von einer Funktion\n"..
" return\n"..
" \n"..
" -- Beginn eines Schleifenblocks\\, <num> ist eine Zahl von 1..999\n"..
" repeat <num>\n"..
" \n"..
" -- Ende eines Schleifenblocks\n"..
" end\n"..
" \n"..
" -- Aufruf einer Funktion (mit Rückkehr über den Befehl 'return')\n"..
" call <label>\n"..
"\n"..
"\n"..
"\n",
" -- Überprüfe\\, ob sich <num> Gegenstände im \n"..
" -- truhenähnlichen Knoten befinden.\n"..
" -- Wenn nicht\\, springe zu <label>.\n"..
" -- <slot> ist der Bot-Inventar-Slot (1..8) um den Artikel anzugeben\\, \n"..
" -- oder 0 für jeden Artikel.\n"..
" jump_check_item <num> <slot> <label>\n"..
" \n"..
" -- Siehe \"Techage spezifische Kommandos\"\n"..
" jump_low_batt <percent> <label>\n"..
"\n"..
"\n"..
"\n",
"",
" -- jump to the label 'main'\n"..
" jump main\n"..
" \n"..
" -- starting point of the function with the name 'foo'\n"..
" foo:\n"..
" cmnd ...\n"..
" cmnd ...\n"..
" -- end of 'foo'. Jump back\n"..
" return\n"..
" \n"..
" -- main program\n"..
" main:\n"..
" cmnd ...\n"..
" -- repeat all commands up to 'end' 10 times\n"..
" repeat 10\n"..
" cmnd ...\n"..
" -- call the subfunction 'foo'\n"..
" call foo\n"..
" cmnd ...\n"..
" -- end of the 'repeat' loop\n"..
" end\n"..
" -- end of the program\n"..
" exit\n"..
"\n",
" cmnd ...\n"..
" -- repeat all commands up to 'end' 10 times\n"..
" repeat 10\n"..
" cmnd ...\n"..
" -- call the subfunction 'foo'\n"..
" call foo\n"..
" cmnd ...\n"..
" -- end of the 'repeat' loop\n"..
" end\n"..
" -- end of the program\n"..
" exit\n"..
" \n"..
" -- starting point of the function with the name 'foo'\n"..
" foo:\n"..
" cmnd ...\n"..
" cmnd ...\n"..
" -- end of 'foo'. Jump back\n"..
" return\n"..
"\n",
},
images = {
"signs_bot_bot_inv.png",
"signs_bot_bot_inv.png",
"signs_bot_sign_left.png",
"signs_bot_sensor_crop_inv.png",
"signs_bot_tool.png",
"signs_bot:box",
"",
"signs_bot:box",
"signs_bot:bot_flap",
"signs_bot:duplicator",
"signs_bot:bot_sensor",
"signs_bot:node_sensor",
"signs_bot:crop_sensor",
"signs_bot:chest",
"signs_bot:timer",
"signs_bot:changer1",
"signs_bot:sensor_extender",
"signs_bot:and1",
"signs_bot:delayer",
"signs_bot:farming",
"signs_bot:pattern",
"signs_bot:copy3x3x3",
"signs_bot:flowers",
"signs_bot:aspen",
"signs_bot:sign_cmnd",
"signs_bot:sign_right",
"signs_bot:sign_left",
"signs_bot:sign_take",
"signs_bot:sign_add",
"signs_bot:sign_stop",
"signs_bot:sign_add_cart",
"signs_bot:sign_take_cart",
"signs_bot:water",
"signs_bot:soup",
"signs_bot_bot_inv.png",
"signs_bot_bot_inv.png",
"signs_bot_bot_inv.png",
"signs_bot_bot_inv.png",
"",
"",
"",
},
plans = {
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
}
}
+595
View File
@@ -0,0 +1,595 @@
# Signs Bot
Ein durch Zeichen/Schilder gesteuerter Roboter.
Web Doku: https://github.com/joe7575/signs_bot/blob/master/manual_DE.md
[signs_bot_bot_inv.png|image]
## Erste Schritte
Nachdem du die Signs Bot Box platziert hast, kannst du den Bot über die
Schaltfläche „An“ im Boxmenü starten. Wenn der Bot sofort in seine Box zurückkehrt,
muss der Bot zuerst mit elektrischer Energie (Techage) aufgeladen werden.
Anschließend läuft der Bot geradeaus, bis er auf ein Hindernis trifft
(eine Stufe mit zwei oder mehr Blöcken nach oben oder unten, oder ein Schild.)
Der Bot kann nur durch Schilder gesteuert werden, die ihm in den Weg gestellt werden.
Falls der Bot ein Schild erreicht, führt er die Befehle auf dem Schild aus.
Falls der erste Befehl auf dem Schild z.B. lautet: „turn_around“, dreht sich der Bot
um und geht zurück. In diesem Fall erreicht der Bot seine Box erneut und
schaltet sich ab.
Falls der Bot ein Hindernis erreicht, stoppt er, oder führt, falls verfügbar,
den nächsten Befehle vom letzten Schild aus.
Die Signs Bot Box verfügt über ein Inventar mit 6 Slots (Speicherplätze) für Schilder
und 8 Slots für andere Gegenstände (die vom Bot platziert/abgebaut werden).
Dieses Inventar simuliert das interne Inventar des Bots. Das bedeutet, dass du
nur dann Zugriff auf das Inventar hast, wenn der Bot ausgeschaltet ist
(in seiner Box „sitzt“).
Außerdem gibt es folgende Blöcke:
- Sensoren: Diese können ein Signal an einen Aktor senden, wenn sie mit dem
Aktor verbunden sind.
- Aktoren: Diese führen eine Aktion aus, wenn sie ein Signal von einem Sensor empfangen.
[signs_bot_bot_inv.png|image]
## Schilder
Du steuerst die Richtung des Bots über die „links drehen“ und
„rechts drehen“-Schilder (Schilder mit dem Pfeil). Der Bot kann Stufen
überwinden (einen Block hoch/runter).
Es gibt aber auch Befehle, um den Bot nach oben und unten zu bewegen.
Es ist nicht notwendig, einen Weg zurück zur Box zu markieren. Mit dem
Befehl „turn_off“ schaltet sich der Bot ab und ist von jeder Position aus wieder
in seiner Box. Das Gleiche gilt, wenn du den Bot über das Box-Menü ausschaltest.
Wenn der Bot ein Schild aus der falschen Richtung (von hinten oder von der Seite)
erreicht, wird das Schild ignoriert.
Der Bot wird einfach über das Schild steigen.
Alle vordefinierten Schilder verfügen über ein Menü mit einer Liste der Bot-Befehle.
Diese Schilder können nicht geändert werden, aber du kannst deine eigenen Schilder
erstellen und programmieren. Hierzu musst du das „command“-Schild verwenden.
Dieses Schild verfügt über ein Bearbeitungsfeld für Befehle und eine Hilfeseite mit
allen verfügbaren Befehlen. Die Hilfeseite verfügt über eine Kopierschaltfläche,
um die Programmierung zu vereinfachen.
Auch für die eigenen Schilder ist es wichtig zu wissen: Nach der Ausführung des
letzten Befehls des Schildes verfällt der Bot wieder in sein Standardverhalten
und läuft in die eingeschlagene Richtung.
Eine Standardaufgabe des Bots besteht darin, Gegenstände von einer Truhe zu
einer anderen Truhe (oder einem Block mit einem truhenähnlichen Inventar) zu
verschieben. Dies kann über die beiden Zeichen „Nehme Gegenstand“ und
„Lege Gegenstand“ erfolgen.
Diese Schilder müssen auf dem Truhenblock platziert werden.
[signs_bot_sign_left.png|image]
## Sensors and Actuators
Zusätzlich zu den Schildern kann der Bot mittels Sensoren gesteuert werden. Sensoren wie der Bot-Sensor haben zwei Zustände: ein und aus. Wenn der Bot-Sensor einen Bot erkennt, wechselt er in den Zustand „Ein“ und sendet ein Signal an einen angeschlossenen Block, einen sogenannten Aktor.
Sensoren sind:
- Bot-Sensor: Sendet ein Signal, wenn ein Roboter am Sensor vorbeikommt
- Block Sensor: Sendet ein Signal, wenn der Sensor einen (neuen) Block erkennt
- Ernte Sensor: Sendet ein Signal, wenn beispielsweise Weizen ausgewachsen ist
- Signs Bot Kiste: Sendet je nach Truhenzustand (leer, voll) ein Signal
Aktuatoren sind:
- Roboter Box: Kann den Bot ein- und ausschalten
- Roboter Steuerungseinheit: Kann verwendet werden, um ein Schild auszutauschen,
um dadurch den Bot zu lenken
Sensoren müssen mit Aktoren verbunden (gepaart) werden. Dafür kann das
„Sensor Verbindungswerkzeug“ genutzt werden.
[signs_bot_sensor_crop_inv.png|image]
## Sensor Verbindungswerkzeug
Um ein Signal von einem Sensor an einen Aktor zu senden, muss der Sensor mit dem
Aktor verbunden (gepaart) werden. Zur Verbindung von Sensor und Aktor muss das
Sensor Connection Tool verwendet werden. Klicke einfach mit dem Werkzeug
auf beide Blöcke und der Sensor wird mit dem Aktor verbunden. Eine erfolgreiche
Verbindung wird durch ein Ping/Pong-Geräusch angezeigt.
Bevor du den Sensor mit dem Aktor verbindest, stelle sicher, dass sich der Aktor im
gewünschten Zustand befindet.
Beispiel: Wenn du den Bot mit einem Sensor starten möchtest, verbinde den Sensor
mit der Bot Box, wenn sich der Bot im Status „An“ befindet. Andernfalls stoppt das
Sensorsignal den Bot, anstatt ihn zu starten.
[signs_bot_tool.png|image]
## Inventar
Das Folgende gilt für alle Befehle, die Gegenstände/Artikel in das Bot-Inventar
legen, wie:
- `take_item <num> <slot>`
- `pickup_items <slot>`
- `trash_sign <slot>`
- `harvest <slot>`
- `dig_front <slot> <lvl>`
Wenn beim Befehl kein Slot oder aber Slot 0 angegeben wurde (Fall A), werden
nacheinander alle 8 Slots des Bot-Inventars überprüft.
Wenn ein Slot angegeben wurde (Fall B), wird nur dieser Slot überprüft.
In beiden Fällen gilt:
Wenn der Slot vorkonfiguriert ist und zum Artikel passt, oder wenn der Slot
nicht konfiguriert und leer ist, oder nur teilweise mit dem Artikeltyp gefüllt ist,
der hinzugefügt werden soll, dann werden die oder der Artikel hinzugefügt.
Dabei werden vorkonfigurierte Slots zuerst gefüllt, bevor leere Slots verwendet werden.
Können nicht alle Artikel hinzugefügt werden, werden im Fall A die verbleibenden
Slots durchprobiert. Alles, was nicht zum eigenen Inventar hinzugefügt werden
konnte, geht zurück oder wird fallen gelassen.
Das Folgende gilt für alle Befehle, die verwendet werden, um Gegenstände aus dem
Bot-Inventar zu entnehmen, wie zum Beispiel:
- `add_item <num> <slot>`
Hier spielt es keine Rolle, ob ein Slot vorkonfiguriert ist oder nicht. Der Bot nimmt
den ersten Stapel, den er aus seinem eigenen Inventar finden kann, und versucht,
ihn zu verwenden. Wenn ein Slot angegeben ist, nimmt er Artikel nur aus diesem
Slot. Ist kein Slot angegeben, prüft der Bot nacheinander alle Positiionen,
beginnend bei Slot 1, bis es etwas findet. Ist die gefundene Anzahl kleiner als
gefordert, versucht er, den Rest aus einem beliebigen anderen Slot zu entnehmen.
[signs_bot:box|image]
## Blöcke
### Signs Bot Box
Die Box ist das Gehäuse des Bots. Platzieren Sie die Box und starten Sie den Bot über
die Schaltfläche „An“. Wenn die Mod Techage installiert ist, benötigt der Bot auch Strom.
Der Bot verlässt die Box auf der rechten Seite. Es startet nicht, wenn diese Position
blockiert ist.
Um den Bot anzuhalten und zu entfernen, drücken Sie die „Aus“-Taste.
Das Box-Inventar simuliert das Inventar des Bots.
Sie können nicht auf das Inventar zugreifen, wenn der Bot aktiv ist..
Der Bot kann bis zu 8 Stapel mit Gengeständen und 6 Schilder mit sich führen.
[signs_bot:box|image]
### Bot Klappe
Die Klappe ist ein einfacher Block, der als Tür für den Bot dient. Platziere die
Klappe in einer beliebigen Wand und der Bot öffnet und schließt die Klappe
automatisch, wenn er an dieser Stelle durch die Wand geht.
[signs_bot:bot_flap|image]
### Zeichen Kopierer
Mit dem Kopierer können Schilderkopien erstellt werden:
1. Fügen Sie ein „cmnd“-Schild, das als Vorlage verwendet werden soll,
in das Inventar „Vorlage“ ein
2. Fügen Sie ein oder mehrere „Leerzeichen“ zum Inventar „Eingabe“ hinzu.
3. Nehmen Sie die Kopien aus dem Inventar „Ausgabe“.
Alternativ können auch geschriebene Bücher [default:book_written] als
Vorlage verwendet werden.
Auch bereits geschriebene Schilder können als Input verwendet werden.
[signs_bot:duplicator|image]
### Bot Sensor
Der Bot-Sensor erkennt jeden Bot und sendet ein Signal, wenn sich ein Bot
in der Nähe befindet.
Der Sensorbereich beträgt einen Block/Meter.
Die Sensorrichtung spielt keine Rolle.
[signs_bot:bot_sensor|image]
### Block Sensor
Der Block Sensor sendet zyklisch Signale, wenn er das Auftauchen oder
Verschwinden von Blöcken erkennt, muss aber entsprechend konfiguriert
werden. Die Sensorreichweite beträgt 3 Blöcke/Meter in eine Richtung.
Der Sensor hat eine aktive Seite (rot), die auf den beobachteten Bereich
zeigen muss.
[signs_bot:node_sensor|image]
### Ernte Sensor
Der Ernte Sensor sendet zyklische Signale, wenn beispielsweise Weizen
ausgewachsen ist. Der Sensorbereich beträgt einen Block/Meter.#
Der Sensor hat eine aktive Seite (rot), die auf die Ernte/das Feld
zeigen muss.
[signs_bot:crop_sensor|image]
### Signs Bot Kiste
Die Signs Bot Kiste ist eine spezielle Truhe mit Sensorfunktion. Sie sendet
je nach Zustand ein Signal.
Mögliche Zustände sind „empty“, „not empty“, „almost full“.
Ein typischer Anwendungsfall ist das Ausschalten des Bots, wenn die Truhe
fast voll oder aber leer ist.
[signs_bot:chest|image]
### Bot Timer
Dies ist eine besondere Typ von Sensor. Er ist programmierbar mit einer Zeit
in Sekunden, z.B. um den Bot zyklisch zu starten.
[signs_bot:timer|image]
### Roboter Steuerungseinheit
Die Roboter Steuerungseinheit dient der Steuerung des Bots mittels Zeichen.
Das Gerät kann mit bis zu 4 verschiedenen Schildern bestückt und mittels
Sensoren programmiert werden.
Um die Steuerungseinheit zu laden, platzieren Sie ein Schild auf der roten Seite
der Steuerungseinheit und klicken Sie auf die Steuerungseinheit.
Das Schild verschwindet / wird in das Inventar der Steuerungseinheit verschoben.
Dies kann dreimal wiederholt werden.
Verwenden Sie das Verbindungstool, um bis zu 4 Sensoren mit der
Steuerungseinheit zu verbinden.
[signs_bot:changer1|image]
### Sensor Erweiterung
Mit der Sensor Erweiterung können Sensorsignale an mehr als einen Aktor
gesendet werden.
Platzieren Sie eine oder mehrere Sensor Erweiterungen in der Nähe des
Sensors und verbinden Sie jede Sensor Erweiterung mithilfe des
Verbindungswerkzeug mit einem weiteren Aktor.
[signs_bot:sensor_extender|image]
### Signal AND
Um mehrere Signale zu kombinieren, verwende den Signal AND Block.
Dieser sendet erst ein Signal, wenn alle Eingangssignale empfangen wurden.
(logisches UND). Der Block hat beliebig viele Eingänge und 1 Ausgang.
Der Block hat drei Zustände, die er farblich anzeigt:
- Schwarzes &-Symbol: Es liegt kein Eingangssignal an
- Blaues &-Symbol: Es liegt mindestens ein Eingangssignal an, aber noch nicht alle
- Rotes &-Symbol: Alle Eingangssignale liegen an, der Block sendet ein Signal
und löscht die Eingangssignale, so dass er wieder ein schwarzes Symbol anzeigt.
Der Block arbeitet nur, wenn der Ausgangsaktor nicht in dem Zustand ist,
in den er durch das Signal gesetzt werden soll.
Ist also bspw. ein Bot bereits unterwegs und der AND-Block ist so progammiert,
dass der Bot starten soll, werden Eingangsssignale nicht angenommen.
Durch einen Schlag auf den Block werden ggf. anliegende Eingangssignale
gelöscht, so dass der Block wieder auf den Ausgangszustand zurückfällt.
[signs_bot:and1|image]
### Signal Verzögerer
Signale werden verzögert weitergeleitet. Nachfolgende Signale werden
in die Warteschlange gestellt.
Die Verzögerungszeit ist konfigurierbar.
[signs_bot:delayer|image]
### Zeichen 'Farming'
Wird zum Ernten und Säen eines 3x3-Feldes verwendet. Platziere das Schild
vor dem Feld.
Der verwendete Samen muss sich im ersten Slot des Bot Inventars
befinden. Wenn der Bot fertig ist, dreht sich der Bot und läuft zurück.
[signs_bot:farming|image]
### Zeichen 'Vorlage'
Wird verwendet, um eine Kopie eines 3x3x3-Würfels zu erstellen. Platziere das
Schild vor die zu kopierenden Blöcke. Verwende das Kopierzeichen, um die Kopie
dieser Blöcke an einem anderen Ort anzufertigen.
Der Bot muss zuerst das "Vorlage" Zeichen abarbeiten, erst dann kann der Bot zum
Kopierzeichen geleitet werden.
[signs_bot:pattern|image]
### Zeichen 'kopiere 3x3x3'
Wird verwendet, um eine Kopie eines 3x3x3-Würfels zu erstellen. Platziere das Schild
vor der Stelle, an der die Kopie angefertigt werden soll. Siehe auch "Vorlage" Zeichen.
[signs_bot:copy3x3x3|image]
### Zeichen 'Blumen'
Wird zum Schneiden von Blumen auf einem 3x3-Feld verwendet. Platziere das SWenn der Bot fertig ist, dreht sich der Bot und geht zurück.child
vor dem Feld.
Wenn der Bot fertig ist, dreht er sich um.
[signs_bot:flowers|image]
### Zeichen 'Espe'
Wird zum Ernten eines Espen- oder Kiefernstamms verwendet:
- Platziere das Schild vor dem Baum.
- Platziere eine Truhe rechts neben dem Schild.
- Legen Sie einen Erdstapel (mindestens 10 Blöcke) in die Truhe.
- Slot 1 des Bot-Inventars für Erde vorkonfigurieren
- Slot 2 des Bot-Inventars für Setzlingen vorkonfigurieren
[signs_bot:aspen|image]
### Zeichen 'Kommando'
Das „Kommando“-Zeichen kann vom Spieler programmiert werden. Platziere
das Schild und verwende das Blockmenü, um die Abfolge von Bot-Befehlen zu
programmieren.
Das Menü verfügt über ein Bearbeitungsfeld für Ihre Befehle und eine Hilfeseite
mit allen verfügbaren Befehle. Die Hilfeseite verfügt über eine Kopierschaltfläche,
um die Programmierung zu vereinfachen.
[signs_bot:sign_cmnd|image]
### Zeichen "Rechts drehen"
Der Bot dreht sich nach rechts, wenn er dieses Schild vor sich erkennt.
[signs_bot:sign_right|image]
### Zeichen "Links drehen"
Der Bot dreht sich nach links, wenn er dieses Schild vor sich erkennt.
[signs_bot:sign_left|image]
### Zeichen "Nehme Gegenstand"
Der Bot nimmt Gegenstände aus einer Truhe/Kiste vor sich und dreht sich dann um.
Dieses Schild muss oben auf der Truhe angebracht werden.
[signs_bot:sign_take|image]
### Zeichen "Lege Gegenstand"
Der Bot legt Gegenstände in eine Truhe /Kistevor sich und dreht sich dann um.
Dieses Schild muss oben auf der Truhe angebracht werden.
[signs_bot:sign_add|image]
### Zeichen "Stopp"
Der Bot bleibt vor diesem Schild stehen, bis das Schild entfernt oder der
Bot ausgeschaltet wird.
[signs_bot:sign_stop|image]
### Zeichen "Lege in den Wagen" (minecart)
Der Bot legt Gegenstände in einen Grubenwagen (minecart) vor sich,
schiebt den Wagen an und dreht sich dann um. Dieses Schild muss an
der Endposition des Wagens über der Schiene angebracht werden.
[signs_bot:sign_add_cart|image]
### Zeichen "Nehme aus dem Wagen" (minecart)
Der Bot nimmt Gegenstände aus einem Grubenwagen (minecart) vor sich,
schiebt den Wagen an und dreht sich dann um. Dieses Schild muss an der
Endposition des Wagens über der Schiene angebracht werden.
[signs_bot:sign_take_cart|image]
### Zeichen 'Schöpfe Wasser' (xdecor)
Wird verwendet, um Wasser in einen Eimer zu füllen. Platzieren Sie das Schild
am Ufer vor dem stillen Wasserbecken.
Gegenstände in den Slots:
1 - leerer Eimer
Das Ergebnis ist ein Eimer mit Wasser im ausgewählten Inventarplatz.
Wenn der Bot fertig ist, dreht er sich um.
[signs_bot:water|image]
### Zeichen 'Koche Suppe' (xdecor)
Wird zum Kochen einer Gemüsesuppe im Kessel verwendet.
Der Kessel sollte leer und über brennbarem Material platziert sein.
Im zu verhindern, dass das Holzschild Feuer fängt, stelle das Schild
ein Feld vor den Kessel.
Gegenstände in den Slots:
1 - Wassereimer"
2 – Gemüse Nr. 1 (z. B. Tomate)
3 – Gemüse Nr. 2 (z. B. Karotte)
4 – leere Schüssel (von Farming- oder Xdecor-Mods)
Das Ergebnis ist eine Schüssel mit Gemüsesuppe im ausgewählten Inventarplatz.
Wenn der Bot fertig ist, dreht er sich um.
[signs_bot:soup|image]
## Bot Kommandos
Die Befehle sind auch alle als Hilfeseite im Zeichen „Kommandos“ beschrieben.
Alle gesetzten Blöcke oder Schilder werden aus dem Bot-Inventar übernommen.
Alle entfernten Blöcke oder Schilder werden wieder dem Bot-Inventar hinzugefügt.
„<slot>“ ist immer der interne Inventarstapel des Bots (1..8).
move <steps> - gehe einen oder mehrere Schritte vorwärts
cond_move - gehe bis zum nächsten Hindernis oder Schild
turn_left - drehe links
turn_right - drehe rechts
turn_around - drehe um
backward - gehe ein Schitt zurück
turn_off - schalte den Bot aus / zurück in die Box
pause <sec> - warte eine oder mehrere Sekunden
move_up - nach oben bewegen (maximal 2 Mal)
move_down - nach unten bewegen
fall_down - in ein Loch/Abgrund fallen lassen (bis zu 10 Blöcke)
take_item <num> <slot> - nehme einen oder mehrere Gegenstände aus einer Kiste
add_item <num> <slot> - lege einen oder mehrere Gegenstände in eine Kiste
add_fuel <num> <slot> - fülle Brennstoff in einen Ofen
place_front <slot> <lvl> - setze den Block vor den Roboter
place_left <slot> <lvl> - setze den Block links vom Roboter
place_right <slot> <lvl> - setze den Block rechts vom Roboter
place_below <slot> - hebe den Roboter an und setze den Block unter den Roboter
place_above <slot> - setze den Block über den Roboter
dig_front <slot> <lvl> - entferne den Block vor dem Roboter
dig_left <slot> <lvl> - entferne den Block links vom Roboter
dig_right <slot> <lvl> - entferne den Block rechts vom Roboter
dig_below <slot> - entferne den Block unter dem Roboter
dig_above <slot> - entferne den Block über dem Roboter
rotate_item <lvl> <steps> - drehe einen Block vor dem Roboter
set_param2 <lvl> <param2> - setze param2 des Blocks vor dem Roboter
place_sign <slot> - setze das Schild vor den Roboter
place_sign_behind <slot> - setze das Schild hinter den Roboter
dig_sign <slot> - entferne das Schild vor den Roboter
trash_sign <slot> - Entferne das Schild, lösche die Daten und fügen es dem Inventar hinzu
stop - stoppe den Bot, bis das Schild entfernt wird
pickup_items <slot> - hebe Gegenstände auf (in einem 3x3 Feld)
drop_items <num> <slot> - lasse Gegenstände fallen
harvest - ernte ein 3x3 Feld ab (farming)
cutting - schneide Blumen in einem 3x3 Feld ab
sow_seed <slot> - sähe/pflanze ein 3x3 Feld an
plant_sapling <slot> - pflanze einen Setzling vor dem Roboter
pattern - speichere die Blockeigenschaften hinter dem Schild (3x3x3 Würfel) als Vorlage
copy <size> - erstelle eine 3x3x3-Kopie der gespeicherten Vorlage
punch_cart - stoße einen Grubenwagen an
add_compost <slot> - gebe 2 Blätter in das Kompostfass
take_compost <slot> - nehme Kompost aus dem Kompostfass
print <text> - gebe eine Chat-Nachricht für Debug-Zwecke aus
take_water <slot> - schöpfe Wasser mit einem leeren Eimer
fill_cauldron <slot> - fülle den xdecor Kessel für eine Suppe
take_soup <slot> - fülle die kochende Suppe aus dem Kessel in eine leere Schüssel
flame_on - mache Feuer an
flame_off - lösche das Feuer
[signs_bot_bot_inv.png|image]
### Techage spezifische Kommandos
ignite - Zünde den Techage-Kohleanzünder an
low_batt <percent> - Schalte den Bot aus, wenn die Batterieleistung
unter dem angegebenen Wert in Prozent (1..99) liegt.
jump_low_batt <percent> <label> - Springe zu <label>, wenn die Batterieleistung
unter dem angegebenen Wert in Prozent (1..99) liegt.
(siehe "Flow Control Kommandos")
send_cmnd <receiver> <command> - Sende ein Techage-Befehl an einen bestimmten Knoten.
Der Empfänger wird über die Techage-Knotennummer angesprochen.
Für Befehle mit zwei oder mehr Wörtern:
Verwenden Sie das Zeichen „*“ statt Leerzeichen, z.B.:
send_cmnd 3465 pull*default:dirt*2
[signs_bot_bot_inv.png|image]
### Flow Control Kommandos
-- Sprungbefehl, <label> ist ein Wort aus den Zeichen a-z oder A-Z
jump <label>
-- Sprungmarke / Beginn einer Funktion
<label>:
-- Rückkehr von einer Funktion
return
-- Beginn eines Schleifenblocks, <num> ist eine Zahl von 1..999
repeat <num>
-- Ende eines Schleifenblocks
end
-- Aufruf einer Funktion (mit Rückkehr über den Befehl 'return')
call <label>
[signs_bot_bot_inv.png|image]
### Weitere Sprungkommands
-- Überprüfe, ob sich <num> Gegenstände im
-- truhenähnlichen Knoten befinden.
-- Wenn nicht, springe zu <label>.
-- <slot> ist der Bot-Inventar-Slot (1..8) um den Artikel anzugeben,
-- oder 0 für jeden Artikel.
jump_check_item <num> <slot> <label>
-- Siehe "Techage spezifische Kommandos"
jump_low_batt <percent> <label>
[signs_bot_bot_inv.png|image]
### Flow Control Beispiele
#### Beispiel mit einer Funktion am Anfang:
-- jump to the label 'main'
jump main
-- starting point of the function with the name 'foo'
foo:
cmnd ...
cmnd ...
-- end of 'foo'. Jump back
return
-- main program
main:
cmnd ...
-- repeat all commands up to 'end' 10 times
repeat 10
cmnd ...
-- call the subfunction 'foo'
call foo
cmnd ...
-- end of the 'repeat' loop
end
-- end of the program
exit
#### Beispiel mit einer Funktion am Ende:
cmnd ...
-- repeat all commands up to 'end' 10 times
repeat 10
cmnd ...
-- call the subfunction 'foo'
call foo
cmnd ...
-- end of the 'repeat' loop
end
-- end of the program
exit
-- starting point of the function with the name 'foo'
foo:
cmnd ...
cmnd ...
-- end of 'foo'. Jump back
return
@@ -0,0 +1,606 @@
return {
titles = {
"1,Signs Bot",
"2,Firt Steps",
"2,Signs",
"2,Sensors and Actuators",
"2,Sensor Connection Tool",
"2,Inventory",
"2,Nodes / Blocks",
"3,Signs Bot Box",
"3,Bot Flap",
"3,Signs Duplicator",
"3,Bot Sensor",
"3,Node Sensor",
"3,Crop Sensor",
"3,Signs Bot Chest",
"3,Bot Timer",
"3,Bot Control Unit",
"3,Sensor Extender",
"3,Signal AND",
"3,Signal Delayer",
"3,Sign 'farming'",
"3,Sign 'pattern'",
"3,Sign 'copy3x3x3'",
"3,Sign 'flowers'",
"3,Sign 'aspen'",
"3,Sign 'command'",
"3,Sign \"turn right\"",
"3,Sign \"turn left\"",
"3,Sign \"take item\"",
"3,Sign \"add item\"",
"3,Sign \"stop\"",
"3,Sign \"add to cart\" (minecart)",
"3,Sign \"take from cart\" (minecart)",
"3,Sign 'take water' (xdecor)",
"3,Sign 'cook soup' (xdecor)",
"2,Bot Commands",
"3,Techage specific commands",
"3,Flow control commands",
"3,Further jump commands",
"3,Flow control Examples",
"4,Example with a function at the beginning:",
"4,Example with a function at the end:",
},
texts = {
"A robot controlled by signs.\n"..
"\n"..
"On the web: https://github.com/joe7575/signs_bot/blob/master/manual_EN.md\n"..
"\n"..
"\n"..
"\n",
"After you have placed the Signs Bot Box\\, you can start the bot by means of the\n"..
"'On' button in the box menu. If the bot returns to its box right away\\,\n"..
"you will need to charge it with electrical energy (techage) first.\n"..
"The bot then runs straight ahead until it reaches an obstacle\n"..
"(a step with two or more blocks up or down or a sign.)\n"..
"\n"..
"The bot can only be controlled by signs that are placed in its path.\n"..
"\n"..
"If the bot first reaches a sign it will execute the commands on the sign.\n"..
"If the first command on the sign is e.g. 'turn_around'\\, the bot turns and goes back.\n"..
"In this case\\, the bot reaches his box again and turns off.\n"..
"\n"..
"If the bot first reaches an obstacle it will stop\\, or if available\\, execute\n"..
"the next commands from the last sign.\n"..
"\n"..
"The Signs Bot Box has an inventory with 6 stacks for signs and 8 stacks for\n"..
"other items (which are placed/mined by the bot). This inventory simulates the bot\n"..
"internal inventory. That means you will only have access to the inventory\n"..
"if the bot is turned off ('sitting' in his box).\n"..
"\n"..
"There are also the following blocks:\n"..
"\n"..
" - Sensors: These can send a signal to an actuator if they are connected to the actuator.\n"..
" - Actuators: These perform an action when they receive a signal from a sensor.\n"..
"\n"..
"\n"..
"\n",
"You control the direction of the bot using the \"turn left\" and\n"..
"\"turn right\" signs (signs with the arrow). The bot can run over steps\n"..
"(one block up/down). But there are also commands to move the bot up and down.\n"..
"\n"..
"It is not necessary to mark a way back to the box. With the command 'turn_off'\n"..
"the bot will turn off and be back in his box from every position. The same applies\n"..
"if you turn off the bot by the box menu. If the bot reaches a sign from the wrong\n"..
"direction (from back or sides) the sign will be ignored.\n"..
"The bot will simply step over the sign.\n"..
"\n"..
"All predefined signs have a menu with a list of the bot commands. These signs\n"..
"can't be changed\\, but you can craft and program your own signs. For this you\n"..
"have to use the 'command' sign. This sign has an edit field for your commands\n"..
"and a help page with all available commands. The help page has a copy button\n"..
"to simplify the programming.\n"..
"\n"..
"Also for your own signs it is important to know: After the execution of the last\n"..
"command of the sign\\, the bot falls back into its default behaviour and runs in\n"..
"its taken direction.\n"..
"\n"..
"A standard job for the bot is to move items from one chest to another chest\n"..
"(or node with a chest like inventory). This can be done by means of the two signs\n"..
"'take item' and 'add item'. These signs have to be placed on top of chest nodes.\n"..
"\n"..
"\n"..
"\n",
"In addition to the signs the bot can be controlled by means of sensors. Sensors\n"..
"like the Bot Sensor have two states: on and off. If the Bot Sensor detects a bot\n"..
"it will switch to the state 'on' and sends a signal to a connected block\\,\n"..
"called an actuator.\n"..
"\n"..
"Sensors are:\n"..
"\n"..
" - Bot Sensor: Sends a signal when a robot passes by\n"..
" - Node Sensor: Sends a signal when it detects any (new) node\n"..
" - Crop Sensor: Sends a signal when\\, for example wheat is fully grown\n"..
" - Bot Chest: Sends a signal depending on the chest state (empty\\, full)\n"..
"\n"..
"Actuators are:\n"..
"\n"..
" - Signs Bot Box: Can turn the bot off and on\n"..
" - Control Unit: Can be used to exchange the sign to lead the bot\n"..
"\n"..
"Sensors must be connected (paired) with actuators. This is what the\n"..
"\"Sensor Connection Tool\" does.\n"..
"\n"..
"\n"..
"\n",
"To send a signal from a sensor to an actuator\\, the sensor has to be connected\n"..
"(paired) with actuator. To connect sensor and actuator\\, the Sensor Connection Tool\n"..
"has to be used. Simply click with the tool on both blocks and the sensor will be\n"..
"connected with the actuator. A successful connection is indicated by a ping/pong noise.\n"..
"\n"..
"Before you connect sensor with actuator\\, take care that the actuator is in the\n"..
"requested state. For example: If you want to start the Bot with a sensor\\, connect\n"..
"the sensor with the Bot Box\\, when the Bot is in the state 'on'. Otherwise the sensor\n"..
"signal will stop the Bot\\, instead of starting it.\n"..
"\n"..
"\n"..
"\n",
"The following applies to all commands that place items/items in the bot inventory\\, such as:\n"..
"\n"..
" - 'take_item <num> <slot>'\n"..
" - 'pickup_items <slot>'\n"..
" - 'trash_sign <slot>'\n"..
" - 'harvest <slot>'\n"..
" - 'dig_front <slot> <lvl>'\n"..
"\n"..
"If no slot or slot 0 was specified with the command (case A)\\, all 8 slots of the bot\n"..
"inventory are checked one after the other. If a slot was specified (case B)\\,\n"..
"only this slot is checked. \n"..
"In both cases the following applies: \n"..
"\n"..
"If the slot is preconfigured and matches the item\\, or if the slot is unconfigured\n"..
"and empty\\, or only partially filled with the item type to be added\\, \n"..
"then the item(s) will be added.\n"..
"If not all items can be added\\, in case A the remaining slots are tried. \n"..
"Anything that couldn't be added to your inventory will go back or be dropped.\n"..
"Preconfigured slots are filled first before empty slots are used.\n"..
"\n"..
"The following applies to all commands that are used to take items from the bot inventory\\, like:\n"..
"\n"..
" - 'add_item <num> <slot>'\n"..
"\n"..
"It doesn't matter whether a slot is configured or not. The bot takes the first stack\n"..
"that it can find from its own inventory and tries to use it. If a slot is specified\\,\n"..
"it only takes this\\, if no slot has been specified\\, it checks all of them one after\n"..
"the other\\, starting from slot 1 until it finds something. If the number found is\n"..
"smaller than requested\\, he tries to take the rest out of any other slot.\n"..
"\n"..
"\n"..
"\n",
"",
"The Box is the housing of the bot. Place the box and start the bot by means of the\n"..
"'On' button. If the mod techage is installed\\, the bot needs electrical power.\n"..
"The bot leaves the box on the right side. It will not start\\, if this position is blocked.\n"..
"\n"..
"To stop and remove the bot\\, press the 'Off' button.\n"..
"The box inventory simulates the inventory of the bot.\n"..
"You will not be able to access the inventory\\, if the bot is running.\n"..
"The bot can carry up to 8 stacks and 6 signs with it.\n"..
"\n"..
"\n"..
"\n",
"The flap is a simple block used as door for the bot. Place the flap in any wall\\,\n"..
"and the bot will automatically open and close the flap as it passes through it.\n"..
"\n"..
"\n"..
"\n",
"The Duplicator can be used to make copies of signs:\n"..
"\n"..
" - Put one 'cmnd' sign to be used as template into the 'Template' inventory\n"..
" - Add one or several 'blank signs' to the 'Input' inventory.\n"..
" - Take the copies from the 'Output' inventory.\n"..
"\n"..
"Written books \\[default:book_written\\] can alternatively be used as template.\n"..
"Already written signs can be used as input\\, too.\n"..
"\n"..
"\n"..
"\n",
"The Bot Sensor detects any bot and sends a signal\\, if a bot is nearby.\n"..
"The sensor range is one node/meter.\" The sensor direction does not care.\n"..
"\n"..
"\n"..
"\n",
"The node sensor sends cyclical signals when it detects that nodes have appeared\n"..
"or disappeared\\, but has to be configured accordingly. Valid nodes are all kind\n"..
"of blocks and plants. The sensor range is 3 nodes/meters in one direction.\n"..
"The sensor has an active side (red) that must point to the observed area.\n"..
"\n"..
"\n"..
"\n",
"The Crop Sensor sends cyclical signals when\\, for example\\, wheat is fully grown.\n"..
"The sensor range is one node/meter. The sensor has an active side (red) that\n"..
"must point to the crop/field.\n"..
"\n"..
"\n"..
"\n",
"The Signs Bot Chest is a special chest with sensor function. It sends a signal\n"..
"depending on the chest state. Possible states are 'empty'\\, 'not empty'\\, 'almost full'\n"..
"\n"..
"A typical use case is to turn off the bot\\, when the chest is almost full or empty.\n"..
"\n"..
"\n"..
"\n",
"This is a special kind of sensor. Can be programmed with a time in seconds\\,\n"..
"e.g. to start the bot cyclically.\n"..
"\n"..
"\n"..
"\n",
"The Bot Control Unit is used to lead the bot by means of signs. The unit can be\n"..
"loaded with up to 4 different signs and can be programmed by means of sensors.\n"..
"\n"..
"To load the unit\\, place a sign on the red side of the unit and click on the unit.\n"..
"The sign disappears / is moved to the inventory of the unit.\n"..
"This can be repeated 3 times.\n"..
"\n"..
"Use the connection tool to connect up to 4 sensors with the Bot Control Unit.\n"..
"\n"..
"\n"..
"\n",
"With the Sensor Extender\\, sensor signals can be sent to more than one actuator.\n"..
"Place one or more extender nearby the sensor and connect each extender with one\n"..
"further actuator by means of the Connection Tool.\n"..
"\n"..
"\n"..
"\n",
"To combine multiple signals\\, use the Signal AND block.\n"..
"This only sends a signal when all input signals have been received.\n"..
"(logical AND). The block has any number of inputs and 1 output.\n"..
"The block has three states\\, which it displays in color:\n"..
"\n"..
" - Black '&' symbol: There is no input signal\n"..
" - Blue '&' symbol: There is at least one input signal\\, but not all of them\n"..
" - Red '&' symbol: All input signals are present\\, the block sends a signal\nand deletes the input signals so that it displays a black symbol again.\n"..
"\n"..
"The block only works if the output actuator is not in the state\n"..
"that it should be set to by the signal.\n"..
"For example\\, if a bot is already on the move and the AND block is programmed so\n"..
"that the bot should start\\, input signals are not accepted.\n"..
"\n"..
"By hitting the block\\, any input signals present are deleted so that the block\n"..
"returns to its initial state.\n"..
"\n"..
"\n"..
"\n",
"Signals are forwarded delayed. Subsequent signals are queued. \n"..
"The delay time can be configured.\n"..
"\n"..
"\n"..
"\n",
"Used to harvest and seed a 3x3 field. Place the sign in front of the field.\n"..
"The seed used must be in the first slot of the bot inventory.\n"..
"When the bot is done\\, the bot will turn and walk back.\n"..
"\n"..
"\n"..
"\n",
"Used to make a copy of a 3x3x3 cube. Place the sign in front of the pattern\n"..
"to be copied. Use the copy sign to make the copy of this pattern on a different\n"..
"location. The bot must first reach the pattern sign\\, then the copy sign.\n"..
"\n"..
"Used to make a copy of a 3x3x3 cube. Place the shield in front of the blocks\n"..
"to be copied. Use the copy sign to make the copy of these blocks in another\n"..
"location. The bot must first process the \"pattern\" sign\\, only then can the bot\n"..
"be directed to the copy sign.\n"..
"\n"..
"\n"..
"\n",
"Used to make a copy of a 3x3x3 cube. Place the sign in front of where you want\n"..
"the copy to be made. See also sign \"pattern\".\n"..
"\n"..
"\n"..
"\n",
"Used to cut flowers on a 3x3 field. Place the sign in front of the field.\n"..
"When finished\\, the bot turns.\n"..
"\n"..
"\n"..
"\n",
"Used to harvest an aspen or pine tree trunk\n"..
"\n"..
" - Place the sign in front of the tree.\n"..
" - Place a chest to the right of the sign.\n"..
" - Put a dirt stack (10 items min.) into the chest.\n"..
" - Preconfigure slot 1 of the bot inventory with dirt\n"..
" - Preconfigure slot 2 of the bot inventory with saplings\n"..
"\n"..
"\n"..
"\n",
"The 'command' sign can be programmed by the player. Place the sign in front\n"..
"of you and use the node menu to program your sequence of bot commands.\n"..
"The menu has an edit field for your commands and a help page with all\n"..
"available commands. The help page has a copy button to simplify the programming.\n"..
"\n"..
"\n"..
"\n",
"The Bot turns right when it detects this sign in front of it.\n"..
"\n"..
"\n"..
"\n",
"The Bot turns left when it detects this sign in front of it.\n"..
"\n"..
"\n"..
"\n",
"The Bot takes items out of a chest in front of it and then turns around.\n"..
"This sign has to be placed on top of the chest.\n"..
"\n"..
"\n"..
"\n",
"The Bot puts items into a chest in front of it and then turns around.\n"..
"This sign has to be placed on top of the chest.\n"..
"\n"..
"\n"..
"\n",
"The Bot will stop in front of this sign until the sign is removed or\n"..
"the bot is turned off.\n"..
"\n"..
"\n"..
"\n",
"The Bot puts items into a minecart in front of it\\, pushes the cart and then turns\n"..
"around. This sign has to be placed on top of the rail at the cart end position.\n"..
"\n"..
"\n"..
"\n",
"The Bot takes items out of a minecart in front of it\\, pushes the cart and then\n"..
"turns around. This sign has to be placed on top of the rail at the cart end position.\n"..
"\n"..
"\n"..
"\n",
"Used to take water into bucket. Place the sign on a shore\\, in front of the still water pool. \n"..
"\n"..
"Items in slots:\n"..
"\n"..
" 1 - empty bucket\n"..
"\n"..
"The result is one bucket with water in selected inventory slot. When finished\\,\n"..
"the bot turns around.\n"..
"\n"..
"\n"..
"\n",
"Used to cook a vegetable soup in cauldron. Cauldon should be empty and located\n"..
"above flammable material. Place the sign in front of the cauldron with one field\n"..
"space\\, to prevent wooden sign from catching fire.\n"..
"\n"..
"Items in slots:\n"..
"\n"..
" 1 - water bucket\"\n"..
" 2 - vegetable #1 (i.e. tomato)\n"..
" 3 - vegetable #2 (i.e. carrot)\n"..
" 4 - empty bowl (from farming or xdecor mods)\n"..
"\n"..
" The result is one bowl with vegetable soup in selected inventory slot.\n"..
"When finished\\, the bot turns around.\n"..
"\n"..
"\n"..
"\n",
"The commands are also all described as help in the \"Sign command\" node.\n"..
"All blocks or signs that are set are taken from the bot inventory.\n"..
"Any blocks or signs removed will be added back to the Bot Inventory.\n"..
"'<slot>' is always the bot internal inventory stack (1..8).\n"..
"\n"..
" move <steps> - go one or more steps forward\n"..
" cond_move - go to the nearest obstacle or sign\n"..
" turn_left - turn left\n"..
" turn_right - turn right\n"..
" turn_around - turn around\n"..
" backward - take a step back\n"..
" turn_off - turn off the robot / back to the box\n"..
" pause <sec> - wait one or more seconds\n"..
" move_up - move up (maximum 2 times)\n"..
" move_down - move down\n"..
" fall_down - fall into a hole/chasm (up to 10 blocks)\n"..
" take_item <num> <slot> - take one or more items from a box\n"..
" add_item <num> <slot> - put one or more items in a box\n"..
" add_fuel <num> <slot> - put fuel in a furnace\n"..
" place_front <slot> <lvl> - place the block in front of the bot\n"..
" place_left <slot> <lvl> - place the block to the left of the bot\n"..
" place_right <slot> <lvl> - place the block to the right of the bot\n"..
" place_below <slot> - lift the robot and put the block under the robot\n"..
" place_above <slot> - set block above the robot\n"..
" dig_front <slot> <lvl> - remove block in front of the robot\n"..
" dig_left <slot> <lvl> - remove block on the left\n"..
" dig_right <slot> <lvl> - remove block on the right\n"..
" dig_below <slot> - remove block under the robot\n"..
" dig_above <slot> - remove block above the robot\n"..
" rotate_item <lvl> <steps> - rotate a block in front of the robot\n"..
" set_param2 <lvl> <param2> - set param2 of the block in front of the robot\n"..
" place_sign <slot> - set sign\n"..
" place_sign_behind <slot> - put a sign behind the bot\n"..
" dig_sign <slot> - remove the sign\n"..
" trash_sign <slot> - Remove the sign\\, clear data and add to the item Inventory\n"..
" stop - Bot stops until the shield is removed\n"..
" pickup_items <slot> - pickup items (in a 3x3 field)\n"..
" drop_items <num> <slot> - drop items\n"..
" harvest - harvest a 3x3 field (farming)\n"..
" cutting - cut flowers in a 3x3 field\n"..
" sow_seed <slot> - see/plant a 3x3 field\n"..
" plant_sapling <slot> - plant a sapling in front of the robot\n"..
" pattern - save the block properties behind the sign (3x3x3 cube) as a template\n"..
" copy <size> - make a 3x3x3 copy of the stored template\n"..
" punch_cart - bump a mine cart\n"..
" add_compost <slot> - Put 2 leaves into the compost barrel\n"..
" take_compost <slot> - Take a compost item from the barrel\n"..
" print <text> - Output chat message for debug purposes\n"..
" take_water <slot> - Take water with empty bucket\n"..
" fill_cauldron <slot> - Fill the xdecor cauldron for a soup\n"..
" take_soup <slot> - Take boiling soup into empty bowl from cauldron\n"..
" flame_on - Make fire\n"..
" flame_off - Put out the fire\n"..
"\n"..
"\n"..
"\n",
" ignite - Ignite the techage charcoal lighter\n"..
" low_batt <percent> - Turn the bot off if the battery power is below the \n"..
" given value in percent (1..99)\n"..
" jump_low_batt <percent> <label> - Jump to <label> if the battery power is below the \n"..
" given value in percent (1..99)\n"..
" (see \"Flow control commands\")\n"..
" send_cmnd <receiver> <command> - Send a techage command to a given node. \n"..
" Receiver is addressed by the techage node number. \n"..
" For commands with two or more words\\, \n"..
" use the '*' character instead of spaces\\, e.g.: \n"..
" send_cmnd 3465 pull*default:dirt*2\n"..
"\n"..
"\n"..
"\n",
" -- jump command\\, <label> is a word from the characters a-z or A-Z\n"..
" jump <label>\n"..
" \n"..
" -- jump label / start of a function\n"..
" <label>:\n"..
" \n"..
" -- return from a function\n"..
" return\n"..
" \n"..
" -- start of a loop block\\, <num> is a number 1..999\n"..
" repeat <num>\n"..
" \n"..
" -- end of a loop block\n"..
" end\n"..
" \n"..
" -- call of a function (with return via the command 'return')\n"..
" call <label>\n"..
"\n"..
"\n"..
"\n",
" -- Check if there are <num> items in the chest like node.\n"..
" -- If not\\, jump to <label>.\n"..
" -- <slot> is the bot inventory slot (1..8) to\n"..
" -- specify the item\\, or 0 for any item.\n"..
" jump_check_item <num> <slot> <label>\n"..
" \n"..
" -- See \"Techage specific commands\"\n"..
" jump_low_batt <percent> <label>\n"..
"\n"..
"\n"..
"\n",
"",
" -- jump to the label 'main'\n"..
" jump main\n"..
" \n"..
" -- starting point of the function with the name 'foo'\n"..
" foo:\n"..
" cmnd ...\n"..
" cmnd ...\n"..
" -- end of 'foo'. Jump back\n"..
" return\n"..
" \n"..
" -- main program\n"..
" main:\n"..
" cmnd ...\n"..
" -- repeat all commands up to 'end' 10 times\n"..
" repeat 10\n"..
" cmnd ...\n"..
" -- call the subfunction 'foo'\n"..
" call foo\n"..
" cmnd ...\n"..
" -- end of the 'repeat' loop\n"..
" end\n"..
" -- end of the program\n"..
" exit\n"..
"\n",
" cmnd ...\n"..
" -- repeat all commands up to 'end' 10 times\n"..
" repeat 10\n"..
" cmnd ...\n"..
" -- call the subfunction 'foo'\n"..
" call foo\n"..
" cmnd ...\n"..
" -- end of the 'repeat' loop\n"..
" end\n"..
" -- end of the program\n"..
" exit\n"..
" \n"..
" -- starting point of the function with the name 'foo'\n"..
" foo:\n"..
" cmnd ...\n"..
" cmnd ...\n"..
" -- end of 'foo'. Jump back\n"..
" return\n"..
"\n",
},
images = {
"signs_bot_bot_inv.png",
"signs_bot_bot_inv.png",
"signs_bot_sign_left.png",
"signs_bot_sensor_crop_inv.png",
"signs_bot_tool.png",
"signs_bot:box",
"",
"signs_bot:box",
"signs_bot:bot_flap",
"signs_bot:duplicator",
"signs_bot:bot_sensor",
"signs_bot:node_sensor",
"signs_bot:crop_sensor",
"signs_bot:chest",
"signs_bot:timer",
"signs_bot:changer1",
"signs_bot:sensor_extender",
"signs_bot:and1",
"signs_bot:delayer",
"signs_bot:farming",
"signs_bot:pattern",
"signs_bot:copy3x3x3",
"signs_bot:flowers",
"signs_bot:aspen",
"signs_bot:sign_cmnd",
"signs_bot:sign_right",
"signs_bot:sign_left",
"signs_bot:sign_take",
"signs_bot:sign_add",
"signs_bot:sign_stop",
"signs_bot:sign_add_cart",
"signs_bot:sign_take_cart",
"signs_bot:water",
"signs_bot:soup",
"signs_bot_bot_inv.png",
"signs_bot_bot_inv.png",
"signs_bot_bot_inv.png",
"signs_bot_bot_inv.png",
"",
"",
"",
},
plans = {
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
}
}
+559
View File
@@ -0,0 +1,559 @@
# Signs Bot
A robot controlled by signs.
On the web: https://github.com/joe7575/signs_bot/blob/master/manual_EN.md
[signs_bot_bot_inv.png|image]
## Firt Steps
After you have placed the Signs Bot Box, you can start the bot by means of the
'On' button in the box menu. If the bot returns to its box right away,
you will need to charge it with electrical energy (techage) first.
The bot then runs straight ahead until it reaches an obstacle
(a step with two or more blocks up or down or a sign.)
The bot can only be controlled by signs that are placed in its path.
If the bot first reaches a sign it will execute the commands on the sign.
If the first command on the sign is e.g. 'turn_around', the bot turns and goes back.
In this case, the bot reaches his box again and turns off.
If the bot first reaches an obstacle it will stop, or if available, execute
the next commands from the last sign.
The Signs Bot Box has an inventory with 6 stacks for signs and 8 stacks for
other items (which are placed/mined by the bot). This inventory simulates the bot
internal inventory. That means you will only have access to the inventory
if the bot is turned off ('sitting' in his box).
There are also the following blocks:
- Sensors: These can send a signal to an actuator if they are connected to the actuator.
- Actuators: These perform an action when they receive a signal from a sensor.
[signs_bot_bot_inv.png|image]
## Signs
You control the direction of the bot using the "turn left" and
"turn right" signs (signs with the arrow). The bot can run over steps
(one block up/down). But there are also commands to move the bot up and down.
It is not necessary to mark a way back to the box. With the command 'turn_off'
the bot will turn off and be back in his box from every position. The same applies
if you turn off the bot by the box menu. If the bot reaches a sign from the wrong
direction (from back or sides) the sign will be ignored.
The bot will simply step over the sign.
All predefined signs have a menu with a list of the bot commands. These signs
can't be changed, but you can craft and program your own signs. For this you
have to use the 'command' sign. This sign has an edit field for your commands
and a help page with all available commands. The help page has a copy button
to simplify the programming.
Also for your own signs it is important to know: After the execution of the last
command of the sign, the bot falls back into its default behaviour and runs in
its taken direction.
A standard job for the bot is to move items from one chest to another chest
(or node with a chest like inventory). This can be done by means of the two signs
'take item' and 'add item'. These signs have to be placed on top of chest nodes.
[signs_bot_sign_left.png|image]
## Sensors and Actuators
In addition to the signs the bot can be controlled by means of sensors. Sensors
like the Bot Sensor have two states: on and off. If the Bot Sensor detects a bot
it will switch to the state 'on' and sends a signal to a connected block,
called an actuator.
Sensors are:
- Bot Sensor: Sends a signal when a robot passes by
- Node Sensor: Sends a signal when it detects any (new) node
- Crop Sensor: Sends a signal when, for example wheat is fully grown
- Bot Chest: Sends a signal depending on the chest state (empty, full)
Actuators are:
- Signs Bot Box: Can turn the bot off and on
- Control Unit: Can be used to exchange the sign to lead the bot
Sensors must be connected (paired) with actuators. This is what the
"Sensor Connection Tool" does.
[signs_bot_sensor_crop_inv.png|image]
## Sensor Connection Tool
To send a signal from a sensor to an actuator, the sensor has to be connected
(paired) with actuator. To connect sensor and actuator, the Sensor Connection Tool
has to be used. Simply click with the tool on both blocks and the sensor will be
connected with the actuator. A successful connection is indicated by a ping/pong noise.
Before you connect sensor with actuator, take care that the actuator is in the
requested state. For example: If you want to start the Bot with a sensor, connect
the sensor with the Bot Box, when the Bot is in the state 'on'. Otherwise the sensor
signal will stop the Bot, instead of starting it.
[signs_bot_tool.png|image]
## Inventory
The following applies to all commands that place items/items in the bot inventory, such as:
- `take_item <num> <slot>`
- `pickup_items <slot>`
- `trash_sign <slot>`
- `harvest <slot>`
- `dig_front <slot> <lvl>`
If no slot or slot 0 was specified with the command (case A), all 8 slots of the bot
inventory are checked one after the other. If a slot was specified (case B),
only this slot is checked.
In both cases the following applies:
If the slot is preconfigured and matches the item, or if the slot is unconfigured
and empty, or only partially filled with the item type to be added,
then the item(s) will be added.
If not all items can be added, in case A the remaining slots are tried.
Anything that couldn't be added to your inventory will go back or be dropped.
Preconfigured slots are filled first before empty slots are used.
The following applies to all commands that are used to take items from the bot inventory, like:
- `add_item <num> <slot>`
It doesn't matter whether a slot is configured or not. The bot takes the first stack
that it can find from its own inventory and tries to use it. If a slot is specified,
it only takes this, if no slot has been specified, it checks all of them one after
the other, starting from slot 1 until it finds something. If the number found is
smaller than requested, he tries to take the rest out of any other slot.
[signs_bot:box|image]
## Nodes / Blocks
### Signs Bot Box
The Box is the housing of the bot. Place the box and start the bot by means of the
'On' button. If the mod techage is installed, the bot needs electrical power.
The bot leaves the box on the right side. It will not start, if this position is blocked.
To stop and remove the bot, press the 'Off' button.
The box inventory simulates the inventory of the bot.
You will not be able to access the inventory, if the bot is running.
The bot can carry up to 8 stacks and 6 signs with it.
[signs_bot:box|image]
### Bot Flap
The flap is a simple block used as door for the bot. Place the flap in any wall,
and the bot will automatically open and close the flap as it passes through it.
[signs_bot:bot_flap|image]
### Signs Duplicator
The Duplicator can be used to make copies of signs:
1. Put one 'cmnd' sign to be used as template into the 'Template' inventory
2. Add one or several 'blank signs' to the 'Input' inventory.
3. Take the copies from the 'Output' inventory.
Written books [default:book_written] can alternatively be used as template.
Already written signs can be used as input, too.
[signs_bot:duplicator|image]
### Bot Sensor
The Bot Sensor detects any bot and sends a signal, if a bot is nearby.
The sensor range is one node/meter." The sensor direction does not care.
[signs_bot:bot_sensor|image]
### Node Sensor
The node sensor sends cyclical signals when it detects that nodes have appeared
or disappeared, but has to be configured accordingly. Valid nodes are all kind
of blocks and plants. The sensor range is 3 nodes/meters in one direction.
The sensor has an active side (red) that must point to the observed area.
[signs_bot:node_sensor|image]
### Crop Sensor
The Crop Sensor sends cyclical signals when, for example, wheat is fully grown.
The sensor range is one node/meter. The sensor has an active side (red) that
must point to the crop/field.
[signs_bot:crop_sensor|image]
### Signs Bot Chest
The Signs Bot Chest is a special chest with sensor function. It sends a signal
depending on the chest state. Possible states are 'empty', 'not empty', 'almost full'
A typical use case is to turn off the bot, when the chest is almost full or empty.
[signs_bot:chest|image]
### Bot Timer
This is a special kind of sensor. Can be programmed with a time in seconds,
e.g. to start the bot cyclically.
[signs_bot:timer|image]
### Bot Control Unit
The Bot Control Unit is used to lead the bot by means of signs. The unit can be
loaded with up to 4 different signs and can be programmed by means of sensors.
To load the unit, place a sign on the red side of the unit and click on the unit.
The sign disappears / is moved to the inventory of the unit.
This can be repeated 3 times.
Use the connection tool to connect up to 4 sensors with the Bot Control Unit.
[signs_bot:changer1|image]
### Sensor Extender
With the Sensor Extender, sensor signals can be sent to more than one actuator.
Place one or more extender nearby the sensor and connect each extender with one
further actuator by means of the Connection Tool.
[signs_bot:sensor_extender|image]
### Signal AND
To combine multiple signals, use the Signal AND block.
This only sends a signal when all input signals have been received.
(logical AND). The block has any number of inputs and 1 output.
The block has three states, which it displays in color:
- Black '&' symbol: There is no input signal
- Blue '&' symbol: There is at least one input signal, but not all of them
- Red '&' symbol: All input signals are present, the block sends a signal
and deletes the input signals so that it displays a black symbol again.
The block only works if the output actuator is not in the state
that it should be set to by the signal.
For example, if a bot is already on the move and the AND block is programmed so
that the bot should start, input signals are not accepted.
By hitting the block, any input signals present are deleted so that the block
returns to its initial state.
[signs_bot:and1|image]
### Signal Delayer
Signals are forwarded delayed. Subsequent signals are queued.
The delay time can be configured.
[signs_bot:delayer|image]
### Sign 'farming'
Used to harvest and seed a 3x3 field. Place the sign in front of the field.
The seed used must be in the first slot of the bot inventory.
When the bot is done, the bot will turn and walk back.
[signs_bot:farming|image]
### Sign 'pattern'
Used to make a copy of a 3x3x3 cube. Place the sign in front of the pattern
to be copied. Use the copy sign to make the copy of this pattern on a different
location. The bot must first reach the pattern sign, then the copy sign.
Used to make a copy of a 3x3x3 cube. Place the shield in front of the blocks
to be copied. Use the copy sign to make the copy of these blocks in another
location. The bot must first process the "pattern" sign, only then can the bot
be directed to the copy sign.
[signs_bot:pattern|image]
### Sign 'copy3x3x3'
Used to make a copy of a 3x3x3 cube. Place the sign in front of where you want
the copy to be made. See also sign "pattern".
[signs_bot:copy3x3x3|image]
### Sign 'flowers'
Used to cut flowers on a 3x3 field. Place the sign in front of the field.
When finished, the bot turns.
[signs_bot:flowers|image]
### Sign 'aspen'
Used to harvest an aspen or pine tree trunk
- Place the sign in front of the tree.
- Place a chest to the right of the sign.
- Put a dirt stack (10 items min.) into the chest.
- Preconfigure slot 1 of the bot inventory with dirt
- Preconfigure slot 2 of the bot inventory with saplings
[signs_bot:aspen|image]
### Sign 'command'
The 'command' sign can be programmed by the player. Place the sign in front
of you and use the node menu to program your sequence of bot commands.
The menu has an edit field for your commands and a help page with all
available commands. The help page has a copy button to simplify the programming.
[signs_bot:sign_cmnd|image]
### Sign "turn right"
The Bot turns right when it detects this sign in front of it.
[signs_bot:sign_right|image]
### Sign "turn left"
The Bot turns left when it detects this sign in front of it.
[signs_bot:sign_left|image]
### Sign "take item"
The Bot takes items out of a chest in front of it and then turns around.
This sign has to be placed on top of the chest.
[signs_bot:sign_take|image]
### Sign "add item"
The Bot puts items into a chest in front of it and then turns around.
This sign has to be placed on top of the chest.
[signs_bot:sign_add|image]
### Sign "stop"
The Bot will stop in front of this sign until the sign is removed or
the bot is turned off.
[signs_bot:sign_stop|image]
### Sign "add to cart" (minecart)
The Bot puts items into a minecart in front of it, pushes the cart and then turns
around. This sign has to be placed on top of the rail at the cart end position.
[signs_bot:sign_add_cart|image]
### Sign "take from cart" (minecart)
The Bot takes items out of a minecart in front of it, pushes the cart and then
turns around. This sign has to be placed on top of the rail at the cart end position.
[signs_bot:sign_take_cart|image]
### Sign 'take water' (xdecor)
Used to take water into bucket. Place the sign on a shore, in front of the still water pool.
Items in slots:
1 - empty bucket
The result is one bucket with water in selected inventory slot. When finished,
the bot turns around.
[signs_bot:water|image]
### Sign 'cook soup' (xdecor)
Used to cook a vegetable soup in cauldron. Cauldon should be empty and located
above flammable material. Place the sign in front of the cauldron with one field
space, to prevent wooden sign from catching fire.
Items in slots:
1 - water bucket"
2 - vegetable #1 (i.e. tomato)
3 - vegetable #2 (i.e. carrot)
4 - empty bowl (from farming or xdecor mods)
The result is one bowl with vegetable soup in selected inventory slot.
When finished, the bot turns around.
[signs_bot:soup|image]
## Bot Commands
The commands are also all described as help in the "Sign command" node.
All blocks or signs that are set are taken from the bot inventory.
Any blocks or signs removed will be added back to the Bot Inventory.
`<slot>` is always the bot internal inventory stack (1..8).
move <steps> - go one or more steps forward
cond_move - go to the nearest obstacle or sign
turn_left - turn left
turn_right - turn right
turn_around - turn around
backward - take a step back
turn_off - turn off the robot / back to the box
pause <sec> - wait one or more seconds
move_up - move up (maximum 2 times)
move_down - move down
fall_down - fall into a hole/chasm (up to 10 blocks)
take_item <num> <slot> - take one or more items from a box
add_item <num> <slot> - put one or more items in a box
add_fuel <num> <slot> - put fuel in a furnace
place_front <slot> <lvl> - place the block in front of the bot
place_left <slot> <lvl> - place the block to the left of the bot
place_right <slot> <lvl> - place the block to the right of the bot
place_below <slot> - lift the robot and put the block under the robot
place_above <slot> - set block above the robot
dig_front <slot> <lvl> - remove block in front of the robot
dig_left <slot> <lvl> - remove block on the left
dig_right <slot> <lvl> - remove block on the right
dig_below <slot> - remove block under the robot
dig_above <slot> - remove block above the robot
rotate_item <lvl> <steps> - rotate a block in front of the robot
set_param2 <lvl> <param2> - set param2 of the block in front of the robot
place_sign <slot> - set sign
place_sign_behind <slot> - put a sign behind the bot
dig_sign <slot> - remove the sign
trash_sign <slot> - Remove the sign, clear data and add to the item Inventory
stop - Bot stops until the shield is removed
pickup_items <slot> - pickup items (in a 3x3 field)
drop_items <num> <slot> - drop items
harvest - harvest a 3x3 field (farming)
cutting - cut flowers in a 3x3 field
sow_seed <slot> - see/plant a 3x3 field
plant_sapling <slot> - plant a sapling in front of the robot
pattern - save the block properties behind the sign (3x3x3 cube) as a template
copy <size> - make a 3x3x3 copy of the stored template
punch_cart - bump a mine cart
add_compost <slot> - Put 2 leaves into the compost barrel
take_compost <slot> - Take a compost item from the barrel
print <text> - Output chat message for debug purposes
take_water <slot> - Take water with empty bucket
fill_cauldron <slot> - Fill the xdecor cauldron for a soup
take_soup <slot> - Take boiling soup into empty bowl from cauldron
flame_on - Make fire
flame_off - Put out the fire
[signs_bot_bot_inv.png|image]
### Techage specific commands
ignite - Ignite the techage charcoal lighter
low_batt <percent> - Turn the bot off if the battery power is below the
given value in percent (1..99)
jump_low_batt <percent> <label> - Jump to <label> if the battery power is below the
given value in percent (1..99)
(see "Flow control commands")
send_cmnd <receiver> <command> - Send a techage command to a given node.
Receiver is addressed by the techage node number.
For commands with two or more words,
use the '*' character instead of spaces, e.g.:
send_cmnd 3465 pull*default:dirt*2
[signs_bot_bot_inv.png|image]
### Flow control commands
-- jump command, <label> is a word from the characters a-z or A-Z
jump <label>
-- jump label / start of a function
<label>:
-- return from a function
return
-- start of a loop block, <num> is a number 1..999
repeat <num>
-- end of a loop block
end
-- call of a function (with return via the command 'return')
call <label>
[signs_bot_bot_inv.png|image]
### Further jump commands
-- Check if there are <num> items in the chest like node.
-- If not, jump to <label>.
-- <slot> is the bot inventory slot (1..8) to
-- specify the item, or 0 for any item.
jump_check_item <num> <slot> <label>
-- See "Techage specific commands"
jump_low_batt <percent> <label>
[signs_bot_bot_inv.png|image]
### Flow control Examples
#### Example with a function at the beginning:
-- jump to the label 'main'
jump main
-- starting point of the function with the name 'foo'
foo:
cmnd ...
cmnd ...
-- end of 'foo'. Jump back
return
-- main program
main:
cmnd ...
-- repeat all commands up to 'end' 10 times
repeat 10
cmnd ...
-- call the subfunction 'foo'
call foo
cmnd ...
-- end of the 'repeat' loop
end
-- end of the program
exit
#### Example with a function at the end:
cmnd ...
-- repeat all commands up to 'end' 10 times
repeat 10
cmnd ...
-- call the subfunction 'foo'
call foo
cmnd ...
-- end of the 'repeat' loop
end
-- end of the program
exit
-- starting point of the function with the name 'foo'
foo:
cmnd ...
cmnd ...
-- end of 'foo'. Jump back
return
@@ -0,0 +1,201 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# install mistune v0.8.4 with: pip install mistune==0.8.4
import re
import mistune
assert(mistune.__version__ == "0.8.4")
__version__ = "1.0"
class WikiLinkInlineLexer(mistune.InlineLexer):
def enable_wiki_link(self):
# add wiki_link rules
self.rules.wiki_link = re.compile(
r'\[' # [
r'([\s\S]+?\|[\s\S]+?)' # name| img-type
r'\](?!\])' # ]
)
# Add wiki_link parser to default rules
# you can insert it some place you like
# but place matters, maybe 3 is not good
self.default_rules.insert(3, 'wiki_link')
def output_wiki_link(self, m):
text = m.group(1)
name, itype = text.split('|')
# you can create an custom render
# you can also return the html if you like
return self.renderer.wiki_link(name, itype)
class MarkdownToLua(mistune.Renderer):
def __init__(self, *args, **kwargs):
mistune.Renderer.__init__(self, *args, **kwargs)
self.item_name = ""
self.plan_table = ""
self.is_first_header = True
self.text_chunck = []
self.lTitle = []
self.lText = []
self.lItemName = []
self.lPlanTable = []
print("Markdown-to-Lua v%s" % __version__)
def m2l_formspec_escape(self, text):
text = text.replace("\\", "")
text = text.replace("[", "\\\\[")
text = text.replace("]", "\\\\]")
text = text.replace(";", "\\\\;")
text = text.replace(",", "\\\\,")
text = text.replace('"', '\\"')
text = text.replace('\n', '\\n')
return text
def m2l_add_last_paragraph(self):
"""
Used to add a text block before the next header or at the end of the document
"""
self.lText.append(self.text_chunck)
self.text_chunck = []
self.lItemName.append(self.item_name)
self.item_name = ""
self.lPlanTable.append(self.plan_table)
self.plan_table = ""
##
## Block Level
##
def block_code(self, code, lang):
text = self.m2l_formspec_escape(code.strip())
lines = text.split("\\n")
lines = [" " + item for item in lines]
self.text_chunck.extend(lines)
self.text_chunck.append("")
return ""
def header(self, text, level, raw=None):
if not self.is_first_header:
self.m2l_add_last_paragraph()
self.is_first_header = False
self.lTitle.append("%u,%s" % (level, self.m2l_formspec_escape(text)))
return ""
def hrule(self):
self.text_chunck.append("\n----------------------------------------------------\n")
return ""
def paragraph(self, text):
lines = text.split("\\n") + [""]
self.text_chunck.extend(lines)
return ""
def list(self, body, ordered=True):
lines = body.split("\n")
self.text_chunck.extend(lines)
return ""
def list_item(self, text):
return " - %s\n" % text.strip()
##
## Span Level
##
def emphasis(self, text):
return "*%s*" % self.m2l_formspec_escape(text)
def double_emphasis(self, text):
return "*%s*" % self.m2l_formspec_escape(text)
def codespan(self, text):
return "'%s'" % self.m2l_formspec_escape(text)
def text(self, text):
return self.m2l_formspec_escape(text)
def link(self, link, title, content):
"""
Used for plans and images:
[myimage](/image/)
[myplan](/plan/)
"""
if link == "/image/":
self.item_name = content
elif link == "/plan/":
self.plan_table = content
return content
def wiki_link(self, name, itype):
"""
Used for plans and images:
[myimage|image]
[myplan|plan]
"""
if itype == "image":
self.item_name = name
elif itype == "plan":
self.plan_table = name
return ""
def autolink(self, link, is_email=False):
return link
def linebreak(self):
return "\\n"
def newline(self):
return "\\n"
def inline_html(self, text):
#print(text)
pass
def parse_md_file(self, src_name):
print(" - Read MD file '%s'" % src_name)
inline = WikiLinkInlineLexer(self)
# enable the feature
inline.enable_wiki_link()
md = mistune.Markdown(renderer=self, inline=inline)
md.renderer.src_name = src_name
md.render(open(src_name, 'r').read())
md.renderer.m2l_add_last_paragraph()
def lua_table(self, key, lData):
lOut = []
lOut.append(" %s = {" % key)
for line in lData:
lOut.append(' "%s",' % line)
lOut.append(" }")
return "\n".join(lOut)
def lua_text_table(self, key, lData):
lOut = []
lOut.append(" %s = {" % key)
for lines in lData:
for line in lines[:-1]:
line = line.replace('<br>', '\\n')
lOut.append(' "%s\\n"..' % line)
if len(lines) > 0:
lOut.append(' "%s\\n",' % lines[-1])
else:
lOut.append(' "",')
lOut.append(" }")
return "\n".join(lOut)
def gen_lua_file(self, dest_name):
print(" - Write Lua file '%s'" % dest_name)
lOut = []
s = ",\n".join([self.lua_table("titles", self.lTitle),
self.lua_text_table("texts", self.lText),
self.lua_table("images", self.lItemName),
self.lua_table("plans", self.lPlanTable)])
open(dest_name, "w").write("return {\n%s\n}" % s)
print("done.")
m2l = MarkdownToLua()
m2l.parse_md_file("./manual_EN.md")
m2l.gen_lua_file("./manual_EN.lua")
m2l = MarkdownToLua()
m2l.parse_md_file("./manual_DE.md")
m2l.gen_lua_file("./manual_DE.lua")
+6
View File
@@ -0,0 +1,6 @@
name=signs_bot
depends = default,farming,basic_materials,tubelib2
optional_depends = node_io,techage,doc,minecart,bucket,fire,xdecor,ethereal,compost,doclib,moretrees,shop
description = A robot controlled by signs
supported_games = minetest_game
@@ -0,0 +1,229 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Node Sensor
]]--
-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local M = minetest.get_meta
-- Load support for I18n.
local S = signs_bot.S
local lib = signs_bot.lib
local CYCLE_TIME = 4
local function update_infotext(pos, dest_pos, cmnd)
M(pos):set_string("infotext", S("Node Sensor: Connected with ")..P2S(dest_pos).." / "..cmnd)
end
local function swap_node(pos, name)
local node = minetest.get_node(pos)
if node.name == name then
return false
end
if string.sub(node.name, 1,21) == "signs_bot:node_sensor" then
node.name = name
minetest.swap_node(pos, node)
return true
end
return false
end
local DropdownValues = {
[S("added")] = 1,
[S("removed")] = 2,
[S("added or removed")] = 3,
}
local function formspec(mem)
local label = S("added")..","..S("removed")..","..S("added or removed")
return "size[6,3]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[0.2,0.4;"..S("Send signal if nodes have been:").."]"..
"dropdown[0.2,1;6,1;mode;"..label..";"..(mem.mode or 3).."]"..
"button_exit[1.5,2.2;3,1;accept;"..S("accept").."]"
end
local function any_node_changed(pos)
local mem = tubelib2.get_mem(pos)
if not mem.pos1 or not mem.pos2 or not mem.num then
local node = minetest.get_node(pos)
local param2 = (node.param2 + 2) % 4
mem.pos1 = lib.dest_pos(pos, param2, {0})
mem.pos2 = lib.dest_pos(pos, param2, {0,0,0})
mem.num = #minetest.find_nodes_in_area(mem.pos1, mem.pos2, {"air"})
return false
end
local num = #minetest.find_nodes_in_area(mem.pos1, mem.pos2, {"air"})
if mem.num ~= num then
if mem.mode == 1 and num < mem.num then
--mem.num = num
return true
elseif mem.mode == 2 and num > mem.num then
--mem.num = num
return true
elseif mem.mode == 3 then
mem.num = num
return true
end
mem.num = num
end
return false
end
local function on_receive_fields(pos, formname, fields, player)
local mem = tubelib2.get_mem(pos)
local meta = M(pos)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
if fields.accept then
mem.mode = DropdownValues[fields.mode] or 3
mem.num = nil
any_node_changed(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
swap_node(pos, "signs_bot:node_sensor")
end
meta:set_string("formspec", formspec(mem))
end
local function node_timer(pos)
if any_node_changed(pos)then
if swap_node(pos, "signs_bot:node_sensor_on") then
signs_bot.send_signal(pos)
signs_bot.lib.activate_extender_nodes(pos, true)
minetest.after(1, swap_node, pos, "signs_bot:node_sensor")
end
end
return true
end
minetest.register_node("signs_bot:node_sensor", {
description = S("Node Sensor"),
inventory_image = "signs_bot_sensor_node_inv.png",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor1.png^signs_bot_sensor_node.png",
"signs_bot_sensor1.png",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR180",
},
after_place_node = function(pos, placer)
local meta = M(pos)
local mem = tubelib2.init_mem(pos)
meta:set_string("infotext", S("Node Sensor: Not connected"))
mem.mode = 3 -- default legacy mode
meta:set_string("formspec", formspec(mem))
minetest.get_node_timer(pos):start(CYCLE_TIME)
any_node_changed(pos)
end,
on_timer = node_timer,
update_infotext = update_infotext,
on_receive_fields = on_receive_fields,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
is_ground_content = false,
groups = {sign_bot_sensor = 1, cracky = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("signs_bot:node_sensor_on", {
description = S("Node Sensor"),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
},
},
tiles = {
-- up, down, right, left, back, front
"signs_bot_sensor1.png^signs_bot_sensor_node_on.png",
"signs_bot_sensor1.png",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR90",
"signs_bot_sensor1.png^[transformFXR180",
},
on_timer = node_timer,
update_infotext = update_infotext,
on_receive_fields = on_receive_fields,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
is_ground_content = false,
drop = "signs_bot:node_sensor",
groups = {sign_bot_sensor = 1, cracky = 1, not_in_creative_inventory = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_craft({
output = "signs_bot:node_sensor",
recipe = {
{"", "", ""},
{"dye:black", "group:stone", "dye:grey"},
{"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"}
}
})
minetest.register_lbm({
label = "[signs_bot] Restart timer",
name = "signs_bot:node_sensor_restart",
nodenames = {"signs_bot:node_sensor", "signs_bot:node_sensor_on"},
run_at_every_load = true,
action = function(pos, node)
minetest.get_node_timer(pos):start(CYCLE_TIME)
if node.name == "signs_bot:node_sensor_on" then
signs_bot.send_signal(pos)
signs_bot.lib.activate_extender_nodes(pos, true)
end
end
})
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "node_sensor", {
name = S("Node Sensor"),
data = {
item = "signs_bot:node_sensor",
text = table.concat({
S("The node sensor sends cyclical signals when it detects that nodes have appeared or disappeared,"),
S("but has to be configured accordingly."),
S("Valid nodes are all kind of blocks and plants."),
S("The sensor range is 3 nodes/meters in one direction."),
S("The sensor has an active side (red) that must point to the observed area."),
}, "\n")
},
})
end
+128
View File
@@ -0,0 +1,128 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Node information tables for the Bot
]]--
signs_bot.FarmingSeed = {}
signs_bot.FarmingCrop = {}
signs_bot.FarmingNeedTrellis = {}
signs_bot.FarmingKeepTrellis = {}
signs_bot.TreeSaplings = {}
-- inv_seed is the seed inventory name
-- plantlet is what has to be placed on the ground (stage 1)
-- crop is the farming crop in the final stage
function signs_bot.register_farming_plant(inv_seed, plantlet, crop, trellis)
signs_bot.FarmingCrop[crop] = true
signs_bot.FarmingSeed[inv_seed] = plantlet
if trellis then
signs_bot.FarmingNeedTrellis[plantlet] = trellis
signs_bot.FarmingKeepTrellis[crop] = trellis
end
end
-- inv_sapling is the sapling inventory name
-- sapling is what has to be placed on the ground
-- t1/t2 is needed for trees which require the node timer
function signs_bot.register_tree_saplings(inv_sapling, sapling, t1, t2)
signs_bot.TreeSaplings[inv_sapling] = {sapling = sapling, t1 = t1 or 300, t2 = t2 or 1500}
end
local fp = signs_bot.register_farming_plant
local ts = signs_bot.register_tree_saplings
if farming.mod ~= "redo" then
fp("farming:seed_wheat", "farming:wheat_1", "farming:wheat_8")
fp("farming:seed_cotton", "farming:cotton_1", "farming:cotton_8")
end
-------------------------------------------------------------------------------
-- Farming Redo
-------------------------------------------------------------------------------
if farming.mod == "redo" then
local fp_grows = function(def, step)
local crop = def.crop .. "_" .. step
local node = minetest.registered_nodes[crop]
if node then
fp(def.seed, def.crop .. "_1", crop, def.trellis)
return node.groups and node.groups.growing
end
end
for name, def in pairs(farming.registered_plants) do
-- everything except cocoa (these can only be placed on jungletree)
if name ~= "farming:cocoa_beans" then
local step = def.steps
if step then
while fp_grows(def, step) do step = step + 1 end
end
end
end
end
-------------------------------------------------------------------------------
-- Default Trees
-------------------------------------------------------------------------------
ts("default:acacia_bush_sapling", "default:acacia_bush_sapling")
ts("default:acacia_sapling", "default:acacia_sapling")
ts("default:aspen_sapling", "default:aspen_sapling")
ts("default:blueberry_bush_sapling", "default:blueberry_bush_sapling")
ts("default:bush_sapling", "default:bush_sapling")
ts("default:emergent_jungle_sapling", "default:emergent_jungle_sapling")
ts("default:junglesapling", "default:junglesapling")
ts("default:pine_bush_sapling", "default:pine_bush_sapling")
ts("default:pine_sapling", "default:pine_sapling")
ts("default:sapling", "default:sapling")
-------------------------------------------------------------------------------
-- Ethereal (Saplings use ABM instead of NodeTimer)
-------------------------------------------------------------------------------
if minetest.get_modpath("ethereal") then
ts("ethereal:bamboo_sprout", "ethereal:bamboo_sprout", nil)
ts("ethereal:banana_tree_sapling", "ethereal:banana_tree_sapling", nil)
ts("ethereal:basandra_bush_sapling", "ethereal:basandra_bush_sapling", nil)
ts("ethereal:big_tree_sapling", "ethereal:big_tree_sapling", nil)
ts("ethereal:birch_sapling", "ethereal:birch_sapling", nil)
ts("ethereal:frost_tree_sapling", "ethereal:frost_tree_sapling", nil)
ts("ethereal:giant_redwood_sapling", "ethereal:giant_redwood_sapling", nil)
ts("ethereal:lemon_tree_sapling", "ethereal:lemon_tree_sapling", nil)
ts("ethereal:mushroom_brown_sapling", "ethereal:mushroom_brown_sapling", nil)
ts("ethereal:mushroom_sapling", "ethereal:mushroom_sapling", nil)
ts("ethereal:olive_tree_sapling", "ethereal:olive_tree_sapling", nil)
ts("ethereal:orange_tree_sapling", "ethereal:orange_tree_sapling", nil)
ts("ethereal:palm_sapling", "ethereal:palm_sapling", nil)
ts("ethereal:redwood_sapling", "ethereal:redwood_sapling", nil)
ts("ethereal:sakura_sapling", "ethereal:sakura_sapling", nil)
ts("ethereal:willow_sapling", "ethereal:willow_sapling", nil)
ts("ethereal:yellow_tree_sapling", "ethereal:yellow_tree_sapling", nil)
end
-------------------------------------------------------------------------------
-- Moretrees
-------------------------------------------------------------------------------
if minetest.get_modpath("moretrees") then
ts("moretrees:apple_tree_sapling", "moretrees:apple_tree_sapling")
ts("moretrees:beech_sapling", "moretrees:beech_sapling")
ts("moretrees:birch_sapling", "moretrees:birch_sapling")
ts("moretrees:cedar_sapling", "moretrees:cedar_sapling")
ts("moretrees:date_palm_sapling", "moretrees:date_palm_sapling")
ts("moretrees:fir_sapling", "moretrees:fir_sapling")
ts("moretrees:oak_sapling", "moretrees:oak_sapling")
ts("moretrees:palm_sapling", "moretrees:palm_sapling")
ts("moretrees:poplar_sapling", "moretrees:poplar_sapling")
ts("moretrees:poplar_small_sapling", "moretrees:poplar_small_sapling")
ts("moretrees:rubber_tree_sapling", "moretrees:rubber_tree_sapling")
ts("moretrees:sequoia_sapling", "moretrees:sequoia_sapling")
ts("moretrees:spruce_sapling", "moretrees:spruce_sapling")
ts("moretrees:willow_sapling", "moretrees:willow_sapling")
end
+72
View File
@@ -0,0 +1,72 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signs Bot: Random Series
]]--
-- generate a table of unique pseudo random numbers
local function gen_serie(inv_size)
local tbl = {}
local offs = inv_size/2
if offs % 2 == 1 then
offs = offs + 1
end
local index = 1
for n = 1, (inv_size*2) do
tbl[#tbl + 1] = index
index = ((index + offs) % inv_size) + 1
end
return tbl
end
-- Pseudo numbers series to iterate "randomly" through inventories
local InvRandomSeries = {}
local function get_serie(inv_size)
if not InvRandomSeries[inv_size] then
InvRandomSeries[inv_size] = gen_serie(inv_size)
end
return InvRandomSeries[inv_size]
end
local function random(inv_size)
local t = get_serie(inv_size)
local i = 0
local n = #t/2
local offs = math.random(n)
return function ()
i = i + 1
if i <= n then return t[i+offs] end
end
end
--
-- API
--
-- func signs_bot.random(inv_size)
-- random iterator providing 'inv_size' possible bot inventory stack numbers
-- inv_size must be even and between 4 and 64
signs_bot.random = random
--
-- Test
--
--for size = 4,64,2 do
-- local tbl = {}
-- for idx in random(size) do
-- tbl[#tbl + 1] = idx
-- end
-- print(table.concat(tbl, ", "))
--end
+145
View File
@@ -0,0 +1,145 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2023 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signs Bot: The Robot itself
]]--
local lib = signs_bot.lib
local enable_LBM = minetest.settings:get_bool("signs_bot_enable_LBM") == true
-- Called when robot is started
function signs_bot.place_robot(pos1, pos2, param2)
local node1 = tubelib2.get_node_lvm(pos1)
local node2 = tubelib2.get_node_lvm(pos2)
if lib.check_pos(pos1, node1, node2, param2) then
minetest.set_node(pos1, {name = "signs_bot:robot", param2 = param2})
end
end
-- Called when robot is removed
function signs_bot.remove_robot(mem)
local pos = mem.robot_pos
local node = tubelib2.get_node_lvm(pos)
if node.name == "signs_bot:robot" then
minetest.remove_node(pos)
local pos1 = {x=pos.x, y=pos.y-1, z=pos.z}
node = tubelib2.get_node_lvm(pos1)
if node.name == "signs_bot:robot_foot" or node.name == "signs_bot:robot_leg" then
if node.name == "signs_bot:robot_foot" then
lib.swap_node(pos1, mem.stored_node or {name = "air"})
else
minetest.remove_node(pos1)
end
pos1 = {x=pos.x, y=pos.y-2, z=pos.z}
node = tubelib2.get_node_lvm(pos1)
if node.name == "signs_bot:robot_foot" then
lib.swap_node(pos1, mem.stored_node or {name = "air"})
end
else
lib.swap_node(pos, mem.stored_node or {name = "air"})
end
end
end
minetest.register_node("signs_bot:robot", {
-- up, down, right, left, back, front
tiles = {
"signs_bot_robot_top.png",
"signs_bot_robot_bottom.png",
"signs_bot_robot_right.png",
"signs_bot_robot_left.png",
"signs_bot_robot_front.png",
"signs_bot_robot_back.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -5/16, 3/16, -5/16, 5/16, 8/16, 5/16},
{ -3/16, 2/16, -3/16, 3/16, 3/16, 3/16},
{ -6/16, -7/16, -6/16, 6/16, 2/16, 6/16},
{ -6/16, -8/16, -3/16, 6/16, -7/16, 3/16},
},
},
paramtype2 = "facedir",
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
is_ground_content = false,
drop = "",
groups = {cracky=1, not_in_creative_inventory = 1,
plant = 1, -- prevents the transformation from wet soil to soil
},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("signs_bot:robot_leg", {
tiles = {"signs_bot_robot.png^[transformR90]"},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -1/8, -4/8, -1/8, 1/8, 4/8, 1/8},
},
},
paramtype2 = "facedir",
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
is_ground_content = false,
drop = "",
groups = {cracky=1, not_in_creative_inventory = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("signs_bot:robot_foot", {
tiles = {"signs_bot_robot.png^[transformR90]"},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -1/8, -4/8, -1/8, 1/8, 4/8, 1/8},
{ -2/8, -4/8, -2/8, 2/8, -3/8, 2/8},
},
},
paramtype2 = "facedir",
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
is_ground_content = false,
drop = "",
groups = {cracky=1, not_in_creative_inventory = 1,
plant = 1, -- prevents the transformation from wet soil to soil
},
sounds = default.node_sound_metal_defaults(),
})
if enable_LBM then
minetest.register_lbm({
label = "[signs_bot] Remove lost robots",
name = "signs_bot:lost_robot_remove",
nodenames = {"signs_bot:robot"},
run_at_every_load = true,
action = function(pos, node)
local found = false
tubelib2.walk_over_all(function(npos, node, mem)
if node.name == "signs_bot:box" and mem.robot_pos and
vector.equals(pos, mem.robot_pos) then
found = true
end
end, "robot_pos")
if not found then
signs_bot.remove_robot({robot_pos = pos})
end
end
})
end
Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

@@ -0,0 +1,4 @@
# Enable robot part removal.
# Activation makes sense if server crashes occur frequently,
# whereby the function requires a lot of computing time.
signs_bot_enable_LBM (Enable robot part removal) bool false
+88
View File
@@ -0,0 +1,88 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signal function
]]--
-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S2P = minetest.string_to_pos
local M = minetest.get_meta
-- Used by the pairing tool
function signs_bot.get_node_type(pos)
local node = tubelib2.get_node_lvm(pos)
local ndef = minetest.registered_nodes[node.name]
local is_sensor = minetest.get_item_group(node.name, "sign_bot_sensor") == 1
if ndef then
if ndef.signs_bot_get_signal and is_sensor then
return "repeater"
elseif ndef.signs_bot_get_signal then
return "actuator"
elseif is_sensor then
return "sensor"
end
end
end
-- Used by the pairing tool
function signs_bot.get_signal(actuator_pos, sensor_pos)
if actuator_pos then
local node = tubelib2.get_node_lvm(actuator_pos)
local ndef = minetest.registered_nodes[node.name]
if ndef and ndef.signs_bot_get_signal then
node.hash = minetest.hash_node_position(sensor_pos)
return ndef.signs_bot_get_signal(actuator_pos, node)
end
end
end
-- Used by the pairing tool
function signs_bot.store_signal(sensor_pos, dest_pos, signal)
local meta = sensor_pos and M(sensor_pos)
if meta then
meta:set_string("signal_pos", P2S(dest_pos))
meta:set_string("signal_data", signal)
end
end
--
-- Send a signal from a sensor to a actuator
--
function signs_bot.send_signal(sensor_pos)
local meta = sensor_pos and M(sensor_pos)
if meta then
local dest_pos = meta:get_string("signal_pos")
local signal = meta:get_string("signal_data")
if dest_pos ~= "" and signal ~= "" then
local pos = S2P(dest_pos)
local node = tubelib2.get_node_lvm(pos)
local ndef = minetest.registered_nodes[node.name]
if ndef and ndef.signs_bot_on_signal then
ndef.signs_bot_on_signal(pos, node, signal)
end
end
end
end
-- Get the state from the bot box
function signs_bot.get_state(sensor_pos)
local meta = sensor_pos and M(sensor_pos)
if meta then
local dest_pos = meta:get_string("signal_pos")
local pos = S2P(dest_pos)
if pos and pos.x then
local mem = tubelib2.get_mem(pos)
return mem.running
end
return false
end
end
+297
View File
@@ -0,0 +1,297 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Signs Bot: Signs
]]--
-- Load support for I18n.
local S = signs_bot.S
local function formspec(cmnd)
cmnd = minetest.formspec_escape(cmnd)
return "size[4,7]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[0.2,0;"..cmnd.."]"
end
local function formspecXL(help_text, bot_cmnds)
help_text = help_text or "no help"
bot_cmnds = minetest.formspec_escape(bot_cmnds)
return "size[9,8]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[0,0;" .. S("Instructions:") .. "]" ..
"label[0.2,0.6;" .. help_text .. "]" ..
"textarea[0.3,4.0;9,4.9;code;" .. S("Code") .. ":;" .. bot_cmnds .. "]"
end
local function register_sign(def)
minetest.register_node("signs_bot:"..def.name, {
description = def.description,
inventory_image = def.image,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -1/16, -8/16, -1/16, 1/16, 4/16, 1/16},
{ -6/16, -5/16, -2/16, 6/16, 3/16, -1/16},
},
},
paramtype2 = "facedir",
tiles = {
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png^"..def.image,
},
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_string("signs_bot_cmnd", def.commands)
meta:set_string("formspec", formspec(def.commands))
meta:set_string("infotext", def.description)
end,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
is_ground_content = false,
groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2, sign_bot_sign = 1},
sounds = default.node_sound_wood_defaults(),
})
end
local function register_signXL(def)
minetest.register_node("signs_bot:"..def.name, {
description = def.description,
inventory_image = def.image,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -1/16, -8/16, -1/16, 1/16, 4/16, 1/16},
{ -6/16, -5/16, -2/16, 6/16, 3/16, -1/16},
},
},
paramtype2 = "facedir",
tiles = {
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png^"..def.image,
},
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_string("signs_bot_cmnd", def.commands)
meta:set_string("formspec", formspecXL(def.help_text, def.commands))
meta:set_string("infotext", def.description)
end,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = signs_bot.CLIP,
sunlight_propagates = true,
is_ground_content = false,
groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2, sign_bot_sign = 1},
sounds = default.node_sound_wood_defaults(),
})
end
signs_bot.register_sign = register_sign
signs_bot.register_signXL = register_signXL
register_sign({
name = "sign_right",
description = S('Sign "turn right"'),
commands = "turn_right",
image = "signs_bot_sign_right.png",
})
minetest.register_craft({
output = "signs_bot:sign_right 6",
recipe = {
{"group:wood", "default:stick", "group:wood"},
{"dye:yellow", "default:stick", "dye:black"},
{"", "", ""}
}
})
register_sign({
name = "sign_left",
description = S('Sign "turn left"'),
commands = "turn_left",
image = "signs_bot_sign_left.png",
})
minetest.register_craft({
output = "signs_bot:sign_left 6",
recipe = {
{"group:wood", "default:stick", "group:wood"},
{"dye:yellow", "default:stick", ""},
{"dye:black", "", ""}
}
})
register_sign({
name = "sign_take",
description = S('Sign "take item"'),
commands = "take_item 99\nturn_around",
image = "signs_bot_sign_take.png",
})
minetest.register_craft({
output = "signs_bot:sign_take 6",
recipe = {
{"group:wood", "default:stick", "group:wood"},
{"dye:yellow", "default:stick", ""},
{"", "dye:black", ""}
}
})
register_sign({
name = "sign_add",
description = S('Sign "add item"'),
commands = "add_item 99\nturn_around",
image = "signs_bot_sign_add.png",
})
minetest.register_craft({
output = "signs_bot:sign_add 6",
recipe = {
{"group:wood", "default:stick", "group:wood"},
{"dye:yellow", "default:stick", ""},
{"", "", "dye:black"}
}
})
register_sign({
name = "sign_stop",
description = S('Sign "stop"'),
commands = "stop",
image = "signs_bot_sign_stop.png",
})
minetest.register_craft({
output = "signs_bot:sign_stop 6",
recipe = {
{"group:wood", "default:stick", "group:wood"},
{"dye:black", "default:stick", "dye:yellow"},
{"", "", ""}
}
})
if minetest.global_exists("minecart") then
register_sign({
name = "sign_add_cart",
description = S('Sign "add to cart"'),
commands = "drop_items 99 1\npunch_cart\nturn_around",
image = "signs_bot_sign_add_cart.png",
})
minetest.register_craft({
output = "signs_bot:sign_add_cart 4",
recipe = {
{"group:wood", "default:stick", "group:wood"},
{"dye:black", "default:stick", "dye:yellow"},
{"dye:black", "", ""}
}
})
register_sign({
name = "sign_take_cart",
description = S('Sign "take from cart"'),
commands = "pickup_items 1\npunch_cart\nturn_around",
image = "signs_bot_sign_take_cart.png",
})
minetest.register_craft({
output = "signs_bot:sign_take_cart 4",
recipe = {
{"group:wood", "default:stick", "group:wood"},
{"dye:black", "default:stick", "dye:yellow"},
{"", "dye:black", ""}
}
})
end
if minetest.get_modpath("doc") then
doc.add_entry("signs_bot", "sign_right", {
name = S('Sign "turn right"'),
data = {
item = "signs_bot:sign_right",
text = S("The Bot turns right when it detects this sign in front of it.")
},
})
doc.add_entry("signs_bot", "sign_left", {
name = S('Sign "turn left"'),
data = {
item = "signs_bot:sign_left",
text = S("The Bot turns left when it detects this sign in front of it.")
},
})
doc.add_entry("signs_bot", "sign_take", {
name = S('Sign "take item"'),
data = {
item = "signs_bot:sign_take",
text = table.concat({
S("The Bot takes items out of a chest in front of it and then turns around."),
S("This sign has to be placed on top of the chest."),
}, "\n")
},
})
doc.add_entry("signs_bot", "sign_add", {
name = S('Sign "add item"'),
data = {
item = "signs_bot:sign_add",
text = table.concat({
S("The Bot puts items into a chest in front of it and then turns around."),
S("This sign has to be placed on top of the chest."),
}, "\n")
},
})
doc.add_entry("signs_bot", "sign_stop", {
name = S('Sign "stop"'),
data = {
item = "signs_bot:sign_stop",
text = S("The Bot will stop in front of this sign until the sign is removed or the bot is turned off.")
},
})
end
if minetest.get_modpath("doc") and minetest.global_exists("minecart") then
doc.add_entry("signs_bot", "sign_add_cart", {
name = S('Sign "add to cart"'),
data = {
item = "signs_bot:sign_add_cart",
text = table.concat({
S("The Bot puts items into a minecart in front of it, pushes the cart and then turns around."),
S("This sign has to be placed on top of the rail at the cart end position."),
}, "\n")
},
})
doc.add_entry("signs_bot", "sign_take_cart", {
name = S('Sign "take from cart"'),
data = {
item = "signs_bot:sign_take_cart",
text = table.concat({
S("The Bot takes items out of a minecart in front of it, pushes the cart and then turns around."),
S("This sign has to be placed on top of the rail at the cart end position."),
}, "\n")
},
})
end
+372
View File
@@ -0,0 +1,372 @@
--[[
Signs Bot
=========
Copyright (C) 2019-2021 Joachim Stolberg
GPLv3
See LICENSE.txt for more information
Signs Bot: interface for techage
]]--
-- Load support for I18n.
local S = signs_bot.S
local MAX_CAPA = signs_bot.MAX_CAPA
local PWR_NEEDED = 8
if minetest.global_exists("techage") then
local function on_power(pos)
local mem = tubelib2.get_mem(pos)
mem.power_available = true
mem.charging = true
signs_bot.infotext(pos, S("charging"))
end
local function on_nopower(pos)
local mem = tubelib2.get_mem(pos)
mem.power_available = false
signs_bot.infotext(pos, S("no power"))
end
local Cable = techage.ElectricCable
local power = networks.power
signs_bot.register_inventory({"techage:chest_ta2", "techage:chest_ta3", "techage:chest_ta4",
"techage:ta3_silo", "techage:ta4_silo", "techage:ta4_sensor_chest",
"techage:ta4_reactor"}, {
allow_inventory_put = function(pos, stack, player_name)
return not minetest.is_protected(pos, player_name)
end,
allow_inventory_take = function(pos, stack, player_name)
return not minetest.is_protected(pos, player_name)
end,
put = {
listname = "main",
},
take = {
listname = "main",
},
})
signs_bot.register_inventory({"techage:meltingpot", "techage:meltingpot_active"}, {
allow_inventory_put = function(pos, stack, player_name)
return not minetest.is_protected(pos, player_name)
end,
allow_inventory_take = function(pos, stack, player_name)
return not minetest.is_protected(pos, player_name)
end,
put = {
listname = "src",
},
take = {
listname = "dst",
},
})
signs_bot.register_inventory({
"techage:ta2_autocrafter_pas", "techage:ta2_autocrafter_act",
"techage:ta3_autocrafter_pas", "techage:ta3_autocrafter_act"}, {
allow_inventory_put = function(pos, stack, player_name)
return not minetest.is_protected(pos, player_name)
end,
allow_inventory_take = function(pos, stack, player_name)
return not minetest.is_protected(pos, player_name)
end,
put = {
listname = "src",
},
take = {
listname = "dst",
},
})
signs_bot.register_inventory({
"techage:ta2_distributor_pas", "techage:ta2_distributor_act",
"techage:ta3_distributor_pas", "techage:ta3_distributor_act",
"techage:ta4_distributor_pas", "techage:ta4_distributor_act",
"techage:ta4_high_performance_distributor_pas", "techage:ta4_high_performance_distributor_act"}, {
allow_inventory_put = function(pos, stack, player_name)
return not minetest.is_protected(pos, player_name)
end,
allow_inventory_take = function(pos, stack, player_name)
return not minetest.is_protected(pos, player_name)
end,
put = {
listname = "src",
},
take = {
listname = "src",
},
})
local function percent_value(max_val, curr_val)
return math.min(math.ceil(((curr_val or 0) * 100.0) / (max_val or 1.0)), 100)
end
signs_bot.percent_value = percent_value
function signs_bot.formspec_battery_capa(max_capa, current_capa)
local percent = percent_value(max_capa, current_capa)
return "image[0.1,0;0.5,1;signs_bot_form_level_bg.png^[lowpart:"..
percent..":signs_bot_form_level_fg.png]"
end
signs_bot.register_botcommand("ignite", {
mod = "techage",
params = "",
num_param = 0,
description = S("Ignite the techage charcoal lighter"),
cmnd = function(base_pos, mem)
local pos = signs_bot.lib.dest_pos(mem.robot_pos, mem.robot_param2, {0})
local node = tubelib2.get_node_lvm(pos)
if minetest.registered_nodes[node.name]
and minetest.registered_nodes[node.name].on_ignite then
minetest.registered_nodes[node.name].on_ignite(pos)
end
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("low_batt", {
mod = "techage",
params = "<percent>",
num_param = 1,
description = S("Turns the bot off if the\nbattery power is below the\ngiven value in percent (1..99)"),
check = function(val)
val = tonumber(val) or 5
return val and val > 0 and val < 100
end,
cmnd = function(base_pos, mem, val)
val = tonumber(val) or 5
local pwr = percent_value(signs_bot.MAX_CAPA, mem.capa)
if pwr < val then
signs_bot.stop_robot(base_pos, mem)
return signs_bot.TURN_OFF
end
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("jump_low_batt", {
mod = "techage",
params = "<percent> <label>",
num_param = 2,
description = S("Jump to <label> if the\nbattery power is below the\ngiven value in percent (1..99)"),
check = function(val, lbl)
val = tonumber(val) or 5
return val and val > 0 and val < 100 and signs_bot.check_label(lbl)
end,
cmnd = function(base_pos, mem, val, addr)
val = tonumber(val) or 5
local pwr = percent_value(signs_bot.MAX_CAPA, mem.capa)
if pwr < val then
mem.pc = addr - 3
return signs_bot.DONE
end
return signs_bot.DONE
end,
})
signs_bot.register_botcommand("send_cmnd", {
mod = "techage",
params = "<receiver> <command>",
num_param = 2,
description = S([[Sends a techage command
to a given node.
Receiver is addressed by
the techage node number.
For commands with two or more
words, use the '*' character
instead of spaces, e.g.:
send_cmnd 3465 pull*default:dirt*2]]),
check = function(address, command)
address = tonumber(address)
return address ~= nil and command ~= nil and command ~= ""
end,
cmnd = function(base_pos, mem, address, command)
command = tostring(command)
command = command:gsub("*", " ")
address = tostring(tonumber(address))
local meta = minetest.get_meta(base_pos)
local number = tostring(meta:get_int("number") or 0)
local topic, payload = unpack(string.split(command, " ", false, 1))
techage.send_single(number, address, topic, payload)
return signs_bot.DONE
end,
})
-- Bot in the box
function signs_bot.while_charging(pos, mem)
mem.capa = mem.capa or 0
if mem.capa < signs_bot.MAX_CAPA then
local consumed = power.consume_power(pos, Cable, nil, PWR_NEEDED)
mem.capa = mem.capa + consumed
else
minetest.get_node_timer(pos):stop()
mem.charging = false
if not mem.running then
signs_bot.infotext(pos, S("fully charged"))
end
return false
end
return true
end
power.register_nodes({"signs_bot:box"}, Cable, "con")
techage.register_node({"signs_bot:box"}, {
on_inv_request = function(pos, in_dir, access_type)
local meta = minetest.get_meta(pos)
return meta:get_inventory(), "main"
end,
on_pull_item = function(pos, in_dir, num, item_name)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if item_name then
local taken = inv:remove_item("main", {name = item_name, count = num})
if taken:get_count() > 0 then
return taken
end
else -- no item given
return techage.get_items(pos, inv, "main", num)
end
end,
on_push_item = function(pos, in_dir, stack, idx)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack, idx)
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack)
end,
on_recv_message = function(pos, src, topic, payload)
local mem = tubelib2.get_mem(pos)
if topic == "state" then
if mem.error then
return "fault"
elseif mem.running then
if mem.curr_cmnd == "stop" then
return "standby"
elseif mem.blocked then
return "blocked"
else
return "running"
end
elseif mem.capa then
if mem.capa <= 0 then
return "nopower"
elseif mem.capa >= signs_bot.MAX_CAPA then
return "stopped"
else
return "charging"
end
else
return "stopped"
end
elseif topic == "load" then
return signs_bot.percent_value(signs_bot.MAX_CAPA, mem.capa)
elseif topic == "on" then
if not mem.running then
signs_bot.start_robot(pos)
end
elseif topic == "off" then
if mem.running then
signs_bot.stop_robot(pos, mem)
end
else
return "unsupported"
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
local mem = tubelib2.get_mem(pos)
if topic == 1 then
if payload[1] == 1 then -- on
if not mem.running then
signs_bot.start_robot(pos)
return 0, {1}
end
else
if mem.running then
signs_bot.stop_robot(pos, mem)
return 0, {1}
end
end
else
return 2, "" -- topic is unknown or invalid
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
local mem = tubelib2.get_mem(pos)
if topic == 129 then -- state
if mem.error then
return 0, {5}
elseif mem.running then
if mem.curr_cmnd == "stop" then
return 0, {3}
elseif mem.blocked then
return 0, {2}
else
return 0, {1} -- running
end
elseif mem.capa then
if mem.capa <= 0 then
return 0, {4} -- nopower
elseif mem.capa >= signs_bot.MAX_CAPA then
return 0, {6} -- stopped
else
return 0, {7} -- charging
end
else
return 0, {6} -- stopped
end
else
return 2, "" -- topic is unknown or invalid
end
end,
})
techage.register_node({"signs_bot:chest"}, {
on_inv_request = function(pos, in_dir, access_type)
local meta = minetest.get_meta(pos)
return meta:get_inventory(), "main"
end,
on_pull_item = function(pos, in_dir, num, item_name)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if item_name then
local taken = inv:remove_item("main", {name = item_name, count = num})
if taken:get_count() > 0 then
return taken
end
else -- no item given
return techage.get_items(pos, inv, "main", num)
end
end,
on_push_item = function(pos, in_dir, stack, idx)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack, idx)
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack)
end,
})
techage.register_node_for_v1_transition({"signs_bot:box"}, function(pos, node)
power.update_network(pos, nil, Cable)
end)
techage.disable_block_for_assembly_tool("signs_bot:box")
else
function signs_bot.formspec_battery_capa(max_capa, current_capa)
return ""
end
end
@@ -0,0 +1,2 @@
#!/bin/bash
pngquant --skip-if-larger --quality=80 --strip *.png --ext .png --force
Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Loaded 100 of 6234 files, more files were not shown because too many files have changed in this diff. Show more