mods that hopefully work for a TechAge-based pack
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/
|
||||
@@ -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>.
|
||||
@@ -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)
|
||||
|
||||

|
||||
|
||||
|
||||
### 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.
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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(),
|
||||
})
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
})
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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?
|
||||
@@ -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"},
|
||||
})
|
||||
|
After Width: | Height: | Size: 70 KiB |
|
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
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
}
|
||||
}
|
||||
@@ -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 = {
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
After Width: | Height: | Size: 171 B |
|
After Width: | Height: | Size: 171 B |
|
After Width: | Height: | Size: 171 B |
|
After Width: | Height: | Size: 202 B |
|
After Width: | Height: | Size: 380 B |
|
After Width: | Height: | Size: 406 B |
|
After Width: | Height: | Size: 436 B |
|
After Width: | Height: | Size: 418 B |
|
After Width: | Height: | Size: 465 B |
|
After Width: | Height: | Size: 327 B |
|
After Width: | Height: | Size: 215 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 147 B |
|
After Width: | Height: | Size: 155 B |
|
After Width: | Height: | Size: 153 B |
|
After Width: | Height: | Size: 151 B |
|
After Width: | Height: | Size: 348 B |
|
After Width: | Height: | Size: 318 B |
|
After Width: | Height: | Size: 344 B |
|
After Width: | Height: | Size: 214 B |
|
After Width: | Height: | Size: 162 B |
|
After Width: | Height: | Size: 189 B |
|
After Width: | Height: | Size: 162 B |
|
After Width: | Height: | Size: 162 B |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 407 B |
|
After Width: | Height: | Size: 142 B |
|
After Width: | Height: | Size: 166 B |
|
After Width: | Height: | Size: 150 B |
|
After Width: | Height: | Size: 125 B |
|
After Width: | Height: | Size: 146 B |
|
After Width: | Height: | Size: 146 B |
|
After Width: | Height: | Size: 97 B |
|
After Width: | Height: | Size: 317 B |
|
After Width: | Height: | Size: 388 B |
|
After Width: | Height: | Size: 188 B |
|
After Width: | Height: | Size: 113 B |
|
After Width: | Height: | Size: 304 B |
|
After Width: | Height: | Size: 219 B |