first
25
.luacheckrc
Executable file
@ -0,0 +1,25 @@
|
||||
unused_args = false
|
||||
allow_defined_top = true
|
||||
|
||||
globals = {
|
||||
"minetest",
|
||||
"mobkit",
|
||||
"core",
|
||||
"player_api",
|
||||
"player_monoids",
|
||||
"math.sign",
|
||||
}
|
||||
|
||||
read_globals = {
|
||||
string = {fields = {"split"}},
|
||||
table = {fields = {"copy", "getn"}},
|
||||
|
||||
-- Builtin
|
||||
"vector", "ItemStack",
|
||||
"dump", "DIR_DELIM", "VoxelArea", "Settings",
|
||||
|
||||
-- MTG
|
||||
"default", "sfinv", "creative",
|
||||
}
|
||||
|
||||
ignore = {"611"}
|
810
LICENSE
Normal file → Executable file
@ -1,661 +1,167 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
GNU LESSER 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 Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are 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.
|
||||
|
||||
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.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
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 Affero 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. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
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 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 work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero 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.
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser 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 Affero 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 Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero 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.
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
|
||||
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 Affero 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero 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 your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
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 AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
Copyright Alexsandro Percy - 2021
|
||||
|
71
README.md
Normal file → Executable file
@ -1 +1,70 @@
|
||||
# pa28
|
||||
Minetest 5.4 mod: Super Cub
|
||||
========================================
|
||||
|
||||
The Super Cub
|
||||
|
||||
Adapted from Super Duck, this is a pure lithoplane, based on Super Cub airplane.
|
||||
In order to fly, it is necessary to first supply the airplane with biofuel.
|
||||
Then with a bottle or gallon selected, punch it against the airplane.
|
||||
You can use 10 bottles them to fill the tank. See the fuel gauge on the airplane
|
||||
panel (right below, with a green F). To embark, click with the right button.
|
||||
While the machine is off, it is possible to move it using the sneak and jump keys (shift an space).
|
||||
W ans S controls the pitch (elevator).
|
||||
Right and Left (A and D) controls the yaw (rudder and ailerons).
|
||||
|
||||
Then to fly, start the engine with the special key E. Press jump (space)
|
||||
to increase the engine power (check the panel for the indicator marked with a yellow P).
|
||||
Adjust to the maximum. Pull the elevator control (S) when it have the speed to lift.
|
||||
|
||||
During the cruise flight, it is ideal to keep the power setting below the red range,
|
||||
to control fuel consumption. Use the climb indicator to stabilize altitude,
|
||||
as at high altitudes you lose sustentation and you spend more fuel.
|
||||
|
||||
For landing, just lower the power and stabilize the airplane. Pay attention at air speed
|
||||
indicator, keeping it at green range, otherwise you will stall.
|
||||
|
||||
Care must be taken with impacts, as it causes damage to the aircraft and the pilot,
|
||||
so training landings is essential.
|
||||
|
||||
To brake the aircraft, use the sneak (shift) key until it comes to a complete stop.
|
||||
Do not stop the engine before this, or it will reverse when it stops
|
||||
|
||||
To repair damages, you can use the repair tool. It subtracts steel ingots to increase
|
||||
airplane hp.
|
||||
|
||||
It can be painted using dye of any color you want, you must punch the airplane with the dye.
|
||||
|
||||
Biofuel mod can be found here: https://github.com/APercy/minetest_biofuel
|
||||
|
||||
The limitations: because the lack in functions to roll the camera, and the rudder acting together the ailerons,
|
||||
the airplane is unable to do a tuneau, barrel roll, loopings and any kind of aerobatics maneuvers.
|
||||
It was limited at the source. You can modify it by your own, just making the roll movement cumulative and increasing the speeds
|
||||
|
||||
**Controls overview:**
|
||||
* Right click: enter in/get off plane
|
||||
* Left click (with biofuel): add fuel to plane
|
||||
* Right click and Sneak: enter in flight instructor mode (limited vision, so use debug info)
|
||||
* E: Start engine
|
||||
* Jump: Increase power, forward on ground
|
||||
* Sneak: Decrease power, brake on ground
|
||||
* Backward: go up flying - nose up
|
||||
* Forward: go down flying - nose down
|
||||
* Left/right: Turn to left/right, work on and out ground.
|
||||
* Left and Right together: center all commands
|
||||
* Sneak and Jump together: give/take the controls to/from pilot student
|
||||
* Up and Down together: enable/disable HUD
|
||||
|
||||
**Chat Commands: **
|
||||
|
||||
/cub_eject - ejects from the vehicle
|
||||
|
||||
/cub_manual - shows the manual
|
||||
|
||||
-----------------------
|
||||
License of source code:
|
||||
LGPL v3 (see file LICENSE)
|
||||
|
||||
License of media (textures and sounds):
|
||||
CC0
|
||||
|
||||
|
||||
|
270
control.lua
Executable file
@ -0,0 +1,270 @@
|
||||
--global constants
|
||||
pa28.vector_up = vector.new(0, 1, 0)
|
||||
pa28.ideal_step = 0.02
|
||||
pa28.rudder_limit = 30
|
||||
pa28.elevator_limit = 40
|
||||
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "utilities.lua")
|
||||
|
||||
function pa28.powerAdjust(self,dtime,factor,dir,max_power)
|
||||
local max = max_power or 100
|
||||
local add_factor = factor/2
|
||||
add_factor = add_factor * (dtime/pa28.ideal_step) --adjusting the command speed by dtime
|
||||
local power_index = self._power_lever
|
||||
|
||||
if dir == 1 then
|
||||
if self._power_lever < max then
|
||||
self._power_lever = self._power_lever + add_factor
|
||||
end
|
||||
if self._power_lever > max then
|
||||
self._power_lever = max
|
||||
end
|
||||
end
|
||||
if dir == -1 then
|
||||
if self._power_lever > 0 then
|
||||
self._power_lever = self._power_lever - add_factor
|
||||
if self._power_lever < 0 then self._power_lever = 0 end
|
||||
end
|
||||
if self._power_lever <= 0 then
|
||||
self._power_lever = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pa28.control(self, dtime, hull_direction, longit_speed, longit_drag,
|
||||
later_speed, later_drag, accel, player, is_flying)
|
||||
--if self.driver_name == nil then return end
|
||||
local retval_accel = accel
|
||||
|
||||
local stop = false
|
||||
local ctrl = nil
|
||||
|
||||
-- player control
|
||||
if player then
|
||||
ctrl = player:get_player_control()
|
||||
|
||||
--engine and power control
|
||||
--[[if ctrl.aux1 and self._last_time_command > 0.5 then
|
||||
self._last_time_command = 0
|
||||
if self._engine_running then
|
||||
self._engine_running = false
|
||||
self._autopilot = false
|
||||
self._power_lever = 0 --zero power
|
||||
self._last_applied_power = 0 --zero engine
|
||||
elseif self._engine_running == false and self._energy > 0 then
|
||||
self._engine_running = true
|
||||
self._last_applied_power = -1 --send signal to start
|
||||
end
|
||||
end]]--
|
||||
|
||||
self._acceleration = 0
|
||||
if self._engine_running then
|
||||
--engine acceleration calc
|
||||
local engineacc = (self._power_lever * pa28.max_engine_acc) / 100;
|
||||
|
||||
local factor = 1
|
||||
|
||||
--increase power lever
|
||||
if ctrl.jump then
|
||||
pa28.powerAdjust(self, dtime, factor, 1)
|
||||
end
|
||||
--decrease power lever
|
||||
if ctrl.sneak then
|
||||
pa28.powerAdjust(self, dtime, factor, -1)
|
||||
if self._power_lever <= 0 and is_flying == false then
|
||||
--break
|
||||
if longit_speed > 0 then
|
||||
engineacc = -1
|
||||
if (longit_speed + engineacc) < 0 then
|
||||
engineacc = longit_speed * -1
|
||||
end
|
||||
end
|
||||
if longit_speed < 0 then
|
||||
engineacc = 1
|
||||
if (longit_speed + engineacc) > 0 then
|
||||
engineacc = longit_speed * -1
|
||||
end
|
||||
end
|
||||
if abs(longit_speed) < 0.2 then
|
||||
stop = true
|
||||
end
|
||||
end
|
||||
end
|
||||
--do not exceed
|
||||
local max_speed = 6
|
||||
if longit_speed > max_speed then
|
||||
engineacc = engineacc - (longit_speed-max_speed)
|
||||
if engineacc < 0 then engineacc = 0 end
|
||||
end
|
||||
self._acceleration = engineacc
|
||||
else
|
||||
local paddleacc = 0
|
||||
if longit_speed < 1.0 then
|
||||
if ctrl.jump then paddleacc = 0.5 end
|
||||
end
|
||||
if longit_speed > -1.0 then
|
||||
if ctrl.sneak then paddleacc = -0.5 end
|
||||
end
|
||||
self._acceleration = paddleacc
|
||||
end
|
||||
|
||||
local hull_acc = vector.multiply(hull_direction,self._acceleration)
|
||||
retval_accel=vector.add(retval_accel,hull_acc)
|
||||
|
||||
--pitch
|
||||
local pitch_cmd = 0
|
||||
if ctrl.up then pitch_cmd = 1 elseif ctrl.down then pitch_cmd = -1 end
|
||||
pa28.set_pitch(self, pitch_cmd, dtime)
|
||||
|
||||
-- yaw
|
||||
local yaw_cmd = 0
|
||||
if ctrl.right then yaw_cmd = 1 elseif ctrl.left then yaw_cmd = -1 end
|
||||
pa28.set_yaw(self, yaw_cmd, dtime)
|
||||
|
||||
--I'm desperate, center all!
|
||||
if ctrl.right and ctrl.left then
|
||||
self._elevator_angle = 0
|
||||
self._rudder_angle = 0
|
||||
end
|
||||
|
||||
----------------------------------
|
||||
-- flap operation
|
||||
----------------------------------
|
||||
if ctrl.aux1 and ctrl.sneak and self._last_time_command >= 0.3 then
|
||||
self._last_time_command = 0
|
||||
pa28.flap_operate(self, player)
|
||||
end
|
||||
end
|
||||
|
||||
if longit_speed > 0 then
|
||||
if ctrl then
|
||||
if ctrl.right or ctrl.left then
|
||||
else
|
||||
pa28.rudder_auto_correction(self, longit_speed, dtime)
|
||||
end
|
||||
else
|
||||
pa28.rudder_auto_correction(self, longit_speed, dtime)
|
||||
end
|
||||
pa28.elevator_auto_correction(self, longit_speed, dtime)
|
||||
end
|
||||
|
||||
return retval_accel, stop
|
||||
end
|
||||
|
||||
function pa28.set_pitch(self, dir, dtime)
|
||||
local pitch_factor = 6
|
||||
if dir == -1 then
|
||||
--minetest.chat_send_all("cabrando")
|
||||
if self._elevator_angle > 0 then pitch_factor = pitch_factor * 2 end
|
||||
self._elevator_angle = math.max(self._elevator_angle-pitch_factor*dtime,-pa28.elevator_limit)
|
||||
elseif dir == 1 then
|
||||
--minetest.chat_send_all("picando")
|
||||
if self._angle_of_attack < 0 then pitch_factor = 1 end --lets reduce the command power to avoid accidents
|
||||
self._elevator_angle = math.min(self._elevator_angle+pitch_factor*dtime,pa28.elevator_limit)
|
||||
end
|
||||
end
|
||||
|
||||
function pa28.set_yaw(self, dir, dtime)
|
||||
local yaw_factor = 25
|
||||
if dir == 1 then
|
||||
self._rudder_angle = math.max(self._rudder_angle-(yaw_factor*dtime),-pa28.rudder_limit)
|
||||
elseif dir == -1 then
|
||||
self._rudder_angle = math.min(self._rudder_angle+(yaw_factor*dtime),pa28.rudder_limit)
|
||||
end
|
||||
end
|
||||
|
||||
function pa28.rudder_auto_correction(self, longit_speed, dtime)
|
||||
local factor = 1
|
||||
if self._rudder_angle > 0 then factor = -1 end
|
||||
local correction = (pa28.rudder_limit*(longit_speed/1000)) * factor * (dtime/pa28.ideal_step)
|
||||
local before_correction = self._rudder_angle
|
||||
local new_rudder_angle = self._rudder_angle + correction
|
||||
if math.sign(before_correction) ~= math.sign(new_rudder_angle) then
|
||||
self._rudder_angle = 0
|
||||
else
|
||||
self._rudder_angle = new_rudder_angle
|
||||
end
|
||||
end
|
||||
|
||||
function pa28.elevator_auto_correction(self, longit_speed, dtime)
|
||||
local factor = 1
|
||||
--if self._elevator_angle > -1.5 then factor = -1 end --here is the "compensator" adjusto to keep it stable
|
||||
if self._elevator_angle > 0 then factor = -1 end
|
||||
local correction = (pa28.elevator_limit*(longit_speed/5000)) * factor * (dtime/pa28.ideal_step)
|
||||
local before_correction = self._elevator_angle
|
||||
local new_elevator_angle = self._elevator_angle + correction
|
||||
if math.sign(before_correction) ~= math.sign(new_elevator_angle) then
|
||||
self._elevator_angle = 0
|
||||
else
|
||||
self._elevator_angle = new_elevator_angle
|
||||
end
|
||||
end
|
||||
|
||||
--obsolete, will be removed
|
||||
function getAdjustFactor(curr_y, desired_y)
|
||||
local max_difference = 0.1
|
||||
local adjust_factor = 0.5
|
||||
local difference = math.abs(curr_y - desired_y)
|
||||
if difference > max_difference then difference = max_difference end
|
||||
return (difference * adjust_factor) / max_difference
|
||||
end
|
||||
|
||||
function pa28.autopilot(self, dtime, hull_direction, longit_speed, accel, curr_pos)
|
||||
|
||||
local retval_accel = accel
|
||||
|
||||
local max_autopilot_power = 85
|
||||
local max_attack_angle = 1.8
|
||||
|
||||
--climb
|
||||
local velocity = self.object:get_velocity()
|
||||
local climb_rate = velocity.y * 1.5
|
||||
if climb_rate > 5 then climb_rate = 5 end
|
||||
if climb_rate < -5 then
|
||||
climb_rate = -5
|
||||
end
|
||||
|
||||
self._acceleration = 0
|
||||
if self._engine_running then
|
||||
--engine acceleration calc
|
||||
local engineacc = (self._power_lever * pa28.max_engine_acc) / 100;
|
||||
--self.engine:set_animation_frame_speed(60 + self._power_lever)
|
||||
|
||||
local factor = math.abs(climb_rate * 0.1) --getAdjustFactor(curr_pos.y, self._auto_pilot_altitude)
|
||||
--increase power lever
|
||||
if climb_rate > 0.2 then
|
||||
pa28.powerAdjust(self, dtime, factor, -1)
|
||||
end
|
||||
--decrease power lever
|
||||
if climb_rate < 0 then
|
||||
pa28.powerAdjust(self, dtime, factor, 1, max_autopilot_power)
|
||||
end
|
||||
--do not exceed
|
||||
local max_speed = pa28.max_speed
|
||||
if longit_speed > max_speed then
|
||||
engineacc = engineacc - (longit_speed-max_speed)
|
||||
if engineacc < 0 then engineacc = 0 end
|
||||
end
|
||||
self._acceleration = engineacc
|
||||
end
|
||||
|
||||
local hull_acc = vector.multiply(hull_direction,self._acceleration)
|
||||
retval_accel=vector.add(retval_accel,hull_acc)
|
||||
|
||||
--pitch
|
||||
if self._angle_of_attack > max_attack_angle then
|
||||
pa28.set_pitch(self, 1, dtime)
|
||||
elseif self._angle_of_attack < max_attack_angle then
|
||||
pa28.set_pitch(self, -1, dtime)
|
||||
end
|
||||
|
||||
-- yaw
|
||||
pa28.set_yaw(self, 0, dtime)
|
||||
|
||||
if longit_speed > 0 then
|
||||
pa28.rudder_auto_correction(self, longit_speed, dtime)
|
||||
pa28.elevator_auto_correction(self, longit_speed, dtime)
|
||||
end
|
||||
|
||||
return retval_accel
|
||||
end
|
73
crafts.lua
Executable file
@ -0,0 +1,73 @@
|
||||
|
||||
-- wing
|
||||
minetest.register_craftitem("pa28:wings",{
|
||||
description = "PA28 wings",
|
||||
inventory_image = "pa28_wings.png",
|
||||
})
|
||||
-- fuselage
|
||||
minetest.register_craftitem("pa28:fuselage",{
|
||||
description = "PA28 fuselage",
|
||||
inventory_image = "pa28_fuselage.png",
|
||||
})
|
||||
|
||||
-- pa28
|
||||
minetest.register_craftitem("pa28:pa28", {
|
||||
description = pa28.plane_text,
|
||||
inventory_image = "pa28.png",
|
||||
liquids_pointable = true,
|
||||
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.type ~= "node" then
|
||||
return
|
||||
end
|
||||
|
||||
local pointed_pos = pointed_thing.under
|
||||
--local node_below = minetest.get_node(pointed_pos).name
|
||||
--local nodedef = minetest.registered_nodes[node_below]
|
||||
|
||||
pointed_pos.y=pointed_pos.y+2.5
|
||||
local pa28_ent = minetest.add_entity(pointed_pos, "pa28:pa28")
|
||||
if pa28_ent and placer then
|
||||
local ent = pa28_ent:get_luaentity()
|
||||
local owner = placer:get_player_name()
|
||||
ent.owner = owner
|
||||
pa28_ent:set_yaw(placer:get_look_horizontal())
|
||||
itemstack:take_item()
|
||||
airutils.create_inventory(ent, pa28.trunk_slots, owner)
|
||||
end
|
||||
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
|
||||
--
|
||||
-- crafting
|
||||
--
|
||||
|
||||
if minetest.get_modpath("default") then
|
||||
--[[minetest.register_craft({
|
||||
output = "pa28:wings",
|
||||
recipe = {
|
||||
{"wool:white", "farming:string", "wool:white"},
|
||||
{"group:wood", "group:wood", "group:wood"},
|
||||
{"wool:white", "default:steel_ingot", "wool:white"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "pa28:fuselage",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "default:diamondblock", "default:steel_ingot"},
|
||||
{"wool:white", "default:steel_ingot", "wool:white"},
|
||||
{"default:steel_ingot", "default:mese_block", "default:steel_ingot"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "pa28:pa28",
|
||||
recipe = {
|
||||
{"pa28:wings",},
|
||||
{"pa28:fuselage",},
|
||||
}
|
||||
})]]--
|
||||
end
|
43
custom_physics.lua
Executable file
@ -0,0 +1,43 @@
|
||||
|
||||
local min = math.min
|
||||
local abs = math.abs
|
||||
--local deg = math.deg
|
||||
|
||||
function pa28.physics(self)
|
||||
local friction = 0.99
|
||||
local vel=self.object:get_velocity()
|
||||
-- dumb friction
|
||||
if self.isonground and not self.isinliquid then
|
||||
vel = {x=vel.x*friction,
|
||||
y=vel.y,
|
||||
z=vel.z*friction}
|
||||
self.object:set_velocity(vel)
|
||||
end
|
||||
|
||||
-- bounciness
|
||||
if self.springiness and self.springiness > 0 then
|
||||
local vnew = vector.new(vel)
|
||||
|
||||
if not self.collided then -- ugly workaround for inconsistent collisions
|
||||
for _,k in ipairs({'y','z','x'}) do
|
||||
if vel[k]==0 and abs(self.lastvelocity[k])> 0.1 then
|
||||
vnew[k]=-self.lastvelocity[k]*self.springiness
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not vector.equals(vel,vnew) then
|
||||
self.collided = true
|
||||
else
|
||||
if self.collided then
|
||||
vnew = vector.new(self.lastvelocity)
|
||||
end
|
||||
self.collided = false
|
||||
end
|
||||
|
||||
self.object:set_velocity(vnew)
|
||||
end
|
||||
|
||||
self.object:set_acceleration({x=0,y=mobkit.gravity,z=0})
|
||||
|
||||
end
|
3
depends.txt
Executable file
@ -0,0 +1,3 @@
|
||||
mobkit
|
||||
default
|
||||
biofuel
|
449
entities.lua
Executable file
@ -0,0 +1,449 @@
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "global_definitions.lua")
|
||||
|
||||
--
|
||||
-- entity
|
||||
--
|
||||
|
||||
pa28.vector_up = vector.new(0, 1, 0)
|
||||
|
||||
minetest.register_entity('pa28:engine',{
|
||||
initial_properties = {
|
||||
physical = false,
|
||||
collide_with_objects=false,
|
||||
pointable=false,
|
||||
visual = "mesh",
|
||||
backface_culling = false,
|
||||
mesh = "pa28_propeller.b3d",
|
||||
--visual_size = {x = 3, y = 3, z = 3},
|
||||
textures = {"pa28_propeller.png", "pa28_black.png", "pa28_white.png"},
|
||||
},
|
||||
|
||||
on_activate = function(self,std)
|
||||
self.sdata = minetest.deserialize(std) or {}
|
||||
if self.sdata.remove then self.object:remove() end
|
||||
end,
|
||||
|
||||
get_staticdata=function(self)
|
||||
self.sdata.remove=true
|
||||
return minetest.serialize(self.sdata)
|
||||
end,
|
||||
|
||||
})
|
||||
|
||||
--
|
||||
-- seat pivot
|
||||
--
|
||||
minetest.register_entity('pa28:seat_base',{
|
||||
initial_properties = {
|
||||
physical = false,
|
||||
collide_with_objects=false,
|
||||
pointable=false,
|
||||
visual = "mesh",
|
||||
mesh = "pa28_seat_base.b3d",
|
||||
textures = {"pa28_black.png",},
|
||||
},
|
||||
|
||||
on_activate = function(self,std)
|
||||
self.sdata = minetest.deserialize(std) or {}
|
||||
if self.sdata.remove then self.object:remove() end
|
||||
end,
|
||||
|
||||
get_staticdata=function(self)
|
||||
self.sdata.remove=true
|
||||
return minetest.serialize(self.sdata)
|
||||
end,
|
||||
|
||||
})
|
||||
|
||||
minetest.register_entity("pa28:pa28", {
|
||||
initial_properties = {
|
||||
physical = true,
|
||||
collide_with_objects = true,
|
||||
collisionbox = {-1.2, -1.5, -1.2, 1.2, 1, 1.2}, --{-1,0,-1, 1,0.3,1},
|
||||
selectionbox = {-2, -1.5, -2, 2, 0, 2},
|
||||
visual = "mesh",
|
||||
mesh = "pa28.b3d",
|
||||
stepheight = 0.5,
|
||||
textures = {
|
||||
"pa28_black.png", --bancos 1
|
||||
"pa28_black.png", --bancos 2
|
||||
"pa28_compass.png", --bussola
|
||||
"pa28_white.png", --ponteiros
|
||||
"pa28_compass_plan.png", --indicador bussola
|
||||
"pa28_white.png", --superficies controle
|
||||
"pa28_painting.png", --topo leme
|
||||
"pa28_black.png", --manches
|
||||
"pa28_l_light.png", --luz posicao
|
||||
"pa28_white.png", --topo fuselagem
|
||||
"pa28_painting.png", --fuselagem
|
||||
"pa28_black.png", --motor
|
||||
"pa28_glass.png", --motor
|
||||
"pa28_interior.png", --fuselagem
|
||||
"pa28_l_light.png", --luz posicao esq
|
||||
"pa28_black.png", --painel topo
|
||||
"pa28_black.png", --painel fundo
|
||||
"pa28_panel.png", --painel
|
||||
"pa28_metal.png", --farol
|
||||
"pa28_compass_ind.png", --compass plane
|
||||
"pa28_r_light.png", --luz posicao dir
|
||||
"pa28_white.png", --topo carenagens
|
||||
"pa28_painting.png", --carenagens
|
||||
"pa28_metal.png", --suporte trem frontal
|
||||
"pa28_white.png", --topo carenagen
|
||||
"pa28_painting.png", --carenagem
|
||||
"pa28_painting.png", --topo est vertical
|
||||
"pa28_black.png", --pneu
|
||||
"pa28_metal.png", --aro roda
|
||||
"pa28_black.png", --pneu
|
||||
"pa28_metal.png", --aro roda
|
||||
"pa28_white.png", --asas
|
||||
"pa28_painting.png", --pontas das asas
|
||||
},
|
||||
},
|
||||
textures = {},
|
||||
driver_name = nil,
|
||||
sound_handle = nil,
|
||||
owner = "",
|
||||
static_save = true,
|
||||
infotext = "A nice airplane",
|
||||
hp_max = 50,
|
||||
shaded = true,
|
||||
show_on_minimap = true,
|
||||
springiness = 0.1,
|
||||
physics = pa28.physics,
|
||||
_passenger = nil,
|
||||
_color = "#0063b0",
|
||||
_rudder_angle = 0,
|
||||
_acceleration = 0,
|
||||
_engine_running = false,
|
||||
_angle_of_attack = 2,
|
||||
_elevator_angle = 0,
|
||||
_power_lever = 0,
|
||||
_last_applied_power = 0,
|
||||
_energy = 0.001,
|
||||
_last_vel = {x=0,y=0,z=0},
|
||||
_longit_speed = 0,
|
||||
_land_retracted = true,
|
||||
_show_hud = false,
|
||||
_instruction_mode = false, --flag to intruction mode
|
||||
_command_is_given = false, --flag to mark the "owner" of the commands now
|
||||
_autopilot = false,
|
||||
_auto_pilot_altitude = 0,
|
||||
_flap = false,
|
||||
_last_accell = {x=0,y=0,z=0},
|
||||
_last_time_command = 1,
|
||||
_wing_configuration = pa28.wing_angle_of_attack,
|
||||
_inv = nil,
|
||||
_inv_id = "",
|
||||
|
||||
get_staticdata = function(self) -- unloaded/unloads ... is now saved
|
||||
return minetest.serialize({
|
||||
--stored_sound_handle = self.sound_handle,
|
||||
stored_energy = self._energy,
|
||||
stored_owner = self.owner,
|
||||
stored_hp = self.hp_max,
|
||||
stored_color = self._color,
|
||||
stored_power_lever = self._power_lever,
|
||||
stored_driver_name = self.driver_name,
|
||||
stored_last_accell = self._last_accell,
|
||||
stored_engine_running = self._engine_running,
|
||||
stored_inv_id = self._inv_id,
|
||||
stored_flap = self._flap,
|
||||
})
|
||||
end,
|
||||
|
||||
on_deactivate = function(self)
|
||||
airutils.save_inventory(self)
|
||||
end,
|
||||
|
||||
on_activate = function(self, staticdata, dtime_s)
|
||||
mobkit.actfunc(self, staticdata, dtime_s)
|
||||
if staticdata ~= "" and staticdata ~= nil then
|
||||
local data = minetest.deserialize(staticdata) or {}
|
||||
self._energy = data.stored_energy
|
||||
self.owner = data.stored_owner
|
||||
self.hp_max = data.stored_hp
|
||||
self._color = data.stored_color
|
||||
self._power_lever = data.stored_power_lever
|
||||
self.driver_name = data.stored_driver_name
|
||||
self._last_accell = data.stored_last_accell
|
||||
self._engine_running = data.stored_engine_running
|
||||
self._inv_id = data.stored_inv_id
|
||||
self._flap = data.stored_flap
|
||||
--self.sound_handle = data.stored_sound_handle
|
||||
--minetest.debug("loaded: ", self._energy)
|
||||
if self._engine_running then
|
||||
self._last_applied_power = -1 --signal to start
|
||||
end
|
||||
end
|
||||
airutils.setText(self, pa28.plane_text)
|
||||
self.object:set_animation({x = 1, y = 12}, 0, 0, true)
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
local engine=minetest.add_entity(pos,'pa28:engine')
|
||||
engine:set_attach(self.object,'',{x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
-- set the animation once and later only change the speed
|
||||
engine:set_animation({x = 1, y = 12}, 0, 0, true)
|
||||
self.engine = engine
|
||||
|
||||
local pilot_seat_base=minetest.add_entity(pos,'pa28:seat_base')
|
||||
pilot_seat_base:set_attach(self.object,'',{x=-4.25,y=-4,z=2},{x=0,y=0,z=0})
|
||||
self.pilot_seat_base = pilot_seat_base
|
||||
|
||||
local co_pilot_seat_base=minetest.add_entity(pos,'pa28:seat_base')
|
||||
co_pilot_seat_base:set_attach(self.object,'',{x=4.25,y=-4,z=2},{x=0,y=0,z=0})
|
||||
self.co_pilot_seat_base = co_pilot_seat_base
|
||||
|
||||
self._passengers_base = {[1]=nil, [2]=nil,}
|
||||
self._passengers = {[1]=nil, [2]=nil,}
|
||||
|
||||
self._passengers_base[1]=minetest.add_entity(pos,'pa28:seat_base')
|
||||
self._passengers_base[1]:set_attach(self.object,'',{x=-4.25,y=-4.5,z=-6},{x=0,y=0,z=0})
|
||||
|
||||
self._passengers_base[2]=minetest.add_entity(pos,'pa28:seat_base')
|
||||
self._passengers_base[2]:set_attach(self.object,'',{x=4.25,y=-4.5,z=-6},{x=0,y=0,z=0})
|
||||
|
||||
airutils.paint(self, self._color, "pa28_painting.png")
|
||||
|
||||
self.object:set_bone_position("flap_a.r", {x=20.8,y=-5.8,z=-6.7}, {x=0,y=0,z=5})
|
||||
self.object:set_bone_position("flap_a.l", {x=-20.8,y=-5.8,z=-6.7}, {x=0,y=0,z=-5})
|
||||
self.object:set_bone_position("aileron_a.r", {x=43.7,y=-3.8,z=-6.7}, {x=0,y=0,z=5})
|
||||
self.object:set_bone_position("aileron_a.l", {x=-43.7,y=-3.8,z=-6.7}, {x=0,y=0,z=-5})
|
||||
self.object:set_bone_position("rudder_a", {x=0,y=12.05,z=-46.7}, {x=-21.2,y=0,z=0})
|
||||
|
||||
self.object:set_armor_groups({immortal=1})
|
||||
|
||||
local inv = minetest.get_inventory({type = "detached", name = self._inv_id})
|
||||
-- if the game was closed the inventories have to be made anew, instead of just reattached
|
||||
if not inv then
|
||||
airutils.create_inventory(self, pa28.trunk_slots)
|
||||
else
|
||||
self.inv = inv
|
||||
end
|
||||
end,
|
||||
|
||||
--on_step = mobkit.stepfunc,
|
||||
on_step = function(self,dtime,colinfo)
|
||||
self.dtime = math.min(dtime,0.2)
|
||||
self.colinfo = colinfo
|
||||
self.height = mobkit.get_box_height(self)
|
||||
|
||||
-- physics comes first
|
||||
local vel = self.object:get_velocity()
|
||||
|
||||
if colinfo then
|
||||
self.isonground = colinfo.touching_ground
|
||||
else
|
||||
if self.lastvelocity.y==0 and vel.y==0 then
|
||||
self.isonground = true
|
||||
else
|
||||
self.isonground = false
|
||||
end
|
||||
end
|
||||
|
||||
self:physics()
|
||||
|
||||
if self.logic then
|
||||
self:logic()
|
||||
end
|
||||
|
||||
self.lastvelocity = self.object:get_velocity()
|
||||
self.time_total=self.time_total+self.dtime
|
||||
end,
|
||||
logic = pa28.flightstep,
|
||||
|
||||
on_punch = function(self, puncher, ttime, toolcaps, dir, damage)
|
||||
if not puncher or not puncher:is_player() then
|
||||
return
|
||||
end
|
||||
|
||||
local is_admin = false
|
||||
is_admin = minetest.check_player_privs(puncher, {server=true})
|
||||
local name = puncher:get_player_name()
|
||||
if self.owner and self.owner ~= name and self.owner ~= "" then
|
||||
if is_admin == false then return end
|
||||
end
|
||||
if self.owner == nil then
|
||||
self.owner = name
|
||||
end
|
||||
|
||||
if self.driver_name and self.driver_name ~= name then
|
||||
-- do not allow other players to remove the object while there is a driver
|
||||
return
|
||||
end
|
||||
|
||||
local is_attached = false
|
||||
if puncher:get_attach() == self.object then is_attached = true end
|
||||
|
||||
local itmstck=puncher:get_wielded_item()
|
||||
local item_name = ""
|
||||
if itmstck then item_name = itmstck:get_name() end
|
||||
|
||||
if is_attached == false then
|
||||
if pa28.loadFuel(self, puncher:get_player_name()) then
|
||||
return
|
||||
end
|
||||
|
||||
--repair
|
||||
if (item_name == "airutils:repair_tool" or item_name == "trike:repair_tool")
|
||||
and self._engine_running == false then
|
||||
if self.hp_max < 50 then
|
||||
local inventory_item = "default:steel_ingot"
|
||||
local inv = puncher:get_inventory()
|
||||
if inv:contains_item("main", inventory_item) then
|
||||
local stack = ItemStack(inventory_item .. " 1")
|
||||
inv:remove_item("main", stack)
|
||||
self.hp_max = self.hp_max + 10
|
||||
if self.hp_max > 50 then self.hp_max = 50 end
|
||||
airutils.setText(self, pa28.plane_text)
|
||||
else
|
||||
minetest.chat_send_player(puncher:get_player_name(), "You need steel ingots in your inventory to perform this repair.")
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- deal with painting or destroying
|
||||
if itmstck then
|
||||
|
||||
if airutils.set_paint(self, puncher, itmstck, "pa28_painting.png") == false then
|
||||
if not self.driver and toolcaps and toolcaps.damage_groups
|
||||
and toolcaps.damage_groups.fleshy and item_name ~= airutils.fuel then
|
||||
--mobkit.hurt(self,toolcaps.damage_groups.fleshy - 1)
|
||||
--mobkit.make_sound(self,'hit')
|
||||
self.hp_max = self.hp_max - 10
|
||||
minetest.sound_play("pa28_collision", {
|
||||
object = self.object,
|
||||
max_hear_distance = 5,
|
||||
gain = 1.0,
|
||||
fade = 0.0,
|
||||
pitch = 1.0,
|
||||
})
|
||||
airutils.setText(self, pa28.plane_text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.hp_max <= 0 then
|
||||
pa28.destroy(self)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end,
|
||||
|
||||
on_rightclick = function(self, clicker)
|
||||
local message = ""
|
||||
if not clicker or not clicker:is_player() then
|
||||
return
|
||||
end
|
||||
|
||||
local name = clicker:get_player_name()
|
||||
|
||||
if self.owner == "" then
|
||||
self.owner = name
|
||||
end
|
||||
|
||||
local copilot_name = nil
|
||||
if self._passenger then
|
||||
copilot_name = self._passenger
|
||||
end
|
||||
|
||||
local touching_ground, liquid_below = airutils.check_node_below(self.object, 2.5)
|
||||
local is_on_ground = self.isinliquid or touching_ground or liquid_below
|
||||
local is_under_water = airutils.check_is_under_water(self.object)
|
||||
|
||||
--minetest.chat_send_all('name '.. dump(name) .. ' - pilot: ' .. dump(self.driver_name) .. ' - pax: ' .. dump(copilot_name))
|
||||
--=========================
|
||||
-- form to pilot
|
||||
--=========================
|
||||
local is_attached = false
|
||||
local seat = clicker:get_attach()
|
||||
if seat then
|
||||
local plane = seat:get_attach()
|
||||
if plane == self.object then is_attached = true end
|
||||
end
|
||||
if name == self.driver_name then
|
||||
if is_attached then
|
||||
pa28.pilot_formspec(name)
|
||||
else
|
||||
self.driver_name = nil
|
||||
end
|
||||
--=========================
|
||||
-- detach copilot
|
||||
--=========================
|
||||
elseif name == copilot_name then
|
||||
pa28.pax_formspec(name)
|
||||
|
||||
--=========================
|
||||
-- attach pilot
|
||||
--=========================
|
||||
elseif not self.driver_name then
|
||||
if self.owner == name or minetest.check_player_privs(clicker, {protection_bypass=true}) then
|
||||
if clicker:get_player_control().aux1 == true then --lets see the inventory
|
||||
airutils.show_vehicle_trunk_formspec(self, clicker, pa28.trunk_slots)
|
||||
else
|
||||
if pa28.restricted == "true" and not minetest.check_player_privs(clicker, {flight_licence=true}) then
|
||||
minetest.show_formspec(name, "pa28:flightlicence",
|
||||
"size[4,2]" ..
|
||||
"label[0.0,0.0;Sorry ...]"..
|
||||
"label[0.0,0.7;You need a flight licence to fly it.]" ..
|
||||
"label[0.0,1.0;You must obtain it from server admin.]" ..
|
||||
"button_exit[1.5,1.9;0.9,0.1;e;Exit]")
|
||||
return
|
||||
end
|
||||
|
||||
if is_under_water then return end
|
||||
--remove pax to prevent bug
|
||||
if self._passenger then
|
||||
local pax_obj = minetest.get_player_by_name(self._passenger)
|
||||
pa28.dettach_pax(self, pax_obj)
|
||||
end
|
||||
for i = 10,1,-1
|
||||
do
|
||||
if self._passengers[i] then
|
||||
if self._passengers[i] then
|
||||
local passenger = minetest.get_player_by_name(self._passengers[i])
|
||||
if passenger then pa28.dettach_pax(self, passenger) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--attach player
|
||||
-- no driver => clicker is new driver
|
||||
pa28.attach(self, clicker)
|
||||
self._command_is_given = false
|
||||
end
|
||||
else
|
||||
pa28.dettach_pax(self, clicker)
|
||||
minetest.chat_send_player(name, core.colorize('#ff0000', " >>> You aren't the owner of this "..pa28.plane_text.."."))
|
||||
end
|
||||
|
||||
--=========================
|
||||
-- attach passenger
|
||||
--=========================
|
||||
elseif self.driver_name ~= nil or self._autoflymode == true then
|
||||
local player = minetest.get_player_by_name(self.driver_name)
|
||||
if player then
|
||||
|
||||
is_attached = pa28.check_passenger_is_attached(self, name)
|
||||
|
||||
if is_attached then
|
||||
--remove pax
|
||||
pa28.pax_formspec(name)
|
||||
--pa28.dettach_pax(self, clicker)
|
||||
else
|
||||
--attach normal passenger
|
||||
pa28.attach_pax(self, clicker)
|
||||
end
|
||||
|
||||
else
|
||||
minetest.chat_send_player(clicker:get_player_name(), message)
|
||||
end
|
||||
else
|
||||
minetest.chat_send_player(clicker:get_player_name(), message)
|
||||
end
|
||||
|
||||
end,
|
||||
})
|
190
forms.lua
Executable file
@ -0,0 +1,190 @@
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "global_definitions.lua")
|
||||
|
||||
--------------
|
||||
-- Manual --
|
||||
--------------
|
||||
|
||||
function pa28.getPlaneFromPlayer(player)
|
||||
local seat = player:get_attach()
|
||||
if seat then
|
||||
local plane = seat:get_attach()
|
||||
return plane
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function pa28.pilot_formspec(name)
|
||||
local basic_form = table.concat({
|
||||
"formspec_version[5]",
|
||||
"size[6,12]",
|
||||
}, "")
|
||||
|
||||
local player = minetest.get_player_by_name(name)
|
||||
local plane_obj = pa28.getPlaneFromPlayer(player)
|
||||
if plane_obj == nil then
|
||||
return
|
||||
end
|
||||
local ent = plane_obj:get_luaentity()
|
||||
|
||||
local pass_list = ""
|
||||
for k, v in pairs(ent._passengers) do
|
||||
pass_list = pass_list .. v .. ","
|
||||
end
|
||||
|
||||
local flap_is_down = "false"
|
||||
if ent._flap then flap_is_down = "true" end
|
||||
local door_is_open = "true"
|
||||
if ent._door_closed then door_is_open = "false" end
|
||||
|
||||
local copilot_name = "test"
|
||||
basic_form = basic_form.."button[1,1.0;4,1;turn_on;Start/Stop Engines]"
|
||||
basic_form = basic_form.."button[1,2.0;4,1;hud;Show/Hide Gauges]"
|
||||
--basic_form = basic_form.."button[1,3.0;4,1;turn_auto_pilot_on;Auto Pilot]"
|
||||
basic_form = basic_form.."button[1,4.0;4,1;pass_control;Pass the Control]"
|
||||
basic_form = basic_form.."checkbox[1,5.8;flap_is_down;Flaps down;"..flap_is_down.."]"
|
||||
basic_form = basic_form.."button[1,7.8;4,1;go_out;Go Offboard]"
|
||||
basic_form = basic_form.."label[1,10;Bring a copilot:]"
|
||||
basic_form = basic_form.."dropdown[1,10.2;4,1;copilot;"..pass_list..";0;false]"
|
||||
|
||||
minetest.show_formspec(name, "pa28:pilot_main", basic_form)
|
||||
end
|
||||
|
||||
function pa28.pax_formspec(name)
|
||||
local basic_form = table.concat({
|
||||
"formspec_version[3]",
|
||||
"size[6,5]",
|
||||
}, "")
|
||||
|
||||
basic_form = basic_form.."button[1,1.0;4,1;new_seat;Change Seat]"
|
||||
basic_form = basic_form.."button[1,2.5;4,1;go_out;Go Offboard]"
|
||||
|
||||
minetest.show_formspec(name, "pa28:passenger_main", basic_form)
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname == "pa28:passenger_main" then
|
||||
local name = player:get_player_name()
|
||||
local plane_obj = pa28.getPlaneFromPlayer(player)
|
||||
if plane_obj == nil then
|
||||
minetest.close_formspec(name, "pa28:passenger_main")
|
||||
return
|
||||
end
|
||||
local ent = plane_obj:get_luaentity()
|
||||
if fields.new_seat then
|
||||
pa28.dettach_pax(ent, player)
|
||||
pa28.attach_pax(ent, player)
|
||||
end
|
||||
if fields.go_out then
|
||||
pa28.dettach_pax(ent, player)
|
||||
end
|
||||
minetest.close_formspec(name, "pa28:passenger_main")
|
||||
end
|
||||
if formname == "pa28:pilot_main" then
|
||||
local name = player:get_player_name()
|
||||
local plane_obj = pa28.getPlaneFromPlayer(player)
|
||||
if plane_obj == nil then
|
||||
minetest.close_formspec(name, "pa28:pilot_main")
|
||||
return
|
||||
end
|
||||
local ent = plane_obj:get_luaentity()
|
||||
if fields.turn_on then
|
||||
pa28.start_engine(ent)
|
||||
end
|
||||
if fields.hud then
|
||||
if ent._show_hud == true then
|
||||
ent._show_hud = false
|
||||
else
|
||||
ent._show_hud = true
|
||||
end
|
||||
end
|
||||
if fields.turn_auto_pilot_on then
|
||||
--
|
||||
end
|
||||
if fields.pass_control then
|
||||
if ent._command_is_given == true then
|
||||
--take the control
|
||||
airutils.transfer_control(ent, false)
|
||||
else
|
||||
--trasnfer the control to student
|
||||
airutils.transfer_control(ent, true)
|
||||
end
|
||||
end
|
||||
if fields.flap_is_down then
|
||||
if fields.flap_is_down == "true" then
|
||||
ent._flap = true
|
||||
else
|
||||
ent._flap = false
|
||||
end
|
||||
minetest.sound_play("pa28_collision", {
|
||||
object = ent.object,
|
||||
max_hear_distance = 10,
|
||||
gain = 1.0,
|
||||
fade = 0.0,
|
||||
pitch = 0.5,
|
||||
}, true)
|
||||
end
|
||||
if fields.go_out then
|
||||
--=========================
|
||||
-- dettach player
|
||||
--=========================
|
||||
-- eject passenger if the plane is on ground
|
||||
local touching_ground, liquid_below = airutils.check_node_below(plane_obj, 2.5)
|
||||
if ent.isinliquid or touching_ground then --isn't flying?
|
||||
--ok, remove pax
|
||||
local passenger = nil
|
||||
if ent._passenger then
|
||||
passenger = minetest.get_player_by_name(ent._passenger)
|
||||
if passenger then pa28.dettach_pax(ent, passenger) end
|
||||
end
|
||||
for i = 10,1,-1
|
||||
do
|
||||
if ent._passengers[i] then
|
||||
passenger = minetest.get_player_by_name(ent._passengers[i])
|
||||
if passenger then
|
||||
pa28.dettach_pax(ent, passenger)
|
||||
--minetest.chat_send_all('saiu')
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
--give the control to the pax
|
||||
if ent._passenger then
|
||||
ent._autopilot = false
|
||||
airutils.transfer_control(ent, true)
|
||||
end
|
||||
end
|
||||
ent._instruction_mode = false
|
||||
pa28.dettachPlayer(ent, player)
|
||||
end
|
||||
if fields.copilot then
|
||||
--look for a free seat first
|
||||
local is_there_a_free_seat = false
|
||||
for i = 2,1,-1
|
||||
do
|
||||
if ent._passengers[i] == nil then
|
||||
is_there_a_free_seat = true
|
||||
break
|
||||
end
|
||||
end
|
||||
--then move the current copilot to a free seat
|
||||
if ent._passenger and is_there_a_free_seat then
|
||||
local copilot_player_obj = minetest.get_player_by_name(ent._passenger)
|
||||
if copilot_player_obj then
|
||||
pa28.dettach_pax(ent, copilot_player_obj)
|
||||
pa28.attach_pax(ent, copilot_player_obj)
|
||||
else
|
||||
ent._passenger = nil
|
||||
end
|
||||
end
|
||||
--so bring the new copilot
|
||||
if ent._passenger == nil then
|
||||
local new_copilot_player_obj = minetest.get_player_by_name(fields.copilot)
|
||||
if new_copilot_player_obj then
|
||||
pa28.dettach_pax(ent, new_copilot_player_obj)
|
||||
pa28.attach_pax(ent, new_copilot_player_obj, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
minetest.close_formspec(name, "pa28:pilot_main")
|
||||
end
|
||||
end)
|
50
fuel_management.lua
Executable file
@ -0,0 +1,50 @@
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "global_definitions.lua")
|
||||
|
||||
function pa28.contains(table, val)
|
||||
for k,v in pairs(table) do
|
||||
if k == val then
|
||||
return v
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function pa28.loadFuel(self, player_name)
|
||||
local player = minetest.get_player_by_name(player_name)
|
||||
local inv = player:get_inventory()
|
||||
|
||||
local itmstck=player:get_wielded_item()
|
||||
local item_name = ""
|
||||
if itmstck then item_name = itmstck:get_name() end
|
||||
|
||||
local fuel = pa28.contains(airutils.fuel, item_name)
|
||||
if fuel then
|
||||
local stack = ItemStack(item_name .. " 1")
|
||||
|
||||
if self._energy < PA28_MAX_FUEL then
|
||||
inv:remove_item("main", stack)
|
||||
self._energy = self._energy + fuel
|
||||
if self._energy > PA28_MAX_FUEL then self._energy = PA28_MAX_FUEL end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function pa28.consumptionCalc(self, accel)
|
||||
if accel == nil then return end
|
||||
if self._energy > 0 and self._engine_running and accel ~= nil then
|
||||
local consumed_power = self._power_lever/200000
|
||||
if consumed_power < 0.00005 then consumed_power = 0.00005 end
|
||||
--minetest.chat_send_all('consumed: '.. consumed_power)
|
||||
self._energy = self._energy - consumed_power;
|
||||
end
|
||||
if self._energy <= 0 and self._engine_running and accel ~= nil then
|
||||
self._engine_running = false
|
||||
self._autopilot = false
|
||||
if self.sound_handle then minetest.sound_stop(self.sound_handle) end
|
||||
self.engine:set_animation_frame_speed(0)
|
||||
end
|
||||
end
|
10
global_definitions.lua
Executable file
@ -0,0 +1,10 @@
|
||||
--
|
||||
-- constants
|
||||
--
|
||||
PA28_LONGIT_DRAG_FACTOR = 0.13*0.13
|
||||
PA28_LATER_DRAG_FACTOR = 2.0
|
||||
|
||||
deg = math.deg
|
||||
abs = math.abs
|
||||
|
||||
PA28_MAX_FUEL = 15.0
|
216
hud.lua
Executable file
@ -0,0 +1,216 @@
|
||||
pa28.hud_list = {}
|
||||
|
||||
function pa28.animate_gauge(player, ids, prefix, x, y, angle)
|
||||
local angle_in_rad = math.rad(angle + 180)
|
||||
local dim = 10
|
||||
local pos_x = math.sin(angle_in_rad) * dim
|
||||
local pos_y = math.cos(angle_in_rad) * dim
|
||||
player:hud_change(ids[prefix .. "2"], "offset", {x = pos_x + x, y = pos_y + y})
|
||||
dim = 20
|
||||
pos_x = math.sin(angle_in_rad) * dim
|
||||
pos_y = math.cos(angle_in_rad) * dim
|
||||
player:hud_change(ids[prefix .. "3"], "offset", {x = pos_x + x, y = pos_y + y})
|
||||
dim = 30
|
||||
pos_x = math.sin(angle_in_rad) * dim
|
||||
pos_y = math.cos(angle_in_rad) * dim
|
||||
player:hud_change(ids[prefix .. "4"], "offset", {x = pos_x + x, y = pos_y + y})
|
||||
dim = 40
|
||||
pos_x = math.sin(angle_in_rad) * dim
|
||||
pos_y = math.cos(angle_in_rad) * dim
|
||||
player:hud_change(ids[prefix .. "5"], "offset", {x = pos_x + x, y = pos_y + y})
|
||||
dim = 50
|
||||
pos_x = math.sin(angle_in_rad) * dim
|
||||
pos_y = math.cos(angle_in_rad) * dim
|
||||
player:hud_change(ids[prefix .. "6"], "offset", {x = pos_x + x, y = pos_y + y})
|
||||
dim = 60
|
||||
pos_x = math.sin(angle_in_rad) * dim
|
||||
pos_y = math.cos(angle_in_rad) * dim
|
||||
player:hud_change(ids[prefix .. "7"], "offset", {x = pos_x + x, y = pos_y + y})
|
||||
end
|
||||
|
||||
function pa28.update_hud(player, climb, speed)
|
||||
local player_name = player:get_player_name()
|
||||
|
||||
local screen_pos_y = -150
|
||||
local screen_pos_x = 10
|
||||
|
||||
local clb_gauge_x = screen_pos_x + 80
|
||||
local clb_gauge_y = screen_pos_y + 5
|
||||
local sp_gauge_x = screen_pos_x + 180
|
||||
local sp_gauge_y = clb_gauge_y
|
||||
|
||||
local ids = pa28.hud_list[player_name]
|
||||
if ids then
|
||||
pa28.animate_gauge(player, ids, "clb_pt_", clb_gauge_x, clb_gauge_y, climb)
|
||||
pa28.animate_gauge(player, ids, "sp_pt_", sp_gauge_x, sp_gauge_y, speed)
|
||||
else
|
||||
ids = {}
|
||||
|
||||
ids["title"] = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = screen_pos_x +140, y = screen_pos_y -100},
|
||||
text = "Flight Information",
|
||||
alignment = 0,
|
||||
scale = { x = 100, y = 30},
|
||||
number = 0xFFFFFF,
|
||||
})
|
||||
|
||||
ids["bg"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = screen_pos_x, y = screen_pos_y},
|
||||
text = "pa28_hud_panel.png",
|
||||
scale = { x = 0.5, y = 0.5},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
|
||||
ids["clb_pt_1"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = clb_gauge_x, y = clb_gauge_y},
|
||||
text = "pa28_ind_box.png",
|
||||
scale = { x = 6, y = 6},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
|
||||
ids["clb_pt_2"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = clb_gauge_x, y = clb_gauge_y},
|
||||
text = "pa28_ind_box.png",
|
||||
scale = { x = 6, y = 6},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
ids["clb_pt_3"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = clb_gauge_x, y = clb_gauge_y},
|
||||
text = "pa28_ind_box.png",
|
||||
scale = { x = 6, y = 6},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
ids["clb_pt_4"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = clb_gauge_x, y = clb_gauge_y},
|
||||
text = "pa28_ind_box.png",
|
||||
scale = { x = 6, y = 6},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
ids["clb_pt_5"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = clb_gauge_x, y = clb_gauge_y},
|
||||
text = "pa28_ind_box.png",
|
||||
scale = { x = 6, y = 6},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
ids["clb_pt_6"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = clb_gauge_x, y = clb_gauge_y},
|
||||
text = "pa28_ind_box.png",
|
||||
scale = { x = 6, y = 6},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
ids["clb_pt_7"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = clb_gauge_x, y = clb_gauge_y},
|
||||
text = "pa28_ind_box.png",
|
||||
scale = { x = 6, y = 6},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
|
||||
ids["sp_pt_1"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = sp_gauge_x, y = sp_gauge_y},
|
||||
text = "pa28_ind_box.png",
|
||||
scale = { x = 6, y = 6},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
ids["sp_pt_2"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = sp_gauge_x, y = sp_gauge_y},
|
||||
text = "pa28_ind_box.png",
|
||||
scale = { x = 6, y = 6},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
ids["sp_pt_3"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = sp_gauge_x, y = sp_gauge_y},
|
||||
text = "pa28_ind_box.png",
|
||||
scale = { x = 6, y = 6},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
ids["sp_pt_4"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = sp_gauge_x, y = sp_gauge_y},
|
||||
text = "pa28_ind_box.png",
|
||||
scale = { x = 6, y = 6},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
ids["sp_pt_5"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = sp_gauge_x, y = sp_gauge_y},
|
||||
text = "pa28_ind_box.png",
|
||||
scale = { x = 6, y = 6},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
ids["sp_pt_6"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = sp_gauge_x, y = sp_gauge_y},
|
||||
text = "pa28_ind_box.png",
|
||||
scale = { x = 6, y = 6},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
ids["sp_pt_7"] = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0, y = 1},
|
||||
offset = {x = sp_gauge_x, y = sp_gauge_y},
|
||||
text = "pa28_ind_box.png",
|
||||
scale = { x = 6, y = 6},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
|
||||
pa28.hud_list[player_name] = ids
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function pa28.remove_hud(player)
|
||||
if player then
|
||||
local player_name = player:get_player_name()
|
||||
--minetest.chat_send_all(player_name)
|
||||
local ids = pa28.hud_list[player_name]
|
||||
if ids then
|
||||
--player:hud_remove(ids["altitude"])
|
||||
--player:hud_remove(ids["time"])
|
||||
player:hud_remove(ids["title"])
|
||||
player:hud_remove(ids["bg"])
|
||||
player:hud_remove(ids["clb_pt_7"])
|
||||
player:hud_remove(ids["clb_pt_6"])
|
||||
player:hud_remove(ids["clb_pt_5"])
|
||||
player:hud_remove(ids["clb_pt_4"])
|
||||
player:hud_remove(ids["clb_pt_3"])
|
||||
player:hud_remove(ids["clb_pt_2"])
|
||||
player:hud_remove(ids["clb_pt_1"])
|
||||
player:hud_remove(ids["sp_pt_7"])
|
||||
player:hud_remove(ids["sp_pt_6"])
|
||||
player:hud_remove(ids["sp_pt_5"])
|
||||
player:hud_remove(ids["sp_pt_4"])
|
||||
player:hud_remove(ids["sp_pt_3"])
|
||||
player:hud_remove(ids["sp_pt_2"])
|
||||
player:hud_remove(ids["sp_pt_1"])
|
||||
end
|
||||
pa28.hud_list[player_name] = nil
|
||||
end
|
||||
|
||||
end
|
86
init.lua
Executable file
@ -0,0 +1,86 @@
|
||||
|
||||
|
||||
pa28={}
|
||||
pa28.gravity = tonumber(minetest.settings:get("movement_gravity")) or 9.8
|
||||
pa28.wing_angle_of_attack = 1.2
|
||||
pa28.min_speed = 6
|
||||
pa28.max_speed = 9
|
||||
pa28.max_engine_acc = 8.5
|
||||
pa28.lift = 10
|
||||
pa28.trunk_slots = 18
|
||||
pa28.plane_text = "PA28"
|
||||
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "global_definitions.lua")
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "control.lua")
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "fuel_management.lua")
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "custom_physics.lua")
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "utilities.lua")
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "entities.lua")
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "manual.lua")
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "forms.lua")
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "crafts.lua")
|
||||
|
||||
--
|
||||
-- helpers and co.
|
||||
--
|
||||
|
||||
--
|
||||
-- items
|
||||
--
|
||||
|
||||
settings = Settings(minetest.get_worldpath() .. "/settings.conf")
|
||||
local function fetch_setting(name)
|
||||
local sname = name
|
||||
return settings and settings:get(sname) or minetest.settings:get(sname)
|
||||
end
|
||||
|
||||
pa28.restricted = fetch_setting("restricted")
|
||||
|
||||
minetest.register_privilege("flight_licence", {
|
||||
description = "Gives a flight licence to the player",
|
||||
give_to_singleplayer = true
|
||||
})
|
||||
|
||||
-- add chatcommand to eject from hydroplane
|
||||
|
||||
minetest.register_chatcommand("pa28_eject", {
|
||||
params = "",
|
||||
description = "Ejects from PA28",
|
||||
privs = {interact = true},
|
||||
func = function(name, param)
|
||||
local colorstring = core.colorize('#ff0000', " >>> you are not inside your PA28")
|
||||
local player = minetest.get_player_by_name(name)
|
||||
local attached_to = player:get_attach()
|
||||
|
||||
if attached_to ~= nil then
|
||||
local seat = attached_to:get_attach()
|
||||
if seat ~= nil then
|
||||
local entity = seat:get_luaentity()
|
||||
if entity then
|
||||
if entity.name == "pa28:pa28" then
|
||||
if entity.driver_name == name then
|
||||
pa28.dettachPlayer(entity, player)
|
||||
elseif entity._passenger == name then
|
||||
local passenger = minetest.get_player_by_name(entity._passenger)
|
||||
pa28.dettach_pax(entity, passenger)
|
||||
end
|
||||
else
|
||||
minetest.chat_send_player(name,colorstring)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
minetest.chat_send_player(name,colorstring)
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("pa28_manual", {
|
||||
params = "",
|
||||
description = "PA28 operation manual",
|
||||
privs = {interact = true},
|
||||
func = function(name, param)
|
||||
pa28.manual_formspec(name)
|
||||
end
|
||||
})
|
||||
|
172
manual.lua
Executable file
@ -0,0 +1,172 @@
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "global_definitions.lua")
|
||||
|
||||
--------------
|
||||
-- Manual --
|
||||
--------------
|
||||
|
||||
function pa28.manual_formspec(name)
|
||||
local basic_form = table.concat({
|
||||
"formspec_version[3]",
|
||||
"size[16,10]",
|
||||
"background[-0.7,-0.5;17.5,11.5;pa28_manual_bg.png]"
|
||||
}, "")
|
||||
|
||||
basic_form = basic_form.."button[1.75,1.5;4,1;short;Shortcuts]"
|
||||
basic_form = basic_form.."button[1.75,3.5;4,1;panel;Panel]"
|
||||
basic_form = basic_form.."button[1.75,5.5;4,1;fuel;Refueling]"
|
||||
basic_form = basic_form.."button[1.75,7.5;4,1;op;Operation]"
|
||||
basic_form = basic_form.."button[10.25,1.5;4,1;paint;Painting]"
|
||||
basic_form = basic_form.."button[10.25,3.5;4,1;tips;Tips]"
|
||||
|
||||
minetest.show_formspec(name, "pa28:manual_main", basic_form)
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname == "pa28:manual_main" then
|
||||
if fields.short then
|
||||
local text = {
|
||||
"Shortcuts \n\n",
|
||||
"* Right click: enter in/get off plane \n",
|
||||
"* Left click (with biofuel): add fuel to plane \n",
|
||||
"* Right click and Sneak: enter in flight instructor mode \n",
|
||||
" (limited vision, so use external camera) \n",
|
||||
"* E (aux1): Start/stop engine \n",
|
||||
"* Jump: Increase power, forward on ground \n",
|
||||
"* Sneak: Decrease power, brake on ground \n",
|
||||
"* Backward: go up flying - nose up \n",
|
||||
"* Forward: go down flying - nose down \n",
|
||||
"* Left/right: Turn to left/right, work on and out ground. \n",
|
||||
"* Left and Right together: center all commands \n",
|
||||
"* Sneak and Jump together (normal): activates the autopilot \n",
|
||||
"* Sneak and Jump together (instruction mode): give/take the \n",
|
||||
" controls to/from pilot student \n",
|
||||
"* Up and Down together: enable/disable HUD \n",
|
||||
"* E and Right click: inventory (only external) \n"
|
||||
}
|
||||
local shortcut_form = table.concat({
|
||||
"formspec_version[3]",
|
||||
"size[16,10]",
|
||||
"background[-0.7,-0.5;17.5,11.5;pa28_manual_bg.png]",
|
||||
"image[0.5,3.75;6,6;pa28_manual_up_view.png]",
|
||||
"label[9.25,0.5;", table.concat(text, ""), "]",
|
||||
}, "")
|
||||
minetest.show_formspec(player:get_player_name(), "pa28:manual_shortcut", shortcut_form)
|
||||
end
|
||||
if fields.panel then
|
||||
local text = {
|
||||
"The Panel \n\n",
|
||||
"In front of the pilot is the instrument panel. \n",
|
||||
"It's used to obtain important flight information, namely: \n",
|
||||
"rate of climb/descent, speed, power applied and fuel level. \n\n",
|
||||
"The climber is the instrument that indicates the rate \n",
|
||||
" of climb and descent, it's on the left of the panel, \n",
|
||||
" marked with the letter C in blue. \n",
|
||||
"The speed indicator indicates the longitudinal speed of the \n",
|
||||
" aircraft. It's on the center of the panel and is marked \n",
|
||||
" with the letter S in white. \n",
|
||||
"The power gauge indicates the power applied to the engine. \n",
|
||||
"It's at upper right position of the panel, with an yellow P. \n",
|
||||
"The fuel gauge, located on the right and below, indicates the \n",
|
||||
"fuel available on the aircraft. It's marked with the green F."
|
||||
}
|
||||
local panel_form = table.concat({
|
||||
"formspec_version[3]",
|
||||
"size[16,10]",
|
||||
"background[-0.7,-0.5;17.5,11.5;pa28_manual_bg.png]",
|
||||
"image[0.2,1.75;7,7;pa28_manual_panel.png]",
|
||||
"label[9.25,0.5;", table.concat(text, ""), "]",
|
||||
}, "")
|
||||
minetest.show_formspec(player:get_player_name(), "pa28:manual_panel", panel_form)
|
||||
end
|
||||
if fields.fuel then
|
||||
local text = {
|
||||
"Fuel \n\n",
|
||||
"To fly, the aircraft needs fuel for its engine. So it is \n",
|
||||
"necessary to supply it. To do this, it is necessary to \n",
|
||||
"have the selected fuel in hand and punch it in the float. \n",
|
||||
"Depending on the fuel mod used and which container, a \n",
|
||||
"greater or lesser number of fuel units may be required to \n",
|
||||
"fill the tank. In the case of the Lokrates biofuel mod, \n",
|
||||
"with 10 bottles it is possible to fill the tank. With the \n",
|
||||
"vial, 40 units will be needed. \n",
|
||||
"Don't forget to check the fuel gauge on the panel."
|
||||
}
|
||||
local fuel_form = table.concat({
|
||||
"formspec_version[3]",
|
||||
"size[16,10]",
|
||||
"background[-0.7,-0.5;17.5,11.5;pa28_manual_bg.png]",
|
||||
"image[2,3.75;4,2;pa28_manual_fuel.png]",
|
||||
"label[9.25,0.5;", table.concat(text, ""), "]",
|
||||
}, "")
|
||||
minetest.show_formspec(player:get_player_name(), "pa28:fuel", fuel_form)
|
||||
end
|
||||
if fields.op then
|
||||
local text = {
|
||||
"Operation \n\n",
|
||||
"The aircraft can operate only on land. \n",
|
||||
"When boarding the aircraft, centralize the commands (A \n",
|
||||
"and D keys), press E to start the engine and hold Jump \n",
|
||||
"until full power. When the speed reaches the green range, \n",
|
||||
"lightly pull the stick using the S key. Always keep the \n",
|
||||
"speed within the green range to avoid stalling. To land, \n",
|
||||
"remove all power, but keep the speed at the limit \n",
|
||||
"between the green and white range. \n",
|
||||
"When you are about to touch the soil, lightly pull \n",
|
||||
"the stick to level and touch it gently. It's possible to \n",
|
||||
"operate with an external camera, activating the HUD. \n",
|
||||
"The autopilot (jump and sneak) only keeps the airplane at the \n",
|
||||
"activation level, limited by power and designed ceiling. \n",
|
||||
"It's possible for a passenger to board the aircraft, just \n",
|
||||
"click the right the plane. But the passenger \n",
|
||||
"will only be able to enter if the pilot has \n",
|
||||
"already boarded."
|
||||
}
|
||||
local op_form = table.concat({
|
||||
"formspec_version[3]",
|
||||
"size[16,10]",
|
||||
"background[-0.7,-0.5;17.5,11.5;pa28_manual_bg.png]",
|
||||
"image[0.5,1.75;6,6;pa28_manual_side_view.png]",
|
||||
"label[9.25,0.25;", table.concat(text, ""), "]",
|
||||
}, "")
|
||||
minetest.show_formspec(player:get_player_name(), "pa28:op", op_form)
|
||||
end
|
||||
if fields.paint then
|
||||
local text = {
|
||||
"Painting \n\n",
|
||||
"Painting the aircraft is quite simple. It works in the same \n",
|
||||
"way as the fuel supply, but instead of using fuel to punch \n",
|
||||
"the floater, use a dye of the chosen color."
|
||||
}
|
||||
local paint_form = table.concat({
|
||||
"formspec_version[3]",
|
||||
"size[16,10]",
|
||||
"background[-0.7,-0.5;17.5,11.5;pa28_manual_colors.png]",
|
||||
"label[9.25,0.5;", table.concat(text, ""), "]",
|
||||
}, "")
|
||||
minetest.show_formspec(player:get_player_name(), "pa28:paint", paint_form)
|
||||
end
|
||||
if fields.tips then
|
||||
local text = {
|
||||
"Tips \n\n",
|
||||
"* During a stall, centralize the controls (A + D shortcut) \n",
|
||||
" and apply maximum power, then gently pull the control. \n",
|
||||
"* The \"repair tool\" can repair damage suffered by the \n",
|
||||
" aircraft. To use it, have some steel ingots in the \n",
|
||||
" inventory, which will be subtracted for repair \n",
|
||||
"* When boarding as a flight instructor, use \n",
|
||||
" the external camera with the hud on. \n",
|
||||
"* As an instructor, only pass control to the student at \n",
|
||||
" altitudes that allow time for reaction, unless you \n",
|
||||
" already trust that student.",
|
||||
}
|
||||
local tips_form = table.concat({
|
||||
"formspec_version[3]",
|
||||
"size[16,10]",
|
||||
"background[-0.7,-0.5;17.5,11.5;pa28_manual_bg.png]",
|
||||
"label[0.2,0.5;", table.concat(text, ""), "]",
|
||||
}, "")
|
||||
minetest.show_formspec(player:get_player_name(), "pa28:tips", tips_form)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
5
mod.conf
Executable file
@ -0,0 +1,5 @@
|
||||
name = pa28
|
||||
depends = mobkit,default,biofuel,airutils
|
||||
author = APercy
|
||||
description = Adds a craftable PA28
|
||||
title = PA28
|
BIN
models/pa28.b3d
Executable file
BIN
models/pa28_propeller.b3d
Executable file
BIN
models/pa28_seat_base.b3d
Executable file
BIN
sounds/pa28_collision.ogg
Executable file
BIN
sounds/pa28_engine.ogg
Executable file
BIN
sounds/pa28_touch.ogg
Executable file
BIN
textures/pa28.png
Executable file
After Width: | Height: | Size: 12 KiB |
BIN
textures/pa28_black.png
Executable file
After Width: | Height: | Size: 382 B |
BIN
textures/pa28_black2.png
Executable file
After Width: | Height: | Size: 5.3 KiB |
BIN
textures/pa28_compass.png
Executable file
After Width: | Height: | Size: 43 KiB |
BIN
textures/pa28_compass_ind.png
Executable file
After Width: | Height: | Size: 8.0 KiB |
BIN
textures/pa28_compass_plan.png
Executable file
After Width: | Height: | Size: 5.5 KiB |
BIN
textures/pa28_fuselage.png
Executable file
After Width: | Height: | Size: 4.4 KiB |
BIN
textures/pa28_glass.png
Executable file
After Width: | Height: | Size: 10 KiB |
BIN
textures/pa28_grey.png
Executable file
After Width: | Height: | Size: 1.1 KiB |
BIN
textures/pa28_hud_panel.png
Executable file
After Width: | Height: | Size: 22 KiB |
BIN
textures/pa28_ind_box.png
Executable file
After Width: | Height: | Size: 4.8 KiB |
BIN
textures/pa28_interior.png
Executable file
After Width: | Height: | Size: 671 B |
BIN
textures/pa28_l_light.png
Executable file
After Width: | Height: | Size: 5.7 KiB |
BIN
textures/pa28_manual_bg.png
Executable file
After Width: | Height: | Size: 25 KiB |
BIN
textures/pa28_manual_colors.png
Executable file
After Width: | Height: | Size: 144 KiB |
BIN
textures/pa28_manual_fuel.png
Executable file
After Width: | Height: | Size: 2.2 KiB |
BIN
textures/pa28_manual_panel.png
Executable file
After Width: | Height: | Size: 98 KiB |
BIN
textures/pa28_manual_side_view.png
Executable file
After Width: | Height: | Size: 142 KiB |
BIN
textures/pa28_manual_up_view.png
Executable file
After Width: | Height: | Size: 136 KiB |
BIN
textures/pa28_metal.png
Executable file
After Width: | Height: | Size: 1.1 KiB |
BIN
textures/pa28_painting.png
Executable file
After Width: | Height: | Size: 5.4 KiB |
BIN
textures/pa28_panel.png
Executable file
After Width: | Height: | Size: 97 KiB |
BIN
textures/pa28_propeller.png
Executable file
After Width: | Height: | Size: 8.3 KiB |
BIN
textures/pa28_r_light.png
Executable file
After Width: | Height: | Size: 5.7 KiB |
BIN
textures/pa28_red.png
Executable file
After Width: | Height: | Size: 2.3 KiB |
BIN
textures/pa28_white.png
Executable file
After Width: | Height: | Size: 2.1 KiB |
BIN
textures/pa28_wing.png
Executable file
After Width: | Height: | Size: 16 KiB |
BIN
textures/pa28_wing_color.png
Executable file
After Width: | Height: | Size: 16 KiB |
BIN
textures/pa28_wings.png
Executable file
After Width: | Height: | Size: 5.1 KiB |
797
utilities.lua
Executable file
@ -0,0 +1,797 @@
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "global_definitions.lua")
|
||||
dofile(minetest.get_modpath("pa28") .. DIR_DELIM .. "hud.lua")
|
||||
|
||||
function pa28.get_hipotenuse_value(point1, point2)
|
||||
return math.sqrt((point1.x - point2.x) ^ 2 + (point1.y - point2.y) ^ 2 + (point1.z - point2.z) ^ 2)
|
||||
end
|
||||
|
||||
function pa28.dot(v1,v2)
|
||||
return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z
|
||||
end
|
||||
|
||||
function pa28.sign(n)
|
||||
return n>=0 and 1 or -1
|
||||
end
|
||||
|
||||
function pa28.minmax(v,m)
|
||||
return math.min(math.abs(v),m)*pa28.sign(v)
|
||||
end
|
||||
|
||||
function pa28.get_gauge_angle(value, initial_angle)
|
||||
initial_angle = initial_angle or 90
|
||||
local angle = value * 18
|
||||
angle = angle - initial_angle
|
||||
angle = angle * -1
|
||||
return angle
|
||||
end
|
||||
|
||||
-- attach player
|
||||
function pa28.attach(self, player, instructor_mode)
|
||||
instructor_mode = instructor_mode or false
|
||||
local name = player:get_player_name()
|
||||
self.driver_name = name
|
||||
|
||||
-- attach the driver
|
||||
local eye_y = 0
|
||||
if instructor_mode == true then
|
||||
eye_y = -2.5
|
||||
player:set_attach(self.passenger_seat_base, "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
|
||||
else
|
||||
eye_y = -4
|
||||
player:set_attach(self.pilot_seat_base, "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
|
||||
end
|
||||
if airutils.detect_player_api(player) == 1 then
|
||||
eye_y = eye_y + 6.5
|
||||
end
|
||||
|
||||
player:set_eye_offset({x = 0, y = eye_y, z = 2}, {x = 0, y = 1, z = -30})
|
||||
player_api.set_animation(player, "sit")
|
||||
player_api.player_attached[name] = true
|
||||
--player:set_physics_override({gravity = 0})
|
||||
-- make the driver sit
|
||||
minetest.after(0.3, function()
|
||||
if player then
|
||||
--minetest.chat_send_all("okay")
|
||||
player_api.set_animation(player, "sit")
|
||||
--apply_physics_override(player, {speed=0,gravity=0,jump=0})
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- attach passenger
|
||||
function pa28.check_passenger_is_attached(self, name)
|
||||
local is_attached = false
|
||||
if self._passenger == name then is_attached = true end
|
||||
if is_attached == false then
|
||||
for i = 2,1,-1
|
||||
do
|
||||
if self._passengers[i] == name then
|
||||
is_attached = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return is_attached
|
||||
end
|
||||
|
||||
-- attach passenger
|
||||
function pa28.attach_pax(self, player, is_copilot)
|
||||
local is_copilot = is_copilot or false
|
||||
local name = player:get_player_name()
|
||||
|
||||
local eye_y = -4
|
||||
if airutils.detect_player_api(player) == 1 then
|
||||
eye_y = 2.5
|
||||
end
|
||||
|
||||
if is_copilot == true then
|
||||
if self._passenger == nil and self.co_pilot_seat_base then
|
||||
self._passenger = name
|
||||
|
||||
-- attach the driver
|
||||
player:set_attach(self.co_pilot_seat_base, "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
|
||||
player:set_eye_offset({x = 0, y = eye_y, z = 2}, {x = 0, y = 3, z = -30})
|
||||
player_api.player_attached[name] = true
|
||||
-- make the driver sit
|
||||
minetest.after(0.2, function()
|
||||
player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
player_api.set_animation(player, "sit")
|
||||
--apply_physics_override(player, {speed=0,gravity=0,jump=0})
|
||||
end
|
||||
end)
|
||||
end
|
||||
else
|
||||
--randomize the seat
|
||||
local t = {1,2}
|
||||
for i = 1, #t*2 do
|
||||
local a = math.random(#t)
|
||||
local b = math.random(#t)
|
||||
t[a],t[b] = t[b],t[a]
|
||||
end
|
||||
|
||||
--for i = 1,10,1 do
|
||||
for k,v in ipairs(t) do
|
||||
i = t[k]
|
||||
if self._passengers[i] == nil then
|
||||
--minetest.chat_send_all(self.driver_name)
|
||||
self._passengers[i] = name
|
||||
player:set_attach(self._passengers_base[i], "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
|
||||
if i > 2 then
|
||||
player:set_eye_offset({x = 0, y = eye_y, z = 2}, {x = 0, y = 3, z = -30})
|
||||
else
|
||||
player:set_eye_offset({x = 0, y = eye_y, z = 0}, {x = 0, y = 3, z = -30})
|
||||
end
|
||||
player_api.player_attached[name] = true
|
||||
-- make the driver sit
|
||||
minetest.after(0.2, function()
|
||||
player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
player_api.set_animation(player, "sit")
|
||||
--apply_physics_override(player, {speed=0,gravity=0,jump=0})
|
||||
end
|
||||
end)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function pa28.dettach_pax(self, player)
|
||||
local name = player:get_player_name() --self._passenger
|
||||
|
||||
-- passenger clicked the object => driver gets off the vehicle
|
||||
if self._passenger == name then
|
||||
self._passenger = nil
|
||||
else
|
||||
for i = 4,1,-1
|
||||
do
|
||||
if self._passengers[i] == name then
|
||||
self._passengers[i] = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- detach the player
|
||||
if player then
|
||||
player:set_detach()
|
||||
player_api.player_attached[name] = nil
|
||||
player_api.set_animation(player, "stand")
|
||||
player:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
--remove_physics_override(player, {speed=1,gravity=1,jump=1})
|
||||
end
|
||||
end
|
||||
|
||||
function pa28.dettachPlayer(self, player)
|
||||
local name = self.driver_name
|
||||
airutils.setText(self, pa28.plane_text)
|
||||
|
||||
pa28.remove_hud(player)
|
||||
|
||||
--self._engine_running = false
|
||||
|
||||
-- driver clicked the object => driver gets off the vehicle
|
||||
self.driver_name = nil
|
||||
|
||||
-- detach the player
|
||||
--player:set_physics_override({speed = 1, jump = 1, gravity = 1, sneak = true})
|
||||
player:set_detach()
|
||||
player_api.player_attached[name] = nil
|
||||
player:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
player_api.set_animation(player, "stand")
|
||||
self.driver = nil
|
||||
--remove_physics_override(player, {speed=1,gravity=1,jump=1})
|
||||
end
|
||||
|
||||
function pa28.checkAttach(self, player)
|
||||
if player then
|
||||
local player_attach = player:get_attach()
|
||||
if player_attach then
|
||||
if player_attach == self.pilot_seat_base or player_attach == self.passenger_seat_base then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- destroy the boat
|
||||
function pa28.destroy(self)
|
||||
if self.sound_handle then
|
||||
minetest.sound_stop(self.sound_handle)
|
||||
self.sound_handle = nil
|
||||
end
|
||||
|
||||
if self._passenger then
|
||||
-- detach the passenger
|
||||
local passenger = minetest.get_player_by_name(self._passenger)
|
||||
if passenger then
|
||||
pa28.dettach_pax(self, passenger)
|
||||
end
|
||||
end
|
||||
|
||||
if self.driver_name then
|
||||
-- detach the driver
|
||||
local player = minetest.get_player_by_name(self.driver_name)
|
||||
pa28.dettachPlayer(self, player)
|
||||
end
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
if self.fuel_gauge then self.fuel_gauge:remove() end
|
||||
if self.power_gauge then self.power_gauge:remove() end
|
||||
if self.climb_gauge then self.climb_gauge:remove() end
|
||||
if self.speed_gauge then self.speed_gauge:remove() end
|
||||
if self.engine then self.engine:remove() end
|
||||
if self.pilot_seat_base then self.pilot_seat_base:remove() end
|
||||
if self.passenger_seat_base then self.passenger_seat_base:remove() end
|
||||
|
||||
if self.stick then self.stick:remove() end
|
||||
|
||||
airutils.destroy_inventory(self)
|
||||
self.object:remove()
|
||||
|
||||
pos.y=pos.y+2
|
||||
minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'hidroplane:wings')
|
||||
|
||||
for i=1,6 do
|
||||
minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'default:steel_ingot')
|
||||
end
|
||||
|
||||
for i=1,2 do
|
||||
minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'wool:white')
|
||||
end
|
||||
|
||||
for i=1,6 do
|
||||
minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'default:mese_crystal')
|
||||
minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'default:diamond')
|
||||
end
|
||||
|
||||
--minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'hidroplane:hidro')
|
||||
end
|
||||
|
||||
function pa28.testImpact(self, velocity, position)
|
||||
local p = position --self.object:get_pos()
|
||||
local collision = false
|
||||
if self._last_vel == nil then return end
|
||||
--lets calculate the vertical speed, to avoid the bug on colliding on floor with hard lag
|
||||
if abs(velocity.y - self._last_vel.y) > 2 then
|
||||
local noded = mobkit.nodeatpos(mobkit.pos_shift(p,{y=-2.8}))
|
||||
if (noded and noded.drawtype ~= 'airlike') then
|
||||
collision = true
|
||||
else
|
||||
self.object:set_velocity(self._last_vel)
|
||||
--self.object:set_acceleration(self._last_accell)
|
||||
self.object:set_velocity(vector.add(velocity, vector.multiply(self._last_accell, self.dtime/8)))
|
||||
end
|
||||
end
|
||||
local impact = abs(pa28.get_hipotenuse_value(velocity, self._last_vel))
|
||||
--minetest.chat_send_all('impact: '.. impact .. ' - hp: ' .. self.hp_max)
|
||||
if impact > 2 then
|
||||
--minetest.chat_send_all('impact: '.. impact .. ' - hp: ' .. self.hp_max)
|
||||
local nodeu = mobkit.nodeatpos(mobkit.pos_shift(p,{y=1}))
|
||||
local noded = mobkit.nodeatpos(mobkit.pos_shift(p,{y=-2.8}))
|
||||
local nodel = mobkit.nodeatpos(mobkit.pos_shift(p,{x=-1}))
|
||||
local noder = mobkit.nodeatpos(mobkit.pos_shift(p,{x=1}))
|
||||
local nodef = mobkit.nodeatpos(mobkit.pos_shift(p,{z=1}))
|
||||
local nodeb = mobkit.nodeatpos(mobkit.pos_shift(p,{z=-1}))
|
||||
if (nodeu and nodeu.drawtype ~= 'airlike') or
|
||||
(nodef and nodef.drawtype ~= 'airlike') or
|
||||
(nodeb and nodeb.drawtype ~= 'airlike') or
|
||||
(noder and noder.drawtype ~= 'airlike') or
|
||||
(nodel and nodel.drawtype ~= 'airlike') then
|
||||
collision = true
|
||||
end
|
||||
end
|
||||
|
||||
if impact > 1.2 and self._longit_speed > 2 then
|
||||
local noded = mobkit.nodeatpos(mobkit.pos_shift(p,{y=-2.8}))
|
||||
if (noded and noded.drawtype ~= 'airlike') then
|
||||
minetest.sound_play("pa28_touch", {
|
||||
--to_player = self.driver_name,
|
||||
object = self.object,
|
||||
max_hear_distance = 15,
|
||||
gain = 1.0,
|
||||
fade = 0.0,
|
||||
pitch = 1.0,
|
||||
}, true)
|
||||
end
|
||||
end
|
||||
|
||||
if collision then
|
||||
--self.object:set_velocity({x=0,y=0,z=0})
|
||||
local damage = impact / 2
|
||||
self.hp_max = self.hp_max - damage --subtract the impact value directly to hp meter
|
||||
minetest.sound_play("pa28_collision", {
|
||||
--to_player = self.driver_name,
|
||||
object = self.object,
|
||||
max_hear_distance = 15,
|
||||
gain = 1.0,
|
||||
fade = 0.0,
|
||||
pitch = 1.0,
|
||||
}, true)
|
||||
|
||||
if self.driver_name then
|
||||
local player_name = self.driver_name
|
||||
airutils.setText(self, pa28.plane_text)
|
||||
|
||||
--minetest.chat_send_all('damage: '.. damage .. ' - hp: ' .. self.hp_max)
|
||||
if self.hp_max < 0 then --if acumulated damage is greater than 50, adieu
|
||||
pa28.destroy(self)
|
||||
end
|
||||
|
||||
local player = minetest.get_player_by_name(player_name)
|
||||
if player then
|
||||
if player:get_hp() > 0 then
|
||||
player:set_hp(player:get_hp()-(damage/2))
|
||||
end
|
||||
end
|
||||
if self._passenger ~= nil then
|
||||
local passenger = minetest.get_player_by_name(self._passenger)
|
||||
if passenger then
|
||||
if passenger:get_hp() > 0 then
|
||||
passenger:set_hp(passenger:get_hp()-(damage/2))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function pa28.checkattachBug(self)
|
||||
-- for some engine error the player can be detached from the submarine, so lets set him attached again
|
||||
if self.owner and self.driver_name then
|
||||
-- attach the driver again
|
||||
local player = minetest.get_player_by_name(self.owner)
|
||||
if player then
|
||||
if player:get_hp() > 0 then
|
||||
pa28.attach(self, player, self._instruction_mode)
|
||||
else
|
||||
pa28.dettachPlayer(self, player)
|
||||
end
|
||||
else
|
||||
if self._passenger ~= nil and self._command_is_given == false then
|
||||
self._autopilot = false
|
||||
airutils.transfer_control(self, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pa28.engineSoundPlay(self)
|
||||
--sound
|
||||
if self.sound_handle then minetest.sound_stop(self.sound_handle) end
|
||||
if self.object then
|
||||
self.sound_handle = minetest.sound_play({name = "pa28_engine"},
|
||||
{object = self.object, gain = 2.0,
|
||||
pitch = 0.5 + ((self._power_lever/100)/2),
|
||||
max_hear_distance = 15,
|
||||
loop = true,})
|
||||
end
|
||||
end
|
||||
|
||||
function pa28.engine_set_sound_and_animation(self)
|
||||
--minetest.chat_send_all('test1 ' .. dump(self._engine_running) )
|
||||
if self._engine_running then
|
||||
if self._last_applied_power ~= self._power_lever then
|
||||
--minetest.chat_send_all('test2')
|
||||
self._last_applied_power = self._power_lever
|
||||
self.engine:set_animation_frame_speed(60 + self._power_lever)
|
||||
pa28.engineSoundPlay(self)
|
||||
end
|
||||
else
|
||||
if self.sound_handle then
|
||||
minetest.sound_stop(self.sound_handle)
|
||||
self.sound_handle = nil
|
||||
self.engine:set_animation_frame_speed(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pa28.start_engine(self)
|
||||
if self._engine_running then
|
||||
self._engine_running = false
|
||||
-- sound and animation
|
||||
if self.sound_handle then
|
||||
minetest.sound_stop(self.sound_handle)
|
||||
self.sound_handle = nil
|
||||
end
|
||||
self.engine:set_animation_frame_speed(0)
|
||||
self._power_lever = 0 --zero power
|
||||
elseif self._engine_running == false and self._energy > 0 then
|
||||
self._engine_running = true
|
||||
-- sound and animation
|
||||
pa28.engineSoundPlay(self)
|
||||
self.engine:set_animation_frame_speed(60)
|
||||
end
|
||||
end
|
||||
|
||||
function pa28.flap_on(self)
|
||||
self._wing_configuration = 2.5
|
||||
self.object:set_bone_position("flap.l", {x=0, y=0, z=0}, {x=-30, y=0, z=0})
|
||||
self.object:set_bone_position("flap.r", {x=0, y=0, z=0}, {x=-30, y=0, z=0})
|
||||
end
|
||||
|
||||
function pa28.flap_off(self)
|
||||
self._wing_configuration = pa28.wing_angle_of_attack
|
||||
self.object:set_bone_position("flap.l", {x=0, y=0, z=0}, {x=0, y=0, z=0})
|
||||
self.object:set_bone_position("flap.r", {x=0, y=0, z=0}, {x=0, y=0, z=0})
|
||||
end
|
||||
|
||||
function pa28.flap_operate(self, player)
|
||||
if self._flap == false then
|
||||
minetest.chat_send_player(player:get_player_name(), ">>> Flap down")
|
||||
self._flap = true
|
||||
pa28.flap_on(self)
|
||||
minetest.sound_play("pa28_collision", {
|
||||
object = self.object,
|
||||
max_hear_distance = 15,
|
||||
gain = 1.0,
|
||||
fade = 0.0,
|
||||
pitch = 0.5,
|
||||
}, true)
|
||||
else
|
||||
minetest.chat_send_player(player:get_player_name(), ">>> Flap up")
|
||||
self._flap = false
|
||||
pa28.flap_off(self)
|
||||
minetest.sound_play("pa28_collision", {
|
||||
object = self.object,
|
||||
max_hear_distance = 15,
|
||||
gain = 1.0,
|
||||
fade = 0.0,
|
||||
pitch = 0.7,
|
||||
}, true)
|
||||
end
|
||||
end
|
||||
|
||||
function pa28.flightstep(self)
|
||||
local velocity = self.object:get_velocity()
|
||||
local curr_pos = self.object:get_pos()
|
||||
|
||||
self._last_time_command = self._last_time_command + self.dtime
|
||||
|
||||
if self._last_time_command > 1 then self._last_time_command = 1 end
|
||||
|
||||
local player = nil
|
||||
if self.driver_name then player = minetest.get_player_by_name(self.driver_name) end
|
||||
local passenger = nil
|
||||
if self._passenger then passenger = minetest.get_player_by_name(self._passenger) end
|
||||
|
||||
if player then
|
||||
local ctrl = player:get_player_control()
|
||||
---------------------
|
||||
-- change the driver
|
||||
---------------------
|
||||
if passenger and self._last_time_command >= 1 and self._instruction_mode == true then
|
||||
if self._command_is_given == true then
|
||||
if ctrl.sneak or ctrl.jump or ctrl.up or ctrl.down or ctrl.right or ctrl.left then
|
||||
self._last_time_command = 0
|
||||
--take the control
|
||||
airutils.transfer_control(self, false)
|
||||
end
|
||||
else
|
||||
if ctrl.sneak == true and ctrl.jump == true then
|
||||
self._last_time_command = 0
|
||||
--trasnfer the control to student
|
||||
airutils.transfer_control(self, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
-----------
|
||||
--autopilot
|
||||
-----------
|
||||
if self._instruction_mode == false and self._last_time_command >= 1 then
|
||||
if self._autopilot == true then
|
||||
if ctrl.sneak or ctrl.jump or ctrl.up or ctrl.down or ctrl.right or ctrl.left then
|
||||
self._last_time_command = 0
|
||||
self._autopilot = false
|
||||
minetest.chat_send_player(self.driver_name," >>> Autopilot deactivated")
|
||||
end
|
||||
else
|
||||
if ctrl.sneak == true and ctrl.jump == true then
|
||||
self._last_time_command = 0
|
||||
self._autopilot = true
|
||||
self._auto_pilot_altitude = curr_pos.y
|
||||
minetest.chat_send_player(self.driver_name,core.colorize('#00ff00', " >>> Autopilot on"))
|
||||
end
|
||||
end
|
||||
end
|
||||
----------------------------------
|
||||
-- shows the hud for the player
|
||||
----------------------------------
|
||||
if ctrl.up == true and ctrl.down == true and self._last_time_command >= 1 then
|
||||
self._last_time_command = 0
|
||||
if self._show_hud == true then
|
||||
self._show_hud = false
|
||||
else
|
||||
self._show_hud = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local accel_y = self.object:get_acceleration().y
|
||||
local rotation = self.object:get_rotation()
|
||||
local yaw = rotation.y
|
||||
local newyaw=yaw
|
||||
local pitch = rotation.x
|
||||
local roll = rotation.z
|
||||
local newroll=roll
|
||||
if newroll > 360 then newroll = newroll - 360 end
|
||||
if newroll < -360 then newroll = newroll + 360 end
|
||||
|
||||
local hull_direction = mobkit.rot_to_dir(rotation) --minetest.yaw_to_dir(yaw)
|
||||
local nhdir = {x=hull_direction.z,y=0,z=-hull_direction.x} -- lateral unit vector
|
||||
|
||||
local longit_speed = vector.dot(velocity,hull_direction)
|
||||
self._longit_speed = longit_speed
|
||||
local longit_drag = vector.multiply(hull_direction,longit_speed*
|
||||
longit_speed*PA28_LONGIT_DRAG_FACTOR*-1*pa28.sign(longit_speed))
|
||||
local later_speed = pa28.dot(velocity,nhdir)
|
||||
--minetest.chat_send_all('later_speed: '.. later_speed)
|
||||
local later_drag = vector.multiply(nhdir,later_speed*later_speed*
|
||||
PA28_LATER_DRAG_FACTOR*-1*pa28.sign(later_speed))
|
||||
local accel = vector.add(longit_drag,later_drag)
|
||||
local stop = false
|
||||
|
||||
local node_bellow = mobkit.nodeatpos(mobkit.pos_shift(curr_pos,{y=-1.51}))
|
||||
local is_flying = true
|
||||
if node_bellow and node_bellow.drawtype ~= 'airlike' then is_flying = false end
|
||||
--if is_flying then minetest.chat_send_all('is flying') end
|
||||
|
||||
local is_attached = pa28.checkAttach(self, player)
|
||||
|
||||
--ajustar angulo de ataque
|
||||
local percentage = math.abs(((longit_speed * 100)/(pa28.min_speed + 5))/100)
|
||||
if percentage > 1.5 then percentage = 1.5 end
|
||||
self._angle_of_attack = self._angle_of_attack - ((self._elevator_angle / 20)*percentage)
|
||||
if self._angle_of_attack < -0.5 then
|
||||
self._angle_of_attack = -0.1
|
||||
self._elevator_angle = self._elevator_angle - 0.1
|
||||
end --limiting the negative angle]]--
|
||||
if self._angle_of_attack > 20 then
|
||||
self._angle_of_attack = 20
|
||||
self._elevator_angle = self._elevator_angle + 0.1
|
||||
end --limiting the very high climb angle due to strange behavior]]--
|
||||
|
||||
--minetest.chat_send_all(self._angle_of_attack)
|
||||
|
||||
-- pitch
|
||||
local speed_factor = 0
|
||||
if longit_speed > pa28.min_speed then speed_factor = (velocity.y * math.rad(1)) end
|
||||
local newpitch = math.rad(self._angle_of_attack) + speed_factor
|
||||
|
||||
|
||||
-- adjust pitch at ground
|
||||
local tail_lift_min_speed = 4
|
||||
local tail_angle = 0
|
||||
if math.abs(longit_speed) < tail_lift_min_speed then
|
||||
newpitch = math.rad(tail_angle)
|
||||
end
|
||||
|
||||
|
||||
-- new yaw
|
||||
if math.abs(self._rudder_angle)>1.5 then
|
||||
local turn_rate = math.rad(14)
|
||||
local yaw_turn = self.dtime * math.rad(self._rudder_angle) * turn_rate *
|
||||
pa28.sign(longit_speed) * math.abs(longit_speed/2)
|
||||
newyaw = yaw + yaw_turn
|
||||
end
|
||||
|
||||
--roll adjust
|
||||
---------------------------------
|
||||
local delta = 0.002
|
||||
if is_flying then
|
||||
local roll_reference = newyaw
|
||||
local sdir = minetest.yaw_to_dir(roll_reference)
|
||||
local snormal = {x=sdir.z,y=0,z=-sdir.x} -- rightside, dot is negative
|
||||
local prsr = pa28.dot(snormal,nhdir)
|
||||
local rollfactor = -90
|
||||
local roll_rate = math.rad(10)
|
||||
newroll = (prsr*math.rad(rollfactor)) * (later_speed * roll_rate) * pa28.sign(longit_speed)
|
||||
--minetest.chat_send_all('newroll: '.. newroll)
|
||||
else
|
||||
delta = 0.2
|
||||
if roll > 0 then
|
||||
newroll = roll - delta
|
||||
if newroll < 0 then newroll = 0 end
|
||||
end
|
||||
if roll < 0 then
|
||||
newroll = roll + delta
|
||||
if newroll > 0 then newroll = 0 end
|
||||
end
|
||||
end
|
||||
|
||||
---------------------------------
|
||||
-- end roll
|
||||
|
||||
if not is_attached then
|
||||
-- for some engine error the player can be detached from the machine, so lets set him attached again
|
||||
pa28.checkattachBug(self)
|
||||
end
|
||||
|
||||
local pilot = player
|
||||
if self._command_is_given and passenger then
|
||||
pilot = passenger
|
||||
else
|
||||
self._command_is_given = false
|
||||
end
|
||||
|
||||
------------------------------------------------------
|
||||
--accell calculation block
|
||||
------------------------------------------------------
|
||||
if is_attached or passenger then
|
||||
if self._autopilot ~= true then
|
||||
accel, stop = pa28.control(self, self.dtime, hull_direction,
|
||||
longit_speed, longit_drag, later_speed, later_drag, accel, pilot, is_flying)
|
||||
else
|
||||
accel = pa28.autopilot(self, self.dtime, hull_direction, longit_speed, accel, curr_pos)
|
||||
end
|
||||
end
|
||||
|
||||
--end accell
|
||||
|
||||
if accel == nil then accel = {x=0,y=0,z=0} end
|
||||
|
||||
--lift calculation
|
||||
accel.y = accel_y
|
||||
|
||||
--lets apply some bob in water
|
||||
if self.isinliquid then
|
||||
local bob = pa28.minmax(pa28.dot(accel,hull_direction),0.2) -- vertical bobbing
|
||||
accel.y = accel.y + bob
|
||||
local max_pitch = 6
|
||||
local h_vel_compensation = (((longit_speed * 2) * 100)/max_pitch)/100
|
||||
if h_vel_compensation < 0 then h_vel_compensation = 0 end
|
||||
if h_vel_compensation > max_pitch then h_vel_compensation = max_pitch end
|
||||
newpitch = newpitch + (velocity.y * math.rad(max_pitch - h_vel_compensation))
|
||||
end
|
||||
|
||||
local new_accel = accel
|
||||
if longit_speed > 1.5 then
|
||||
new_accel = airutils.getLiftAccel(self, velocity, new_accel, longit_speed, roll, curr_pos, pa28.lift, 15000, 12) --I added more 3 meters for wingspan to increase the ground effect for the low wing (the wingspan variable is only used for ground effect)
|
||||
end
|
||||
-- end lift
|
||||
|
||||
if stop ~= true then --maybe == nil
|
||||
self._last_accell = new_accel
|
||||
self.object:move_to(curr_pos)
|
||||
--self.object:set_velocity(velocity)
|
||||
if player then
|
||||
pa28.attach(self, player, self._instruction_mode)
|
||||
end
|
||||
mobkit.set_acceleration(self.object, new_accel)
|
||||
else
|
||||
if stop == true then
|
||||
self.object:set_acceleration({x=0,y=0,z=0})
|
||||
self.object:set_velocity({x=0,y=0,z=0})
|
||||
end
|
||||
end
|
||||
|
||||
if is_flying == false then --isn't flying?
|
||||
--animate wheels
|
||||
if math.abs(longit_speed) > 0.2 then
|
||||
self.object:set_animation_frame_speed(longit_speed * 20)
|
||||
else
|
||||
self.object:set_animation_frame_speed(0)
|
||||
end
|
||||
else
|
||||
--stop wheels
|
||||
self.object:set_animation_frame_speed(0)
|
||||
end
|
||||
|
||||
|
||||
|
||||
------------------------------------------------------
|
||||
-- end accell
|
||||
------------------------------------------------------
|
||||
|
||||
------------------------------------------------------
|
||||
-- sound and animation
|
||||
------------------------------------------------------
|
||||
pa28.engine_set_sound_and_animation(self)
|
||||
------------------------------------------------------
|
||||
|
||||
--adjust climb indicator
|
||||
local climb_rate = velocity.y
|
||||
if climb_rate > 5 then climb_rate = 5 end
|
||||
if climb_rate < -5 then
|
||||
climb_rate = -5
|
||||
end
|
||||
|
||||
--is an stall, force a recover
|
||||
if longit_speed < (pa28.min_speed / 2) and climb_rate < -3.5 and is_flying then
|
||||
self._elevator_angle = 0
|
||||
self._angle_of_attack = -2
|
||||
newpitch = math.rad(self._angle_of_attack)
|
||||
end
|
||||
|
||||
--minetest.chat_send_all('rate '.. climb_rate)
|
||||
local climb_angle = pa28.get_gauge_angle(climb_rate)
|
||||
self.object:set_bone_position("climber", {x=-1.98,y=2.40,z=10.2}, {x=0,y=0,z=climb_angle-90})
|
||||
|
||||
local indicated_speed = longit_speed * 0.9
|
||||
if indicated_speed < 0 then indicated_speed = 0 end
|
||||
local speed_angle = pa28.get_gauge_angle(indicated_speed, -45)
|
||||
self.object:set_bone_position("speed", {x=-7.01,y=1.26,z=10.2}, {x=0,y=0,z=speed_angle})
|
||||
|
||||
local energy_indicator_angle = pa28.get_gauge_angle((PA28_MAX_FUEL - self._energy)/1.5) - 90
|
||||
self.object:set_bone_position("fuel", {x=0, y=0, z=10.2}, {x=0, y=0, z=-energy_indicator_angle+180})
|
||||
|
||||
self.object:set_bone_position("compass", {x=0, y=2.8, z=10.3}, {x=0, y=0, z=-(math.deg(newyaw))})
|
||||
self.object:set_bone_position("compass_plan", {x=0, y=2.8, z=10.25}, {x=0, y=0, z=0})
|
||||
|
||||
--altimeter
|
||||
local altitude = (curr_pos.y / 0.32) / 100
|
||||
local hour, minutes = math.modf( altitude )
|
||||
hour = math.fmod (hour, 10)
|
||||
minutes = minutes * 100
|
||||
minutes = (minutes * 100) / 100
|
||||
local minute_angle = (minutes*-360)/100
|
||||
local hour_angle = (hour*-360)/10 + ((minute_angle*36)/360)
|
||||
self.object:set_bone_position("altimeter_pt_1", {x=-4.63, y=2.4, z=10.2}, {x=0, y=0, z=(hour_angle)})
|
||||
self.object:set_bone_position("altimeter_pt_2", {x=-4.63, y=2.4, z=10.2}, {x=0, y=0, z=(minute_angle)})
|
||||
|
||||
|
||||
if is_attached then
|
||||
if self._show_hud then
|
||||
pa28.update_hud(player, climb_angle, speed_angle)
|
||||
else
|
||||
pa28.remove_hud(player)
|
||||
end
|
||||
end
|
||||
|
||||
--adjust power indicator
|
||||
local power_indicator_angle = pa28.get_gauge_angle(self._power_lever/6.5)
|
||||
self.object:set_bone_position("power", {x=2.8,y=2.40,z=10.2}, {x=0,y=0,z=power_indicator_angle - 90})
|
||||
|
||||
if is_flying == false then
|
||||
-- new yaw
|
||||
local turn_rate = math.rad(30)
|
||||
local yaw_turn = self.dtime * math.rad(self._rudder_angle) * turn_rate *
|
||||
pa28.sign(longit_speed) * math.abs(longit_speed/2)
|
||||
newyaw = yaw + yaw_turn
|
||||
end
|
||||
--apply rotations
|
||||
self.object:set_rotation({x=newpitch,y=newyaw,z=newroll})
|
||||
--end
|
||||
|
||||
--adjust elevator pitch (3d model)
|
||||
self.object:set_bone_position("elevator", {x=0, y=2.5, z=-45}, {x=(-self._elevator_angle/3) - 90, y=0, z=0})
|
||||
--adjust rudder
|
||||
self.object:set_bone_position("rudder", {x=0,y=0,z=0}, {x=0,y=self._rudder_angle,z=0})
|
||||
--adjust ailerons
|
||||
self.object:set_bone_position("aileron.r", {x=0,y=0,z=0}, {x=-self._rudder_angle - 90,y=0,z=0})
|
||||
self.object:set_bone_position("aileron.l", {x=0,y=0,z=0}, {x=self._rudder_angle - 90,y=0,z=0})
|
||||
|
||||
--set stick position
|
||||
local stick_z = 9 + (self._elevator_angle / pa28.elevator_limit )
|
||||
self.object:set_bone_position("stick.l", {x=-4.25, y=0.5, z=stick_z}, {x=0,y=0,z=self._rudder_angle})
|
||||
self.object:set_bone_position("stick.r", {x=4.25, y=0.5, z=stick_z}, {x=0,y=0,z=self._rudder_angle})
|
||||
|
||||
if self._wing_configuration == pa28.wing_angle_of_attack and self._flap then
|
||||
pa28.flap_on(self)
|
||||
end
|
||||
if self._wing_configuration ~= pa28.wing_angle_of_attack and self._flap == false then
|
||||
pa28.flap_off(self)
|
||||
end
|
||||
|
||||
if longit_speed > 8.5 and self._flap == true then
|
||||
if is_attached and self.driver_name then
|
||||
minetest.chat_send_player(self.driver_name, core.colorize('#ff0000', " >>> Flap retracted due for overspeed"))
|
||||
end
|
||||
self._flap = false
|
||||
end
|
||||
|
||||
|
||||
-- calculate energy consumption --
|
||||
pa28.consumptionCalc(self, accel)
|
||||
|
||||
--test collision
|
||||
pa28.testImpact(self, velocity, curr_pos)
|
||||
|
||||
--saves last velocity for collision detection (abrupt stop)
|
||||
self._last_vel = self.object:get_velocity()
|
||||
end
|
||||
|