replace protector with areas and marker

This commit is contained in:
Elkien3 2020-09-28 20:58:41 -05:00
parent db35bc0294
commit 8306acd727
73 changed files with 4255 additions and 3071 deletions

View File

@ -0,0 +1,17 @@
name: luacheck
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: apt
run: sudo apt-get install -y luarocks
- name: luacheck install
run: luarocks install --local luacheck
- name: luacheck run
run: $HOME/.luarocks/bin/luacheck ./

2
mods/areas/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*~

24
mods/areas/.luacheckrc Normal file
View File

@ -0,0 +1,24 @@
unused_args = false
read_globals = {
"DIR_DELIM",
"core",
"dump",
"vector", "nodeupdate",
"VoxelManip", "VoxelArea",
"PseudoRandom", "ItemStack",
"AreaStore",
"default",
"factions",
table = { fields = { "copy", "getn", "indexof" } }
}
globals = {
"minetest",
-- mod namespace
"areas"
}
files["legacy.lua"] = {
ignore = {"512"}
}

502
mods/areas/LICENSE.txt Normal file
View File

@ -0,0 +1,502 @@
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!

127
mods/areas/README.md Normal file
View File

@ -0,0 +1,127 @@
Areas mod for Minetest
======================
Dependencies
------------
Minetest 5.0.0+ is recommended, but 0.4.16+ should work as well.
Minetest 5.0.0+
Optional support for following mods:
* [playerfactions](https://git.leagueh.xyz/katp32/playerfactions/) by [katp32](https://git.leagueh.xyz/katp32) & [Kalio_42](https://git.leagueh.xyz/Kalio_42)
Configuration
-------------
Open the tab `Settings -> All Settings -> Mods -> areas` to get a list of all
possible settings.
For server owners: Check `settingtypes.txt` and modify your `minetest.conf`
according to the wanted setting changes.
Tutorial
--------
1) Specify the corner positions of the area you would like to protect.
Use one of the following commands:
* `/area_pos set` and punch the two corner nodes to set them.
* `/area_pos set1/set2` and punch only the first or second corner node to
set them one at a time.
* `/area_pos1/2` to set one of the positions to your current position.
* `/area_pos1/2 X Y Z` to set one of the positions to the specified
coordinates.
2) Protect the selected area by running one of the following commands:
* `/set_owner <OwnerName> <AreaName>` -- If you have the `areas` privilege.
* `/protect <AreaName>` -- If you have the `areas` privilege or the server
administrator has enabled area self-protection.
The area name is used only for informational purposes and has no functional
importance.
For example: `/set_owner SomePlayer Mese city`
3) You now own an area. You may now add sub-owners to it if you want to (see command `/add_owner`). Before using the `/add_owner` command you have to
select the corners of the sub-area as you did in step 1.
If your markers are still around your original area and you want to grant
access to your entire area you will not have to re-set them. Use `/select_area` to place the markers at the corners of an existing area if you've reset your
markers and want to grant access to a full area.
The `/add_owner` command expects three arguments:
1. The ID number of the parent area (the area that you want to add a
sub-area to).
2. The name of the player that will own the sub-area.
3. The name of the sub-area. (can contain spaces)
For example: `/add_owner 123 BobTheBuilder Diamond lighthouse`
Commands
--------
* `/protect <AreaName>` -- Protects an area for yourself. (if
self-protection is enabled)
* `/set_owner <OwnerName> <AreaName>` -- Protects an area for a specified
player. (requires the `areas` privilege)
* `/add_owner <ParentID> <OwnerName> <ChildName>` -- Grants another player
control over part (or all) of an area.
* `/rename_area <ID> <NewName>` -- Renames an existing area.
* `/list_areas` -- Lists all of the areas that you own, or all areas if you
have the `areas` privilege.
* `/find_areas <Regex>` -- Finds areas using a Lua regular expresion.
For example, to find castles:
/find_areas [Cc]astle
* `/remove_area <ID>` -- Removes an area that you own. Any sub-areas of that
area are made sub-areas of the removed area's parent, if it exists.
If the removed area has no parent it's sub-areas will have no parent.
* `/recursive_remove_areas <ID>` -- Removes an area and all sub-areas of it.
* `/change_owner <ID> <NewOwner>` -- Change the owner of an area.
* `/area_info` -- Returns information about area configuration and usage.
* `/select_area <ID>` -- Sets the area positions to those of an existing
area.
* `/area_pos {set,set1,set2,get}` -- Sets the area positions by punching
nodes or shows the current area positions.
* `/area_pos1 [X,Y,Z|X Y Z]` -- Sets area position one to your position or
the one supplied.
* `/area_pos2 [X,Y,Z|X Y Z]` -- Sets area position two to your position or
the one supplied.
* `/areas_cleanup` -- Removes all ownerless areas.
Useful for cleaning after user deletion, for example using /remove_player.
* `/area_open <ID>` -- Toggle open/closed the specified area for everyone.
* `/area_faction_open <ID> <faction>` -- Toggle open/closed the specified
area for members of the faction. Factions are created and managed by
playerfactions mod.
License
-------
Copyright (C) 2013-2017 ShadowNinja
Copyright (C) 2015-2020 various contributors
Licensed under the GNU LGPL version 2.1 or later.
See LICENSE.txt and http://www.gnu.org/licenses/lgpl-2.1.txt

174
mods/areas/api.lua Normal file
View File

@ -0,0 +1,174 @@
local hudHandlers = {}
areas.registered_on_adds = {}
areas.registered_on_removes = {}
areas.registered_on_moves = {}
function areas:registerOnAdd(func)
table.insert(areas.registered_on_adds, func)
end
function areas:registerOnRemove(func)
table.insert(areas.registered_on_removes, func)
end
function areas:registerOnMove(func)
table.insert(areas.registered_on_moves, func)
end
--- Adds a function as a HUD handler, it will be able to add items to the Areas HUD element.
function areas:registerHudHandler(handler)
table.insert(hudHandlers, handler)
end
function areas:getExternalHudEntries(pos)
local areas = {}
for _, func in pairs(hudHandlers) do
func(pos, areas)
end
return areas
end
--- Returns a list of areas that include the provided position.
function areas:getAreasAtPos(pos)
local res = {}
if self.store then
local a = self.store:get_areas_for_pos(pos, false, true)
for store_id, store_area in pairs(a) do
local id = tonumber(store_area.data)
res[id] = self.areas[id]
end
else
local px, py, pz = pos.x, pos.y, pos.z
for id, area in pairs(self.areas) do
local ap1, ap2 = area.pos1, area.pos2
if
(px >= ap1.x and px <= ap2.x) and
(py >= ap1.y and py <= ap2.y) and
(pz >= ap1.z and pz <= ap2.z) then
res[id] = area
end
end
end
return res
end
--- Returns areas that intersect with the passed area.
function areas:getAreasIntersectingArea(pos1, pos2)
local res = {}
if self.store then
local a = self.store:get_areas_in_area(pos1, pos2,
true, false, true)
for store_id, store_area in pairs(a) do
local id = tonumber(store_area.data)
res[id] = self.areas[id]
end
else
self:sortPos(pos1, pos2)
local p1x, p1y, p1z = pos1.x, pos1.y, pos1.z
local p2x, p2y, p2z = pos2.x, pos2.y, pos2.z
for id, area in pairs(self.areas) do
local ap1, ap2 = area.pos1, area.pos2
if
(ap1.x <= p2x and ap2.x >= p1x) and
(ap1.y <= p2y and ap2.y >= p1y) and
(ap1.z <= p2z and ap2.z >= p1z) then
-- Found an intersecting area.
res[id] = area
end
end
end
return res
end
-- Checks if the area is unprotected or owned by you
function areas:canInteract(pos, name)
if minetest.check_player_privs(name, self.adminPrivs) then
return true
end
local owned = false
for _, area in pairs(self:getAreasAtPos(pos)) do
if area.owner == name or area.open then
return true
elseif areas.factions_available and area.faction_open then
if (factions.version or 0) < 2 then
local faction_name = factions.get_player_faction(name)
if faction_name then
for _, fname in ipairs(area.faction_open or {}) do
if faction_name == fname then
return true
end
end
end
else
for _, fname in ipairs(area.faction_open or {}) do
if factions.player_is_in_faction(fname, name) then
return true
end
end
end
end
owned = true
end
return not owned
end
-- Returns a table (list) of all players that own an area
function areas:getNodeOwners(pos)
local owners = {}
for _, area in pairs(self:getAreasAtPos(pos)) do
table.insert(owners, area.owner)
end
return owners
end
--- Checks if the area intersects with an area that the player can't interact in.
-- Note that this fails and returns false when the specified area is fully
-- owned by the player, but with multiple protection zones, none of which
-- cover the entire checked area.
-- @param name (optional) Player name. If not specified checks for any intersecting areas.
-- @param allow_open Whether open areas should be counted as if they didn't exist.
-- @return Boolean indicating whether the player can interact in that area.
-- @return Un-owned intersecting area ID, if found.
function areas:canInteractInArea(pos1, pos2, name, allow_open)
if name and minetest.check_player_privs(name, self.adminPrivs) then
return true
end
self:sortPos(pos1, pos2)
-- Intersecting non-owned area ID, if found.
local blocking_area = nil
local areas = self:getAreasIntersectingArea(pos1, pos2)
for id, area in pairs(areas) do
-- First check for a fully enclosing owned area.
-- A little optimization: isAreaOwner isn't necessary
-- here since we're iterating over all relevant areas.
if area.owner == name and
self:isSubarea(pos1, pos2, id) then
return true
end
-- Then check for intersecting non-owned (blocking) areas.
-- We don't bother with this check if we've already found a
-- blocking area, as the check is somewhat expensive.
-- The area blocks if the area is closed or open areas aren't
-- acceptable to the caller, and the area isn't owned.
-- Note: We can't return directly here, because there might be
-- an exclosing owned area that we haven't gotten to yet.
if not blocking_area and
(not allow_open or not area.open) and
(not name or not self:isAreaOwner(id, name)) then
blocking_area = id
end
end
if blocking_area then
return false, blocking_area
end
-- There are no intersecting areas or they are only partially
-- intersecting areas and they are all owned by the player.
return true
end

48
mods/areas/api.md Normal file
View File

