Version 1.0.0

master
Noodlemire 2021-08-29 20:24:51 -05:00
commit f44d862e7e
58 changed files with 3993 additions and 0 deletions

458
LICENSE.txt Normal file
View File

@ -0,0 +1,458 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
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
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS

53
README.md Normal file
View File

@ -0,0 +1,53 @@
-------------------------------------------------------------------------------------------------------------
Complete and Total Lua-Only Inventory Rewrite
[lua_inv]
-------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------
About
-------------------------------------------------------------------------------------------------------------
**!Warning!**: This is an abonination.
**!Warning!**: There is no way to automatically make this mod compatible with others. Support for any other mod that uses inventories or formspecs will probably need to be manually created in the near (or far) future.
**!Warning!**: This mod will replace and delete players' previous inventories, if they existed. It will attempt to import old items to the new system, but there is currently no way to put them back. USE THIS MOD IN A FRESH WORLD unless you either never remove lua_inv, or you're fine with an inventory wipe.
This mod attempts to create a total replacement for Minetest's entire inventory system. This does *not* just provide polishing or workarounds for the various shortcomings of Minetest's inventories. Instead, absolutely everything is custom-made in Lua. MetaData, ItemStacks, and Inventory objects are all created from scratch in this system, granting it total control over the very nature of these objects.
So why was this made in the first place? As mentioned previously, Minetest's regular inventory system has a variety of shortcomings and missing features. Some have somewhat decent workarounds, but others are virtually impossible, and there are times where a workaround just doesn't provide a proper solution. Below this is a list of some, but not all of the particular shortcomings that inspired lua_inv.
* Items that can change their appearances. The common workaround is to create an entire registration for every single possible visual state that the item can have. This can work on a small or medium scale, but it has the potential to either be excessive, or unworkable if you don't know every possible visual at the time of registration.
* Items that have animated sprites. There is normally no true workaround for this, except to give up and just use a still image.
* Stackable items that can have durability applied to them. In normal Minetest, count and wear are mutually exclusive, and there is no true, bug-free way to implement having them both be together.
* Inventory callbacks that apply to Lua functions. By default, move/put/take callbacks are completely bypassed by Lua code, or by just *picking up an item off of the ground*, unless the programmer of each inventory-related mod goes out of their way to try to respect these. In some cases, they might as well not exist.
By creating lua_inventory, the above features and more are finally possible. Every object that this new system uses is built from the ground-up to be more configurable and feature-rich than anything in Minetest's inventories.
* You can use ItemStack's MetaData to set the "inventory_image" field to any image file, allowing item images to be changed at any time.
* You can set _lua_inv_animation() in any item's definition, and this will allow it to be animated in inventory, hotbar, wielded_item, and item entity forms. There are even Meta fields to change the item's animation parameters at any time.
* Setting count and wear on an object is supported natively. Just give tool_capabilities to a craftitem's definition, and it will be provided automatically.
* *Everything* has a callback you can set to allow changes, or make things happen upon changes, even including the MetaData. These callbacks apply to all Lua functions as one would expect. Methods to bypass them *do* exist and aren't even that complicated. However, programmers now have to go out of their way to bypass the callbacks- not the other way around.
* Full circular parent/child relationships. Wherever applicable, each object is given a read-only "parent" field, which can be used to check its owner. From just a MetaData object, you can fetch the ItemStack that owns it, the Inventory that owns the ItemStack and even the player/node that owns the Inventory.
* In a similar manner, all ItemStacks are passed via reference, rather than copy, unless specified otherwise. You don't need to "commit" changes to an ItemStack, the same way you need to in regular Minetest.
* The on_use function on an item's definition will no longer replace the usual digging behavior.
* There is a "Dynamic Formspec" system in place. You can create essentially custom formspec elements by using pre-existing ones as building blocks. You can easily change the very layout of a formspec as needed, and create semi-persistent data with the help of a single MetaData object owned by the formspec as a whole, usable by every individual FormspecElement.
* And more! Check the provided api.txt document for details about how everything is used.
-------------------------------------------------------------------------------------------------------------
Dependencies and Support
-------------------------------------------------------------------------------------------------------------
Required mods:
* controls: https://github.com/mt-mods/controls (This fork is necessary for updated support)
* entitycontrol: https://content.minetest.net/packages/Noodlemire/entitycontrol/
* smart_vector_table: https://content.minetest.net/packages/Noodlemire/smart_vector_table/
Supported mods:
* default: Chests work with this system properly.
-------------------------------------------------------------------------------------------------------------
License
-------------------------------------------------------------------------------------------------------------
The LGPL v2.1 License is used with this mod. See https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html or LICENSE.txt for more details.
-------------------------------------------------------------------------------------------------------------
Installation
-------------------------------------------------------------------------------------------------------------
Download, unzip, and place within the usual minetest/current/mods folder, and it will behave in relation to the Minetest engine like any other mod.

404
api.txt Normal file
View File

