Convert the modpack into a standalone game for the Minetest engine

Integrated into the game is most of minetest_game as the stable base
master
VanessaE 2021-02-23 23:40:11 -05:00 committed by Vanessa Dannenberg
parent 11adf56b75
commit 4394beec6a
8861 changed files with 37424 additions and 4811 deletions

519
LICENSE.txt Normal file
View File

@ -0,0 +1,519 @@
License of media (textures and sounds)
--------------------------------------
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
See README.txt in each mod directory for information about other authors.
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
http://creativecommons.org/licenses/by-sa/3.0/
License of menu/header.png
Copyright (C) 2015 paramat CC BY-SA 3.0
License of source code
----------------------
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
See README.txt in each mod directory for information about other authors.
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
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

179
README.md Normal file
View File

@ -0,0 +1,179 @@
# Overview
Dreambuilder is my attempt to give the player pretty much everything they'll ever want to build with, and all the tools they should ever need to actually get the job done.
This game is in use on my Creative server and on my Survival server, which also has a few extra mods installed for its specific needs. It should give you a pretty good idea nonetheless. Expect lag, as it's a significantly-developed multiplayer server, after all.
##  
# What's in it? What's changed from the default stuff?
* This game retains the light-colored user interface theme that was once present in old versions of minetest_game, because I don't like feeling like I live in a cave when I open a formspec/menu.
* The complete Plantlife Modpack along with More Trees and Vines mods add a huge amount of variation to your landscape (as a result, they will add mapgen lag). Active spawning of Horsetail ferns is disabled by default, and I've added papyrus growth on dirt/grass with leaves (using a copy of the default growth ABM).
* This game includes the Unified Inventory mod, which overrides the old default inventory to give you a much more powerful user interface, with crafting guide, bags, and more, and it also means that if you're using this game in creative mode, your stacks wil behave as if they're infinite, but can still have a proper stack count so that you can craft easily when needed.
* The default bones and TNT mods have been disabled. They're not exactly useful for building stuff with.
* Stu's split-limb player model replaces the default one.
* The default hotbar HUD holds 16 items instead of 8, taken from the top two rows of your inventory. The first 10 slots can be accessed by number keys 1-9 and 0, the rest via your mouse wheel. You can use `/hotbar ##` to change the number of slots from 1 to 23.
* The default lavacooling code has been supplanted by better, safer code from my Gloopblocks mod. That mod also provides stone/cobble --> mossy stone/cobble transformation in the presence of water.
* An extensive selection of administration tools for single-player and server use are included, such as areas, maptools, worldedit, xban, and more.
* A few textures here and there are different.
* The mapgen won't spawn apples on default trees, nor will they appear on a sapling-grown default tree. Only the *real* apple trees supplied by the Moretrees mod will bear apples (both at mapgen time and sapling-grown). Or at least that's how it's supposed to work. :stuck_out_tongue: While on that subject, apples now use a 3d model instead of the plantlike version.
##  
# Okay, what else?
A whole boatload of other mods have been added, which is where most of the content actually comes from. To be a little more specific, as of February 2021, this game has a total of 208 mods (counting all of the various components that themselves come as part of some modpack, such as mesecons or homedecor) and supplies over 3100 items in the inventory/craft guide (there are tens of thousands of unique items in total, counting everything that isn't displayed in the inventory)! A mostly-complete list of mods is as follows:
ambience redo
arrowboards
bakedclay
basic_materials
basic_signs
bedrock
bees
biome_lib
blox
bobblocks
bonemeal
caverealms_lite
cblocks
coloredwood
cottages
currency
datastorage
digidisplay
digilines
digistuff
display_blocks_redo
dreambuilder_hotbar
extra_stairsplus
facade
farming
framedglass
function_delayer
gardening
gloopblocks
glooptest
ilights
invsaw
item_drop
jumping
led_marquee
locks
maptools
memorandum
misc_overrides
moreblocks
moreores
moretrees
mymillwork
new_campfire
nixie_tubes
pipeworks
plasticbox
player_textures
prefab_redo
quartz
replacer
rgblightstone
signs_lib
simple_streetlights
solidcolor
stained_glass
street_signs
titanium
travelnet
unifiedbricks
unifieddyes
unified_inventory
unifiedmesecons
windmill
The full Home Decor modpack
The full Technic modpack
The full Plantlife modpack
Cheapie's Roads modpack
Zeg9's Steel modpack
Zeg9's UFO modpack
The full Mesecons modpack
Jeija's Jumping modpack
The full Worldedit modpack
### Your Inventory Display
This game, as previously mentioned, replaces the standard inventory with Unified Inventory, which almost defies description here. Unified Inventory includes waypoints, a crafting guide, set/go home buttons, set day/set night buttons, a full creative inventory on the right if you're playing in that mode - and you only have to click/tap the item once to get the it, instead of multiple clicks/drag and drop, a trash slot, a clear all inventory button, a search feature for the inventory, and more. Basically, you just need to use it a few times and you'll find yourself wondering how you ever got along with the standard inventory!
### The Circular Saw
This game uses the More Blocks mod, which comes with the Stairsplus mod and more importantly, the Circular Saw mod by Sokomine and co. This mod replaces the traditional method of creating stairs, slabs, and the like: rather that crafting a stairs block by placing several of the material into your crafting grid, you must first craft a circular saw (really, a table saw), place that on the ground, and then use that to shape the material you had in mind. It can create dozens of shapes, including the standard stairs and slabs. Give it a try and see for yourself!
### Land Ownership
This game uses ShadowNinja's areas mod for land protection, as well as cheapie's protector blocks. Of course, land protection is only useful if you're using this game on a public server.
#### Protection blocks:
These are easy. Craft one, place it, and everything within 15m of it becomes yours immediately (if someone else doesn't own some of the land therein, of course). If you dig one of these, the area protection it created is removed; if you shift-dig, the protection is preserved, but the block is deleted, giving back some steel ingots if you're in survival mode, or nothing at all if in creative mode.
#### Areas:
If you want fine control, use the areas mod's commands. There are three ways to select a region to protect:
**Option A:**
Just type `/area_pos set` and then punch two nodes that are diagonally opposite one another, so that they form a 3d box that fully encloses the area you want to claim. A black **`[1]`** or **`[2]`** will appear where you punched.
**Option B:**
1. Move to one corner, on the ground.
2. Type `/area_pos1` and press enter. A black **`[1]`** will appear.
3. Move to the other corner and go up a ways above your area - not too high though.
4. Type `/area_pos2` and press enter. A black **`[2]`** will appear.
**Option C:**
Just give actual coordinates to the `/area_xxx` commands, e.g.:
`/area_pos1 123,45,678 /area_pos2 987,654,321`
**Claim it:**
Once you've marked your area using one of the above methods, you must actually claim it. This is the step that actually protects it against vandalism and unauthorized access. Just type:
/protect some description here
By default, users may protect up to 3 zones with these commands, and each can be up to 50x100x50 meters in size, but this can be changed by plugging appropriate settings into your minetest.conf.
**Sublet it:**
Ok, you've claimed an area, and you want to let someone else build there. Simple. Set the coordinates of the box you want to let them build in, using the commands above (Options A, B, or C). Then do:
`/add_owner your_area# their_name description here`
For example, if you own area #123 and the other person's name is "Mike", and you want to sublet them some area called "Mike's home", you might do something like this:
`/add_owner 123 Mike Mikes Home`
You can add as many users as you like to your areas. You will need to issue one such command per user.
## Dependencies:
This game requires Minetest 5.3.0 or later, and a corresponding copy of minetest_game. Anything too old will likely either crash, show nodes with the wrong shape or colors, throw nonsensical warnings/errors, or open up wormholes.
## Hardware requirements:
This game defines a very large number of items and produces a well-detailed landscape, and so it requires a significant amount of resources compared to vanilla Minetest game. At least a 2 GHz dual core CPU and 2 GB free RAM are required for good performance. If you use my HDX texture pack, you'll need more RAM (at least 4 GB free recommended).
This game is NOT intended for use on mobile devices.
## Download/Install:
...if you're reading this, you're either on the Dreambuilder Gitlab repo page, so clone it from there, or download the ZIP... or maybe you already have it. ;-)
Just rename the project folder to "dreambuilder_game", if necessary, and move it to your Minetest mods directory. Then select and enable it for the world you want to use it in. Depending on the condition of the world you are using, and for brand new maps, this game may take a minute or two to start, during which time you may see the hotbar and hand, all-grey window content where the world should be, or other odd-looking things. Just wait it out, it will eventually start and settle down.
## License:
Each of the base mods in this game retains the standard license that its author has assigned, even if the license file is missing from the archive. All changes and any supplemental content made by me is WTFPL unless explicitly stated otherwise.
# Open Source Software
This game is open source, or at least as much so as I have control over. Since it started from the standard minetest_game distribution, you'll want to look at that. You can find it at its usual Github repository, here:
[url]https://github.com/minetest/minetest_game[/url]
An online copy of the archive of mods the game is built from can be found here (with full git histories, including my changes and any files that went missing from the completed game, where applicable):
[url]http://minetest.daconcepts.com/my-main-mod-archive/[/url]
# Notes:
For best results, I recommend adding the following to your minetest.conf (perhaps to a secondary copy of that file that you only use when playing worlds with this game):

View File

@ -1,329 +0,0 @@
ambience = {}
-- override default water sounds
minetest.override_item("default:water_source", { sounds = {} })
minetest.override_item("default:water_flowing", { sounds = {} })
minetest.override_item("default:river_water_source", { sounds = {} })
minetest.override_item("default:river_water_flowing", { sounds = {} })
-- settings
local SOUNDVOLUME = 1.0
local MUSICVOLUME = 1.0
local play_music = minetest.settings:get_bool("ambience_music") ~= false
local pplus = minetest.get_modpath("playerplus")
local radius = 6
local playing = {}
local sound_sets = {} -- all the sounds and their settings
local sound_set_order = {} -- needed because pairs loops randomly through tables
local set_nodes = {} -- all the nodes needed for sets
-- global functions
-- add set to list
ambience.add_set = function(set_name, def)
if not set_name or not def then
return
end
sound_sets[set_name] = {
frequency = def.frequency or 50,
sounds = def.sounds,
sound_check = def.sound_check,
nodes = def.nodes
}
-- add set name to the sound_set_order table
local can_add = true
for i = 1, #sound_set_order do
if sound_set_order[i] == set_name then
can_add = false
end
end
if can_add then
table.insert(sound_set_order, set_name)
end
-- add any missing nodes to the set_nodes table
if def.nodes then
for i = 1, #def.nodes do
can_add = def.nodes[i]
for j = 1, #set_nodes do
if def.nodes[i] == set_nodes[j] then
can_add = false
end
end
if can_add then
table.insert(set_nodes, can_add)
end
end
end
end
-- return set from list using name
ambience.get_set = function(set_name)
if sound_sets[set_name] then
return sound_sets[set_name]
end
end
-- remove set from list
ambience.del_set = function(set_name)
sound_sets[set_name] = nil
local can_del = false
for i = 1, #sound_set_order do
if sound_set_order[i] == set_name then
can_del = i
end
end
if can_del then
table.remove(sound_set_order, can_del)
end
end
-- plays music and selects sound set
local get_ambience = function(player, tod, name)
-- play server or local music if available
if play_music and playing[name] then
-- play at midnight
if tod >= 0.0 and tod <= 0.01 then
if not playing[name].music then
playing[name].music = minetest.sound_play("ambience_music", {
to_player = player:get_player_name(),
gain = MUSICVOLUME
})
end
elseif tod > 0.1 and playing[name].music then
playing[name].music = nil
end
end
-- get foot and head level nodes at player position
local pos = player:get_pos()
pos.y = pos.y + 1.4 -- head level
local nod_head = pplus and name and playerplus[name].nod_head or
minetest.get_node(pos).name
pos.y = pos.y - 1.2 -- foot level
local nod_feet = pplus and name and playerplus[name].nod_feet or
minetest.get_node(pos).name
pos.y = pos.y - 0.2 -- reset pos
-- get all set nodes around player
local ps, cn = minetest.find_nodes_in_area(
{x = pos.x - radius, y = pos.y - radius, z = pos.z - radius},
{x = pos.x + radius, y = pos.y + radius, z = pos.z + radius}, set_nodes)
-- loop through sets in order and choose first that meets it's conditions
for n = 1, #sound_set_order do
local set = sound_sets[ sound_set_order[n] ]
if set and set.sound_check then
-- pass settings to function for condition check
local set_name, gain = set.sound_check({
player = player,
pos = pos,
tod = tod,
totals = cn,
positions = ps,
head_node = nod_head,
feet_node = nod_feet
})
-- if conditions met return set name and gain value
if set_name then
return set_name, gain
end
end
end
end
local timer = 0
local random = math.random
-- players routine
minetest.register_globalstep(function(dtime)
-- one second timer
timer = timer + dtime
if timer < 1 then return end
timer = 0
-- get list of players and set some variables
local players = minetest.get_connected_players()
local player_name, number, chance, ambience, handler, ok
local tod = minetest.get_timeofday()
-- loop through players
for n = 1, #players do
player_name = players[n]:get_player_name()
--local t1 = os.clock()
local set_name, MORE_GAIN = get_ambience(players[n], tod, player_name)
--print(string.format("elapsed time: %.4f\n", os.clock() - t1))
ok = true -- everything starts off ok
-- stop current sound if another set active or gain changed
if playing[player_name]
and playing[player_name].handler then
if playing[player_name].set ~= set_name
or (playing[player_name].set == set_name
and playing[player_name].gain ~= MORE_GAIN) then
--print ("-- change stop", set_name, playing[player_name].old_handler)
minetest.sound_stop(playing[player_name].old_handler)
playing[player_name].set = nil
playing[player_name].handler = nil
playing[player_name].gain = nil
else
ok = false -- sound set still playing, skip new sound
end
end
-- set random chance and reset seed
chance = random(1, 1000)
math.randomseed(tod + chance)
-- if chance is lower than set frequency then select set
if ok and set_name and chance < sound_sets[set_name].frequency then
-- choose random sound from set
number = random(#sound_sets[set_name].sounds)
ambience = sound_sets[set_name].sounds[number]
-- play sound
handler = minetest.sound_play(ambience.name, {
to_player = player_name,
gain = ((ambience.gain or 0.3) + (MORE_GAIN or 0)) * SOUNDVOLUME,
pitch = ambience.pitch or 1.0
}, ambience.ephemeral)
--print ("playing... " .. ambience.name .. " (" .. chance .. " < "
-- .. sound_sets[set_name].frequency .. ") @ ", MORE_GAIN, handler)
-- only continue if sound playing returns handler
if handler then
--print("-- current handler", handler)
-- set what player is currently listening to
playing[player_name] = {
set = set_name, gain = MORE_GAIN,
handler = handler, old_handler = handler
}
-- set timer to stop sound
minetest.after(ambience.length, function()
--print("-- after", set_name, handler)
-- make sure we are stopping same sound we started
if playing[player_name]
and playing[player_name].handler
and playing[player_name].old_handler == handler then
--print("-- timed stop", set_name, handler)
--minetest.sound_stop(playing[player_name].handler)
minetest.sound_stop(handler)
-- reset player variables and backup handler
playing[player_name] = {
set = nil, gain = nil,
handler = nil, old_handler = nil
}
end
end)
end
end
end
end)
-- sound volume command
minetest.register_chatcommand("svol", {
params = "<svol>",
description = "set sound volume (0.1 to 1.0)",
privs = {server = true},
func = function(name, param)
SOUNDVOLUME = tonumber(param) or SOUNDVOLUME
if SOUNDVOLUME < 0.1 then SOUNDVOLUME = 0.1 end
if SOUNDVOLUME > 1.0 then SOUNDVOLUME = 1.0 end
return true, "Sound volume set to " .. SOUNDVOLUME
end,
})
-- music volume command (0 stops music)
minetest.register_chatcommand("mvol", {
params = "<mvol>",
description = "set music volume (0.1 to 1.0)",
privs = {server = true},
func = function(name, param)
MUSICVOLUME = tonumber(param) or MUSICVOLUME
-- ability to stop music just as it begins
if MUSICVOLUME == 0 and playing[name].music then
minetest.sound_stop(playing[name].music)
end
if MUSICVOLUME < 0.1 then MUSICVOLUME = 0.1 end
if MUSICVOLUME > 1.0 then MUSICVOLUME = 1.0 end
return true, "Music volume set to " .. MUSICVOLUME
end,
})
-- load default sound sets
dofile(minetest.get_modpath("ambience") .. "/soundsets.lua")
print("[MOD] Ambience Lite loaded")

View File

@ -1,679 +0,0 @@
bonemeal = {}
local path = minetest.get_modpath("bonemeal")
local min, max, random = math.min, math.max, math.random
-- Load support for intllib.
local S = minetest.get_translator and minetest.get_translator("bonemeal") or
dofile(path .. "/intllib.lua")
-- creative check
local creative_mode_cache = minetest.settings:get_bool("creative_mode")
function bonemeal.is_creative(name)
return creative_mode_cache or minetest.check_player_privs(name, {creative = true})
end
-- default crops
local crops = {
{"farming:cotton_", 8, "farming:seed_cotton"},
{"farming:wheat_", 8, "farming:seed_wheat"}
}
-- special pine check for nearby snow
local function pine_grow(pos)
if minetest.find_node_near(pos, 1,
{"default:snow", "default:snowblock", "default:dirt_with_snow"}) then
default.grow_new_snowy_pine_tree(pos)
else
default.grow_new_pine_tree(pos)
end
end
-- default saplings
local saplings = {
{"default:sapling", default.grow_new_apple_tree, "soil"},
{"default:junglesapling", default.grow_new_jungle_tree, "soil"},
{"default:emergent_jungle_sapling", default.grow_new_emergent_jungle_tree, "soil"},
{"default:acacia_sapling", default.grow_new_acacia_tree, "soil"},
{"default:aspen_sapling", default.grow_new_aspen_tree, "soil"},
{"default:pine_sapling", pine_grow, "soil"},
{"default:bush_sapling", default.grow_bush, "soil"},
{"default:acacia_bush_sapling", default.grow_acacia_bush, "soil"},
{"default:large_cactus_seedling", default.grow_large_cactus, "sand"},
{"default:blueberry_bush_sapling", default.grow_blueberry_bush, "soil"},
{"default:pine_bush_sapling", default.grow_pine_bush, "soil"}
}
-- helper tables ( "" denotes a blank item )
local green_grass = {
"default:grass_2", "default:grass_3", "default:grass_4",
"default:grass_5", "", ""
}
local dry_grass = {
"default:dry_grass_2", "default:dry_grass_3", "default:dry_grass_4",
"default:dry_grass_5", "", ""
}
-- add all in-game flowers except waterlily
local flowers = {}
for node, def in pairs(minetest.registered_nodes) do
if def.groups.flower and not node:find("waterlily") then
flowers[#flowers + 1] = node
end
end
-- add additional bakedclay flowers if enabled
if minetest.get_modpath("bakedclay") then
flowers[#flowers + 1] = "bakedclay:delphinium"
flowers[#flowers + 1] = "bakedclay:thistle"
flowers[#flowers + 1] = "bakedclay:lazarus"
flowers[#flowers + 1] = "bakedclay:mannagrass"
flowers[#flowers + 1] = ""
end
-- default biomes deco
local deco = {
{"default:dirt_with_dry_grass", dry_grass, flowers},
{"default:sand", {}, {"default:dry_shrub", "", "", ""} },
{"default:desert_sand", {}, {"default:dry_shrub", "", "", ""} },
{"default:silver_sand", {}, {"default:dry_shrub", "", "", ""} },
}
--
-- local functions
--
-- particles
local function particle_effect(pos)
minetest.add_particlespawner({
amount = 4,
time = 0.15,
minpos = pos,
maxpos = pos,
minvel = {x = -1, y = 2, z = -1},
maxvel = {x = 1, y = 4, z = 1},
minacc = {x = -1, y = -1, z = -1},
maxacc = {x = 1, y = 1, z = 1},
minexptime = 1,
maxexptime = 1,
minsize = 1,
maxsize = 3,
texture = "bonemeal_particle.png"
})
end
-- tree type check
local function grow_tree(pos, object)
if type(object) == "table" and object.axiom then
-- grow L-system tree
minetest.remove_node(pos)
minetest.spawn_tree(pos, object)
elseif type(object) == "string" and minetest.registered_nodes[object] then
-- place node
minetest.set_node(pos, {name = object})
elseif type(object) == "function" then
-- function
object(pos)
end
end
-- sapling check
local function check_sapling(pos, nodename)
-- what is sapling placed on?
local under = minetest.get_node({
x = pos.x,
y = pos.y - 1,
z = pos.z
})
local can_grow, grow_on
-- check list for sapling and function
for n = 1, #saplings do
if saplings[n][1] == nodename then
grow_on = saplings[n][3]
-- sapling grows on top of specific node
if grow_on
and grow_on ~= "soil"
and grow_on ~= "sand"
and grow_on == under.name then
can_grow = true
end
-- sapling grows on top of soil (default)
if can_grow == nil
and (grow_on == nil or grow_on == "soil")
and minetest.get_item_group(under.name, "soil") > 0 then
can_grow = true
end
-- sapling grows on top of sand
if can_grow == nil
and grow_on == "sand"
and minetest.get_item_group(under.name, "sand") > 0 then
can_grow = true
end
-- check if we can grow sapling
if can_grow then
particle_effect(pos)
grow_tree(pos, saplings[n][2])
return true
end
end
end
end
-- crops check
local function check_crops(pos, nodename, strength)
local mod, crop, stage, nod, def
-- grow registered crops
for n = 1, #crops do
if nodename:find(crops[n][1])
or nodename == crops[n][3] then
-- separate mod and node name
mod = nodename:split(":")[1] .. ":"
crop = nodename:split(":")[2]
-- get stage number or set to 0 for seed
stage = tonumber( crop:split("_")[2] ) or 0
stage = min(stage + strength, crops[n][2])
-- check for place_param setting
nod = crops[n][1] .. stage
def = minetest.registered_nodes[nod]
def = def and def.place_param2 or 0
minetest.set_node(pos, {name = nod, param2 = def})
particle_effect(pos)
return true
end
end
end
-- check soil for specific decoration placement
local function check_soil(pos, nodename, strength)
-- set radius according to strength
local side = strength - 1
local tall = max(strength - 2, 0)
local floor
local groups = minetest.registered_items[nodename]
and minetest.registered_items[nodename].groups or {}
-- only place decoration on one type of surface
if groups.soil then
floor = {"group:soil"}
elseif groups.sand then
floor = {"group:sand"}
else
floor = {nodename}
end
-- get area of land with free space above
local dirt = minetest.find_nodes_in_area_under_air(
{x = pos.x - side, y = pos.y - tall, z = pos.z - side},
{x = pos.x + side, y = pos.y + tall, z = pos.z + side}, floor)
-- set default grass and decoration
local grass = green_grass
local decor = flowers
-- choose grass and decoration to use on dirt patch
for n = 1, #deco do
-- do we have a grass match?
if nodename == deco[n][1] then
grass = deco[n][2] or {}
decor = deco[n][3] or {}
end
end
local pos2, nod, def
-- loop through soil
for _, n in pairs(dirt) do
if random(5) == 5 then
if decor and #decor > 0 then
-- place random decoration (rare)
local dnum = #decor or 1
nod = decor[random(dnum)] or ""
end
else
if grass and #grass > 0 then
-- place random grass (common)
local dgra = #grass or 1
nod = #grass > 0 and grass[random(dgra)] or ""
end
end
pos2 = n
pos2.y = pos2.y + 1
if nod and nod ~= "" then
-- get crop param2 value
def = minetest.registered_nodes[nod]
def = def and def.place_param2
-- if param2 not preset then get from existing node
if not def then
local node = minetest.get_node_or_nil(pos2)
def = node and node.param2 or 0
end
minetest.set_node(pos2, {name = nod, param2 = def})
end
particle_effect(pos2)
end
end
-- global functions
-- add to sapling list
-- {sapling node, schematic or function name, "soil"|"sand"|specific_node}
--e.g. {"default:sapling", default.grow_new_apple_tree, "soil"}
function bonemeal:add_sapling(list)
for n = 1, #list do
saplings[#saplings + 1] = list[n]
end
end
-- add to crop list to force grow
-- {crop name start_, growth steps, seed node (if required)}
-- e.g. {"farming:wheat_", 8, "farming:seed_wheat"}
function bonemeal:add_crop(list)
for n = 1, #list do
crops[#crops + 1] = list[n]
end
end
-- add grass and flower/plant decoration for specific dirt types
-- {dirt_node, {grass_nodes}, {flower_nodes}
-- e.g. {"default:dirt_with_dry_grass", dry_grass, flowers}
-- if an entry already exists for a given dirt type, it will add new entries and all empty
-- entries, allowing to both add decorations and decrease their frequency.
function bonemeal:add_deco(list)
for l = 1, #list do
for n = 1, #deco do
-- update existing entry
if list[l][1] == deco[n][1] then
-- adding grass types
for _, extra in pairs(list[l][2]) do
if extra ~= "" then
for _, entry in pairs(deco[n][2]) do
if extra == entry then
extra = false
break
end
end
end
if extra then
deco[n][2][#deco[n][2] + 1] = extra
end
end
-- adding decoration types
for _, extra in ipairs(list[l][3]) do
if extra ~= "" then
for __, entry in pairs(deco[n][3]) do
if extra == entry then
extra = false
break
end
end
end
if extra then
deco[n][3][#deco[n][3] + 1] = extra
end
end
list[l] = false
break
end
end
if list[l] then
deco[#deco + 1] = list[l]
end
end
end
-- definitively set a decration scheme
-- this function will either add a new entry as is, or replace the existing one
function bonemeal:set_deco(list)
for l = 1, #list do
for n = 1, #deco do
-- replace existing entry
if list[l][1] == deco[n][1] then
deco[n][2] = list[l][2]
deco[n][3] = list[l][3]
list[l] = false
break
end
end
if list[l] then
deco[#deco + 1] = list[l]
end
end
end
-- global on_use function for bonemeal
function bonemeal:on_use(pos, strength, node)
-- get node pointed at
local node = node or minetest.get_node(pos)
-- return if nothing there
if node.name == "ignore" then
return
end
-- make sure strength is between 1 and 4
strength = strength or 1
strength = max(strength, 1)
strength = min(strength, 4)
-- papyrus and cactus
if node.name == "default:papyrus" then
default.grow_papyrus(pos, node)
particle_effect(pos)
return true
elseif node.name == "default:cactus" then
default.grow_cactus(pos, node)
particle_effect(pos)
return true
end
-- grow grass and flowers
if minetest.get_item_group(node.name, "soil") > 0
or minetest.get_item_group(node.name, "sand") > 0
or minetest.get_item_group(node.name, "can_bonemeal") > 0 then
check_soil(pos, node.name, strength)
return true
end
-- light check depending on strength (strength of 4 = no light needed)
if (minetest.get_node_light(pos) or 0) < (12 - (strength * 3)) then
return
end
-- check for tree growth if pointing at sapling
if minetest.get_item_group(node.name, "sapling") > 0
and random(5 - strength) == 1 then
check_sapling(pos, node.name)
return true
end
-- check for crop growth
if check_crops(pos, node.name, strength) then
return true
end
end
--
-- items
--
-- mulch (strength 1)
minetest.register_craftitem("bonemeal:mulch", {
description = S("Mulch"),
inventory_image = "bonemeal_mulch.png",
on_use = function(itemstack, user, pointed_thing)
-- did we point at a node?
if pointed_thing.type ~= "node" then
return
end
-- is area protected?
if minetest.is_protected(pointed_thing.under, user:get_player_name()) then
return
end
-- call global on_use function with strength of 1
if bonemeal:on_use(pointed_thing.under, 1) then
-- take item if not in creative
if not bonemeal.is_creative(user:get_player_name()) then
itemstack:take_item()
end
end
return itemstack
end
})
-- bonemeal (strength 2)
minetest.register_craftitem("bonemeal:bonemeal", {
description = S("Bone Meal"),
inventory_image = "bonemeal_item.png",
on_use = function(itemstack, user, pointed_thing)
-- did we point at a node?
if pointed_thing.type ~= "node" then
return
end
-- is area protected?
if minetest.is_protected(pointed_thing.under, user:get_player_name()) then
return
end
-- call global on_use function with strength of 2
if bonemeal:on_use(pointed_thing.under, 2) then
-- take item if not in creative
if not bonemeal.is_creative(user:get_player_name()) then
itemstack:take_item()
end
end
return itemstack
end
})
-- fertiliser (strength 3)
minetest.register_craftitem("bonemeal:fertiliser", {
description = S("Fertiliser"),
inventory_image = "bonemeal_fertiliser.png",
on_use = function(itemstack, user, pointed_thing)
-- did we point at a node?
if pointed_thing.type ~= "node" then
return
end
-- is area protected?
if minetest.is_protected(pointed_thing.under, user:get_player_name()) then
return
end
-- call global on_use function with strength of 3
if bonemeal:on_use(pointed_thing.under, 3) then
-- take item if not in creative
if not bonemeal.is_creative(user:get_player_name()) then
itemstack:take_item()
end
end
return itemstack
end
})
-- bone
minetest.register_craftitem("bonemeal:bone", {
description = S("Bone"),
inventory_image = "bonemeal_bone.png",
groups = {bone = 1}
})
-- gelatin powder
minetest.register_craftitem("bonemeal:gelatin_powder", {
description = S("Gelatin Powder"),
inventory_image = "bonemeal_gelatin_powder.png",
groups = {food_gelatin = 1, flammable = 2}
})
--
-- crafting recipes
--
-- gelatin powder
minetest.register_craft({
output = "bonemeal:gelatin_powder 4",
recipe = {
{"group:bone", "group:bone", "group:bone"},
{"bucket:bucket_water", "bucket:bucket_water", "bucket:bucket_water"},
{"bucket:bucket_water", "default:torch", "bucket:bucket_water"},
},
replacements = {
{"bucket:bucket_water", "bucket:bucket_empty 5"},
}
})
-- bonemeal (from bone)
minetest.register_craft({
type = "shapeless",
output = "bonemeal:bonemeal 2",
recipe = {"group:bone"}
})
-- bonemeal (from player bones)
minetest.register_craft({
type = "shapeless",
output = "bonemeal:bonemeal 4",
recipe = {"bones:bones"}
})
-- bonemeal (from coral skeleton)
minetest.register_craft({
type = "shapeless",
output = "bonemeal:bonemeal 2",
recipe = {"default:coral_skeleton"}
})
-- mulch
minetest.register_craft({
type = "shapeless",
output = "bonemeal:mulch 4",
recipe = {
"group:tree", "group:leaves", "group:leaves",
"group:leaves", "group:leaves", "group:leaves",
"group:leaves", "group:leaves", "group:leaves"
}
})
minetest.register_craft({
type = "shapeless",
output = "bonemeal:mulch",
recipe = {
"group:seed", "group:seed", "group:seed",
"group:seed", "group:seed", "group:seed",
"group:seed", "group:seed", "group:seed"
}
})
-- fertiliser
minetest.register_craft({
type = "shapeless",
output = "bonemeal:fertiliser 2",
recipe = {"bonemeal:bonemeal", "bonemeal:mulch"}
})
-- add bones to dirt
minetest.override_item("default:dirt", {
drop = {
max_items = 1,
items = {
{
items = {"bonemeal:bone"},
rarity = 40
},
{
items = {"default:dirt"}
}
}
}
})
-- add support for other mods
dofile(path .. "/mods.lua")
dofile(path .. "/lucky_block.lua")
print (S("[MOD] bonemeal loaded"))

View File

@ -1,303 +0,0 @@
---------------------------------------------------------------------------------------
-- simple anvil that can be used to repair tools
---------------------------------------------------------------------------------------
-- * can be used to repair tools
-- * the hammer gets dammaged a bit at each repair step
---------------------------------------------------------------------------------------
-- License of the hammer picture: CC-by-SA; done by GloopMaster; source:
-- https://github.com/GloopMaster/glooptest/blob/master/glooptest/textures/glooptest_tool_steelhammer.png
local S = cottages.S
-- the hammer for the anvil
minetest.register_tool("cottages:hammer", {
description = S("Steel hammer for repairing tools on the anvil"),
image = "glooptest_tool_steelhammer.png",
inventory_image = "glooptest_tool_steelhammer.png",
tool_capabilities = {
full_punch_interval = 0.8,
max_drop_level=1,
groupcaps={
-- about equal to a stone pick (it's not intended as a tool)
cracky={times={[2]=2.00, [3]=1.20}, uses=30, maxlevel=1},
},
damage_groups = {fleshy=6},
}
})
local cottages_anvil_formspec =
"size[8,8]"..
"image[7,3;1,1;glooptest_tool_steelhammer.png]"..
-- "list[current_name;sample;0,0.5;1,1;]"..
"list[current_name;input;2.5,1.5;1,1;]"..
-- "list[current_name;material;5,0;3,3;]"..
"list[current_name;hammer;5,3;1,1;]"..
-- "label[0.0,0.0;Sample:]"..
-- "label[0.0,1.0;(Receipe)]"..
"label[2.5,1.0;"..S("Workpiece:").."]"..
-- "label[6.0,-0.5;Materials:]"..
"label[6.0,2.7;"..S("Optional").."]"..
"label[6.0,3.0;"..S("storage for").."]"..
"label[6.0,3.3;"..S("your hammer").."]"..
"label[0,-0.5;"..S("Anvil").."]"..
"label[0,3.0;"..S("Punch anvil with hammer to").."]"..
"label[0,3.3;"..S("repair tool in workpiece-slot.").."]"..
"list[current_player;main;0,4;8,4;]";
minetest.register_node("cottages:anvil", {
drawtype = "nodebox",
description = S("anvil"),
tiles = {"cottages_stone.png"}, -- TODO default_steel_block.png, default_obsidian.png are also nice
paramtype = "light",
paramtype2 = "facedir",
groups = {cracky=2},
-- the nodebox model comes from realtest
node_box = {
type = "fixed",
fixed = {
{-0.5,-0.5,-0.3,0.5,-0.4,0.3},
{-0.35,-0.4,-0.25,0.35,-0.3,0.25},
{-0.3,-0.3,-0.15,0.3,-0.1,0.15},
{-0.35,-0.1,-0.2,0.35,0.1,0.2},
},
},
selection_box = {
type = "fixed",
fixed = {
{-0.5,-0.5,-0.3,0.5,-0.4,0.3},
{-0.35,-0.4,-0.25,0.35,-0.3,0.25},
{-0.3,-0.3,-0.15,0.3,-0.1,0.15},
{-0.35,-0.1,-0.2,0.35,0.1,0.2},
}
},
on_construct = function(pos)
local meta = minetest.get_meta(pos);
meta:set_string("infotext", S("Anvil"));
local inv = meta:get_inventory();
inv:set_size("input", 1);
-- inv:set_size("material", 9);
-- inv:set_size("sample", 1);
inv:set_size("hammer", 1);
meta:set_string("formspec", cottages_anvil_formspec );
end,
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos);
meta:set_string("owner", placer:get_player_name() or "");
meta:set_string("infotext", S("Anvil (owned by %s)"):format((meta:get_string("owner") or "")));
meta:set_string("formspec",
cottages_anvil_formspec,
"label[2.5,-0.5;"..S("Owner: %s"):format(meta:get_string('owner') or "").."]");
end,
can_dig = function(pos,player)
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory();
local owner = meta:get_string('owner');
if( not( inv:is_empty("input"))
-- or not( inv:is_empty("material"))
-- or not( inv:is_empty("sample"))
or not( inv:is_empty("hammer"))
or not( player )
or ( owner and owner ~= '' and player:get_player_name() ~= owner )) then
return false;
end
return true;
end,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
local meta = minetest.get_meta(pos)
if( player and player:get_player_name() ~= meta:get_string('owner' ) and from_list~="input") then
return 0
end
return count;
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
if( player and player:get_player_name() ~= meta:get_string('owner' ) and listname~="input") then
return 0;
end
if( listname=='hammer' and stack and stack:get_name() ~= 'cottages:hammer') then
return 0;
end
if( listname=='input'
and( stack:get_wear() == 0
or stack:get_name() == "technic:water_can"
or stack:get_name() == "technic:lava_can" )) then
minetest.chat_send_player( player:get_player_name(),
S('The workpiece slot is for damaged tools only.'));
return 0;
end
return stack:get_count()
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
if( player and player:get_player_name() ~= meta:get_string('owner' ) and listname~="input") then
return 0
end
return stack:get_count()
end,
on_punch = function(pos, node, puncher)
if( not( pos ) or not( node ) or not( puncher )) then
return;
end
-- only punching with the hammer is supposed to work
local wielded = puncher:get_wielded_item();
if( not( wielded ) or not( wielded:get_name() ) or wielded:get_name() ~= 'cottages:hammer') then
return;
end
local name = puncher:get_player_name();
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory();
local input = inv:get_stack('input',1);
-- only tools can be repaired
if( not( input )
or input:is_empty()
or input:get_name() == "technic:water_can"
or input:get_name() == "technic:lava_can" ) then
meta:set_string("formspec",
cottages_anvil_formspec,
"label[2.5,-0.5;"..S("Owner: %s"):format(meta:get_string('owner') or "").."]");
return;
end
-- 65535 is max damage
local damage_state = 40-math.floor(input:get_wear()/1638);
local tool_name = input:get_name();
local hud_image = "";
if( tool_name
and minetest.registered_items[ tool_name ] ) then
if( minetest.registered_items[ tool_name ].inventory_image ) then
hud_image = minetest.registered_items[ tool_name ].inventory_image;
elseif( minetest.registered_items[ tool_name ].textures
and type(minetest.registered_items[ tool_name ].textures)=='table') then
hud_image = minetest.registered_items[ tool_name ].textures[1];
elseif( minetest.registered_items[ tool_name ].textures
and type(minetest.registered_items[ tool_name ].textures)=='string') then
hud_image = minetest.registered_items[ tool_name ].textures;
end
end
local hud1 = puncher:hud_add({
hud_elem_type = "image",
scale = {x = 15, y = 15},
text = hud_image,
position = {x = 0.5, y = 0.5},
alignment = {x = 0, y = 0}
});
local hud2 = nil;
local hud3 = nil;
if( input:get_wear()>0 ) then
hud2 = puncher:hud_add({
hud_elem_type = "statbar",
text = "default_cloud.png^[colorize:#ff0000:256",
number = 40,
direction = 0, -- left to right
position = {x=0.5, y=0.65},
alignment = {x = 0, y = 0},
offset = {x = -320, y = 0},
size = {x=32, y=32},
})
hud3 = puncher:hud_add({
hud_elem_type = "statbar",
text = "default_cloud.png^[colorize:#00ff00:256",
number = damage_state,
direction = 0, -- left to right
position = {x=0.5, y=0.65},
alignment = {x = 0, y = 0},
offset = {x = -320, y = 0},
size = {x=32, y=32},
});
end
minetest.after(2, function()
if( puncher ) then
puncher:hud_remove(hud1);
puncher:hud_remove(hud2);
puncher:hud_remove(hud3);
end
end)
-- tell the player when the job is done
if( input:get_wear() == 0 ) then
-- minetest.chat_send_player( puncher:get_player_name(),
-- S('Your tool has been repaired successfully.'));
return;
end
-- do the actual repair
input:add_wear( -5000 ); -- equals to what technic toolshop does in 5 seconds
inv:set_stack("input", 1, input)
-- damage the hammer slightly
wielded:add_wear( 100 );
puncher:set_wielded_item( wielded );
-- do not spam too much
-- if( math.random( 1,5 )==1 ) then
-- minetest.chat_send_player( puncher:get_player_name(),
-- S('Your workpiece improves.'));
-- end
end,
is_ground_content = false,
})
---------------------------------------------------------------------------------------
-- crafting receipes
---------------------------------------------------------------------------------------
minetest.register_craft({
output = "cottages:anvil",
recipe = {
{cottages.craftitem_steel,cottages.craftitem_steel,cottages.craftitem_steel},
{'', cottages.craftitem_steel,'' },
{cottages.craftitem_steel,cottages.craftitem_steel,cottages.craftitem_steel} },
})
-- the castle-mod has an anvil as well - with the same receipe. convert the two into each other
if ( minetest.get_modpath("castle") ~= nil ) then
minetest.register_craft({
output = "cottages:anvil",
recipe = {
{'castle:anvil'},
},
})
minetest.register_craft({
output = "castle:anvil",
recipe = {
{'cottages:anvil'},
},
})
end
minetest.register_craft({
output = "cottages:hammer",
recipe = {
{cottages.craftitem_steel},
{'cottages:anvil'},
{cottages.craftitem_stick} }
})

View File

@ -1,585 +0,0 @@
---------------------------------------------------------------------------------------
-- straw - a very basic material
---------------------------------------------------------------------------------------
-- * straw mat - for animals and very poor NPC; also basis for other straw things
-- * straw bale - well, just a good source for building and decoration
local S = cottages.S
-- an even simpler from of bed - usually for animals
-- it is a nodebox and not wallmounted because that makes it easier to replace beds with straw mats
minetest.register_node("cottages:straw_mat", {
description = S("layer of straw"),
drawtype = 'nodebox',
tiles = { cottages.straw_texture }, -- done by VanessaE
wield_image = cottages.straw_texture,
inventory_image = cottages.straw_texture,
sunlight_propagates = true,
paramtype = 'light',
paramtype2 = "facedir",
walkable = false,
groups = { hay = 3, snappy = 2, oddly_breakable_by_hand = 2, flammable=3 },
sounds = cottages.sounds.leaves,
node_box = {
type = "fixed",
fixed = {
{-0.48, -0.5,-0.48, 0.48, -0.45, 0.48},
}
},
selection_box = {
type = "fixed",
fixed = {
{-0.48, -0.5,-0.48, 0.48, -0.25, 0.48},
}
},
is_ground_content = false,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
return cottages.sleep_in_bed( pos, node, clicker, itemstack, pointed_thing );
end
})
-- straw bales are a must for farming environments; if you for some reason do not have the darkage mod installed, this here gets you a straw bale
minetest.register_node("cottages:straw_bale", {
drawtype = "nodebox",
description = S("straw bale"),
tiles = {"cottages_darkage_straw_bale.png"},
paramtype = "light",
groups = { hay = 3, snappy = 2, oddly_breakable_by_hand = 2, flammable=3 },
sounds = cottages.sounds.leaves,
-- the bale is slightly smaller than a full node
node_box = {
type = "fixed",
fixed = {
{-0.45, -0.5,-0.45, 0.45, 0.45, 0.45},
}
},
selection_box = {
type = "fixed",
fixed = {
{-0.45, -0.5,-0.45, 0.45, 0.45, 0.45},
}
},
is_ground_content = false,
})
-- just straw
if( not(minetest.registered_nodes["farming:straw"])) then
minetest.register_node("cottages:straw", {
drawtype = "normal",
description = S("straw"),
tiles = {cottages.straw_texture},
groups = { hay = 3, snappy = 2, oddly_breakable_by_hand = 2, flammable=3 },
sounds = cottages.sounds.leaves,
-- the bale is slightly smaller than a full node
is_ground_content = false,
})
else
minetest.register_alias("cottages:straw", "farming:straw")
end
local cottages_formspec_treshing_floor =
"size[8,8]"..
"image[1.5,0;1,1;"..cottages.texture_stick.."]"..
"image[0,1;1,1;farming_wheat.png]"..
"button_exit[6.8,0.0;1.5,0.5;public;"..S("Public?").."]"..
"list[current_name;harvest;1,1;2,1;]"..
"list[current_name;straw;5,0;2,2;]"..
"list[current_name;seeds;5,2;2,2;]"..
"label[1,0.5;"..S("Harvested wheat:").."]"..
"label[4,0.0;"..S("Straw:").."]"..
"label[4,2.0;"..S("Seeds:").."]"..
"label[0,-0.5;"..S("Threshing floor").."]"..
"label[0,2.5;"..S("Punch threshing floor with a stick").."]"..
"label[0,3.0;"..S("to get straw and seeds from wheat.").."]"..
"list[current_player;main;0,4;8,4;]";
minetest.register_node("cottages:threshing_floor", {
drawtype = "nodebox",
description = S("threshing floor"),
-- TODO: stone also looks pretty well for this
tiles = {"cottages_junglewood.png^farming_wheat.png","cottages_junglewood.png","cottages_junglewood.png^"..cottages.texture_stick},
paramtype = "light",
paramtype2 = "facedir",
-- can be digged with axe and pick
groups = {cracky=2, choppy=2},
is_ground_content = false,
node_box = {
type = "fixed",
fixed = {
{-0.50, -0.5,-0.50, 0.50, -0.40, 0.50},
{-0.50, -0.4,-0.50,-0.45, -0.20, 0.50},
{ 0.45, -0.4,-0.50, 0.50, -0.20, 0.50},
{-0.45, -0.4,-0.50, 0.45, -0.20,-0.45},
{-0.45, -0.4, 0.45, 0.45, -0.20, 0.50},
}
},
selection_box = {
type = "fixed",
fixed = {
{-0.50, -0.5,-0.50, 0.50, -0.20, 0.50},
}
},
on_construct = function(pos)
local meta = minetest.get_meta(pos);
meta:set_string("infotext", S("Public threshing floor"));
local inv = meta:get_inventory();
inv:set_size("harvest", 2);
inv:set_size("straw", 4);
inv:set_size("seeds", 4);
meta:set_string("formspec", cottages_formspec_treshing_floor );
meta:set_string("public", "public")
end,
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos);
meta:set_string("owner", placer:get_player_name() or "");
meta:set_string("infotext", S("Private threshing floor (owned by %s)"):format(meta:get_string("owner") or ""));
meta:set_string("formspec",
cottages_formspec_treshing_floor..
"label[2.5,-0.5;"..S("Owner: %s"):format(meta:get_string("owner") or "").."]" );
meta:set_string("public", "private")
end,
on_receive_fields = function(pos, formname, fields, sender)
cottages.switch_public(pos, formname, fields, sender, 'threshing floor')
end,
can_dig = function(pos,player)
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory();
local owner = meta:get_string('owner');
if( not( inv:is_empty("harvest"))
or not( inv:is_empty("straw"))
or not( inv:is_empty("seeds"))
or not( player )
or ( owner and owner ~= '' and player:get_player_name() ~= owner )) then
return false;
end
return true;
end,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
local meta = minetest.get_meta(pos)
if( not( cottages.player_can_use( meta, player ))) then
return 0
end
return count;
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
-- only accept input the threshing floor can use/process
if( listname=='straw'
or listname=='seeds'
or (listname=='harvest' and stack and stack:get_name() ~= 'farming:wheat' )) then
return 0;
end
if( not( cottages.player_can_use( meta, player ))) then
return 0
end
return stack:get_count()
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
if( not( cottages.player_can_use( meta, player ))) then
return 0
end
return stack:get_count()
end,
on_punch = function(pos, node, puncher)
if( not( pos ) or not( node ) or not( puncher )) then
return;
end
-- only punching with a normal stick is supposed to work
local wielded = puncher:get_wielded_item();
if( not( wielded )
or not( wielded:get_name() )
or not( minetest.registered_items[ wielded:get_name() ])
or not( minetest.registered_items[ wielded:get_name() ].groups )
or not( minetest.registered_items[ wielded:get_name() ].groups.stick )) then
return;
end
local name = puncher:get_player_name();
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory();
local input = inv:get_list('harvest');
-- we have two input slots
local stack1 = inv:get_stack( 'harvest', 1);
local stack2 = inv:get_stack( 'harvest', 2);
if( ( stack1:is_empty() and stack2:is_empty())
or( not( stack1:is_empty()) and stack1:get_name() ~= 'farming:wheat')
or( not( stack2:is_empty()) and stack2:get_name() ~= 'farming:wheat')) then
-- minetest.chat_send_player( name, 'One of the input slots contains something else than wheat, or there is no wheat at all.');
-- update the formspec
meta:set_string("formspec",
cottages_formspec_treshing_floor..
"label[2.5,-0.5;"..S("Owner: %s"):format(meta:get_string("owner") or "").."]" );
return;
end
-- on average, process 25 wheat at each punch (10..40 are possible)
local anz_wheat = 10 + math.random( 0, 30 );
-- we already made sure there is only wheat inside
local found_wheat = stack1:get_count() + stack2:get_count();
-- do not process more wheat than present in the input slots
if( found_wheat < anz_wheat ) then
anz_wheat = found_wheat;
end
local overlay1 = "^farming_wheat.png";
local overlay2 = "^"..cottages.straw_texture;
local overlay3 = "^"..cottages.texture_wheat_seed;
-- this can be enlarged by a multiplicator if desired
local anz_straw = anz_wheat;
local anz_seeds = anz_wheat;
if( inv:room_for_item('straw','cottages:straw_mat '..tostring( anz_straw ))
and inv:room_for_item('seeds',cottages.craftitem_seed_wheat..' '..tostring( anz_seeds ))) then
-- the player gets two kind of output
inv:add_item("straw",'cottages:straw_mat '..tostring( anz_straw ));
inv:add_item("seeds",cottages.craftitem_seed_wheat..' '..tostring( anz_seeds ));
-- consume the wheat
inv:remove_item("harvest", 'farming:wheat '..tostring( anz_wheat ));
local anz_left = found_wheat - anz_wheat;
if( anz_left > 0 ) then
-- minetest.chat_send_player( name, S('You have threshed %s wheat (%s are left).'):format(anz_wheat,anz_left));
else
-- minetest.chat_send_player( name, S('You have threshed the last %s wheat.'):format(anz_wheat));
overlay1 = "";
end
end
local hud0 = puncher:hud_add({
hud_elem_type = "image",
scale = {x = 38, y = 38},
text = "cottages_junglewood.png^[colorize:#888888:128",
position = {x = 0.5, y = 0.5},
alignment = {x = 0, y = 0}
});
local hud1 = puncher:hud_add({
hud_elem_type = "image",
scale = {x = 15, y = 15},
text = "cottages_junglewood.png"..overlay1,
position = {x = 0.4, y = 0.5},
alignment = {x = 0, y = 0}
});
local hud2 = puncher:hud_add({
hud_elem_type = "image",
scale = {x = 15, y = 15},
text = "cottages_junglewood.png"..overlay2,
position = {x = 0.6, y = 0.35},
alignment = {x = 0, y = 0}
});
local hud3 = puncher:hud_add({
hud_elem_type = "image",
scale = {x = 15, y = 15},
text = "cottages_junglewood.png"..overlay3,
position = {x = 0.6, y = 0.65},
alignment = {x = 0, y = 0}
});
local hud4 = puncher:hud_add({
hud_elem_type = "text",
text = tostring( found_wheat-anz_wheat ),
number = 0x00CC00,
alignment = {x = 0, y = 0},
scale = {x = 100, y = 100}, -- bounding rectangle of the text
position = {x = 0.4, y = 0.5},
});
if( not( anz_straw )) then
anz_straw = "0";
end
if( not( anz_seed )) then
anz_seed = "0";
end
local hud5 = puncher:hud_add({
hud_elem_type = "text",
text = '+ '..tostring( anz_straw )..' straw',
number = 0x00CC00,
alignment = {x = 0, y = 0},
scale = {x = 100, y = 100}, -- bounding rectangle of the text
position = {x = 0.6, y = 0.35},
});
local hud6 = puncher:hud_add({
hud_elem_type = "text",
text = '+ '..tostring( anz_seed )..' seeds',
number = 0x00CC00,
alignment = {x = 0, y = 0},
scale = {x = 100, y = 100}, -- bounding rectangle of the text
position = {x = 0.6, y = 0.65},
});
minetest.after(2, function()
if( puncher ) then
puncher:hud_remove(hud1);
puncher:hud_remove(hud2);
puncher:hud_remove(hud3);
puncher:hud_remove(hud4);
puncher:hud_remove(hud5);
puncher:hud_remove(hud6);
puncher:hud_remove(hud0);
end
end)
end,
})
local cottages_handmill_formspec = "size[8,8]"..
"image[0,1;1,1;"..cottages.texture_wheat_seed.."]"..
"button_exit[6.0,0.0;1.5,0.5;public;"..S("Public?").."]"..
"list[current_name;seeds;1,1;1,1;]"..
"list[current_name;flour;5,1;2,2;]"..
"label[0,0.5;"..S("Wheat seeds:").."]"..
"label[4,0.5;"..S("Flour:").."]"..
"label[0,-0.3;"..S("Mill").."]"..
"label[0,2.5;"..S("Punch this hand-driven mill").."]"..
"label[0,3.0;"..S("to convert wheat seeds into flour.").."]"..
"list[current_player;main;0,4;8,4;]";
minetest.register_node("cottages:handmill", {
description = S("mill, powered by punching"),
drawtype = "mesh",
mesh = "cottages_handmill.obj",
tiles = {"cottages_stone.png"},
paramtype = "light",
paramtype2 = "facedir",
groups = {cracky=2},
is_ground_content = false,
selection_box = {
type = "fixed",
fixed = {
{-0.50, -0.5,-0.50, 0.50, 0.25, 0.50},
}
},
collision_box = {
type = "fixed",
fixed = {
{-0.50, -0.5,-0.50, 0.50, 0.25, 0.50},
}
},
on_construct = function(pos)
local meta = minetest.get_meta(pos);
meta:set_string("infotext", S("Public mill, powered by punching"));
local inv = meta:get_inventory();
inv:set_size("seeds", 1);
inv:set_size("flour", 4);
meta:set_string("formspec", cottages_handmill_formspec );
meta:set_string("public", "public")
end,
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos);
meta:set_string("owner", placer:get_player_name() or "");
meta:set_string("infotext", S("Private mill, powered by punching (owned by %s)"):format(meta:get_string("owner") or ""));
meta:set_string("formspec",
cottages_handmill_formspec..
"label[2.5,-0.5;"..S("Owner: %s"):format(meta:get_string('owner') or "").."]" );
meta:set_string("public", "private")
end,
on_receive_fields = function(pos, formname, fields, sender)
cottages.switch_public(pos, formname, fields, sender, 'mill, powered by punching')
end,
can_dig = function(pos,player)
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory();
local owner = meta:get_string('owner');
if( not( inv:is_empty("flour"))
or not( inv:is_empty("seeds"))
or not( player )
or ( owner and owner ~= '' and player:get_player_name() ~= owner )) then
return false;
end
return true;
end,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
local meta = minetest.get_meta(pos)
if( not( cottages.player_can_use( meta, player ))) then
return 0
end
return count;
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
-- only accept input the threshing floor can use/process
if( listname=='flour'
or (listname=='seeds' and stack and not( cottages.handmill_product[ stack:get_name()] ))) then
return 0;
end
if( not( cottages.player_can_use( meta, player ))) then
return 0
end
return stack:get_count()
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
if( not( cottages.player_can_use( meta, player ))) then
return 0
end
return stack:get_count()
end,
-- this code is very similar to the threshing floor; except that it has only one input- and output-slot
-- and does not require the usage of a stick
on_punch = function(pos, node, puncher)
if( not( pos ) or not( node ) or not( puncher )) then
return;
end
local name = puncher:get_player_name();
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory();
local input = inv:get_list('seeds');
local stack1 = inv:get_stack( 'seeds', 1);
if( ( stack1:is_empty())
or( not( stack1:is_empty())
and not( cottages.handmill_product[ stack1:get_name() ] ))) then
if not( stack1:is_empty() ) then
minetest.chat_send_player(name,"Nothing happens...")
end
-- update the formspec
meta:set_string("formspec",
cottages_handmill_formspec..
"label[2.5,-0.5;"..S("Owner: %s"):format(meta:get_string('owner') or "").."]" );
return;
end
-- turning the mill is a slow process; 1-21 flour are generated per turn
local anz = 1 + math.random( cottages.handmill_min_per_turn, cottages.handmill_max_per_turn );
-- we already made sure there is only wheat inside
local found = stack1:get_count();
-- do not process more wheat than present in the input slots
if( found < anz ) then
anz = found;
end
local product_stack = ItemStack( cottages.handmill_product[ stack1:get_name() ]);
local anz_result = anz;
-- items that produce more
if( product_stack:get_count()> 1 ) then
anz_result = anz * product_stack:get_count();
end
if( inv:room_for_item('flour', product_stack:get_name()..' '..tostring( anz_result ))) then
inv:add_item( 'flour', product_stack:get_name()..' '..tostring( anz_result ));
inv:remove_item( 'seeds', stack1:get_name()..' '..tostring( anz ));
local anz_left = found - anz;
if( anz_left > 0 ) then
minetest.chat_send_player( name, S('You have ground a %s (%s are left).'):format(stack1:get_definition().description,(anz_left)));
else
minetest.chat_send_player( name, S('You have ground the last %s.'):format(stack1:get_definition().description));
end
-- if the version of MT is recent enough, rotate the mill a bit
if( minetest.swap_node ) then
node.param2 = node.param2 + 1;
if( node.param2 > 3 ) then
node.param2 = 0;
end
minetest.swap_node( pos, node );
end
end
end,
})
---------------------------------------------------------------------------------------
-- crafting receipes
---------------------------------------------------------------------------------------
-- this returns corn as well
-- the replacements work only if the replaced slot gets empty...
minetest.register_craft({
output = "cottages:straw_mat 6",
recipe = {
{cottages.craftitem_stone,'',''},
{"farming:wheat", "farming:wheat", "farming:wheat", },
},
replacements = {{ cottages.craftitem_stone, cottages.craftitem_seed_wheat.." 3" }},
})
-- this is a better way to get straw mats
minetest.register_craft({
output = "cottages:threshing_floor",
recipe = {
{cottages.craftitem_junglewood, cottages.craftitem_chest_locked, cottages.craftitem_junglewood, },
{cottages.craftitem_junglewood, cottages.craftitem_stone, cottages.craftitem_junglewood, },
},
})
-- and a way to turn wheat seeds into flour
minetest.register_craft({
output = "cottages:handmill",
recipe = {
{cottages.craftitem_stick, cottages.craftitem_stone, "", },
{"", cottages.craftitem_steel, "", },
{"", cottages.craftitem_stone, "", },
},
})
minetest.register_craft({
output = "cottages:straw_bale",
recipe = {
{"cottages:straw_mat"},
{"cottages:straw_mat"},
{"cottages:straw_mat"},
},
})
minetest.register_craft({
output = "cottages:straw",
recipe = {
{"cottages:straw_bale"},
},
})
minetest.register_craft({
output = "cottages:straw_bale",
recipe = {
{"cottages:straw"},
},
})
minetest.register_craft({
output = "cottages:straw_mat 3",
recipe = {
{"cottages:straw_bale"},
},
})

View File

@ -1,128 +0,0 @@
Digilines Stuff
===============
License:
Code - LGPL v3 or later (contains some code from mesecons and digilines)
Textures WITHOUT "adwaita" in the file name - CC BY-SA 3.0 Generic (contains modified versions of textures from mesecons and digilines)
Textures WITH "adwaita" in the file name - These are icons by the GNOME Project, licensed under GNU LGPL v3 or CC BY-SA 3.0.
Depends:
Required: digilines (base only) and mesecons (base only)
Optional: mesecons_noteblock (for digilines noteblock), mesecons_mvps (for digilines piston and movestone), mesecons_luacontroller (for I/O expander)
Only needed for craft recipes: default, basic_materials
How to use digilines buttons:
Connect to a digiline (or digimese), right-click, and set a channel and message.
When the button is pressed (right-click), it will send that message on that channel, over digilines.
If the "Protected" checkbox is checked, only players allowed to interact in the area can push the button.
If the "Manual Light Control" checkbox is checked, the light will not illuminate automatically when the button is pushed - use the "light_on" and "light_off" commands to control it.
Note that the settings cannot be changed after setting - you must dig and re-place the button to do so.
How to use the wall knob:
Connect to a digiline, right-click, and set the channel and the minimum and maximum values.
Left-click to decrease the current setting or right-click to increase it. If the "protected" checkbox was checked, then only players allowed to interact in the area can do this.
Each time the setting is changed, the new setting is sent on the selected channel.
Note that the settings cannot be changed after setting - you must dig and re-place the knob to do so.
How to use digimese:
It conducts digilines signals (like digilines) in all directions (like mese). That's about it, really.
How to use vertical/insulated digilines:
These work exactly like the mesecons equivalents, that is:
Vertical digilines will automatically connect to other vertical digilines directly above or below them, and form "plates" on each end of the stack. Signals can only be conducted into or out of the stack at these "plates".
Insulated digilines conduct like regular digilines, but only into/out of the ends of the "wire" or at locations where an intermediate connection has been placed.
How to use the digilines player detector:
Set a channel and radius (radius must be a number >0 and <10 - anything invalid will be ignored and "6" used instead).
Every second while a player is within the radius, a table listing the players in range will be sent via digilines on the chosen channel.
How to use the digilines control panel:
Once a channel is set, any messages sent on that channel will be shown on the "LCD". The buttons, when pressed, send the messages "up", "down", "left", "right", "back", and "enter" on the same channel. If the panel is placed in a protected area (all standard protection mods are supported), only the owner of the area (and players with the protection_bypass privilege) can set the channel. There is also a "lock" function in the bottom-right of the "LCD" area. Click the padlock icon to lock/unlock it. If locked, only the owner of the area is allowed to use the buttons. If unlocked, anyone can use the buttons, although channel setting and (for reasons that shuld be obvious) locking/unlocking is still limited to the area owner and players with protection_bypass.
How to use the NIC:
Send a digilines signal with the URL you want to download. The HTTPRequestResult table will be sent back on the same channel.
How to use the camera:
Set the channel, distance, and radius. The camera will search for a node "distance" meters away and up to 10m down.
Every second while a player is within "radius" meters of that point, a table listing the players in range will be sent via digilines on the chosen channel.
How to use the dimmable lights:
After setting the channel, send a number from 0 to 14 to set the light level.
How to use the timer:
Send a number representing a time in seconds, from 0.5 to 3600. When the time expires, the timer will send "done" back on the same channel. If the loop feature is enabled (use the commands "loop_on" and "loop_off" to set this) the timer will automatically be set for the same time again each time it expires.
How to use the junction box:
These are just plain digilines conductors (like digimese) but can skip over one node to another junction box or certain other nodes.
As in, [digiline][junction box][dirt][junction box][digiline] will work to transmit signals "through" the dirt.
How to use the I/O expander:
After setting a channel, send a table (same format as a Luacontroller's "port" table) to set the output states.
A table in this same format will be sent back whenever an input changes or you manually poll it by sending a "GET" message.
How to use the card reader:
After setting a channel, swiping a card (punch the reader with the card to swipe) will send a message in the following format:
{event = "read",data = "The data that was on the card"}
To write a card, send a command in the following format:
{command = "write",data = "The data to put on the card",description = "A description of what the card is for"}
After sending the write command, swipe the card to be written and the reader will send back the following message:
{event = "write"}
Both blank and previously written cards can be written to. If the card was not blank, it will be overwritten.
How to use the game controller:
After setting a channel, right-click the controller to start/stop using it.
While using a controller, it will send a table with the control inputs, pitch, yaw, look vector, and name of the player using the controller each time one of these values changes, up to 5 times per second.
When a player leaves a controller, the string "player_left" is sent.
In addition to right-clicking the controller in use to stop using it, the following will also cause a player to stop using the controller:
* The controller is moved or removed
* The player leaves the game
* The player is teleported away from the controller
* The controller receives the string "release" on its digilines channel
How to use the RAM and EEPROM chips:
First, set a channel.
Messages should consist of a table, with "command" set to either "read" or "write". "address" should be set to the number (0-31) of the 512-character block to read or write, and if writing then "data" should contain the data to write.
Example (to write - reading is similar, but with no data):
{command = "write",address = 7,data = "9a91a9e451b94dc262972557ab0d406f"}
The RAM and EEPROM chips behave identically, except that the RAM chip loses its contents when dug whereas the EEPROM does not.
How to use the 2D graphics processor:
Please see gpu.txt for information on this part.
How to use the digilines pistons:
The following commands are accepted as strings: "extend" (extend the piston), "retract" (retract the piston), and "retract_sticky" (retract the piston, pulling one node like a sticky piston)
You can also send a command as a table. If so, the fields that can be used in the table are as follows:
* action: "extend" or "retract"
* max: The maximum number of nodes to push/pull, cannot be set higher than 16. Set to 0 (or omit) when retracting to perform a non-sticky retraction.
* allsticky: Pull a whole stack of nodes (like movestone), not just one.
* sound: The sound to make. "mesecons" for the mesecons piston sounds, "digilines" for the digilines piston sounds (default), or "none" for no sounds at all.
How to use the digilines movestone:
Commands for this node are in the form of a table, with the field "command" set to the desired action, and other fields providing parameters.
The commands are as follows:
* "getstate": Returns a table containing the following elements: "targetpos" (table representing the target position), "pos" (table representing the current position), and "moveaxis" (the current axis being moved along, nil if not moving)
* "absmove": Moves to the absolute position specified by "x" "y" and "z". No axis can move more than 50m as a result of one command. If movements along more than one axis are needed, they are processed in alphabetical order (X,Y,Z).
* "relmove": Same as absmove, but relative to the current position (for example, y=1 moves up 1m, not *to* Y=1)
The available parameters for absmove and relmove are:
* x: Target X position (for absmove) or target change in X position (for relmove)
* y: Same, but for Y
* z: Same, but for Z
* sticky: Whether to pull nodes along behind the movestone
* allsticky: Whether to pull a full stack of nodes like normal movestone (true) or just one like a sticky piston (false)
* maxstack: The maximum number of nodes to push/pull, with the movestone itself counting as 1. Cannot be set higher than 50.
* sound: "mesecons" to have the mesecons movestone sound play or "none" for no sound at all
If any of x/y/z are omitted, then they default to the current position (for absmove) or 0 (for relmove).
If any of maxstack/sticky/allsticky/sound are omitted, they default to the values last used.

View File

@ -1,238 +0,0 @@
local digiline_rules = {
{x = 1,y = 0,z = 0},
{x = -1,y = 0,z = 0},
{x = 0,y = 0,z = 1},
{x = 0,y = 0,z = -1},
{x = 0,y = -1,z = 0},
{x = 1,y = -1,z = 0},
{x = -1,y = -1,z = 0},
{x = 0,y = -1,z = 1},
{x = 0,y = -1,z = -1},
}
local players_on_controller = {}
local last_seen_inputs = {}
local function process_inputs(pos)
local meta = minetest.get_meta(pos)
local hash = minetest.hash_node_position(pos)
if minetest.get_node(pos).name ~= "digistuff:controller_programmed" then
local player = minetest.get_player_by_name(players_on_controller[hash])
if player then
player:set_physics_override({speed = 1,jump = 1,})
player:set_pos(vector.add(pos,vector.new(0.25,0,0.25)))
minetest.chat_send_player(players_on_controller[hash],"You are now free to move.")
end
last_seen_inputs[players_on_controller[hash]] = nil
players_on_controller[hash] = nil
return
end
local name = players_on_controller[hash]
local player = minetest.get_player_by_name(name)
if not player then
digiline:receptor_send(pos,digiline_rules,meta:get_string("channel"),"player_left")
minetest.get_meta(pos):set_string("infotext","Digilines Game Controller Ready\n(right-click to use)")
players_on_controller[hash] = nil
return
end
local distance = vector.distance(pos,player:get_pos())
if distance > 1 then
digiline:receptor_send(pos,digiline_rules,meta:get_string("channel"),"player_left")
minetest.get_meta(pos):set_string("infotext","Digilines Game Controller Ready\n(right-click to use)")
player:set_physics_override({speed = 1,jump = 1,})
players_on_controller[hash] = nil
return
end
local inputs = player:get_player_control()
inputs.pitch = player:get_look_vertical()
inputs.yaw = player:get_look_horizontal()
local send_needed = false
if not last_seen_inputs[name] then
send_needed = true
else
for k,v in pairs(inputs) do
if v ~= last_seen_inputs[name][k] then
send_needed = true
break
end
end
end
last_seen_inputs[name] = inputs
if send_needed then
local channel = meta:get_string("channel")
local inputs = table.copy(inputs)
inputs.look_vector = player:get_look_dir()
inputs.name = name
digiline:receptor_send(pos,digiline_rules,channel,inputs)
end
end
local function release_player(pos)
local hash = minetest.hash_node_position(pos)
local player = minetest.get_player_by_name(players_on_controller[hash])
if player then
player:set_physics_override({speed = 1,jump = 1,})
player:set_pos(vector.add(pos,vector.new(0.25,0,0.25)))
minetest.chat_send_player(players_on_controller[hash],"You are now free to move.")
end
local meta = minetest.get_meta(pos)
meta:set_string("infotext","Digilines Game Controller Ready\n(right-click to use)")
last_seen_inputs[players_on_controller[hash]] = nil
players_on_controller[hash] = nil
digiline:receptor_send(pos,digiline_rules,meta:get_string("channel"),"player_left")
end
local function trap_player(pos,player)
local hash = minetest.hash_node_position(pos)
local oldname = players_on_controller[hash]
local newname = player:get_player_name()
if oldname and minetest.get_player_by_name(oldname) then
minetest.chat_send_player(player:get_player_name(),"Controller is already occupied by "..oldname)
return
else
players_on_controller[hash] = newname
player:set_pos(vector.add(pos,vector.new(0,-0.4,0)))
player:set_physics_override({speed = 0,jump = 0,})
minetest.chat_send_player(newname,"You are now using a digilines game controller. Right-click the controller again to be released.")
local meta = minetest.get_meta(pos)
meta:set_string("infotext","Digilines Game Controller\nIn use by: "..newname)
process_inputs(pos)
end
end
local function toggle_trap_player(pos,player)
if players_on_controller[minetest.hash_node_position(pos)] then
release_player(pos)
else
trap_player(pos,player)
end
end
minetest.register_node("digistuff:controller", {
description = "Digilines Game Controller",
tiles = {
"digistuff_controller_top.png",
"digistuff_controller_sides.png",
},
paramtype = "light",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.5,-0.5,-0.5,0.5,-0.45,0.5},
}
},
_digistuff_channelcopier_fieldname = "channel",
_digistuff_channelcopier_onset = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec","")
meta:set_string("infotext","Digilines Game Controller Ready\n(right-click to use)")
minetest.swap_node(pos,{name = "digistuff:controller_programmed",})
end,
groups = {cracky = 1,},
is_ground_content = false,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec","field[channel;Channel;${channel}")
end,
on_receive_fields = function(pos, formname, fields, sender)
local name = sender:get_player_name()
if minetest.is_protected(pos,name) and not minetest.check_player_privs(name,{protection_bypass=true}) then
minetest.record_protection_violation(pos,name)
return
end
local meta = minetest.get_meta(pos)
if fields.channel then
meta:set_string("channel",fields.channel)
meta:set_string("formspec","")
meta:set_string("infotext","Digilines Game Controller Ready\n(right-click to use)")
minetest.swap_node(pos,{name = "digistuff:controller_programmed",})
end
end,
digiline = {
receptor = {},
wire = {
rules = digiline_rules,
},
},
})
minetest.register_node("digistuff:controller_programmed", {
description = "Digilines Game Controller (programmed state - you hacker you!)",
drop = "digistuff:controller",
tiles = {
"digistuff_controller_top.png",
"digistuff_controller_sides.png",
},
paramtype = "light",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.5,-0.5,-0.5,0.5,-0.45,0.5},
}
},
_digistuff_channelcopier_fieldname = "channel",
groups = {cracky = 1,not_in_creative_inventory = 1,},
is_ground_content = false,
on_rightclick = function(pos,_,clicker)
if clicker and clicker:get_player_name() then
toggle_trap_player(pos,clicker)
end
end,
_digistuff_channelcopier_fieldname = "channel",
digiline = {
receptor = {},
wire = {
rules = digiline_rules,
},
effector = {
action = function(pos,node,channel,msg)
local setchannel = minetest.get_meta(pos):get_string("channel")
if channel ~= setchannel then return end
if msg == "release" then
local hash = minetest.hash_node_position(pos)
if players_on_controller[hash] then
release_player(pos)
end
end
end,
},
},
})
local acc_dtime = 0
minetest.register_globalstep(function(dtime)
acc_dtime = acc_dtime + dtime
if acc_dtime < 0.2 then return end
acc_dtime = 0
for hash in pairs(players_on_controller) do
local pos = minetest.get_position_from_hash(hash)
process_inputs(pos)
end
end)
minetest.register_lbm({
name = "digistuff:reset_controllers",
label = "Reset game controllers to idle",
nodenames = {"digistuff:controller_programmed"},
run_at_every_load = true,
action = function(pos)
if not players_on_controller[minetest.hash_node_position(pos)] then
local meta = minetest.get_meta(pos)
digiline:receptor_send(pos,digiline_rules,meta:get_string("channel"),"player_left")
meta:set_string("infotext","Digilines Game Controller Ready\n(right-click to use)")
end
end,
})
minetest.register_craft({
output = "digistuff:controller",
recipe = {
{"","digistuff:button","",},
{"digistuff:button","group:wool","digistuff:button",},
{"","digistuff:button","",},
},
})

View File

@ -1,6 +0,0 @@
default?
digilines
mesecons?
mesecons_mvps?
screwdriver?
pipeworks?

View File

@ -1,431 +0,0 @@
local font = dofile(minetest.get_modpath("digistuff")..DIR_DELIM.."gpu-font.lua")
local function explodebits(input)
local output = {}
for i=0,7,1 do
output[i] = input%(2^(i+1)) >= 2^i
end
return output
end
local function implodebits(input)
local output = 0
for i=0,7,1 do
output = output + (input[i] and 2^i or 0)
end
return output
end
local function rgbtohsv(r,g,b)
r = r/255
g = g/255
b = b/255
max = math.max(r,g,b)
min = math.min(r,g,b)
delta = max-min
local hue = 0
if delta > 0 then
if max == r then
hue = (g-b)/delta
hue = (hue%6)*60
elseif max == g then
hue = (b-r)/delta
hue = 60*(hue+2)
elseif max == b then
hue = (r-g)/delta
hue = 60*(hue+4)
end
hue = hue/360
end
local sat = 0
if max > 0 then
sat = delta/max
end
return math.floor(hue*255),math.floor(sat*255),math.floor(max*255)
end
local function hsvtorgb(h,s,v)
h = h/255*360
s = s/255
v = v/255
local c = s*v
local x = (h/60)%2
x = 1-math.abs(x-1)
x = x*c
local m = v-c
local r = 0
local g = 0
local b = 0
if h < 60 then
r = c
g = x
elseif h < 120 then
r = x
g = c
elseif h < 180 then
g = c
b = x
elseif h < 240 then
g = x
b = c
elseif h < 300 then
r = x
b = c
else
r = c
b = x
end
r = r+m
g = g+m
b = b+m
return math.floor(r*255),math.floor(g*255),math.floor(b*255)
end
local function bitwiseblend(srcr,dstr,srcg,dstg,srcb,dstb,mode)
local srbits = explodebits(srcr)
local sgbits = explodebits(srcg)
local sbbits = explodebits(srcb)
local drbits = explodebits(dstr)
local dgbits = explodebits(dstg)
local dbbits = explodebits(dstb)
for i=0,7,1 do
if mode == "and" then
drbits[i] = srbits[i] and drbits[i]
dgbits[i] = sgbits[i] and dgbits[i]
dbbits[i] = sbbits[i] and dbbits[i]
elseif mode == "or" then
drbits[i] = srbits[i] or drbits[i]
dgbits[i] = sgbits[i] or dgbits[i]
dbbits[i] = sbbits[i] or dbbits[i]
elseif mode == "xor" then
drbits[i] = srbits[i] ~= drbits[i]
dgbits[i] = sgbits[i] ~= dgbits[i]
dbbits[i] = sbbits[i] ~= dbbits[i]
elseif mode == "xnor" then
drbits[i] = srbits[i] == drbits[i]
dgbits[i] = sgbits[i] == dgbits[i]
dbbits[i] = sbbits[i] == dbbits[i]
elseif mode == "not" then
drbits[i] = not srbits[i]
dgbits[i] = not sgbits[i]
dbbits[i] = not sbbits[i]
elseif mode == "nand" then
drbits[i] = not (srbits[i] and drbits[i])
dgbits[i] = not (sgbits[i] and dgbits[i])
dbbits[i] = not (sbbits[i] and dbbits[i])
elseif mode == "nor" then
drbits[i] = not (srbits[i] or drbits[i])
dgbits[i] = not (sgbits[i] or dgbits[i])
dbbits[i] = not (sbbits[i] or dbbits[i])
end
end
return string.format("%02X%02X%02X",implodebits(drbits),implodebits(dgbits),implodebits(dbbits))
end
local function blend(src,dst,mode,transparent)
local srcr = tonumber(string.sub(src,1,2),16)
local srcg = tonumber(string.sub(src,3,4),16)
local srcb = tonumber(string.sub(src,5,6),16)
local dstr = tonumber(string.sub(dst,1,2),16)
local dstg = tonumber(string.sub(dst,3,4),16)
local dstb = tonumber(string.sub(dst,5,6),16)
local op = "normal"
if type(mode) == "string" then op = string.lower(mode) end
if op == "normal" then
return src
elseif op == "nop" then
return dst
elseif op == "overlay" then
return (string.upper(src) == string.upper(transparent)) and dst or src
elseif op == "add" then
local r = math.min(255,srcr+dstr)
local g = math.min(255,srcg+dstg)
local b = math.min(255,srcb+dstb)
return string.format("%02X%02X%02X",r,g,b)
elseif op == "sub" then
local r = math.max(0,dstr-srcr)
local g = math.max(0,dstg-srcg)
local b = math.max(0,dstb-srcb)
return string.format("%02X%02X%02X",r,g,b)
elseif op == "isub" then
local r = math.max(0,srcr-dstr)
local g = math.max(0,srcg-dstg)
local b = math.max(0,srcb-dstb)
return string.format("%02X%02X%02X",r,g,b)
elseif op == "average" then
local r = math.min(255,(srcr+dstr)/2)
local g = math.min(255,(srcg+dstg)/2)
local b = math.min(255,(srcb+dstb)/2)
return string.format("%02X%02X%02X",r,g,b)
elseif op == "and" or op == "or" or op == "xor" or op == "xnor" or op == "not" or op == "nand" or op == "nor" then
return bitwiseblend(srcr,dstr,srcg,dstg,srcb,dstb,op)
elseif op == "tohsv" or op == "rgbtohsv" then
return string.format("%02X%02X%02X",rgbtohsv(srcr,srcg,srcb))
elseif op == "torgb" or op == "hsvtorgb" then
return string.format("%02X%02X%02X",hsvtorgb(srcr,srcg,srcb))
else
return src
end
end
local function runcommand(pos,meta,command)
if type(command) ~= "table" then return end
if command.command == "createbuffer" then
if type(command.buffer) ~= "number" or type(command.xsize) ~= "number" or type(command.ysize) ~= "number" then return end
local bufnum = math.floor(command.buffer)
if bufnum < 0 or bufnum > 7 then return end
local xsize = math.min(64,math.floor(command.xsize))
local ysize = math.min(64,math.floor(command.ysize))
if xsize < 1 or ysize < 1 then return end
local fillcolor = command.fill
if type(fillcolor) ~= "string" or string.len(fillcolor) > 7 or string.len(fillcolor) < 6 then fillcolor = "000000" end
if string.sub(fillcolor,1,1) == "#" then fillcolor = string.sub(fillcolor,2,7) end
if not tonumber(fillcolor,16) then fillcolor = "000000" end
local buffer = {}
buffer.xsize = xsize
buffer.ysize = ysize
for y=1,ysize,1 do
buffer[y] = {}
for x=1,xsize,1 do
buffer[y][x] = fillcolor
end
end
meta:set_string("buffer"..bufnum,minetest.serialize(buffer))
elseif command.command == "send" then
if type(command.buffer) ~= "number" or type(command.channel) ~= "string" then return end
local bufnum = math.floor(command.buffer)
if bufnum < 0 or bufnum > 7 then return end
local buffer = meta:get_string("buffer"..bufnum)
if string.len(buffer) == 0 then return end
buffer = minetest.deserialize(buffer)
if type(buffer) == "table" then
digiline:receptor_send(pos,digiline.rules.default,command.channel,buffer)
end
elseif command.command == "drawrect" then
if type(command.buffer) ~= "number" or type(command.x1) ~= "number" or type(command.y1) ~= "number" or type(command.x2) ~= "number" or type(command.y2) ~= "number" then return end
local bufnum = math.floor(command.buffer)
if bufnum < 0 or bufnum > 7 then return end
local x1 = math.min(64,math.floor(command.x1))
local y1 = math.min(64,math.floor(command.y1))
local x2 = math.min(64,math.floor(command.x2))
local y2 = math.min(64,math.floor(command.y2))
if x1 < 1 or y1 < 1 or x2 < 1 or y2 < 1 then return end
local buffer = meta:get_string("buffer"..bufnum)
if string.len(buffer) == 0 then return end
buffer = minetest.deserialize(buffer)
if type(buffer) ~= "table" then return end
x2 = math.min(x2,buffer.xsize)
y2 = math.min(y2,buffer.ysize)
if x1 > x2 or y1 > y2 then return end
local fillcolor = command.fill
if type(fillcolor) ~= "string" or string.len(fillcolor) > 7 or string.len(fillcolor) < 6 then fillcolor = "000000" end
if string.sub(fillcolor,1,1) == "#" then fillcolor = string.sub(fillcolor,2,7) end
if not tonumber(fillcolor,16) then fillcolor = "000000" end
local edgecolor = command.edge
if type(edgecolor) ~= "string" or string.len(edgecolor) > 7 or string.len(edgecolor) < 6 then edgecolor = fillcolor end
if string.sub(edgecolor,1,1) == "#" then edgecolor = string.sub(edgecolor,2,7) end
if not tonumber(edgecolor,16) then edgecolor = fillcolor end
for y=y1,y2,1 do
for x=x1,x2,1 do
buffer[y][x] = fillcolor
end
end
if fillcolor ~= edgecolor then
for x=x1,x2,1 do
buffer[y1][x] = edgecolor
buffer[y2][x] = edgecolor
end
for y=y1,y2,1 do
buffer[y][x1] = edgecolor
buffer[y][x2] = edgecolor
end
end
meta:set_string("buffer"..bufnum,minetest.serialize(buffer))
elseif command.command == "drawpoint" then
if type(command.buffer) ~= "number" or type(command.x) ~= "number" or type(command.y) ~= "number" then return end
local bufnum = math.floor(command.buffer)
if bufnum < 0 or bufnum > 7 then return end
local x = math.floor(command.x)
local y = math.floor(command.y)
if x < 1 or y < 1 then return end
local buffer = meta:get_string("buffer"..bufnum)
if string.len(buffer) == 0 then return end
buffer = minetest.deserialize(buffer)
if type(buffer) ~= "table" then return end
if x > buffer.xsize or y > buffer.ysize then return end
local color = command.color
if type(color) ~= "string" or string.len(color) > 7 or string.len(color) < 6 then color = "000000" end
if string.sub(color,1,1) == "#" then color = string.sub(color,2,7) end
if not tonumber(color,16) then color = "000000" end
buffer[y][x] = color
meta:set_string("buffer"..bufnum,minetest.serialize(buffer))
elseif command.command == "copy" then
if type(command.src) ~= "number" or type(command.dst) ~= "number" or type(command.srcx) ~= "number" or type(command.srcy) ~= "number" or type(command.dstx) ~= "number" or type(command.dsty) ~= "number" or type(command.xsize) ~= "number" or type(command.ysize) ~= "number" then return end
local src = math.floor(command.src)
if src < 0 or src > 7 then return end
local dst = math.floor(command.dst)
if dst < 0 or dst > 7 then return end
local srcx = math.floor(command.srcx)
local srcy = math.floor(command.srcy)
local dstx = math.floor(command.dstx)
local dsty = math.floor(command.dsty)
local xsize = math.floor(command.xsize)
local ysize = math.floor(command.ysize)
if srcx < 1 or srcy < 1 or dstx < 1 or dsty < 1 or xsize < 1 or ysize < 1 then return end
local sourcebuffer = meta:get_string("buffer"..src)
local destbuffer = meta:get_string("buffer"..dst)
if string.len(sourcebuffer) == 0 then return end
sourcebuffer = minetest.deserialize(sourcebuffer)
if type(sourcebuffer) ~= "table" then return end
if string.len(destbuffer) == 0 then return end
destbuffer = minetest.deserialize(destbuffer)
if type(destbuffer) ~= "table" then return end
if srcx + xsize-1 > sourcebuffer.xsize or srcy + ysize-1 > sourcebuffer.ysize then return end
if dstx + xsize-1 > destbuffer.xsize or dsty + ysize-1 > destbuffer.ysize then return end
local transparent = command.transparent
if type(transparent) ~= "string" or string.len(transparent) > 7 or string.len(transparent) < 6 then transparent = "000000" end
if string.sub(transparent,1,1) == "#" then transparent = string.sub(transparent,2,7) end
if not tonumber(transparent,16) then transparent = "000000" end
for y=0,ysize-1,1 do
for x=0,xsize-1,1 do
local srcpx = sourcebuffer[srcy+y][srcx+x]
local destpx = destbuffer[dsty+y][dstx+x]
destbuffer[dsty+y][dstx+x] = blend(srcpx,destpx,command.mode,transparent)
end
end
meta:set_string("buffer"..dst,minetest.serialize(destbuffer))
elseif command.command == "load" then
if type(command.buffer) ~= "number" or type(command.x) ~= "number" or type(command.y) ~= "number" or type(command.data) ~= "table" then return end
local bufnum = math.floor(command.buffer)
if bufnum < 0 or bufnum > 7 then return end
local xstart = math.floor(command.x)
local ystart = math.floor(command.y)
if xstart < 1 or ystart < 1 then return end
local buffer = meta:get_string("buffer"..bufnum)
if string.len(buffer) == 0 then return end
buffer = minetest.deserialize(buffer)
if type(buffer) ~= "table" then return end
if type(command.data[1]) ~= "table" then return end
if #command.data[1] < 1 then return end
local ysize = #command.data
local xsize = #command.data[1]
if xstart+xsize-1 > buffer.xsize or ystart+ysize-1 > buffer.ysize then return end
for y=1,ysize,1 do
if type(command.data[y]) == "table" then
for x=1,xsize,1 do
local color = command.data[y][x]
if type(color) == "string" then
if string.len(color) == 7 then color = string.sub(color,2,7) end
if tonumber(color,16) then
buffer[ystart+y-1][xstart+x-1] = color
end
end
end
end
end
meta:set_string("buffer"..bufnum,minetest.serialize(buffer))
elseif command.command == "text" then
if type(command.buffer) ~= "number" or type(command.x) ~= "number" or type(command.y) ~= "number" or type(command.text) ~= "string" or string.len(command.text) < 1 then return end
command.text = string.sub(command.text,1,16)
local bufnum = math.floor(command.buffer)
if bufnum < 0 or bufnum > 7 then return end
local x = math.floor(command.x)
local y = math.floor(command.y)
if x < 1 or y < 1 then return end
local buffer = meta:get_string("buffer"..bufnum)
if string.len(buffer) == 0 then return end
buffer = minetest.deserialize(buffer)
if type(buffer) ~= "table" then return end
if x > buffer.xsize or y > buffer.ysize then return end
local color = command.color
if type(color) ~= "string" or string.len(color) > 7 or string.len(color) < 6 then color = "ff6600" end
if string.sub(color,1,1) == "#" then color = string.sub(color,2,7) end
if not tonumber(color,16) then color = "ff6600" end
for i=1,string.len(command.text),1 do
local char = font[string.byte(string.sub(command.text,i,i))]
for chary=1,12,1 do
for charx=1,5,1 do
local startx = x + (i*6-6)
if char[chary][charx] and y+chary-1 <= buffer.ysize and startx+charx-1 <= buffer.xsize then
local dstpx = buffer[y+chary-1][startx+charx-1]
buffer[y+chary-1][startx+charx-1] = blend(color,dstpx,command.mode,"")
end
end
end
end
meta:set_string("buffer"..bufnum,minetest.serialize(buffer))
end
end
minetest.register_node("digistuff:gpu", {
description = "Digilines 2D Graphics Processor",
groups = {cracky=3},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec","field[channel;Channel;${channel}")
end,
tiles = {
"digistuff_gpu_top.png",
"jeija_microcontroller_bottom.png",
"jeija_microcontroller_sides.png",
"jeija_microcontroller_sides.png",
"jeija_microcontroller_sides.png",
"jeija_microcontroller_sides.png"
},
inventory_image = "digistuff_gpu_top.png",
drawtype = "nodebox",
selection_box = {
--From luacontroller
type = "fixed",
fixed = { -8/16, -8/16, -8/16, 8/16, -5/16, 8/16 },
},
_digistuff_channelcopier_fieldname = "channel",
node_box = {
--From Luacontroller
type = "fixed",
fixed = {
{-8/16, -8/16, -8/16, 8/16, -7/16, 8/16}, -- Bottom slab
{-5/16, -7/16, -5/16, 5/16, -6/16, 5/16}, -- Circuit board
{-3/16, -6/16, -3/16, 3/16, -5/16, 3/16}, -- IC
}
},
paramtype = "light",
sunlight_propagates = true,
on_receive_fields = function(pos, formname, fields, sender)
local name = sender:get_player_name()
if minetest.is_protected(pos,name) and not minetest.check_player_privs(name,{protection_bypass=true}) then
minetest.record_protection_violation(pos,name)
return
end
local meta = minetest.get_meta(pos)
if fields.channel then meta:set_string("channel",fields.channel) end
end,
digiline =
{
receptor = {},
effector = {
action = function(pos,node,channel,msg)
local meta = minetest.get_meta(pos)
if meta:get_string("channel") ~= channel or type(msg) ~= "table" then return end
if type(msg[1]) == "table" then
for i=1,32,1 do
if type(msg[i]) == "table" then
runcommand(pos,meta,msg[i])
end
end
else
runcommand(pos,meta,msg)
end
end
},
},
})
minetest.register_craft({
output = "digistuff:gpu",
recipe = {
{"","default:steel_ingot",""},
{"digilines:wire_std_00000000","mesecons_luacontroller:luacontroller0000","digilines:wire_std_00000000"},
{"dye:red","dye:green","dye:blue"},
}
})

View File

@ -1,121 +0,0 @@
Digilines 2D Graphics Processor
===============================
Commands for this part are in the form of a table, consisting of a field called "command" with the command to be executed, and other fields supplying parameters to the command.
Example:
{command="send",buffer=0,channel="example"}
Up to 32 commands may be sent at once by placing all of the tables into one.
Example:
{
{command="createbuffer",buffer=0,xsize=16,ysize=16,color="aaaaaa"},
{command="send",buffer=0,channel="example"}
}
Command: createbuffer
---------------------
Creates a new buffer. Up to 8 buffers may exist at one time, and each can be up to 64x64 pixels in size.
Parameters:
buffer [integer 0-7]: The slot number to create the new buffer in. If the slot is already occupied, the existing contents will be erased.
xsize [integer 1-64]: The width of the new buffer in pixels.
ysize [integer 1-64]: The height of the new buffer in pixels.
color [hex color, default "000000"]: A color to fill the new buffer with.
Command: send
-------------
Sends the contents of a buffer to a digiscreen, rgblightstone panel, or other digilines device.
Parameters:
buffer [integer 0-7]: The buffer to send the contents of.
channel [string]: The digilines channel to send the message on.
Command: drawrect
-----------------
Draws a rectangle with optional border on a buffer.
Parameters:
buffer [integer 0-7]: The buffer to draw the rectangle on.
x1 [integer 1-64]: The X position of the left side of the rectangle.
x2 [integer 1-64]: The X position of the right side of the rectangle.
y1 [integer 1-64]: The Y position of the top side of the rectangle.
y2 [integer 1-64]: The Y position of the bottom side of the rectangle.
fill [hex color, default "000000"]: The color of the rectangle.
edge [hex color, default same as fill]: The color of the outside edge of the rectangle.
Command: drawpoint
------------------
Draws a point on a buffer.
This command is intended for use when writing a single pixel at a time.
For writing large blocks at one time, it is recommended to use the "load" command instead.
Parameters:
buffer [integer 0-7]: The buffer to draw the point on.
x [integer 1-64]: The X position of the point.
y [integer 1-64]: The Y position of the point.
color [hex color, default "000000"]: The color of the point.
Command: copy
-------------
Perform a BitBLT operation (such as copying one buffer to another).
Parameters:
src [integer 0-7]: The buffer to copy from.
dst [integer 0-7]: The buffer to copy to. May be the same or different from "src".
srcx [integer 1-64]: The X position of the left side of the region to copy from.
srcy [integer 1-64]: The Y position of the top side of the region to copy from.
dstx [integer 1-64]: The X position of the left side of the region to copy to.
dsty [integer 1-64]: The Y position of the top side of the region to copy to.
xsize [integer 1-64]: The width of the region to copy.
ysize [integer 1-64]: The height of the region to copy.
mode [string from list below, default "normal"]: The blend mode to use for the copy operation.
transparent [hex color, default "000000"]: The color to treat as transparency when using the "overlay" blend mode. No effect in other modes.
Blend modes:
normal: Copy the source to the destination, overwriting the destination.
nop: Do nothing.
overlay: Same as normal, but skip pixels in the source matching the "transparent" color.
add: For each subpixel (red, green, blue) add the source values to the destination and write the sum to the destination.
sub: For each subpixel (red, green, blue) subtract the source values from the destination and write the difference to the destination.
isub: For each subpixel (red, green, blue) subtract the destination values from the source and write the difference to the destination.
average: For each subpixel (red, green, blue) calculate the average of the source and destination and write the average to the destination.
and: Perform a bitwise AND of the source and destination and write the result to the destination.
or: Perform a bitwise OR of the source and destination and write the result to the destination.
nand: Perform a bitwise NAND of the source and destination and write the result to the destination.
nor: Perform a bitwise NOR of the source and destination and write the result to the destination.
xor: Perform a bitwise XOR of the source and destination and write the result to the destination.
xnor: Perform a bitwise XNOR of the source and destination and write the result to the destination.
not: Perform a bitwise NOT of the source and write the result to the destination.
rgbtohsv: Convert the source from the RGB color system to the HSV color system and write the result to the destination, storing hue as "red", saturation as "green", and value as "blue".
hsvtorgb: Convert the source from the HSV color system to the RGB color system, reading hue from the red channel, saturation from the green channel, and value from the blue channel, and write the result to the destination.
Command: load
-------------
Transfer a bitmap image into a buffer.
The width and height of the image will be automatically determined from the data given.
Parameters:
buffer [integer 0-7]: The buffer to write the image into.
x [integer 1-64]: The X position of the left side of the image.
y [integer 1-64]: The Y position of the top side of the image.
data [2D array of hex color values, default for each is transparency]: The bitmap image to write.
Command: text
-------------
Draw one or more text characters on a buffer.
The font being used is 5*12 pixels in size, with one pixel spacing between characters.
Parameters:
buffer [integer 0-7]: The buffer to draw the text on.
x [integer 1-64]: The X position of the left side of the text.
y [integer 1-64]: The Y position of the right side of the text.
color [hex color, default "ff6600"]: The color of the text.
text: The text string to draw.

View File

@ -1,177 +0,0 @@
minetest.register_node("digistuff:ram", {
description = "Digilines 128Kbit SRAM",
groups = {cracky=3},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec","field[channel;Channel;${channel}")
for i=0,31,1 do
meta:set_string(string.format("data%02d",i),"")
end
end,
tiles = {
"digistuff_ram_top.png",
"jeija_microcontroller_bottom.png",
"jeija_microcontroller_sides.png",
"jeija_microcontroller_sides.png",
"jeija_microcontroller_sides.png",
"jeija_microcontroller_sides.png"
},
inventory_image = "digistuff_ram_top.png",
drawtype = "nodebox",
selection_box = {
--From luacontroller
type = "fixed",
fixed = { -8/16, -8/16, -8/16, 8/16, -5/16, 8/16 },
},
_digistuff_channelcopier_fieldname = "channel",
node_box = {
--From Luacontroller
type = "fixed",
fixed = {
{-8/16, -8/16, -8/16, 8/16, -7/16, 8/16}, -- Bottom slab
{-5/16, -7/16, -5/16, 5/16, -6/16, 5/16}, -- Circuit board
{-3/16, -6/16, -3/16, 3/16, -5/16, 3/16}, -- IC
}
},
paramtype = "light",
sunlight_propagates = true,
on_receive_fields = function(pos, formname, fields, sender)
local name = sender:get_player_name()
if minetest.is_protected(pos,name) and not minetest.check_player_privs(name,{protection_bypass=true}) then
minetest.record_protection_violation(pos,name)
return
end
local meta = minetest.get_meta(pos)
if fields.channel then meta:set_string("channel",fields.channel) end
end,
digiline =
{
receptor = {},
effector = {
action = function(pos,node,channel,msg)
local meta = minetest.get_meta(pos)
if meta:get_string("channel") ~= channel or type(msg) ~= "table" then return end
if msg.command == "read" then
if type(msg.address) == "number" and msg.address >= 0 and msg.address <= 31 then
digiline:receptor_send(pos,digiline.rules.default,channel,meta:get_string(string.format("data%02i",math.floor(msg.address))))
end
elseif msg.command == "write" then
if type(msg.address) == "number" and msg.address >= 0 and msg.address <= 31 and type(msg.data) == "string" then
meta:set_string(string.format("data%02i",math.floor(msg.address)),string.sub(msg.data,1,512))
end
end
end
},
},
})
minetest.register_node("digistuff:eeprom", {
description = "Digilines 128Kbit EEPROM",
groups = {cracky=3},
stack_max = 1,
after_place_node = function(pos,_,istack)
local meta = minetest.get_meta(pos)
local smeta = istack:get_meta()
for i=0,31,1 do
meta:set_string(string.format("data%02d",i),smeta:get_string(string.format("data%02d",i)))
end
meta:set_string("channel",smeta:get_string("channel"))
meta:set_string("formspec","field[channel;Channel;${channel}")
end,
on_dig = function(pos,node,player)
local name = player:get_player_name()
if minetest.is_protected(pos,name) and not minetest.check_player_privs(name,{protection_bypass=true}) then
minetest.record_protection_violation(pos,name)
return
end
local meta = minetest.get_meta(pos)
local istack = ItemStack("digistuff:eeprom")
local smeta = istack:get_meta()
for i=0,31,1 do
smeta:set_string(string.format("data%02d",i),meta:get_string(string.format("data%02d",i)))
end
smeta:set_string("channel",meta:get_string("channel"))
minetest.remove_node(pos)
smeta:set_string("description","Digilines 128KBit EEPROM (with data)")
local inv = minetest.get_inventory({type = "player",name = name,})
if player.is_fake_player or not inv:room_for_item("main",istack) then
minetest.handle_node_drops(pos,{istack},player)
else
inv:add_item("main",istack)
end
digilines.update_autoconnect(pos)
end,
tiles = {
"digistuff_eeprom_top.png",
"jeija_microcontroller_bottom.png",
"jeija_microcontroller_sides.png",
"jeija_microcontroller_sides.png",
"jeija_microcontroller_sides.png",
"jeija_microcontroller_sides.png"
},
inventory_image = "digistuff_eeprom_top.png",
drawtype = "nodebox",
selection_box = {
--From luacontroller
type = "fixed",
fixed = { -8/16, -8/16, -8/16, 8/16, -5/16, 8/16 },
},
_digistuff_channelcopier_fieldname = "channel",
node_box = {
--From Luacontroller
type = "fixed",
fixed = {
{-8/16, -8/16, -8/16, 8/16, -7/16, 8/16}, -- Bottom slab
{-5/16, -7/16, -5/16, 5/16, -6/16, 5/16}, -- Circuit board
{-3/16, -6/16, -3/16, 3/16, -5/16, 3/16}, -- IC
}
},
paramtype = "light",
sunlight_propagates = true,
on_receive_fields = function(pos, formname, fields, sender)
local name = sender:get_player_name()
if minetest.is_protected(pos,name) and not minetest.check_player_privs(name,{protection_bypass=true}) then
minetest.record_protection_violation(pos,name)
return
end
local meta = minetest.get_meta(pos)
if fields.channel then meta:set_string("channel",fields.channel) end
end,
digiline =
{
receptor = {},
effector = {
action = function(pos,node,channel,msg)
local meta = minetest.get_meta(pos)
if meta:get_string("channel") ~= channel or type(msg) ~= "table" then return end
if msg.command == "read" then
if type(msg.address) == "number" and msg.address >= 0 and msg.address <= 31 then
digiline:receptor_send(pos,digiline.rules.default,channel,meta:get_string(string.format("data%02i",math.floor(msg.address))))
end
elseif msg.command == "write" then
if type(msg.address) == "number" and msg.address >= 0 and msg.address <= 31 and type(msg.data) == "string" then
meta:set_string(string.format("data%02i",math.floor(msg.address)),string.sub(msg.data,1,512))
end
end
end
},
},
})
minetest.register_craft({
output = "digistuff:ram",
recipe = {
{"basic_materials:plastic_sheet","basic_materials:plastic_sheet","basic_materials:plastic_sheet"},
{"mesecons_gates:nand_off","basic_materials:plastic_sheet","mesecons_gates:nand_off"},
{"mesecons:wire_00000000_off","basic_materials:silicon","mesecons:wire_00000000_off"},
}
})
minetest.register_craft({
output = "digistuff:eeprom",
recipe = {
{"basic_materials:plastic_sheet","mesecons:wire_00000000_off","basic_materials:plastic_sheet"},
{"digilines:wire_std_00000000","basic_materials:plastic_sheet","digilines:wire_std_00000000"},
{"mesecons:wire_00000000_off","basic_materials:silicon","mesecons:wire_00000000_off"},
}
})

View File

@ -1,269 +0,0 @@
if not minetest.get_modpath("mesecons_mvps") then
minetest.log("warning","mesecons_mvps is not installed - digilines movestone will not be available")
return
end
local function abortmovement(pos)
local meta = minetest.get_meta(pos)
local state = meta:get_string("state")
if state ~= "" then state = minetest.deserialize(state) else state = {} end
state.targetx = pos.x
state.targety = pos.y
state.targetz = pos.z
state.moveaxis = nil
meta:set_string("state",minetest.serialize(state))
end
local function checkprotection(pos,player)
if not player then player = "" end
if type(player) ~= "string" then player = player:get_player_name() end
if minetest.is_protected(pos,player) and not minetest.check_player_privs(player,{protection_bypass=true}) then
minetest.record_protection_violation(pos,player)
return false
end
return true
end
local function move(pos,dir,state)
local newpos = vector.add(pos,dir)
local stack = mesecon.mvps_get_stack(pos,dir,state.maxstack,state.sticky and state.allsticky)
if not stack then
abortmovement(pos)
return false
end
for _,i in pairs(stack) do
if not checkprotection(i.pos,state.player) then
abortmovement(pos)
return false
end
end
local success,stack,oldstack = mesecon.mvps_push(pos,dir,state.maxstack)
if not success then
abortmovement(pos)
return false
end
mesecon.mvps_process_stack(stack)
mesecon.mvps_move_objects(pos,dir,oldstack)
if state.sound == "mesecons" then
minetest.sound_play("movestone",{pos = pos,max_hear_distance = 20,gain = 0.5,},true)
end
if not state.sticky then return true end
local ppos = vector.add(pos,vector.multiply(dir,-1))
local success,stack,oldstack
if state.allsticky then
success,stack,oldstack = mesecon.mvps_pull_all(ppos,dir,state.maxstack)
else
success,stack,oldstack = mesecon.mvps_pull_single(ppos,dir,state.maxstack)
end
if success then
mesecon.mvps_move_objects(ppos,dir,oldstack,-1)
else
abortmovement(pos)
return false
end
return true
end
local rules = {
{x = 1, y = 0, z = 0},
{x =-1, y = 0, z = 0},
{x = 0, y = 1, z = 0},
{x = 0, y =-1, z = 0},
{x = 0, y = 0, z = 1},
{x = 0, y = 0, z =-1},
}
minetest.register_node("digistuff:movestone", {
description = "Digilines Movestone",
groups = {cracky = 3,},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec","field[channel;Channel;${channel}")
local initialstate = {
targetx = pos.x,
targety = pos.y,
targetz = pos.z,
sound = "mesecons",
maxstack = 0,
allsticky = false,
}
meta:set_int("active",0)
meta:set_string("state",minetest.serialize(initialstate))
end,
after_place_node = function(pos,player)
if not player then return end
local meta = minetest.get_meta(pos)
meta:set_string("owner",player:get_player_name())
end,
tiles = {
"jeija_movestone_side.png",
},
on_receive_fields = function(pos, formname, fields, sender)
local name = sender:get_player_name()
if minetest.is_protected(pos,name) and not minetest.check_player_privs(name,{protection_bypass=true}) then
minetest.record_protection_violation(pos,name)
return
end
local meta = minetest.get_meta(pos)
if fields.channel then meta:set_string("channel",fields.channel) end
end,
on_timer = function(pos)
local meta = minetest.get_meta(pos)
if meta:get_int("active") < 1 then return end
local state = meta:get_string("state")
local newpos = pos
if state ~= "" then state = minetest.deserialize(state) else return end
if state.moveaxis == "x" then
local dir = vector.new(state.targetx > pos.x and 1 or -1,0,0)
move(pos,dir,state)
newpos = vector.add(pos,dir)
if newpos.x == state.targetx then
if newpos.y ~= state.targety then
state.moveaxis = "y"
elseif newpos.z ~= state.targetz then
state.moveaxis = "z"
else
state.moveaxis = nil
end
end
elseif state.moveaxis == "y" then
local dir = vector.new(0,state.targety > pos.y and 1 or -1,0)
move(pos,dir,state)
newpos = vector.add(pos,dir)
if newpos.y == state.targety then
if newpos.z ~= state.targetz then
state.moveaxis = "z"
else
state.moveaxis = nil
end
end
elseif state.moveaxis == "z" then
local dir = vector.new(0,0,state.targetz > pos.z and 1 or -1)
move(pos,dir,state)
newpos = vector.add(pos,dir)
if newpos.z == state.targetz then
state.moveaxis = nil
end
end
local newmeta = minetest.get_meta(newpos)
newmeta:set_int("active",state.moveaxis and 1 or 0)
newmeta:set_string("state",minetest.serialize(state))
if state.moveaxis then
local timer = minetest.get_node_timer(newpos)
timer:start(0.33)
end
end,
_digistuff_channelcopier_fieldname = "channel",
digiline = {
wire = {
rules = rules,
},
receptor = {},
effector = {
action = function(pos,node,channel,msg)
local meta = minetest.get_meta(pos)
local setchan = meta:get_string("channel")
if channel ~= setchan then return end
if type(msg) ~= "table" or not msg.command then return end
if msg.command == "getstate" then
local ret = {}
local meta = minetest.get_meta(pos)
local state = meta:get_string("state")
if state ~= "" then state = minetest.deserialize(state) else state = {} end
if not state then
minetest.log("error",string.format("Invalid state information for digilines movestone at %d,%d,%d: %s",pos.x,pos.y,pos.z,meta:get_string("state")))
return
end
ret.pos = pos
ret.targetpos = vector.new(state.targetx,state.targety,state.targetz)
ret.moveaxis = state.moveaxis
digiline:receptor_send(pos,rules,channel,ret)
elseif msg.command == "absmove" then
local ret = {}
local meta = minetest.get_meta(pos)
local state = meta:get_string("state")
if state ~= "" then state = minetest.deserialize(state) else state = {} end
if not state then
minetest.log("error",string.format("Invalid state information for digilines movestone at %d,%d,%d: %s",pos.x,pos.y,pos.z,meta:get_string("state")))
return
end
if type(msg.sound) == "string" then state.sound = msg.sound end
if type(msg.x) ~= "number" then msg.x = pos.x end
if type(msg.y) ~= "number" then msg.y = pos.y end
if type(msg.z) ~= "number" then msg.z = pos.z end
msg.x = math.max(pos.x-50,math.min(pos.x+50,math.floor(msg.x)))
msg.y = math.max(pos.y-50,math.min(pos.y+50,math.floor(msg.y)))
msg.z = math.max(pos.z-50,math.min(pos.z+50,math.floor(msg.z)))
local firstaxis
if msg.x ~= pos.x then firstaxis = "x"
elseif msg.y ~= pos.y then firstaxis = "y"
elseif msg.z ~= pos.z then firstaxis = "z" end
if firstaxis then
state.targetx = msg.x
state.targety = msg.y
state.targetz = msg.z
state.moveaxis = firstaxis
if msg.sticky then
state.sticky = true
elseif msg.sticky == false then
state.sticky = false
end
if msg.allsticky then
state.allsticky = true
elseif msg.allsticky == false then
state.allsticky = false
end
if type(msg.maxstack) == "number" and msg.maxstack >= 0 and msg.maxstack <= 50 then
state.maxstack = math.floor(msg.maxstack)
end
meta:set_string("state",minetest.serialize(state))
meta:set_int("active",1)
minetest.get_node_timer(pos):start(0.1)
end
elseif msg.command == "relmove" then
local ret = {}
local meta = minetest.get_meta(pos)
local state = meta:get_string("state")
if state ~= "" then state = minetest.deserialize(state) else state = {} end
if not state then
minetest.log("error",string.format("Invalid state information for digilines movestone at %d,%d,%d: %s",pos.x,pos.y,pos.z,meta:get_string("state")))
return
end
if type(msg.sound) == "string" then state.sound = msg.sound end
if type(msg.x) ~= "number" then msg.x = 0 end
if type(msg.y) ~= "number" then msg.y = 0 end
if type(msg.z) ~= "number" then msg.z = 0 end
msg.x = pos.x+math.max(0,math.min(50,math.floor(msg.x)))
msg.y = pos.y+math.max(0,math.min(50,math.floor(msg.y)))
msg.z = pos.z+math.max(0,math.min(50,math.floor(msg.z)))
local firstaxis
if msg.x ~= pos.x then firstaxis = "x"
elseif msg.y ~= pos.y then firstaxis = "y"
elseif msg.z ~= pos.z then firstaxis = "z" end
if firstaxis then
state.targetx = msg.x
state.targety = msg.y
state.targetz = msg.z
state.moveaxis = firstaxis
if msg.sticky then
state.sticky = true
elseif msg.sticky == false then
state.sticky = false
end
if msg.allsticky then
state.allsticky = true
elseif msg.allsticky == false then
state.allsticky = false
end
if type(msg.maxstack) == "number" and msg.maxstack >= 0 and msg.maxstack <= 50 then
state.maxstack = math.floor(msg.maxstack)
end
meta:set_string("state",minetest.serialize(state))
meta:set_int("active",1)
minetest.get_node_timer(pos):start(0.1)
end
end
end
},
},
})

View File

@ -1,156 +0,0 @@
digistuff.update_panel_formspec = function (pos,dispstr)
local meta = minetest.get_meta(pos)
local locked = meta:get_int("locked") == 1
local fs = "size[10,8]"..
"background[0,0;0,0;digistuff_panel_bg.png;true]"..
"label[0,0;%s]"..
(locked and "image_button[9,3;1,1;digistuff_panel_locked.png;unlock;]" or "image_button[9,3;1,1;digistuff_panel_unlocked.png;lock;]")..
"image_button[2,4.5;1,1;digistuff_adwaita_go-up.png;up;]"..
"image_button[1,5;1,1;digistuff_adwaita_go-previous.png;left;]"..
"image_button[3,5;1,1;digistuff_adwaita_go-next.png;right;]"..
"image_button[2,5.5;1,1;digistuff_adwaita_go-down.png;down;]"..
"image_button[1,6.5;1,1;digistuff_adwaita_edit-undo.png;back;]"..
"image_button[3,6.5;1,1;digistuff_adwaita_emblem-default.png;enter;]"..
"field[6,5.75;2,1;channel;Channel;${channel}]"..
"button[8,5.5;1,1;savechan;Set]"
fs = fs:format(minetest.formspec_escape(dispstr)):gsub("|","\n")
meta:set_string("formspec",fs)
meta:set_string("text",dispstr)
end
digistuff.panel_on_digiline_receive = function (pos, node, channel, msg)
local meta = minetest.get_meta(pos)
local setchan = meta:get_string("channel")
if channel ~= setchan then return end
if type(msg) ~= "string" then return end
digistuff.update_panel_formspec(pos,msg)
end
digistuff.panel_on_receive_fields = function(pos, formname, fields, sender)
local meta = minetest.get_meta(pos)
local setchan = meta:get_string("channel")
local playername = sender:get_player_name()
local locked = meta:get_int("locked") == 1
local can_bypass = minetest.check_player_privs(playername,{protection_bypass=true})
local is_protected = minetest.is_protected(pos,playername)
if fields.savechan then
if can_bypass or not is_protected then
meta:set_string("channel",fields.channel)
local helpmsg = "Channel has been set. Waiting for data..."
digistuff.update_panel_formspec(pos,helpmsg)
else
minetest.record_protection_violation(pos,playername)
minetest.chat_send_player(playername,"You are not authorized to change the channel of this panel.")
end
elseif fields.up then
if can_bypass or not is_protected or not locked then
digiline:receptor_send(pos, digiline.rules.default, setchan, "up")
else
minetest.record_protection_violation(pos,playername)
minetest.chat_send_player(playername,"You are not authorized to use this panel.")
end
elseif fields.down then
if can_bypass or not is_protected or not locked then
digiline:receptor_send(pos, digiline.rules.default, setchan, "down")
else
minetest.record_protection_violation(pos,playername)
minetest.chat_send_player(playername,"You are not authorized to use this panel.")
end
elseif fields.left then
if can_bypass or not is_protected or not locked then
digiline:receptor_send(pos, digiline.rules.default, setchan, "left")
else
minetest.record_protection_violation(pos,playername)
minetest.chat_send_player(playername,"You are not authorized to use this panel.")
end
elseif fields.right then
if can_bypass or not is_protected or not locked then
digiline:receptor_send(pos, digiline.rules.default, setchan, "right")
else
minetest.record_protection_violation(pos,playername)
minetest.chat_send_player(playername,"You are not authorized to use this panel.")
end
elseif fields.back then
if can_bypass or not is_protected or not locked then
digiline:receptor_send(pos, digiline.rules.default, setchan, "back")
else
minetest.record_protection_violation(pos,playername)
minetest.chat_send_player(playername,"You are not authorized to use this panel.")
end
elseif fields.enter then
if can_bypass or not is_protected or not locked then
digiline:receptor_send(pos, digiline.rules.default, setchan, "enter")
else
minetest.record_protection_violation(pos,playername)
minetest.chat_send_player(playername,"You are not authorized to use this panel.")
end
elseif fields.lock then
if can_bypass or not is_protected then
meta:set_int("locked",1)
minetest.chat_send_player(playername,"This panel has been locked. Access will now be controlled according to area protection.")
digistuff.update_panel_formspec(pos,meta:get_string("text"))
else
minetest.record_protection_violation(pos,playername)
minetest.chat_send_player(playername,"You are not authorized to lock this panel.")
end
elseif fields.unlock then
if can_bypass or not is_protected then
meta:set_int("locked",0)
minetest.chat_send_player(playername,"This panel has been unlocked. It can now be used (but not locked or have the channel changed) by anyone.")
digistuff.update_panel_formspec(pos,meta:get_string("text"))
else
minetest.record_protection_violation(pos,playername)
minetest.chat_send_player(playername,"You are not authorized to unlock this panel.")
end
end
end
minetest.register_node("digistuff:panel", {
description = "Digilines Control Panel",
groups = {cracky=3},
on_construct = function(pos)
local helpmsg = "Please set a channel."
digistuff.update_panel_formspec(pos,helpmsg)
minetest.get_meta(pos):set_int("locked",0)
end,
drawtype = "nodebox",
tiles = {
"digistuff_panel_back.png",
"digistuff_panel_back.png",
"digistuff_panel_back.png",
"digistuff_panel_back.png",
"digistuff_panel_back.png",
"digistuff_panel_front.png"
},
_digistuff_channelcopier_fieldname = "channel",
_digistuff_channelcopier_onset = function(pos)
local helpmsg = "Channel has been set. Waiting for data..."
digistuff.update_panel_formspec(pos,helpmsg)
end,
paramtype = "light",
paramtype2 = "facedir",
node_box = {
type = "fixed",
fixed = {
{ -0.5, -0.5, 0.4, 0.5, 0.5, 0.5 }
}
},
on_receive_fields = digistuff.panel_on_receive_fields,
digiline =
{
receptor = {},
effector = {
action = digistuff.panel_on_digiline_receive
},
},
})
minetest.register_craft({
output = "digistuff:panel",
recipe = {
{"","digistuff:button",""},
{"digistuff:button","digilines:lcd","digistuff:button"},
{"","digistuff:button",""}
}
})

View File

@ -1,89 +0,0 @@
digistuff.sounds_playing = {}
minetest.register_node("digistuff:piezo", {
description = "Digilines Piezoelectric Beeper",
groups = {cracky=3},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec","field[channel;Channel;${channel}")
end,
on_destruct = function(pos)
local pos_hash = minetest.hash_node_position(pos)
if digistuff.sounds_playing[pos_hash] then
minetest.sound_stop(digistuff.sounds_playing[pos_hash])
digistuff.sounds_playing[pos_hash] = nil
end
end,
_digistuff_channelcopier_fieldname = "channel",
tiles = {
"digistuff_piezo_top.png",
"digistuff_piezo_sides.png",
"digistuff_piezo_sides.png",
"digistuff_piezo_sides.png",
"digistuff_piezo_sides.png",
"digistuff_piezo_sides.png"
},
on_receive_fields = function(pos, formname, fields, sender)
local name = sender:get_player_name()
if minetest.is_protected(pos,name) and not minetest.check_player_privs(name,{protection_bypass=true}) then
minetest.record_protection_violation(pos,name)
return
end
local meta = minetest.get_meta(pos)
if fields.channel then meta:set_string("channel",fields.channel) end
end,
digiline =
{
receptor = {},
effector = {
action = function(pos,node,channel,msg)
local meta = minetest.get_meta(pos)
local setchan = meta:get_string("channel")
if channel ~= setchan then return end
if msg == "shortbeep" then
local pos_hash = minetest.hash_node_position(pos)
if digistuff.sounds_playing[pos_hash] then
minetest.sound_stop(digistuff.sounds_playing[pos_hash])
digistuff.sounds_playing[pos_hash] = nil
end
minetest.sound_play({name = "digistuff_piezo_short_single",gain = 0.2},{pos = pos,max_hear_distance = 16})
elseif msg == "longbeep" then
local pos_hash = minetest.hash_node_position(pos)
if digistuff.sounds_playing[pos_hash] then
minetest.sound_stop(digistuff.sounds_playing[pos_hash])
digistuff.sounds_playing[pos_hash] = nil
end
minetest.sound_play({name = "digistuff_piezo_long_single",gain = 0.2},{pos = pos,max_hear_distance = 16})
elseif msg == "fastrepeat" then
local pos_hash = minetest.hash_node_position(pos)
if digistuff.sounds_playing[pos_hash] then
minetest.sound_stop(digistuff.sounds_playing[pos_hash])
digistuff.sounds_playing[pos_hash] = nil
end
digistuff.sounds_playing[pos_hash] = minetest.sound_play({name = "digistuff_piezo_fast_repeat",gain = 0.2},{pos = pos,max_hear_distance = 16,loop = true})
elseif msg == "slowrepeat" then
local pos_hash = minetest.hash_node_position(pos)
if digistuff.sounds_playing[pos_hash] then
minetest.sound_stop(digistuff.sounds_playing[pos_hash])
digistuff.sounds_playing[pos_hash] = nil
end
digistuff.sounds_playing[pos_hash] = minetest.sound_play({name = "digistuff_piezo_slow_repeat",gain = 0.2},{pos = pos,max_hear_distance = 16,loop = true})
elseif msg == "stop" then
local pos_hash = minetest.hash_node_position(pos)
if digistuff.sounds_playing[pos_hash] then
minetest.sound_stop(digistuff.sounds_playing[pos_hash])
digistuff.sounds_playing[pos_hash] = nil
end
end
end
},
},
})
minetest.register_craft({
output = "digistuff:piezo",
recipe = {
{"quartz:quartz_crystal_piece","basic_materials:steel_strip"},
{"digilines:wire_std_00000000","mesecons_luacontroller:luacontroller0000"},
},
})

View File

@ -1,315 +0,0 @@
digistuff.update_ts_formspec = function (pos)
local meta = minetest.get_meta(pos)
local fs = "size[10,8]"..
"background[0,0;0,0;digistuff_ts_bg.png;true]"
if meta:get_int("realcoordinates") > 0 then
fs = fs.."real_coordinates[true]"
end
if meta:get_int("init") == 0 then
fs = fs.."field[3.75,3;3,1;channel;Channel;]"..
"button_exit[4,3.75;2,1;save;Save]"
else
local data = minetest.deserialize(meta:get_string("data")) or {}
for _,field in pairs(data) do
if field.type == "image" then
fs = fs..string.format("image[%s,%s;%s,%s;%s]",field.X,field.Y,field.W,field.H,field.texture_name)
elseif field.type == "field" then
fs = fs..string.format("field[%s,%s;%s,%s;%s;%s;%s]",field.X,field.Y,field.W,field.H,field.name,field.label,field.default)
elseif field.type == "pwdfield" then
fs = fs..string.format("pwdfield[%s,%s;%s,%s;%s;%s]",field.X,field.Y,field.W,field.H,field.name,field.label)
elseif field.type == "textarea" then
fs = fs..string.format("textarea[%s,%s;%s,%s;%s;%s;%s]",field.X,field.Y,field.W,field.H,field.name,field.label,field.default)
elseif field.type == "label" then
fs = fs..string.format("label[%s,%s;%s]",field.X,field.Y,field.label)
elseif field.type == "vertlabel" then
fs = fs..string.format("vertlabel[%s,%s;%s]",field.X,field.Y,field.label)
elseif field.type == "button" then
fs = fs..string.format("button[%s,%s;%s,%s;%s;%s]",field.X,field.Y,field.W,field.H,field.name,field.label)
elseif field.type == "button_exit" then
fs = fs..string.format("button_exit[%s,%s;%s,%s;%s;%s]",field.X,field.Y,field.W,field.H,field.name,field.label)
elseif field.type == "image_button" then
fs = fs..string.format("image_button[%s,%s;%s,%s;%s;%s;%s]",field.X,field.Y,field.W,field.H,field.image,field.name,field.label)
elseif field.type == "image_button_exit" then
fs = fs..string.format("image_button_exit[%s,%s;%s,%s;%s;%s;%s]",field.X,field.Y,field.W,field.H,field.image,field.name,field.label)
elseif field.type == "dropdown" then
local choices = ""
for _,i in ipairs(field.choices) do
if type(i) == "string" then
choices = choices..minetest.formspec_escape(i)..","
end
end
choices = string.sub(choices,1,-2)
fs = fs..string.format("dropdown[%s,%s;%s,%s;%s;%s;%s]",field.X,field.Y,field.W,field.H,field.name,choices,field.selected_id)
elseif field.type == "textlist" then
local listelements = ""
for _,i in ipairs(field.listelements) do
if type(i) == "string" then
listelements = listelements..minetest.formspec_escape(i)..","
end
end
listelements = string.sub(listelements,1,-2)
fs = fs..string.format("textlist[%s,%s;%s,%s;%s;%s;%s;%s]",field.X,field.Y,field.W,field.H,field.name,listelements,field.selected_id,field.transparent)
end
end
end
meta:set_string("formspec",fs)
end
digistuff.ts_on_receive_fields = function (pos, formname, fields, sender)
local meta = minetest.get_meta(pos)
local setchan = meta:get_string("channel")
local playername = sender:get_player_name()
local locked = meta:get_int("locked") == 1
local can_bypass = minetest.check_player_privs(playername,{protection_bypass=true})
local is_protected = minetest.is_protected(pos,playername)
if (locked and is_protected) and not can_bypass then
minetest.record_protection_violation(pos,playername)
minetest.chat_send_player(playername,"You are not authorized to use this screen.")
return
end
local init = meta:get_int("init") == 1
if not init then
if fields.save then
meta:set_string("channel",fields.channel)
meta:set_int("init",1)
digistuff.update_ts_formspec(pos)
end
else
fields.clicker = sender:get_player_name()
digiline:receptor_send(pos, digiline.rules.default, setchan, fields)
end
end
digistuff.process_command = function (meta, data, msg)
if msg.command == "clear" then
data = {}
elseif msg.command == "realcoordinates" then
meta:set_int("realcoordinates",msg.enabled and 1 or 0)
elseif msg.command == "addimage" then
for _,i in pairs({"X","Y","W","H"}) do
if not msg[i] or type(msg[i]) ~= "number" then
return
end
end
if not msg.texture_name or type(msg.texture_name) ~= "string" then
return
end
local field = {type="image",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,texture_name=minetest.formspec_escape(msg.texture_name)}
table.insert(data,field)
elseif msg.command == "addfield" then
for _,i in pairs({"X","Y","W","H"}) do
if not msg[i] or type(msg[i]) ~= "number" then
return
end
end
for _,i in pairs({"name","label","default"}) do
if not msg[i] or type(msg[i]) ~= "string" then
return
end
end
local field = {type="field",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,name=minetest.formspec_escape(msg.name),label=minetest.formspec_escape(msg.label),default=minetest.formspec_escape(msg.default)}
table.insert(data,field)
elseif msg.command == "addpwdfield" then
for _,i in pairs({"X","Y","W","H"}) do
if not msg[i] or type(msg[i]) ~= "number" then
return
end
end
for _,i in pairs({"name","label"}) do
if not msg[i] or type(msg[i]) ~= "string" then
return
end
end
local field = {type="pwdfield",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,name=minetest.formspec_escape(msg.name),label=minetest.formspec_escape(msg.label)}
table.insert(data,field)
elseif msg.command == "addtextarea" then
for _,i in pairs({"X","Y","W","H"}) do
if not msg[i] or type(msg[i]) ~= "number" then
return
end
end
for _,i in pairs({"name","label","default"}) do
if not msg[i] or type(msg[i]) ~= "string" then
return
end
end
local field = {type="textarea",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,name=minetest.formspec_escape(msg.name),label=minetest.formspec_escape(msg.label),default=minetest.formspec_escape(msg.default)}
table.insert(data,field)
elseif msg.command == "addlabel" then
for _,i in pairs({"X","Y"}) do
if not msg[i] or type(msg[i]) ~= "number" then
return
end
end
if not msg.label or type(msg.label) ~= "string" then
return
end
local field = {type="label",X=msg.X,Y=msg.Y,label=minetest.formspec_escape(msg.label)}
table.insert(data,field)
elseif msg.command == "addvertlabel" then
for _,i in pairs({"X","Y"}) do
if not msg[i] or type(msg[i]) ~= "number" then
return
end
end
if not msg.label or type(msg.label) ~= "string" then
return
end
local field = {type="vertlabel",X=msg.X,Y=msg.Y,label=minetest.formspec_escape(msg.label)}
table.insert(data,field)
elseif msg.command == "addbutton" then
for _,i in pairs({"X","Y","W","H"}) do
if not msg[i] or type(msg[i]) ~= "number" then
return
end
end
for _,i in pairs({"name","label"}) do
if not msg[i] or type(msg[i]) ~= "string" then
return
end
end
local field = {type="button",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,name=minetest.formspec_escape(msg.name),label=minetest.formspec_escape(msg.label)}
table.insert(data,field)
elseif msg.command == "addbutton_exit" then
for _,i in pairs({"X","Y","W","H"}) do
if not msg[i] or type(msg[i]) ~= "number" then
return
end
end
for _,i in pairs({"name","label"}) do
if not msg[i] or type(msg[i]) ~= "string" then
return
end
end
local field = {type="button_exit",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,name=minetest.formspec_escape(msg.name),label=minetest.formspec_escape(msg.label)}
table.insert(data,field)
elseif msg.command == "addimage_button" then
for _,i in pairs({"X","Y","W","H"}) do
if not msg[i] or type(msg[i]) ~= "number" then
return
end
end
for _,i in pairs({"image","name","label"}) do
if not msg[i] or type(msg[i]) ~= "string" then
return
end
end
local field = {type="image_button",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,image=minetest.formspec_escape(msg.image),name=minetest.formspec_escape(msg.name),label=minetest.formspec_escape(msg.label)}
table.insert(data,field)
elseif msg.command == "addimage_button_exit" then
for _,i in pairs({"X","Y","W","H"}) do
if not msg[i] or type(msg[i]) ~= "number" then
return
end
end
for _,i in pairs({"image","name","label"}) do
if not msg[i] or type(msg[i]) ~= "string" then
return
end
end
local field = {type="image_button_exit",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,image=minetest.formspec_escape(msg.image),name=minetest.formspec_escape(msg.name),label=minetest.formspec_escape(msg.label)}
table.insert(data,field)
elseif msg.command == "adddropdown" then
for _,i in pairs({"X","Y","W","H","selected_id"}) do
if not msg[i] or type(msg[i]) ~= "number" then
return
end
end
if not msg.name or type(msg.name) ~= "string" then
return
end
if not msg.choices or type(msg.choices) ~= "table" or #msg.choices < 1 then
return
end
local field = {type="dropdown",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,name=minetest.formspec_escape(msg.name),selected_id=msg.selected_id,choices=msg.choices}
table.insert(data,field)
elseif msg.command == "addtextlist" then
for _,i in pairs({"X","Y","W","H","selected_id"}) do
if not msg[i] or type(msg[i]) ~= "number" then
return
end
end
if not msg.name or type(msg.name) ~= "string" then
return
end
if not msg.listelements or type(msg.listelements) ~= "table" or #msg.listelements < 1 then
return
end
if not msg.transparent or type(msg.transparent) ~= "boolean" then
msg.transparent = false
end
local field = {type="textlist",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,name=minetest.formspec_escape(msg.name),selected_id=msg.selected_id,listelements=msg.listelements,transparent=msg.transparent}
table.insert(data,field)
elseif msg.command == "lock" then
meta:set_int("locked",1)
elseif msg.command == "unlock" then
meta:set_int("locked",0)
end
return data
end
digistuff.ts_on_digiline_receive = function (pos, node, channel, msg)
local meta = minetest.get_meta(pos)
local setchan = meta:get_string("channel")
if channel ~= setchan then return end
if type(msg) ~= "table" then return end
local data = minetest.deserialize(meta:get_string("data")) or {}
if msg.command then
data = digistuff.process_command(meta,data,msg)
else
for _,i in ipairs(msg) do
if type(i) == "table" and i.command then
data = digistuff.process_command(meta,data,i) or data
end
end
end
meta:set_string("data",minetest.serialize(data))
digistuff.update_ts_formspec(pos)
end
minetest.register_node("digistuff:touchscreen", {
description = "Digilines Touchscreen",
groups = {cracky=3},
on_construct = function(pos)
digistuff.update_ts_formspec(pos,true)
end,
drawtype = "nodebox",
tiles = {
"digistuff_panel_back.png",
"digistuff_panel_back.png",
"digistuff_panel_back.png",
"digistuff_panel_back.png",
"digistuff_panel_back.png",
"digistuff_ts_front.png"
},
paramtype = "light",
paramtype2 = "facedir",
node_box = {
type = "fixed",
fixed = {
{ -0.5, -0.5, 0.4, 0.5, 0.5, 0.5 }
}
},
_digistuff_channelcopier_fieldname = "channel",
_digistuff_channelcopier_onset = function(pos)
minetest.get_meta(pos):set_int("init",1)
digistuff.update_ts_formspec(pos)
end,
on_receive_fields = digistuff.ts_on_receive_fields,
digiline =
{
receptor = {},
effector = {
action = digistuff.ts_on_digiline_receive
},
},
})
minetest.register_craft({
output = "digistuff:touchscreen",
recipe = {
{"mesecons_luacontroller:luacontroller0000","default:glass","default:glass"},
{"default:glass","digilines:lcd","default:glass"},
{"default:glass","default:glass","default:glass"}
}
})

View File

@ -1,179 +0,0 @@
# Overview
Dreambuilder is my attempt to give the player pretty much everything they'll ever want to build with, and all the tools they should ever need to actually get the job done. This modpack was, for most of its life, maintained as a subgame based on minetest_game, minus a couple of mods that I don't like, with a number of minor things changed, and a number of extra mods added on. Since then, many things have changed, from game content to packaging format. Read on!
This modpack is in use on my Creative server and on my Survival server, which also has a few extra mods installed for its specific needs. It should give you a pretty good idea nonetheless. Expect lag, as it's a significantly-developed multiplayer server, after all.
##  
# What's in it? What's changed from the default stuff?
* Being that this was originally based on an old version of minetest_game, it retains the light-colored user interface theme despite having since been updated, because I don't like feeling like I live in a cave when I open a formspec.
* The complete Plantlife Modpack along with More Trees and Vines mods add a huge amount of variation to your landscape (as a result, they will add mapgen lag). Active spawning of Horsetail ferns is disabled by default, and I've added papyrus growth on dirt/grass with leaves (using a copy of the default growth ABM).
* This modpack includes RealBadAngel's Unified Inventory mod, which overrides minetest_game's default inventory to give you a much more powerful user interface, with crafting guide, bags, and more, and it also means that if you're using this modpack in creative mode, your stacks are NOT infinite (and they shouldn't be).
* The default bones and TNT mods have been disabled (by way of "empty" mods, to trick the dependency resolver into skipping the real ones). They're not exactly useful for building stuff with.
* Stu's split-limb player model replaces the default one.
* The default hotbar HUD holds 16 items instead of 8, taken from the top two rows of your inventory. The first 10 slots can be accessed by number keys 1-9 and 0, the rest via your mouse wheel. You can use `/hotbar ##` to change the number of slots from 1 to 23.
* The default lavacooling code has been supplanted by better, safer code from my Gloopblocks mod. That mod also provides stone/cobble --> mossy stone/cobble transformation in the presence of water.
* An extensive selection of administration tools for single-player and server use are included, such as areas, maptools, worldedit, xban, and more.
* A few textures here and there are different.
* The mapgen won't spawn apples on default trees, nor will they appear on a sapling-grown default tree. Only the *real* apple trees supplied by the Moretrees mod will bear apples (both at mapgen time and sapling-grown). Or at least that's how it's supposed to work. :stuck_out_tongue: While on that subject, apples now use a 3d model instead of the plantlike version.
##  
# Okay, what else?
A whole boatload of other mods have been added, which is where most of the content actually comes from. To be a little more specific, as of August 2017, this modpack has a total of 165 mods (counting all of the various sub-mods that themselves come as part of some modpack, such as mesecons or home decor) and supplies almost 2000 items in the inventory/craft guide (almost 14,300 unique items in total, counting everything that isn't displayed in the inventory)! A mostly-complete list of mods is as follows:
areas
arrowboards
bedrock
bees
biome_lib
blox
bobblocks (without the traps or mesecons support)
campfire
castles++ (Philipbenr's re-fork, without the "orbs")
caverealms
coloredwood
colormachine
cottages
currency
datastorage
digilines
digistuff
display_blocks
farming_plus
framedglass
gardening
gloopblocks
glooptest (without treasure chests)
ilights
inventory_sorter
invsaw
item_tweaks
locks
maptools
markers
memorandum
moreblocks
moreores
moretrees
nixie_tubes
notice
peaceful_npc
pipeworks
plasticbox
player_textures (cheapie's fork, with several default skins)
prefab_redo
quartz
replacer
rgblightstone
signs_lib
solidcolor
stained_glass
teleport_request
titanium
travelnet
unifiedbricks (bucket dependency removed)
unifieddyes
unified_inventory
unifiedmesecons
vines
windmill
xban2
The full Home Decor modpack
The full Technic modpack
The full Plantlife modpack
Cheapie's Roads modpack
Zeg9's Steel modpack
Zeg9's UFO modpack
The full Mesecons modpack
Jeija's Jumping modpack
The full Worldedit modpack
### Your Inventory Display
This modpack, as previously mentioned, replaces the standard inventory with Unified Inventory, which almost defies description here. Unified Inventory includes waypoints, a crafting guide, set/go home buttons, set day/set night buttons, a full creative inventory on the right if you're playing in that mode - and you only have to click/tap the item once to get the it, instead of multiple clicks/drag and drop, a trash slot, a clear all inventory button, a search feature for the inventory, and more. Basically, you just need to use it a few times and you'll find yourself wondering how you ever got along with the standard inventory!
### The Circular Saw
This modpack uses the More Blocks mod, which comes with the Stairsplus mod and more importantly, the Circular Saw mod by Sokomine and co. This mod replaces the traditional method of creating stairs, slabs, and the like: rather that crafting a stairs block by placing several of the material into your crafting grid, you must first craft a circular saw (really, a table saw), place that on the ground, and then use that to shape the material you had in mind. It can create dozens of shapes, including the standard stairs and slabs. Give it a try and see for yourself!
### Land Ownership
This modpack uses ShadowNinja's areas mod for land protection, as well as cheapie's protector blocks. Of course, land protection is only useful if you're using this Modpack on a public server.
#### Protection blocks:
These are easy. Craft one, place it, and everything within 15m of it becomes yours immediately (if someone else doesn't own some of the land therein, of course). If you dig one of these, the area protection it created is removed; if you shift-dig, the protection is preserved, but the block is deleted, giving back some steel ingots if you're in survival mode, or nothing at all if in creative mode.
#### Areas:
If you want fine control, use the areas mod's commands. There are three ways to select a region to protect:
**Option A:**
Just type `/area_pos set` and then punch two nodes that are diagonally opposite one another, so that they form a 3d box that fully encloses the area you want to claim. A black **`[1]`** or **`[2]`** will appear where you punched.
**Option B:**
1. Move to one corner, on the ground.
2. Type `/area_pos1` and press enter. A black **`[1]`** will appear.
3. Move to the other corner and go up a ways above your area - not too high though.
4. Type `/area_pos2` and press enter. A black **`[2]`** will appear.
**Option C:**
Just give actual coordinates to the `/area_xxx` commands, e.g.:
`/area_pos1 123,45,678 /area_pos2 987,654,321`
**Claim it:**
Once you've marked your area using one of the above methods, you must actually claim it. This is the step that actually protects it against vandalism and unauthorized access. Just type:
/protect some description here
By default, users may protect up to 3 zones with these commands, and each can be up to 50x100x50 meters in size, but this can be changed by plugging appropriate settings into your minetest.conf.
**Sublet it:**
Ok, you've claimed an area, and you want to let someone else build there. Simple. Set the coordinates of the box you want to let them build in, using the commands above (Options A, B, or C). Then do:
`/add_owner your_area# their_name description here`
For example, if you own area #123 and the other person's name is "Mike", and you want to sublet them some area called "Mike's home", you might do something like this:
`/add_owner 123 Mike Mikes Home`
You can add as many users as you like to your areas. You will need to issue one such command per user.
## Dependencies:
This modpack requires Minetest 0.4.16 or later, and a corresponding copy of minetest_game. Anything too old will likely either crash, show nodes with the wrong shape or colors, throw nonsensical warnings/errors, or open up wormholes.
## Hardware requirements:
This modpack defines a very large number of items and produces a well-detailed landscape, and so it requires a significant amount of resources compared to vanilla Minetest game. At least a 2 GHz dual core CPU and 2 GB free RAM are required for good performance. If you use my HDX texture pack, you'll need more RAM (at least 4 GB free recommended).
This modpack is NOT intended for use on mobile devices.
## Download/Install:
...if you're reading this, you're either on the Dreambuilder Github repo page, so clone it from there, or download the ZIP... or maybe you already have it. ;-) You can also fetch it from [url]https://daconcepts.com/vanessa/hobbies/minetest/Dreambuilder_Modpack.tar.bz2[/url]
Just rename the project folder to "dreambuilder_modpack", if necessary, and move it to your Minetest mods directory. Then select and enable it for the world you want to use it in. Depending on the condition of the world you are using, and for brand new maps, this modpack may take a minute or two to start, during which time you may see the hotbar and hand, all-grey window content where the world should be, or other odd-looking things. Just wait it out, it will eventually start and settle down.
## License:
Each of the base mods in this modpack retains the standard license that its author has assigned, even if the license file is missing from the archive. All changes and any supplemental content made by me is WTFPL unless explicitly stated otherwise.
# Open Source Software
This modpack is open source, or at least as much so as I have control over. Since it started from the standard minetest_game distribution, you'll want to look at that. You can find it at its usual Github repository, here:
[url]https://github.com/minetest/minetest_game[/url]
An online copy of the archive of mods the modpack is built from can be found here (with full git histories, including my changes and any files that went missing from the completed modpack, where applicable):
[url]http://minetest.daconcepts.com/my-main-mod-archive/[/url]
# Notes:
For best results, I recommend adding the following to your minetest.conf (perhaps to a secondary copy of that file that you only use when playing worlds with this modpack):
```
enable_item_drops = false
enable_item_pickup = true
remove_items = -1
disable_fire = true
enable_mesh_cache = false
```

View File

@ -1,478 +0,0 @@
local S = farming.intllib
--= Sugar
minetest.register_craftitem("farming:sugar", {
description = S("Sugar"),
inventory_image = "farming_sugar.png",
groups = {food_sugar = 1, flammable = 3}
})
minetest.register_craft({
type = "cooking",
cooktime = 3,
output = "farming:sugar 2",
recipe = "default:papyrus"
})
--= Salt
minetest.register_node("farming:salt", {
description = S("Salt"),
inventory_image = "farming_salt.png",
wield_image = "farming_salt.png",
drawtype = "plantlike",
visual_scale = 0.8,
paramtype = "light",
tiles = {"farming_salt.png"},
groups = {food_salt = 1, vessel = 1, dig_immediate = 3,
attached_node = 1},
sounds = default.node_sound_defaults(),
selection_box = {
type = "fixed",
fixed = {-0.25, -0.5, -0.25, 0.25, 0.3, 0.25}
},
-- special function to make salt crystals form inside water
dropped_step = function(self, pos, dtime)
self.ctimer = (self.ctimer or 0) + dtime
if self.ctimer < 15.0 then return end
self.ctimer = 0
local needed
if self.node_inside
and self.node_inside.name == "default:water_source" then
needed = 8
elseif self.node_inside
and self.node_inside.name == "default:river_water_source" then
needed = 9
end
if not needed then return end
local objs = core.get_objects_inside_radius(pos, 0.5)
if not objs or #objs ~= 1 then return end
local salt, ent = nil, nil
for k, obj in pairs(objs) do
ent = obj:get_luaentity()
if ent and ent.name == "__builtin:item"
and ent.itemstring == "farming:salt " .. needed then
obj:remove()
core.add_item(pos, "farming:salt_crystal")
return false -- return with no further action
end
end
end
})
minetest.register_craft({
type = "cooking",
cooktime = 15,
output = "farming:salt",
recipe = "bucket:bucket_water",
replacements = {{"bucket:bucket_water", "bucket:bucket_empty"}}
})
--= Salt Crystal
minetest.register_node("farming:salt_crystal", {
description = ("Salt crystal"),
inventory_image = "farming_salt_crystal.png",
wield_image = "farming_salt_crystal.png",
drawtype = "plantlike",
visual_scale = 0.8,
paramtype = "light",
light_source = 1,
tiles = {"farming_salt_crystal.png"},
groups = { dig_immediate = 3, attached_node = 1},
sounds = default.node_sound_defaults(),
selection_box = {
type = "fixed",
fixed = {-0.25, -0.5, -0.25, 0.25, 0.3, 0.25}
},
})
minetest.register_craft({
type = "shapeless",
output = "farming:salt 9",
recipe = {"farming:salt_crystal", "farming:mortar_pestle"},
replacements = {{"farming:mortar_pestle", "farming:mortar_pestle"}}
})
minetest.register_craft({
output = "farming:salt_crystal",
recipe = {
{"farming:salt", "farming:salt", "farming:salt"},
{"farming:salt", "farming:salt", "farming:salt"},
{"farming:salt", "farming:salt", "farming:salt"}
}
})
--= Rose Water
minetest.register_node("farming:rose_water", {
description = S("Rose Water"),
inventory_image = "farming_rose_water.png",
wield_image = "farming_rose_water.png",
drawtype = "plantlike",
visual_scale = 0.8,
paramtype = "light",
tiles = {"farming_rose_water.png"},
groups = {food_rose_water = 1, vessel = 1, dig_immediate = 3,
attached_node = 1},
sounds = default.node_sound_defaults(),
selection_box = {
type = "fixed",
fixed = {-0.25, -0.5, -0.25, 0.25, 0.3, 0.25}
}
})
minetest.register_craft({
output = "farming:rose_water",
recipe = {
{"flowers:rose", "flowers:rose", "flowers:rose"},
{"flowers:rose", "flowers:rose", "flowers:rose"},
{"group:water_bucket", "group:food_pot", "vessels:glass_bottle"}
},
replacements = {
{"group:water_bucket", "bucket:bucket_empty"},
{"group:food_pot", "farming:pot"}
}
})
if minetest.get_modpath("bucket_wooden") then
minetest.register_craft({
output = "farming:rose_water",
recipe = {
{"flowers:rose", "flowers:rose", "flowers:rose"},
{"flowers:rose", "flowers:rose", "flowers:rose"},
{"group:water_bucket_wooden", "group:food_pot", "vessels:glass_bottle"}
},
replacements = {
{"group:water_bucket_wooden", "bucket_wooden:bucket_empty"},
{"group:food_pot", "farming:pot"}
}
})
end
--= Turkish Delight
minetest.register_craftitem("farming:turkish_delight", {
description = S("Turkish Delight"),
inventory_image = "farming_turkish_delight.png",
groups = {flammable = 3},
on_use = minetest.item_eat(2)
})
minetest.register_craft({
output = "farming:turkish_delight 4",
recipe = {
{"group:food_gelatin", "group:food_sugar", "group:food_gelatin"},
{"group:food_sugar", "group:food_rose_water", "group:food_sugar"},
{"group:food_cornstarch", "group:food_sugar", "dye:pink"}
},
replacements = {
{"group:food_cornstarch", "farming:bowl"},
{"group:food_rose_water", "vessels:glass_bottle"}
}
})
--= Garlic Bread
minetest.register_craftitem("farming:garlic_bread", {
description = S("Garlic Bread"),
inventory_image = "farming_garlic_bread.png",
groups = {flammable = 3},
on_use = minetest.item_eat(2)
})
minetest.register_craft({
type = "shapeless",
output = "farming:garlic_bread",
recipe = {"group:food_toast", "group:food_garlic_clove", "group:food_garlic_clove"}
})
--= Donuts (thanks to Bockwurst for making the donut images)
minetest.register_craftitem("farming:donut", {
description = S("Donut"),
inventory_image = "farming_donut.png",
on_use = minetest.item_eat(4)
})
minetest.register_craft({
output = "farming:donut 3",
recipe = {
{"", "group:food_wheat", ""},
{"group:food_wheat", "group:food_sugar", "group:food_wheat"},
{"", "group:food_wheat", ""}
}
})
minetest.register_craftitem("farming:donut_chocolate", {
description = S("Chocolate Donut"),
inventory_image = "farming_donut_chocolate.png",
on_use = minetest.item_eat(6)
})
minetest.register_craft({
output = "farming:donut_chocolate",
recipe = {
{"group:food_cocoa"},
{"farming:donut"}
}
})
minetest.register_craftitem("farming:donut_apple", {
description = S("Apple Donut"),
inventory_image = "farming_donut_apple.png",
on_use = minetest.item_eat(6)
})
minetest.register_craft({
output = "farming:donut_apple",
recipe = {
{"default:apple"},
{"farming:donut"}
}
})
--= Porridge Oats
minetest.register_craftitem("farming:porridge", {
description = S("Porridge"),
inventory_image = "farming_porridge.png",
on_use = minetest.item_eat(6, "farming:bowl")
})
minetest.register_craft({
type = "shapeless",
output = "farming:porridge",
recipe = {
"group:food_oats", "group:food_oats", "group:food_oats",
"group:food_oats", "group:food_bowl", "group:food_milk_glass"
},
replacements = {
{"mobs:glass_milk", "vessels:drinking_glass"},
{"farming:soy_milk", "vessels:drinking_glass"}
}
})
--= Jaffa Cake
minetest.register_craftitem("farming:jaffa_cake", {
description = S("Jaffa Cake"),
inventory_image = "farming_jaffa_cake.png",
on_use = minetest.item_eat(6)
})
minetest.register_craft({
type = "shapeless",
output = "farming:jaffa_cake",
recipe = {
"farming:baking_tray", "group:food_egg", "group:food_sugar",
"group:food_flour", "group:food_cocoa", "group:food_orange",
"group:food_milk"
},
replacements = {
{"farming:baking_tray", "farming:baking_tray"},
{"mobs:bucket_milk", "bucket:bucket_empty"}
}
})
-- Apple Pie
minetest.register_craftitem("farming:apple_pie", {
description = S("Apple Pie"),
inventory_image = "farming_apple_pie.png",
on_use = minetest.item_eat(6)
})
minetest.register_craft({
output = "farming:apple_pie",
type = "shapeless",
recipe = {
"group:food_flour", "group:food_sugar",
"group:food_apple", "group:food_baking_tray"
},
replacements = {{"group:food_baking_tray", "farming:baking_tray"}}
})
-- Cactus Juice
minetest.register_craftitem("farming:cactus_juice", {
description = S("Cactus Juice"),
inventory_image = "farming_cactus_juice.png",
groups = {vessel = 1, drink = 1},
on_use = function(itemstack, user, pointed_thing)
if user then
if math.random(5) == 1 then
return minetest.do_item_eat(-1, "vessels:drinking_glass",
itemstack, user, pointed_thing)
else
return minetest.do_item_eat(2, "vessels:drinking_glass",
itemstack, user, pointed_thing)
end
end
end
})
minetest.register_craft({
output = "farming:cactus_juice 2",
type = "shapeless",
recipe = {
"vessels:drinking_glass", "vessels:drinking_glass",
"default:cactus", "farming:juicer"
},
replacements = {
{"group:food_juicer", "farming:juicer"}
}
})
-- Pasta
minetest.register_craftitem("farming:pasta", {
description = S("Pasta"),
inventory_image = "farming_pasta.png",
groups = {food_pasta = 1}
})
if minetest.get_modpath("mobs_animal") or minetest.get_modpath("xanadu")then
minetest.register_craft({
output = "farming:pasta",
type = "shapeless",
recipe = {
"group:food_flour", "group:food_mixing_bowl",
"group:food_butter"
},
replacements = {{"group:food_mixing_bowl", "farming:mixing_bowl"}}
})
else
minetest.register_craft({
output = "farming:pasta",
type = "shapeless",
recipe = {
"group:food_flour", "group:food_mixing_bowl",
"group:food_oil"
},
replacements = {
{"group:food_mixing_bowl", "farming:mixing_bowl"},
{"group:food_oil", "vessels:glass_bottle"}
}
})
end
-- Spaghetti
minetest.register_craftitem("farming:spaghetti", {
description = S("Spaghetti"),
inventory_image = "farming_spaghetti.png",
on_use = minetest.item_eat(8)
})
minetest.register_craft({
output = "farming:spaghetti",
type = "shapeless",
recipe = {
"group:food_pasta", "group:food_saucepan",
"group:food_tomato", "group:food_garlic_clove", "group:food_garlic_clove"
},
replacements = {{"group:food_saucepan", "farming:saucepan"}}
})
-- Korean Bibimbap
minetest.register_craftitem("farming:bibimbap", {
description = S("Bibimbap"),
inventory_image = "farming_bibimbap.png",
on_use = minetest.item_eat(8, "farming:bowl")
})
if minetest.get_modpath("mobs_animal") or minetest.get_modpath("xanadu")then
minetest.register_craft({
output = "farming:bibimbap",
type = "shapeless",
recipe = {
"group:food_skillet", "group:food_bowl", "group:food_egg", "group:food_rice",
"group:food_chicken_raw", "group:food_cabbage", "group:food_carrot",
"group:food_chili_pepper"
},
replacements = {{"group:food_skillet", "farming:skillet"}}
})
else
minetest.register_craft({
output = "farming:bibimbap",
type = "shapeless",
recipe = {
"group:food_skillet", "group:food_bowl", "group:food_mushroom",
"group:food_rice", "group:food_cabbage", "group:food_carrot",
"group:food_mushroom", "group:food_chili_pepper"
},
replacements = {{"group:food_skillet", "farming:skillet"}}
})
end
-- Burger
minetest.register_craftitem("farming:burger", {
description = S("Burger"),
inventory_image = "farming_burger.png",
on_use = minetest.item_eat(16),
})
minetest.register_craft({
type = "shapeless",
output = "farming:burger",
recipe = {
"farming:bread", "group:food_meat", "group:food_cheese",
"group:food_tomato", "group:food_cucumber", "group:food_onion",
"group:food_lettuce"
}
})
-- Salad
minetest.register_craftitem("farming:salad", {
description = S("Salad"),
inventory_image = "farming_salad.png",
on_use = minetest.item_eat(8, "farming:bowl")
})
minetest.register_craft({
output = "farming:salad",
type = "shapeless",
recipe = {
"group:food_bowl", "group:food_tomato", "group:food_cucumber",
"group:food_lettuce", "group:food_oil"
},
})
-- Triple Berry Smoothie
minetest.register_craftitem("farming:smoothie_berry", {
description = S("Triple Berry Smoothie"),
inventory_image = "farming_berry_smoothie.png",
on_use = minetest.item_eat(6, "vessels:drinking_glass"),
groups = {vessel = 1, drink = 1}
})
minetest.register_craft({
output = "farming:smoothie_berry",
type = "shapeless",
recipe = {
"group:food_raspberries", "group:food_blackberries",
"group:food_strawberry", "group:food_banana",
"vessels:drinking_glass"
}
})

View File

@ -1,119 +0,0 @@
Seed=种子
Banana=香蕉
Banana Leaves=香蕉叶
Orange=橙色
Strawberry=草莓
Sugar=糖
Salt=盐
Rose Water=玫瑰汁
Turkish Delight=土耳其软糖
Garlic Bread=蒜香面包
Donut=甜甜圈
Chocolate Donut=巧克力甜甜圈
Apple Donut=苹果甜甜圈
Porridge=粥
Jaffa Cake=佳发饼
Hoe=锄头
Wooden Hoe=木锄
Stone Hoe=石锄
Steel Hoe=钢锄头
Bronze Hoe=青铜锄头
Mese Hoe=黄石锄头
Diamond Hoe=钻石锄
Hoe Bomb (use or throw on grassy areas to hoe land)=锄弹(在草地上使用或扔在锄地上)
Mithril Scythe (Right-click to harvest and replant crops)=秘银镰刀(右击可收获并重新种植作物)
Soil=土壤
Wet Soil=湿土
Wooden Bowl=木碗
Saucepan=平底锅
Cooking Pot=锅
Baking Tray=烤盘
Skillet=平底锅
Mortar and Pestle=研钵
Cutting Board=砧板
Juicer=榨汁机
Glass Mixing Bowl=搅拌杯
Barley Seed=大麦种子
Barley=大麦
Green Beans=青豆
Bean Pole (place on soil before planting beans)=豆杆(种豆前先放在土上)
Beetroot=甜菜根
Beetroot Soup=甜菜根汤
Blueberries=蓝莓
Blueberry Muffin=蓝莓松糕
Blueberry Pie=蓝莓派
Carrot=胡萝卜
Carrot Juice=胡萝卜汁
Golden Carrot=金萝卜
Chili Pepper=辣椒
Bowl of Chili=一碗辣椒
Cocoa Beans=可可豆
Cookie=曲奇
Bar of Dark Chocolate=黑巧克力条
Chocolate Block=巧克力块
Coffee Beans=咖啡豆
Cup of Coffee=一杯咖啡
Corn=玉米
Corn on the Cob=玉米棒
Cornstarch=玉米淀粉
Bottle of Ethanol=一瓶乙醇
Cotton Seed=棉籽
Cotton=棉花
String=字符串
Cucumber=黄瓜
Garlic clove=蒜瓣
Garlic=大蒜
Garlic Braid=蒜辫
Grapes=葡萄
Trellis (place on soil before planting grapes)=棚架(种植葡萄前先放在土壤上)
Hemp Seed=大麻籽
Hemp Leaf=大麻叶
Bottle of Hemp Oil=一瓶大麻油
Hemp Fibre=大麻纤维
Hemp Block=麻块
Hemp Rope=麻绳
Melon Slice=西瓜片
Melon=甜瓜
Onion=洋葱
Pea Pod=豌豆荚
Peas=豌豆
Pea Soup=豌豆汤
Peppercorn=胡椒粉
Pepper=胡椒粉
Ground Pepper=胡椒粉
Pineapple Top=菠萝上衣
Pineapple=菠萝
Pineapple Ring=菠萝圈
Pineapple Juice=菠萝汁
Potato=土豆
Baked Potato=焗马铃薯
Cucumber and Potato Salad=黄瓜土豆沙拉
Pumpkin Slice=南瓜片
Jack 'O Lantern (punch to turn on and off)=杰克灯(按一下开关)
Scarecrow Bottom=稻草人屁股
Pumpkin Bread=南瓜面包
Pumpkin Dough=南瓜面团
Pumpkin=南瓜
Raspberries=覆盆子
Raspberry Smoothie=覆盆子冰沙
Rhubarb=大黄
Rhubarb Pie=大黄派
Rye=黑麦
Rye seed=黑麦种子
Oat=燕麦
Oat seed=燕麦籽
Rice=大米
Rice grains=稻谷
Rice Bread=米饭面包
Rice Flour=米粉
Multigrain Flour=多粒面粉
Multigrain Bread=杂粮面包
Tomato=番茄
Wheat Seed=小麦种子
Wheat=小麦
Straw=稻草
Flour=面粉
Bread=面包
Sliced Bread=切片面包
Toast=烤面包片
Toast Sandwich=三明治面包

3
game.conf Normal file
View File

@ -0,0 +1,3 @@
name = Dreambuilder Game
author = Vanessa Dannenberg
description = Dreambuilder is my attempt to give the player pretty much everything they could ever want to build with, and all the tools they should need to actually get the job done. It is based on minetest_game, the base game content package maintained by Minetest engine game developers, and should be compatible with mods written for it.

1084
game_api.txt Normal file

File diff suppressed because it is too large Load Diff

BIN
menu/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

BIN
menu/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

6
minetest.conf Normal file
View File

@ -0,0 +1,6 @@
enable_item_drops = false
enable_item_pickup = true
remove_items = -1
disable_fire = true
enable_mesh_cache = false
moreblocks.stairsplus_in_creative_inventory = false

77
minetest.conf.example Normal file
View File

@ -0,0 +1,77 @@
# This file contains settings of Dreambuilder Game that can be changed in
# minetest.conf.
# By default, all the settings are commented and not functional.
# Uncomment settings by removing the preceding #.
# Whether creative mode (fast digging of all blocks, unlimited resources) should
# be enabled.
#creative_mode = false
# Sets the behaviour of the inventory items when a player dies.
# bones: Store items in a bone node but drop items if inside protected area.
# drop: Drop items on the ground.
# keep: Player keeps items.
#bones_mode = bones
# The time in seconds after which the bones of a dead player can be looted by
# everyone.
# 0 to disable.
#share_bones_time = 1200
# How much earlier the bones of a dead player can be looted by
# everyone if the player dies in a protected area they don't own.
# 0 to disable. By default it is "share_bones_time" divide by four.
#share_bones_time_early = 300
# Inform player of condition and location of new bones.
#bones_position_message = false
# Whether fire should be enabled. If disabled, 'basic_flame' nodes will
# disappear.
# 'permanent_flame' nodes will remain with either setting.
#enable_fire = true
# Enable flame sound.
#flame_sound = true
# Whether lavacooling should be enabled.
#enable_lavacooling = true
# Whether the stuff in initial_stuff should be given to new players.
#give_initial_stuff = false
#initial_stuff = default:pick_steel,default:axe_steel,default:shovel_steel,
default:torch 99,default:cobble 99
# Whether the TNT mod should be enabled.
#enable_tnt = <true in singleplayer, false in multiplayer>
# The radius of a TNT explosion.
#tnt_radius = 3
# Enable the stairs mod ABM that replaces the old 'upside down'
# stair and slab nodes in old maps with the new param2 versions.
#enable_stairs_replace_abm = false
# Whether to allow respawning in beds.
# Default value is true.
#enable_bed_respawn = true
# Whether players can skip night by sleeping.
# Default value is true.
#enable_bed_night_skip = true
# If enabled, fences and walls cannot be jumped over.
#enable_fence_tall = false
# Whether the engine's spawn search, which does not check for a suitable
# starting biome, is used.
# Default value is false.
#engine_spawn = false
# Whether river water source nodes create flowing sounds.
# Helps rivers create more sound, especially on level sections.
#river_source_sounds = false
# Enable cloud variation by the 'weather' mod.
# Non-functional in V6 or Singlenode mapgens.
#enable_weather = true

View File

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 234 B

After

Width:  |  Height:  |  Size: 234 B

View File

Before

Width:  |  Height:  |  Size: 439 B

After

Width:  |  Height:  |  Size: 439 B

View File

Before

Width:  |  Height:  |  Size: 305 B

After

Width:  |  Height:  |  Size: 305 B

View File

Before

Width:  |  Height:  |  Size: 74 B

After

Width:  |  Height:  |  Size: 74 B

View File

Before

Width:  |  Height:  |  Size: 305 B

After

Width:  |  Height:  |  Size: 305 B

View File

Before

Width:  |  Height:  |  Size: 382 B

After

Width:  |  Height:  |  Size: 382 B

View File

Before

Width:  |  Height:  |  Size: 314 B

After

Width:  |  Height:  |  Size: 314 B

View File

Before

Width:  |  Height:  |  Size: 209 B

After

Width:  |  Height:  |  Size: 209 B

View File

Before

Width:  |  Height:  |  Size: 543 B

After

Width:  |  Height:  |  Size: 543 B

View File

Before

Width:  |  Height:  |  Size: 222 B

After

Width:  |  Height:  |  Size: 222 B

View File

Before

Width:  |  Height:  |  Size: 224 B

After

Width:  |  Height:  |  Size: 224 B

View File

Before

Width:  |  Height:  |  Size: 226 B

After

Width:  |  Height:  |  Size: 226 B

View File

Before

Width:  |  Height:  |  Size: 178 B

After

Width:  |  Height:  |  Size: 178 B

View File

Before

Width:  |  Height:  |  Size: 222 B

After

Width:  |  Height:  |  Size: 222 B

View File

Before

Width:  |  Height:  |  Size: 404 B

After

Width:  |  Height:  |  Size: 404 B

View File

Before

Width:  |  Height:  |  Size: 429 B

After

Width:  |  Height:  |  Size: 429 B

View File

Before

Width:  |  Height:  |  Size: 424 B

After

Width:  |  Height:  |  Size: 424 B

View File

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 427 B

View File

Before

Width:  |  Height:  |  Size: 308 B

After

Width:  |  Height:  |  Size: 308 B

View File

Before

Width:  |  Height:  |  Size: 411 B

After

Width:  |  Height:  |  Size: 411 B

View File

Before

Width:  |  Height:  |  Size: 543 B

After

Width:  |  Height:  |  Size: 543 B

329
mods/ambience/init.lua Normal file
View File

@ -0,0 +1,329 @@
ambience = {}
-- override default water sounds
minetest.override_item("default:water_source", { sounds = {} })
minetest.override_item("default:water_flowing", { sounds = {} })
minetest.override_item("default:river_water_source", { sounds = {} })
minetest.override_item("default:river_water_flowing", { sounds = {} })
-- settings
local SOUNDVOLUME = 1.0
local MUSICVOLUME = 1.0
local play_music = minetest.settings:get_bool("ambience_music") ~= false
local pplus = minetest.get_modpath("playerplus")
local radius = 6
local playing = {}
local sound_sets = {} -- all the sounds and their settings
local sound_set_order = {} -- needed because pairs loops randomly through tables
local set_nodes = {} -- all the nodes needed for sets
-- global functions
-- add set to list
ambience.add_set = function(set_name, def)
if not set_name or not def then
return
end
sound_sets[set_name] = {
frequency = def.frequency or 50,
sounds = def.sounds,
sound_check = def.sound_check,
nodes = def.nodes
}
-- add set name to the sound_set_order table
local can_add = true
for i = 1, #sound_set_order do
if sound_set_order[i] == set_name then
can_add = false
end
end
if can_add then
table.insert(sound_set_order, set_name)
end
-- add any missing nodes to the set_nodes table
if def.nodes then
for i = 1, #def.nodes do
can_add = def.nodes[i]
for j = 1, #set_nodes do
if def.nodes[i] == set_nodes[j] then
can_add = false
end
end
if can_add then
table.insert(set_nodes, can_add)
end
end
end
end
-- return set from list using name
ambience.get_set = function(set_name)
if sound_sets[set_name] then
return sound_sets[set_name]
end
end
-- remove set from list
ambience.del_set = function(set_name)
sound_sets[set_name] = nil
local can_del = false
for i = 1, #sound_set_order do
if sound_set_order[i] == set_name then
can_del = i
end
end
if can_del then
table.remove(sound_set_order, can_del)
end
end
-- plays music and selects sound set
local get_ambience = function(player, tod, name)
-- play server or local music if available
if play_music and playing[name] then
-- play at midnight
if tod >= 0.0 and tod <= 0.01 then
if not playing[name].music then
playing[name].music = minetest.sound_play("ambience_music", {
to_player = player:get_player_name(),
gain = MUSICVOLUME
})
end
elseif tod > 0.1 and playing[name].music then
playing[name].music = nil
end
end
-- get foot and head level nodes at player position
local pos = player:get_pos() ; if not pos then return end
pos.y = pos.y + 1.4 -- head level
local nod_head = pplus and name and playerplus[name].nod_head or
minetest.get_node(pos).name
pos.y = pos.y - 1.2 -- foot level
local nod_feet = pplus and name and playerplus[name].nod_feet or
minetest.get_node(pos).name
pos.y = pos.y - 0.2 -- reset pos
-- get all set nodes around player
local ps, cn = minetest.find_nodes_in_area(
{x = pos.x - radius, y = pos.y - radius, z = pos.z - radius},
{x = pos.x + radius, y = pos.y + radius, z = pos.z + radius}, set_nodes)
-- loop through sets in order and choose first that meets it's conditions
for n = 1, #sound_set_order do
local set = sound_sets[ sound_set_order[n] ]
if set and set.sound_check then
-- pass settings to function for condition check
local set_name, gain = set.sound_check({
player = player,
pos = pos,
tod = tod,
totals = cn,
positions = ps,
head_node = nod_head,
feet_node = nod_feet
})
-- if conditions met return set name and gain value
if set_name then
return set_name, gain
end
end
end
end
local timer = 0
local random = math.random
-- players routine
minetest.register_globalstep(function(dtime)
-- one second timer
timer = timer + dtime
if timer < 1 then return end
timer = 0
-- get list of players and set some variables
local players = minetest.get_connected_players()
local player_name, number, chance, ambience, handler, ok
local tod = minetest.get_timeofday()
-- loop through players
for n = 1, #players do
player_name = players[n]:get_player_name()
--local t1 = os.clock()
local set_name, MORE_GAIN = get_ambience(players[n], tod, player_name)
--print(string.format("elapsed time: %.4f\n", os.clock() - t1))
ok = true -- everything starts off ok
-- stop current sound if another set active or gain changed
if playing[player_name]
and playing[player_name].handler then
if playing[player_name].set ~= set_name
or (playing[player_name].set == set_name
and playing[player_name].gain ~= MORE_GAIN) then
--print ("-- change stop", set_name, playing[player_name].old_handler)
minetest.sound_stop(playing[player_name].old_handler)
playing[player_name].set = nil
playing[player_name].handler = nil
playing[player_name].gain = nil
else
ok = false -- sound set still playing, skip new sound
end
end
-- set random chance and reset seed
chance = random(1, 1000)
math.randomseed(tod + chance)
-- if chance is lower than set frequency then select set
if ok and set_name and chance < sound_sets[set_name].frequency then
-- choose random sound from set
number = random(#sound_sets[set_name].sounds)
ambience = sound_sets[set_name].sounds[number]
-- play sound
handler = minetest.sound_play(ambience.name, {
to_player = player_name,
gain = ((ambience.gain or 0.3) + (MORE_GAIN or 0)) * SOUNDVOLUME,
pitch = ambience.pitch or 1.0
}, ambience.ephemeral)
--print ("playing... " .. ambience.name .. " (" .. chance .. " < "
-- .. sound_sets[set_name].frequency .. ") @ ", MORE_GAIN, handler)
-- only continue if sound playing returns handler
if handler then
--print("-- current handler", handler)
-- set what player is currently listening to
playing[player_name] = {
set = set_name, gain = MORE_GAIN,
handler = handler, old_handler = handler
}
-- set timer to stop sound
minetest.after(ambience.length, function()
--print("-- after", set_name, handler)
-- make sure we are stopping same sound we started
if playing[player_name]
and playing[player_name].handler
and playing[player_name].old_handler == handler then
--print("-- timed stop", set_name, handler)
--minetest.sound_stop(playing[player_name].handler)
minetest.sound_stop(handler)
-- reset player variables and backup handler
playing[player_name] = {
set = nil, gain = nil,
handler = nil, old_handler = nil
}
end
end)
end
end
end
end)
-- sound volume command
minetest.register_chatcommand("svol", {
params = "<svol>",
description = "set sound volume (0.1 to 1.0)",
privs = {server = true},
func = function(name, param)
SOUNDVOLUME = tonumber(param) or SOUNDVOLUME
if SOUNDVOLUME < 0.1 then SOUNDVOLUME = 0.1 end
if SOUNDVOLUME > 1.0 then SOUNDVOLUME = 1.0 end
return true, "Sound volume set to " .. SOUNDVOLUME
end,
})
-- music volume command (0 stops music)
minetest.register_chatcommand("mvol", {
params = "<mvol>",
description = "set music volume (0.1 to 1.0)",
privs = {server = true},
func = function(name, param)
MUSICVOLUME = tonumber(param) or MUSICVOLUME
-- ability to stop music just as it begins
if MUSICVOLUME == 0 and playing[name].music then
minetest.sound_stop(playing[name].music)
end
if MUSICVOLUME < 0.1 then MUSICVOLUME = 0.1 end
if MUSICVOLUME > 1.0 then MUSICVOLUME = 1.0 end
return true, "Music volume set to " .. MUSICVOLUME
end,
})
-- load default sound sets
dofile(minetest.get_modpath("ambience") .. "/soundsets.lua")
print("[MOD] Ambience Lite loaded")

View File

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Some files were not shown because too many files have changed in this diff Show More