@ -0,0 +1,48 @@
Areas mod API
===
API list
---
* `areas:registerHudHandler(handler)` - Registers a handler to add items to the Areas HUD. See [HUD](#hud).
* `areas:registerOnAdd(func(id, area))`
* `areas:registerOnRemove(func(id))`
* `areas:registerOnMove(func(id, area, pos1, pos2))`
HUD
---
If you are making a protection mod or a similar mod that adds invisible regions
to the world, and you would like then to show up in the areas HUD element, you
can register a callback to show your areas.
HUD handler specification:
* `handler(pos, list)`
* `pos` - The position to check.
* `list` - The list of area HUD elements, this should be modified in-place.
The area list item is a table containing a list of tables with the following fields:
* `id` - An identifier for the area. This should be a unique string in the format `mod:id`.
* `name` - The name of the area.
* `owner` - The player name of the region owner, if any.
All of the fields are optional but at least one of them must be set.
### Example
local function areas_hud_handler(pos, areas)
local val = find_my_protection(pos)
if val then
table.insert(areas, {
id = "mod:"..val.id,
name = val.name,
owner = val.owner,
})
end
end
areas:registerHudHandler(areas_hud_handler)

462
mods/areas/chatcommands.lua Normal file
View File

@ -0,0 +1,462 @@
local S = minetest.get_translator("areas")
minetest.register_chatcommand("protect", {
params = S("<AreaName>"),
description = S("Protect your own area"),
privs = {[areas.config.self_protection_privilege]=true},
func = function(name, param)
if param == "" then
return false, S("Invalid usage, see /help @1.", "protect")
end
local pos1, pos2 = areas:getPos(name)
if not (pos1 and pos2) then
return false, S("You need to select an area first.")
end
minetest.log("action", "/protect invoked, owner="..name..
" AreaName="..param..
" StartPos="..minetest.pos_to_string(pos1)..
" EndPos=" ..minetest.pos_to_string(pos2))
local canAdd, errMsg = areas:canPlayerAddArea(pos1, pos2, name)
if not canAdd then
return false, S("You can't protect that area: @1", errMsg)
end
local id = areas:add(name, param, pos1, pos2, nil)
areas:save()
return true, S("Area protected. ID: @1", id)
end
})
minetest.register_chatcommand("set_owner", {
params = S("<PlayerName>").." "..S("<AreaName>"),
description = S("Protect an area between two positions and give"
.." a player access to it without setting the parent of the"
.." area to any existing area"),
privs = areas.adminPrivs,
func = function(name, param)
local ownerName, areaName = param:match('^(%S+)%s(.+)$')
if not ownerName then
return false, S("Invalid usage, see /help @1.", "set_owner")
end
local pos1, pos2 = areas:getPos(name)
if not (pos1 and pos2) then
return false, S("You need to select an area first.")
end
if not areas:player_exists(ownerName) then
return false, S("The player \"@1\" does not exist.", ownerName)
end
minetest.log("action", name.." runs /set_owner. Owner = "..ownerName..
" AreaName = "..areaName..
" StartPos = "..minetest.pos_to_string(pos1)..
" EndPos = " ..minetest.pos_to_string(pos2))
local id = areas:add(ownerName, areaName, pos1, pos2, nil)
areas:save()
minetest.chat_send_player(ownerName,
S("You have been granted control over area #@1. "..
"Type /list_areas to show your areas.", id))
return true, S("Area protected. ID: @1", id)
end
})
minetest.register_chatcommand("add_owner", {
params = S("<ParentID>").." "..S("<PlayerName>").." "..S("<AreaName>"),
description = S("Give a player access to a sub-area beetween two"
.." positions that have already been protected,"
.." Use set_owner if you don't want the parent to be set."),
func = function(name, param)
local pid, ownerName, areaName = param:match('^(%d+) ([^ ]+) (.+)$')
if not pid then
minetest.chat_send_player(name, S("Invalid usage, see /help @1.", "add_owner"))
return
end
local pos1, pos2 = areas:getPos(name)
if not (pos1 and pos2) then
return false, S("You need to select an area first.")
end
if not areas:player_exists(ownerName) then
return false, S("The player \"@1\" does not exist.", ownerName)
end
minetest.log("action", name.." runs /add_owner. Owner = "..ownerName..
" AreaName = "..areaName.." ParentID = "..pid..
" StartPos = "..pos1.x..","..pos1.y..","..pos1.z..
" EndPos = " ..pos2.x..","..pos2.y..","..pos2.z)
-- Check if this new area is inside an area owned by the player
pid = tonumber(pid)
if (not areas:isAreaOwner(pid, name)) or
(not areas:isSubarea(pos1, pos2, pid)) then
return false, S("You can't protect that area.")
end
local id = areas:add(ownerName, areaName, pos1, pos2, pid)
areas:save()
minetest.chat_send_player(ownerName,
S("You have been granted control over area #@1. "..
"Type /list_areas to show your areas.", id))
return true, S("Area protected. ID: @1", id)
end
})
minetest.register_chatcommand("rename_area", {
params = S("<ID>").." "..S("<newName>"),
description = S("Rename an area that you own"),
func = function(name, param)
local id, newName = param:match("^(%d+)%s(.+)$")
if not id then
return false, S("Invalid usage, see /help @1.", "rename_area")
end
id = tonumber(id)
if not id then
return false, S("That area doesn't exist.")
end
if not areas:isAreaOwner(id, name) then
return true, S("You don't own that area.")
end
areas.areas[id].name = newName
areas:save()
return true, S("Area renamed.")
end
})
minetest.register_chatcommand("find_areas", {
params = "<regexp>",
description = S("Find areas using a Lua regular expression"),
privs = areas.adminPrivs,
func = function(name, param)
if param == "" then
return false, S("A regular expression is required.")
end
-- Check expression for validity
local function testRegExp()
("Test [1]: Player (0,0,0) (0,0,0)"):find(param)
end
if not pcall(testRegExp) then
return false, S("Invalid regular expression.")
end
local matches = {}
for id, area in pairs(areas.areas) do
local str = areas:toString(id)
if str:find(param) then
table.insert(matches, str)
end
end
if #matches > 0 then
return true, table.concat(matches, "\n")
else
return true, S("No matches found.")
end
end
})
minetest.register_chatcommand("list_areas", {
description = S("List your areas, or all areas if you are an admin."),
func = function(name, param)
local admin = minetest.check_player_privs(name, areas.adminPrivs)
local areaStrings = {}
for id, area in pairs(areas.areas) do
if admin or areas:isAreaOwner(id, name) then
table.insert(areaStrings, areas:toString(id))
end
end
if #areaStrings == 0 then
return true, S("No visible areas.")
end
return true, table.concat(areaStrings, "\n")
end
})
minetest.register_chatcommand("recursive_remove_areas", {
params = S("<ID>"),
description = S("Recursively remove areas using an ID"),
func = function(name, param)
local id = tonumber(param)
if not id then
return false, S("Invalid usage, see"
.." /help @1.", "recursive_remove_areas")
end
if not areas:isAreaOwner(id, name) then
return false, S("Area @1 does not exist or is"
.." not owned by you.", id)
end
areas:remove(id, true)
areas:save()
return true, S("Removed area @1 and it's sub areas.", id)
end
})
minetest.register_chatcommand("remove_area", {
params = S("<ID>"),
description = S("Remove an area using an ID"),
func = function(name, param)
local id = tonumber(param)
if not id then
return false, S("Invalid usage, see /help @1.", "remove_area")
end
if not areas:isAreaOwner(id, name) then
return false, S("Area @1 does not exist or"
.." is not owned by you.", id)
end
areas:remove(id)
areas:save()
return true, S("Removed area @1", id)
end
})
minetest.register_chatcommand("change_owner", {
params = S("<ID>").." "..S("<NewOwner>"),
description = S("Change the owner of an area using its ID"),
func = function(name, param)
local id, newOwner = param:match("^(%d+)%s(%S+)$")
if not id then
return false, S("Invalid usage, see"
.." /help @1.", "change_owner")
end
if not areas:player_exists(newOwner) then
return false, S("The player \"@1\" does not exist.", newOwner)
end
id = tonumber(id)
if not areas:isAreaOwner(id, name) then
return false, S("Area @1 does not exist"
.." or is not owned by you.", id)
end
areas.areas[id].owner = newOwner
areas:save()
minetest.chat_send_player(newOwner,
S("@1 has given you control over the area \"@2\" (ID @3).",
name, areas.areas[id].name, id))
return true, S("Owner changed.")
end
})
minetest.register_chatcommand("area_open", {
params = S("<ID>"),
description = S("Toggle an area open (anyone can interact) or closed"),
func = function(name, param)
local id = tonumber(param)
if not id then
return false, S("Invalid usage, see /help @1.", "area_open")
end
if not areas:isAreaOwner(id, name) then
return false, S("Area @1 does not exist"
.." or is not owned by you.", id)
end
local open = not areas.areas[id].open
-- Save false as nil to avoid inflating the DB.
areas.areas[id].open = open or nil
areas:save()
return true, open and S("Area opened.") or S("Area closed.")
end
})
if areas.factions_available then
minetest.register_chatcommand("area_faction_open", {
params = S("<ID> [faction_name]"),
description = S("Toggle an area open/closed for members in your faction."),
func = function(name, param)
local params = param:split(" ")
local id = tonumber(params[1])
local faction_name = params[2]
if not id or not faction_name then
return false, S("Invalid usage, see /help @1.", "area_faction_open")
end
if not areas:isAreaOwner(id, name) then
return false, S("Area @1 does not exist"
.." or is not owned by you.", id)
end
if not factions.get_owner(faction_name) then
return false, S("Faction doesn't exists")
end
local fnames = areas.areas[id].faction_open or {}
local pos = table.indexof(fnames, faction_name)
if pos < 0 then
-- Add new faction to the list
table.insert(fnames, faction_name)
else
table.remove(fnames, pos)
end
if #fnames == 0 then
-- Save {} as nil to avoid inflating the DB.
fnames = nil
end
areas.areas[id].faction_open = fnames
areas:save()
return true, fnames and S("Area is open for members of: @1", table.concat(fnames, ", "))
or S("Area closed for faction members.")
end
})
end
minetest.register_chatcommand("move_area", {
params = S("<ID>"),
description = S("Move (or resize) an area to the current positions."),
privs = areas.adminPrivs,
func = function(name, param)
local id = tonumber(param)
if not id then
return false, S("Invalid usage, see /help @1.", "move_area")
end
local area = areas.areas[id]
if not area then
return false, S("Area does not exist.")
end
local pos1, pos2 = areas:getPos(name)
if not pos1 then
return false, S("You need to select an area first.")
end
areas:move(id, area, pos1, pos2)
areas:save()
return true, S("Area successfully moved.")
end,
})
minetest.register_chatcommand("area_info", {
description = S("Get information about area configuration and usage."),
func = function(name, param)
local lines = {}
local privs = minetest.get_player_privs(name)
-- Short (and fast to access) names
local cfg = areas.config
local self_prot = cfg.self_protection
local prot_priv = cfg.self_protection_privilege
local limit = cfg.self_protection_max_areas
local limit_high = cfg.self_protection_max_areas_high
local size_limit = cfg.self_protection_max_size
local size_limit_high = cfg.self_protection_max_size_high
local has_high_limit = privs.areas_high_limit
local has_prot_priv = not prot_priv or privs[prot_priv]
local can_prot = privs.areas or (self_prot and has_prot_priv)
local max_count = can_prot and
(has_high_limit and limit_high or limit) or 0
local max_size = has_high_limit and
size_limit_high or size_limit
-- Self protection information
local self_prot_line = self_prot and S("Self protection is enabled.") or
S("Self protection is disabled.")
table.insert(lines, self_prot_line)
-- Privilege information
local priv_line = has_prot_priv and
S("You have the necessary privilege (\"@1\").", prot_priv) or
S("You don't have the necessary privilege (\"@1\").", prot_priv)
table.insert(lines, priv_line)
if privs.areas then
table.insert(lines, S("You are an area"..
" administrator (\"areas\" privilege)."))
elseif has_high_limit then
table.insert(lines,
S("You have extended area protection"..
" limits (\"areas_high_limit\" privilege)."))
end
-- Area count
local area_num = 0
for id, area in pairs(areas.areas) do
if area.owner == name then
area_num = area_num + 1
end
end
table.insert(lines, S("You have @1 areas.", area_num))
-- Area limit
local area_limit_line = privs.areas and
S("Limit: no area count limit") or
S("Limit: @1 areas", max_count)
table.insert(lines, area_limit_line)
-- Area size limits
local function size_info(str, size)
table.insert(lines, S("@1 spanning up to @2x@3x@4.",
str, size.x, size.y, size.z))
end
local function priv_limit_info(lpriv, lmax_count, lmax_size)
size_info(S("Players with the \"@1\" privilege"..
" can protect up to @2 areas", lpriv, lmax_count),
lmax_size)
end
if self_prot then
if privs.areas then
priv_limit_info(prot_priv,
limit, size_limit)
priv_limit_info("areas_high_limit",
limit_high, size_limit_high)
elseif has_prot_priv then
size_info(S("You can protect areas"), max_size)
end
end
return true, table.concat(lines, "\n")
end,
})
minetest.register_chatcommand("areas_cleanup", {
description = S("Removes all ownerless areas"),
privs = areas.adminPrivs,
func = function()
local total, count = 0, 0
local aareas = areas.areas
for id, _ in pairs(aareas) do
local owner = aareas[id].owner
if not areas:player_exists(owner) then
areas:remove(id)
count = count + 1
end
total = total + 1
end
areas:save()
return true, "Total areas: " .. total .. ", Removed " ..
count .. " areas. New count: " .. (total - count)
end
})

89
mods/areas/hud.lua Normal file
View File

@ -0,0 +1,89 @@
-- This is inspired by the landrush mod by Bremaweb
local S = minetest.get_translator("areas")
areas.hud = {}
areas.hud.refresh = 0
minetest.register_globalstep(function(dtime)
areas.hud.refresh = areas.hud.refresh + dtime
if areas.hud.refresh > areas.config["tick"] then
areas.hud.refresh = 0
else
return
end
for _, player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
local pos = vector.round(player:get_pos())
pos = vector.apply(pos, function(p)
return math.max(math.min(p, 2147483), -2147483)
end)
local areaStrings = {}
for id, area in pairs(areas:getAreasAtPos(pos)) do
local faction_info
if area.faction_open and areas.factions_available then
-- Gather and clean up disbanded factions
local changed = false
for i, fac_name in ipairs(area.faction_open) do
if not factions.get_owner(fac_name) then
table.remove(area.faction_open, i)
changed = true
end
end
if #area.faction_open == 0 then
-- Prevent DB clutter, remove value
area.faction_open = nil
else
faction_info = table.concat(area.faction_open, ", ")
end
if changed then
areas:save()
end
end
table.insert(areaStrings, ("%s [%u] (%s%s%s)")
:format(area.name, id, area.owner,
area.open and S(":open") or "",
faction_info and ": "..faction_info or ""))
end
for i, area in pairs(areas:getExternalHudEntries(pos)) do
local str = ""
if area.name then str = area.name .. " " end
if area.id then str = str.."["..area.id.."] " end
if area.owner then str = str.."("..area.owner..")" end
table.insert(areaStrings, str)
end
local areaString = S("Areas:")
if #areaStrings > 0 then
areaString = areaString.."\n"..
table.concat(areaStrings, "\n")
end
local hud = areas.hud[name]
if not hud then
hud = {}
areas.hud[name] = hud
hud.areasId = player:hud_add({
hud_elem_type = "text",
name = "Areas",
number = 0xFFFFFF,
position = {x=0, y=1},
offset = {x=8, y=-8},
text = areaString,
scale = {x=200, y=60},
alignment = {x=1, y=-1},
})
hud.oldAreas = areaString
return
elseif hud.oldAreas ~= areaString then
player:hud_change(hud.areasId, "text", areaString)
hud.oldAreas = areaString
end
end
end)
minetest.register_on_leaveplayer(function(player)
areas.hud[player:get_player_name()] = nil
end)

44
mods/areas/init.lua Normal file
View File

@ -0,0 +1,44 @@
-- Areas mod by ShadowNinja
-- Based on node_ownership
-- License: LGPLv2+
areas = {}
areas.factions_available = minetest.get_modpath("playerfactions") and true
areas.adminPrivs = {areas=true}
areas.startTime = os.clock()
areas.modpath = minetest.get_modpath("areas")
dofile(areas.modpath.."/settings.lua")
dofile(areas.modpath.."/api.lua")
dofile(areas.modpath.."/internal.lua")
dofile(areas.modpath.."/chatcommands.lua")
dofile(areas.modpath.."/pos.lua")
dofile(areas.modpath.."/interact.lua")
dofile(areas.modpath.."/legacy.lua")
dofile(areas.modpath.."/hud.lua")
areas:load()
local S = minetest.get_translator("areas")
minetest.register_privilege("areas", {
description = S("Can administer areas."),
give_to_singleplayer = false
})
minetest.register_privilege("areas_high_limit", {
description = S("Can protect more, bigger areas."),
give_to_singleplayer = false
})
if not minetest.registered_privileges[areas.config.self_protection_privilege] then
minetest.register_privilege(areas.config.self_protection_privilege, {
description = S("Can protect areas."),
})
end
if minetest.settings:get_bool("log_mods") then
local diffTime = os.clock() - areas.startTime
minetest.log("action", "areas loaded in "..diffTime.."s.")
end

19
mods/areas/interact.lua Normal file
View File

@ -0,0 +1,19 @@
local S = minetest.get_translator("areas")
local old_is_protected = minetest.is_protected
function minetest.is_protected(pos, name)
if not areas:canInteract(pos, name) then
return true
end
return old_is_protected(pos, name)
end
minetest.register_on_protection_violation(function(pos, name)
if not areas:canInteract(pos, name) then
local owners = areas:getNodeOwners(pos)
minetest.chat_send_player(name,
S("@1 is protected by @2.",
minetest.pos_to_string(pos),
table.concat(owners, ", ")))
end
end)

314
mods/areas/internal.lua Normal file
View File

@ -0,0 +1,314 @@
local S = minetest.get_translator("areas")
function areas:player_exists(name)
return minetest.get_auth_handler().get_auth(name) ~= nil
end
local safe_file_write = minetest.safe_file_write
if safe_file_write == nil then
function safe_file_write(path, content)
local file, err = io.open(path, "w")
if err then
return err
end
file:write(content)
file:close()
end
end
-- Save the areas table to a file
function areas:save()
local datastr = minetest.write_json(self.areas)
if not datastr then
minetest.log("error", "[areas] Failed to serialize area data!")
return
end
return safe_file_write(self.config.filename, datastr)
end
-- Load the areas table from the save file
function areas:load()
local file, err = io.open(self.config.filename, "r")
if err then
self.areas = self.areas or {}
return err
end
local data = file:read("*a")
if data:sub(1, 1) == "[" then
self.areas, err = minetest.parse_json(data)
else
self.areas, err = minetest.deserialize(data)
end
if type(self.areas) ~= "table" then
self.areas = {}
end
if err and #data > 10 then
minetest.log("error", "[areas] Failed to load area data: " ..
tostring(err))
end
file:close()
self:populateStore()
end
--- Checks an AreaStore ID.
-- Deletes the AreaStore (falling back to the iterative method)
-- and prints an error message if the ID is invalid.
-- @return Whether the ID was valid.
function areas:checkAreaStoreId(sid)
if not sid then
minetest.log("error", "AreaStore failed to find an ID for an "
.."area! Falling back to iterative area checking.")
self.store = nil
self.store_ids = nil
end
return sid and true or false
end
-- Populates the AreaStore after loading, if needed.
function areas:populateStore()
if not rawget(_G, "AreaStore") then
return
end
local store = AreaStore()
local store_ids = {}
for id, area in pairs(areas.areas) do
local sid = store:insert_area(area.pos1,
area.pos2, tostring(id))
if not self:checkAreaStoreId(sid) then
return
end
store_ids[id] = sid
end
self.store = store
self.store_ids = store_ids
end
-- Finds the first usable index in a table
-- Eg: {[1]=false,[4]=true} -> 2
local function findFirstUnusedIndex(t)
local i = 0
repeat i = i + 1
until t[i] == nil
return i
end
--- Add a area.
-- @return The new area's ID.
function areas:add(owner, name, pos1, pos2, parent)
local id = findFirstUnusedIndex(self.areas)
self.areas[id] = {
name = name,
pos1 = pos1,
pos2 = pos2,
owner = owner,
parent = parent
}
for i=1, #areas.registered_on_adds do
areas.registered_on_adds[i](id, self.areas[id])
end
-- Add to AreaStore
if self.store then
local sid = self.store:insert_area(pos1, pos2, tostring(id))
if self:checkAreaStoreId(sid) then
self.store_ids[id] = sid
end
end
return id
end
--- Remove a area, and optionally it's children recursively.
-- If a area is deleted non-recursively the children will
-- have the removed area's parent as their new parent.
function areas:remove(id, recurse)
if recurse then
-- Recursively find child entries and remove them
local cids = self:getChildren(id)
for _, cid in pairs(cids) do
self:remove(cid, true)
end
else
-- Update parents
local parent = self.areas[id].parent
local children = self:getChildren(id)
for _, cid in pairs(children) do
-- The subarea parent will be niled out if the
-- removed area does not have a parent
self.areas[cid].parent = parent
end
end
for i=1, #areas.registered_on_removes do
areas.registered_on_removes[i](id)
end
-- Remove main entry
self.areas[id] = nil
-- Remove from AreaStore
if self.store then
self.store:remove_area(self.store_ids[id])
self.store_ids[id] = nil
end
end
--- Move an area.
function areas:move(id, area, pos1, pos2)
area.pos1 = pos1
area.pos2 = pos2
for i=1, #areas.registered_on_moves do
areas.registered_on_moves[i](id, area, pos1, pos2)
end
if self.store then
self.store:remove_area(areas.store_ids[id])
local sid = self.store:insert_area(pos1, pos2, tostring(id))
if self:checkAreaStoreId(sid) then
self.store_ids[id] = sid
end
end
end
-- Checks if a area between two points is entirely contained by another area.
-- Positions must be sorted.
function areas:isSubarea(pos1, pos2, id)
local area = self.areas[id]
if not area then
return false
end
local ap1, ap2 = area.pos1, area.pos2
local ap1x, ap1y, ap1z = ap1.x, ap1.y, ap1.z
local ap2x, ap2y, ap2z = ap2.x, ap2.y, ap2.z
local p1x, p1y, p1z = pos1.x, pos1.y, pos1.z
local p2x, p2y, p2z = pos2.x, pos2.y, pos2.z
if
(p1x >= ap1x and p1x <= ap2x) and
(p2x >= ap1x and p2x <= ap2x) and
(p1y >= ap1y and p1y <= ap2y) and
(p2y >= ap1y and p2y <= ap2y) and
(p1z >= ap1z and p1z <= ap2z) and
(p2z >= ap1z and p2z <= ap2z) then
return true
end
end
-- Returns a table (list) of children of an area given it's identifier
function areas:getChildren(id)
local children = {}
for cid, area in pairs(self.areas) do
if area.parent and area.parent == id then
table.insert(children, cid)
end
end
return children
end
-- Checks if the user has sufficient privileges.
-- If the player is not a administrator it also checks
-- if the area intersects other areas that they do not own.
-- Also checks the size of the area and if the user already
-- has more than max_areas.
function areas:canPlayerAddArea(pos1, pos2, name)
local privs = minetest.get_player_privs(name)
if privs.areas then
return true
end
-- Check self protection privilege, if it is enabled,
-- and if the area is too big.
if not self.config.self_protection or
not privs[areas.config.self_protection_privilege] then
return false, S("Self protection is disabled or you do not have"
.." the necessary privilege.")
end
local max_size = privs.areas_high_limit and
self.config.self_protection_max_size_high or
self.config.self_protection_max_size
if
(pos2.x - pos1.x) > max_size.x or
(pos2.y - pos1.y) > max_size.y or
(pos2.z - pos1.z) > max_size.z then
return false, S("Area is too big.")
end
-- Check number of areas the user has and make sure it not above the max
local count = 0
for _, area in pairs(self.areas) do
if area.owner == name then
count = count + 1
end
end
local max_areas = privs.areas_high_limit and
self.config.self_protection_max_areas_high or
self.config.self_protection_max_areas
if count >= max_areas then
return false, S("You have reached the maximum amount of"
.." areas that you are allowed to protect.")
end
-- Check intersecting areas
local can, id = self:canInteractInArea(pos1, pos2, name)
if not can then
local area = self.areas[id]
return false, S("The area intersects with @1 [@2] (@3).",
area.name, id, area.owner)
end
return true
end
-- Given a id returns a string in the format:
-- "name [id]: owner (x1, y1, z1) (x2, y2, z2) -> children"
function areas:toString(id)
local area = self.areas[id]
local message = ("%s [%d]: %s %s %s"):format(
area.name, id, area.owner,
minetest.pos_to_string(area.pos1),
minetest.pos_to_string(area.pos2))
local children = areas:getChildren(id)
if #children > 0 then
message = message.." -> "..table.concat(children, ", ")
end
return message
end
-- Re-order areas in table by their identifiers
function areas:sort()
local sa = {}
for k, area in pairs(self.areas) do
if not area.parent then
table.insert(sa, area)
local newid = #sa
for _, subarea in pairs(self.areas) do
if subarea.parent == k then
subarea.parent = newid
table.insert(sa, subarea)
end
end
end
end
self.areas = sa
end
-- Checks if a player owns an area or a parent of it
function areas:isAreaOwner(id, name)
local cur = self.areas[id]
if cur and minetest.check_player_privs(name, self.adminPrivs) then
return true
end
while cur do
if cur.owner == name then
return true
elseif cur.parent then
cur = self.areas[cur.parent]
else
return false
end
end
return false
end

108
mods/areas/legacy.lua Normal file
View File

@ -0,0 +1,108 @@
-- This file contains functions to convert from
-- the old areas format and other compatability code.
local S = minetest.get_translator("areas")
minetest.register_chatcommand("legacy_load_areas", {
params = S("<version>"),
description = S("Loads, converts, and saves the areas from"
.." a legacy save file."),
privs = {areas=true, server=true},
func = function(name, param)
minetest.chat_send_player(name, S("Converting areas…"))
local version = tonumber(param)
if version == 0 then
local err = areas:node_ownership_load()
if err then
minetest.chat_send_player(name, S("Error loading legacy file: @1", err))
return
end
else
minetest.chat_send_player(name, S("Invalid version number. (0 allowed)"))
return
end
minetest.chat_send_player(name, S("Legacy file loaded."))
for k, area in pairs(areas.areas) do
-- New position format
area.pos1 = {x=area.x1, y=area.y1, z=area.z1}
area.pos2 = {x=area.x2, y=area.y2, z=area.z2}
area.x1, area.y1, area.z1,
area.x2, area.y2, area.z2 =
nil, nil, nil, nil, nil, nil
-- Area positions sorting
areas:sortPos(area.pos1, area.pos2)
-- Add name
area.name = S("unnamed")
-- Remove ID
area.id = nil
end
minetest.chat_send_player(name, S("Table format updated."))
areas:save()
minetest.chat_send_player(name, S("Converted areas saved. Done."))
end
})
function areas:node_ownership_load()
local filename = minetest.get_worldpath().."/owners.tbl"
local tables, err
tables, err = loadfile(filename)
if err then
return err
end
tables = tables()
for idx = 1, #tables do
local tolinkv, tolinki = {}, {}
for i, v in pairs(tables[idx]) do
if type(v) == "table" and tables[v[1]] then
table.insert(tolinkv, {i, tables[v[1]]})
end
if type(i) == "table" and tables[i[1]] then
table.insert(tolinki, {i, tables[i[1]]})
end
end
-- link values, first due to possible changes of indices
for _, v in ipairs(tolinkv) do
tables[idx][v[1]] = v[2]
end
-- link indices
for _, v in ipairs(tolinki) do
tables[idx][v[2]], tables[idx][v[1]] = tables[idx][v[1]], nil
end
end
self.areas = tables[1]
end
-- Returns the name of the first player that owns an area
function areas.getNodeOwnerName(pos)
for id, area in pairs(areas:getAreasAtPos(pos)) do
return area.owner
end
return false
end
-- Checks if a node is owned by you
function areas.isNodeOwner(pos, name)
if minetest.check_player_privs(name, areas.adminPrivs) then
return true
end
for id, area in pairs(areas:getAreasAtPos(pos)) do
if name == area.owner then
return true
end
end
return false
end
-- Checks if an area has an owner
function areas.hasOwner(pos)
for id, area in pairs(areas:getAreasAtPos(pos)) do
return true
end
return false
end

View File

@ -0,0 +1,135 @@
# textdomain: areas
### chatcommands.lua ###
<AreaName>=<NomZone>
<ID> [faction_name]=<ID> [nom_de_faction]
<NewOwner>=<NouveauPropriétaire>
<ParentID>=<IDZonePrincipale>
<PlayerName>=<NomJoueur>
<newName>=<NouveauNom>
@1 has given you control over the area "@2" (ID @3).=@1 vous a donné le contrôle de la zone "@2" (ID @3).
@1 spanning up to @2x@3x@4.=@1 sétendant jusquà @2x@3x@4.
A regular expression is required.=Une expression régulière est requise.
Area @1 does not exist or is not owned by you.=La zone @1 nexiste pas ou ne vous appartient pas.
Area closed for faction members.=Zone fermée aux membres de la faction.
Area closed.=Zone fermée.
Area does not exist.=La zone nexiste pas.
Area is open for members of: @1=Zone ouverte aux membres de ces factions : @1
Area opened.=Zone ouverte.
Area protected. ID: @1=Zone protégée. ID : @1
Area renamed.=Zone renommée.
Area successfully moved.=Zone déplacée avec succès.
Change the owner of an area using its ID=Change le propriétaire dune zone en utilisant son ID.
Faction doesn't exists=La faction n'existe pas
Find areas using a Lua regular expression=Trouve les zones en utilisant une expression régulière Lua.
Get information about area configuration and usage.=Obtient des informations sur la configuration des zones et lutilisation des zones.
Give a player access to a sub-area beetween two positions that have already been protected, Use set_owner if you don't want the parent to be set.=Donne au joueur accès aux sous-zones entre deux positions qui ont déjà été protégées ; utilisez set_owner si vous ne voulez pas que la zone pricipale soit définie.
Invalid regular expression.=Expression régulière invalide.
Limit: @1 areas=Limite: @1 zones.
Limit: no area count limit=Limite: pas de limite de nombre de zones.
List your areas, or all areas if you are an admin.=Liste vos zones, ou toutes les zones si vous êtes administrateur.
Move (or resize) an area to the current positions.=Déplace (ou redimensionne) une zone aux positions actuelles.
No matches found.=Aucun résultat.
No visible areas.=Pas de zone visible.
Owner changed.=Propriétaire changé.
Players with the "@1" privilege can protect up to @2 areas=Les joueurs avec le privilège "@1" peuvent protéger jusquà @2 zones
Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=Protège une zone entre deux positions et donne à un joueur accès à cette zone sans définir la zone principale de cette zone ni aucune zone existante.
Protect your own area=Protège votre zone.
Recursively remove areas using an ID=Supprime les zones récursivement en utilisant un ID.
Remove an area using an ID=Supprime une zone en utilisant son ID.
Removed area @1=Zone @1 supprimée.
Removed area @1 and it's sub areas.=Zone @1 et ses sous-zones supprimées.
Removes all ownerless areas=Supprime toutes les zones sans propriétaire
Rename an area that you own=Renomme une zone qui vous appartient.
Self protection is disabled.=Lautoprotection est désactivée.
Self protection is enabled.=Lautoprotection est activée.
That area doesn't exist.=La zone nexiste pas.
The player "@1" does not exist.=Le joueur "@1" nexiste pas.
Toggle an area open (anyone can interact) or closed=Bascule entre zone ouverte (tout le monde peut intéragir) ou fermée.
Toggle an area open/closed for members in your faction.=Bascule entre zone ouverte/fermée pour les membres de votre faction.
You are an area administrator ("areas" privilege).=Vous êtes un administrateur de zone (privilège "areas").
You can protect areas=Vous pouvez protéger des zones.
You can't protect that area.=Vous ne pouvez pas protéger cette zone.
You can't protect that area: @1=Vous ne pouvez pas protéger cette zone : @1.
You don't have the necessary privilege ("@1").=Vous navez pas le privilège nécessaire ("@1").
You don't own that area.=Vous ne possédez pas cette zone.
You have @1 areas.=Vous avez @1 zones.
You have been granted control over area #@1. Type /list_areas to show your areas.=Vous avez reçu lautorisation de contrôler la zone #@1.
You have extended area protection limits ("areas_high_limit" privilege).=Votre limite de protection de zones est étendue (privilège "areas_high_limit").
You have the necessary privilege ("@1").=Vous avez le privilège nécessaire ("@1").
You need to select an area first.=Vous devez sélectionner une zone dabord.
### chatcommands.lua ###
### pos.lua ###
<ID>=<ID>
Invalid usage, see /help @1.=Utilisation incorrecte, voir /help @1.
### hud.lua ###
:open= : ouverte
Areas:=Zones :
### init.lua ###
Can administer areas.=Permet dadministrer des zones.
Can protect areas.=Permet de protéger des zones.
Can protect more, bigger areas.=Permet de protéger plus, et de plus grandes zones.
### interact.lua ###
@1 is protected by @2.=@1 est protégée par @2.
### internal.lua ###
Area is too big.=La zone est trop grande.
Self protection is disabled or you do not have the necessary privilege.=Lautoprotection est désactivée ou vous navez pas le privilège nécessaire.
The area intersects with @1 [@2] (@3).=La zone a une intersection avec @1 [@2] (@3).
You have reached the maximum amount of areas that you are allowed to protect.=Vous avez atteint le nombre maximum de zones que vous êtes autorisé à protéger.
### legacy.lua ###
<version>=<version>
Converted areas saved. Done.=Zones converties sauvegardées. Fait.
Converting areas…=Conversion des zones…
Error loading legacy file: @1=Erreur lors du chargement du fichier : @1
Invalid version number. (0 allowed)=Numéro de version invalide. (0 autorisé)
Legacy file loaded.=Fichier obsolète chargé.
Loads, converts, and saves the areas from a legacy save file.=Charge, fait la conversion et sauvegarde les zones depuis un fichier de sauvegarde obsolète.
Table format updated.=Format de tableau mis à jour.
unnamed=Non nommé
### pos.lua ###
<not set>=<no définie>
Area @1 selected.=Zone @1 sélectionnée.
Area position @1 set to @2=Position @1 de la zone définie à @2.
Position @1 set to @2=Position @1 définie à @2.
Position @1: =Position @1 :
Select an area by ID.=Sélectionnez une zone par son ID.
Select position @1 by punching a node.=Sélectionnez une position en frappant un bloc.
Select positions by punching two nodes.=Sélectionnez une position en frappant deux blocs.
Set area protection region position @1 to your location or the one specified=Définit la position @1 de la région de protection de zone à votre position ou à celle spécifiée.
Set area protection region, position 1, or position 2 by punching nodes, or display the region=Définit la région de protection de zone, la position 1, ou la position 2 en frappant des blocs, ou en affichant la région.
The area @1 does not exist.=La zone @1 nexiste pas.
Unable to get position.=Impossible dobtenir la position.
Unknown subcommand: @1=Sous-commande inconnue : @1

View File

@ -0,0 +1,135 @@
# textdomain: areas
### chatcommands.lua ###
<AreaName>=<NomeArea>
<ID> [faction_name]= <ID> [nome_fazione]
<NewOwner>=<NuovoProprietario>
<ParentID>=<IDparent>
<PlayerName>=<NomeGiocatore>
<newName>=<nuovoNome>
@1 has given you control over the area "@2" (ID @3).=@1 ti ha dato il controllo sull'area "@2" (ID @3).
@1 spanning up to @2x@3x@4.=@1 si estende fino a @2x@3@4.
A regular expression is required.=È necessaria una espressione regolare.
Area @1 does not exist or is not owned by you.=L'area @1 non esiste o non è di tua proprietà.
Area closed for faction members.=Area chiusa per i membri della fazione.
Area closed.=Area chiusa.
Area does not exist.=L'area non esiste.
Area is open for members of: @1=L'area è aperta ai membri di: @1
Area opened.=Area aperta.
Area protected. ID: @1=Area protetta. ID: @1
Area renamed.=Area rinominata.
Area successfully moved.=Area spostata con successo.
Change the owner of an area using its ID=Cambia il proprietario di un'area usando il suo ID
Faction doesn't exists=La fazione non esiste
Find areas using a Lua regular expression=Trova aree usando una espressione regolare Lua
Get information about area configuration and usage.=Ottieni informazioni sulla configurazione e l'uso delle aree.
Give a player access to a sub-area beetween two positions that have already been protected, Use set_owner if you don't want the parent to be set.=Dai a un giocatore l'accesso a una sotto-area tra due posizioni che sono già state protette, usa set_owner se non vuoi che sia impostato il parent.
Invalid regular expression.=Espressione regolare non valida.
Limit: @1 areas=Limite: @1 aree
Limit: no area count limit=Limite: nessun limite al numero delle aree
List your areas, or all areas if you are an admin.=Elenca le tue aree, o tutte le aree se sei un amministratore.
Move (or resize) an area to the current positions.=Sposta (o ridimensiona) un'area alle posizioni attuali.
No matches found.=Nessuna corrispondenza trovata.
No visible areas.=Nessuna area visibile.
Owner changed.=Proprietario cambiato.
Players with the "@1" privilege can protect up to @2 areas=I giocatori col privilegio "@1" possono proteggere fino a @2 aree
Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=Proteggi un'area tra due posizioni e danne l'accesso a un giocatore senza impostare il parent dell'area a qualsiasi area esistente
Protect your own area=Proteggi la tua area
Recursively remove areas using an ID=Elimina ricorsivamente delle aree usando un ID
Remove an area using an ID=Elimina un'area usando un ID
Removed area @1=Eliminata l'area @1
Removed area @1 and it's sub areas.=Eliminata l'area @1 e le sue sotto-aree.
Removes all ownerless areas=
Rename an area that you own=Rinomina un'area che ti appartiene
Self protection is disabled.=L'auto-protezione è disattivata.
Self protection is enabled.=L'auto-protezione è attivata.
That area doesn't exist.=Quell'area non esiste.
The player "@1" does not exist.=Il giocatore "@1" non esiste.
Toggle an area open (anyone can interact) or closed=Apri o chiudi un'area (chiunque può interagirvi)
Toggle an area open/closed for members in your faction.=Apri o chiudi un'area per i membri della tua fazione.
You are an area administrator ("areas" privilege).=Sei un amministratore di aree (privilegio "areas")
You can protect areas=Puoi proteggere aree
You can't protect that area.=Non puoi proteggere quell'area.
You can't protect that area: @1=Non puoi proteggere quell'area: @1
You don't have the necessary privilege ("@1").=Non hai il privilegio necessario ("@1")
You don't own that area.=Non possiedi quell'area.
You have @1 areas.=Hai @1 aree.
You have been granted control over area #@1. Type /list_areas to show your areas.=Ti è stato concesso il controllo sull'area #@1. Digita /list_areas per mostrare le tue aree.
You have extended area protection limits ("areas_high_limit" privilege).=Hai limiti di protezione aree estesi (privilegio "areas_high_limit")
You have the necessary privilege ("@1").=Hai il privilegio necessario ("@1")
You need to select an area first.=Prima devi selezionare un'area.
### chatcommands.lua ###
### pos.lua ###
<ID>=<ID>
Invalid usage, see /help @1.=Utilizzo non valido, si veda /help @1.
### hud.lua ###
:open=:aperta
Areas:=Aree:
### init.lua ###
Can administer areas.=
Can protect areas.=
Can protect more, bigger areas.=
### interact.lua ###
@1 is protected by @2.=@1 è protetta da @2.
### internal.lua ###
Area is too big.=L'area è troppo grande.
Self protection is disabled or you do not have the necessary privilege.=L'auto-protezione è disattivata o non possiedi il privilegio necessario.
The area intersects with @1 [@2] (@3).=L'area interseca con @1 [@2] (@3).
You have reached the maximum amount of areas that you are allowed to protect.=Hai raggiunto il numero massimo di aree che ti è consentito proteggere.
### legacy.lua ###
<version>=<versione>
Converted areas saved. Done.=Aree convertite salvate. Fatto.
Converting areas…=Conversione delle aree...
Error loading legacy file: @1=Errore nel caricamento del file precedente: @1
Invalid version number. (0 allowed)=Numero di versione non valido. (0 permesso)
Legacy file loaded.=File precedente caricato.
Loads, converts, and saves the areas from a legacy save file.=Carica, converte e salva le aree da un file di salvataggio precedente.
Table format updated.=Aggiornato il formato della tabella.
unnamed=innominato
### pos.lua ###
<not set>=<non impostato>
Area @1 selected.=Area @1 selezionata.
Area position @1 set to @2=Posizione @1 dell'area impostata a @2
Position @1 set to @2=Posizione @1 impostata a @2
Position @1: =Posizione @1:
Select an area by ID.=Scegli un'area tramite l'ID.
Select position @1 by punching a node.=Seleziona la posizione @1 colpendo un nodo.
Select positions by punching two nodes.=Seleziona le posizioni colpendo due nodi.
Set area protection region position @1 to your location or the one specified=Imposta la protezione area della posizione @1 della regione alla tua posizione o quella specificata
Set area protection region, position 1, or position 2 by punching nodes, or display the region=Imposta la protezione area della regione, posizione 1, o posizione 2, colpendo due nodi, o mostra la regione
The area @1 does not exist.=L'area @1 non esiste.
Unable to get position.=Impossibile ottenere la posizione.
Unknown subcommand: @1=Sotto-comando sconosciuto: @1

View File

@ -0,0 +1,135 @@
# textdomain: areas
### chatcommands.lua ###
<AreaName>=
<ID> [faction_name]=
<NewOwner>=
<ParentID>=
<PlayerName>=
<newName>=
@1 has given you control over the area "@2" (ID @3).=
@1 spanning up to @2x@3x@4.=
A regular expression is required.=
Area @1 does not exist or is not owned by you.=
Area closed for faction members.=
Area closed.=
Area does not exist.=
Area is open for members of: @1=
Area opened.=
Area protected. ID: @1=
Area renamed.=
Area successfully moved.=
Change the owner of an area using its ID=
Faction doesn't exists=
Find areas using a Lua regular expression=
Get information about area configuration and usage.=
Give a player access to a sub-area beetween two positions that have already been protected, Use set_owner if you don't want the parent to be set.=
Invalid regular expression.=
Limit: @1 areas=
Limit: no area count limit=
List your areas, or all areas if you are an admin.=
Move (or resize) an area to the current positions.=
No matches found.=
No visible areas.=
Owner changed.=
Players with the "@1" privilege can protect up to @2 areas=
Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=
Protect your own area=
Recursively remove areas using an ID=
Remove an area using an ID=
Removed area @1=
Removed area @1 and it's sub areas.=
Removes all ownerless areas=
Rename an area that you own=
Self protection is disabled.=
Self protection is enabled.=
That area doesn't exist.=
The player "@1" does not exist.=
Toggle an area open (anyone can interact) or closed=
Toggle an area open/closed for members in your faction.=
You are an area administrator ("areas" privilege).=
You can protect areas=
You can't protect that area.=
You can't protect that area: @1=
You don't have the necessary privilege ("@1").=
You don't own that area.=
You have @1 areas.=
You have been granted control over area #@1. Type /list_areas to show your areas.=
You have extended area protection limits ("areas_high_limit" privilege).=
You have the necessary privilege ("@1").=
You need to select an area first.=
### chatcommands.lua ###
### pos.lua ###
<ID>=
Invalid usage, see /help @1.=
### hud.lua ###
:open=
Areas:=
### init.lua ###
Can administer areas.=
Can protect areas.=
Can protect more, bigger areas.=
### interact.lua ###
@1 is protected by @2.=
### internal.lua ###
Area is too big.=
Self protection is disabled or you do not have the necessary privilege.=
The area intersects with @1 [@2] (@3).=
You have reached the maximum amount of areas that you are allowed to protect.=
### legacy.lua ###
<version>=
Converted areas saved. Done.=
Converting areas…=
Error loading legacy file: @1=
Invalid version number. (0 allowed)=
Legacy file loaded.=
Loads, converts, and saves the areas from a legacy save file.=
Table format updated.=
unnamed=
### pos.lua ###
<not set>=
Area @1 selected.=
Area position @1 set to @2=
Position @1 set to @2=
Position @1: =
Select an area by ID.=
Select position @1 by punching a node.=
Select positions by punching two nodes.=
Set area protection region position @1 to your location or the one specified=
Set area protection region, position 1, or position 2 by punching nodes, or display the region=
The area @1 does not exist.=
Unable to get position.=
Unknown subcommand: @1=

2
mods/areas/mod.conf Normal file
View File

@ -0,0 +1,2 @@
name = areas
optional_depends = playerfactions

265
mods/areas/pos.lua Normal file
View File

@ -0,0 +1,265 @@
local S = minetest.get_translator("areas")
-- I could depend on WorldEdit for this, but you need to have the 'worldedit'
-- permission to use those commands and you don't have
-- /area_pos{1,2} [X Y Z|X,Y,Z].
-- Since this is mostly copied from WorldEdit it is mostly
-- licensed under the AGPL. (select_area is a exception)
areas.marker1 = {}
areas.marker2 = {}
areas.set_pos = {}
areas.pos1 = {}
areas.pos2 = {}
local LIMIT = 30992 -- this is due to MAPBLOCK_SIZE=16!
local function posLimit(pos)
return {
x = math.max(math.min(pos.x, LIMIT), -LIMIT),
y = math.max(math.min(pos.y, LIMIT), -LIMIT),
z = math.max(math.min(pos.z, LIMIT), -LIMIT)
}
end
minetest.register_chatcommand("select_area", {
params = S("<ID>"),
description = S("Select an area by ID."),
func = function(name, param)
local id = tonumber(param)
if not id then
return false, S("Invalid usage, see /help @1.", "select_area")
end
if not areas.areas[id] then
return false, S("The area @1 does not exist.", id)
end
areas:setPos1(name, areas.areas[id].pos1)
areas:setPos2(name, areas.areas[id].pos2)
return true, S("Area @1 selected.", id)
end,
})
minetest.register_chatcommand("area_pos1", {
params = "[X Y Z|X,Y,Z]",
description = S("Set area protection region position @1 to your"
.." location or the one specified", "1"),
privs = {},
func = function(name, param)
local pos
local found, _, x, y, z = param:find(
"^(-?%d+)[, ](-?%d+)[, ](-?%d+)$")
if found then
pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
elseif param == "" then
local player = minetest.get_player_by_name(name)
if player then
pos = player:get_pos()
else
return false, S("Unable to get position.")
end
else
return false, S("Invalid usage, see /help @1.", "area_pos1")
end
pos = posLimit(vector.round(pos))
areas:setPos1(name, pos)
return true, S("Area position @1 set to @2", "1",
minetest.pos_to_string(pos))
end,
})
minetest.register_chatcommand("area_pos2", {
params = "[X Y Z|X,Y,Z]",
description = S("Set area protection region position @1 to your"
.." location or the one specified", "2"),
func = function(name, param)
local pos
local found, _, x, y, z = param:find(
"^(-?%d+)[, ](-?%d+)[, ](-?%d+)$")
if found then
pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
elseif param == "" then
local player = minetest.get_player_by_name(name)
if player then
pos = player:get_pos()
else
return false, S("Unable to get position.")
end
else
return false, S("Invalid usage, see /help @1.", "area_pos2")
end
pos = posLimit(vector.round(pos))
areas:setPos2(name, pos)
return true, S("Area position @1 set to @2", "2",
minetest.pos_to_string(pos))
end,
})
minetest.register_chatcommand("area_pos", {
params = "set/set1/set2/get",
description = S("Set area protection region, position 1, or position 2"
.." by punching nodes, or display the region"),
func = function(name, param)
if param == "set" then -- Set both area positions
areas.set_pos[name] = "pos1"
return true, S("Select positions by punching two nodes.")
elseif param == "set1" then -- Set area position 1
areas.set_pos[name] = "pos1only"
return true, S("Select position @1 by punching a node.", "1")
elseif param == "set2" then -- Set area position 2
areas.set_pos[name] = "pos2"
return true, S("Select position @1 by punching a node.", "2")
elseif param == "get" then -- Display current area positions
local pos1str, pos2str = S("Position @1: ", "1"), S("Position @1: ", "2")
if areas.pos1[name] then
pos1str = pos1str..minetest.pos_to_string(areas.pos1[name])
else
pos1str = pos1str..S("<not set>")
end
if areas.pos2[name] then
pos2str = pos2str..minetest.pos_to_string(areas.pos2[name])
else
pos2str = pos2str..S("<not set>")
end
return true, pos1str.."\n"..pos2str
else
return false, S("Unknown subcommand: @1", param)
end
end,
})
function areas:getPos(playerName)
local pos1, pos2 = areas.pos1[playerName], areas.pos2[playerName]
if not (pos1 and pos2) then
return nil
end
-- Copy positions so that the area table doesn't contain multiple
-- references to the same position.
pos1, pos2 = vector.new(pos1), vector.new(pos2)
return areas:sortPos(pos1, pos2)
end
function areas:setPos1(playerName, pos)
areas.pos1[playerName] = posLimit(pos)
areas.markPos1(playerName)
end
function areas:setPos2(playerName, pos)
areas.pos2[playerName] = posLimit(pos)
areas.markPos2(playerName)
end
minetest.register_on_punchnode(function(pos, node, puncher)
local name = puncher:get_player_name()
-- Currently setting position
if name ~= "" and areas.set_pos[name] then
if areas.set_pos[name] == "pos1" then
areas.pos1[name] = pos
areas.markPos1(name)
areas.set_pos[name] = "pos2"
minetest.chat_send_player(name,
S("Position @1 set to @2", "1",
minetest.pos_to_string(pos)))
elseif areas.set_pos[name] == "pos1only" then
areas.pos1[name] = pos
areas.markPos1(name)
areas.set_pos[name] = nil
minetest.chat_send_player(name,
S("Position @1 set to @2", "1",
minetest.pos_to_string(pos)))
elseif areas.set_pos[name] == "pos2" then
areas.pos2[name] = pos
areas.markPos2(name)
areas.set_pos[name] = nil
minetest.chat_send_player(name,
S("Position @1 set to @2", "2",
minetest.pos_to_string(pos)))
end
end
end)
-- Modifies positions `pos1` and `pos2` so that each component of `pos1`
-- is less than or equal to its corresponding component of `pos2`,
-- returning the two positions.
function areas:sortPos(pos1, pos2)
if pos1.x > pos2.x then
pos2.x, pos1.x = pos1.x, pos2.x
end
if pos1.y > pos2.y then
pos2.y, pos1.y = pos1.y, pos2.y
end
if pos1.z > pos2.z then
pos2.z, pos1.z = pos1.z, pos2.z
end
return pos1, pos2
end
-- Marks area position 1
areas.markPos1 = function(name)
local pos = areas.pos1[name]
if areas.marker1[name] ~= nil then -- Marker already exists
areas.marker1[name]:remove() -- Remove marker
areas.marker1[name] = nil
end
if pos ~= nil then -- Add marker
areas.marker1[name] = minetest.add_entity(pos, "areas:pos1")
areas.marker1[name]:get_luaentity().active = true
end
end
-- Marks area position 2
areas.markPos2 = function(name)
local pos = areas.pos2[name]
if areas.marker2[name] ~= nil then -- Marker already exists
areas.marker2[name]:remove() -- Remove marker
areas.marker2[name] = nil
end
if pos ~= nil then -- Add marker
areas.marker2[name] = minetest.add_entity(pos, "areas:pos2")
areas.marker2[name]:get_luaentity().active = true
end
end
minetest.register_entity("areas:pos1", {
initial_properties = {
visual = "cube",
visual_size = {x=1.1, y=1.1},
textures = {"areas_pos1.png", "areas_pos1.png",
"areas_pos1.png", "areas_pos1.png",
"areas_pos1.png", "areas_pos1.png"},
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
},
on_step = function(self, dtime)
if self.active == nil then
self.object:remove()
end
end,
on_punch = function(self, hitter)
self.object:remove()
local name = hitter:get_player_name()
areas.marker1[name] = nil
end,
})
minetest.register_entity("areas:pos2", {
initial_properties = {
visual = "cube",
visual_size = {x=1.1, y=1.1},
textures = {"areas_pos2.png", "areas_pos2.png",
"areas_pos2.png", "areas_pos2.png",
"areas_pos2.png", "areas_pos2.png"},
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
},
on_step = function(self, dtime)
if self.active == nil then
self.object:remove()
end
end,
on_punch = function(self, hitter)
self.object:remove()
local name = hitter:get_player_name()
areas.marker2[name] = nil
end,
})

45
mods/areas/settings.lua Normal file
View File

@ -0,0 +1,45 @@
local world_path = minetest.get_worldpath()
areas.config = {}
local function setting(name, tp, default)
local full_name = "areas." .. name
local value
if tp == "bool" then
value = minetest.settings:get_bool(full_name)
default = value == nil and minetest.is_yes(default)
elseif tp == "string" then
value = minetest.settings:get(full_name)
elseif tp == "v3f" then
value = minetest.setting_get_pos(full_name)
default = value == nil and minetest.string_to_pos(default)
elseif tp == "float" or tp == "int" then
value = tonumber(minetest.settings:get(full_name))
local v, other = default:match("^(%S+) (.+)")
default = value == nil and tonumber(other and v or default)
else
error("Cannot parse setting type " .. tp)
end
if value == nil then
value = default
assert(default ~= nil, "Cannot parse default for " .. full_name)
end
--print("add", name, default, value)
areas.config[name] = value
end
local file = io.open(areas.modpath .. "/settingtypes.txt", "r")
for line in file:lines() do
local name, tp, value = line:match("^areas%.(%S+) %(.*%) (%S+) (.*)")
if value then
setting(name, tp, value)
end
end
file:close()
--------------
-- Settings --
--------------
setting("filename", "string", world_path.."/areas.dat")

View File

@ -0,0 +1,37 @@
# This file is parsed in "settings.lua". Check regex first.
# Static paths do not work well with settings
#areas.filename (Configuration file path) string (world_path)/areas.dat
# Allow players with a privilege create their own areas using /protect
# within the specified size and amount limits.
areas.self_protection (Self protection) bool false
# Self protection: Privilege required to protect an area
areas.self_protection_privilege (Self protection: Required privs) string interact
# Refresh delay for the name displays in the HUD in seconds
areas.tick (HUD update delay) float 0.5 0 100
# Enable the legacy owner_defs metatable mode. Untested and possibly unstable
areas.legacy_table (Legacy owner_defs metatable) bool false
[Self protection (normal)]
# Self protection (normal): Maximal size of the protectable area
# Only enter positive whole numbers for the coordinate values or you'll mess up stuff.
areas.self_protection_max_size (Maximal area size) v3f (64, 128, 64)
# Self protection (normal): Maximal amount of protected areas per player
areas.self_protection_max_areas (Maximal area count) int 4
[Self protection (high)]
# Self protection (normal): Maximal size of the protectable area
# This setting applies for plyaers with the privilege 'areas_high_limit'
areas.self_protection_max_size_high (Maximal area size) v3f (512, 512, 512)
# Self protection (normal): Maximal amount of protected areas per player
# Only enter positive whole numbers for the coordinate values or you'll mess up stuff.
# This setting applies for plyaers with the privilege 'areas_high_limit'
areas.self_protection_max_areas_high (Maximal area count) float 32

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

3
mods/markers/README.md Normal file
View File

@ -0,0 +1,3 @@
This is work in progress.

865
mods/markers/areas.lua Normal file
View File

@ -0,0 +1,865 @@
-- TODO: offer teleport button?
-- Temporary compatibility function - see minetest PR#1180
if not vector.interpolate then
vector.interpolate = function(pos1, pos2, factor)
return {x = pos1.x + (pos2.x - pos1.x) * factor,
y = pos1.y + (pos2.y - pos1.y) * factor,
z = pos1.z + (pos2.z - pos1.z) * factor}
end
end
-- taken from mobf
local COLOR_RED = "#FF0000";
local COLOR_GREEN = "#00FF00";
local COLOR_WHITE = "#FFFFFF";
-- we need to store which list we present to which player
markers.menu_data_by_player = {}
markers.get_area_by_pos = function(pos)
local found_areas = {};
for id, area in pairs(areas.areas) do
if( pos.x >= area.pos1.x and pos.x <= area.pos2.x and
pos.y >= area.pos1.y and pos.y <= area.pos2.y and
pos.z >= area.pos1.z and pos.z <= area.pos2.z )then
area[ 'id' ] = id;
table.insert(found_areas, area );
end
end
return found_areas;
end
-- ppos: current player (or marker stone) position - used for sorting the list
-- mode: can be pos, player, all, subarea, main_areas
-- mode_data: content depends on mode
-- selected: display information about the area the player single-clicked on
markers.get_area_list_formspec = function(ppos, player, mode, pos, mode_data, selected )
local id_list = {};
local title = '???';
local tlabel = '';
-- expects a position in mode_data
if( mode=='pos' ) then
-- title would be too long for a label
title = 'All areas which contain position..';
tlabel = '<'..minetest.pos_to_string( mode_data )..'>:';
for id, area in pairs(areas.areas) do
if( mode_data.x >= area.pos1.x and mode_data.x <= area.pos2.x and
mode_data.y >= area.pos1.y and mode_data.y <= area.pos2.y and
mode_data.z >= area.pos1.z and mode_data.z <= area.pos2.z )then
table.insert( id_list, id );
end
end
-- expects a playername in mode_data
elseif( mode=='player' ) then
title = 'All areas owned by player..';
tlabel = '<'..tostring( mode_data )..'>:';
for id, area in pairs(areas.areas) do
if( area.owner == mode_data ) then
table.insert( id_list, id );
end
end
-- expects an area_id in mode_data
elseif( mode=='subareas' ) then
title = 'All subareas of area..';
tlabel = '<'..tostring( areas.areas[ mode_data ].name )..'> ['..tostring( mode_data )..']:';
for id, area in pairs(areas.areas) do
if( area.parent and area.parent == mode_data ) then
table.insert( id_list, id );
end
end
-- show only areas that do not have parents
elseif( mode=='main_areas' ) then
title = 'All main areas withhin '..tostring( markers.AREA_RANGE )..' m:';
tlabel = '*all main areas*';
for id, area in pairs(areas.areas) do
if( not( area.parent )
-- ppos is always available
and( (area.pos1.x >= ppos.x-markers.AREA_RANGE and area.pos1.x <= ppos.x+markers.AREA_RANGE )
or(area.pos2.x >= ppos.x-markers.AREA_RANGE and area.pos2.x <= ppos.x+markers.AREA_RANGE ))
and( (area.pos1.y >= ppos.y-markers.AREA_RANGE and area.pos1.y <= ppos.y+markers.AREA_RANGE )
or(area.pos2.y >= ppos.y-markers.AREA_RANGE and area.pos2.y <= ppos.y+markers.AREA_RANGE ))
and( (area.pos1.z >= ppos.z-markers.AREA_RANGE and area.pos1.z <= ppos.z+markers.AREA_RANGE )
or(area.pos2.z >= ppos.z-markers.AREA_RANGE and area.pos2.z <= ppos.z+markers.AREA_RANGE ))) then
table.insert( id_list, id );
end
end
elseif( mode=='all' ) then
title = 'All areas withhin '..tostring( markers.AREA_RANGE )..' m:';
tlabel = '*all areas*';
for id, area in pairs(areas.areas) do
if( ( (area.pos1.x >= ppos.x-markers.AREA_RANGE and area.pos1.x <= ppos.x+markers.AREA_RANGE )
or(area.pos2.x >= ppos.x-markers.AREA_RANGE and area.pos2.x <= ppos.x+markers.AREA_RANGE ))
and( (area.pos1.y >= ppos.y-markers.AREA_RANGE and area.pos1.y <= ppos.y+markers.AREA_RANGE )
or(area.pos2.y >= ppos.y-markers.AREA_RANGE and area.pos2.y <= ppos.y+markers.AREA_RANGE ))
and( (area.pos1.z >= ppos.z-markers.AREA_RANGE and area.pos1.z <= ppos.z+markers.AREA_RANGE )
or(area.pos2.z >= ppos.z-markers.AREA_RANGE and area.pos2.z <= ppos.z+markers.AREA_RANGE ))) then
table.insert( id_list, id );
end
end
end
-- Sort the list of areas so the nearest comes first
local nearsorter = function(a, b)
return vector.distance(vector.interpolate(areas.areas[a].pos1, areas.areas[a].pos2, 0.5), ppos) <
vector.distance(vector.interpolate(areas.areas[b].pos1, areas.areas[b].pos2, 0.5), ppos)
end
table.sort(id_list, nearsorter)
local formspec = 'size[10,9]';
title = minetest.formspec_escape( title );
tlabel = minetest.formspec_escape( tlabel );
formspec = formspec..
"label[0.5,0;"..title.."]"..
"label[4.7,0;"..tlabel.."]"..
"label[0.5,8.5;Doubleclick to select area.]"..
"label[4.7,8.5;Areas found: "..tostring( #id_list )..".]"..
"textlist[0.5,0.5;7,8;markers_area_list_selection;";
local liste = '';
for i,v in ipairs( id_list ) do
if( liste ~= '' ) then
liste = liste..',';
end
liste = liste..minetest.formspec_escape( areas:toString( v ) );
end
-- highlight selected entry
if( selected ) then
formspec = formspec..liste..';'..selected..';false]';
else
formspec = formspec..liste..';]';
end
local pname = player:get_player_name();
if( not( markers.menu_data_by_player[ pname ] )) then
markers.menu_data_by_player[ pname ] = {};
end
-- display information about the location of the area the player clicked on
if( selected
and id_list[ selected ]
and areas.areas[ id_list[ selected ]] ) then
local this_area = areas.areas[ id_list[ selected ]];
local subareas = {};
for i,v in pairs( areas.areas ) do
if( v.parent and v.parent == id_list[ selected ]) then
table.insert( subareas, i );
end
end
formspec = formspec..
markers.show_compass_marker( 8.5, 3.0, false, pos, this_area.pos1, this_area.pos2 );
if( this_area.parent) then
formspec = formspec..
'button[8.0,0.5;2,0.5;show_parent;'..
minetest.formspec_escape( areas.areas[ this_area.parent ].name )..']';
end
if( #subareas > 0 ) then
formspec = formspec..
'button[8.0,1.0;2,0.5;list_subareas;'..
minetest.formspec_escape( 'List subareas ('..tostring( #subareas )..')')..']';
end
if( mode=='player' ) then
formspec = formspec..
'label[8.0,1.5;'..
minetest.formspec_escape( this_area.owner..'\'s areas')..']';
else
formspec = formspec..
'button[8.0,1.5;2,0.5;list_player_areas;'..
minetest.formspec_escape( this_area.owner..'\'s areas')..']';
end
end
formspec = formspec..
'button[8.0,8.5;2,0.5;list_main_areas;List all main areas]';
-- we need to remember especially the id_list - else it would be impossible to know what the
-- player selected
markers.menu_data_by_player[ pname ] = {
typ = 'area_list',
mode = mode,
pos = pos,
mode_data = mode_data,
list = id_list,
selected = id_list[ selected ],
};
return formspec;
end
-- shows a formspec with information about a particular area
-- pos is the position of the marker stone or place where the player clicked
-- with the land title register; it is used for relative display of coordinates
markers.get_area_desc_formspec = function( id, player, pos )
if( not( areas.areas ) or not( id ) or not( areas.areas[ id ] )) then
return 'field[info;Error:;Area not found.]';
end
local this_area = areas.areas[ id ];
local pname = player:get_player_name();
-- show some buttons only if area is owned by the player
local is_owner = false;
if( this_area.owner == pname ) then
is_owner = true;
end
local formspec = 'size[10,9]'..
'label[2.5,0.0;Area information and management]'..
'button_exit[4.7,7.0;1,0.5;abort;OK]';
-- general information about the area
formspec = formspec..
'label[0.5,1.0;This is area number ]'..
'label[4.7,1.0;'..tostring( id )..']'..
'label[0.5,1.5;The area is called ]'..
'label[4.7,1.5;'..minetest.formspec_escape( this_area.name or '-not set-')..']'..
'label[0.5,2.0;It is owned by ]'..
'label[4.7,2.0;'..minetest.formspec_escape( this_area.owner)..']';
-- these functions are only available to the owner of the area
if( is_owner ) then
formspec = formspec..
'button_exit[8.0,0.0;2,0.5;change_owner;Change owner]'..
'button_exit[8.0,1.0;2,0.5;delete;Delete]'..
'button_exit[8.0,1.5;2,0.5;rename;Rename]'..
'button_exit[8.0,2.0;2,0.5;list_player_areas;My areas]';
-- subareas of own areas can be deleted (but not renamed)
elseif( not( is_owner )
and this_area.parent
and areas.areas[ this_area.parent ]
and areas.areas[ this_area.parent ].owner == pname ) then
formspec = formspec..
'button_exit[8.0,1.0;2,0.5;delete;Delete subarea]'..
'button_exit[8.0,2.0;2,0.5;list_player_areas;Player\'s areas]';
else
formspec = formspec..
'button_exit[8.0,2.0;2,0.5;list_player_areas;Player\'s areas]';
end
-- players with the areas priv get an extra menu
if( minetest.check_player_privs(pname, {areas=true})) then
formspec = formspec..
'label[8.0,6.0;Admin commands:]'..
'button_exit[8.0,6.5;2,0.5;change_owner;Change owner]'..
'button_exit[8.0,7.0;2,0.5;delete;Delete]'..
'button_exit[8.0,7.5;2,0.5;rename;Rename]';
end
-- show subowners and areas with the same coordinates
formspec = formspec..
'label[0.5,2.5;Further owners of the entire area:]';
local further_owners = {};
for i, area in pairs(areas.areas) do
if( i ~= id and
this_area.pos1.x == area.pos1.x and this_area.pos2.x == area.pos2.x and
this_area.pos1.y == area.pos1.y and this_area.pos2.y == area.pos2.y and
this_area.pos1.z == area.pos1.z and this_area.pos2.z == area.pos2.z )then
table.insert( further_owners, area.owner );
end
end
if( #further_owners > 0 ) then
formspec = formspec..
'label[4.7,2.5;'..minetest.formspec_escape( table.concat( further_owners, ', '))..'.]';
-- deleting subowners is done by deleting their areas
if( is_owner ) then
formspec = formspec..
'button_exit[8.0,2.5;2,0.5;add_owner;Add]';
end
else
formspec = formspec..
'label[4.7,2.5;-none-]';
if( is_owner ) then
formspec = formspec..
'button_exit[8.0,2.5;2,0.5;add_owner;Add]';
end
end
-- is the area a subarea?
if( this_area.parent ) then
formspec = formspec..
'label[0.5,3.0;This area is a sub-area of area]'..
'label[4.7,3.0;'..minetest.formspec_escape( areas.areas[ this_area.parent ].name..' ['..this_area.parent..']' )..']'..
'button_exit[8.0,3.0;2,0.5;show_parent;Show main area]';
end
-- does the area have subareas, i.e. is it a parent area for others?
local sub_areas = {};
for i, area in pairs(areas.areas) do
if( i ~= id and
area.parent == id ) then
table.insert( sub_areas, i );
end
end
if( #sub_areas > 0 ) then
formspec = formspec..
'label[0.5,4.0;Number of defined subareas:]'..
'label[4.7,4.0;'..tostring( #sub_areas )..']'..
'button_exit[8.0,4.0;2,0.5;list_subareas;List subareas]';
else
formspec = formspec..
'label[0.5,4.0;There are no subareas defined.]';
end
-- give information about the size of the area
local length_x = (math.abs( this_area.pos2.x - this_area.pos1.x )+1);
local length_y = (math.abs( this_area.pos2.y - this_area.pos1.y )+1);
local length_z = (math.abs( this_area.pos2.z - this_area.pos1.z )+1);
formspec = formspec..
'label[0.5,4.5;The area extends from]'..
'label[4.7,4.5;'..minetest.pos_to_string( this_area.pos1 )..' to '..minetest.pos_to_string( this_area.pos2 )..'.]'..
'label[4.7,4.75;It spans '..tostring( length_x )..
' x '..tostring( length_z )..
' = '..tostring( length_x * length_z )..
' m^2. Height: '..tostring( length_y )..' m.]';
formspec = formspec..
markers.show_compass_marker( 2.0, 7.0, true, pos, this_area.pos1, this_area.pos2 );
-- TODO: buy / sell button
local pname = player:get_player_name();
if( not( markers.menu_data_by_player[ pname ] )) then
markers.menu_data_by_player[ pname ] = {};
end
-- we need to remember especially the id_list - else it would be impossible to know what the
-- player selected
markers.menu_data_by_player[ pname ] =
{ typ = 'show_area',
mode = nil,
pos = pos,
mode_data = nil,
list = nil,
selected = id,
};
return formspec;
end
-- shows where the area (defined by pos1/pos2) is located relative to the given position pos
-- row_offset/col_offset are offsets for the formspec
markers.show_compass_marker = function( col_offset, row_offset, with_text, pos, pos1, pos2 )
local formspec = '';
-- TODO: show up/down information somehow
-- TODO: what if checked with a land claim register?
-- if possible, show how far the area streches into each direction relative to pos
if( pos.x >= pos1.x and pos.x <= pos2.x
and pos.y >= pos1.y and pos.y <= pos2.y
and pos.z >= pos1.z and pos.z <= pos2.z ) then
if( with_text ) then
formspec = formspec..
'label[0.5,5.5;Dimensions of the area in relation to..]'..
-- TODO: check if there is a marker; else write 'position you clicked on'
'label[4.7,5.5;the marker at '..minetest.pos_to_string( pos )..':]'..
'button_exit[8.0,5.5;2,0.5;list_areas_at;Local areas]';
end
formspec = formspec..
'image['..col_offset..','..row_offset..';1,1;markers_stone.png]'..
'label['..(col_offset-0.8)..','..(row_offset+0.05)..';'..tostring( pos.x - pos1.x )..' m W]'..
'label['..(col_offset+1.0)..','..(row_offset+0.05)..';'..tostring( pos2.x - pos.x )..' m E]'..
'label['..(col_offset+0.1)..','..(row_offset+0.80)..';'..tostring( pos.z - pos1.z )..' m S]'..
'label['..(col_offset+0.1)..','..(row_offset-0.80)..';'..tostring( pos2.z - pos.z )..' m N]';
-- else show how far the area is away
else
local starts_north = '';
local starts_south = '';
local starts_east = '';
local starts_west = '';
if( pos.z > pos2.z ) then
starts_north = '';
starts_south = tostring( pos.z - pos2.z )..' m S';
else
starts_north = tostring( pos1.z - pos.z )..' m N';
starts_south = '';
end
if( pos.x > pos2.x ) then
starts_east = '';
starts_west = tostring( pos.x - pos2.x )..' m W';
else
starts_east = tostring( pos1.x - pos.x )..' m E';
starts_west = '';
end
if( with_text ) then
formspec = formspec..
'label[0.5,5.5;Position of the area in relation to..]'..
-- TODO: check if there is a marker; else write 'position you clicked on'
'label[4.7,5.5;the marker at '..minetest.pos_to_string( pos )..':]'..
'button_exit[8.0,5.5;2,0.5;list_areas_at;Local areas]';
end
formspec = formspec..
'image['..col_offset..','..row_offset..';1,1;compass_side_top.png]'..
'label['..(col_offset-0.8)..','..(row_offset+0.05)..';'..starts_west..']'..
'label['..(col_offset+1.0)..','..(row_offset+0.05)..';'..starts_east..']'..
'label['..(col_offset+0.1)..','..(row_offset-0.80)..';'..starts_north..']'..
'label['..(col_offset+0.1)..','..(row_offset+0.80)..';'..starts_south..']';
end
return formspec;
end
-- formspec input needs to be handled diffrently
markers.form_input_handler_areas = function( player, formname, fields)
local pname = player:get_player_name();
local ppos = player:getpos()
if( formname ~= "markers:info"
or not( player )
or not( markers.menu_data_by_player[ pname ] )) then
return false;
end
local menu_data = markers.menu_data_by_player[ pname ];
local formspec = '';
-- rename an area
if( fields.rename
and menu_data.selected
and areas.areas[ menu_data.selected ]
and areas.areas[ menu_data.selected ].owner == pname ) then
local area = areas.areas[ menu_data.selected ];
if( not( area.name )) then
area.name = '-enter area name-';
end
formspec = 'field[rename_new_name;Enter new name for area:;'..minetest.formspec_escape( area.name )..']';
elseif( fields.rename_new_name
and menu_data.selected
and areas.areas[ menu_data.selected ]
and ((areas.areas[ menu_data.selected ].owner == pname )
or minetest.check_player_privs(pname, {areas=true}))) then
local area = areas.areas[ menu_data.selected ];
-- actually rename the area
areas.areas[ menu_data.selected ].name = fields.rename_new_name;
areas:save();
minetest.chat_send_player( pname, 'Area successfully renamed.');
-- shwo the renamed area
formspec = markers.get_area_desc_formspec( menu_data.selected, player, menu_data.pos );
-- change owner the area
elseif( fields.change_owner
and menu_data.selected
and areas.areas[ menu_data.selected ] ) then
-- there are no checks here - those happen when the area is transferred
local area = areas.areas[ menu_data.selected ];
formspec = 'field[change_owner_name;Give area \"'..minetest.formspec_escape( area.name )..'\" to player:;-enter name of NEW OWNER-]';
elseif( fields.change_owner_name
and menu_data.selected
and areas.areas[ menu_data.selected ] ) then
local area = areas.areas[ menu_data.selected ];
-- only own areas can be transfered to another player (or if the areas priv is there)
if( area.owner ~= pname
and not( minetest.check_player_privs(pname, {areas=true}))) then
minetest.chat_send_player( pname, 'Permission denied. You do not own the area.');
elseif( not( areas:player_exists( fields.change_owner_name ))) then
minetest.chat_send_player( pname, 'That player does not exist.');
else
-- actually change the owner
areas.areas[ menu_data.selected ].owner = fields.change_owner_name;
areas:save()
minetest.chat_send_player( pname, 'Your area '..tostring( area.name )..' has been transfered to '..tostring( fields.change_owner_name )..'.');
minetest.chat_send_player( fields.change_owner_name, pname..'" has given you control over an area.')
end
formspec = markers.get_area_desc_formspec( menu_data.selected, player, menu_data.pos );
-- add an owner to the entire area
elseif( fields.add_owner
and menu_data.selected
and areas.areas[ menu_data.selected ]
and areas.areas[ menu_data.selected ].owner == pname ) then
local area = areas.areas[ menu_data.selected ];
formspec = 'field[add_owner_name;Grant access to area \"'..minetest.formspec_escape( area.name )..'\" to player:;-enter player name-]';
elseif( fields.add_owner_name
-- the player has to own the area already; we need a diffrent name here
and fields.add_owner_name ~= pname
and menu_data.selected
and areas.areas[ menu_data.selected ]
and areas.areas[ menu_data.selected ].owner == pname ) then
local area = areas.areas[ menu_data.selected ];
-- does the player exist?
if( not( areas:player_exists( fields.add_owner_name ))) then
minetest.chat_send_player( pname, 'That player does not exist.');
-- show the formspec
formspec = markers.get_area_desc_formspec( menu_data.selected, player, menu_data.pos );
else
-- log the creation of the new area
minetest.log("action", pname.." runs /add_owner through the markers-mod. Owner = "..fields.add_owner_name..
" AreaName = "..area.name.." ParentID = "..menu_data.selected..
" StartPos = "..area.pos1.x..","..area.pos1.y..","..area.pos1.z..
" EndPos = " ..area.pos2.x..","..area.pos2.y..","..area.pos2.z)
-- take the name from the old/parent area
local new_id = areas:add( fields.add_owner_name, area.name, area.pos1, area.pos2, menu_data.selected );
areas:save()
minetest.chat_send_player( fields.add_owner_name,
"You have been granted control over area #"..
new_id..". Type /list_areas to show your areas.")
minetest.chat_send_player( pname, 'The player may now build and dig in your area.');
-- shwo the new area
markers.menu_data_by_player[ pname ].selected = new_id;
formspec = markers.get_area_desc_formspec( new_id, player, menu_data.pos );
end
-- delete area
elseif( fields.delete
and menu_data.selected
and areas.areas[ menu_data.selected ] ) then
local area = areas.areas[ menu_data.selected ];
-- a player can only delete own areas or subareas of own areas
if( area.owner ~= pname
and not( area.parent
and areas.areas[ area.parent ]
and areas.areas[ area.parent ].owner
and areas.areas[ area.parent ].owner == pname )
and not( minetest.check_player_privs(pname, {areas=true}))) then
minetest.chat_send_player( pname, 'Permission denied. You own neither the area itshelf nor its parent area.');
-- shwo the area where the renaming failed
formspec = markers.get_area_desc_formspec( menu_data.selected, player, menu_data.pos );
else
formspec = 'field[rename_new_name;Enter new name for area:;'..minetest.formspec_escape( area.name )..']';
formspec = 'field[delete_confirm;'..minetest.formspec_escape( 'Really delete area \"'..area.name..
'\" (owned by '..area.owner..')? Confirm with YES:')..';-type yes in capitals to confirm-]';
end
elseif( fields.delete_confirm
and menu_data.selected
and areas.areas[ menu_data.selected ] ) then
local area = areas.areas[ menu_data.selected ];
local old_owner = area.owner;
local subareas = {};
for i,v in pairs( areas.areas ) do
if( v.parent and v.parent == menu_data.selected ) then
table.insert( subareas, i );
end
end
-- a player can only delete own areas or subareas of own areas
if( area.owner ~= pname
and not( area.parent
and areas.areas[ area.parent ]
and areas.areas[ area.parent ].owner
and areas.areas[ area.parent ].owner == pname )
and not( minetest.check_player_privs(pname, {areas=true}))) then
minetest.chat_send_player( pname, 'Permission denied. You own neither the area itshelf nor its parent area.');
-- shwo the renamed area
formspec = markers.get_area_desc_formspec( menu_data.selected, player, menu_data.pos );
-- avoid accidents
elseif( fields.delete_confirm ~= 'YES' ) then
minetest.chat_send_player( pname, 'Delition of area \"'..tostring( area.name )..'\" (owned by '..old_owner..') aborted.');
formspec = markers.get_area_desc_formspec( menu_data.selected, player, menu_data.pos );
-- only areas without subareas can be deleted
elseif( #subareas > 0 ) then
minetest.chat_send_player( pname, 'The area has '..tostring( #subareas )..' subarea(s). Please delete those first!');
formspec = markers.get_area_desc_formspec( menu_data.selected, player, menu_data.pos );
else
minetest.chat_send_player( pname, 'Area \"'..tostring( area.name )..'\" (owned by '..old_owner..') deleted.');
-- really delete
areas:remove( menu_data.selected, false ); -- no recursive delete
areas:save();
-- show the list of areas owned by the previous owner
formspec = markers.get_area_list_formspec(ppos, player, 'player', menu_data.pos, old_owner, nil );
end
elseif( fields.show_parent
and menu_data.selected
and areas.areas[ menu_data.selected ]
and areas.areas[ menu_data.selected ].parent ) then
formspec = markers.get_area_desc_formspec( areas.areas[ menu_data.selected ].parent, player, menu_data.pos );
elseif( fields.list_player_areas
and menu_data.selected
and areas.areas[ menu_data.selected ] ) then
formspec = markers.get_area_list_formspec(ppos, player, 'player', menu_data.pos, areas.areas[ menu_data.selected ].owner, nil );
elseif( fields.list_subareas
and menu_data.selected
and areas.areas[ menu_data.selected ] ) then
formspec = markers.get_area_list_formspec(ppos, player, 'subareas', menu_data.pos, menu_data.selected, nil );
elseif( fields.list_main_areas ) then
formspec = markers.get_area_list_formspec(ppos, player, 'main_areas', menu_data.pos, nil, nil );
elseif( fields.list_areas_at
and menu_data.pos ) then
formspec = markers.get_area_list_formspec(ppos, player, 'pos', menu_data.pos, menu_data.pos, nil );
elseif( fields.markers_area_list_selection
and menu_data.typ
and menu_data.typ == 'area_list'
and menu_data.list
and #menu_data.list > 0 ) then
local field_data = fields.markers_area_list_selection:split( ':' );
if( not( field_data ) or #field_data < 2 ) then
field_data = { '', '' };
end
local selected = tonumber( field_data[ 2 ] );
if( field_data[1]=='DCL' ) then
-- on doubleclick, show detailed area information
formspec = markers.get_area_desc_formspec( tonumber( menu_data.list[ selected ] ), player, menu_data.pos );
else
-- on single click, just show the position of that particular area
formspec = markers.get_area_list_formspec(ppos, player, menu_data.mode, menu_data.pos, menu_data.mode_data, selected );
end
else
return false;
end
minetest.show_formspec( pname, "markers:info", formspec )
return true;
end
-- search the area at the given position pos that might be of most intrest to the player
markers.show_marker_stone_formspec = function( player, pos )
local pname = player:get_player_name();
local ppos = pos
-- this table stores the list the player may have selected from; at the beginning, there is no list
if( not( markers.menu_data_by_player[ pname ] )) then
markers.menu_data_by_player[ pname ] = {
typ = 'area_list',
mode = 'main_areas',
pos = pos,
mode_data = pos,
list = {},
selected = nil,
};
end
local formspec = '';
local found_areas = {};
local min_area_size = 100000000000;
for id, area in pairs(areas.areas) do
if( pos.x >= area.pos1.x and pos.x <= area.pos2.x and
pos.y >= area.pos1.y and pos.y <= area.pos2.y and
pos.z >= area.pos1.z and pos.z <= area.pos2.z )then
-- ignore y (height) value because some areas may go from bottom to top
local area_size = math.abs( area.pos2.x - area.pos1.x )
* math.abs( area.pos2.z - area.pos1.z );
-- collect subareas that have the same size
if( area_size == min_area_size ) then
table.insert(found_areas, id );
-- we have found a smaller area - that is more intresting here
elseif( area_size <= min_area_size ) then
found_areas = {};
min_area_size = area_size;
table.insert(found_areas, id );
end
end
end
-- no areas found; display error message and selection menu
if( #found_areas < 1 ) then
formspec = 'size[4,3]'..
'label[0.5,0.5;This position is not protected.]'..
'button[1.0,1.5;2,0.5;list_main_areas;List all main areas]'..
'button_exit[3.0,1.5;1,0.5;abort;OK]';
-- found exactly one areaa - display it
elseif( #found_areas == 1 ) then
formspec = markers.get_area_desc_formspec( found_areas[ 1 ], player, pos );
-- found more than one area; we have saved only those with the smallest size
else
local own_area = 0;
local parent_area = 0;
local upper_area = 0;
for i,v in ipairs( found_areas ) do
local area = areas.areas[ v ];
-- owned by player?
if( area.owner == pname ) then
own_area = v;
-- parentless area?
elseif( not( area.parent )) then
parent_area = v;
-- the parent has diffrent coordinates?
elseif( areas.areas[ area.parent ].pos1.x ~= area.pos1.x
or areas.areas[ area.parent ].pos1.y ~= area.pos1.y
or areas.areas[ area.parent ].pos1.z ~= area.pos1.z
or areas.areas[ area.parent ].pos2.x ~= area.pos2.x
or areas.areas[ area.parent ].pos2.y ~= area.pos2.y
or areas.areas[ area.parent ].pos2.z ~= area.pos2.z ) then
upper_area = v;
end
end
-- the area owned by the player is most intresting
if( own_area > 0 ) then
formspec = markers.get_area_desc_formspec( own_area, player, pos );
-- if the player owns none of these areas, show the topmost (parentless) area
elseif( parent_area > 0 ) then
formspec = markers.get_area_desc_formspec( parent_area, player, pos );
-- an area which has a parent with diffrent coordinates from its child may (or may not) be the
-- parent of all these subareas we've found here; there is no guarantee, but it's a best guess.
-- If it is not good enough, then the player can still search for himshelf.
elseif( upper_area > 0 ) then
formspec = markers.get_area_desc_formspec( upper_area, player, pos );
-- our superficial analysis of the structure of the areas failed; it is up to the player to
-- find out which of the candidates he is intrested in; we list them all
else
formspec = markers.get_area_list_formspec(ppos, player, 'pos', pos, pos, nil );
end
end
minetest.show_formspec( player:get_player_name(), "markers:info", formspec );
end

47
mods/markers/config.lua Normal file
View File

@ -0,0 +1,47 @@
-- stores up to 4 marker positions for each player
markers.positions = {}
-- store the positions of that many markers for each player (until server restart)
markers.MAX_MARKERS = 50;
-- the protection against digging of the marker by other players expires after this time
markers.EXPIRE_AFTER = 60*60*24;
-- self-protected areas can not get higher than 100 blocks
markers.MAX_HEIGHT = 100;
-- only areas up to this size (in square meters) can be protected
markers.MAX_SIZE = 4096; -- 64m * 64m= 4096 m^2
-- show only areas withhin this range when showing the list of ALL areas
-- (else it does get too crowded on multiplayer servers)
-- set to something >60000 in order to view all areas; set to a smaller
-- value (i.e. 500) on multiplayer servers with many protected areas
if( #areas.areas > 1000 ) then
markers.AREA_RANGE = 100;
elseif( #areas.areas > 100 ) then
markers.AREA_RANGE = 1000;
else
markers.AREA_RANGE = 100000;
end
-- for most cases, the default values ought to work
--markers.AREA_RANGE = 100000;
local settings = Settings( minetest.get_worldpath().."/markers_settings.txt");
if( settings ) then
if( settings:get( "markers_MAX_MARKERS" )) then
markers.MAX_MARKERS = tonumber(settings:get( "markers_MAX_MARKERS"));
end
if( settings:get( "markers_EXPIRE_AFTER" )) then
markers.EXPIRE_AFTER = tonumber(settings:get( "markers_EXPIRE_AFTER"));
end
if( settings:get( "markers_MAX_HEIGHT" )) then
markers.MAX_HEIGHT = tonumber(settings:get( "markers_MAX_HEIGHT"));
end
if( settings:get( "markers_MAX_SIZE" )) then
markers.MAX_SIZE = tonumber(settings:get( "markers_MAX_SIZE"));
end
end

1
mods/markers/depends.txt Normal file
View File

@ -0,0 +1 @@
areas

536
mods/markers/init.lua Normal file
View File

@ -0,0 +1,536 @@
-- markers are useful for measuring distances and for marking areas
-- markers are protected from digging by other players for one day
-- (the protection for the *marker* auto-expires then, and it can be digged)
markers = {}
dofile(minetest.get_modpath("markers").."/config.lua");
dofile(minetest.get_modpath("markers").."/areas.lua");
dofile(minetest.get_modpath("markers").."/marker_stone.lua");
dofile(minetest.get_modpath("markers").."/land_title_register.lua");
-- returns the first area found
markers.get_area_by_pos1_pos2 = function(pos1, pos2)
for id, area in pairs(areas.areas) do
if( ((area.pos1.x == pos1.x and area.pos1.z == pos1.z )
or (area.pos1.x == pos1.x and area.pos1.z == pos2.z )
or (area.pos1.x == pos2.x and area.pos1.z == pos1.z )
or (area.pos1.x == pos2.x and area.pos1.z == pos2.z ))
and((area.pos2.x == pos1.x and area.pos2.z == pos1.z )
or (area.pos2.x == pos1.x and area.pos2.z == pos2.z )
or (area.pos2.x == pos2.x and area.pos2.z == pos1.z )
or (area.pos2.x == pos2.x and area.pos2.z == pos2.z ))) then
-- at least pos1 needs to have a hight value that fits in
if( (area.pos1.y <= pos1.y and area.pos2.y >= pos1.y)
or (area.pos1.y >= pos1.y and area.pos2.y <= pos1.y)) then
local found = area;
found[ 'id' ] = id;
return found;
end
end
end
return nil;
end
-- this function is supposed to return a text string describing the price of the land between po1 and pos2
-- You can return somethiing like "for free" or "the promise to build anything good" as well as any
-- real prices in credits or materials - it's really just a text here.
-- Make sure you do not charge the player more than what you ask here.
markers.calculate_area_price_text = function( pos1, pos2, playername )
local price = ( math.abs( pos1.x - pos2.x )+1 )
* ( math.abs( pos1.z - pos2.z )+1 );
-- * math.ceil( ( math.abs( pos1.y - pos2.y )+1 )/10);
return tostring( price )..' credits';
end
markers.marker_placed = function( pos, placer, itemstack )
if( not( pos ) or not( placer )) then
return;
end
local meta = minetest.get_meta( pos );
local name = placer:get_player_name();
meta:set_string( 'infotext', 'Marker at '..minetest.pos_to_string( pos )..
' (placed by '..tostring( name )..'). '..
'Right-click to update.');
meta:set_string( 'owner', name );
-- this allows protection of this particular marker to expire
meta:set_string( 'time', tostring( os.time()) );
local txt = '';
if( not( markers.positions[ name ] ) or #markers.positions[name]<1) then
markers.positions[ name ] = {};
markers.positions[ name ][ 1 ] = pos;
minetest.chat_send_player( name,
'First marker set to position '..
minetest.pos_to_string( markers.positions[ name ][ 1 ] )..
'. Please place a second marker to measure distance. '..
'Place four markers in a square to define an area.');
else
table.insert( markers.positions[ name ], pos );
local n = #markers.positions[ name ];
local dx = markers.positions[ name ][ n ].x - markers.positions[ name ][ n-1 ].x;
local dy = markers.positions[ name ][ n ].y - markers.positions[ name ][ n-1 ].y;
local dz = markers.positions[ name ][ n ].z - markers.positions[ name ][ n-1 ].z;
local dir_name = "unknown";
local d = 0;
if( dx == 0 and dz > 0 ) then dir_name = "north"; d = math.abs(dz);
elseif( dx == 0 and dz < 0 ) then dir_name = "south"; d = math.abs(dz);
elseif( dz == 0 and dx > 0 ) then dir_name = "east"; d = math.abs(dx);
elseif( dz == 0 and dx < 0 ) then dir_name = "west"; d = math.abs(dx);
elseif( dx == 0 and dz == 0 and dy > 0 ) then dir_name = "above"; d = math.abs(dy);
elseif( dx == 0 and dz == 0 and dy < 0 ) then dir_name = "below"; d = math.abs(dy);
else
local area = (math.abs( dx )+1)
* (math.abs( dz )+1);
local volume = area * (math.abs( dy )+1);
minetest.chat_send_player( name, 'This marker is at '..
minetest.pos_to_string( markers.positions[ name ][ n ] )..', while the last one is at '..
minetest.pos_to_string( markers.positions[ name ][ n-1 ] )..'. Distance (x/y/z): '..
tostring(math.abs(dx))..'/'..
tostring(math.abs(dy))..'/'..
tostring(math.abs(dz))..
'. Area: '..tostring( area )..' m^2. Volume: '..tostring( volume )..' m^3.');
end
-- this marker is aligned to the last one
if( d > 0 ) then
minetest.chat_send_player( name, 'Marker placed at '..minetest.pos_to_string( pos )..
'. Relative to the marker you placed before, this one is '..
tostring( d )..' m '..dir_name..'.');
end
-- make sure the list does not grow too large
if( n > markers.MAX_MARKERS ) then
table.remove( markers.positions[ name ], 1 );
end
end
end
markers.marker_can_dig = function(pos,player)
if( not( pos ) or not( player )) then
return true;
end
local meta = minetest.get_meta( pos );
local owner = meta:get_string( 'owner' );
local time = meta:get_string( 'time' );
-- can the marker be removed?
if( not( owner )
or owner==''
or not( time )
or time==''
or (os.time() - tonumber( time )) > markers.EXPIRE_AFTER ) then
return true;
-- marker whose data got lost anyway
elseif( not( markers.positions[ owner ] )
or #markers.positions[ owner ] < 1 ) then
return true;
-- marker owned by someone else and still in use
elseif( owner ~= player:get_player_name()) then
minetest.chat_send_player( player:get_player_name(),
'Sorry, this marker belongs to '..tostring( owner )..
'. If you still want to remove it, try again in '..
( tostring( markers.EXPIRE_AFTER + tonumber( time ) - os.time()))..' seconds.');
return false;
end
return true;
end
markers.marker_after_dig_node = function(pos, oldnode, oldmetadata, digger)
if( not(oldmetadata ) or not(oldmetadata['fields'])) then
return;
end
local owner = oldmetadata['fields']['owner'];
if( not( owner )
or owner==''
or not( markers.positions[ owner ] )
or #markers.positions[ owner ] < 1 ) then
return;
end
-- remove the markers position from our table of stored positions
local found = 0;
for i,v in ipairs( markers.positions[ owner ] ) do
if( v.x == pos.x
and v.y == pos.y
and v.z == pos.z ) then
found = i;
end
end
if( found ~= 0 ) then
table.remove( markers.positions[ owner ], found );
end
return true;
end
--this function returns a min_pos and max_pos that are the corners
--of a box that contains ALL of the players active markers.
markers.get_box_from_markers = function(name)
if (not name) or (not (markers.positions[ name ][ 1 ] )) then
return {x=0,y=0,z=0},{x=1,y=1,z=1}
end
local min_pos={}
min_pos.x = markers.positions[ name ][ 1 ].x
min_pos.y = markers.positions[ name ][ 1 ].y
min_pos.z = markers.positions[ name ][ 1 ].z
local max_pos={}
max_pos.x = markers.positions[ name ][ 1 ].x
max_pos.y = markers.positions[ name ][ 1 ].y
max_pos.z = markers.positions[ name ][ 1 ].z
for i,p in ipairs( markers.positions[ name ] ) do
if p.x < min_pos.x then min_pos.x = p.x end
if p.x > max_pos.x then max_pos.x = p.x end
if p.y < min_pos.y then min_pos.y = p.y end
if p.y > max_pos.y then max_pos.y = p.y end
if p.z < min_pos.z then min_pos.z = p.z end
if p.z > max_pos.z then max_pos.z = p.z end
end
--print("getbox: min_pos.x="..min_pos.x.." y="..min_pos.y.." z="..min_pos.z)
--print(" : max_pos.x="..max_pos.x.." y="..max_pos.y.." z="..max_pos.z)
return min_pos, max_pos
end --get_box_from_markers
markers.get_marker_formspec = function(player, pos, error_msg)
local formspec = "";
local meta = minetest.get_meta( pos );
local owner = meta:get_string( 'owner' );
local name = player:get_player_name();
local formspec_info = "size[6,4]"..
"button_exit[2,2.5;1,0.5;abort;OK]"..
"textarea[1,1;4,2;info;Information;";
if( owner ~= nil and owner ~= '' and owner ~= name ) then
return formspec_info.."This marker\ncan only be used by\n"..tostring( owner )..", who\nplaced the markers.]";
end
if( not( markers.positions[ name ]) or #markers.positions[name]<1) then
return formspec_info.."Information about the positions\nof your other markers\ngot lost.\nPlease dig and place\nyour markers again!]";
end
local n = #markers.positions[ name ];
if ( n < 2 ) then
return formspec_info.."Please place 2 or more markers\n - at least one in each corner\n of your area first]";
end
local coords={}
coords[1],coords[2] = markers.get_box_from_markers(name)
-- save data
meta:set_string( 'coords', minetest.serialize( coords ) );
if( not( coords ) or #coords < 2 or not( coords[1] ) or not( coords[2] )) then
return formspec_info.."Error in markers.]";
end
-- the coordinates are set; we may present an input form now
-- has the area already been defined?
local area = markers.get_area_by_pos1_pos2( coords[1], coords[2] );
local size = (math.abs( coords[1].x - coords[2].x )+1)
* (math.abs( coords[1].z - coords[2].z )+1);
-- check if area is too large
if( markers.MAX_SIZE < size ) then
return formspec_info.."Error: You can only protect\nareas of up to "..tostring( markers.MAX_SIZE ).."m^2.\n"..
"Your marked area is "..tostring( size ).." m^2 large.]";
end
local formspec = 'size[10,7]'..
'label[0.5,1;The area you marked extends from]'..
'label[4.7,1;'..minetest.pos_to_string( coords[ 1 ] )..' to '..minetest.pos_to_string( coords[ 2 ] )..'.]'..
'label[4.7,1.5;It spans '..tostring( math.abs( coords[1].x - coords[2].x )+1 )..
' x '..tostring( math.abs( coords[1].z - coords[2].z )+1 )..
' = '..tostring( size )..' m^2.]';
-- display the error message (if there is any)
if( error_msg ~= nil ) then
formspec = formspec..
'label[0.5,0.0;Error: ]'..
'textarea[5.0,0;4,1.5;info;;'..error_msg..']';
end
if( area and area['id'] ) then
formspec = formspec..
'label[0.5,2.0;This is area number ]'..
'label[4.7,2.0;'..tostring( area['id'] )..'.]'..
'label[0.5,2.5;It is owned by ]'..
'label[4.7,2.5;'..tostring( area['owner'] )..'.]'..
'label[0.5,3.0;The area is called ]'..
'label[4.7,3.0;'..tostring( area['name'] )..'.]'..
"button_exit[2,6.0;2,0.5;abort;OK]";
else
formspec = formspec..
-- 'label[0.5,2.0;Buying this area will cost you ]'..
-- 'label[4.7,2.0;'..markers.calculate_area_price_text( coords[1], coords[2], name )..'.]'..
'label[0.5,3.0;Your area ought to go..]'..
'label[0.5,3.5;this many blocks up:]'..
'field[5.0,4.0;1,0.5;add_height;;40]'..
'label[6.0,3.5;(above '..coords[2].y..' )]'..
'label[0.5,4.0;and this many blocks down:]'..
'field[5.0,4.5;1,0.5;add_depth;;10]'..
'label[6.0,4.0;(below '..coords[1].y..' )]'..
'label[0.5,4.5;The area shall be named]'..
'field[5.0,5.0;6,0.5;set_area_name;;please enter a name]'..
"button_exit[2,6.0;2,0.5;abort;Abort]"..
-- code the position in the "Buy area" field
"button_exit[6,6.0;2,0.5;"..minetest.pos_to_string(pos)..";Protect area]";
end
return formspec;
end
-- protect/buy an area
markers.marker_on_receive_fields = function(pos, formname, fields, sender)
if( not( pos )) then
minetest.chat_send_player( name, 'Sorry, could not find the marker you where using to access this formspec.' );
return;
end
local meta = minetest.get_meta( pos );
local name = sender:get_player_name();
local coords_string = meta:get_string( 'coords' );
if( not( coords_string ) or coords_string == '' ) then
minetest.chat_send_player( name, 'Could not find marked area. Please dig and place your markers again!');
return;
end
local coords = minetest.deserialize( coords_string );
-- do not protect areas twice
local area = markers.get_area_by_pos1_pos2( coords[1], coords[2] );
if( area ) then
minetest.chat_send_player( name, 'This area is already protected.');
return;
end
-- check input
local add_height = tonumber( fields['add_height'] );
local add_depth = tonumber( fields['add_depth'] );
local error_msg = '';
if( not( add_height ) or add_height < 0 or add_height > markers.MAX_HEIGHT ) then
minetest.chat_send_player( name, 'Please enter a number between 0 and '..tostring( markers.MAX_HEIGHT )..
' in the field where the height of your area is requested. Your area will stretch that many blocks '..
'up into the sky from the position of this marker onward.');
error_msg = 'The height value\nhas to be larger than 0\nand smaller than '..tostring( markers.MAX_HEIGHT );
elseif( not( add_depth ) or add_depth < 0 or add_depth > markers.MAX_HEIGHT ) then
minetest.chat_send_player( name, 'Please enter a number between 0 and '..tostring( markers.MAX_HEIGHT )..
' in the field where the depth of your area is requested. Your area will stretch that many blocks '..
'into the ground from the position of this marker onward.');
error_msg = 'The depth value\nhas to be larger than 0\nand smaller than '..tostring( markers.MAX_HEIGHT );
elseif( add_height + add_depth > markers.MAX_HEIGHT ) then
minetest.chat_send_player( name, 'Sorry, your area exceeds the height limit. Height and depth added have to '..
'be smaller than '..tostring( markers.MAX_HEIGHT )..'.');
error_msg = 'height + depth has to\nbe smaller than '..tostring( markers.MAX_HEIGHT )..'.'
elseif( not( fields[ 'set_area_name' ] ) or fields['set_area_name'] == 'please enter a name' ) then
minetest.chat_send_player( name, 'Please provide a name for your area, i.e. \"'..
tostring( name )..'s first house\" The name ought to describe what you intend to build here.');
error_msg = 'Please provide a\nname for your area!';
else
error_msg = nil;
end
if( error_msg ~= nil ) then
minetest.show_formspec( name, "markers:mark", markers.get_marker_formspec(sender, pos, error_msg));
return;
end
-- those coords lack the height component
local pos1 = coords[1];
local pos2 = coords[2];
-- apply height values from the formspeck
pos1.y = pos1.y - add_depth;
pos2.y = pos2.y + add_height;
pos1, pos2 = areas:sortPos( pos1, pos2 );
--minetest.chat_send_player('singleplayer','INPUT: '..minetest.serialize( pos1 )..' pos2: '..minetest.serialize( pos2 ));
minetest.log("action", "[markers] /protect invoked, owner="..name..
" areaname="..fields['set_area_name']..
" startpos="..minetest.pos_to_string(pos1)..
" endpos=" ..minetest.pos_to_string(pos2));
local canAdd, errMsg = areas:canPlayerAddArea(pos1, pos2, name)
if not canAdd then
minetest.chat_send_player(name, "You can't protect that area: "..errMsg)
minetest.show_formspec( name, "markers:mark", markers.get_marker_formspec(sender, pos, errMsg));
return
end
local id = areas:add(name, fields['set_area_name'], pos1, pos2, nil)
areas:save()
minetest.chat_send_player(name, "Area protected. ID: "..id)
minetest.show_formspec( name, "markers:mark", markers.get_marker_formspec(sender, pos, nil));
end
-- formspec input needs to be handled diffrently
markers.form_input_handler = function( player, formname, fields)
if( formname == "markers:mark" ) then
if( not(fields) or fields['abort']) then
return true;
end
--- decode the position of the marker (which is hidden in the Buy-buttons name
local pos = {};
for k, v in pairs( fields ) do
if( v == 'Protect area' ) then
pos = minetest.string_to_pos( k );
end
end
if( pos and pos.x and pos.y and pos.z ) then
markers.marker_on_receive_fields(pos, formname, fields, player);
end
return true;
elseif( formname == "markers:info"
and player
and markers.menu_data_by_player[ player:get_player_name() ] ) then
local res = markers.form_input_handler_areas( player, formname, fields);
if( res ) then
return true;
end
-- TODO
-- minetest.chat_send_player('singleplayer','MARKERS:INFO WITH '..minetest.serialize( fields ));
else
-- TODO
-- minetest.chat_send_player('singleplayer','YOU CALLED '..tostring( formname )..' WITH '..minetest.serialize( fields ));
end
return false;
end
minetest.register_on_player_receive_fields( markers.form_input_handler)
minetest.register_node("markers:mark", {
description = "Marker",
tiles = {"markers_mark.png"},
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "facedir",
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=1}, --fixed on both buttons dig client crash
light_source = 1,
node_box = {
type = "fixed",
fixed = {
{ -0.1, -0.5, -0.1, 0.1, 1.5, 0.1 },
},
},
after_place_node = function(pos, placer, itemstack)
markers.marker_placed( pos, placer, itemstack );
end,
-- the node is digged immediately, so we may as well do all the work in can_dig (any wrong digs are not that critical)
can_dig = function(pos,player)
return markers.marker_can_dig( pos, player );
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
return markers.marker_after_dig_node( pos, oldnode, oldmetadata, digger );
end,
on_rightclick = function(pos, node, clicker)
minetest.show_formspec( clicker:get_player_name(),
"markers:mark",
markers.get_marker_formspec(clicker, pos, nil)
);
end,
})
minetest.register_craft({
output = "markers:mark 4",
recipe = { { "group:stick" },
{ "default:apple" },
{ "group:stick" },
} });

View File

@ -0,0 +1,76 @@
minetest.register_tool( "markers:land_title_register",
{
description = "Land title register. Left-click with it to get information about who owns the land you clicked on.",
groups = {},
inventory_image = "default_book.png", -- TODO
wield_image = "",
wield_scale = {x=1,y=1,z=1},
stack_max = 1, -- there is no need to have more than one
liquids_pointable = true, -- ground with only water on can be owned as well
--[[
-- the tool_capabilities are completely irrelevant here - no need to dig
tool_capabilities = {
full_punch_interval = 1.0,
max_drop_level=0,
groupcaps={
fleshy={times={[2]=0.80, [3]=0.40}, maxwear=0.05, maxlevel=1},
snappy={times={[2]=0.80, [3]=0.40}, maxwear=0.05, maxlevel=1},
choppy={times={[3]=0.90}, maxwear=0.05, maxlevel=0}
}
},
--]]
node_placement_prediction = nil,
on_place = function(itemstack, placer, pointed_thing)
if( placer == nil or pointed_thing == nil) then
return itemstack; -- nothing consumed
end
local name = placer:get_player_name();
-- the position is what we're actually looking for
local pos = minetest.get_pointed_thing_position( pointed_thing, false ); -- not above
if( not( pos ) or not( pos.x )) then
minetest.chat_send_player( name, "Position not found.");
return itemstack;
end
-- this function shows the formspec with the information about the area(s)
markers.show_marker_stone_formspec( placer, pos );
return itemstack; -- nothing consumed, nothing changed
end,
on_use = function(itemstack, placer, pointed_thing)
if( placer == nil or pointed_thing == nil) then
return itemstack; -- nothing consumed
end
local name = placer:get_player_name();
local pos = minetest.get_pointed_thing_position( pointed_thing, false ); -- not above
if( not( pos ) or not( pos.x )) then
minetest.chat_send_player( name, "Position not found.");
return itemstack;
end
-- this function shows the formspec with the information about the area(s)
markers.show_marker_stone_formspec( placer, pos );
return itemstack; -- nothing consumed, nothing changed
end,
})
minetest.register_craft({
output = "markers:land_title_register",
recipe = { { "markers:mark" },
{ "markers:stone" },
{ "default:book"}
} });

View File

@ -0,0 +1,24 @@
minetest.register_node("markers:stone", {
description = "Boundary marker for land administration",
tiles = {"markers_stone.png", "markers_stone.png", "markers_stone_side.png",
"markers_stone_side.png", "markers_stone_side.png", "markers_stone_side.png" },
-- no facedir here - we want a fixed north indication!
-- paramtype2 = "facedir",
groups = {cracky=2},
legacy_facedir_simple = true,
is_ground_content = false,
on_rightclick = function(pos, node, clicker)
markers.show_marker_stone_formspec( clicker, pos );
end,
})
minetest.register_craft({
output = "markers:stone",
recipe = { { "markers:mark" },
{ "default:cobble" },
} });

View File

@ -0,0 +1,19 @@
# copy this to your worldpath if you want to override these values
# store the positions of that many markers for each player (until server restart)
# default: 50
markers_MAX_MARKERS = 50
# the protection against digging of the marker by other players expires after this time
# default: 60*60*24 (1 day)
markers_EXPIRE_AFTER = 86400
# self-protected areas can not get higher than this
# default: 100
markers_MAX_HEIGHT = 100
# only areas up to this size (in square meters) can be protected
# default: 4096
markers_MAX_SIZE = 4096

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 793 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 B

View File

@ -1,121 +0,0 @@
Protector Redo mod [protect]
Protector redo for minetest is a mod that protects a players builds by placing
a block that stops other players from digging or placing blocks in that area.
based on glomie's mod, remade by Zeg9 and rewritten by TenPlus1.
https://forum.minetest.net/viewtopic.php?f=11&t=9376
Change log:
- 0.1 - Initial release
- 0.2 - Texture update
- 0.3 - Added Protection Logo to blend in with player builds
- 0.4 - Code tweak for 0.4.10+
- 0.5 - Added protector.radius variable in init.lua (default: 5)
- 0.6 - Added Protected Doors (wood and steel) and Protected Chest
- 0.7 - Protected Chests now have "To Chest" and "To Inventory" buttons to copy
contents across, also chests can be named
- 0.8 - Updated to work with Minetest 0.4.12, simplified textures
- 0.9 - Tweaked code
- 1.0 - Only owner can remove protector
- 1.1 - Set 'protector_pvp = true' in minetest.conf to disable pvp in protected
areas except your own, also setting protector_pvp_spawn higher than 0 will
disable pvp around spawn area with the radius you entered
- 1.2 - Shift and click support added with Minetest 0.4.13 to quickly copy stacks
to and from protected chest
- 1.3 - Moved protector on_place into node itself, protector zone display changed
from 10 to 5 seconds, general code tidy
- 1.4 - Changed protector recipes to give single item instead of 4, added + button
to interface, tweaked and tidied code, added admin command /delprot to remove
protectors in bulk from banned/old players
- 1.5 - Added much requested protected trapdoor
- 1.6 - Added protector_drop (true or false) and protector_hurt (hurt by this num)
variables to minetest.conf settings to stop players breaking protected
areas by dropping tools and hurting player.
- 1.7 - Included an edited version of WTFPL doors mod since protected doors didn't
work with the doors mod in the latest daily build... Now it's fine :)
added support for "protection_bypass" privelage.
- 1.8 - Added 'protector_flip' setting to stop players using lag to grief into
another players house, it flips them around to stop them digging.
- 1.9 - Renamed 'protector_pvp_spawn' setting to 'protector_spawn' which protects
an area around static spawnpoint and disables pvp if active.
(note: previous name can still be used)
- 2.0 - Added protector placement tool (thanks to Shara) so that players can easily
stand on a protector, face in a direction and it places a new one at a set
distance to cover protection radius. Added /protector_show command (thanks agaran)
Protectors and chest cannot be moved by mesecon pistons or machines.
- 2.1 - Added 'protector_night_pvp' setting so night-time becomes a free for all and
players can hurt one another even inside protected areas (not spawn protected)
- 2.2 - Updated protector tool so that player only needs to stand nearby (2 block radius)
It can also place vertically (up and down) as well. New protector recipe added.
- 2.3 - Localise many of the protector functions and tidy code.
- 2.4 - Update to newer functions, Minetest 0.4.16 needed to run now.
- 2.5 - Added HUD text to show when player is inside a protected area (updates every 5 seconds)
- 2.6 - Add protection against CSM tampering, updated Intllib support (thanks codexp), tweaked block textures
Lucky Blocks: 10
Usage: (requires server privelage)
list names to remove
/protector_remove
remove specific user names
/protector_remove name1 name2
remove all names from list
/protector_remove -
Whenever a player is near any protectors with name1 or name2 then it will be
replaced by an air block.
show owner name to replace
/protector_replace
replace owner with new name
/protector_replace owner new_owner
reset name list
/protector_replace -
show protected areas of your nearby protectors (max of 5)
/protector_show
The following lines can be added to your minetest.conf file to configure specific features of the mod:
protector_radius = 5
- Sets the area around each protection node so that other players cannot dig, place or enter through protected doors or chests.
protector_pvp = true
- true or false this setting disabled pvp inside of protected areas for all players apart from those listed on the protector node.
protector_night_pvp = false
- when true this setting enables pvp at night time only, even inside protected areas, requires protector_pvp to be active to work.
protector_spawn = 10
- Sets an area 10 nodes around static spawnpoint that is protected.
protector_hurt = 2
- When set to above 0, players digging in protected areas will be hurt by 2 health points (or whichever number it's set to)
protector_flip = true
- When true players who dig inside a protected area will flipped around to stop them using lag to grief into someone else's build
Protector Tool
Can be crafted with a protector surrounded by steel ingots and is used to place new protectors at a set distance of protector.radius in all directions including up and down simply by looking in a direction.
Use by standing near an existing protector, looking in a direction and using as a tool, hold sneak/shift to place new protector containing member list from inside nearest one.

View File

@ -1,146 +0,0 @@
local S = protector.intllib
local removal_names = ""
local replace_names = ""
minetest.register_chatcommand("protector_remove", {
params = S("<names list>"),
description = S("Remove Protectors around players (separate names with spaces)"),
privs = {server = true},
func = function(name, param)
if not param or param == "" then
minetest.chat_send_player(name,
S("Protector Names to remove: @1",
removal_names))
return
end
if param == "-" then
minetest.chat_send_player(name,
S("Name List Reset"))
removal_names = ""
return
end
removal_names = param
end,
})
minetest.register_chatcommand("protector_replace", {
params = S("<owner name> <name to replace with>"),
description = S("Replace Protector Owner with name provided"),
privs = {server = true},
func = function(name, param)
-- reset list to empty
if param == "-" then
minetest.chat_send_player(name, S("Name List Reset"))
replace_names = ""
return
end
-- show name info
if param == ""
and replace_names ~= "" then
local names = replace_names:split(" ")
minetest.chat_send_player(name,
S("Replacing Protector name '@1' with '@2'",
names[1] or "", names[2] or ""))
return
end
replace_names = param
end,
})
minetest.register_abm({
nodenames = {"protector:protect", "protector:protect2"},
interval = 8,
chance = 1,
catch_up = false,
action = function(pos, node)
if removal_names == ""
and replace_names == "" then
return
end
local meta = minetest.get_meta(pos)
if not meta then return end
local owner = meta:get_string("owner")
if removal_names ~= "" then
local names = removal_names:split(" ")
for _, n in pairs(names) do
if n == owner then
minetest.set_node(pos, {name = "air"})
end
end
end
if replace_names ~= "" then
local names = replace_names:split(" ")
if names[1] and names[2] and owner == names[1] then
meta:set_string("owner", names[2])
meta:set_string("infotext", S("Protection (owned by @1)", names[2]))
end
end
end
})
-- get protection radius
local r = tonumber(minetest.settings:get("protector_radius")) or 5
-- show protection areas of nearby protectors owned by you (thanks agaran)
minetest.register_chatcommand("protector_show", {
params = "",
description = S("Show protected areas of your nearby protectors"),
privs = {},
func = function(name, param)
local player = minetest.get_player_by_name(name)
local pos = player:get_pos()
-- find the protector nodes
local pos = minetest.find_nodes_in_area(
{x = pos.x - r, y = pos.y - r, z = pos.z - r},
{x = pos.x + r, y = pos.y + r, z = pos.z + r},
{"protector:protect", "protector:protect2"})
local meta, owner
-- show a maximum of 5 protected areas only
for n = 1, math.min(#pos, 5) do
meta = minetest.get_meta(pos[n])
owner = meta:get_string("owner") or ""
if owner == name then
minetest.add_entity(pos[n], "protector:display")
end
end
end
})

View File

@ -1,4 +0,0 @@
default
intllib?
lucky_block?
mesecons_mvps?

View File

@ -1 +0,0 @@
Lets players craft special blocks to protect their builds or disable PVP in areas.

View File

@ -1,689 +0,0 @@
-- Since the doors mod has changed in the latest daily builds I have taken the
-- WTFPL licenced code from the old doors mod and included an edited version
-- within this mod for local use.
local S = protector.intllib
local F = minetest.formspec_escape
-- Registers a door
function register_door(name, def)
def.groups.not_in_creative_inventory = 1
local box = {{-0.5, -0.5, -0.5, 0.5, 0.5, -0.5+1.5/16}}
def.node_box_bottom = box
def.node_box_top = box
def.selection_box_bottom = box
def.selection_box_top = box
def.sound_close_door = "doors_door_close"
def.sound_open_door = "doors_door_open"
minetest.register_craftitem(name, {
description = def.description,
inventory_image = def.inventory_image,
on_place = function(itemstack, placer, pointed_thing)
if not pointed_thing.type == "node" then
return itemstack
end
local ptu = pointed_thing.under
local nu = minetest.get_node(ptu)
if minetest.registered_nodes[nu.name]
and minetest.registered_nodes[nu.name].on_rightclick then
return minetest.registered_nodes[nu.name].on_rightclick(ptu, nu, placer, itemstack)
end
local pt = pointed_thing.above
local pt2 = {x=pt.x, y=pt.y, z=pt.z}
pt2.y = pt2.y+1
if
not minetest.registered_nodes[minetest.get_node(pt).name].buildable_to or
not minetest.registered_nodes[minetest.get_node(pt2).name].buildable_to or
not placer or
not placer:is_player()
then
return itemstack
end
if minetest.is_protected(pt, placer:get_player_name()) or
minetest.is_protected(pt2, placer:get_player_name()) then
minetest.record_protection_violation(pt, placer:get_player_name())
return itemstack
end
local p2 = minetest.dir_to_facedir(placer:get_look_dir())
local pt3 = {x=pt.x, y=pt.y, z=pt.z}
if p2 == 0 then
pt3.x = pt3.x-1
elseif p2 == 1 then
pt3.z = pt3.z+1
elseif p2 == 2 then
pt3.x = pt3.x+1
elseif p2 == 3 then
pt3.z = pt3.z-1
end
if minetest.get_item_group(minetest.get_node(pt3).name, "door") == 0 then
minetest.set_node(pt, {name=name.."_b_1", param2=p2})
minetest.set_node(pt2, {name=name.."_t_1", param2=p2})
else
minetest.set_node(pt, {name=name.."_b_2", param2=p2})
minetest.set_node(pt2, {name=name.."_t_2", param2=p2})
minetest.get_meta(pt):set_int("right", 1)
minetest.get_meta(pt2):set_int("right", 1)
end
if not minetest.setting_getbool("creative_mode") then
itemstack:take_item()
end
return itemstack
end,
})
local tt = def.tiles_top
local tb = def.tiles_bottom
local function after_dig_node(pos, name, digger)
local node = minetest.get_node(pos)
if node.name == name then
minetest.node_dig(pos, node, digger)
end
end
local function on_rightclick(pos, dir, check_name, replace, replace_dir, params)
pos.y = pos.y+dir
if minetest.get_node(pos).name ~= check_name then
return
end
local p2 = minetest.get_node(pos).param2
p2 = params[p2+1]
minetest.swap_node(pos, {name=replace_dir, param2=p2})
pos.y = pos.y-dir
minetest.swap_node(pos, {name=replace, param2=p2})
local snd_1 = def.sound_close_door
local snd_2 = def.sound_open_door
if params[1] == 3 then
snd_1 = def.sound_open_door
snd_2 = def.sound_close_door
end
if minetest.get_meta(pos):get_int("right") ~= 0 then
minetest.sound_play(snd_1, {pos = pos, gain = 0.3, max_hear_distance = 10})
else
minetest.sound_play(snd_2, {pos = pos, gain = 0.3, max_hear_distance = 10})
end
end
local function on_rotate(pos, node, dir, user, check_name, mode, new_param2)
if mode ~= screwdriver.ROTATE_FACE then
return false
end
pos.y = pos.y + dir
if not minetest.get_node(pos).name == check_name then
return false
end
if minetest.is_protected(pos, user:get_player_name()) then
minetest.record_protection_violation(pos, user:get_player_name())
return false
end
local node2 = minetest.get_node(pos)
node2.param2 = (node2.param2 + 1) % 4
minetest.swap_node(pos, node2)
pos.y = pos.y - dir
node.param2 = (node.param2 + 1) % 4
minetest.swap_node(pos, node)
return true
end
minetest.register_node(name.."_b_1", {
tiles = {tb[2], tb[2], tb[2], tb[2], tb[1], tb[1].."^[transformfx"},
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
drop = name,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = def.node_box_bottom
},
selection_box = {
type = "fixed",
fixed = def.selection_box_bottom
},
groups = def.groups,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
pos.y = pos.y+1
after_dig_node(pos, name.."_t_1", digger)
end,
on_rightclick = function(pos, node, clicker)
if not minetest.is_protected(pos, clicker:get_player_name()) then
on_rightclick(pos, 1, name.."_t_1", name.."_b_2", name.."_t_2", {1,2,3,0})
end
end,
on_rotate = function(pos, node, user, mode, new_param2)
return on_rotate(pos, node, 1, user, name.."_t_1", mode)
end,
sounds = def.sounds,
sunlight_propagates = def.sunlight,
on_blast = function() end,
})
minetest.register_node(name.."_t_1", {
tiles = {tt[2], tt[2], tt[2], tt[2], tt[1], tt[1].."^[transformfx"},
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
drop = "",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = def.node_box_top
},
selection_box = {
type = "fixed",
fixed = def.selection_box_top
},
groups = def.groups,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
pos.y = pos.y-1
after_dig_node(pos, name.."_b_1", digger)
end,
on_rightclick = function(pos, node, clicker)
if not minetest.is_protected(pos, clicker:get_player_name()) then
on_rightclick(pos, -1, name.."_b_1", name.."_t_2", name.."_b_2", {1,2,3,0})
end
end,
on_rotate = function(pos, node, user, mode, new_param2)
return on_rotate(pos, node, -1, user, name.."_b_1", mode)
end,
sounds = def.sounds,
sunlight_propagates = def.sunlight,
on_blast = function() end,
})
minetest.register_node(name.."_b_2", {
tiles = {tb[2], tb[2], tb[2], tb[2], tb[1].."^[transformfx", tb[1]},
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
drop = name,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = def.node_box_bottom
},
selection_box = {
type = "fixed",
fixed = def.selection_box_bottom
},
groups = def.groups,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
pos.y = pos.y+1
after_dig_node(pos, name.."_t_2", digger)
end,
on_rightclick = function(pos, node, clicker)
if not minetest.is_protected(pos, clicker:get_player_name()) then
on_rightclick(pos, 1, name.."_t_2", name.."_b_1", name.."_t_1", {3,0,1,2})
end
end,
on_rotate = function(pos, node, user, mode, new_param2)
return on_rotate(pos, node, 1, user, name.."_t_2", mode)
end,
sounds = def.sounds,
sunlight_propagates = def.sunlight,
on_blast = function() end,
})
minetest.register_node(name.."_t_2", {
tiles = {tt[2], tt[2], tt[2], tt[2], tt[1].."^[transformfx", tt[1]},
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
drop = "",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = def.node_box_top
},
selection_box = {
type = "fixed",
fixed = def.selection_box_top
},
groups = def.groups,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
pos.y = pos.y-1
after_dig_node(pos, name.."_b_2", digger)
end,
on_rightclick = function(pos, node, clicker)
if not minetest.is_protected(pos, clicker:get_player_name()) then
on_rightclick(pos, -1, name.."_b_2", name.."_t_1", name.."_b_1", {3,0,1,2})
end
end,
on_rotate = function(pos, node, user, mode, new_param2)
return on_rotate(pos, node, -1, user, name.."_b_2", mode)
end,
sounds = def.sounds,
sunlight_propagates = def.sunlight,
on_blast = function() end,
})
end
-- Protected Wooden Door
local name = "protector:door_wood"
register_door(name, {
description = S("Protected Wooden Door"),
inventory_image = "doors_wood.png^protector_logo.png",
groups = {
snappy = 1, choppy = 2, oddly_breakable_by_hand = 2,
unbreakable = 1, --door = 1
},
tiles_bottom = {"doors_wood_b.png^protector_logo.png", "doors_brown.png"},
tiles_top = {"doors_wood_a.png", "doors_brown.png"},
sounds = default.node_sound_wood_defaults(),
sunlight = false,
})
minetest.register_craft({
output = name,
recipe = {
{"group:wood", "group:wood"},
{"group:wood", "default:copper_ingot"},
{"group:wood", "group:wood"}
}
})
minetest.register_craft({
output = name,
recipe = {
{"doors:door_wood", "default:copper_ingot"}
}
})
-- Protected Steel Door
local name = "protector:door_steel"
register_door(name, {
description = S("Protected Steel Door"),
inventory_image = "doors_steel.png^protector_logo.png",
groups = {
snappy = 1, bendy = 2, cracky = 1,
level = 2, unbreakable = 1, -- door = 1
},
tiles_bottom = {"doors_steel_b.png^protector_logo.png", "doors_grey.png"},
tiles_top = {"doors_steel_a.png", "doors_grey.png"},
sounds = default.node_sound_wood_defaults(),
sunlight = false,
})
minetest.register_craft({
output = name,
recipe = {
{"default:steel_ingot", "default:steel_ingot"},
{"default:steel_ingot", "default:copper_ingot"},
{"default:steel_ingot", "default:steel_ingot"}
}
})
minetest.register_craft({
output = name,
recipe = {
{"doors:door_steel", "default:copper_ingot"}
}
})
----trapdoor----
function register_trapdoor(name, def)
local name_closed = name
local name_opened = name.."_open"
def.on_rightclick = function (pos, node, clicker, itemstack, pointed_thing)
if minetest.is_protected(pos, clicker:get_player_name()) then
return
end
local newname = node.name == name_closed and name_opened or name_closed
local sound = false
if node.name == name_closed then sound = "doors_door_open" end
if node.name == name_opened then sound = "doors_door_close" end
if sound then
minetest.sound_play(sound, {pos = pos, gain = 0.3, max_hear_distance = 10})
end
minetest.swap_node(pos, {name = newname, param1 = node.param1, param2 = node.param2})
end
-- Common trapdoor configuration
def.drawtype = "nodebox"
def.paramtype = "light"
def.paramtype2 = "facedir"
def.is_ground_content = false
local def_opened = table.copy(def)
local def_closed = table.copy(def)
def_closed.node_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, -6/16, 0.5}
}
def_closed.selection_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, -6/16, 0.5}
}
def_closed.tiles = { def.tile_front, def.tile_front, def.tile_side, def.tile_side,
def.tile_side, def.tile_side }
def_opened.node_box = {
type = "fixed",
fixed = {-0.5, -0.5, 6/16, 0.5, 0.5, 0.5}
}
def_opened.selection_box = {
type = "fixed",
fixed = {-0.5, -0.5, 6/16, 0.5, 0.5, 0.5}
}
def_opened.tiles = { def.tile_side, def.tile_side,
def.tile_side .. '^[transform3',
def.tile_side .. '^[transform1',
def.tile_front, def.tile_front }
def_opened.drop = name_closed
def_opened.groups.not_in_creative_inventory = 1
minetest.register_node(name_opened, def_opened)
minetest.register_node(name_closed, def_closed)
end
-- Protected Wooden Trapdoor
register_trapdoor("protector:trapdoor", {
description = S("Protected Trapdoor"),
inventory_image = "doors_trapdoor.png^protector_logo.png",
wield_image = "doors_trapdoor.png^protector_logo.png",
tile_front = "doors_trapdoor.png^protector_logo.png",
tile_side = "doors_trapdoor_side.png",
groups = {
snappy = 1, choppy = 2, oddly_breakable_by_hand = 2,
unbreakable = 1, --door = 1
},
sounds = default.node_sound_wood_defaults(),
})
minetest.register_craft({
output = 'protector:trapdoor 2',
recipe = {
{'group:wood', 'default:copper_ingot', 'group:wood'},
{'group:wood', 'group:wood', 'group:wood'},
{'', '', ''},
}
})
minetest.register_craft({
output = "protector:trapdoor",
recipe = {
{"doors:trapdoor", "default:copper_ingot"}
}
})
-- Protected Steel Trapdoor
register_trapdoor("protector:trapdoor_steel", {
description = S("Protected Steel Trapdoor"),
inventory_image = "doors_trapdoor_steel.png^protector_logo.png",
wield_image = "doors_trapdoor_steel.png^protector_logo.png",
tile_front = "doors_trapdoor_steel.png^protector_logo.png",
tile_side = "doors_trapdoor_steel_side.png",
groups = {
snappy = 1, bendy = 2, cracky = 1, melty = 2, level = 2,
unbreakable = 1, --door = 1
},
sounds = default.node_sound_wood_defaults(),
})
minetest.register_craft({
output = 'protector:trapdoor_steel',
recipe = {
{'default:copper_ingot', 'default:steel_ingot'},
{'default:steel_ingot', 'default:steel_ingot'},
}
})
minetest.register_craft({
output = "protector:trapdoor_steel",
recipe = {
{"doors:trapdoor_steel", "default:copper_ingot"}
}
})
-- Protected Chest
minetest.register_node("protector:chest", {
description = S("Protected Chest"),
tiles = {
"default_chest_top.png", "default_chest_top.png",
"default_chest_side.png", "default_chest_side.png",
"default_chest_side.png", "default_chest_front.png^protector_logo.png"
},
paramtype2 = "facedir",
groups = {choppy = 2, oddly_breakable_by_hand = 2, unbreakable = 1},
legacy_facedir_simple = true,
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
meta:set_string("infotext", S("Protected Chest"))
meta:set_string("name", "")
inv:set_size("main", 8 * 4)
end,
can_dig = function(pos,player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if inv:is_empty("main") then
if not minetest.is_protected(pos, player:get_player_name()) then
return true
end
end
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
minetest.log("action", S("@1 moves stuff to protected chest at @2",
player:get_player_name(), minetest.pos_to_string(pos)))
end,
on_metadata_inventory_take = function(pos, listname, index, stack, player)
minetest.log("action", S("@1 takes stuff from protected chest at @2",
player:get_player_name(), minetest.pos_to_string(pos)))
end,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
minetest.log("action", S("@1 moves stuff inside protected chest at @2",
player:get_player_name(), minetest.pos_to_string(pos)))
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return count
end,
on_rightclick = function(pos, node, clicker)
if minetest.is_protected(pos, clicker:get_player_name()) then
return
end
local meta = minetest.get_meta(pos)
if not meta then
return
end
local spos = pos.x .. "," .. pos.y .. "," ..pos.z
local formspec = "size[8,9]"
.. default.gui_bg
.. default.gui_bg_img
.. default.gui_slots
.. "list[nodemeta:".. spos .. ";main;0,0.3;8,4;]"
.. "button[0,4.5;2,0.25;toup;" .. F(S("To Chest")) .. "]"
.. "field[2.3,4.8;4,0.25;chestname;;"
.. meta:get_string("name") .. "]"
.. "button[6,4.5;2,0.25;todn;" .. F(S("To Inventory")) .. "]"
.. "list[current_player;main;0,5;8,1;]"
.. "list[current_player;main;0,6.08;8,3;8]"
.. "listring[nodemeta:" .. spos .. ";main]"
.. "listring[current_player;main]"
minetest.show_formspec(
clicker:get_player_name(),
"protector:chest_" .. minetest.pos_to_string(pos),
formspec)
end,
on_blast = function() end,
})
-- Protected Chest formspec buttons
minetest.register_on_player_receive_fields(function(player, formname, fields)
if string.sub(formname, 0, string.len("protector:chest_")) ~= "protector:chest_" then
return
end
local pos_s = string.sub(formname,string.len("protector:chest_") + 1)
local pos = minetest.string_to_pos(pos_s)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = minetest.get_meta(pos) ; if not meta then return end
local chest_inv = meta:get_inventory() ; if not chest_inv then return end
local player_inv = player:get_inventory()
local leftover
if fields.toup then
-- copy contents of players inventory to chest
for i, v in ipairs(player_inv:get_list("main") or {}) do
if chest_inv:room_for_item("main", v) then
leftover = chest_inv:add_item("main", v)
player_inv:remove_item("main", v)
if leftover
and not leftover:is_empty() then
player_inv:add_item("main", v)
end
end
end
elseif fields.todn then
-- copy contents of chest to players inventory
for i, v in ipairs(chest_inv:get_list("main") or {}) do
if player_inv:room_for_item("main", v) then
leftover = player_inv:add_item("main", v)
chest_inv:remove_item("main", v)
if leftover
and not leftover:is_empty() then
chest_inv:add_item("main", v)
end
end
end
elseif fields.chestname then
-- change chest infotext to display name
if fields.chestname ~= "" then
meta:set_string("name", fields.chestname)
meta:set_string("infotext",
S("Protected Chest (@1)", fields.chestname))
else
meta:set_string("infotext", S("Protected Chest"))
end
end
end)
-- Protected Chest recipes
minetest.register_craft({
output = 'protector:chest',
recipe = {
{'group:wood', 'group:wood', 'group:wood'},
{'group:wood', 'default:copper_ingot', 'group:wood'},
{'group:wood', 'group:wood', 'group:wood'},
}
})
minetest.register_craft({
output = 'protector:chest',
recipe = {
{'default:chest', 'default:copper_ingot', ''},
}
})

View File

@ -1,61 +0,0 @@
local S = protector.intllib
local radius = (tonumber(minetest.setting_get("protector_radius")) or 5)
local hud = {}
local hud_timer = 0
minetest.register_globalstep(function(dtime)
-- every 5 seconds
hud_timer = hud_timer + dtime
if hud_timer < 5 then
return
end
hud_timer = 0
for _, player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
local pos = vector.round(player:get_pos())
local hud_text = ""
local protectors = 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},
{"protector:protect","protector:protect2"})
if #protectors > 0 then
local npos = protectors[1]
local meta = minetest.get_meta(npos)
local nodeowner = meta:get_string("owner")
hud_text = S("Owner: @1", nodeowner)
end
if not hud[name] then
hud[name] = {}
hud[name].id = player:hud_add({
hud_elem_type = "text",
name = "Protector Area",
number = 0xFFFF22,
position = {x=0, y=0.95},
offset = {x=8, y=-8},
text = hud_text,
scale = {x=200, y=60},
alignment = {x=1, y=-1},
})
return
else
player:hud_change(hud[name].id, "text", hud_text)
end
end
end)
minetest.register_on_leaveplayer(function(player)
hud[player:get_player_name()] = nil
end)

View File

@ -1,750 +0,0 @@
-- Load support for intllib.
local MP = minetest.get_modpath(minetest.get_current_modname())
local S = dofile(MP .. "/intllib.lua")
local F = minetest.formspec_escape
protector = {}
protector.mod = "redo"
protector.modpath = MP
protector.intllib = S
local protector_max_share_count = 12
-- get minetest.conf settings
local protector_radius = tonumber(minetest.settings:get("protector_radius")) or 8
local protector_flip = minetest.settings:get_bool("protector_flip") or false
local protector_hurt = tonumber(minetest.settings:get("protector_hurt")) or 0
local protector_spawn = tonumber(minetest.settings:get("protector_spawn")
or minetest.settings:get("protector_pvp_spawn")) or 0
-- get static spawn position
local statspawn = minetest.string_to_pos(minetest.settings:get("static_spawnpoint"))
or {x = 0, y = 2, z = 0}
-- return list of members as a table
local get_member_list = function(meta)
return meta:get_string("members"):split(" ")
end
-- write member list table in protector meta as string
local set_member_list = function(meta, list)
meta:set_string("members", table.concat(list, " "))
end
-- check for owner name
local is_owner = function(meta, name)
return name == meta:get_string("owner")
end
-- check for member name
local is_member = function (meta, name)
for _, n in pairs(get_member_list(meta)) do
if n == name then
return true
end
end
return false
end
-- add player name to table as member
local add_member = function(meta, name)
-- Constant (20) defined by player.h
if name:len() > 25 then
return
end
-- does name already exist?
if is_owner(meta, name)
or is_member(meta, name) then
return
end
local list = get_member_list(meta)
if #list >= protector_max_share_count then
return
end
table.insert(list, name)
set_member_list(meta, list)
end
-- remove player name from table
local del_member = function(meta, name)
local list = get_member_list(meta)
for i, n in pairs(list) do
if n == name then
table.remove(list, i)
break
end
end
set_member_list(meta, list)
end
-- protector interface
local protector_formspec = function(meta)
local formspec = "size[8,7]"
.. default.gui_bg
.. default.gui_bg_img
.. default.gui_slots
.. "label[2.5,0;" .. F(S("-- Protector interface --")) .. "]"
.. "label[0,1;" .. F(S("PUNCH node to show protected area")) .. "]"
.. "label[0,2;" .. F(S("Members:")) .. "]"
.. "button_exit[2.5,6.2;3,0.5;close_me;" .. F(S("Close")) .. "]"
.. "field_close_on_enter[protector_add_member;false]"
local members = get_member_list(meta)
local npp = protector_max_share_count -- max users added to protector list
local i = 0
for n = 1, #members do
if i < npp then
-- show username
formspec = formspec .. "button[" .. (i % 4 * 2)
.. "," .. math.floor(i / 4 + 3)
.. ";1.5,.5;protector_member;" .. F(members[n]) .. "]"
-- username remove button
.. "button[" .. (i % 4 * 2 + 1.25) .. ","
.. math.floor(i / 4 + 3)
.. ";.75,.5;protector_del_member_" .. F(members[n]) .. ";X]"
end
i = i + 1
end
if i < npp then
-- user name entry field
formspec = formspec .. "field[" .. (i % 4 * 2 + 1 / 3) .. ","
.. (math.floor(i / 4 + 3) + 1 / 3)
.. ";1.433,.5;protector_add_member;;]"
-- username add button
.."button[" .. (i % 4 * 2 + 1.25) .. ","
.. math.floor(i / 4 + 3) .. ";.75,.5;protector_submit;+]"
end
return formspec
end
-- check if pos is inside a protected spawn area
local inside_spawn = function(pos, radius)
if protector_spawn <= 0 then
return false
end
if pos.x < statspawn.x + radius
and pos.x > statspawn.x - radius
and pos.y < statspawn.y + radius
and pos.y > statspawn.y - radius
and pos.z < statspawn.z + radius
and pos.z > statspawn.z - radius then
return true
end
return false
end
-- Infolevel:
-- 0 for no info
-- 1 for "This area is owned by <owner> !" if you can't dig
-- 2 for "This area is owned by <owner>.
-- 3 for checking protector overlaps
protector.can_dig = function(r, pos, digger, onlyowner, infolevel, small)
if not digger or not pos then
return false
end
-- protector_bypass privileged users can override protection
if infolevel == 1
and minetest.check_player_privs(digger, {protection_bypass = true}) then
return true
end
-- infolevel 3 is only used to bypass priv check, change to 1 now
if infolevel == 3 then infolevel = 1 end
-- is spawn area protected ?
if inside_spawn(pos, protector_spawn) then
minetest.chat_send_player(digger,
S("Spawn @1 has been protected up to a @2 block radius.",
minetest.pos_to_string(statspawn), protector_spawn))
return false
end
-- find the protector nodes
local r2 = r
local checkpos = pos
if small then r = r - (protector_radius/2) end
local pos = minetest.find_nodes_in_area(
{x = pos.x - r, y = pos.y - r, z = pos.z - r},
{x = pos.x + r, y = pos.y + r, z = pos.z + r},
{"protector:protect"})
local pos2 = minetest.find_nodes_in_area(
{x = checkpos.x - ((protector_radius/2)-r), y = checkpos.y - ((protector_radius/2)-r), z = checkpos.z - ((protector_radius/2)-r)},
{x = checkpos.x + ((protector_radius/2)-r), y = checkpos.y + ((protector_radius/2)-r), z = checkpos.z + ((protector_radius/2)-r)},
{"protector:protect2"})
for k,v in pairs(pos2) do table.insert(pos, v) end
local meta, owner, members
for n = 1, #pos do
meta = minetest.get_meta(pos[n])
owner = meta:get_string("owner") or ""
members = meta:get_string("members") or ""
-- node change and digger isn't owner
if infolevel == 1 and owner ~= digger then
-- and you aren't on the member list
if onlyowner or not is_member(meta, digger) then
local rand = math.random(0,10)
if rand == 0 then
return true
else
minetest.chat_send_player(digger,
S("This area is owned by @1", owner) .. "!")
--anti-glitch measures
local player = minetest.get_player_by_name(digger)
if player then
-- if digging below player, move up to avoid falling through hole
local pla_pos = player:get_pos()
if player:get_look_vertical() > .8 then
player:setpos({
x = pla_pos.x,
y = pla_pos.y + 0.4,
z = pla_pos.z
})
else
player:setpos(pla_pos)
end
end
return false
end
end
end
-- when using protector as tool, show protector information
if infolevel == 2 then
minetest.chat_send_player(digger,
S("This area is owned by @1", owner) .. ".")
minetest.chat_send_player(digger,
S("Protection located at: @1", minetest.pos_to_string(pos[n])))
if members ~= "" then
minetest.chat_send_player(digger, S("Members: @1.", members))
end
return false
end
end
-- show when you can build on unprotected area
if infolevel == 2 then
if #pos < 1 then
minetest.chat_send_player(digger, S("This area is not protected."))
end
minetest.chat_send_player(digger, S("You can build here."))
end
return true
end
local old_is_protected = minetest.is_protected
-- check for protected area, return true if protected and digger isn't on list
function minetest.is_protected(pos, digger)
digger = digger or "" -- nil check
-- is area protected against digger?
if not protector.can_dig(protector_radius, pos, digger, false, 1) then
local player = minetest.get_player_by_name(digger)
if player and player:is_player() then
-- hurt player if protection violated
if protector_hurt > 0 and player:get_hp() > 0 then
player:set_hp(player:get_hp() - protector_hurt)
end
-- flip player when protection violated
if protector_flip then
-- yaw + 180°
local yaw = player:get_look_horizontal() + math.pi
if yaw > 2 * math.pi then
yaw = yaw - 2 * math.pi
end
player:set_look_horizontal(yaw)
-- invert pitch
player:set_look_vertical(-player:get_look_vertical())
-- if digging below player, move up to avoid falling through hole
local pla_pos = player:get_pos()
if pos.y < pla_pos.y then
player:set_pos({
x = pla_pos.x,
y = pla_pos.y + 0.8,
z = pla_pos.z
})
end
end
end
return true
end
-- otherwise can dig or place
return old_is_protected(pos, digger)
end
-- make sure protection block doesn't overlap another protector's area
local check_overlap = function(itemstack, placer, pointed_thing, small)
if pointed_thing.type ~= "node" then
return itemstack
end
local pos = pointed_thing.above
local name = placer:get_player_name()
-- make sure protector doesn't overlap onto protected spawn area
if inside_spawn(pos, protector_spawn + protector_radius) then
minetest.chat_send_player(name,
S("Spawn @1 has been protected up to a @2 block radius.",
minetest.pos_to_string(statspawn), protector_spawn))
return itemstack
end
-- make sure protector doesn't overlap any other player's area
if not protector.can_dig(protector_radius * 2, pos, name, true, 3, small) then
minetest.chat_send_player(name,
S("Overlaps into above players protected area"))
return itemstack
end
return minetest.item_place(itemstack, placer, pointed_thing)
end
-- temporary pos store
local player_pos = {}
-- protection node
minetest.register_node("protector:protect", {
description = S("Protection Block") .. " (" .. S("USE for area check") .. ")",
drawtype = "nodebox",
tiles = {
"default_stone.png^protector_overlay.png",
"default_stone.png^protector_overlay.png",
"default_stone.png^protector_overlay.png^protector_logo.png"
},
sounds = default.node_sound_stone_defaults(),
groups = {dig_immediate = 2, unbreakable = 1},
is_ground_content = false,
paramtype = "light",
light_source = 4,
node_box = {
type = "fixed",
fixed = {
{-0.5 ,-0.5, -0.5, 0.5, 0.5, 0.5},
}
},
on_place = check_overlap,
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("Protection (owned by @1)", meta:get_string("owner")))
meta:set_string("members", "")
end,
on_use = function(itemstack, user, pointed_thing)
if pointed_thing.type ~= "node" then
return
end
protector.can_dig(protector_radius, pointed_thing.under, user:get_player_name(), false, 2)
end,
on_rightclick = function(pos, node, clicker, itemstack)
local meta = minetest.get_meta(pos)
local name = clicker:get_player_name()
if meta
and protector.can_dig(1, pos, name, true, 1) then
player_pos[name] = pos
minetest.show_formspec(name, "protector:node", protector_formspec(meta))
end
end,
on_punch = function(pos, node, puncher)
if minetest.is_protected(pos, puncher:get_player_name()) then
return
end
minetest.add_entity(pos, "protector:display")
end,
can_dig = function(pos, player)
return player and protector.can_dig(1, pos, player:get_player_name(), false, 1)
end,
on_blast = function() end,
})
minetest.register_craft({
output = "protector:protect 2",
recipe = {
{"default:stone", "default:stone", "default:stone"},
{"default:stone", "default:steel_ingot", "default:stone"},
{"default:stone", "default:stone", "default:stone"},
}
})
-- protection logo
minetest.register_node("protector:protect2", {
description = S("Small Protection") .. " (" .. S("USE for area check") .. ")",
tiles = {"protector_logo.png"},
wield_image = "protector_logo.png",
inventory_image = "protector_logo.png",
sounds = default.node_sound_stone_defaults(),
groups = {dig_immediate = 2, unbreakable = 1},
paramtype = "light",
paramtype2 = "wallmounted",
legacy_wallmounted = true,
light_source = 4,
drawtype = "nodebox",
sunlight_propagates = true,
walkable = true,
node_box = {
type = "wallmounted",
wall_top = {-0.375, 0.4375, -0.5, 0.375, 0.5, 0.5},
wall_bottom = {-0.375, -0.5, -0.5, 0.375, -0.4375, 0.5},
wall_side = {-0.5, -0.5, -0.375, -0.4375, 0.5, 0.375},
},
selection_box = {type = "wallmounted"},
on_place = function(itemstack, placer, pointed_thing)
if check_overlap(itemstack, placer, pointed_thing, true) then
return itemstack
end
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("Protection (owned by @1)", meta:get_string("owner")))
meta:set_string("members", "")
end,
on_use = function(itemstack, user, pointed_thing)
if pointed_thing.type ~= "node" then
return
end
protector.can_dig(protector_radius, pointed_thing.under, user:get_player_name(), false, 2)
end,
on_rightclick = function(pos, node, clicker, itemstack)
local meta = minetest.get_meta(pos)
local name = clicker:get_player_name()
if meta
and protector.can_dig(1, pos, name, true, 1) then
player_pos[name] = pos
minetest.show_formspec(name, "protector:node", protector_formspec(meta))
end
end,
on_punch = function(pos, node, puncher)
if minetest.is_protected(pos, puncher:get_player_name()) then
return
end
minetest.add_entity(pos, "protector:display2")
end,
can_dig = function(pos, player)
return player and protector.can_dig(1, pos, player:get_player_name(), true, 1)
end,
on_blast = function() end,
})
-- recipes to switch between protectors
minetest.register_craft({
type = "shapeless",
output = "protector:protect",
recipe = {"protector:protect2", "protector:protect2", "protector:protect2"}
})
minetest.register_craft({
type = "shapeless",
output = "protector:protect2 3",
recipe = {"protector:protect"}
})
-- check formspec buttons or when name entered
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "protector:node" then
return
end
local name = player:get_player_name()
local pos = player_pos[name]
if not name or not pos then
return
end
local add_member_input = fields.protector_add_member
-- reset formspec until close button pressed
if (fields.close_me or fields.quit)
and (not add_member_input or add_member_input == "") then
player_pos[name] = nil
return
end
-- only owner can add names
if not protector.can_dig(1, pos, player:get_player_name(), true, 1) then
return
end
-- are we adding member to a protection node ? (csm protection)
local nod = minetest.get_node(pos).name
if nod ~= "protector:protect"
and nod ~= "protector:protect2" then
player_pos[name] = nil
return
end
local meta = minetest.get_meta(pos)
if not meta then
return
end
-- add member [+]
if add_member_input then
for _, i in pairs(add_member_input:split(" ")) do
add_member(meta, i)
end
end
-- remove member [x]
for field, value in pairs(fields) do
if string.sub(field, 0,
string.len("protector_del_member_")) == "protector_del_member_" then
del_member(meta,
string.sub(field,string.len("protector_del_member_") + 1))
end
end
minetest.show_formspec(name, formname, protector_formspec(meta))
end)
-- display entity shown when protector node is punched
minetest.register_entity("protector:display", {
physical = false,
collisionbox = {0, 0, 0, 0, 0, 0},
visual = "wielditem",
-- wielditem seems to be scaled to 1.5 times original node size
visual_size = {x = 1.0 / 1.5, y = 1.0 / 1.5},
textures = {"protector:display_node"},
timer = 0,
on_step = function(self, dtime)
self.timer = self.timer + dtime
-- remove after 5 seconds
if self.timer > 5 then
self.object:remove()
end
end,
})
minetest.register_entity("protector:display2", {
physical = false,
collisionbox = {0, 0, 0, 0, 0, 0},
visual = "wielditem",
-- wielditem seems to be scaled to 1.5 times original node size
visual_size = {x = 1.0 / 1.5, y = 1.0 / 1.5},
textures = {"protector:display_node2"},
timer = 0,
on_step = function(self, dtime)
self.timer = self.timer + dtime
-- remove after 5 seconds
if self.timer > 5 then
self.object:remove()
end
end,
})
-- Display-zone node, Do NOT place the display as a node,
-- it is made to be used as an entity (see above)
local x = protector_radius
minetest.register_node("protector:display_node", {
tiles = {"protector_display.png"},
use_texture_alpha = true,
walkable = false,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
-- sides
{-(x+.55), -(x+.55), -(x+.55), -(x+.45), (x+.55), (x+.55)},
{-(x+.55), -(x+.55), (x+.45), (x+.55), (x+.55), (x+.55)},
{(x+.45), -(x+.55), -(x+.55), (x+.55), (x+.55), (x+.55)},
{-(x+.55), -(x+.55), -(x+.55), (x+.55), (x+.55), -(x+.45)},
-- top
{-(x+.55), (x+.45), -(x+.55), (x+.55), (x+.55), (x+.55)},
-- bottom
{-(x+.55), -(x+.55), -(x+.55), (x+.55), -(x+.45), (x+.55)},
-- middle (surround protector)
{-.55,-.55,-.55, .55,.55,.55},
},
},
selection_box = {
type = "regular",
},
paramtype = "light",
groups = {dig_immediate = 3, not_in_creative_inventory = 1},
drop = "",
})
local x = (protector_radius/2)
minetest.register_node("protector:display_node2", {
tiles = {"protector_display.png"},
use_texture_alpha = true,
walkable = false,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
-- sides
{-(x+.55), -(x+.55), -(x+.55), -(x+.45), (x+.55), (x+.55)},
{-(x+.55), -(x+.55), (x+.45), (x+.55), (x+.55), (x+.55)},
{(x+.45), -(x+.55), -(x+.55), (x+.55), (x+.55), (x+.55)},
{-(x+.55), -(x+.55), -(x+.55), (x+.55), (x+.55), -(x+.45)},
-- top
{-(x+.55), (x+.45), -(x+.55), (x+.55), (x+.55), (x+.55)},
-- bottom
{-(x+.55), -(x+.55), -(x+.55), (x+.55), -(x+.45), (x+.55)},
-- middle (surround protector)
{-.55,-.55,-.55, .55,.55,.55},
},
},
selection_box = {
type = "regular",
},
paramtype = "light",
groups = {dig_immediate = 3, not_in_creative_inventory = 1},
drop = "",
})
--dofile(MP .. "/doors_chest.lua")
--dofile(MP .. "/pvp.lua")
dofile(MP .. "/admin.lua")
--dofile(MP .. "/tool.lua")
dofile(MP .. "/hud.lua")
--dofile(MP .. "/lucky_block.lua")
-- stop mesecon pistons from pushing protectors
if minetest.get_modpath("mesecons_mvps") then
mesecon.register_mvps_stopper("protector:protect")
mesecon.register_mvps_stopper("protector:protect2")
mesecon.register_mvps_stopper("protector:chest")
end
print (S("[MOD] Protector Redo loaded"))

View File

@ -1,45 +0,0 @@
-- Fallback functions for when `intllib` is not installed.
-- Code released under Unlicense <http://unlicense.org>.
-- Get the latest version of this file at:
-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua
local function format(str, ...)
local args = { ... }
local function repl(escape, open, num, close)
if escape == "" then
local replacement = tostring(args[tonumber(num)])
if open == "" then
replacement = replacement..close
end
return replacement
else
return "@"..open..num..close
end
end
return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
end
local gettext, ngettext
if minetest.get_modpath("intllib") then
if intllib.make_gettext_pair then
-- New method using gettext.
gettext, ngettext = intllib.make_gettext_pair()
else
-- Old method using text files.
gettext = intllib.Getter()
end
end
-- Fill in missing functions.
gettext = gettext or function(msgid, ...)
return format(msgid, ...)
end
ngettext = ngettext or function(msgid, msgid_plural, n, ...)
return format(n==1 and msgid or msgid_plural, ...)
end
return gettext, ngettext

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2016 TenPlus1
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,196 +0,0 @@
# German translation for PROTECTOR MOD.
# Copyright (C) 2018 TenPlus1
# This file is distributed under the same license as the PROTECTOR MOD package.
# Xanthin <xanthin@wiuwiu.de>, 2016.
# CodeXP <codexp@gmx.net>, 2018.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROTECTOR MOD\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-07-10 17:33+0200\n"
"PO-Revision-Date: \n"
"Last-Translator: CodeXP <codexp@gmx.net>\n"
"Language-Team: \n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: admin.lua
#, fuzzy
msgid "Remove Protectors around players (separate names with spaces)"
msgstr ""
"Entferne Störschützer von bestimmten Namen in der Nähe von Spielern (trenne "
"Namen durch Leerzeichen)"
#: admin.lua
msgid "<names list>"
msgstr "<Namensliste der Spieler>"
#: admin.lua
msgid "Replace Protector Owner with name provided"
msgstr "Ersetze Besitzer der Störschützer mit neuem Besitzer"
#: admin.lua
msgid "<owner name> <name to replace with>"
msgstr "<Name des Besitzers> <Name des neuen Besitzers>"
#: admin.lua
msgid "Replacing Protector name '@1' with '@2'"
msgstr "Ersetze Besitzer der Störschützer von '@1' mit '@2'"
#: admin.lua
msgid "Show protected areas of your nearby protectors"
msgstr "Zeige geschützte Bereiche der Störschützer in der Nähe"
#: admin.lua
msgid "Protector Names to remove: @1"
msgstr "Störschutznamen zum Entfernen: @1"
#: admin.lua
msgid "Name List Reset"
msgstr "Namensliste zurückgesetzt"
#: doors_chest.lua
msgid "Protected Wooden Door"
msgstr "Geschützte Holztür"
#: doors_chest.lua
msgid "Protected Steel Door"
msgstr "Geschützte Stahltür"
#: doors_chest.lua
msgid "Protected Trapdoor"
msgstr "Geschützte Falltür"
#: doors_chest.lua
msgid "Protected Steel Trapdoor"
msgstr "Geschützte Stahlfalltür"
#: doors_chest.lua
msgid "Protected Chest"
msgstr "Geschützte Truhe"
#: doors_chest.lua
msgid "@1 moves stuff to protected chest at @2"
msgstr "@1 verlagert Dinge in geschützte Truhe bei @2"
#: doors_chest.lua
msgid "@1 takes stuff from protected chest at @2"
msgstr "@1 nimmt Dinge aus geschützter Truhe bei @2"
#: doors_chest.lua
msgid "@1 moves stuff inside protected chest at @2"
msgstr "@1 verschiebt Dinge innerhalb der geschützten Truhe bei @2"
#: doors_chest.lua
msgid "To Chest"
msgstr "Zur Truhe"
#: doors_chest.lua
msgid "To Inventory"
msgstr "Zum Inventar"
#: doors_chest.lua
msgid "Protected Chest (@1)"
msgstr "Geschützte Truhe (@1)"
#: init.lua
msgid "-- Protector interface --"
msgstr "-- Störschutz-Interface --"
#: init.lua
msgid "PUNCH node to show protected area"
msgstr "SCHLAGE Node, um geschützten Bereich anzuzeigen oder"
#: init.lua
msgid "USE for area check"
msgstr "BENUTZE für Bereichsprüfung"
#: init.lua
msgid "Members:"
msgstr "Mitglieder:"
#: init.lua
msgid "Close"
msgstr "Schließen"
#: init.lua
msgid "Protection located at: @1"
msgstr "Störschutz befindet sich bei: @1"
#: init.lua
msgid "Members: @1."
msgstr "Mitglieder: @1."
#: init.lua
msgid "This area is not protected."
msgstr "Dieser Bereich ist nicht geschützt."
#: init.lua
msgid "You can build here."
msgstr "Du kannst hier bauen."
#: init.lua tool.lua
msgid "Overlaps into above players protected area"
msgstr "Überlappung im geschützen Bereich eines Spielers"
#: init.lua
msgid "Protection Block"
msgstr "Störschutzblock"
#: admin.lua init.lua tool.lua
msgid "Protection (owned by @1)"
msgstr "Störschutz (gehört @1)"
#: init.lua
msgid "Protection Logo"
msgstr "Störschutzlogo"
#: init.lua
msgid "[MOD] Protector Redo loaded"
msgstr "[MOD] Protector Redo geladen"
#: init.lua
msgid "Spawn @1 has been protected up to a @2 block radius."
msgstr "Spawn @1 ist geschützt mit einem Radius von @2 Blöcke."
#: init.lua
msgid "This area is owned by @1"
msgstr "Dieser Bereich gehört @1"
#: pvp.lua
msgid "[Protector] on_punchplayer called with nil objects"
msgstr "on_punchplayer wurde durch \"nil objects\" aufgerufen"
#: pvp.lua
msgid "[Protector] pvp_protect not active, update your version of Minetest"
msgstr "pvp_protect ist nicht aktiv, aktualisiere deine Minetestversion"
#: pvp.lua
msgid "[Protector] pvp_protect is disabled"
msgstr "pvp_protect ist ausgeschaltet"
#: hud.lua
msgid "Owner: @1"
msgstr "Besitzer: @1"
#: tool.lua
msgid "Protector Placer Tool (stand near protector, face direction and use)"
msgstr ""
"Störschutz Platzier-Werkzeug (stehe neben Störschutz, schaue in die "
"gewünschte Richtung und anwenden)"
#: tool.lua
msgid "Protector already in place!"
msgstr "Störschutz is bereits platziert!"
#: tool.lua
msgid "No protectors available to place!"
msgstr "Keine Störschützer mehr im Inventar!"
#: tool.lua
msgid "Protector placed at @1"
msgstr "Störschutz befindet sich bei: @1"

View File

@ -1,198 +0,0 @@
# French translation for PROTECTOR MOD.
# Copyright (C) 2018 TenPlus1
# This file is distributed under the same license as the PROTECTOR MOD package.
# Doloment, 2017.
# CodeXP <codexp@gmx.net>, 2018.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROTECTOR MOD\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-07-10 17:33+0200\n"
"PO-Revision-Date: \n"
"Last-Translator: CodeXP <codexp@gmx.net>\n"
"Language-Team: \n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: admin.lua
#, fuzzy
msgid "Remove Protectors around players (separate names with spaces)"
msgstr ""
"Retirer les protecteurs près des joueurs avec les noms fournis (noms séparés "
"avec des espaces)"
#: admin.lua
msgid "<names list>"
msgstr ""
#: admin.lua
msgid "Replace Protector Owner with name provided"
msgstr ""
#: admin.lua
msgid "<owner name> <name to replace with>"
msgstr ""
#: admin.lua
msgid "Replacing Protector name '@1' with '@2'"
msgstr ""
#: admin.lua
msgid "Show protected areas of your nearby protectors"
msgstr ""
#: admin.lua
msgid "Protector Names to remove: @1"
msgstr "Noms de protecteurs à supprimer: @1"
#: admin.lua
msgid "Name List Reset"
msgstr "Liste de noms réinitialiser"
#: doors_chest.lua
msgid "Protected Wooden Door"
msgstr "Porte en bois protégée"
#: doors_chest.lua
msgid "Protected Steel Door"
msgstr "Porte en acier protégée"
#: doors_chest.lua
msgid "Protected Trapdoor"
msgstr "Trappe protégé"
#: doors_chest.lua
msgid "Protected Steel Trapdoor"
msgstr "Trap en acier protégé"
#: doors_chest.lua
msgid "Protected Chest"
msgstr "Coffre protégé"
#: doors_chest.lua
msgid "@1 moves stuff to protected chest at @2"
msgstr "@1 déplace des objets vers le coffre protégée à @2"
#: doors_chest.lua
msgid "@1 takes stuff from protected chest at @2"
msgstr "@1 prend des objets du coffre protégée à @2"
#: doors_chest.lua
#, fuzzy
msgid "@1 moves stuff inside protected chest at @2"
msgstr "@1 déplace des objets à l'intérieur de du coffre protégé à @2"
#: doors_chest.lua
msgid "To Chest"
msgstr "Vers le coffre"
#: doors_chest.lua
msgid "To Inventory"
msgstr "Vers l'inventaire"
#: doors_chest.lua
msgid "Protected Chest (@1)"
msgstr "Coffre protégé (@1)"
#: init.lua
msgid "-- Protector interface --"
msgstr "-- Interface Protector --"
#: init.lua
msgid "PUNCH node to show protected area"
msgstr "TAPÉ le bloc pour afficher la zone protégée"
#: init.lua
msgid "USE for area check"
msgstr "UTILISER pour vérifier la zone"
#: init.lua
msgid "Members:"
msgstr "Membres:"
#: init.lua
msgid "Close"
msgstr "Fermer"
#: init.lua
msgid "Protection located at: @1"
msgstr "Protection située à: @1"
#: init.lua
msgid "Members: @1."
msgstr "Membres: @1."
#: init.lua
msgid "This area is not protected."
msgstr "Cette zone n'est pas protégée."
#: init.lua
msgid "You can build here."
msgstr "Vous pouvez construire ici."
#: init.lua tool.lua
msgid "Overlaps into above players protected area"
msgstr "Vous chevauché une zone protégé."
#: init.lua
msgid "Protection Block"
msgstr "Bloc de protection"
#: admin.lua init.lua tool.lua
msgid "Protection (owned by @1)"
msgstr "Protection (détenue par @1)"
#: init.lua
msgid "Protection Logo"
msgstr "Logo de protection"
#: init.lua
msgid "[MOD] Protector Redo loaded"
msgstr "[MOD] Protector Redo chargé"
#: init.lua
msgid "Spawn @1 has been protected up to a @2 block radius."
msgstr ""
#: init.lua
#, fuzzy
msgid "This area is owned by @1"
msgstr "Cette zone appartient à @1!"
#: pvp.lua
msgid "[Protector] on_punchplayer called with nil objects"
msgstr "[Protector] on_punchplayer appelé avec des objets nil"
#: pvp.lua
msgid "[Protector] pvp_protect not active, update your version of Minetest"
msgstr ""
"[Protector] pvp_protect est désactivé, mettez à jour votre version de "
"Minetest"
#: pvp.lua
msgid "[Protector] pvp_protect is disabled"
msgstr "[Protector] pvp_protect est désactivé"
#: hud.lua
msgid "Owner: @1"
msgstr ""
#: tool.lua
msgid "Protector Placer Tool (stand near protector, face direction and use)"
msgstr ""
#: tool.lua
msgid "Protector already in place!"
msgstr ""
#: tool.lua
msgid "No protectors available to place!"
msgstr ""
#: tool.lua
msgid "Protector placed at @1"
msgstr "Protection située à: @1"

View File

@ -1,197 +0,0 @@
# Russian translation for PROTECTOR MOD.
# Copyright (C) 2018 TenPlus1
# This file is distributed under the same license as the PROTECTOR MOD package.
# Doloment, 2017.
# CodeXP <codexp@gmx.net>, 2018.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROTECTOR MOD\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-07-10 17:33+0200\n"
"PO-Revision-Date: \n"
"Last-Translator: CodeXP <codexp@gmx.net>\n"
"Language-Team: \n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: admin.lua
#, fuzzy
msgid "Remove Protectors around players (separate names with spaces)"
msgstr ""
"Удалить защиту рядом с игроками (перечислить имена, разделяя пробелами)"
#: admin.lua
msgid "<names list>"
msgstr "<список имён>"
#: admin.lua
msgid "Replace Protector Owner with name provided"
msgstr "Заменить владельца защиты новым владельцем"
#: admin.lua
msgid "<owner name> <name to replace with>"
msgstr "<имя владельца> <имя нового владельца>"
#: admin.lua
msgid "Replacing Protector name '@1' with '@2'"
msgstr "Заменяется владелец защиты с '@1' на '@2'"
#: admin.lua
msgid "Show protected areas of your nearby protectors"
msgstr "Показать защищенные территории с ближней защитой"
#: admin.lua
msgid "Protector Names to remove: @1"
msgstr "Имена защит, подлежащих удалению: @1"
#: admin.lua
msgid "Name List Reset"
msgstr "Сброс списка имен"
#: doors_chest.lua
msgid "Protected Wooden Door"
msgstr "Защищенная яблоневая дверь"
#: doors_chest.lua
msgid "Protected Steel Door"
msgstr "Защищенная стальная дверь"
#: doors_chest.lua
msgid "Protected Trapdoor"
msgstr "Защищенный яблоневый люк"
#: doors_chest.lua
msgid "Protected Steel Trapdoor"
msgstr "Защищенный стальной люк"
#: doors_chest.lua
msgid "Protected Chest"
msgstr "Защищенный сундук"
#: doors_chest.lua
msgid "@1 moves stuff to protected chest at @2"
msgstr "@1 перемещает вещи в защищенный сундук на координатах @2"
#: doors_chest.lua
msgid "@1 takes stuff from protected chest at @2"
msgstr "@1 берет вещи из защищенного сундука на координатах @2"
#: doors_chest.lua
msgid "@1 moves stuff inside protected chest at @2"
msgstr "@1 перемещает вещи внутри защищенного сундука на координатах @2"
#: doors_chest.lua
msgid "To Chest"
msgstr "В сундук"
#: doors_chest.lua
msgid "To Inventory"
msgstr "В инвентрарь"
#: doors_chest.lua
msgid "Protected Chest (@1)"
msgstr "Защищенный сундук (@1)"
#: init.lua
msgid "-- Protector interface --"
msgstr "-- Настройка защиты --"
#: init.lua
msgid "PUNCH node to show protected area"
msgstr "СТУКНУТЬ узел для подсведки защищенной территории"
#: init.lua
msgid "USE for area check"
msgstr "ЛКМ для проверки защищенной территории"
#: init.lua
msgid "Members:"
msgstr "Участники:"
#: init.lua
msgid "Close"
msgstr "Закрыть"
#: init.lua
msgid "Protection located at: @1"
msgstr "Защита находится на координатах @1"
#: init.lua
msgid "Members: @1."
msgstr "Участники: @1."
#: init.lua
msgid "This area is not protected."
msgstr "Территория свободна."
#: init.lua
msgid "You can build here."
msgstr "Здесь можно ставить блоки."
#: init.lua tool.lua
msgid "Overlaps into above players protected area"
msgstr ""
"Защитный блок не может быть установлен: пересечение с областями, защищенными "
"другими игроками"
#: init.lua
msgid "Protection Block"
msgstr "Защитный блок"
#: admin.lua init.lua tool.lua
msgid "Protection (owned by @1)"
msgstr "Защита игрока @1"
#: init.lua
msgid "Protection Logo"
msgstr "Защитный знак"
#: init.lua
msgid "[MOD] Protector Redo loaded"
msgstr "[MOD] Модификация \"Переделанная Защита\" загружена"
#: init.lua
msgid "Spawn @1 has been protected up to a @2 block radius."
msgstr "Спаун @1 защищен в радиусе @2 блока."
#: init.lua
msgid "This area is owned by @1"
msgstr "Эта территория пренадлежит @1"
#: pvp.lua
msgid "[Protector] on_punchplayer called with nil objects"
msgstr "[Защита] on_punchplayer вызвана с нулевыми объектами"
#: pvp.lua
msgid "[Protector] pvp_protect not active, update your version of Minetest"
msgstr "[Защита] pvp_protect не активен, обновите версию Minetest"
#: pvp.lua
msgid "[Protector] pvp_protect is disabled"
msgstr "[Защита] pvp_protect отключен"
#: hud.lua
msgid "Owner: @1"
msgstr "Владелец: @1"
#: tool.lua
msgid "Protector Placer Tool (stand near protector, face direction and use)"
msgstr ""
"Инструмент установки защиты (станьте рядом с защитой, повернитесь в нужное "
"направление и используйте)"
#: tool.lua
msgid "Protector already in place!"
msgstr "Защита уже установлена!"
#: tool.lua
msgid "No protectors available to place!"
msgstr "У вас нет защитных блоков в инвентаре!"
#: tool.lua
msgid "Protector placed at @1"
msgstr "Защита находится на координатах @1"

View File

@ -1,191 +0,0 @@
# Template for PROTECTOR MOD.
# Copyright (C) 2018 TenPlus1
# This file is distributed under the same license as the PROTECTOR MOD package.
# Xanthin <xanthin@wiuwiu.de>, 2016.
# CodeXP <codexp@gmx.net>, 2018.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROTECTOR MOD\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-07-10 17:33+0200\n"
"PO-Revision-Date: \n"
"Last-Translator: CodeXP <codexp@gmx.net>\n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: admin.lua
msgid "Remove Protectors around players (separate names with spaces)"
msgstr ""
#: admin.lua
msgid "<names list>"
msgstr ""
#: admin.lua
msgid "Replace Protector Owner with name provided"
msgstr ""
#: admin.lua
msgid "<owner name> <name to replace with>"
msgstr ""
#: admin.lua
msgid "Replacing Protector name '@1' with '@2'"
msgstr ""
#: admin.lua
msgid "Show protected areas of your nearby protectors"
msgstr ""
#: admin.lua
msgid "Protector Names to remove: @1"
msgstr ""
#: admin.lua
msgid "Name List Reset"
msgstr ""
#: doors_chest.lua
msgid "Protected Wooden Door"
msgstr ""
#: doors_chest.lua
msgid "Protected Steel Door"
msgstr ""
#: doors_chest.lua
msgid "Protected Trapdoor"
msgstr ""
#: doors_chest.lua
msgid "Protected Steel Trapdoor"
msgstr ""
#: doors_chest.lua
msgid "Protected Chest"
msgstr ""
#: doors_chest.lua
msgid "@1 moves stuff to protected chest at @2"
msgstr ""
#: doors_chest.lua
msgid "@1 takes stuff from protected chest at @2"
msgstr ""
#: doors_chest.lua
msgid "@1 moves stuff inside protected chest at @2"
msgstr ""
#: doors_chest.lua
msgid "To Chest"
msgstr ""
#: doors_chest.lua
msgid "To Inventory"
msgstr ""
#: doors_chest.lua
msgid "Protected Chest (@1)"
msgstr ""
#: init.lua
msgid "-- Protector interface --"
msgstr ""
#: init.lua
msgid "PUNCH node to show protected area"
msgstr ""
#: init.lua
msgid "USE for area check"
msgstr ""
#: init.lua
msgid "Members:"
msgstr ""
#: init.lua
msgid "Close"
msgstr ""
#: init.lua
msgid "Protection located at: @1"
msgstr ""
#: init.lua
msgid "Members: @1."
msgstr ""
#: init.lua
msgid "This area is not protected."
msgstr ""
#: init.lua
msgid "You can build here."
msgstr ""
#: init.lua tool.lua
msgid "Overlaps into above players protected area"
msgstr ""
#: init.lua
msgid "Protection Block"
msgstr ""
#: admin.lua init.lua tool.lua
msgid "Protection (owned by @1)"
msgstr ""
#: init.lua
msgid "Protection Logo"
msgstr ""
#: init.lua
msgid "[MOD] Protector Redo loaded"
msgstr ""
#: init.lua
msgid "Spawn @1 has been protected up to a @2 block radius."
msgstr ""
#: init.lua
msgid "This area is owned by @1"
msgstr ""
#: pvp.lua
msgid "[Protector] on_punchplayer called with nil objects"
msgstr ""
#: pvp.lua
msgid "[Protector] pvp_protect not active, update your version of Minetest"
msgstr ""
#: pvp.lua
msgid "[Protector] pvp_protect is disabled"
msgstr ""
#: hud.lua
msgid "Owner: @1"
msgstr ""
#: tool.lua
msgid "Protector Placer Tool (stand near protector, face direction and use)"
msgstr ""
#: tool.lua
msgid "Protector already in place!"
msgstr ""
#: tool.lua
msgid "No protectors available to place!"
msgstr ""
#: tool.lua
msgid "Protector placed at @1"
msgstr ""

View File

@ -1,195 +0,0 @@
# Turkish translation for PROTECTOR MOD.
# Copyright (C) 2018 TenPlus1
# This file is distributed under the same license as the PROTECTOR MOD package.
# mahmutelmas06, 2016.
# CodeXP <codexp@gmx.net>, 2018.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROTECTOR MOD\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-07-10 17:33+0200\n"
"PO-Revision-Date: \n"
"Last-Translator: CodeXP <codexp@gmx.net>\n"
"Language-Team: \n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: admin.lua
#, fuzzy
msgid "Remove Protectors around players (separate names with spaces)"
msgstr ""
"Ismi verilen oyuncuların yanındaki korumaları kaldır. (İsimleri boşlukla "
"ayır)"
#: admin.lua
msgid "<names list>"
msgstr ""
#: admin.lua
msgid "Replace Protector Owner with name provided"
msgstr ""
#: admin.lua
msgid "<owner name> <name to replace with>"
msgstr ""
#: admin.lua
msgid "Replacing Protector name '@1' with '@2'"
msgstr ""
#: admin.lua
msgid "Show protected areas of your nearby protectors"
msgstr ""
#: admin.lua
msgid "Protector Names to remove: @1"
msgstr "Silinecek korumaların isimleri: @1"
#: admin.lua
msgid "Name List Reset"
msgstr "İsim listesini sıfırla"
#: doors_chest.lua
msgid "Protected Wooden Door"
msgstr "Korumalı ahşap kapı"
#: doors_chest.lua
msgid "Protected Steel Door"
msgstr "Korumalı çelik kapı"
#: doors_chest.lua
msgid "Protected Trapdoor"
msgstr "Korumalı tuzak kapısı"
#: doors_chest.lua
msgid "Protected Steel Trapdoor"
msgstr "Korumalı çelik tuzak kapısı"
#: doors_chest.lua
msgid "Protected Chest"
msgstr "Korumalı sandık"
#: doors_chest.lua
msgid "@1 moves stuff to protected chest at @2"
msgstr "@1 korumalı sandığa birşeyler koydu @2"
#: doors_chest.lua
msgid "@1 takes stuff from protected chest at @2"
msgstr "@1 korumalı sandıktan birşeyler aldı @2"
#: doors_chest.lua
#, fuzzy
msgid "@1 moves stuff inside protected chest at @2"
msgstr "@1 korumalı sandığa içinde bir şeyler taşındı @2"
#: doors_chest.lua
msgid "To Chest"
msgstr "Sandığa"
#: doors_chest.lua
msgid "To Inventory"
msgstr "Envantere"
#: doors_chest.lua
msgid "Protected Chest (@1)"
msgstr "Korumalı sandık (@1)"
#: init.lua
msgid "-- Protector interface --"
msgstr ""
#: init.lua
msgid "PUNCH node to show protected area"
msgstr "Korunan alanı göstermek için yumruk"
#: init.lua
msgid "USE for area check"
msgstr "Bölge kontrolü için kullan"
#: init.lua
msgid "Members:"
msgstr "Üyeler"
#: init.lua
msgid "Close"
msgstr "Kapat"
#: init.lua
msgid "Protection located at: @1"
msgstr "Korumanın bulunduğu yer @1"
#: init.lua
msgid "Members: @1."
msgstr "Üyeler @1."
#: init.lua
msgid "This area is not protected."
msgstr "Bu alan korumalı değildir."
#: init.lua
msgid "You can build here."
msgstr "Buraya inşaa edebilirsiniz."
#: init.lua tool.lua
msgid "Overlaps into above players protected area"
msgstr ""
#: init.lua
msgid "Protection Block"
msgstr "Koruma kutusu"
#: admin.lua init.lua tool.lua
msgid "Protection (owned by @1)"
msgstr "Koruma (@1 sahibidir)"
#: init.lua
msgid "Protection Logo"
msgstr "Koruma arması"
#: init.lua
msgid "[MOD] Protector Redo loaded"
msgstr "[MOD] Protector Redo yüklendi"
#: init.lua
msgid "Spawn @1 has been protected up to a @2 block radius."
msgstr ""
#: init.lua
msgid "This area is owned by @1"
msgstr "Burasının sahibi @1!"
#: pvp.lua
msgid "[Protector] on_punchplayer called with nil objects"
msgstr "[Protector] on_punchplayer boş objelerle çağrıldı"
#: pvp.lua
msgid "[Protector] pvp_protect not active, update your version of Minetest"
msgstr "[Protector] pvp_protect aktif değil, Minetest sürümünüzü güncelleyin."
#: pvp.lua
msgid "[Protector] pvp_protect is disabled"
msgstr "[Protector] pvp_protect kapatıldı."
#: hud.lua
msgid "Owner: @1"
msgstr ""
#: tool.lua
msgid "Protector Placer Tool (stand near protector, face direction and use)"
msgstr ""
#: tool.lua
msgid "Protector already in place!"
msgstr ""
#: tool.lua
msgid "No protectors available to place!"
msgstr ""
#: tool.lua
msgid "Protector placed at @1"
msgstr "Korumanın bulunduğu yer @1"

View File

@ -1,18 +0,0 @@
-- add lucky blocks
if minetest.get_modpath("lucky_block") then
lucky_block:add_blocks({
{"dro", {"protector:protect"}, 3},
{"dro", {"protector:protect2"}, 3},
{"dro", {"protector:door_wood"}, 1},
{"dro", {"protector:door_steel"}, 1},
{"exp", 5, true},
{"dro", {"protector:trapdoor"}, 1},
{"dro", {"protector:trapdoor_steel"}, 1},
{"dro", {"protector:tool"}, 1},
{"dro", {"protector:chest"}, 1},
{"exp"},
})
end

View File

@ -1 +0,0 @@
name = protector

View File

@ -1,72 +0,0 @@
local S = protector.intllib
-- get static spawn position
local statspawn = minetest.string_to_pos(minetest.settings:get("static_spawnpoint"))
or {x = 0, y = 2, z = 0}
-- is spawn protected
local protector_spawn = tonumber(minetest.settings:get("protector_spawn")
or minetest.settings:get("protector_pvp_spawn")) or 0
-- is night-only pvp enabled
local protector_night_pvp = minetest.settings:get_bool("protector_night_pvp")
-- disables PVP in your own protected areas
if minetest.settings:get_bool("enable_pvp")
and minetest.settings:get_bool("protector_pvp") then
if minetest.register_on_punchplayer then
minetest.register_on_punchplayer(function(player, hitter,
time_from_last_punch, tool_capabilities, dir, damage)
if not player
or not hitter then
print(S("[Protector] on_punchplayer called with nil objects"))
end
if not hitter:is_player() then
return false
end
-- no pvp at spawn area
local pos = player:get_pos()
if pos.x < statspawn.x + protector_spawn
and pos.x > statspawn.x - protector_spawn
and pos.y < statspawn.y + protector_spawn
and pos.y > statspawn.y - protector_spawn
and pos.z < statspawn.z + protector_spawn
and pos.z > statspawn.z - protector_spawn then
return true
end
-- do we enable pvp at night time only ?
if protector_night_pvp then
-- get time of day
local tod = minetest.get_timeofday() or 0
if tod > 0.2 and tod < 0.8 then
--
else
return false
end
end
-- is player being punched inside a protected area ?
if minetest.is_protected(pos, hitter:get_player_name()) then
return true
end
return false
end)
else
print(S("[Protector] pvp_protect not active, update your version of Minetest"))
end
else
print(S("[Protector] pvp_protect is disabled"))
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 804 B

View File

@ -1,30 +0,0 @@
following Textures created by Fernando Zapata (CC BY-SA 3.0):
doors_wood.png
doors_wood_a.png
doors_wood_b.png
doors_brown.png
following Textures created by BlockMen (WTFPL):
doors_trapdoor.png
following textures created by celeron55 (CC BY-SA 3.0):
doors_trapdoor_side.png
following textures created by PilzAdam (WTFPL):
doors_steel.png
doors_steel_a.png
doors_steel_b.png
doors_grey.png
doors_trapdoor_steel.png
doors_trapdoor_steel_side.png
following textures by Cisoun (WTFPL):
default_chest_front.png
default_chest_side.png
default_chest_top.png
following textures by TenPlus1 (CC BY-SA 3.0):
protector_logo.png
protector_display.png
protector_overlay.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 B

View File

@ -1,135 +0,0 @@
-- protector placement tool (thanks to Shara for code and idea)
-- get protection radius
local r = tonumber(minetest.settings:get("protector_radius")) or 5
minetest.register_craftitem("protector:tool", {
description = "Protector Placer Tool (stand near protector, face direction and use)",
inventory_image = "protector_display.png^protector_logo.png",
stack_max = 1,
on_use = function(itemstack, user, pointed_thing)
local name = user:get_player_name()
-- check for protector near player (2 block radius)
local pos = user:get_pos()
local pp = minetest.find_nodes_in_area(
vector.subtract(pos, 2), vector.add(pos, 2),
{"protector:protect", "protector:protect2"})
if #pp == 0 then return end -- none found
pos = pp[1] -- take position of first protector found
-- get members on protector
local meta = minetest.get_meta(pos)
local members = meta:get_string("members") or ""
-- get direction player is facing
local dir = minetest.dir_to_facedir( user:get_look_dir() )
local vec = {x = 0, y = 0, z = 0}
local gap = (r * 2) + 1
local pit = user:get_look_pitch()
-- set placement coords
if pit > 1.2 then
vec.y = gap -- up
elseif pit < -1.2 then
vec.y = -gap -- down
elseif dir == 0 then
vec.z = gap -- north
elseif dir == 1 then
vec.x = gap -- east
elseif dir == 2 then
vec.z = -gap -- south
elseif dir == 3 then
vec.x = -gap -- west
end
-- new position
pos.x = pos.x + vec.x
pos.y = pos.y + vec.y
pos.z = pos.z + vec.z
-- does placing a protector overlap existing area
if not protector.can_dig(r * 2, pos, user:get_player_name(), true, 3) then
minetest.chat_send_player(name,
"Overlaps into above players protected area")
return
end
-- does a protector already exist ?
if #minetest.find_nodes_in_area(
vector.subtract(pos, 1), vector.add(pos, 1),
{"protector:protect", "protector:protect2"}) > 0 then
minetest.chat_send_player(name, "Protector already in place!")
return
end
-- do we have protectors to use ?
local nod
local inv = user:get_inventory()
if not inv:contains_item("main", "protector:protect")
and not inv:contains_item("main", "protector:protect2") then
minetest.chat_send_player(name, "No protectors available to place!")
return
end
-- take protector (block first then logo)
if inv:contains_item("main", "protector:protect") then
inv:remove_item("main", "protector:protect")
nod = "protector:protect"
elseif inv:contains_item("main", "protector:protect2") then
inv:remove_item("main", "protector:protect2")
nod = "protector:protect2"
end
-- do not replace containers with inventory space
local inv = minetest.get_inventory({type = "node", pos = pos})
if inv then
minetest.chat_send_player(name,
"Cannot place protector, container at " .. minetest.pos_to_string(pos))
return
end
-- place protector
minetest.set_node(pos, {name = nod, param2 = 1})
-- set protector metadata
local meta = minetest.get_meta(pos)
meta:set_string("owner", name)
meta:set_string("infotext", "Protection (owned by " .. name .. ")")
-- copy members across if holding sneak when using tool
if user:get_player_control().sneak then
meta:set_string("members", members)
else
meta:set_string("members", "")
end
minetest.chat_send_player(name,
"Protector placed at " .. minetest.pos_to_string(pos))
end,
})
-- tool recipe
minetest.register_craft({
output = "protector:tool",
recipe = {
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
{"default:steel_ingot", "protector:protect", "default:steel_ingot"},
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
}
})