@ -0,0 +1,404 @@
Complete and Total Lua-Only Inventory Rewrite [lua_inv]
Modding API Reference
=============================================
[MetaData]
A table of data attached to various objects, to allow for extra custom data to be added, which can be controlled via its own callbacks.
### Constructors:
* lua_inv.metadata(parent) - Create a new metadata object. The "parent" should be the object that owns this. See MetaDataParent below.
### Fields [Read-Only]:
* data - The table where the metadata's key/value pairs are actually stored.
* parent - A reference to the owner of the MetaData. If there is none, this will instead contain {orphaned = true}
### Functions:
* :allow_change(key, value) - Check if setting the key/value pair is allowed. Returns a boolean indicating permission.
* :on_change(key, value) - Simulate effects of setting the key/value pair. Returns the final value.
* :contains(key) - Returns true if the value that they key contains is anything other than nil.
* :get(key) - Returns the exact value that this key contains, or nil if it doesn't exist.
* :remove(key) - Sets the value at the given key to nil.
* :set_string(key, value) - Sets the given value, converting to a string if necessary. Returns the final value or false if it failed.
* :get_string(key) - Gets the value at the given key as a string. If it doesn't exist, "" is returned.
* :set_int(key, value) - Sets the given value, converting to a whole number if necessary. Returns the final value or false if it failed.
* :get_int(key) - Gets the value at the given key as a whole number. If it doesn't exist or isn't a number, 0 is returned.
* :set_float(key, value) - Sets the given value, converting to a number if necessary. Returns the final value or false if it failed.
* :get_float(key) - Gets the value at the given key as a number. If it doesn't exist or isn't a number, 0 is returned.
* :to_table() - Returns a writable copy of this MetaData's data table.
* :from_table(table) - Clears all of the previous data and adds every key/value pair within the provided table. Returns self.
* :equals(MetaData) - Returns true if the provided MetaData has all the same key/value pairs as this one. Callbacks aren't considered for this.
* :serialize() - Returns a JSON string representing all the key/value pairs in the data table.
### Global Callbacks (Use with caution):
* lua_inv.register_metadata_allow_change(function(MetaData, key value)) - Create a permission function that applies to all MetaData objects.
* lua_inv.register_metadata_on_change(function(MetaData, key value)) - Create an on-change modifier function for all MetaData objects.
### Operators:
* MetaData == MetaData - An alternate way to check if two MetaData objects have all the same key/value data.
[MetaDataParent]
Any object can be considered if set as a MetaData's parent. They gain special functionality for possessing the following functions:
* metadata_allow_change(Parent, MetaData, k, v) - Apply a special permission function to the owned MetaData that it wouldn't normally have.
* metadata_on_change(Parent, MetaData, k, v) - Apply a special transformation function to the owned MetaData that it wouldn't normally have.
[ItemStack]
A reference to a slot that can hold an item. Unlike the normal kind, these are most often accessed directly via reference.
When you make a change to one, there is usually no need to "commit" the change by using set_stack in the inventory.
### Constructors:
* lua_inv.itemstack(name, count, wear, meta, parent) - The normal way to create a new ItemStack.
-- name: The name of the item, usually in the format of modname:itemname. Defaults to ""
-- count: How many of this object you wish to create. Defaults to 1.
-- wear: Number/100 for how close this item is to breaking. If the count is higher than 1, only the stack's top item will have the wear. Defaults to 0.
-- meta: A key/value table that will be supplied to this item's MetaData. Defaults to an empty table. Can't accept an entire MetaData object; use :to_table() instead.
-- parent: The object that owns this ItemStack, usually expected to be an inventory. See ItemStackInInventoryParent below.
* lua_inv.itemstack_from_string(String) - Create an ItemStack based on a provided ItemString.
* lua_inv.itemstack_from_userdata(UserData) - Port a UserData-type ItemStack into a lua_inv-type one.
* lua_inv.itemstack_to_userdata(ItemStack) - The opposite of the above. Export an ItemStack back into a regular UserData item.
### Fields [Read-Only]:
* parent - A reference to the owner of the ItemStack. If there is none, this will instead contain {orphaned = true}
### Special MetaData Fields
* description - If present, this will be shown as the description of the item.
* inventory_image - If present, this filename will be used for the item's sprite.
* frames - If present, overrides the amount of frames in an item's animation.
* speed - If present, overrides the animation speed of the item.
* frame_template - If present, this will be used to grab the next frame's image, by formatting it with the current frame number.
-- If both frames and frame_template are in the MetaData, you can animate an itemstack even if its definition lacks the _lua_inv_animation function.
### Functions:
* :allow_change(key, value) - Check if setting the key/value pair is allowed. Returns a boolean indicating permission.
* :on_change(key, value) - Simulate effects of setting the key/value pair. Returns the final value.
* :after_change(key, init, final) - Simulate effects after successfully setting the key's value from init to final. Returns nothing.
* :metadata_allow_change(MetaData, key, value) - Apply a special permission function to the owned MetaData that it wouldn't normally have.
* :metadata_on_change(MetaData, key, value) - Apply a special transformation function to the owned MetaData that it wouldn't normally have.
* :is_empty() - Returns true if this doesn't contain anything.
* :is_full() - Returns true only if the stack's count is equal to its max count.
* :get_name() - Returns the internal name of the item that this contains.
* :set_name(name) - Change the name of the contained item. Setting "" will instead erase it. Returns the final name.
* :get_count() - Returns the amount of items this stack contains.
* :set_count(count) - Set the amount of items that this stack contains. Setting 0 will instead empty it. Returns the final number.
* :get_wear() - Returns how worn out the top item is, from 0 to 100.
* :set_wear(wear) - Set this object's wear. If it's over 100, the count will be decremented. Returns the final wear.
* :get_meta() - Returns the MetaData of this stack.
* :get_total_size() - Returns the count, except the last item is counted as a partial item according to how worn the stack is.
* :get_description() - Returns the description of this item, or a custom description if the MetaData contains it.
* :get_inventory_image() - Attempts to create an inventory image for this item. If it doesn't exist, it attempt to make a cube formspec element.
* :get_animation(frame) - Returns ItemStackAnimation if defined in the registration or MetaData. Returns nil otherwise.
* :get_wear_visual() - Returns a colored bar for a formspec or HUD, representing how worn the item is.
* :to_string() - Returns an itemstring that represents this item.
* :get_stack_max() - Returns the maximum count that this item can have. Defaults to 99 for unknown items.
* :get_free_space() - Returns the amount of items that this can gain before it reaches the stack_max.
* :is_known() - Returns true if this item has an existing definition.
* :get_definition() - Returns this item's entry in minetest.registered_items if it exists.
* :has_custom_tool_capabilities() - Returns true if tool_capabilities were set for this item.
* :get_tool_capabilities() - Returns custom tool capabilities if set, its definition's capabilites if defined, or the hand's capabilities.
* :set_tool_capabilities(table) - Set a table of custom tool capabilities to apply to this item. Returns nothing.
* :add_wear(wear) - Add an amount of wear to this item. Works the same as set_wear.
* :is_similar(ItemStack) - By default, returns true if this item's name and MetaData match the provided ItemStack. Custom conditions may be set.
* :add_item(ItemStack) - Attempt to put ItemStack into this stack. ItemStack will contain the leftovers, if any. Returns self.
* :item_fits(ItemStack) - Return true if ItemStack is similar to this, and there is enough free space to fit all of ItemStack.
* :take_item(count) - Take the desired amount of items from this stack and return it.
* :peek_item(count) - Copy the desired amount of items from this stack and return it.
* :serialize() - Returns a JSON string representing the contents of this ItemStack.
### Global Callbacks (Use with caution):
* lua_inv.register_itemstack_allow_change(function(ItemStack, key, value)) - Create a permission function to apply to all ItemStacks.
* lua_inv.register_itemstack_on_change(function(ItemStack, key, value)) - Create an on_change function to apply to all ItemStack.
* lua_inv.register_itemstack_after_change(function(ItemStack, key, init, final)) - Create a function to apply after all changes to ItemStacks.
### Definition Entries for minetest.register_item:
* _lua_inv_allow_change(self, key, value) - Set a restriction for how this item can be changed. Return a boolean to indicate permission.
* _lua_inv_on_change(self, key, value) - Do something whenever this item is changed. Return a value to change it.
* _lua_inv_after_change(self, key, init, final) - Do something after this item is changed.
* _lua_inv_metadata_allow_change(self, meta, key, value) - Set a restriction for how this item's MetaData can be changed. Return false to prevent it.
* _lua_inv_metadata_on_change(self, meta, key, value) - Do something whenever the MetaData of this item is changed.
* _lua_inv_is_similar(self, other) - Set a custom condition when checking if this item should be considered similar to another.
* _lua_inv_add(self, other) - Specify custom behavior for adding other into self.
* _lua_inv_on_use(self, user, pointed_thing) - Like on_use, but this one supports lua_inv itemstacks and will NOT replace default behavior.
* _lua_inv_on_place(self, placer, pointed_thing) - Like on_place, but this provides a lua_inv itemstack instead.
* _lua_inv_on_secondary_use(self, placer, pointed_thing) - Like on_secondary_use, but this provides a lua_inv itemstack instead.
* _lua_inv_animation(self, frame) - If used, this should return an ItemStackAnimation.
### Operators:
* ItemStack + ItemStack - Attempt to add the second ItemStack into the first, like in the ItemStack:add_item function.
* ItemStack == ItemStack - Returns true if both ItemStacks have the same name, count, wear, and MetaData contents.
* ItemStack < ItemStack - Returns true if the total size of the first ItemStack is less than the second.
* ItemStack <= ItemStack - Returns true if the total size of the first ItemStack is less than or equal to the second.
[ItemStackInInventoryParent]
When an Inventory is the parent of an ItemStack, it has the following fields:
* inv - The actual Inventory object that the ItemStack belongs to.
* list - The name of the Inventory list that the ItemStack is in.
* index - The specific index within the Inventory's list that the ItemStack is in.
[ItemStackAnimation]
A mini-definition table used to specify how an object should be animated. It uses the fields in the list below.
Note that when used, the item's inventory_image should provide a vertical animation strip so the animation works in formspecs.
* frames - The amount of frames in the animation.
* speed - The time (in milliseconds) that each frame is shown. Optional; default is (1000 / frames)
* frame_template - The name of the file of each individual frame. Use %d in place of a specific frame number, so that string.format will work with it.
[Inventory]
A reference to a table of lists of itemstacks. Once a list is set, it sticks with the same set of ItemStacks for as long as it possibly can.
Every slot is set via reference, and items are added and removed simply by directly changing the contents of contained ItemStacks.
### Constructors:
* lua_inv.inventory(parent, allow_change, on_change, after_change) - Create a new inventory with many optional values.
-- parent: The object that owns this. If there is none, it will instead be {orphaned = true}
-- allow_change(self, InventoryChange): A function used to allow or disallow items from being moved or changed. See InventoryChange below.
-- on_change(self, InventoryChange): A function called when a change would occur in the Inventory. See InventoryChange below.
-- after_change(self, InventoryChange): A function called after a change occurs in the Inventory. See InventoryChange below.
* lua_inv.inventory_from_serialized_string(serial, new_inv) - Load the given inventory with deserialized data.
-- serial: Data from a past use of Inventory:serialize(). Must manually prepend it with "return " including the space.
-- new_inv: The inventory reference to put the data in. Must already exist, typically from the same lua_inv.inventory call that created the serialzed inventory.
### Fields [Read-Only]:
* data - The table of lists that this inventory contains, indexed by listname.
* width - The table of widths for each list, indexed by listname.
* callbacks - A table of the functions that were provided to the constructor, indexed by function names.
* parent - A reference to the owner of the Inventory. If there is none, this will instead contain {orphaned = true}
### Functions:
* :get_allow_change() - Return the allow_change function that was passed to the Constructor. Defaults to a function that always returns true.
* :get_on_change() - Return the on_change function that was passed to the Constructor. Defaults to an empty function.
* :get_after_chagne() - Return the after_change function that was passed to the Constructor. Defaults to an empty function.
* :allow_change(self, InventoryChange) - Returns true or false if a given change is allowed in this inventory.
* :on_change(self, InventoryChange) - Applies callbacks when a change would be set in this inventory.
* :after_change(self, InventoryChange) - Applies callbacks after a change is set in this inventory.
* :is_empty(listname) - Returns true if every ItemStack in every list in this inventory is empty. If listname is provided, only that list is checked for emptiness.
* :get_size(listname) - Returns the amount of ItemStacks in a given list.
* :set_size(listname, size) - Change the size of a given list. If it's shortened, ItemStacks will be deleted. If size is 0, the whole list is deleted.
* :get_width(listname) - Returns the given width of a list, usually only relavent for crafting. Defaults to 1.
* :set_width(listname, width) - Sets the Inventory's width. Returns the width if successful, or false otherwise.
* :get_stack(listname, index) - Returns the ItemStack from the given list's index.
* :set_stack(listname, index, ItemStack)- Swap the provided ItemStack into the ItemSlot found in the given listname and index.
* :get_list(listname) - Returns a read-only table based on the provided list name.
* :set_list(listname, list) - Swap the contents of an entire list with those of the provided list.
* :get_lists() - Get the read-only table that this Inventory contains.
* :set_lists(listname, lists) - Completely overwrite the contents of an Inventory with the provided lists.
* :add_item(listname, ItemStack) - Add the ItemStack into the inventory. It will try to find a similar slot to fill, before trying an empty slot.
* :room_for_item(listname, ItemStack) - Return true if there is at least one slot in the list that can fit the provided ItemStack.
* :contains_item(listname, ItemStack, match_meta) -Check if the list already contains the provided ItemStack. May optionally check MetaData too.
* :remove_item(listname, ItemStack, match_meta) -Remove the requested item from the list and return said item. May optional check MetaData too.
* :serialize() - Returns a JSON string representing the contents of this Inventory.
### Global Callbacks (Use with caution):
* lua_inv.register_inventory_allow_change(function(Inventory, InventoryChange)) - Create a permission function to apply to all Inventories.
* lua_inv.register_inventory_on_change(function(Inventory, InventoryChange)) - Create an on_change function to apply to all Inventories.
* lua_inv.register_inventory_after_change(function(Inventory, InventoryChange)) - Create a function to apply after all changes to Inventories.
### Definition Entries for minetest.register_node:
* _lua_inv_inventory(pos) - Overrides behavior for creating an inventory at the given position. Usually useful for setting list sizes right away.
[InventoryChange]
This has a couple variations, depending on the type of change that occurs in an inventory.
* ItemStack :set_name, :set_count, :set_wear
{
type = "set",
stack = ItemStack,
key = key,
val = value, --Only when allow_change and on_change sends the callback
init_val = initial_value, --Only when after_change sends the callback
final_val = final_value, --Only when after_change sends the callback
}
* Inventory:set_stack
{
type = "swap",
stack1 = ItemStack, --The one that could have been retrieved by ItemStack:get_stack() before the swap
stack2 = ItemStack, --The one that was inputted into Inventory:set_stack to trigger the swap.
}
[FormspecElement]
A Lua Table representation of a line in a Formspec. As a table, it can easily have dynamic behaviors upon being converted into a Formspec String.
### Constructors:
* lua_inv.formspec_element(name, arguments, to_string) - The usual way to create an element for a Dyanamic Formspec.
name: The name of the element. You will usually want this to match one of Minetest's Formspec Element types, unless you set to_string.
arguments: A table of arguments. See below for examples.
to_string: A function to define custom behavior for turning this into a Formspec string. See below for details.
* lua_inv.default_formspec_element_to_string(FormspecElement) - The default for to_string. Turns a FormspecElement table into a formspec string.
* lua_inv.formspec_element_from_string(String) - This function will interpret a formspec string and create a FormspecElement table based off of it.
### Arguments:
A regular Formspec Element will separate values by either a semi-colon or a comma.
A semi-colon separates two entire arguments.
A comma separates sub-arguments, which would then go into their own specific table.
For example: list[current_player;main;0,5;8,4;]
The arguments for the above "list" Formspec Element would be represented as such:
{
[1] = "current_player",
[2] = "main",
[3] = {[1] = 0, [2] = 5},
[4] = {[1] = 8, [2] = 4},
}
### :to_string(self, player, formname, fields)
* self - The FormspecElement table that this function belongs to.
* player - The Player that this is being shown to.
* formname - The name of the form that will be sent to the player when the final formspec is shown.
* fields - Optional. Will be present if the player did something that would require feedback and a new formspec to be formed. Works like regular Minetest.
### Subclasses:
* lua_inv.dynamic_list(inv_location, listname, x, y, w, h, start_index) - Like a list[], except it's a grid of buttons to form a lua_inv-compatible UI.
* lua_inv.stack_mode_selector(x, y) - Meant to be paired with dynamic_list to offer more options for moving around ItemStacks.
* lua_inv.drop_item_button(x, y) - Provides a way for formspecs with dynamic lists to drop items onto the ground.
[DynamicFormspec]
This is essentially a formspec that is built to use FormspecElement as defined above.
### Constructors:
lua_inv.dynamic_formspec({FormspecElements}) - The standard method for creating a DynamicFormspec.
lua_inv.dynamic_formspec_from_string(formspec) - This is a way to automatically interpret and convert an existing formspec string into a DynamicFormspec.
### Functions
* :size() - Returns the number of FormspecElements.
* :get(index) - Returns the FormspecElement at the given index.
* :set(index, FormspecElement) - Replace the FormspecElement at the given index with a new one, or deletes it if the second argument is nil.
* :add(FormspecElement) - Add the given FormspecElement to the end of the list.
* :del(index) - Remove the FormspecElement at the given index. Elements after that one have their index reduced by 1 to close the gap.
* :form(Player, formname, fields) - Build a presentable formspec string out of all existing FormspecElements according to their to_string method.
[Manager]
An internal manager is used to keep track of DynamicFormspec states. Also comes with helpful functions.
* lua_inv.get_df_meta(Player) - Returns two MetaData objects tied to the Player's open DynamicFormspec. It's important as it allows FormspecElements to communicate.
-- meta: For more persistent variables that are kept even after the DynamicFormspec is closed.
-- temp_meta: For less persistent variables that get deleted when the DynamicFormspec is closed or updated. Occasionally needed to fix cases of looping or other oddities in formspecs.
* lua_inv.show_formspec(Player, formname, DynamicFormspec) - The official method to showing a DynamicFormspec in a way that indicates lua_inv compatability.
[SurvivalInventory]
A subclass of a DynamicFormspec that is given to all players. It's a lua_inv compatible recreation of a basic survival inventory formspec.
### Methods:
* lua_inv.survival_inventory.form() - Builds the DynamicFormspec to be shown to the player. It has a custom element to show a 2D image or 3D model on the owner based of their properties.
* lua_inv.survival_inventory.ref(Player) - Builds the Player's Inventory, with the Player as its parent, with allow_change and after_change callbacks.
[PlayerInventory]
Although mainly for internal use, it comes with a few tools that may be useful in various cases.
### Fields:
* lua_inv.player_inventory - A table, indexed by player names, which can be used to access a player's inventory or inventory formspec.
-- inv: A link to the player's Inventory.
-- form: A link to the DynamicFormspec shown to the player to let them interact with their inventory.
### Methods:
* lua_inv.get_player_wielded_item(player) - Get the ItemStack that the player is wielding.
* lua_inv.set_player_wielded_item(player, itemstack) - Set the ItemStack that the playee is wielding. A Userdata item may be sent instead, and will automaticall be converted.
* lua_inv.update_held_item_data(player) - Update internal information about the wielded item's tool_capabilities. Automatically called for most use cases.
[NodeInventory]
A separate table of these is maintained to keep track of node inventories and ensure that they are readily accessible.
### Constructor:
lua_inv.fetch_node_inventory(pos, keep_nil) - Returns a node inventory for the given position, and creates one if it didn't already exist.
-- keep_nil: Optional. If true, this function will return nil instead of creating a new inventory.
[DetachedInventory]
A table of these is maintained to allow them to appear in formspecs.
Note that unlike other inventory types, these are NOT saved automatically. For these, you should implement persistence yourself, if necessary.
### Constructor:
lua_inv.create_detached_inventory(name, input_parent, allow_change_func, on_change_func, after_change_func) - Returns a new detached inventory.
-- name: The name of the detached inventory, used to fetch it later.
-- The other arguments work the same as lua_inv.inventory()
-- Note that you are not allowed to create a detached inventory using the name of one that already exists. This will cause an error.
### Method:
lua_inv.get_detached_inventory(name) - Returns a detached inventory indexed by the given name, if it exists.
[Misc. Functions]
* lua_inv.update_hotbar(player) - Update the player's hotbar visuals in the event of a change. Automatically called for most use cases.
* lua_inv.tiles_to_cube_textures({tiles}) - Converts a table of tiles from an object's properties into a table of textures for an entity.

