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
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
Version 3, 19 November 2007
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates
|
||||||
The GNU Affero General Public License is a free, copyleft license for
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
software and other kinds of works, specifically designed to ensure
|
License, supplemented by the additional permissions listed below.
|
||||||
cooperation with the community in the case of network server software.
|
|
||||||
|
0. Additional Definitions.
|
||||||
The licenses for most software and other practical works are designed
|
|
||||||
to take away your freedom to share and change the works. By contrast,
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
our General Public Licenses are intended to guarantee your freedom to
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
share and change all versions of a program--to make sure it remains free
|
General Public License.
|
||||||
software for all its users.
|
|
||||||
|
"The Library" refers to a covered work governed by this License,
|
||||||
When we speak of free software, we are referring to freedom, not
|
other than an Application or a Combined Work as defined below.
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
|
||||||
have the freedom to distribute copies of free software (and charge for
|
An "Application" is any work that makes use of an interface provided
|
||||||
them if you wish), that you receive source code or can get it if you
|
by the Library, but which is not otherwise based on the Library.
|
||||||
want it, that you can change the software or use pieces of it in new
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
free programs, and that you know you can do these things.
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
Developers that use our General Public Licenses protect your rights
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
with two steps: (1) assert copyright on the software, and (2) offer
|
Application with the Library. The particular version of the Library
|
||||||
you this License which gives you legal permission to copy, distribute
|
with which the Combined Work was made is also called the "Linked
|
||||||
and/or modify the software.
|
Version".
|
||||||
|
|
||||||
A secondary benefit of defending all users' freedom is that
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
improvements made in alternate versions of the program, if they
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
receive widespread use, become available for other developers to
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
incorporate. Many developers of free software are heartened and
|
based on the Application, and not on the Linked Version.
|
||||||
encouraged by the resulting cooperation. However, in the case of
|
|
||||||
software used on network servers, this result may fail to come about.
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
The GNU General Public License permits making a modified version and
|
object code and/or source code for the Application, including any data
|
||||||
letting the public access it on a server without ever releasing its
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
source code to the public.
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
The GNU Affero General Public License is designed specifically to
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
ensure that, in such cases, the modified source code becomes available
|
|
||||||
to the community. It requires the operator of a network server to
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
provide the source code of the modified version running there to the
|
without being bound by section 3 of the GNU GPL.
|
||||||
users of that server. Therefore, public use of a modified version, on
|
|
||||||
a publicly accessible server, gives the public access to the source
|
2. Conveying Modified Versions.
|
||||||
code of the modified version.
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
An older license, called the Affero General Public License and
|
facility refers to a function or data to be supplied by an Application
|
||||||
published by Affero, was designed to accomplish similar goals. This is
|
that uses the facility (other than as an argument passed when the
|
||||||
a different license, not a version of the Affero GPL, but Affero has
|
facility is invoked), then you may convey a copy of the modified
|
||||||
released a new version of the Affero GPL which permits relicensing under
|
version:
|
||||||
this license.
|
|
||||||
|
a) under this License, provided that you make a good faith effort to
|
||||||
The precise terms and conditions for copying, distribution and
|
ensure that, in the event an Application does not supply the
|
||||||
modification follow.
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
TERMS AND CONDITIONS
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
0. Definitions.
|
this License applicable to that copy.
|
||||||
|
|
||||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
The object code form of an Application may incorporate material from
|
||||||
works, such as semiconductor masks.
|
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
|
||||||
"The Program" refers to any copyrightable work licensed under this
|
material is not limited to numerical parameters, data structure
|
||||||
License. Each licensee is addressed as "you". "Licensees" and
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
"recipients" may be individuals or organizations.
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
To "modify" a work means to copy from or adapt all or part of the work
|
a) Give prominent notice with each copy of the object code that the
|
||||||
in a fashion requiring copyright permission, other than the making of an
|
Library is used in it and that the Library and its use are
|
||||||
exact copy. The resulting work is called a "modified version" of the
|
covered by this License.
|
||||||
earlier work or a work "based on" the earlier work.
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
A "covered work" means either the unmodified Program or a work based
|
document.
|
||||||
on the Program.
|
|
||||||
|
4. Combined Works.
|
||||||
To "propagate" a work means to do anything with it that, without
|
|
||||||
permission, would make you directly or secondarily liable for
|
You may convey a Combined Work under terms of your choice that,
|
||||||
infringement under applicable copyright law, except executing it on a
|
taken together, effectively do not restrict modification of the
|
||||||
computer or modifying a private copy. Propagation includes copying,
|
portions of the Library contained in the Combined Work and reverse
|
||||||
distribution (with or without modification), making available to the
|
engineering for debugging such modifications, if you also do each of
|
||||||
public, and in some countries other activities as well.
|
the following:
|
||||||
|
|
||||||
To "convey" a work means any kind of propagation that enables other
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
parties to make or receive copies. Mere interaction with a user through
|
the Library is used in it and that the Library and its use are
|
||||||
a computer network, with no transfer of a copy, is not conveying.
|
covered by this License.
|
||||||
|
|
||||||
An interactive user interface displays "Appropriate Legal Notices"
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
to the extent that it includes a convenient and prominently visible
|
document.
|
||||||
feature that (1) displays an appropriate copyright notice, and (2)
|
|
||||||
tells the user that there is no warranty for the work (except to the
|
c) For a Combined Work that displays copyright notices during
|
||||||
extent that warranties are provided), that licensees may convey the
|
execution, include the copyright notice for the Library among
|
||||||
work under this License, and how to view a copy of this License. If
|
these notices, as well as a reference directing the user to the
|
||||||
the interface presents a list of user commands or options, such as a
|
copies of the GNU GPL and this license document.
|
||||||
menu, a prominent item in the list meets this criterion.
|
|
||||||
|
d) Do one of the following:
|
||||||
1. Source Code.
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
The "source code" for a work means the preferred form of the work
|
License, and the Corresponding Application Code in a form
|
||||||
for making modifications to it. "Object code" means any non-source
|
suitable for, and under terms that permit, the user to
|
||||||
form of a work.
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
A "Standard Interface" means an interface that either is an official
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
standard defined by a recognized standards body, or, in the case of
|
Corresponding Source.
|
||||||
interfaces specified for a particular programming language, one that
|
|
||||||
is widely used among developers working in that language.
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
The "System Libraries" of an executable work include anything, other
|
a copy of the Library already present on the user's computer
|
||||||
than the work as a whole, that (a) is included in the normal form of
|
system, and (b) will operate properly with a modified version
|
||||||
packaging a Major Component, but which is not part of that Major
|
of the Library that is interface-compatible with the Linked
|
||||||
Component, and (b) serves only to enable use of the work with that
|
Version.
|
||||||
Major Component, or to implement a Standard Interface for which an
|
|
||||||
implementation is available to the public in source code form. A
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
"Major Component", in this context, means a major essential component
|
be required to provide such information under section 6 of the
|
||||||
(kernel, window system, and so on) of the specific operating system
|
GNU GPL, and only to the extent that such information is
|
||||||
(if any) on which the executable work runs, or a compiler used to
|
necessary to install and execute a modified version of the
|
||||||
produce the work, or an object code interpreter used to run it.
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
The "Corresponding Source" for a work in object code form means all
|
you use option 4d0, the Installation Information must accompany
|
||||||
the source code needed to generate, install, and (for an executable
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
work) run the object code and to modify the work, including scripts to
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
control those activities. However, it does not include the work's
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
System Libraries, or general-purpose tools or generally available free
|
for conveying Corresponding Source.)
|
||||||
programs which are used unmodified in performing those activities but
|
|
||||||
which are not part of the work. For example, Corresponding Source
|
5. Combined Libraries.
|
||||||
includes interface definition files associated with source files for
|
|
||||||
the work, and the source code for shared libraries and dynamically
|
You may place library facilities that are a work based on the
|
||||||
linked subprograms that the work is specifically designed to require,
|
Library side by side in a single library together with other library
|
||||||
such as by intimate data communication or control flow between those
|
facilities that are not Applications and are not covered by this
|
||||||
subprograms and other parts of the work.
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
The Corresponding Source need not include anything that users
|
|
||||||
can regenerate automatically from other parts of the Corresponding
|
a) Accompany the combined library with a copy of the same work based
|
||||||
Source.
|
on the Library, uncombined with any other library facilities,
|
||||||
|
conveyed under the terms of this License.
|
||||||
The Corresponding Source for a work in source code form is that
|
|
||||||
same work.
|
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
|
||||||
2. Basic Permissions.
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
All rights granted under this License are granted for the term of
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
copyright on the Program, and are irrevocable provided the stated
|
|
||||||
conditions are met. This License explicitly affirms your unlimited
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
permission to run the unmodified Program. The output from running a
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
covered work is covered by this License only if the output, given its
|
versions will be similar in spirit to the present version, but may
|
||||||
content, constitutes a covered work. This License acknowledges your
|
differ in detail to address new problems or concerns.
|
||||||
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.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the
|
Each version is given a distinguishing version number. If the
|
||||||
Program specifies that a certain numbered version of the GNU Affero General
|
Library as you received it specifies that a certain numbered version
|
||||||
Public License "or any later version" applies to it, you have the
|
of the GNU Lesser General Public License "or any later version"
|
||||||
option of following the terms and conditions either of that numbered
|
applies to it, you have the option of following the terms and
|
||||||
version or of any later version published by the Free Software
|
conditions either of that published version or of any later version
|
||||||
Foundation. If the Program does not specify a version number of the
|
published by the Free Software Foundation. If the Library as you
|
||||||
GNU Affero General Public License, you may choose any version ever published
|
received it does not specify a version number of the GNU Lesser
|
||||||
by the Free Software Foundation.
|
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
|
If the Library as you received it specifies that a proxy can decide
|
||||||
versions of the GNU Affero General Public License can be used, that proxy's
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
public statement of acceptance of a version permanently authorizes you
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
to choose that version for the Program.
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
||||||
|
|
||||||
Later license versions may give you additional or different
|
Copyright Alexsandro Percy - 2021
|
||||||
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/>.
|
|
||||||
|
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
|
||||||
|
|