View File

@ -0,0 +1,107 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
function lua_inv.dynamic_formspec(input_elems)
local index = {
elems = setmetatable(input_elems or {}, {
__newindex = function(self, key, val)
error("Error: Attempt to directly set a value in a dynamic formspec's list of elements. Please call one of the provided functions instead.")
end,
__metatable = false,
}),
size = function(self)
return #self.elems
end,
get = function(self, i)
return self.elems[i]
end,
set = function(self, i, element)
if not element then
return self:del(i)
else
rawset(self.elems, i, element)
end
end,
add = function(self, element)
self:set(self:size() + 1, element)
end,
del = function(self, i)
for n = i, self:size() - 1 do
rawset(self.elems, n, self:get(n + 1))
end
rawset(self.elems, self:size(), nil)
end,
form = function(self, player, formname, fields)
local formspec = ""
for i = 1, self:size() do
local element = self:get(i):to_string(player, formname, fields)
formspec = formspec..element
end
self.temp_meta:from_table({})
return formspec
end,
}
index.meta = lua_inv.metadata(index)
index.temp_meta = lua_inv.metadata(index)
return setmetatable({}, {
__index = index,
__newindex = function(self, key, val)
error("Error: Attempt to directly set a value in a dynamic formspec object. Please call one of the provided functions instead.")
end,
__metatable = false,
})
end
function lua_inv.get_df_meta(player)
if lua_inv.open_formspecs[player:get_player_name()] then
return lua_inv.open_formspecs[player:get_player_name()].meta, lua_inv.open_formspecs[player:get_player_name()].temp_meta
end
end
function lua_inv.dynamic_formspec_from_string(str)
local lines = str:split(']')
for i = 1, #lines do
lines[i] = lua_inv.formspec_element_from_string(lines[i])
end
local df = lua_inv.dynamic_formspec()
for i = 1, #lines do
df:add(lines[i])
end
return df
end

View File

@ -0,0 +1,66 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
lua_inv.open_formspecs = {}
local cooldown = {}
function lua_inv.show_formspec(player, formname, dynamic_formspec)
local pname = player:get_player_name()
lua_inv.open_formspecs[pname] = dynamic_formspec
minetest.show_formspec(pname, formname, dynamic_formspec:form(player, formname))
end
local old_show_formspec = minetest.show_formspec
minetest.show_formspec = function(playername, formname, formspec)
if not lua_inv.open_formspecs[playername] then
minetest.log("warning", "Formspec opened outside of lua_inv API. More implementation work is needed.")
end
return old_show_formspec(playername, formname, formspec)
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
local pname = player:get_player_name()
if cooldown[pname] and cooldown[pname] > 0 then
fields.quit = false
end
if fields.quit and formname ~= "" then
lua_inv.open_formspecs[pname] = nil
return
end
cooldown[pname] = 0.25
if formname == "" then
lua_inv.show_formspec(player, "lua_inv:inventory", lua_inv.player_inventory[pname].form)
elseif lua_inv.open_formspecs[pname] then
minetest.show_formspec(pname, formname, lua_inv.open_formspecs[pname]:form(player, formname, fields))
end
end)
minetest.register_globalstep(function(dtime)
for pname, c in pairs(cooldown) do
if c > 0 then
cooldown[pname] = c - dtime
end
end
end)

View File

@ -0,0 +1,126 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
lua_inv.survival_inventory = {}
local function get_player_display(player)
local player_display = nil
local props = player:get_properties()
if not props.mesh or props.mesh == "" then
return "image[1,0.6;1,"..props.visual_size.y..";"..props.textures[1].."]"
else
local t = {}
for _, v in ipairs(props.textures) do
t[#t+1] = core.formspec_escape(v):gsub(",", "!")
end
local textures = table.concat(t, ","):gsub("!", ",")
return "model[0.5,0.6;"..(2*props.visual_size.x)..","..(2*props.visual_size.y)..";player_model;"..props.mesh..";"..textures..";-15,195;"
.."false;true;1,1]"
end
end
function lua_inv.survival_inventory.form()
return lua_inv.dynamic_formspec({
lua_inv.formspec_element("size", {{8, 8.5}}),
lua_inv.formspec_element("player_view", {}, function(self, player, formname, fields) return get_player_display(player) end),
lua_inv.drop_item_button(7, 7.9),
lua_inv.dynamic_list("current_player", "main", 0, 3.75, 8, 4),
lua_inv.dynamic_list("current_player", "craft", 3, 0, 3, 3),
lua_inv.dynamic_list("current_player", "craftpreview", 7, 1, 1, 1),
lua_inv.stack_mode_selector(0, 7.9),
})
end
function lua_inv.survival_inventory.ref(player)
return lua_inv.inventory(player:get_player_name(),
--Allow Change
function(inv, change)
if change.type == "swap" and ((change.stack1.parent.list == "craftpreview" and change.stack2:is_empty()) or
(change.stack2.parent.list == "craftpreview" and change.stack1:is_empty())) then
return true
end
if change.type == "swap" and (change.stack1.parent.list == "craftpreview" or change.stack2.parent.list == "craftpreview") then
return false
end
if change.type == "set" and change.stack.parent.list == "craftpreview" and (change.key ~= "count" or change.val ~= 0) then
return false
end
return true
end,
nil,
--After Change
function(inv, change)
local is_craft = false
local is_result = false
local protected_i = false
if change.type == "set" then
is_craft = change.stack.parent.list == "craft"
is_result = change.stack.parent.list == "craftpreview"
elseif change.type == "swap" then
is_craft = change.stack1.parent.list == "craft" or change.stack2.parent.list == "craft"
is_result = change.stack1.parent.list == "craftpreview"
if is_craft and is_result then
protected_i = change.stack2.parent.index
end
end
if is_result then
for i = 1, inv:get_size("craft") do
if i ~= protected_i then
local stack = inv:get_stack("craft", i)
stack:take_item()
end
end
end
if is_craft then
local items = {}
for i = 1, inv:get_size("craft") do
items[i] = lua_inv.itemstack_to_userdata(inv:get_stack("craft", i))
end
local output = minetest.get_craft_result({method = "normal", width = 3, items = items})
local craftpreview = inv:get_stack("craftpreview", 1)
local previewstack = lua_inv.itemstack_from_userdata(output.item)
rawset(craftpreview, "name", previewstack:get_name())
rawset(craftpreview, "count", previewstack:get_count())
rawset(craftpreview, "wear", previewstack:get_wear())
rawset(craftpreview, "tool_capabilities", previewstack:get_tool_capabilities())
craftpreview:get_meta():from_table(previewstack:get_meta():to_table())
end
lua_inv.update_hotbar(player)
lua_inv.update_held_item_data(player)
end
)
end

View File

@ -0,0 +1,68 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
function lua_inv.drop_item_button(in_x, in_y)
return lua_inv.formspec_element(
"drop_item_button",
{{in_x, in_y}},
function(self, player, formname, fields)
local meta, temp_meta = lua_inv.get_df_meta(player)
if not meta then
minetest.log("warning", "Warning: Attempt to form a closed formspec.")
return ""
end
local x, y = self.args[1][1], self.args[1][2]
local formspec = "image_button["..x..","..y..";1,1;lua_inv_drop_item.png;drop_item_button;]"
.."tooltip[drop_item_button;Drop Selected Item]"
if not fields or not fields.drop_item_button or not meta:contains("selection") then return formspec end
local context = lua_inv.button_field_to_context(meta:get_string("selection"))
local inv = lua_inv.inventory_from_location(context.inv_location, player)
local selected_item = inv:get_stack(context.listname, context.index)
if selected_item:is_empty() then return formspec end
local dropped = selected_item:take_item(selected_item:get_count())
if dropped:is_empty() then return formspec end
meta:remove("selection")
local pos = player:get_pos()
pos = {
x = pos.x,
y = pos.y + player:get_properties().eye_height,
z = pos.z
}
local ent = minetest.add_entity(pos, "__builtin:item")
if ent then
ent:get_luaentity():set_item(dropped:to_string())
end
return formspec
end
)
end

View File

@ -0,0 +1,219 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
function lua_inv.button_field_to_context(field)
local splits = field:split("__")
local il = splits[2]
if il ~= "context" and il ~= "current_player" then
local il_splits = il:split('_', false, 1)
if il_splits[1] == "nodemeta" then
local pos = il_splits[2]:split('_')
il = {k = "nodemeta", v = {tonumber(pos[1]), tonumber(pos[2]), tonumber(pos[3])}}
else
il = {type = il_splits[1], name = il_splits[2]}
end
end
return {
inv_location = il,
listname = splits[3],
index = splits[4],
}
end
function lua_inv.inventory_from_location(inv_location, player)
local inv = lua_inv.player_inventory[player:get_player_name()].inv
if inv_location == "current_player" and player then
inv = lua_inv.player_inventory[player:get_player_name()].inv
elseif type(inv_location) == "table" then
if inv_location.k == "player" then
if not lua_inv.player_inventory[inv_location.v] then
error("Error: Attempt to find the inventory of a non-existant player named \""..inv_location.v.."\"")
end
inv = lua_inv.player_inventory[inv_location.v].inv
elseif inv_location.k == "nodemeta" then
inv = lua_inv.fetch_node_inventory({
x = tonumber(inv_location.v[1]),
y = tonumber(inv_location.v[2]),
z = tonumber(inv_location.v[3])
})
elseif location.k == "detached" then
inv = lua_inv.get_detached_inventory[inv_location.v]
if not inv then
error("Error: Attempt to find non-existant inventory location \"detached:"..inv_lovation.v.."\"")
end
else
inv = nil
end
end
if not inv then
error("Error: Attempt to create unknown inventory type: "..dump(inv_location))
end
return inv
end
function lua_inv.dynamic_list(in_inv_location, in_listname, in_x, in_y, in_w, in_h, in_start_i)
return lua_inv.formspec_element(
"dynamic_list",
{
in_inv_location,
in_listname,
{in_x, in_y},
{in_w, in_h},
in_start_i,
},
function(self, player, formname, fields)
local meta, temp_meta = lua_inv.get_df_meta(player)
if not meta then
minetest.log("warning", "Warning: Attempt to form a closed formspec.")
return ""
end
if fields and not temp_meta:contains("dynamic_list_flag") then
temp_meta:set_string("dynamic_list_flag", "true")
for field in pairs(fields) do
if field:sub(1, 15) == "lua_inv_button_" then
if meta:contains("selection") then
if field == meta:get_string("selection") then
meta:remove("selection")
else
local stack_mode = meta:get("stack_mode") or "lua_inv_stack_mode_all"
local context_1 = lua_inv.button_field_to_context(meta:get_string("selection"))
local context_2 = lua_inv.button_field_to_context(field)
local inv_1 = lua_inv.inventory_from_location(context_1.inv_location, player)
local inv_2 = lua_inv.inventory_from_location(context_2.inv_location, player)
local stack_1 = inv_1:get_stack(context_1.listname, context_1.index)
local stack_2 = inv_2:get_stack(context_2.listname, context_2.index)
if stack_mode == "lua_inv_stack_mode_all" or stack_1:get_count() <= 1 then
if stack_1:is_similar(stack_2) and not (stack_1:is_full() or stack_2:is_full()) then
stack_2 = stack_2 + stack_1
else
inv_1:set_stack(context_1.listname, context_1.index, stack_2)
end
elseif stack_mode == "lua_inv_stack_mode_half" then
if stack_1:peek_item(math.ceil(stack_1:get_count()/2)):item_fits(stack_2) then
local half_stack = stack_1:take_item(math.ceil(stack_1:get_count()/2))
stack_2 = stack_2 + half_stack
stack_1 = stack_1 + half_stack
end
else
if stack_1:peek_item():item_fits(stack_2) then
local single_stack = stack_1:take_item(1)
stack_2 = stack_2 + single_stack
stack_1 = stack_1 + single_stack
end
end
if stack_1:is_empty() then
meta:remove("selection")
end
end
else
local con = lua_inv.button_field_to_context(field)
if not lua_inv.inventory_from_location(con.inv_location, player):get_stack(con.listname, con.index):is_empty() then
meta:set_string("selection", field)
end
end
break
end
end
end
local str = ""
local inv_location = self.args[1]
local listname = self.args[2]
local pos = self.args[3]
local size = self.args[4]
local start_i = (tonumber(self.args[5]) or 0)
local inv = lua_inv.inventory_from_location(inv_location, player)
for x = 1, size[1] do
for y = 1, size[2] do
local ind = start_i + x + (y - 1) * size[1]
local stack = inv:get_stack(listname, ind)
local slotname = '_'..listname.."__"..ind
if type(inv_location) == "table" then
if type(inv_location.v) == "table" then
slotname = '_'..inv_location.k..'_'..inv_location.v[1]..'_'..inv_location.v[2]..'_'..
inv_location.v[3]..'_'..slotname
else
slotname = '_'..inv_location.k..'_'..inv_location.v..'_'..slotname
end
else
slotname = '_'..inv_location..'_'..slotname
end
slotname = "lua_inv_button_"..slotname
str = str.."image_button["..(pos[1] + x - 1)..','..(pos[2] + y - 1)..";1,1;;"..slotname..";]"
if not stack:is_empty() then
str = str.."tooltip["..slotname..';'..stack:get_description()..']'
local anim = stack:get_animation()
if anim then
str = str.."animated_image["..(pos[1] + x - 1)..','..(pos[2] + y - 1)..";1,1;"..stack:get_name()..";"
..stack:get_inventory_image()..";"..anim.frames..";"..anim.speed..";]"
else
str = str.."image["..(pos[1] + x - 1)..','..(pos[2] + y - 1)..";1,1;"..stack:get_inventory_image()..']'
end
if stack:get_count() > 1 then
str = str.."label["..(pos[1] + x - 0.4)..','..(pos[2] + y - 0.5)..';'..stack:get_count()..']'
end
if stack:get_wear() > 0 then
str = str.."image["..(pos[1] + x - 0.95)..','..(pos[2] + y - 1.0625)..";0.875,1;"..stack:get_wear_visual()..']'
end
end
if meta:get_string("selection") == slotname then
str = str.."image["..(pos[1] + x - 1.0456)..','..(pos[2] + y - 1.0456)..";1.111,1.111;lua_inv_selected.png]"
end
end
end
return str
end
)
end

View File

@ -0,0 +1,109 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
function lua_inv.formspec_element(in_name, in_args, in_to_string)
return setmetatable({}, {
__index = {
name = in_name,
args = in_args or {},
to_string = in_to_string or lua_inv.default_formspec_element_to_string,
},
__newindex = function(self, key, val)
error("Error: Attempt to directly set a value in an formspec element object. Please alter only values in the 'args' and 'meta' subtables.")
end,
__metatable = false,
})
end
function lua_inv.default_formspec_element_to_string(self)
local str = self.name..'['
for n, arg in ipairs(self.args) do
if type(arg) ~= "table" then
str = str..tostring(arg)
else
if arg.k then
str = str..arg.k..':'
if type(arg.v) ~= "table" then
str = str..tostring(arg.v)
else
for t, subarg in ipairs(arg.v) do
str = str..subarg
if t ~= #arg.v then
str = str..','
end
end
end
else
for t, subarg in ipairs(arg) do
str = str..subarg
if t ~= #arg then
str = str..','
end
end
end
end
if type(arg) == "table" or n ~= #self.args then
str = str..';'
end
end
return str..']'
end
function lua_inv.formspec_element_from_string(line)
if line:sub(line:len()) == ']' then
line = line:sub(1, line:len() - 1)
end
local splits = line:split('[', true, 1)
local elem = {
name = splits[1]:trim(),
args = splits[2]:split(';', true)
}
for n, arg in ipairs(elem.args) do
if arg:find(':') and not arg:find('%[') and elem.name ~= "item_image" and elem.name ~= "item_image_button" then
local argsplits = arg:split(':')
local subargsplits = argsplits[2]:split(',')
if #subargsplits == 1 then
subargsplits = subargsplits[1]
end
elem.args[n] = {k = argsplits[1], v = subargsplits}
elseif elem.name ~= "label" and elem.name ~= "tooltip" then
local argsplits = arg:split(',')
if #argsplits > 1 then
elem.args[n] = argsplits
end
end
end
return lua_inv.formspec_element(elem.name, elem.args)
end

View File

@ -0,0 +1,70 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
function lua_inv.stack_mode_selector(in_x, in_y)
return lua_inv.formspec_element(
"stack_mode_selector",
{
{in_x, in_y},
},
function(self, player, formname, fields)
local meta, temp_meta = lua_inv.get_df_meta(player)
if not meta then
minetest.log("warning", "Warning: Attempt to form a closed formspec.")
return ""
end
local stack_mode = meta:get("stack_mode") or "lua_inv_stack_mode_all"
if fields and not temp_meta:contains("lua_inv_stack_mode_selector_flag") then
temp_meta:set_string("lua_inv_stack_mode_selector_flag", "true")
for field in pairs(fields) do
if field == "lua_inv_stack_mode_all" or field == "lua_inv_stack_mode_half" or field == "lua_inv_stack_mode_single" then
meta:set_string("stack_mode", field)
stack_mode = field
break
end
end
end
local pos = self.args[1]
local str = "image_button["..pos[1]..","..pos[2]..";1,1;lua_inv_stack_mode_all.png;lua_inv_stack_mode_all;]"..
"image_button["..(pos[1]+1)..","..pos[2]..";1,1;lua_inv_stack_mode_half.png;lua_inv_stack_mode_half;]"..
"image_button["..(pos[1]+2)..","..pos[2]..";1,1;lua_inv_stack_mode_single.png;lua_inv_stack_mode_single;]"..
"tooltip[lua_inv_stack_mode_all;Set Stack Mode: All]"..
"tooltip[lua_inv_stack_mode_half;Set Stack Mode: Half]"..
"tooltip[lua_inv_stack_mode_single;Set Stack Mode: Single]"
if stack_mode == "lua_inv_stack_mode_single" then
str = str.."image["..(pos[1]+2)..","..pos[2]..";1,1;lua_inv_selected.png]"
elseif stack_mode == "lua_inv_stack_mode_half" then
str = str.."image["..(pos[1]+1)..","..pos[2]..";1,1;lua_inv_selected.png]"
else
str = str.."image["..(pos[1])..","..pos[2]..";1,1;lua_inv_selected.png]"
end
return str
end
)
end

225
hotbar.lua Normal file
View File

@ -0,0 +1,225 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
local hotbar_data = {}
local default_ratio = minetest.settings:get("screen_w") / minetest.settings:get("screen_h")
function lua_inv.update_hotbar(player)
local pname = player:get_player_name()
if not lua_inv.player_inventory[pname] then return end
local inv = lua_inv.player_inventory[pname].inv
for i = 1, 8 do
local img = "blank.png"
local count_txt = ""
local wear_img = "blank.png"
local stack = inv:get_stack("main", i)
if not stack:is_empty() then
img = stack:get_inventory_image()
if stack:get_count() > 1 then
count_txt = stack:get_count()
end
if stack:get_wear() > 0 then
wear_img = stack:get_wear_visual()
end
local anim = stack:get_animation()
if anim then
local anim_data = hotbar_data[pname].animation[i]
local frame = anim_data and anim_data.frame or 1
anim = stack:get_animation(frame)
img = anim.frame_template and anim.frame_template:format(frame) or img
hotbar_data[pname].animation[i] = anim_data or {}
anim_data = hotbar_data[pname].animation[i]
anim_data.frame = anim_data.frame or 1
anim_data.max_frame = anim_data.max_frame or anim.frames
anim_data.time = anim.speed
elseif hotbar_data[pname].animation[i] then
hotbar_data[pname].animation[i] = nil
end
elseif hotbar_data[pname].animation[i] then
hotbar_data[pname].animation[i] = nil
end
if not hotbar_data[pname]["hotbar_slot_"..i] then
hotbar_data[pname]["hotbar_slot_"..i] = player:hud_add({
hud_elem_type = "image",
text = img,
scale = {x = -4, y = -4 * hotbar_data[pname].aspect_ratio},
position = {x = 0.348 + .0435 * (i - 1), y = 0.9625},
direction = 1,
alignment = {x = 0, y = 0},
offset = {x = 0, y = 0},
z_index = 1
})
hotbar_data[pname]["hotbar_slot_count_"..i] = player:hud_add({
hud_elem_type = "text",
text = count_txt,
number = "0xFFFFFF",
scale = {x = -4, y = -4 * hotbar_data[pname].aspect_ratio},
position = {x = 0.363 + .0435 * (i - 1), y = 0.9825},
direction = 1,
alignment = {x = 0, y = 0},
offset = {x = 0, y = 0},
z_index = 1
})
hotbar_data[pname]["hotbar_slot_wear_"..i] = player:hud_add({
hud_elem_type = "image",
text = wear_img,
scale = {x = -4, y = -4 * hotbar_data[pname].aspect_ratio},
position = {x = 0.348 + .0435 * (i - 1), y = 0.9625},
direction = 1,
alignment = {x = 0, y = 0},
offset = {x = 0, y = 0},
z_index = 1
})
else
player:hud_change(hotbar_data[pname]["hotbar_slot_"..i], "text", img)
player:hud_change(hotbar_data[pname]["hotbar_slot_count_"..i], "text", count_txt)
player:hud_change(hotbar_data[pname]["hotbar_slot_wear_"..i], "text", wear_img)
end
end
end
minetest.register_on_joinplayer(function(player, last_login)
local pname = player:get_player_name()
hotbar_data[pname] = {
wield_index = player:get_wield_index(),
aspect_ratio = default_ratio,
animation = {}
}
player:hud_set_flags({hotbar = false})
player:hud_add({
hud_elem_type = "image",
text = "lua_inv_hotbar.png",
scale = {x = 0.75, y = 0.75},
position = {x = 0.5, y = 0.9625},
direction = 1,
alignment = {x = 0, y = 0},
offset = {x = 0, y = 0},
z_index = 0
})
hotbar_data[pname].hotbar_wield_index = player:hud_add({
hud_elem_type = "image",
text = "lua_inv_selected.png",
scale = {x = 3.25, y = 3.25},
position = {x = 0.348, y = 0.9625},
direction = 1,
alignment = {x = 0, y = 0},
offset = {x = 0, y = 0},
z_index = 2
})
minetest.after(0.1, lua_inv.update_hotbar, player)
if not last_login then
minetest.chat_send_player(pname, "Some parts of your HUD may be distored. This is normal. You can fix it using /set_aspect_ratio.")
end
end)
minetest.register_globalstep(function(dtime)
for _, player in pairs(minetest.get_connected_players()) do
local pname = player:get_player_name()
if not hotbar_data[pname] then return end
local update_hotbar = false
for i = 1, 8 do
--if not update_hotbar and (hotbar_data[pname].animation[i] == nil) ~= (lua_inv.player_inventory[pname].inv:get_stack("main", i):get_animation() == nil) then
--update_hotbar = true
--end
if hotbar_data[pname].animation[i] then
hotbar_data[pname].animation[i].time = hotbar_data[pname].animation[i].time - dtime * 1000
if hotbar_data[pname].animation[i].time < 0 then
hotbar_data[pname].animation[i].frame = hotbar_data[pname].animation[i].frame + 1
if hotbar_data[pname].animation[i].frame > hotbar_data[pname].animation[i].max_frame then
hotbar_data[pname].animation[i].frame = 1
end
update_hotbar = true
end
end
end
if hotbar_data[pname].wield_index ~= player:get_wield_index() then
hotbar_data[pname].wield_index = player:get_wield_index()
player:hud_change(hotbar_data[pname].hotbar_wield_index, "position", {
x = 0.348 + .0435 * (player:get_wield_index() - 1),
y = 0.9625
})
lua_inv.update_held_item_data(player)
end
if update_hotbar then
lua_inv.update_hotbar(player)
end
end
end)
minetest.register_chatcommand("set_aspect_ratio", {
description = "Set your aspect ratio to fix distorted HUD elements.",
params = "[width,height|ratio]",
func = function(name, param)
local player = minetest.get_player_by_name(name)
local ratio = param:split(',', false, 1)
if ratio[2] then
local n1, n2 = tonumber(ratio[1]), tonumber(ratio[2])
ratio = n1 and n2 and n1 / n2 or default_ratio
else
ratio = tonumber(ratio[1]) or default_ratio
end
hotbar_data[name].aspect_ratio = ratio
for i = 1, 8 do
player:hud_change(hotbar_data[name]["hotbar_slot_"..i], "scale", {x = -4, y = -4 * ratio})
end
minetest.chat_send_player(name, "Your aspect ratio was successfully set to "..ratio)
end
})

52
init.lua Normal file
View File

@ -0,0 +1,52 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
lua_inv = {}
local mp = minetest.get_modpath(minetest.get_current_modname())..'/'
dofile(mp.."metadata.lua")
dofile(mp.."itemstack.lua")
dofile(mp.."item_entity.lua")
dofile(mp.."hotbar.lua")
dofile(mp.."wield_item.lua")
dofile(mp.."misc.lua")
dofile(mp.."inventories/inventory.lua")
dofile(mp.."inventories/player_inventory.lua")
dofile(mp.."inventories/node_inventory.lua")
dofile(mp.."formspec_elements/formspec_element.lua")
dofile(mp.."formspec_elements/dynamic_list.lua")
dofile(mp.."formspec_elements/stack_mode_selector.lua")
dofile(mp.."formspec_elements/drop_item_button.lua")
dofile(mp.."dynamic_formspecs/manager.lua")
dofile(mp.."dynamic_formspecs/dynamic_formspec.lua")
dofile(mp.."dynamic_formspecs/survival_inventory.lua")
if minetest.get_modpath("default") then
dofile(mp.."optional_depends/default.lua")
end
minetest.register_on_mods_loaded(function()
if minetest.get_modpath("sfinv") then
sfinv.enabled = false
end
end)

View File

@ -0,0 +1,33 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
local detached_inventory = {}
function lua_inv.create_detached_inventory(name, input_parent, allow_change_func, on_change_func, after_change_func)
if detached_inventory[name] then
error("Error: Attempt to create a detached inventory named \""..name.."\", but one already exists!")
end
detached_inventory[name] = lua_inv.inventory(input_parent, allow_change_func, on_change_func, after_change_func)
return detached_inventory[name]
end
function lua_inv.get_detached_inventory(name)
return detached_inventory[name]
end

413
inventories/inventory.lua Normal file
View File

@ -0,0 +1,413 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
local function replace(self, replacement)
local self_name = self:get_name()
local self_count = self:get_count()
local self_wear = self:get_wear()
local self_tool_capabilities = self:has_custom_tool_capabilities() and self:get_tool_capabilities()
local self_meta = self:get_meta():to_table()
rawset(self, "name", replacement:get_name())
rawset(self, "count", replacement:get_count())
rawset(self, "wear", replacement:get_wear())
rawset(self, "tool_capabilities", replacement:has_custom_tool_capabilities() and replacement:get_tool_capabilities())
self:get_meta():from_table(replacement:get_meta():to_table())
rawset(replacement, "name", self_name)
rawset(replacement, "count", self_count)
rawset(replacement, "wear", self_wear)
rawset(replacement, "tool_capabilities", self_tool_capabilities)
replacement:get_meta():from_table(self_meta)
end
lua_inv.registered_inventory_allow_changes = {}
lua_inv.registered_inventory_on_changes = {}
lua_inv.registered_inventory_after_changes = {}
function lua_inv.register_inventory_allow_change(func)
table.insert(lua_inv.registered_inventory_allow_changes, func)
end
function lua_inv.register_inventory_on_change(func)
table.insert(lua_inv.registered_inventory_on_changes, func)
end
function lua_inv.register_inventory_after_change(func)
table.insert(lua_inv.registered_inventory_after_changes, func)
end
local static = {
get_allow_change = function(self)
return self.callbacks.allow_change or function() return true end
end,
get_on_change = function(self)
return self.callbacks.on_change or function() end
end,
get_after_change = function(self)
return self.callbacks.after_change or function() end
end,
allow_change = function(self, change)
for _, func in ipairs(lua_inv.registered_inventory_allow_changes) do
if not func(self, change) then
return false
end
end
if not self:get_allow_change()(self, change) then
return false
end
return true
end,
on_change = function(self, change)
for _, func in ipairs(lua_inv.registered_inventory_on_changes) do
func(self, change)
end
self:get_on_change()(self, change)
end,
after_change = function(self, change)
for _, func in ipairs(lua_inv.registered_inventory_after_changes) do
func(self, change)
end
self:get_after_change()(self, change)
end,
is_empty = function(self, listname)
if listname then
for i = 1, self:get_size(listname) do
if not self:get_stack(listname, i):is_empty() then
return false
end
end
return true
else
for list in pairs(self.data) do
if not self:is_empty(list) then
return false
end
end
return true
end
end,
get_size = function(self, listname)
return #(self.data[listname] or {})
end,
set_size = function(self, listname, size)
if size < 0 then
return self
elseif size == 0 then
rawset(self.data, listname, nil)
return self
end
local newlist = {}
rawset(self.data, listname, {})
for i = 1, size do
newlist[i] = self.data[listname][i] or lua_inv.itemstack(nil, nil, nil, nil, {inv = self, list = listname, index = i})
end
newlist = setmetatable(newlist, {
--__index = newlist,
__newindex = function(self, key, val)
error("Error: Attempt to directly set a value in an inventory list object. Please call one of the provided functions instead.")
end,
__metatable = false,
})
rawset(self.data, listname, newlist)
return self
end,
get_width = function(self, listname)
return self.width[listname] or 1
end,
set_width = function(self, listname, width)
width = tonumber(width)
if width then
width = math.floor(width)
rawset(self.width, listname, width)
return width
else
return false
end
end,
get_stack = function(self, listname, i)
i = tonumber(i)
return self.data[listname][i]
end,
set_stack = function(self, listname, i, itemstack)
i = tonumber(i)
if i >= 1 and i <= self:get_size(listname) and self:allow_change({type = "swap", stack1 = self:get_stack(listname, i), stack2 = itemstack})
and (itemstack.parent.orphaned or itemstack.parent.inv:allow_change({type = "swap", stack1 = itemstack, stack2 = self:get_stack(listname, i)})) then
self:on_change({type = "swap", stack1 = self:get_stack(listname, i), stack2 = itemstack})
if not itemstack.parent.orphaned then
itemstack.parent.inv:on_change({type = "swap", stack1 = itemstack, stack2 = self:get_stack(listname, i)})
end
if itemstack then
replace(self.data[listname][i], itemstack)
else
self.data[listname][i]:set_name("")
end
self:after_change({type = "swap", stack1 = self:get_stack(listname, i), stack2 = itemstack})
if not itemstack.parent.orphaned then
itemstack.parent.inv:after_change({type = "swap", stack1 = itemstack, stack2 = self:get_stack(listname, i)})
end
else
return false
end
return self:get_stack(listname, i)
end,
get_list = function(self, listname)
return self.data[listname]
end,
set_list = function(self, listname, list)
for i = 1, self:get_size(listname) do
self:set_stack(listname, i, list[i])
end
end,
get_lists = function(self)
return self.data
end,
set_lists = function(self, lists)
for listname in pairs(self.data) do
self:set_size(listname, 0)
end
for listname, list in pairs(lists) do
self:set_size(listname, #list)
self:set_list(listname, list)
end
end,
add_item = function(self, listname, stack)
local first_empty = false
for i = 1, self:get_size(listname) do
if not first_empty and self:get_stack(listname, i):is_empty() then
first_empty = i
end
if self:get_stack(listname, i):is_similar(stack) then
self:get_stack(listname, i):add_item(stack)
if stack:is_empty() then
return stack
end
end
end
if first_empty then
for i = first_empty, self:get_size(listname) do
if self:get_stack(listname, i):is_empty() then
self:get_stack(listname, i):add_item(stack)
if stack:is_empty() then
return stack
end
end
end
end
return stack
end,
room_for_item = function(self, listname, stack)
for i = 1, self:get_size(listname) do
if self:get_stack(listname, i):item_fits(stack) then
return true
end
end
return false
end,
contains_item = function(self, listname, stack, match_meta)
for i = 1, self:get_size(listname) do
local cur_stack = self:get_stack(listname, i)
if cur_stack:get_name() == stack:get_name() and cur_stack:get_total_size() <= stack:get_total_size() and
(not match_meta or cur_stack:is_similar(stack)) then
return true
end
end
return false
end,
remove_item = function(self, listname, stack, match_meta)
local removed = lua_inv.itemstack(stack:get_name())
if match_meta then
removed:get_meta():from_table(stack:get_meta():to_table())
end
for i = 1, self:get_size(listname) do
local cur_stack = self:get_stack(listname, i)
if cur_stack:get_name() == stack:get_name() and (not match_meta or cur_stack:get_meta() == stack:get_meta()) then
local needed = stack:get_total_size() - removed:get_total_size()
removed:add_item(cur_stack:take_item(needed))
if removed:get_total_size() >= stack:get_total_size() then
break
end
end
end
return removed
end,
serialize = function(self)
local ser = "{"
for listname in pairs(self:get_lists()) do
ser = ser.."[\""..listname.."\"] = {"
for i = 1, self:get_size(listname) do
ser = ser.."["..i.."] = "..self:get_stack(listname, i):serialize()..","
end
ser = ser:sub(1, ser:len()-1).."},"
end
return ser:sub(1, math.max(1, ser:len()-1)).."}"
end,
}
function lua_inv.inventory(input_parent, allow_change_func, on_change_func, after_change_func)
return setmetatable({}, {
__index = {
data = setmetatable({}, {
__newindex = function(self, key, val)
error("Error: Attempt to directly set a value in an inventory's data object. Please call one of the provided functions instead.")
end,
__metatable = false,
}),
width = setmetatable({}, {
__newindex = function(self, key, val)
error("Error: Attempt to directly set a value in an inventory's width list. Please call one of the provided functions instead.")
end,
__metatable = false,
}),
callbacks = setmetatable({}, {
__index = {
allow_change = allow_change_func,
on_change = on_change_func,
after_change = after_change_func,
},
__newindex = function(self, key, val)
error("Error: Attempt to directly set a value in an inventory's callback list. Please call one of the provided functions instead.")
end,
__metatable = false,
}),
parent = setmetatable({}, {
__index = input_parent or {orphaned = true},
__newindex = function(self, key, val)
error("Error: Attempt to directly set an inventory's parent. This can only be done in the constructor.")
end,
__metatable = false
}),
get_allow_change = static.get_allow_change,
get_on_change = static.get_on_change,
get_after_change = static.get_after_change,
allow_change = static.allow_change,
on_change = static.on_change,
after_change = static.after_change,
is_empty = static.is_empty,
get_size = static.get_size,
set_size = static.set_size,
get_width = static.get_width,
set_width = static.set_width,
get_stack = static.get_stack,
set_stack = static.set_stack,
get_list = static.get_list,
set_list = static.set_list,
get_lists = static.get_lists,
set_lists = static.set_lists,
add_item = static.add_item,
room_for_item = static.room_for_item,
contains_item = static.contains_item,
remove_item = static.remove_item,
serialize = static.serialize,
},
__newindex = function(self, key, val)
error("Error: Attempt to directly set a value in an inventory object. Please call one of the provided functions instead.")
end,
__metatable = false,
})
end
function lua_inv.inventory_from_serialized_string(serial, new_inv)
local stored_inv = minetest.deserialize(serial)
for listname, list in pairs(stored_inv or {}) do
new_inv:set_size(listname, #list)
for i = 1, #list do
new_inv:set_stack(listname, i, lua_inv.itemstack(list[i].name, list[i].count, list[i].wear, list[i].meta))
end
end
end

View File

@ -0,0 +1,49 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
local node_inventory = smart_vector_table.new()
function lua_inv.fetch_node_inventory(pos, keep_nil)
local inv = node_inventory.get(pos)
if not inv and not keep_nil then
local node = minetest.get_node(pos)
local def = minetest.registered_nodes[node.name]
local meta = minetest.get_meta(pos)
inv = def and def._lua_inv_inventory and def._lua_inv_inventory(pos) or lua_inv.inventory(pos)
if meta:contains("lua_inv") then
lua_inv.inventory_from_serialized_string(meta:get_string("lua_inv"), inv)
end
node_inventory.set(pos, inv)
end
return inv
end
minetest.register_on_shutdown(function()
for i = 1, node_inventory.size() do
local pos, inv = node_inventory.getVector(i), node_inventory.getValue(i)
local meta = minetest.get_meta(pos)
meta:set_string("lua_inv", "return "..inv:serialize())
end
end)

View File

@ -0,0 +1,244 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
lua_inv.player_inventory = {}
local storage = minetest.get_mod_storage()
minetest.register_craftitem("lua_inv:held_item_data", {
description = "If you see this message in-game, there is either a bug or an incompatible mod.",
groups = {not_in_creative_inventory = 1},
inventory_image = "blank.png",
after_use = function(itemstack, user, node, digparams)
local tool = lua_inv.get_player_wielded_item(user)
local def = tool:get_definition()
if not def then return end
if def.after_use then
local result = def.after_use(lua_inv.itemstack_to_userdata(tool), user, node, digparams)
if result then
lua_inv.set_player_wielded_item(user, lua_inv.itemstack_from_userdata(result))
end
elseif tool:is_known() then
tool:add_wear(digparams.wear * 100 / 65535)
end
end
})
function lua_inv.update_held_item_data(player)
if not lua_inv.player_inventory[player:get_player_name()] then return end
local wielded = ItemStack("lua_inv:held_item_data")
wielded:get_meta():set_tool_capabilities(lua_inv.get_player_wielded_item(player):get_tool_capabilities())
player:set_wielded_item(wielded)
end
minetest.register_on_joinplayer(function(player)
local player_inventory = {}
local new_inv = lua_inv.survival_inventory.ref(player)
local stored_inv = storage:get_string("inv_"..player:get_player_name())
if stored_inv ~= "" then
lua_inv.inventory_from_serialized_string(stored_inv, new_inv)
end
if new_inv:is_empty() then
local old_inv = player:get_inventory()
for listname in pairs(old_inv:get_lists()) do
new_inv:set_size(listname, old_inv:get_size(listname))
new_inv:set_width(listname, old_inv:get_width(listname))
for i = 1, old_inv:get_size(listname) do
new_inv:set_stack(listname, i, lua_inv.itemstack_from_userdata(old_inv:get_stack(listname, i)))
old_inv:set_stack(listname, i, ItemStack())
end
end
end
player_inventory.inv = new_inv
player_inventory.form = lua_inv.survival_inventory.form(player)
lua_inv.player_inventory[player:get_player_name()] = player_inventory
player:set_inventory_formspec("size[8,8]button_exit[1,1;6,6;continue;Please hit ESC to continue.]")
lua_inv.update_held_item_data(player)
end)
function lua_inv.get_player_wielded_item(player)
return lua_inv.player_inventory[player:get_player_name()].inv:get_stack("main", player:get_wield_index())
end
function lua_inv.set_player_wielded_item(player, itemstack)
if not player or not player:is_player() then return end
if type(itemstack) == "userdata" then
itemstack = lua_inv.itemstack_from_userdata(itemstack)
end
lua_inv.player_inventory[player:get_player_name()].inv:set_stack("main", player:get_wield_index(), itemstack)
end
minetest.register_on_dignode(function(pos, oldnode, digger)
local itemname = ""
if digger and digger:is_player() then
itemname = lua_inv.get_player_wielded_item(digger):get_name()
end
local drops = minetest.get_node_drops(oldnode, itemname)
for i = 1, #drops do
if digger and digger:is_player() then
local leftover = lua_inv.player_inventory[digger:get_player_name()].inv:add_item("main", lua_inv.itemstack_from_string(drops[i]))
if not leftover:is_empty() then
minetest.add_item(pos, lua_inv.itemstack_to_userdata(leftover))
end
digger:get_inventory():remove_item("main", ItemStack(drops[i]))
else
minetest.add_item(pos, drops[i])
end
end
end)
minetest.register_allow_player_inventory_action(function(player, action, inventory, inventory_info)
return 0
end)
local function get_pointed_thing(player, tool)
--Get the position of the player's eyes, to determine pointed_thing
local pos = player:get_pos()
pos.y = pos.y + player:get_properties().eye_height
--Get the tool's definition, to check its digparams and range
local def = tool:get_definition()
--Create a ray between the player's eyes and where they're looking, limited by their tool's range
local ray = Raycast(pos, vector.add(pos, vector.multiply(player:get_look_dir(), def.range or minetest.registered_items[""].range or 4)))
--Return the first pointable thing found that isn't the player calling this
for pt in ray do
if pt.type ~= "nothing" and not (pt.ref and pt.ref == player) then
return pt
end
end
--Return a pointed thing of "nothing" if nothing could be found.
return {type = "nothing"}
end
controls.register_on_press(function(player, control)
if control == "dig" then
local itemstack = lua_inv.get_player_wielded_item(player)
if itemstack:is_empty() then return end
local pointed_thing = get_pointed_thing(player, itemstack)
local def = itemstack:get_definition()
if not def then return end
if def._lua_inv_on_use then
def._lua_inv_on_use(itemstack, player, pointed_thing)
end
if def.on_use then
itemstack = lua_inv.itemstack_to_userdata(itemstack)
local output = def.on_use(itemstack, player, pointed_thing)
if output then
itemstack = output
end
itemstack = lua_inv.itemstack_from_userdata(itemstack)
end
lua_inv.set_player_wielded_item(player, itemstack)
end
if control == "place" then
local itemstack = lua_inv.get_player_wielded_item(player)
if itemstack:is_empty() then return end
local def = itemstack:get_definition()
if not def then return end
local pointed_thing = get_pointed_thing(player, itemstack)
if pointed_thing.type == "node" then
if def._lua_inv_on_place then
def._lua_inv_on_place(itemstack, player, pointed_thing)
end
elseif def._lua_inv_on_secondary_use then
def._lua_inv_on_secondary_use(itemstack, player, pointed_thing)
end
if pointed_thing.type == "node" then
itemstack = lua_inv.itemstack_to_userdata(itemstack)
local output = (def.on_place or minetest.item_place_node)(itemstack, player, pointed_thing)
if output then
itemstack = output
end
elseif def.on_secondary_use then
itemstack = lua_inv.itemstack_to_userdata(itemstack)
local output = def.on_secondary_use(itemstack, player, pointed_thing)
if output then
itemstack = output
end
end
if type(itemstack) == "userdata" then
itemstack = lua_inv.itemstack_from_userdata(itemstack)
end
if itemstack then
lua_inv.set_player_wielded_item(player, lua_inv.itemstack_from_userdata(itemstack))
end
end
end)
local function serialize_inventory(player)
local pname = player:get_player_name()
storage:set_string("inv_"..pname, "return "..lua_inv.player_inventory[player:get_player_name()].inv:serialize())
player:get_inventory():set_lists({})
end
minetest.register_on_leaveplayer(function(player)
serialize_inventory(player)
end)
minetest.register_on_shutdown(function()
for _, player in pairs(minetest.get_connected_players()) do
serialize_inventory(player)
end
end)

144
item_entity.lua Normal file
View File

@ -0,0 +1,144 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
function lua_inv.tiles_to_cube_textures(tiles)
local textures = table.copy(tiles)
if not textures[2] then textures[2] = textures[1] end
if not textures[3] then textures[3] = textures[2] end
if not textures[4] then textures[4] = textures[3] end
if not textures[5] then textures[5] = textures[4] end
if not textures[6] then textures[6] = textures[5] end
for i = 1, 6 do
if type(textures[i]) == "table" then
textures[i] = textures[i].name
end
end
return textures
end
local old_on_punch = minetest.registered_entities["__builtin:item"].on_punch
entitycontrol.override_entity("__builtin:item", {
initial_properties = {
hp_max = 1,
physical = true,
collide_with_objects = false,
collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3},
visual_size = {x = 0.4, y = 0.4},
textures = {""},
is_visible = false,
},
set_item = function(self, itemstack)
if not itemstack then
itemstack = lua_inv.itemstack()
end
if type(itemstack) == "userdata" then
itemstack = lua_inv.itemstack_from_userdata(itemstack)
end
if type(itemstack) == "string" then
itemstack = lua_inv.itemstack_from_string(itemstack)
end
self.itemstring = itemstack:to_string()
if self.itemstring == "" then
return
end
local max_count = itemstack:get_stack_max()
local count = itemstack:get_count()
local size = 0.2 + 0.1 * (count / max_count) ^ (1 / 3)
local def = itemstack:get_definition()
local glow = def and math.floor((def.light_source or 0) / 2 + 0.5)
local anim = itemstack:get_animation(1)
if anim then
self._anim = {
frame = 1,
max_frame = anim.frames,
get_frame = anim.frame_template,
time = anim.speed,
init_time = anim.speed
}
end
if itemstack:get_inventory_image():sub(1, 14) == "[inventorycube" then
self.object:set_properties({
is_visible = true,
visual_size = {x = size, y = size},
collisionbox = {-size, -size, -size, size, size, size},
automatic_rotate = math.pi * 0.5 * 0.2 / size,
visual = "cube",
textures = lua_inv.tiles_to_cube_textures(def and def.tiles or {{}}),
glow = glow,
})
else
self.object:set_properties({
is_visible = true,
visual_size = {x = size, y = size},
collisionbox = {-size, -size, -size, size, size, size},
automatic_rotate = math.pi * 0.5 * 0.2 / size,
visual = "mesh",
mesh = "lua_inv_extrusion.obj",
textures = {anim and anim.current_frame or itemstack:get_inventory_image()},
backface_culling = false,
glow = glow,
})
end
end,
on_punch = function(self, hitter)
local inv = lua_inv.player_inventory[hitter:get_player_name()].inv
if inv and self.itemstring ~= "" then
local left = inv:add_item("main", lua_inv.itemstack_from_string(self.itemstring))
if left and not left:is_empty() then
self:set_item(left)
return
end
end
self.itemstring = ""
self.object:remove()
end,
on_step = function(self, dtime)
if self._anim then
self._anim.time = self._anim.time - dtime * 1000
if self._anim.time < 0 then
self._anim.time = self._anim.init_time
self._anim.frame = self._anim.frame + 1
if self._anim.frame > self._anim.max_frame then
self._anim.frame = 1
end
self.object:set_properties({textures = {self._anim.get_frame:format(self._anim.frame)}})
end
end
end
})

535
itemstack.lua Normal file
View File

@ -0,0 +1,535 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
local function clear(self)
rawset(self, "name", "")
rawset(self, "count", 0)
rawset(self, "wear", 0)
rawset(self, "tool_capabilities", nil)
self:get_meta():from_table({})
end
lua_inv.registered_itemstack_allow_changes = {}
lua_inv.registered_itemstack_on_changes = {}
lua_inv.registered_itemstack_after_changes = {}
function lua_inv.register_itemstack_allow_change(func)
table.insert(lua_inv.registered_itemstack_allow_changes, func)
end
function lua_inv.register_itemstack_on_change(func)
table.insert(lua_inv.registered_itemstack_on_changes, func)
end
function lua_inv.register_itemstack_after_change(func)
table.insert(lua_inv.registered_itemstack_after_changes, func)
end
local function tileToString(tile)
if not tile then return end
if type(tile) == "string" then
return tile
else
return tile.name
end
end
local static = {
allow_change = function(self, k, v)
for _, func in ipairs(lua_inv.registered_itemstack_allow_changes) do
if not func(self, k, v) then
return false
end
end
if self:is_known() and self:get_definition()._lua_inv_allow_change and not self:get_definition()._lua_inv_allow_change(self, k, v) then
return false
end
if self.parent and self.parent.inv and self.parent.inv.allow_change and not self.parent.inv:allow_change({type = "set", stack = self, key = k, val = v}) then
return false
end
return true
end,
on_change = function(self, k, v)
for _, func in ipairs(lua_inv.registered_itemstack_on_changes) do
v = func(self, k, v) or v
end
if self:is_known() and self:get_definition()._lua_inv_on_change then
v = self:get_definition()._lua_inv_on_change(self, k, v) or v
end
if self.parent and self.parent.inv and self.parent.inv.on_change then
v = self.parent.inv:on_change({type = "set", stack = self, key = k, val = v}) or v
end
return v
end,
after_change = function(self, k, init_v, final_v)
for _, func in ipairs(lua_inv.registered_itemstack_after_changes) do
func(self, k, init_v, final_v)
end
if self:is_known() and self:get_definition()._lua_inv_after_change then
self:get_definition()._lua_inv_after_change(self, k, init_v, final_v)
end
if self.parent and self.parent.inv and self.parent.inv.after_change then
self.parent.inv:after_change({type = "set", stack = self, key = k, init_val = init_v, final_val = final_v})
end
end,
metadata_allow_change = function(self, meta, k, v)
if self:is_known() and self:get_definition()._lua_inv_metadata_allow_change and self:get_definition()._lua_inv_metadata_allow_change(self, meta, k, v) then
return false
end
return true
end,
metadata_on_change = function(self, meta, k, v)
return self:is_known() and self:get_definition()._lua_inv_metadata_on_change and
self:get_definition()._lua_inv_metadata_on_change(self, meta, k, v)
end,
is_empty = function(self)
return self:get_name() == "" or self:get_count() == 0
end,
is_full = function(self)
return self:get_count() == self:get_stack_max()
end,
get_name = function(self)
return tostring(rawget(self, "name"))
end,
set_name = function(self, n)
local init_name = n
if not self:allow_change("name", n) then
return self:get_name()
end
n = self:on_change("name", n)
if n ~= "" then
rawset(self, "name", tostring(n))
else
clear(self)
end
self:after_change("name", init_name, self:get_name())
return self:get_name()
end,
get_count = function(self)
return tonumber(rawget(self, "count"))
end,
set_count = function(self, c)
local init_count = c
if not self:allow_change("count", c) then
return self:get_count()
end
c = self:on_change("count", c)
c = tonumber(c)
if c > 0 then
rawset(self, "count", math.min(c, self:get_stack_max()))
else
clear(self)
end
self:after_change("count", init_count, self:get_count())
return self:get_count()
end,
get_wear = function(self)
return tonumber(rawget(self, "wear"))
end,
set_wear = function(self, w)
local init_wear = w
if not self:allow_change("wear", w) then
return self:get_wear()
end
w = self:on_change("wear", w)
w = tonumber(w)
if w >= 100 then
local taken = math.floor(w / 100)
self:take_item(taken)
w = w - taken * 100
end
if not self:is_empty() then
rawset(self, "wear", w)
end
self:after_change("wear", init_wear, self:get_wear())
return self:get_wear()
end,
get_meta = function(self)
return self.meta
end,
get_total_size = function(self)
return self:get_count() + (100 - self:get_wear()) / 100 - 1
end,
get_description = function(self)
return self:get_meta():get("description") or
self:is_known() and self:get_definition().description or
minetest.registered_items["unknown"].description
end,
get_inventory_image = function(self)
if not self:is_known() then
return "unknown_item.png"
end
local image = self:get_meta():get("inventory_image") or self:get_definition().inventory_image
local tiles = self:get_definition().tiles
if (not image or image == "") then
if tiles then
local t2 = tileToString(tiles[3]) or tileToString(tiles[2]) or tileToString(tiles[1])
local t3 = tileToString(tiles[6]) or t2
image = minetest.inventorycube(tileToString(tiles[1]), t3, t2)
else
image = "lua_inv_INV_IMG_NIL.png"
end
end
return image
end,
get_animation = function(self, frame)
local def = self:get_definition()
local meta = self:get_meta()
local anim = nil
if def and def._lua_inv_animation then
anim = def._lua_inv_animation(self, frame)
anim.speed = anim.speed or (1000 / anim.frames)
end
if anim and false then
anim.frames = meta:contains("frames") and meta:get_int("frames") or anim.frames
anim.speed = meta:contains("speed") and meta:get_int("speed") or anim.speed
anim.frame_template = meta:contains("frame_template") and meta:get_string("frame_template") or anim.frames
elseif meta:contains("frames") and meta:contains("frame_template") then
anim = {}
anim.frames = meta:get_int("frames")
anim.speed = meta:contains("speed") and meta:get_float("speed") or (1000 / anim.frames)
anim.frame_template = meta:get_string("frame_template")
end
return anim
end,
get_wear_visual = function(self)
local wear_percent = math.floor(100 - self:get_wear())
local r = string.format("%x", math.min(255, 500 - 5 * wear_percent))
local g = string.format("%x", math.min(255, 5 * wear_percent))
if r:len() == 1 then r = '0'..r end
if g:len() == 1 then g = '0'..g end
return "lua_inv_bar_bg.png^[lowpart:"..wear_percent..":lua_inv_bar.png^[multiply:#"..r..g.."00^[transformR270]"
end,
to_string = function(self)
local str = self:get_name()
if self:get_count() > 1 or self:get_wear() > 0 or #self:get_meta() > 0 then
str = str..' '..self:get_count()
end
if self:get_wear() > 0 or #self:get_meta() > 0 then
str = str..' '..self:get_wear()
end
local metatable = self:get_meta():to_table()
if next(metatable) then
str = str..' '..minetest.serialize(metatable)
end
return str
end,
get_stack_max = function(self)
return self:is_known() and self:get_definition().stack_max or minetest.settings:get("default_stack_max") or 99
end,
get_free_space = function(self)
return self:get_stack_max() - self:get_count()
end,
is_known = function(self)
return self:get_definition() ~= nil
end,
get_definition = function(self)
return minetest.registered_items[self:get_name()]
end,
has_custom_tool_capabilities = function(self)
return rawget(self, "tool_capabilities") ~= nil
end,
get_tool_capabilities = function(self)
return rawget(self, "tool_capabilities") or
self:is_known() and self:get_definition().tool_capabilities or
minetest.registered_items[""].tool_capabilities
end,
set_tool_capabilities = function(self, tool_caps)
rawset(self, "tool_capabilities", tool_caps)
end,
add_wear = function(self, amount)
return self:set_wear(self:get_wear() + amount)
end,
is_similar = function(self, other)
if self:is_known() and self:get_definition()._lua_inv_is_similar then
return self:get_definition()._lua_inv_is_similar(self, other)
else
return self:get_name() == other:get_name() and self:get_meta() == other:get_meta()
end
end,
add_item = function(self, other)
return self + other
end,
item_fits = function(self, other)
return (self:is_similar(other) or self:is_empty() or other:is_empty()) and self:get_total_size() + other:get_total_size() <= self:get_stack_max()
end,
take_item = function(self, n)
n = n or 1
local taken = math.min(self:get_count(), n)
local old_name, old_wear, old_meta = self:get_name(), self:get_wear(), self:get_meta():to_table()
taken = self:get_count() - self:set_count(self:get_count() - taken)
if taken > 0 then
local wear = old_wear - self:set_wear(0)
return lua_inv.itemstack(old_name, taken, wear, old_meta)
else
return lua_inv.itemstack()
end
end,
peek_item = function(self, n)
n = n or 1
return lua_inv.itemstack(self:get_name(), n, self:get_wear(), self:get_meta():to_table())
end,
serialize = function(self)
return "{[\"name\"] = \""..self:get_name().."\","
.."[\"count\"] = "..self:get_count()..","
.."[\"wear\"] = "..self:get_wear()..","
.."[\"meta\"] = "..self:get_meta():serialize().."}"
end,
add = function(self, other)
if self:is_empty() then
self:set_name(other:get_name())
end
if other:is_empty() then
return self
end
if self:is_similar(other) then
if self:is_known() and self:get_definition()._lua_inv_add then
self:get_definition()._lua_inv_add(self, other)
else
local max = self:get_stack_max()
local total_count = self:get_count() + other:get_count()
local total_wear = self:get_wear() + other:get_wear()
self:get_meta():from_table(other:get_meta():to_table())
if total_count > max then
local c = self:set_count(max)
other:set_count(total_count - c)
if total_wear / 100 > other:get_count() then
local w = self:set_wear(total_wear - other:get_count() * 100)
other:set_wear(total_wear - (w + other:get_count() * 100))
else
local w = total_wear - self:set_wear(0)
other:set_wear(w)
end
else
local c = total_count - self:set_count(total_count)
local w = total_wear - self:set_wear(total_wear)
if other:set_count(c) > 0 then
other:set_wear(w)
end
end
end
end
return self
end,
eq = function(self, other)
return self:get_name() == other:get_name() and self:get_count() == other:get_count() and self:get_wear() == other:get_wear() and self:get_meta() == other:get_meta()
end,
lt = function(self, other)
return self:get_total_size() < other:get_total_size()
end,
le = function(self, other)
return self:get_total_size() <= other:get_total_size()
end,
}
function lua_inv.itemstack(input_name, input_count, input_wear, input_meta, input_parent)
local itemstack = {
name = input_name or "",
count = input_count or (input_name and 1) or 0,
wear = input_wear or 0,
tool_capabilities = nil,
}
local return_stack = setmetatable(itemstack, {
__index = {
parent = setmetatable({}, {
__index = input_parent or {orphaned = true},
__newindex = function(self, key, val)
error("Error: Attempt to directly set an itemstack's parent. This can only be done in the constructor.")
end,
__metatable = false
}),
allow_change = static.allow_change,
on_change = static.on_change,
after_change = static.after_change,
metadata_allow_change = static.metadata_allow_change,
metadata_on_change = static.metadata_on_change,
is_empty = static.is_empty,
is_full = static.is_full,
get_name = static.get_name,
set_name = static.set_name,
get_count = static.get_count,
set_count = static.set_count,
get_wear = static.get_wear,
set_wear = static.set_wear,
get_meta = static.get_meta,
get_total_size = static.get_total_size,
get_description = static.get_description,
get_inventory_image = static.get_inventory_image,
get_animation = static.get_animation,
get_wear_visual = static.get_wear_visual,
to_string = static.to_string,
get_stack_max = static.get_stack_max,
get_free_space = static.get_free_space,
is_known = static.is_known,
get_definition = static.get_definition,
has_custom_tool_capabilities = static.has_custom_tool_capabilities,
get_tool_capabilities = static.get_tool_capabilities,
set_tool_capabilities = static.set_tool_capabilities,
add_wear = static.add_wear,
is_similar = static.is_similar,
add_item = static.add_item,
item_fits = static.item_fits,
take_item = static.take_item,
peek_item = static.peek_item,
serialize = static.serialize,
},
__newindex = function(self, key, val)
error("Error: Attempt to directly set a value in an itemstack object. Please call one of the provided functions instead.")
end,
__add = static.add,
__eq = static.eq,
__lt = static.lt,
__le = static.le,
})
getmetatable(return_stack).__index.meta = lua_inv.metadata(itemstack)
getmetatable(return_stack).__metatable = false
if input_meta then
return_stack:get_meta():from_table(input_meta)
end
return return_stack
end
function lua_inv.itemstack_from_string(str)
local splits = str:split("return", false, 1)
local stats = splits[1]:split(' ')
local meta = nil
if splits[2] then
meta = minetest.deserialize("return"..splits[2])
end
return lua_inv.itemstack(stats[1], stats[2] or 1, stats[3], meta)
end
function lua_inv.itemstack_from_userdata(itemstack)
return lua_inv.itemstack(itemstack:get_name(), itemstack:get_count(), itemstack:get_wear() / 655.35, itemstack:get_meta():to_table().fields)
end
function lua_inv.itemstack_to_userdata(itemstack)
local stack = ItemStack({
name = itemstack:get_name(),
count = itemstack:get_count(),
wear = math.floor(itemstack:get_wear() * 655.35),
meta = itemstack:get_meta():to_table()
})
return stack
end

259
metadata.lua Normal file
View File

@ -0,0 +1,259 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
lua_inv.registered_metadata_allow_changes = {}
lua_inv.registered_metadata_on_changes = {}
function lua_inv.register_metadata_allow_change(func)
table.insert(lua_inv.registered_itemstack_allow_changes, func)
end
function lua_inv.register_metadata_on_change(func)
table.insert(lua_inv.registered_itemstack_on_changes, func)
end
local static = {
allow_change = function(self, k, v)
for _, func in ipairs(lua_inv.registered_metadata_allow_changes) do
if not func(self, k, v) then
return false
end
end
if self.parent and self.parent.metadata_allow_change and not self.parent.metadata_allow_change(self.parent, self, k, v) then
return false
end
return true
end,
on_change = function(self, k, v)
for _, func in ipairs(lua_inv.registered_metadata_on_changes) do
v = func(self, k, v) or v
end
if self.parent and self.parent.metadata_on_change then
v = self.parent.metadata_on_change(self.parent, self, k, v) or v
end
return v
end,
contains = function(self, key)
return self:get(key) ~= nil
end,
get = function(self, key)
return self.data[key]
end,
remove = function(self, key)
if not self:allow_change(key, nil) then
return false
end
local v = nil
v = self:on_change(key, v)
rawset(self.data, key, v)
return v or true
end,
set_string = function(self, key, str)
if not self:allow_change(key, str) then
return false
end
str = self:on_change(key, str)
if (not str or str == "") then
str = self:remove(key)
if str == true then
return nil
end
end
rawset(self.data, key, tostring(str))
return self:get(key)
end,
get_string = function(self, key)
return tostring(self:get(key) or "")
end,
set_int = function(self, key, int)
if not self:allow_change(key, int) then
return false
end
int = self:on_change(key, int)
int = tonumber(int)
if int then
rawset(self.data, key, math.floor(int + 0.5))
return self:get(key)
else
return false
end
end,
get_int = function(self, key)
return math.floor((tonumber(self:get(key)) or 0) + 0.5)
end,
set_float = function(self, key, flt)
if not self:allow_change(key, flt) then
return false
end
flt = self:on_change(key, flt)
flt = tonumber(flt)
if flt then
rawset(self.data, key, flt)
return self:get(key)
else
return false
end
end,
get_float = function(self, key)
return tonumber(self:get(key) or 0.0)
end,
to_table = function(self)
local tbl = {}
for k, v in pairs(self.data) do
tbl[k] = v
end
return tbl
end,
from_table = function(self, tbl)
for k in pairs(self.data) do
self:remove(k)
end
for k, v in pairs(tbl) do
if type(v) == "string" then
self:set_string(k, v)
elseif type(v) == "number" then
self:set_float(k, v)
end
end
return self
end,
equals = function(self, other)
return self == other
end,
serialize = function(self)
local ser = "{"
for k, v in pairs(self:to_table()) do
if type(v) == "string" then
v = "\""..v.."\""
end
ser = ser.."[\""..k.."\"] = "..v..","
end
if ser == "{" then
return "{}"
end
return ser:sub(1, ser:len()-1).."}"
end,
eq = function(self, other)
local selft = self:to_table()
local othert = other:to_table()
for k, v in pairs(selft) do
if v ~= othert[k] then
return false
end
end
for k, v in pairs(othert) do
if v ~= selft[k] then
return false
end
end
return true
end,
}
function lua_inv.metadata(p)
return setmetatable({}, {
__index = {
data = setmetatable({}, {
__newindex = function(self, key, val)
error("Error: Attempt to directly set a value in a metadata's internal storage. Please call one of the provided functions instead.")
end,
__metatable = false,
}),
parent = setmetatable({}, {
__index = p or {orphaned = true},
__newindex = function(self, key, val)
error("Error: Attempt to directly set a metadata's parent. This can only be done in the constructor.")
end,
__metatable = false
}),
allow_change = static.allow_change,
on_change = static.on_change,
contains = static.contains,
get = static.get,
remove = static.remove,
set_string = static.set_string,
get_string = static.get_string,
set_int = static.set_int,
get_int = static.get_int,
set_float = static.set_float,
get_float = static.get_float,
to_table = static.to_table,
from_table = static.from_table,
equals = static.equals,
serialize = static.serialize,
},
__newindex = function(self, key, val)
error("Error: Attempt to directly set a value in a metadata object. Please call one of the provided functions instead.")
end,
__eq = static.eq,
__metatable = {},
})
end

94
misc.lua Normal file
View File

@ -0,0 +1,94 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
minetest.register_chatcommand("is", {
description = "Get the item string of the item that you are holding.",
func = function(name, param)
local player = minetest.get_player_by_name(name)
if not player:get_pos() then return end
minetest.chat_send_player(name, lua_inv.get_player_wielded_item(player):to_string())
end
})
minetest.register_craftitem("lua_inv:die", {
description = "Roll the die!",
inventory_image = "lua_inv_die_1.png",
_lua_inv_on_use = function(itemstack, user, pointed_thing)
local meta = itemstack:get_meta()
local side = math.random(6)
minetest.chat_send_player(user:get_player_name(), "You got a "..side.."!")
meta:set_string("inventory_image", "lua_inv_die_"..side..".png")
end
})
local torch_def = {
description = "Animated Torch",
inventory_image = "lua_inv_torch_animated.png",
_lua_inv_animation = function(self, frame)
return {frames = 16, speed = 250, frame_template = "lua_inv_torch_%d.png"}
end
}
if minetest.get_modpath("default") then
minetest.override_item("default:torch", torch_def)
minetest.register_alias("lua_inv:torch", "default:torch")
else
minetest.register_craftitem("lua_inv:torch", torch_def)
end
minetest.register_craftitem("lua_inv:pick", {
description = "Stackable Pickaxe",
inventory_image = "lua_inv_stackwear_pick.png",
tool_capabilities = {
full_punch_interval = 1.2,
max_drop_level=0,
groupcaps={
cracky = {times={[3]=0.60}, uses=2, maxlevel=1},
},
damage_groups = {fleshy=2},
},
sound = {breaks = "default_tool_breaks"},
groups = {pickaxe = 1}
})
minetest.register_chatcommand("testitems", {
description = "Gain a set of test lua_inv test items.",
privs = {debug = true},
func = function(name, param)
local player = minetest.get_player_by_name(name)
if not player:get_pos() then return end
local inv = lua_inv.player_inventory[name].inv
inv:add_item("main", lua_inv.itemstack("lua_inv:die"))
inv:add_item("main", lua_inv.itemstack("lua_inv:torch"))
inv:add_item("main", lua_inv.itemstack("lua_inv:pick", 66, 75))
inv:add_item("main", lua_inv.itemstack("lua_inv:pick", 75, 33))
end
})

6
mod.conf Normal file
View File

@ -0,0 +1,6 @@
name = lua_inv
title = Complete and Total Lua-Only Inventory Rewrite
description = Creates a completely custom inventory system, which doesn't rely at all on any of the usual UserData objects. It's built from the ground up to support various features that are considered "impossible" in normal Minetest. Animated items, items that can dynamically change their sprite without making a million copies of the same registration, itemstacks that have both count and wear set at once, and more.
depends = controls, entitycontrol, smart_vector_table
optional_depends = default, sfinv
min_minetest_version = 5.4

View File

@ -0,0 +1,88 @@
# Blender v2.79 (sub 0) OBJ File: 'lua_inv_extrusion.blend'
# www.blender.org
mtllib lua_inv_extrusion.mtl
o Plane.001
v 0.500000 8.000000 8.000000
v 0.500000 -8.000000 8.000000
v 0.500000 8.000000 -8.000000
v 0.500000 -8.000000 -8.000000
v 0.375000 8.000000 8.000000
v 0.375000 -8.000000 8.000000
v 0.375000 8.000000 -8.000000
v 0.375000 -8.000000 -8.000000
v 0.250000 8.000000 8.000000
v 0.250000 -8.000000 8.000000
v 0.250000 8.000000 -8.000000
v 0.250000 -8.000000 -8.000000
v 0.125000 8.000000 8.000000
v 0.125000 -8.000000 8.000000
v 0.125000 8.000000 -8.000000
v 0.125000 -8.000000 -8.000000
v 0.000000 8.000000 8.000000
v 0.000000 -8.000000 8.000000
v 0.000000 8.000000 -8.000000
v 0.000000 -8.000000 -8.000000
v -0.125000 8.000000 8.000000
v -0.125000 -8.000000 8.000000
v -0.125000 8.000000 -8.000000
v -0.125000 -8.000000 -8.000000
v -0.250000 8.000000 8.000000
v -0.250000 -8.000000 8.000000
v -0.250000 8.000000 -8.000000
v -0.250000 -8.000000 -8.000000
v -0.375000 8.000000 8.000000
v -0.375000 -8.000000 8.000000
v -0.375000 8.000000 -8.000000
v -0.375000 -8.000000 -8.000000
v -0.500000 8.000000 8.000000
v -0.500000 -8.000000 8.000000
v -0.500000 8.000000 -8.000000
v -0.500000 -8.000000 -8.000000
vt 0.000898 0.999102
vt 0.000898 0.000898
vt 0.999102 0.000898
vt 0.999102 0.999102
vt 0.000898 0.999102
vt 0.000898 0.000898
vt 0.999101 0.000898
vt 0.999102 0.999102
vt 0.000898 0.999102
vt 0.000898 0.000898
vt 0.999102 0.000898
vt 0.999102 0.999102
vt 0.000898 0.999102
vt 0.000898 0.000898
vt 0.999102 0.000898
vt 0.999102 0.999102
vt 0.000898 0.999102
vt 0.000898 0.000898
vt 0.999102 0.000898
vt 0.999102 0.999102
vt 0.000898 0.999102
vt 0.000898 0.000898
vt 0.999102 0.000898
vt 0.999102 0.999102
vt 0.000898 0.999102
vt 0.000898 0.000898
vt 0.999102 0.000898
vt 0.999102 0.999102
vt 0.000898 0.999102
vt 0.000898 0.000898
vt 0.999102 0.000898
vt 0.999102 0.999102
vt 0.000898 0.999102
vt 0.000898 0.000898
vt 0.999102 0.000898
vt 0.999102 0.999102
vn 1.0000 -0.0000 0.0000
usemtl None
s off
f 1/1/1 2/2/1 4/3/1 3/4/1
f 5/5/1 6/6/1 8/7/1 7/8/1
f 9/9/1 10/10/1 12/11/1 11/12/1
f 13/13/1 14/14/1 16/15/1 15/16/1
f 17/17/1 18/18/1 20/19/1 19/20/1
f 21/21/1 22/22/1 24/23/1 23/24/1
f 25/25/1 26/26/1 28/27/1 27/28/1
f 29/29/1 30/30/1 32/31/1 31/32/1
f 33/33/1 34/34/1 36/35/1 35/36/1

View File

@ -0,0 +1,71 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
-----------------------------------------------------
-- Chests --
-----------------------------------------------------
local function chest_formspec(pos)
return lua_inv.dynamic_formspec({
lua_inv.formspec_element("size", {{8, 10}}),
lua_inv.dynamic_list({k = "nodemeta", v = {pos.x, pos.y, pos.z}}, "main", 0, 0.3, 8, 4),
lua_inv.dynamic_list("current_player", "main", 0, 4.85, 8, 1),
lua_inv.dynamic_list("current_player", "main", 0, 6.08, 8, 3, 8),
lua_inv.stack_mode_selector(0, 9.1)
})
end
local function chest_override(name)
local def = minetest.registered_items[name]
local override_def = {}
override_def.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
minetest.sound_play(def.sound_open, {gain = 0.3, pos = pos,
max_hear_distance = 10}, true)
if not default.chest.chest_lid_obstructed(pos) then
minetest.swap_node(pos, {
name = name .. "_open",
param2 = node.param2 })
end
minetest.after(0.2, lua_inv.show_formspec,
clicker, name, chest_formspec(pos))
default.chest.open_chests[clicker:get_player_name()] = { pos = pos,
sound = def.sound_close, swap = name }
end
override_def.can_dig = function(pos, player)
local inv = lua_inv.fetch_node_inventory(pos, true)
return inv and inv:is_empty("main") and default.can_interact_with_node(player, pos)
end
override_def._lua_inv_inventory = function(pos)
local inv = lua_inv.inventory(pos)
inv:set_size("main", 32)
return inv
end
minetest.override_item(name, override_def)
minetest.override_item(name.."_open", override_def)
end
chest_override("default:chest")
chest_override("default:chest_locked")

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 B

BIN
textures/lua_inv_bar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

BIN
textures/lua_inv_bar_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

BIN
textures/lua_inv_die_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

BIN
textures/lua_inv_die_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

BIN
textures/lua_inv_die_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

BIN
textures/lua_inv_die_4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

BIN
textures/lua_inv_die_5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

BIN
textures/lua_inv_die_6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

BIN
textures/lua_inv_hotbar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

100
wield_item.lua Normal file
View File

@ -0,0 +1,100 @@
--[[
Complete and Total Lua-Only Inventory Rewrite
Copyright (C) 2021 Noodlemire
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--]]
local wielded_item_entities = {}
minetest.register_entity("lua_inv:wielded_item", {
initial_properties = {
visual_size = {x = 0.25, y = 0.25},
textures = {""},
pointable = false,
is_visible = false,
},
_item = "",
on_step = function(self, dtime)
if not self._owner or not lua_inv.player_inventory[self._owner:get_player_name()] then return end
local wielded_item = lua_inv.get_player_wielded_item(self._owner)
if self._item ~= wielded_item:to_string() then
self._item = wielded_item:to_string()
local anim = wielded_item:get_animation(1)
if anim then
self._anim = {
frame = 1,
max_frame = anim.frames,
get_frame = anim.frame_template,
time = anim.speed,
init_time = anim.speed
}
elseif self._anim then
self._anim = nil
end
if self._item ~= "" then
if wielded_item:get_inventory_image():sub(1, 14) == "[inventorycube" then
self.object:set_properties({
is_visible = true,
visual = "cube",
textures = lua_inv.tiles_to_cube_textures(wielded_item:get_definition().tiles)
})
else
self.object:set_properties({
is_visible = true,
visual = "mesh",
mesh = "lua_inv_extrusion.obj",
textures = {anim and anim.frame_template:format(1) or wielded_item:get_inventory_image()},
backface_culling = false,
})
end
else
self.object:set_properties({
is_visible = false
})
end
elseif self._anim then
self._anim.time = self._anim.time - dtime * 1000
if self._anim.time < 0 then
self._anim.time = self._anim.init_time
self._anim.frame = self._anim.frame + 1
if self._anim.frame > self._anim.max_frame then
self._anim.frame = 1
end
self.object:set_properties({textures = {self._anim.get_frame:format(self._anim.frame)}})
end
end
end
})
minetest.register_on_joinplayer(function(player)
player:hud_set_flags({wielditem = false})
local ent = minetest.add_entity(player:get_pos(), "lua_inv:wielded_item"):get_luaentity()
ent._owner = player
wielded_item_entities[player:get_player_name()] = ent
ent.object:set_attach(player, "Arm_Right", {x=0, y=4, z=2.5}, {x=-45, y=180, z=0}, true)
end)