Add files via upload

This commit is contained in:
VoidCosmos 2021-09-22 13:49:25 +05:30 committed by GitHub
parent 080fa3ee1d
commit 157dcc6ec7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
92 changed files with 14039 additions and 0 deletions

View File

@ -0,0 +1,19 @@
local function look_nearest()
if not minetest.localplayer then return end
for k, v in ipairs(minetest.localplayer.get_nearby_objects(10)) do
if (v:is_player() and v:get_name() ~= minetest.localplayer:get_name()) then
local pos = v:get_pos()
pos.y = pos.y - 1
autofly.aim(pos)
return
end
end
end
minetest.register_globalstep(function()
if minetest.settings:get_bool("autoaim") then
look_nearest()
end
end)
minetest.register_cheat("Autoaim", "Combat", "autoaim")

View File

@ -0,0 +1,264 @@
-- TODO
--[[
free space could be a concern
traverse_recurse should replace groups with the concrete item
maybe recursions yield an item name?
that way has_sub could indicate the item which would be yielded in traverse_recurse and replaced in the recipe before being queued
autocraft.recipes should be loaded and custom per modset
it might choke on empty items in the recipe
this needs to handle groups a bit better, i dont think it will mix woods/etc
--]]
autocraft = {}
autocraft.recipes = {
["mcl_core:crafting_table"] = {
recipes = {
{
recipe = {
{"group:planks", "group:planks"},
{"group:planks", "group:planks"}}
}
}
},
["mcl_core:wood"] = {
groups = {"planks"},
recipes = {
{
count = 4,
recipe = {
"mcl_core:tree"
},
shapeless = true -- redundant, can detect from the lack of subtables
}
}
}
}
autocraft.groups = {}
-- extract groups from autocraft.recipes
local function group_arrange()
for k, v in pairs(autocraft.recipes) do
if v.groups then
for gi, gv in ipairs(v.groups) do
if not autocraft.groups[gv] then
autocraft.groups[gv] = {}
end
table.insert(autocraft.groups[gv], k)
end
end
end
end
group_arrange()
local function startswith(str, start)
return string.sub(str, 1, #start) == start
end
local function combine(t1, t2)
local t1l = #t1
local o = {}
for i, v in ipairs(t1) do
o[i] = v
end
for i, v in ipairs(t2) do
o[t1l + i] = v
end
return o
end
local function parse_group(str)
if startswith(str, "group:") then
return str:match("group:(.+)")
end
end
-- get recipes for an item/group
local function get_recipes(str)
local group = parse_group(str)
if group then
local o = {}
if autocraft.groups[group] then
for i, v in ipairs(autocraft.groups[group]) do
o = combine(o, get_recipes(v))
end
return o
end
else
local idef = autocraft.recipes[str]
if idef then
local o = idef.recipes
for i, v in ipairs(o) do
v.name = str
end
return o
end
end
return {}
end
-- count up all the items in the player's inventory
-- output:
-- {
-- item = n,
-- item = n
-- }
local function count_inv()
local o = {}
local lpim = minetest.get_inventory("current_player").main
for i, v in ipairs(lpim) do
if not v:is_empty() then
o[v:get_name()] = (o[v:get_name()] or 0) + v:get_count()
end
end
return o
end
-- effectively turn a recipe shapeless
local function flatten_recipe(recipe)
if type(recipe[1]) == "table" then
local o = {}
for i, v in ipairs(recipe) do
o = combine(o, v)
end
return o
else
return recipe
end
end
-- count the requirements for a recipe, uses count_inv format
local function count_recipe(recipe)
local o = {}
for i, v in ipairs(flatten_recipe(recipe)) do
local item = ItemStack(v)
o[item:get_name()] = (o[item:get_name()] or 0) + item:get_count()
end
return o
end
-- get all item strings for an item or group string
local function get_items_of(str)
local group = parse_group(str)
if group then
return autocraft.groups[group]
else
return {str}
end
end
-- check if the recipe can be crafted with current resources
local function can_craft(resources, recipe)
for k, count in pairs(count_recipe(recipe)) do
for i, vv in ipairs(get_items_of(k)) do
local item = ItemStack(vv)
if (resources[item:get_name()] or 0) >= count then
break
end
end
end
return true
end
-- traverse all items in a recipe
local function recurse_recipe(resources, queue, recipe)
for i, v in ipairs(flatten_recipe(recipe)) do
if not autocraft.traverse_recurse(resources, queue, ItemStack(v)) then
return false
end
end
return true
end
-- checks if the item/group is in the resource list and subtracts it
-- is the base case for traverse_recurse
local function has_sub(resources, item)
for i, v in ipairs(get_items_of(item:get_name())) do
if resources[v] and resources[v] >= item:get_count() then
resources[v] = resources[v] - item:get_count()
return true
end
end
return false
end
-- enqueues a recipe for an item and its needed sub items
function autocraft.traverse_recurse(resources, queue, item)
if type(item) == "string" then
item = ItemStack(item)
end
-- base case, uncraftibles/already in inventory
if has_sub(resources, item) then
return true
else
for i, v in ipairs(get_recipes(item:get_name())) do
if can_craft(resources, v.recipe) then
if recurse_recipe(resources, queue, v.recipe) then
table.insert(queue, v.recipe)
local tgt = item:get_count()
local result = v.count or 1
local delta = tgt - result
resources[v.name] = (resources[v.name] or 0) + result
if delta > 0 then
item:set_count(delta)
traverse_recurse(resources, queue, item)
end
return true
end
end
end
end
-- not enough resources
return false
end
-- create a crafting queue for an item
-- return queue if enough resources, nil if not
function autocraft.traverse(item)
local queue = {}
local resources = count_inv()
if autocraft.traverse_recurse(resources, queue, item) then
return queue
end
end
-- craft a traversed craft tree
local function queuecraft(tree)
end
-- make a queue and craft it
function autocraft.craft(item)
end

View File

@ -0,0 +1,3 @@
name = autocraft
author = emilia
description = Automatic crafting. Supports crafting blocks (Mineclone) and custom crafting trees.

674
clientmods/autoeat/LICENSE Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@ -0,0 +1,2 @@
# autoeat
A dragonfire CSM to automatically eat when a certain hunger is reached (MineClone2).

View File

@ -0,0 +1,76 @@
autoeat = {}
autoeat.lock = false
local autodupe = false --rawget(_G, "autodupe")
local hud_id = nil
local function get_float(name, default)
return tonumber(minetest.settings:get("autoeat_" .. name) or "") or default
end
local etime = 0
function autoeat.eat()
local food_index
local food_count = 0
for index, stack in pairs(minetest.get_inventory("current_player").main) do
local stackname = stack:get_name()
if stackname ~= "" then
local def = minetest.get_item_def(stackname)
if def and def.groups.food then
food_count = food_count + 1
if food_index then
break
end
food_index = index
end
end
end
if food_index then
if food_count == 1 and autodupe then
--autodupe.needed(food_index)
autoeat.lock = true
else
local player = minetest.localplayer
local old_index = player:get_wield_index()
player:set_wield_index(food_index)
--minetest.interact("activate", {type = "nothing"})
minetest.place_node(minetest.localplayer:get_pos())
player:set_wield_index(old_index)
autoeat.lock = false
end
end
end
function autoeat.get_hunger()
if hud_id then
return minetest.localplayer:hud_get(hud_id).number
else
return 20
end
end
minetest.register_globalstep(function(dtime)
if not minetest.localplayer then return end
etime = etime + dtime
if autoeat.lock or minetest.settings:get_bool("autoeat") and etime >= get_float("cooldown", 0.5) and autoeat.get_hunger() < get_float("hunger", 9) then
etime = 0
autoeat.eat()
end
end)
local function get_hud()
local player = minetest.localplayer
local def
local i = -1
if not player then minetest.after(5,get_hud) return end
repeat
i = i + 1
def = player:hud_get(i)
until not def or def.text == "hbhunger_icon.png"
if def then
hud_id = i
end
end
minetest.after(15,get_hud )
minetest.register_cheat("AutoEat", "Player", "autoeat")

View File

@ -0,0 +1,4 @@
name = autoeat
author = Fleckenstein
description = Automatically eat when a certain hunger is reached (MineClone2).
optional_depends = autodupe

View File

@ -0,0 +1,3 @@
autoeat (AutoEat) bool false
autoeat_cooldown (AutoEat cooldown) float 0.5
autoeat_hunger (AutoEat hunger) float 9.0

732
clientmods/autofly/init.lua Normal file
View File

@ -0,0 +1,732 @@
-- autofly by cora
-- gui shit shamelessly stolen from advmarkers
-- https://git.minetest.land/luk3yx/advmarkers-csm
--[[
PATCHING MINETEST: (for autoaim)
in l_localplayer.h add:
static int l_set_yaw(lua_State *L);
static int l_set_pitch(lua_State *L);
in l_localplayer.cpp add:
int LuaLocalPlayer::l_set_yaw(lua_State *L)
{
LocalPlayer *player = getobject(L, 1);
f32 p = (float) luaL_checkinteger(L, 2);
player->setYaw(p);
g_game->cam_view.camera_yaw = p;
g_game->cam_view_target.camera_yaw = p;
player->setYaw(p);
return 0;
}
int LuaLocalPlayer::l_set_pitch(lua_State *L)
{
LocalPlayer *player = getobject(L, 1);
f32 p = (float) luaL_checkinteger(L, 2);
player->setPitch(p);
g_game->cam_view.camera_pitch = p;
g_game->cam_view_target.camera_pitch = p;
player->setPitch(p);
return 0;
}
in src/client/game.h, below class Game { public: add:
CameraOrientation cam_view = {0};
CameraOrientation cam_view_target = { 0 };
from src/client/game.cpp remove
CameraOrientation cam_view = {0};
CameraOrientation cam_view_target = { 0 };
--]]
-- Chat commands:
-- .wa x,y,z name - add waypoint with coords and name
-- .wah - quickadd this location (name will be time and date)
-- .wp - open the selection menu
-- .cls - remove hud
autofly = {}
wps={}
local landing_distance=5
local speed=0;
local ltime=0
local storage = minetest.get_mod_storage()
local oldpm=false
local lpos={x=0,y=0,z=0}
local info=minetest.get_server_info()
local stprefix="autofly-".. info['address'] .. '-'
--local stprefix="autofly-"
local hud_wps={}
autofly.flying=false
autofly.cruiseheight = 30
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
dofile(modpath .. "/wpforms.lua")
dofile(modpath .. "/pathfly.lua")
local hud_wp
local hud_info
-- /COMMON
local pos_to_string = ws.pos_to_string
local string_to_pos = ws.string_to_pos
function autofly.get2ddst(pos1,pos2)
return vector.distance({x=pos1.x,y=0,z=pos1.z},{x=pos2.x,y=0,z=pos2.z})
end
local last_sprint = false
local hud_ah=nil
function autofly.update_ah()
local pos=vector.new(0,0,0)
local ppos=minetest.localplayer:get_pos()
local yaw=math.floor(minetest.localplayer:get_yaw())
local theta =(yaw * math.pi / 180)
pos.x= math.floor( 100 * math.cos(theta) )
pos.z= math.floor( 100 * math.sin(theta) )
pos=vector.add(ppos,pos)
pos.y=ppos.y
local nname=pos_to_string(pos).."\n"..yaw.."\n"..'__________________________________________________________________________________________________________________________________________________'
if hud_ah then
minetest.display_chat_message(pos.x..","..pos.z)
minetest.localplayer:hud_change(hud_ah, 'world_pos', pos)
minetest.localplayer:hud_change(hud_ah, 'name', nname)
else
hud_ah = minetest.localplayer:hud_add({
hud_elem_type = 'waypoint',
name = nname,
title = pos_to_string(pos),
text = '',
number = 0x00ff00,
world_pos = pos,
precision = 0,
width = 1000
})
end
end
minetest.register_globalstep(function()
if not minetest.localplayer then return end
-- autofly.update_ah()
end)
minetest.register_globalstep(function()
if not minetest.localplayer then return end
autofly.axissnap()
if minetest.settings:get_bool("autosprint") or (minetest.settings:get_bool("continuous_forward") and minetest.settings:get_bool("autofsprint")) then
core.set_keypress("special1", true)
last_sprint = true
elseif last_sprint then
core.set_keypress("special1", false)
last_sprint = false
end
if not autofly.flying then autofly.set_hud_info("")
else
autofly.set_hud_info("")
local pos = autofly.last_coords
if pos then
local dst = vector.distance(pos,minetest.localplayer:get_pos())
local etatime=-1
if not (speed == 0) then etatime = ws.round2(dst / speed / 60,2) end
autofly.etatime=etatime
autofly.set_hud_info(autofly.last_name .. "\n" .. pos_to_string(pos) .. "\n" .. "ETA" .. etatime .. " mins")
local pm=minetest.settings:get_bool('pitch_move')
local hdst=autofly.get2ddst(pos,minetest.localplayer:get_pos())
if pm then hdst=vector.distance(pos,ws.dircoord(0,0,0)) end
if autofly.flying and hdst < landing_distance then
autofly.arrived()
end
end
end
if not minetest.settings:get_bool("freecam") and autofly.flying and (minetest.settings:get_bool('afly_autoaim')) then
autofly.aim(autofly.last_coords)
end
if ( os.time() < ltime + 1 ) then return end
ltime=os.time()
if lpos then
local dst=vector.distance(minetest.localplayer:get_pos(),lpos)
speed=ws.round2(dst,1)
autofly.speed=speed
end
lpos=minetest.localplayer:get_pos()
autofly.cruise()
end)
function autofly.get_speed()
return speed
end
function autofly.set_hud_wp(pos, title)
if hud_wp then
minetest.localplayer:hud_remove(hud_wp)
end
pos = string_to_pos(pos)
hud_wp=nil
if not pos then return end
if not title then
title = pos.x .. ', ' .. pos.y .. ', ' .. pos.z
end
autofly.last_name=title
if hud_wp then
minetest.localplayer:hud_change(hud_wp, 'name', title)
minetest.localplayer:hud_change(hud_wp, 'world_pos', pos)
else
hud_wp = minetest.localplayer:hud_add({
hud_elem_type = 'waypoint',
name = title,
text = 'm',
number = 0x00ff00,
world_pos = pos
})
end
return true
end
local hud_info
function autofly.get_quad()
local lp=minetest.localplayer:get_pos()
local quad=""
if lp.z < 0 then quad="South"
else quad="North" end
if lp.x < 0 then quad=quad.."-west"
else quad=quad.."-east" end
return quad
end
function autofly.get_wdir()
local qd=autofly.get_quad()
end
function autofly.get_local_name()
local ww=autofly.getwps()
local lp=minetest.localplayer:get_pos()
local odst=500;
local rt=false
for k,v in pairs(ww) do
local lwp=autofly.get_waypoint(v)
if type(lwp) == 'table' then
local dst=vector.distance(lp,lwp)
if dst < 500 then
if dst < odst then
odst=dst
rt=v
end
end
end
end
if not rt then rt=autofly.get_quad() end
return rt
end
local function countents()
local obj = minetest.localplayer.get_nearby_objects(10000)
return #obj
end
function autofly.set_hud_info(text)
if not minetest.localplayer then return end
if type(text) ~= "string" then return end
local dir=ws.getdir()
local ddir=""
if dir == "north" then
ddir="north(+z)"
elseif dir == "east" then
ddir="east(+x)"
elseif dir == "south" then
ddir="south(-z)"
elseif dir == "west" then
ddir="west(-x)"
end
local lp=minetest.localplayer
local vspeed=lp:get_velocity()
local ttext=text.."\nSpeed: "..speed.."n/s\n"
..ws.round2(vspeed.x,2) ..','
..ws.round2(vspeed.y,2) ..','
..ws.round2(vspeed.z,2) .."\n"
.."Yaw:"..ws.round2(lp:get_yaw(),2).."° Pitch:" ..ws.round2(lp:get_pitch(),2).."° "
if turtle then ttext=ttext..ddir end
if minetest.settings:get_bool('afly_shownames') then
ttext=ttext.."\n"..autofly.get_local_name() .."\nEntities: " .. countents()
end
if hud_info then
minetest.localplayer:hud_change(hud_info,'text',ttext)
else
hud_info = minetest.localplayer:hud_add({
hud_elem_type = 'text',
name = "Flight Info",
text = ttext,
number = 0x00ff00,
direction = 0,
position = {x=0,y=0.8},
alignment ={x=1,y=1},
offset = {x=0, y=0}
})
end
return true
end
function autofly.display(pos,name)
if name == nil then name=pos_to_string(pos) end
local pos=string_to_pos(pos)
autofly.set_hud_wp(pos, name)
return true
end
function autofly.display_waypoint(name)
local pos=name
if type(name) ~= 'table' then pos=autofly.get_waypoint(name) end
autofly.last_name = name
--autofly.last_coords = pos
autofly.set_hud_info(name)
autofly.aim(autofly.last_coords)
autofly.display(pos,name)
return true
end
function autofly.goto_waypoint(name)
local wp=autofly.get_waypoint(name)
autofly.goto(wp)
autofly.last_name=name
autofly.display_waypoint(autofly.last_name)
return true
end
function autofly.goto(pos)
minetest.settings:set_bool("free_move",true)
minetest.settings:set_bool("continuous_forward",true)
if minetest.settings:get_bool("afly_sprint") then
minetest.settings:set_bool("autofsprint",true)
minetest.settings:set_bool("autoeat_timed",true)
end
minetest.settings:set_bool("afly_autoaim",true)
autofly.last_coords = pos
autofly.last_name = minetest.pos_to_string(pos)
autofly.aim(autofly.last_coords)
autofly.flying=true
autofly.set_hud_wp(autofly.last_coords, autofly.last_name)
return true
end
function autofly.fly3d(pos)
minetest.settings:set_bool("pitch_move",true)
autofly.goto(pos)
end
function autofly.fly2d(pos)
minetest.settings:set_bool("pitch_move",false)
autofly.goto(pos)
end
function autofly.arrived()
if not autofly.flying then return end
minetest.settings:set("continuous_forward", "false")
minetest.settings:set_bool("autofsprint",false)
minetest.settings:set_bool("pitch_move",oldpm)
minetest.settings:set_bool("afly_autoaim",false)
minetest.settings:set_bool("autoeat_timed",false)
autofly.set_hud_info("Arrived!")
autofly.flying = false
minetest.sound_play({name = "default_alert", gain = 1.0})
end
local cruise_wason=false
local nfctr=0
function autofly.cruise()
if not minetest.settings:get_bool('afly_cruise') then
if cruise_wason then
cruise_wason=false
core.set_keypress("jump",false)
core.set_keypress("sneak",false)
end
return end
local lp=minetest.localplayer:get_pos()
local pos1 = vector.add(lp,{x=16,y=100,z=16})
local pos2 = vector.add(lp,{x=-16,y=-100,z=-16})
local nds=minetest.find_nodes_in_area_under_air(pos1, pos2, nlist.get_mclnodes())
local y=0
local found=false
for k,v in ipairs(nds) do
local nd = minetest.get_node_or_nil(v)
if nd ~= nil and nd.name ~= "air" then
if v.y > y then
y=v.y
found=true
end
end
end
if (autofly.cruiseheight ~= nil) then y=y+autofly.cruiseheight end
local diff = math.ceil(lp.y - y)
if not cruise_wason then --initially set the cruiseheight to the current value above ground
-- if not found then return end --wait with activation til a ground node has been found.
local clr,nnd=minetest.line_of_sight(lp,vector.add(lp,{x=1,y=-200,z=1}))
if not clr then diff = math.ceil(lp.y - nnd.y)
elseif not found then return end
if diff < 1 then autofly.cruiseheight = 20
else autofly.cruiseheight = diff end
cruise_wason=true
minetest.display_chat_message("cruise mode activated. target height set to " .. diff .. " nodes above ground.")
end
if not found then
if nfctr<20 then nfctr = nfctr + 1 return end
--minetest.display_chat_message("no nodes found for 20 iterations. lowering altitude.")
nfctr=0
minetest.settings:set_bool("free_move",false)
core.set_keypress("jump",false)
core.set_keypress("sneak",false)
return
end
local tolerance = 1
if diff < -tolerance then
minetest.settings:set_bool("free_move",true)
core.set_keypress("jump",true)
core.set_keypress("sneak",false)
--minetest.display_chat_message("too low: " .. y)
elseif diff > tolerance * 10 then
core.set_keypress("jump",false)
core.set_keypress("sneak",true)
minetest.settings:set_bool("free_move",false)
--minetest.display_chat_message("too high: " .. y)
elseif diff > tolerance then
core.set_keypress("jump",false)
core.set_keypress("sneak",true)
else
minetest.settings:set_bool("free_move",true)
core.set_keypress("jump",false)
core.set_keypress("sneak",false)
--minetest.display_chat_message("target height reached: " .. y)
end
end
function autofly.aim(tpos)
return ws.aim(tpos)
end
function autofly.autotp(tpname)
if minetest.localplayer == nil then autofly.autotp(tpname) end
local tpos=nil
if tpname == nil then
tpos = autofly.get_waypoint('AUTOTP')
elseif type(tpname) == "table" then
tpos = tpname
else
tpos=autofly.get_waypoint(tpname)
end
if tpos == nil then return end
local lp=minetest.localplayer
local dst=vector.distance(lp:get_pos(),tpos)
if (dst < 300) then
minetest.sound_play({name = "default_alert", gain = 3.0})
autofly.delete_waypoint('AUTOTP')
return true
end
autofly.set_waypoint(tpos,'AUTOTP')
local boat_found=false
for k, v in ipairs(lp.get_nearby_objects(4)) do
local txt = v:get_item_textures()
if ( txt:find('mcl_boats_texture')) then
boat_found=true
minetest.display_chat_message("boat found. entering and tping to "..minetest.pos_to_string(autofly.get_waypoint('AUTOTP')))
autofly.aim(vector.add(v:get_pos(),{x=0,y=-1.5,z=0}))
minetest.after("0.2",function()
minetest.interact("place") end)
minetest.after("1.5",function()
autofly.warpae('AUTOTP')
end)
return true
end
end
if not boat_found then
minetest.display_chat_message("no boat found. trying again in 5.")
minetest.after("5.0",function() autofly.autotp(tpname) end)
return end
end
autofly.register_transport('Fly3D',function(pos,name) autofly.fly3d(pos,name) end)
autofly.register_transport('Fly2D',function(pos,name) autofly.fly2d(pos,name) end)
autofly.register_transport('wrp',function(pos,name) autofly.warp(name) end)
--autofly.register_transport('atp',function(pos,name) autofly.autotp(name) end)
function autofly.axissnap()
if not minetest.settings:get_bool('afly_snap') then return end
if minetest.settings:get_bool("freecam") then return end
local y=minetest.localplayer:get_yaw()
local yy=nil
if ( y < 45 or y > 315 ) then
yy=0
elseif (y < 135) then
yy=90
elseif (y < 225 ) then
yy=180
elseif ( y < 315 ) then
yy=270
end
if yy ~= nil then
minetest.localplayer:set_yaw(yy)
end
end
minetest.register_on_death(function()
if minetest.localplayer then
local name = 'Death waypoint'
local pos = minetest.localplayer:get_pos()
autofly.last_coords = pos
autofly.last_name = name
autofly.set_waypoint(pos,name)
autofly.display(pos,name)
end
end)
local function get_dimension(pos)
if pos.y > -65 then return "overworld"
elseif pos.y > -8000 then return "void"
elseif pos.y > -27000 then return "end"
elseif pos.y >29000 then return "void"
elseif pos.y >31000 then return "nether"
else return "void"
end
end
function autofly.warp(name)
local pos=autofly.get_waypoint(name)
if pos then
if get_dimension(pos) == "void" then return false end
minetest.localplayer:set_pos(pos)
return true
end
end
function autofly.warpae(name)
local s, m = autofly.warp(name)
if s then
minetest.disconnect()
end
return true
end
function autofly.getwps()
local wp={}
for name, _ in pairs(storage:to_table().fields) do
if name:sub(1, string.len(stprefix)) == stprefix then
table.insert(wp, name:sub(string.len(stprefix)+1))
end
end
table.sort(wp)
return wp
end
function autofly.impfromsrv(srv,sel)
local srvstr="autofly-".. srv .. '-'
for name, _ in pairs(storage:to_table().fields) do
if name:sub(1, string.len(srvstr)) == srvstr then
local name=name:sub(string.len(srvstr)+1)
if not sel or ( sel and name:sub(1, string.len(sel)) == sel ) then
local pos=string_to_pos(storage:get_string(srvstr .. tostring(name)))
autofly.set_waypoint(pos,name)
end
end
end
return wp
end
function autofly.set_waypoint(pos, name)
pos = pos_to_string(pos)
if not pos then return end
storage:set_string(stprefix .. tostring(name), pos)
return true
end
function autofly.delete_waypoint(name)
storage:set_string(stprefix .. tostring(name), '')
end
function autofly.get_waypoint(name)
return string_to_pos(storage:get_string(stprefix .. tostring(name)))
end
function autofly.rename_waypoint(oldname, newname)
oldname, newname = tostring(oldname), tostring(newname)
local pos = autofly.get_waypoint(oldname)
if not pos or not autofly.set_waypoint(pos, newname) then return end
if oldname ~= newname then
autofly.delete_waypoint(oldname)
end
return true
end
local function log(level, message)
minetest.log(level, ('[%s] %s'):format(mod_name, message))
end
function autofly.dumptolog()
local wp=autofly.getwps()
for name, _ in pairs(wp) do
--local lname=name:sub(string.len(stprefix)+1)
-- local ppos=string_to_pos(storage:get_string(tostring(name)))
if ppos then
log('action',name .. ' :: ')
end
end
end
minetest.after("5.0",function()
if autofly.get_waypoint('AUTOTP') ~= nil then autofly.autotp(nil) end
end)
math.randomseed(os.time())
local randflying = false
minetest.register_globalstep(function()
if randflying and not autofly.flying then
local x = math.random(-31000, 31000)
local y = math.random(2000, 31000)
local z = math.random(-31000, 31000)
autofly.goto({x = x, y = y, z = z})
end
end)
local function randfly()
if not randflying then
randflying = true
local lp = minetest.localplayer:get_pos()
autofly.goto(turtle.coord(lp.x, 6000, lp.z))
else
randflying = false
autofly.arrived()
end
end
minetest.register_chatcommand('waypoints', {
params = '',
description = 'Open the autofly GUI',
func = function(param) autofly.display_formspec() end
})
ws.register_chatcommand_alias('waypoints','wp', 'wps', 'waypoint')
-- Add a waypoint
minetest.register_chatcommand('add_waypoint', {
params = '<pos / "here" / "there"> <name>',
description = 'Adds a waypoint.',
func = function(param)
local s, e = param:find(' ')
if not s or not e then
return false, 'Invalid syntax! See .help add_mrkr for more info.'
end
local pos = param:sub(1, s - 1)
local name = param:sub(e + 1)
if not pos then
return false, err
end
if not name or #name < 1 then
return false, 'Invalid name!'
end
return autofly.set_waypoint(pos, name), 'Done!'
end
})
ws.register_chatcommand_alias('add_waypoint','wa', 'add_wp')
minetest.register_chatcommand('add_waypoint_here', {
params = 'name',
description = 'marks the current position',
func = function(param)
local name = os.date("%Y-%m-%d %H:%M:%S")
local pos = minetest.localplayer:get_pos()
return autofly.set_waypoint(pos, name), 'Done!'
end
})
ws.register_chatcommand_alias('add_waypoint_here', 'wah', 'add_wph')
minetest.register_chatcommand('clear_waypoint', {
params = '',
description = 'Hides the displayed waypoint.',
func = function(param)
if autofly.flying then autofly.flying=false end
if hud_wp then
minetest.localplayer:hud_remove(hud_wp)
hud_wp = nil
return true, 'Hidden the currently displayed waypoint.'
elseif not minetest.localplayer.hud_add then
minetest.run_server_chatcommand('clrmrkr')
return
elseif not hud_wp then
return false, 'No waypoint is currently being displayed!'
end
for k,v in wps do
minetest.localplayer:hud_remove(v)
table.remove(k)
end
end,
})
ws.register_chatcommand_alias('clear_waypoint', 'cwp','cls')
minetest.register_chatcommand('autotp', {
params = 'position',
description = 'autotp',
func = function(param)
autofly.autotp(minetest.string_to_pos(param))
end
})
ws.register_chatcommand_alias('autotp', 'atp')
minetest.register_chatcommand('wpdisplay', {
params = 'position name',
description = 'display waypoint',
func = function(pos,name)
autofly.display(pos,name)
end
})
ws.register_chatcommand_alias('wpdisplay', 'wpd')
minetest.register_chatcommand("randfly", {
description = "Randomly fly up high (toggle).",
func = randfly
})
minetest.register_cheat("Aim", "Autofly", "afly_autoaim")
minetest.register_cheat("AxisSnap", "Autofly", "afly_snap")
minetest.register_cheat("Cruise", "Autofly", "afly_cruise")
minetest.register_cheat("Sprint", "Autofly", "afly_sprint")
minetest.register_cheat("ShowNames", "Autofly", "afly_shownames")
minetest.register_cheat("Waypoints", "Autofly", autofly.display_formspec)

View File

@ -0,0 +1,3 @@
name = autofly
author = cora
description = autonomous flight

View File

@ -0,0 +1,35 @@
local function get_3dpos_from_yaw_and_pitch(r,yaw,pitch)
local tg=vector.new(0,0,0)
tg.x= r * math.sin(yaw)
tg.y= r * math.sin(pitch)
tg.z= r * math.cos(yaw)
return tg
end
local nexttarget=vector.new(0,0,0)
local sdst=40
function autofly.pathfind(coords)
local lp=minetest.localplayer
autofly.aim(coords)
local yaw=lp:get_yaw()
local pitch=lp:get_pitch()
local ltgt=vector.add(lp:get_pos(),get_3dpos_from_yaw_and_pitch(sdst,yaw,pitch))
local tgt=vector.new(0,0,0)
if not minetest.line_of_sight(lp:get_pos(), ltgt) then
local path=minetest.find_path(lp:get_pos(),ltgt,sdst*2,100,100,'Dijkstra')
if not path then
minetest.display_chat_message("no path found.")
return
end
tgt=vector.add(path[1],vector.new(0,2,0))
if vector.distance(lp:get_pos(),tgt) < 6 then
tgt=vector.add(path[2],vector.new(0,2,0))
end
else
tgt=ltgt
end
autofly.aim(tgt)
autofly.goto(tgt)
end

View File

@ -0,0 +1,3 @@
afly_autoaim (Autoaim) bool false
afly_softlanding (Soft Landing) bool true
afly_sprint (Sprint while flying) bool true

View File

@ -0,0 +1,251 @@
-- ADVMARKERS Stuff
-- Get the waypoints formspec
local formspec_list = {}
local selected_name = false
local storage = minetest.get_mod_storage()
local wpr=false;
local twpname=nil
local info=minetest.get_server_info()
local stprefix="autofly-".. info['address'] .. '-'
autofly = {}
wps={}
autofly.registered_transports={}
local tspeed = 20 -- speed in blocks per second
local speed=0;
local ltime=0
function autofly.register_transport(name,func)
table.insert(autofly.registered_transports,{name=name,func=func})
end
function autofly.display_formspec()
local formspec = 'size[6.25,9]' ..
'label[0,0;Waypoint list]' ..
'button_exit[0,7.5;1,0.5;display;Show]' ..
'button[3.625,7.5;1.3,0.5;rename;Rename]' ..
'button[4.9375,7.5;1.3,0.5;delete;Delete]'
local sp=0
for k,v in pairs(autofly.registered_transports) do
formspec=formspec..'button_exit['..sp..',8.5;1,0.5;'..v.name..';'..v.name..']'
sp=sp+0.8
end
formspec=formspec..'textlist[0,0.75;6,6;marker;'
local selected = 1
formspec_list = {}
local waypoints = autofly.getwps()
for id, name in ipairs(waypoints) do
if id > 1 then
formspec = formspec .. ','
end
if not selected_name then
selected_name = name
end
if name == selected_name then
selected = id
end
formspec_list[#formspec_list + 1] = name
formspec = formspec .. '##' .. minetest.formspec_escape(name)
end
formspec = formspec .. ';' .. tostring(selected) .. ']'
if selected_name then
local pos = autofly.get_waypoint(selected_name)
if pos then
pos = minetest.formspec_escape(tostring(pos.x) .. ', ' ..
tostring(pos.y) .. ', ' .. tostring(pos.z))
pos = 'Waypoint position: ' .. pos
formspec = formspec .. 'label[0,6.75;' .. pos .. ']'
end
else
-- Draw over the buttons
formspec = formspec .. 'button_exit[0,7.5;5.25,0.5;quit;Close dialog]' ..
'label[0,6.75;No waypoints. Add one with ".wa".]'
end
-- Display the formspec
return minetest.show_formspec('autofly-csm', formspec)
end
minetest.register_on_formspec_input(function(formname, fields)
if formname == 'autofly-ignore' then
return true
elseif formname ~= 'autofly-csm' then
return
end
local name = false
if fields.marker then
local event = minetest.explode_textlist_event(fields.marker)
if event.index then
name = formspec_list[event.index]
end
else
name = selected_name
end
if name then
for k,v in pairs(autofly.registered_transports) do
if fields[v.name] then
if not v.func(autofly.get_waypoint(name),name) then
minetest.display_chat_message('Error with '..v.name)
end
end
end
if fields.display then
if not autofly.display_waypoint(name) then
minetest.display_chat_message('Error displaying waypoint!')
end
elseif fields.goto then
if not autofly.goto_waypoint(name) then
minetest.display_chat_message('Error flying to waypoint!')
end
elseif fields.warp then
if not autofly.warp(name) then
minetest.display_chat_message('warp error')
end
elseif fields.autotp then
if not autofly.autotp(name) then
minetest.display_chat_message('warpandexit error')
end
elseif fields.itp then
if incremental_tp then
incremental_tp.tp(autofly.get_waypoint(name),1)
end
elseif fields.jitp then
if incremental_tp then
incremental_tp.tp(autofly.get_waypoint(name),0.5,0.4)
end
elseif fields.rename then
minetest.show_formspec('autofly-csm', 'size[6,3]' ..
'label[0.35,0.2;Rename waypoint]' ..
'field[0.3,1.3;6,1;new_name;New name;' ..
minetest.formspec_escape(name) .. ']' ..
'button[0,2;3,1;cancel;Cancel]' ..
'button[3,2;3,1;rename_confirm;Rename]')
elseif fields.rename_confirm then
if fields.new_name and #fields.new_name > 0 then
if autofly.rename_waypoint(name, fields.new_name) then
selected_name = fields.new_name
else
minetest.display_chat_message('Error renaming waypoint!')
end
autofly.display_formspec()
else
minetest.display_chat_message(
'Please enter a new name for the marker.'
)
end
elseif fields.delete then
minetest.show_formspec('autofly-csm', 'size[6,2]' ..
'label[0.35,0.25;Are you sure you want to delete this waypoint?]' ..
'button[0,1;3,1;cancel;Cancel]' ..
'button[3,1;3,1;delete_confirm;Delete]')
elseif fields.delete_confirm then
autofly.delete_waypoint(name)
selected_name = false
autofly.display_formspec()
elseif fields.cancel then
autofly.display_formspec()
elseif name ~= selected_name then
selected_name = name
autofly.display_formspec()
end
elseif fields.display or fields.delete then
minetest.display_chat_message('Please select a waypoint.')
end
return true
end)
-- Export waypoints
function autofly.export(raw)
local s = storage:to_table().fields
if raw == 'M' then
s = minetest.compress(minetest.serialize(s))
s = 'M' .. minetest.encode_base64(s)
elseif not raw then
s = minetest.compress(minetest.write_json(s))
s = 'J' .. minetest.encode_base64(s)
end
return s
end
-- Allow string exporting
minetest.register_chatcommand('wpexp', {
params = '[old]',
description = 'Exports an autofly string containing all your pois.',
func = function(param)
local export
if param == 'old' then
export = autofly.export('M')
else
export = autofly.export()
end
minetest.show_formspec('autofly-ignore',
'field[_;Your waypoint export string;' ..
minetest.formspec_escape(export) .. ']')
end
})
--register_chatcommand_alias('wpexp', 'wp_export', 'waypoint_export')
-- String importing
minetest.register_chatcommand('wpimp', {
params = '<autofly string>',
description = 'Imports an autofly string. This will not overwrite ' ..
'existing pois that have the same name.',
func = function(param)
if autofly.import(param) then
return true, 'Waypoints imported!'
else
return false, 'Invalid autofly string!'
end
end
})
--register_chatcommand_alias('wpimp', 'wp_import', 'waypoint_import')
-- Import waypoints
function autofly.import(s)
if type(s) ~= 'table' then
local ver = s:sub(1, 1)
if ver ~= 'M' and ver ~= 'J' then return end
s = minetest.decode_base64(s:sub(2))
local success, msg = pcall(minetest.decompress, s)
if not success then return end
if ver == 'M' then
s = minetest.deserialize(msg, true)
else
s = minetest.parse_json(msg)
end
end
-- Iterate over waypoints to preserve existing ones and check for errors.
if type(s) == 'table' then
for name, pos in pairs(s) do
if type(name) == 'string' and type(pos) == 'string' and
name:sub(1, 7) == 'marker-' and minetest.string_to_pos(pos) and
storage:get_string(name) ~= pos then
-- Prevent collisions
local c = 0
while #storage:get_string(name) > 0 and c < 50 do
name = name .. '_'
c = c + 1
end
-- Sanity check
if c < 50 then
storage:set_string(name, pos)
end
end
end
return true
end
end

View File

@ -0,0 +1,187 @@
---
-- autominer
autominer = {}
local dmg=false
local digging=false
local radius=6
local nodes=nlist.get("autominer")
local function sleep(n) -- seconds
local t0 = os.clock()
while os.clock() - t0 <= n do end
end
local function shuffle(tbl)
for i = #tbl, 2, -1 do
local j = math.random(i)
tbl[i], tbl[j] = tbl[j], tbl[i]
end
return tbl
end
local function checklava(pos)
local n=minetest.find_node_near(pos, 2, {'mcl_core:lava_source','mcl_core:lava_flowing'}, true)
if n == nil then return false end
return true
end
local function checkgravel(pos)
local n=minetest.find_node_near(pos, 1, {'mcl_core:gravel','mcl_core:sand'}, true)
if n == nil then return false end
return true
end
-- shamelessly stolen from dragonfire autotool
local function check_tool(stack, node_groups, old_best_time)
local toolcaps = stack:get_tool_capabilities()
if not toolcaps then return end
local best_time = old_best_time
for group, groupdef in pairs(toolcaps.groupcaps) do
local level = node_groups[group]
if level then
local this_time = groupdef.times[level]
if this_time < best_time then
best_time = this_time
end
end
end
return best_time < old_best_time, best_time
end
local function amautotool(pos)
local player = minetest.localplayer
local inventory = minetest.get_inventory("current_player")
local node=minetest.get_node_or_nil(pos)
local node_groups = minetest.get_node_def(node.name).groups
local new_index = player:get_wield_index()
local is_better, best_time = false, math.huge
is_better, best_time = check_tool(player:get_wielded_item(), node_groups, best_time)
is_better, best_time = check_tool(inventory.hand[1], node_groups, best_time)
for index, stack in pairs(inventory.main) do
is_better, best_time = check_tool(stack, node_groups, best_time)
if is_better then
new_index = index
end
end
player:set_wield_index(new_index)
end
local function find_tnod()
local pos = minetest.localplayer:get_pos()
local rr = vector.add(pos,{x=0,y=0,z=radius})
local pos1 = vector.add(pos,{x=radius,y=radius,z=radius})
local pos2 = vector.add(pos,{x=-radius,y=-radius,z=-radius})
local rt=shuffle(minetest.find_nodes_in_area(pos1, pos2, shuffle(nodes), true))
for k,v in pairs(rt) do
for kk,vv in pairs(shuffle(v)) do
-- minetest.display_chat_message("Found nodes:" ..dump(rt))
if ( vv.y > -57 ) and not checkgravel(vv) and not checklava(vv) then
rr=vv
break
end
end
end
if (checkgravel(rr) or checklava(rr)) then return minetest.after(0.2,find_tnod) end
return rr
-- return rt
end
local function get_hnode()
local ppos=minetest.localplayer:get_pos()
local n=minetest.get_node_or_nil(vector.add(ppos,{x=0,y=1,z=0}))
return n
end
local function dighead()
if not minetest.localplayer then return end
local ppos=vector.add(minetest.localplayer:get_pos(),{x=0,y=1,z=0})
local n=get_hnode()
if n==nil or n['name'] == 'air' then return end
--amautotool(ppos)
minetest.localplayer:set_wield_index(1)
minetest.dig_node(ppos)
minetest.dig_node(vector.add(ppos,{x=0,y=1,z=0}))
digging=false
if (minetest.settings:get_bool('aminer_active')) then
local hp=minetest.localplayer:get_hp()
local hn=get_hnode()
if (hp > 17) then
minetest.after(0.2,autominer.aminer )
else
minetest.display_chat_message("taken too much damage. wait.")
local ppos=vector.add(minetest.localplayer:get_pos(),{x=0,y=1,z=0})
minetest.dig_node(ppos)
minetest.dig_node(vector.add(ppos,{x=0,y=1,z=0}))
minetest.after(1.0,function() minetest.dig_node(vector.add(ppos,{x=0,y=0,z=0})) end )
minetest.after(1.4,function() minetest.dig_node(vector.add(ppos,{x=0,y=1,z=0})) end )
minetest.after(1.8,function() minetest.dig_node(vector.add(ppos,{x=0,y=0,z=0})) end )
minetest.after(2.5,function() minetest.dig_node(vector.add(ppos,{x=0,y=1,z=0})) end )
-- minetest.settings:set_bool("aminer_active",false)
end
end
end
local function rwarp()
if not (minetest.settings:get_bool("aminer_active")) then return end
digging=true
local nod=find_tnod()
if not nod then
minetest.display_chat_message('lava detected. stop.')
return
end
minetest.localplayer:set_pos(vector.add(nod,{x=0.3,y=-1.2,z=0.3}))
dighead()
minetest.after(0.2, dighead)
end
local function amine()
minetest.after(1.0,rwarp)
end
function autominer.aminer()
if not digging then
digging=true
dmg=hpchange.get_status()
if dmg then
minetest.after(3.0,rwarp)
else
minetest.after(0.5,rwarp)
end
end
end
local lastch=0
minetest.register_globalstep(function()
if os.time() < lastch + 5 then return end
lastch=os.time()
if ( minetest.settings:get_bool('aminer_active') ) then
dmg=true
digging=false
autominer.aminer()
end
end)
minetest.register_chatcommand("aminer", {
description = "",
func = function()
dmg=true
digging=false
minetest.settings:set_bool("aminer_active",true)
autominer.aminer()
end,
})
minetest.register_chatcommand("amine", {
description = "",
func = amine
})
minetest.register_chatcommand("dhe", {
description = "",
func = dighead
})
minetest.register_on_damage_taken(function(hp)
dmg=true
end)
if (_G["minetest"]["register_cheat"] ~= nil) then
minetest.register_cheat("Autominer (!!! ALPHA!! this will lead to you dying!!!)", "Player", "aminer_active")
else
minetest.settings:set_bool('aminer_active',true)
end

View File

@ -0,0 +1,3 @@
name = autominer
author = cora
description = Mining bot

View File

@ -0,0 +1 @@
aminer_active (Enable Autominer) bool false

View File

@ -0,0 +1,7 @@
minetest.register_chatcommand('mtq', {
description = 'automt-quit',
func = function(param)
minetest.log("AUTOMT Actually Quit")
minetest.disconnect()
end
})

232
clientmods/bookbot/init.lua Normal file
View File

@ -0,0 +1,232 @@
-- CC0/Unlicense Emilia 2020
-- TODO: API
-- TODO: all count should be changed to support using a bookbot as a rolling writer
-- an alternate solution is to just have an inventory manager bot that intermittently swaps the inventory and activates bookbot
-- TODO: local formspec stuff
--[[
COMMANDS
write <book> <times> Writes the book named <book>, <times> times
randwrite <times> Writes randomized books
bookadd_form Opens a book formspec for adding a book to the library
bookadd_this <shortname> Adds the currently selected book to the library
bookdel <shortname> Removes a book from the library
bookpeek <book> Peeks at a book in the library
booklist Lists all books
--]]
bookbot = {}
local book_length_max = 4500
local write = 0
local books = {
example = {
title = "Autobook",
author = "Emilia",
text = "This is an automatically written book."
}
}
local book
local book_iterator
local read_form = [[
size[9,8]
label[0,0;N]
label[0,1;A]
textlist[0,2;9,5;;T]
]]
local edit_form = [[
size[9,8]
field[0,0;9,1;title;;N]
field[0,1;9,1;author;;A]
textarea[0,2;9,5;text;;T]
]]
local storage = minetest.get_mod_storage()
local function storage_save()
storage:set_string("books", minetest.write_json(books))
end
local function storage_load()
local sbooks = storage:get("books")
if sbooks then
books = minetest.parse_json(sbooks)
end
end
storage_load()
local function open_book()
if minetest.switch_to_item("mcl_books:writable_book") then
minetest.interact("place")
book = book_iterator()
else
write = 0
book = nil
book_iterator = nil
end
end
local function count_books()
local lpmain = minetest.get_inventory("current_player").main
local count = 0
for i, v in ipairs(lpmain) do
if v:get_name() == "mcl_books:writable_book" then
count = count + 1
end
end
return count
end
minetest.register_on_receiving_inventory_form(function(formname, formspec)
if formname == "mcl_books:writable_book" and write ~= 0 then
minetest.send_inventory_fields("mcl_books:writable_book", {
text = book.text,
sign = "true",
})
minetest.close_formspec("")
elseif formname == "mcl_books:signing" and write ~= 0 then
minetest.send_inventory_fields("mcl_books:signing", {
title = book.title,
sign = "true"
})
minetest.close_formspec("")
write = write - 1
if write > 0 then
-- this should take lag into consideration
minetest.after(0.5, open_book)
end
end
end)
local function timesparse(times)
local count = 1
if times then
if times == "all" then
count = count_books()
elseif type(times) == "number" or times:match("^[0-9]+$") then
count = tonumber(times)
end
end
return count
end
function bookbot.write_iterator(iterator, write_no)
book_iterator = iterator
write = timesparse(write_no)
open_book()
end
function bookbot.write_simple(book, write_no)
local book_iterator = function()
return book
end
bookbot.write_iterator(book_iterator, write_no)
end
function bookbot.write(bookname, write_no)
local book = "example"
if bookname and books[bookname] then
book = books[bookname]
end
bookbot.write_simple(book, write_no)
end
function bookbot.add(bookname, title, author, text)
books[bookname] = {
title = title,
author = author,
text = text
}
storage_save()
end
minetest.register_chatcommand("write", {
description = "Write a book.",
params = "<book> <?ntimes/all>",
func = function(params)
local p = string.split(params, " ")
if #p == 0 then
minetest.display_chat_message("Error: book short name required")
return
end
bookbot.write(p[1], p[2])
end
})
minetest.register_chatcommand("bookadd_this", {
description = "Add the currently wielded book to the library, <name> is for the name in the library.",
params = "<name>",
func = function(params)
if params == "" then
minetest.display_chat_message("Error: no short name given")
return
end
local wielded = minetest.localplayer:get_wielded_item()
if wielded:get_name() == "mcl_books:written_book" then
local meta = wielded:get_meta():to_table()
bookbot.add(params,
meta.fields.title,
meta.fields.author,
meta.fields.text)
end
end
})
minetest.register_chatcommand("booklist", {
description = "List all saved books.",
func = function()
local out = ""
local first = true
for k, v in pairs(books) do
if not first then
out = out .. ", "
end
out = out .. k
first = false
end
minetest.display_chat_message("Saved books:")
minetest.display_chat_message(out)
end
})
minetest.register_chatcommand("randwrite", {
description = "Write random books.",
params = "<?times/all>",
func = function(params)
local p = string.split(params, " ")
book_iterator = function()
local out = {}
for i = 1, book_length_max do
table.insert(out, string.char(math.random(0x20, 0xFF)))
end
return {
title = "Random book",
author = "The computer",
text = table.concat(out)
}
end
bookbot.write_iterator(book_iterator, p[1])
end
})

113
clientmods/cchat/init.lua Normal file
View File

@ -0,0 +1,113 @@
--
-- coras Chat hacks
-- * verify death messages
-- * log chat to stdout
cchat = {}
-- verify death
table.insert(minetest.registered_on_receiving_chat_message, 1, function(msg)
local d = msg:find('\1b@mcl_death_messages\1b') --mineclone specific
if d then
-- minetest.send_chat_message("real.") --uncomment to publish approval
minetest.display_chat_message("real.")
end
end)
-- chat logging
local mod_name = minetest.get_current_modname()
local function log(level, message)
minetest.log(level, ('[%s] %s'):format(mod_name, message))
end
log('action', 'Chatlog loading...')
local LOG_LEVEL = 'action'
local server_info = minetest.get_server_info()
local server_id = server_info.address .. ':' .. server_info.port
local my_name = ''
local register_on_send = minetest.register_on_sending_chat_message or minetest.register_on_sending_chat_messages
local register_on_receive = minetest.register_on_receiving_chat_message or minetest.register_on_receiving_chat_messages
local function safe(func)
-- wrap a function w/ logic to avoid crashing the game
local f = function(...)
local status, out = pcall(func, ...)
if status then
return out
else
log('warning', 'Error (func): ' .. out)
return nil
end
end
return f
end
local set_my_name_tries = 0
local function set_my_name()
if minetest.localplayer then
my_name = minetest.localplayer:get_name()
elseif set_my_name_tries < 20 then
set_my_name_tries = set_my_name_tries + 1
minetest.after(1, set_my_name)
else
my_name = ''
end
end
local function loglastlogs()
if not fren then return end
for k,v in pairs(fren.friends) do
if fren.on_server(fren.name_of(k)) then
log("LASTLOGLOG START")
--minetest.display_chat_message('Last login of friend ' .. fren.name_of(k))
log("Last login of friend "..fren.name_of(k))
minetest.send_chat_message("/last-login "..fren.name_of(k))
end
end
for k,v in pairs(fren.enemies) do
if fren.on_server(fren.name_of(k)) then
log("Last login of enemy "..fren.name_of(k))
--minetest.display_chat_message('Last login of friend ' .. fren.name_of(k))
minetest.send_chat_message("/last-login "..fren.name_of(k))
minetest.after("5.0",function() log("LASTLOGLOG END") end)
end
end
end
--minetest.after("5.0",function() loglastlogs() end)
if minetest.register_on_connect then
minetest.register_on_connect(set_my_name)
elseif minetest.register_on_mods_loaded then
minetest.register_on_mods_loaded(set_my_name)
else
minetest.after(1, set_my_name)
end
if register_on_send then
register_on_send(safe(function(message)
local msg = minetest.strip_colors(message)
if msg ~= '' then
log(LOG_LEVEL, ('%s@%s [sent] %s'):format(my_name, server_id, msg))
end
end))
end
if register_on_receive then
register_on_receive(safe(function(message)
local msg = minetest.strip_colors(message)
if msg ~= '' then
log(LOG_LEVEL, ('%s@%s %s'):format(my_name, server_id, msg))
end
end))
end

View File

@ -0,0 +1,3 @@
name = cchat
author = cora
description = cora's chat extensions, primarily log to stdout

View File

@ -0,0 +1,91 @@
if INIT == "client" then
core.register_chatcommand("say", {
description = "Send raw text",
func = function(text)
minetest.send_chat_message(text)
return true
end,
})
core.register_chatcommand("teleport", {
params = "<X>,<Y>,<Z>",
description = "Teleport to relative coordinates.",
func = function(param)
local success, pos = minetest.parse_relative_pos(param)
if success then
minetest.localplayer:set_pos(pos)
return true, "Teleporting to " .. minetest.pos_to_string(pos)
end
return false, pos
end,
})
core.register_chatcommand("wielded", {
description = "Print itemstring of wieleded item",
func = function()
return true, minetest.localplayer:get_wielded_item():get_name()
end
})
core.register_chatcommand("disconnect", {
description = "Exit to main menu",
func = function(param)
minetest.disconnect()
end,
})
core.register_chatcommand("players", {
description = "List online players",
func = function(param)
return true, "Online players: " .. table.concat(minetest.get_player_names(), ", ")
end
})
core.register_chatcommand("kill", {
description = "Kill yourself",
func = function()
minetest.send_damage(minetest.localplayer:get_hp())
end,
})
core.register_chatcommand("hop", {
description = "Hop",
func = function()
minetest.set_keypress("jump", true)
end,
})
core.register_chatcommand("set", {
params = "([-n] <name> <value>) | <name>",
description = "Set or read client configuration setting",
func = function(param)
local arg, setname, setvalue = string.match(param, "(-[n]) ([^ ]+) (.+)")
if arg and arg == "-n" and setname and setvalue then
minetest.settings:set(setname, setvalue)
return true, setname .. " = " .. setvalue
end
setname, setvalue = string.match(param, "([^ ]+) (.+)")
if setname and setvalue then
if not minetest.settings:get(setname) then
return false, "Failed. Use '.set -n <name> <value>' to create a new setting."
end
minetest.settings:set(setname, setvalue)
return true, setname .. " = " .. setvalue
end
setname = string.match(param, "([^ ]+)")
if setname then
setvalue = minetest.settings:get(setname)
if not setvalue then
setvalue = "<not set>"
end
return true, setname .. " = " .. setvalue
end
return false, "Invalid parameters (see .help set)."
end,
})
end

View File

@ -0,0 +1,3 @@
name = commands
author = Fleckenstein
description = Misc cheat commands

View File

@ -0,0 +1,48 @@
minetest.register_on_receiving_chat_message(function(message)
if message:sub(1, 1) == "#" and minetest.settings:get_bool("ignore_status_messages") ~= false then
return true
elseif message:find('\1b@mcl_death_messages\1b') and minetest.settings:get_bool("mark_deathmessages") ~= false then
minetest.display_chat_message(minetest.colorize("#F25819", "[Deathmessage] ") .. message)
return true
end
end)
function minetest.send_colorized(message)
local starts_with = message:sub(1, 1)
if starts_with == "/" or starts_with == "." then return end
local reverse = minetest.settings:get_bool("chat_reverse")
if reverse then
local msg = ""
for i = 1, #message do
msg = message:sub(i, i) .. msg
end
message = msg
end
local use_chat_color = minetest.settings:get_bool("use_chat_color")
local color = minetest.settings:get("chat_color")
if use_chat_color and color then
local msg
if color == "rainbow" then
msg = minetest.rainbow(message)
else
msg = minetest.colorize(color, message)
end
message = msg
end
minetest.send_chat_message(message)
return true
end
minetest.register_on_sending_chat_message(minetest.send_colorized)
minetest.register_cheat("IgnoreStatus", "Chat", "ignore_status_messages")
minetest.register_cheat("DeathMessages", "Chat", "mark_deathmessages")
minetest.register_cheat("ColoredChat", "Chat", "use_chat_color")
minetest.register_cheat("ReversedChat", "Chat", "chat_reverse")

View File

@ -0,0 +1,3 @@
name = chat
author = Fleckenstein
description = The chat modifications of Dragonfireclient

View File

@ -0,0 +1,3 @@
chat_color (Chat Color) string white
chat_reverse (Reverse Chat messages) bool false
ignore_status_messages (Ignore status messages from server) bool false

View File

@ -0,0 +1,193 @@
local placed_crystal
local switched_to_totem = 0
local used_sneak = true
local totem_move_action = InventoryAction("move")
totem_move_action:to("current_player", "main", 9)
local mobs_friends = {
'mobs_mc_bat.png',
'mobs_mc_cat_black.png',
'mobs_mc_cat_ocelot.png',
'mobs_mc_cat_red.png',
'mobs_mc_cat_siamese.png',
'mobs_mc_diamond_horse_armor.png',
'mobs_mc_donkey.png',
'mobs_mc_wolf_collar.png',
'mobs_mc_wolf.png',
'mobs_mc_wolf_tame.png',
'mobs_mc_villager_butcher.png',
'mobs_mc_villager_farmer.png',
'mobs_mc_villager_librarian.png',
'mobs_mc_villager.png',
'mobs_mc_villager_priest.png',
'mobs_mc_villager_smith.png',
'mobs_mc_iron_golem.png',
'mobs_mc_iron_horse_armor.png',
'mobs_mc_mooshroom.png',
'mobs_mc_mule.png',
'mobs_mc_pig.png',
'mobs_mc_pig_saddle.png',
'mobs_mc_polarbear.png',
'mobs_mc_rabbit_black.png',
'mobs_mc_rabbit_brown.png',
'mobs_mc_rabbit_caerbannog.png',
'mobs_mc_rabbit_gold.png',
'mobs_mc_rabbit_salt.png',
'mobs_mc_rabbit_toast.png',
'mobs_mc_rabbit_white.png',
'mobs_mc_rabbit_white_splotched.png',
'mobs_mc_sheep_fur.png',
'mobs_mc_sheep.png',
'mobs_mc_horse_armor_diamond.png',
'mobs_mc_horse_armor_gold.png',
'mobs_mc_horse_armor_iron.png',
'mobs_mc_horse_black.png',
'mobs_mc_horse_brown.png',
'mobs_mc_horse_chestnut.png',
'mobs_mc_horse_darkbrown.png',
'mobs_mc_horse_gray.png',
'mobs_mc_horse_creamy.png',
'mobs_mc_horse_markings_blackdots.png',
'mobs_mc_horse_markings_whitedots.png',
'mobs_mc_horse_markings_whitefield.png',
'mobs_mc_horse_markings_white.png',
'mobs_mc_horse_white.png',
'mobs_mc_snowman',
'mobs_mc_chicken.png',
'mobs_mc_enderman.png',
'mobs_mc_cow.png'
}
local mobs_bad = {
'mcl_totems_totem.png',
'mobs_mc_blaze.png',
'mobs_mc_cave_spider.png',
'mobs_mc_creeper.png',
'mobs_mc_dragon.png',
'mobs_mc_endergolem.png',
'mobs_mc_magmacube.png',
'mobs_mc_enderman_eyes.png',
'mobs_mc_endermite.png',
'mobs_mc_ghast.png',
'mobs_mc_gold_horse_armor.png',
'mobs_mc_guardian_elder.png',
'mobs_mc_guardian.png',
'mobs_mc_husk.png',
'mobs_mc_shulker_black.png',
'mobs_mc_shulker_blue.png',
'mobs_mc_shulker_brown.png',
'mobs_mc_shulker_cyan.png',
'mobs_mc_shulker_gray.png',
'mobs_mc_shulker_green.png',
'mobs_mc_shulker_light_blue.png',
'mobs_mc_shulker_lime.png',
'mobs_mc_shulker_magenta.png',
'mobs_mc_shulker_orange.png',
'mobs_mc_shulker_pink.png',
'mobs_mc_shulker_purple.png',
'mobs_mc_shulker_red.png',
'mobs_mc_shulker_silver.png',
'mobs_mc_shulker_white.png',
'mobs_mc_shulker_yellow.png',
'mobs_mc_silverfish.png',
'mobs_mc_skeleton.png',
'mobs_mc_slime.png',
'mobs_mc_spider_eyes.png',
'mobs_mc_spider.png',
'mobs_mc_squid.png',
'mobs_mc_stray.png',
'mobs_mc_stray_overlay.png',
'mobs_mc_vex.png',
'mobs_mc_vex_charging.png',
'mobs_mc_vindicator.png',
'mobs_mc_evoker.png',
'mobs_mc_illusionist.png',
'mobs_mc_witch.png',
'mobs_mc_wither.png',
'mobs_mc_wither_skeleton.png',
'mobs_mc_wolf_angry.png',
'mobs_mc_zombie_butcher.png',
'mobs_mc_zombie_farmer.png',
'mobs_mc_zombie_librarian.png',
'mobs_mc_zombie_priest.png',
'mobs_mc_zombie_smith.png',
'mobs_mc_zombie_villager.png',
'mobs_mc_zombie_pigman.png',
'mobs_mc_zombie.png',
'mobs_mc_horse_zombie.png'
}
--minetest.register_list_command("friend", "Configure Friend List (friends dont get attacked by Killaura or Forcefield)", "friendlist")
local nexthit=0
minetest.register_globalstep(function(dtime)
local player = minetest.localplayer
if not player then return end
local control = player:get_control()
local pointed = minetest.get_pointed_thing()
local item = player:get_wielded_item():get_name()
if minetest.settings:get_bool("killaura") or minetest.settings:get_bool("forcefield") and control.dig then
if nexthit > os.clock() then return end
nexthit=os.clock() + 0.01
for _, obj in pairs(minetest.get_objects_inside_radius(player:get_pos(), 5)) do
local do_attack = false
local txt=obj:get_item_textures()
if(obj:is_player() and fren.is_enemy(obj:get_name())) then do_attack=true end
for k,v in pairs(mobs_bad) do if txt:find(v) then do_attack=true end end
if do_attack then
local owx=core.localplayer:get_wield_index()
minetest.switch_to_item('mcl_tools:sword_diamond')
obj:punch()
core.localplayer:set_wield_index(owx)
end
end
elseif minetest.settings:get_bool("crystal_pvp") then
if placed_crystal then
if minetest.switch_to_item("mobs_mc:totem") then
switched_to_totem = 5
end
placed_crystal = false
elseif switched_to_totem > 0 then
if item ~= "mobs_mc:totem" then
switched_to_totem = 0
elseif pointed and pointed.type == "object" then
pointed.ref:punch()
switched_to_totem = 0
else
switched_to_totem = switched_to_totem
end
elseif control.place and item == "mcl_end:crystal" then
placed_crystal = true
elseif control.sneak then
if pointed and pointed.type == "node" and not used_sneak then
local pos = minetest.get_pointed_thing_position(pointed)
local node = minetest.get_node_or_nil(pos)
if node and (node.name == "mcl_core:obsidian" or node.name == "mcl_core:bedrock") then
minetest.switch_to_item("mcl_end:crystal")
minetest.place_node(pos)
placed_crystal = true
end
end
used_sneak = true
else
used_sneak = false
end
end
if minetest.settings:get_bool("autototem") then
local totem_stack = minetest.get_inventory("current_player").main[9]
if totem_stack and totem_stack:get_name() ~= "mobs_mc:totem" then
local totem_index = minetest.find_item("mobs_mc:totem")
if totem_index then
totem_move_action:from("current_player", "main", totem_index)
totem_move_action:apply()
player:set_wield_index(9)
end
end
end
end)
minetest.register_cheat("Killaura", "Combat", "killaura")
minetest.register_cheat("Forcefield", "Combat", "forcefield")
minetest.register_cheat("CrystalPvP", "Combat", "crystal_pvp")
minetest.register_cheat("AutoTotem", "Combat", "autototem")

View File

@ -0,0 +1,179 @@
local drop_action = InventoryAction("drop")
local strip_move_act = InventoryAction("move")
strip_move_act:to("current_player", "craft", 1)
local strip_craft_act = InventoryAction("craft")
strip_craft_act:craft("current_player")
local strip_move_back_act = InventoryAction("move")
strip_move_back_act:from("current_player", "craftresult", 1)
minetest.register_globalstep(function(dtime)
local player = minetest.localplayer
if not player then return end
local item = player:get_wielded_item()
local itemdef = minetest.get_item_def(item:get_name())
local wieldindex = player:get_wield_index()
-- AutoRefill
if minetest.settings:get_bool("autorefill") and itemdef then
local space = item:get_free_space()
local i = minetest.find_item(item:get_name(), wieldindex + 1)
if i and space > 0 then
local move_act = InventoryAction("move")
move_act:to("current_player", "main", wieldindex)
move_act:from("current_player", "main", i)
move_act:set_count(space)
move_act:apply()
end
end
-- AutoPlanks (Strip in DF)
if minetest.settings:get_bool("autoplanks") then
if itemdef and itemdef.groups.tree and player:get_control().place then
strip_move_act:from("current_player", "main", wieldindex)
strip_move_back_act:to("current_player", "main", wieldindex)
strip_move_act:apply()
strip_craft_act:apply()
strip_move_back_act:apply()
end
end
-- AutoEject
if minetest.settings:get_bool("autoeject") then
local list = (minetest.settings:get("eject_items") or ""):split(",")
local inventory = minetest.get_inventory("current_player")
for index, stack in pairs(inventory.main) do
if table.indexof(list, stack:get_name()) ~= -1 then
drop_action:from("current_player", "main", index)
drop_action:apply()
end
end
end
end)
minetest.register_list_command("eject", "Configure AutoEject", "eject_items")
-- AutoTool
local function check_tool(stack, node_groups, old_best_time)
local toolcaps = stack:get_tool_capabilities()
if not toolcaps then return end
local best_time = old_best_time
for group, groupdef in pairs(toolcaps.groupcaps) do
local level = node_groups[group]
if level then
local this_time = groupdef.times[level]
if this_time and this_time < best_time then
best_time = this_time
end
end
end
return best_time < old_best_time, best_time
end
local function find_best_tool(nodename, switch)
local player = minetest.localplayer
local inventory = minetest.get_inventory("current_player")
local node_groups = minetest.get_node_def(nodename).groups
local new_index = player:get_wield_index()
local is_better, best_time = false, math.huge
is_better, best_time = check_tool(player:get_wielded_item(), node_groups, best_time)
if inventory.hand then
is_better, best_time = check_tool(inventory.hand[1], node_groups, best_time)
end
for index, stack in ipairs(inventory.main) do
is_better, best_time = check_tool(stack, node_groups, best_time)
if is_better then
new_index = index
end
end
return new_index
end
function minetest.select_best_tool(nodename)
minetest.localplayer:set_wield_index(find_best_tool(nodename))
end
local new_index, old_index, pointed_pos
minetest.register_on_punchnode(function(pos, node)
if minetest.settings:get_bool("autotool") then
pointed_pos = pos
old_index = old_index or minetest.localplayer:get_wield_index()
new_index = find_best_tool(node.name)
end
end)
minetest.register_globalstep(function()
local player = minetest.localplayer
if not new_index then return end
if minetest.settings:get_bool("autotool") then
local pt = minetest.get_pointed_thing()
if pt and pt.type == "node" and vector.equals(minetest.get_pointed_thing_position(pt), pointed_pos) and player:get_control().dig then
player:set_wield_index(new_index)
return
end
end
player:set_wield_index(old_index)
new_index, old_index, pointed_pos = nil
end)
-- Enderchest
function get_itemslot_bg(x, y, w, h)
local out = ""
for i = 0, w - 1, 1 do
for j = 0, h - 1, 1 do
out = out .."image["..x+i..","..y+j..";1,1;mcl_formspec_itemslot.png]"
end
end
return out
end
local enderchest_formspec = "size[9,8.75]"..
"label[0,0;"..minetest.formspec_escape(minetest.colorize("#313131", "Ender Chest")).."]"..
"list[current_player;enderchest;0,0.5;9,3;]"..
get_itemslot_bg(0,0.5,9,3)..
"label[0,4.0;"..minetest.formspec_escape(minetest.colorize("#313131", "Inventory")).."]"..
"list[current_player;main;0,4.5;9,3;9]"..
get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
get_itemslot_bg(0,7.74,9,1)..
"listring[current_player;enderchest]"..
"listring[current_player;main]"
function minetest.open_enderchest()
minetest.show_formspec("inventory:enderchest", enderchest_formspec)
end
-- HandSlot
local hand_formspec = "size[9,8.75]"..
"label[0,0;"..minetest.formspec_escape(minetest.colorize("#313131", "Hand")).."]"..
"list[current_player;hand;0,0.5;1,1;]"..
get_itemslot_bg(0,0.5,1,1)..
"label[0,4.0;"..minetest.formspec_escape(minetest.colorize("#313131", "Inventory")).."]"..
"list[current_player;main;0,4.5;9,3;9]"..
get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
get_itemslot_bg(0,7.74,9,1)..
"listring[current_player;hand]"..
"listring[current_player;main]"
function minetest.open_handslot()
minetest.show_formspec("inventory:hand", hand_formspec)
end
minetest.register_cheat("AutoEject", "Inventory", "autoeject")
minetest.register_cheat("AutoTool", "Inventory", "autotool")
minetest.register_cheat("Hand", "Inventory", minetest.open_handslot)
minetest.register_cheat("Enderchest", "Inventory", minetest.open_enderchest)
minetest.register_cheat("AutoPlanks", "Inventory", "autoplanks")
minetest.register_cheat("AutoRefill", "Inventory", "autorefill")
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
dofile(modpath .. "/next_item.lua")
dofile(modpath .. "/invhack.lua")

View File

@ -0,0 +1,13 @@
minetest.register_chatcommand("invhack", {
func = function(player)
minetest.show_formspec(
"invhack:invhack",
""
.. "size[8,7.5]"
.. "list[player:" .. player .. ";main;0,3.5;8,4;]"
.. "list[player:" .. player .. ";craft;3,0;3,3;]"
.. "list[player:" .. player .. ";craftpreview;7,1;1,1;]"
)
end
})

View File

@ -0,0 +1,4 @@
name = inventory
author = Fleckenstein
description = The inventory cheats for Dragonfireclient
dependencies = list

View File

@ -0,0 +1,18 @@
local elapsed_time = 0
local tick_time = 0.05
minetest.register_globalstep(function(dtime)
elapsed_time = elapsed_time + dtime
if elapsed_time < tick_time then return end
local player = minetest.localplayer
if not player then return end
local item = player:get_wielded_item()
if item:get_count() == 0 and minetest.settings:get_bool("next_item") then
local index = player:get_wield_index()
player:set_wield_index(index + 1)
end
elapsed_time = 0
end)
minetest.register_cheat("NextItem", "Inventory", "next_item")

View File

@ -0,0 +1,4 @@
next_item (NextItem) bool false
autotool (AutoTool) bool false
autoeject (AutoEject) bool false
eject_items (AutoEject Items) string

View File

@ -0,0 +1,47 @@
list = {}
function list.new(desc, setting)
local def = {}
def.description = desc
def.params = "del <item> | add <item> | list"
function def.func(param)
local list = (minetest.settings:get(setting) or ""):split(",")
if param == "list" then
return true, table.concat(list, ", ")
else
local sparam = param:split(" ")
local cmd = sparam[1]
local item = sparam[2]
if cmd == "del" then
if not item then
return false, "Missing item."
end
local i = table.indexof(list, item)
if i == -1 then
return false, item .. " is not on the list."
else
table.remove(list, i)
minetest.settings:set(setting, table.concat(list, ","))
return true, "Removed " .. item .. " from the list."
end
elseif cmd == "add" then
if not item then
return false, "Missing item."
end
local i = table.indexof(list, item)
if i ~= -1 then
return false, item .. " is already on the list."
else
table.insert(list, item)
minetest.settings:set(setting, table.concat(list, ","))
return true, "Added " .. item .. " to the list."
end
end
end
return false, "Invalid usage. (See /help <command>)"
end
return def
end
minetest.register_chatcommand("xray", list.new("Configure X-Ray", "xray_nodes"))
--minetest.register_chatcommand("Configure Search Nodes", "search_nodes")

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,44 @@
local function register_keypress_cheat(cheat, keyname, condition)
local was_active = false
minetest.register_globalstep(function()
local is_active = minetest.settings:get_bool(cheat) and (not condition or condition())
if is_active then
minetest.set_keypress(keyname, true)
elseif was_active then
minetest.set_keypress(keyname, false)
end
was_active = is_active
end)
end
register_keypress_cheat("autosneak", "sneak", function()
return minetest.localplayer:is_touching_ground()
end)
register_keypress_cheat("autosprint", "special1")
local legit_override
local function get_override_factor(name)
if minetest.settings:get_bool("override_" .. name) then
return tonumber(minetest.settings:get("override_" .. name .. "_factor")) or 1
else
return 1.0
end
end
minetest.register_globalstep(function()
if not legit_override then return end
local override = table.copy(legit_override)
override.speed = override.speed * get_override_factor("speed")
override.jump = override.jump * get_override_factor("jump")
override.gravity = override.gravity * get_override_factor("gravity")
minetest.localplayer:set_physics_override(override)
end)
minetest.register_on_recieve_physics_override(function(override)
legit_override = override
return true
end)
minetest.register_cheat("AutoSneak", "Movement", "autosneak")
minetest.register_cheat("AutoSprint", "Movement", "autosprint")

View File

@ -0,0 +1,3 @@
name = autosneak
desciption = Adds the AutoSneak feature to dragonfire.
author = Fleckenstein

View File

@ -0,0 +1 @@
autosneak (AutoSneak) bool false

View File

@ -0,0 +1,109 @@
local positions, index, global_goal
local function roundvec(v, d)
return vector.divide(vector.round(vector.multiply(v, d)), d)
end
local function findpath(pos)
global_goal = pos
index = 2
positions = minetest.find_path(
minetest.localplayer:get_pos(),
pos,
tonumber(minetest.settings:get("goto_max_distance") or 25),
tonumber(minetest.settings:get("goto_max_jump") or 1),
tonumber(minetest.settings:get("goto_max_drop") or minetest.settings:get_bool("prevent_natural_damage") and 1000 or 5)
)
end
minetest.register_chatcommand("goto", {
description = "Go to a position (use pathfinding).",
param = "<pos>",
func = function(param)
if positions then
return false, "Goto is still active. Use .gotoabort to abort it."
end
local success, pos = minetest.parse_pos(param)
if not success then
return false, pos
end
findpath(pos)
end,
})
minetest.register_chatcommand("gotoabort", {
description = "Abort goto.",
param = "<pos>",
func = function(param)
if not positions then
return false, "Goto is currently not running (and also not walking haha)"
end
minetest.set_keypress("forward", false)
minetest.set_keypress("sneak", false)
positions, index, global_goal = nil
return true, "Aborted."
end,
})
minetest.register_globalstep(function(dtime)
if positions then
minetest.set_keypress("forward", true)
minetest.set_keypress("sneak", false)
local player = minetest.localplayer
local pos = player:get_pos()
local goal, next_goal = positions[index], positions[index+1]
if not goal then
positions, index, global_goal = nil
minetest.set_keypress("forward", false)
minetest.display_chat_message("Reached goal.")
return
end
if next_goal then
local d, dn = vector.subtract(pos, goal), vector.subtract(next_goal, goal)
for k, v in pairs(dn) do
if v ~= 0 and k ~= "y" then
local cv = d[k]
if v > 0 and cv > 0 or v < 0 and cv < 0 then
index = index + 1
goal = next_goal
end
break
end
end
end
local npos = vector.add(goal, {x = 0, y = 1, z = 0})
local node = minetest.get_node_or_nil(npos)
if node and node.name ~= air then
minetest.dig_node(npos)
end
local velocity = player:get_velocity()
velocity.y = 0
if vector.length(velocity) < 0.1 then
findpath(global_goal)
return
end
local distance = vector.distance(pos, goal)
if not next_goal and distance < 1 then
index = index + 1
end
local direction = vector.direction(pos, vector.new(goal.x, 0, goal.z))
local yaw = player:get_yaw() % 360
local goal_yaw = math.deg(math.atan2(-direction.x, direction.z)) % 360
local diff = math.abs(goal_yaw - yaw)
if diff > 175 and diff < 185 and distance < 1 then
index = index + 1
elseif diff > 10 and diff < 350 then
if yaw < goal_yaw and diff < 180 or yaw > goal_yaw and diff > 180 then
yaw = yaw + 10
elseif yaw < goal_yaw and diff > 180 or yaw > goal_yaw and diff < 180 then
yaw = yaw - 10
end
if diff >= 90 and diff <= 270 then
minetest.set_keypress("sneak", true)
end
player:set_yaw(yaw)
else
player:set_yaw(goal_yaw)
end
end
end)

View File

@ -0,0 +1,3 @@
name = pathfinding
description = Adds the .goto command!
author = Fleckenstein

View File

@ -0,0 +1,3 @@
goto_max_distance (Maximum distance from the search positions to search in) int 25
goto_max_jump (Jump height) int 1
goto_max_drop (Maximum drop height) int 5

View File

@ -0,0 +1,33 @@
perlin = dofile(minetest.get_modpath("perlin") .. "/perlin.lua")
local start, height, stretch
minetest.register_chatcommand("perlin", {
description = "Start perlin terraforming",
param = "<height> <stretch>",
func = function(param)
local sparam = param:split(" ")
start, height, stretch = math.floor(minetest.localplayer:get_pos().y), sparam[1], sparam[2]
end
})
minetest.register_chatcommand("perlinstop", {
description = "Abort perlin terraforming",
func = function(param)
start, height, stretch = nil
end
})
minetest.register_globalstep(function()
if start then
local player = minetest.localplayer
local pos = vector.floor(player:get_pos())
for x = pos.x - 1, pos.x + 1 do
for z = pos.z - 1, pos.z + 1 do
local y = math.floor(start + height * perlin:noise(x / stretch, z / stretch))
local p = vector.new(x, y, z)
minetest.place_node(p)
end
end
end
end)

View File

@ -0,0 +1,3 @@
name = perlin
description = A bot that does terraforming automatically using perlin noise.
author = Fleckenstein

View File

@ -0,0 +1,144 @@
--[[
Implemented as described here:
http://flafla2.github.io/2014/08/09/perlinnoise.html
]]--
local perlin = {}
perlin.p = {}
local bit32 = {}
function bit32.band(a, b)
local result = 0
local bitval = 1
while a > 0 and b > 0 do
if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits
result = result + bitval -- set the current bit
end
bitval = bitval * 2 -- shift left
a = math.floor(a/2) -- shift right
b = math.floor(b/2)
end
return result
end
-- Hash lookup table as defined by Ken Perlin
-- This is a randomly arranged array of all numbers from 0-255 inclusive
local permutation = {151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
}
-- p is used to hash unit cube coordinates to [0, 255]
for i=0,255 do
-- Convert to 0 based index table
perlin.p[i] = permutation[i+1]
-- Repeat the array to avoid buffer overflow in hash function
perlin.p[i+256] = permutation[i+1]
end
-- Return range: [-1, 1]
function perlin:noise(x, y, z)
y = y or 0
z = z or 0
-- Calculate the "unit cube" that the point asked will be located in
local xi = bit32.band(math.floor(x),255)
local yi = bit32.band(math.floor(y),255)
local zi = bit32.band(math.floor(z),255)
-- Next we calculate the location (from 0 to 1) in that cube
x = x - math.floor(x)
y = y - math.floor(y)
z = z - math.floor(z)
-- We also fade the location to smooth the result
local u = self.fade(x)
local v = self.fade(y)
local w = self.fade(z)
-- Hash all 8 unit cube coordinates surrounding input coordinate
local p = self.p
local A, AA, AB, AAA, ABA, AAB, ABB, B, BA, BB, BAA, BBA, BAB, BBB
A = p[xi ] + yi
AA = p[A ] + zi
AB = p[A+1 ] + zi
AAA = p[ AA ]
ABA = p[ AB ]
AAB = p[ AA+1 ]
ABB = p[ AB+1 ]
B = p[xi+1] + yi
BA = p[B ] + zi
BB = p[B+1 ] + zi
BAA = p[ BA ]
BBA = p[ BB ]
BAB = p[ BA+1 ]
BBB = p[ BB+1 ]
-- Take the weighted average between all 8 unit cube coordinates
return self.lerp(w,
self.lerp(v,
self.lerp(u,
self:grad(AAA,x,y,z),
self:grad(BAA,x-1,y,z)
),
self.lerp(u,
self:grad(ABA,x,y-1,z),
self:grad(BBA,x-1,y-1,z)
)
),
self.lerp(v,
self.lerp(u,
self:grad(AAB,x,y,z-1), self:grad(BAB,x-1,y,z-1)
),
self.lerp(u,
self:grad(ABB,x,y-1,z-1), self:grad(BBB,x-1,y-1,z-1)
)
)
)
end
-- Gradient function finds dot product between pseudorandom gradient vector
-- and the vector from input coordinate to a unit cube vertex
perlin.dot_product = {
[0x0]=function(x,y,z) return x + y end,
[0x1]=function(x,y,z) return -x + y end,
[0x2]=function(x,y,z) return x - y end,
[0x3]=function(x,y,z) return -x - y end,
[0x4]=function(x,y,z) return x + z end,
[0x5]=function(x,y,z) return -x + z end,
[0x6]=function(x,y,z) return x - z end,
[0x7]=function(x,y,z) return -x - z end,
[0x8]=function(x,y,z) return y + z end,
[0x9]=function(x,y,z) return -y + z end,
[0xA]=function(x,y,z) return y - z end,
[0xB]=function(x,y,z) return -y - z end,
[0xC]=function(x,y,z) return y + x end,
[0xD]=function(x,y,z) return -y + z end,
[0xE]=function(x,y,z) return y - x end,
[0xF]=function(x,y,z) return -y - z end
}
function perlin:grad(hash, x, y, z)
return self.dot_product[bit32.band(hash,0xF)](x,y,z)
end
-- Fade function is used to smooth final output
function perlin.fade(t)
return t * t * t * (t * (t * 6 - 15) + 10)
end
function perlin.lerp(t, a, b)
return a + t * (b - a)
end
return perlin

View File

@ -0,0 +1,16 @@
core.register_list_command("xray", "Configure X-Ray", "xray_nodes")
core.register_list_command("search", "Configure NodeESP", "node_esp_nodes")
core.register_on_spawn_particle(function(particle)
if core.settings:get_bool("noweather") and particle.texture:sub(1, 12) == "weather_pack" then
return true
end
end)
core.register_on_play_sound(function(sound)
if core.settings:get_bool("noweather") and sound.name == "weather_rain" then
return true
end
end)
minetest.register_cheat("NoWeather", "Render", "noweather")

View File

@ -0,0 +1,48 @@
local warp = warp or {set_here = function() return false end}
local formspec = ""
.. "size[11,5.5]"
.. "bgcolor[#320000b4;true]"
.. "label[4.85,1.35;" .. "You died" .. "]"
.. "button_exit[2,3;3,0.5;btn_respawn;" .. "Respawn" .. "]"
.. "button_exit[6,3;3,0.5;btn_ghost_mode;" .. "Ghost Mode" .. "]"
.. "set_focus[btn_respawn;true]"
minetest.register_on_death(function()
if minetest.settings:get_bool("deathwarp") then
local warp_success, warp_msg = warp.set_here("death")
if warp_success then
minetest.display_chat_message(warp_msg)
end
end
if minetest.settings:get_bool("autorespawn") then
minetest.send_respawn()
else
minetest.show_formspec("respawn:death", formspec)
end
end)
minetest.register_on_formspec_input(function(formname, fields)
if formname == "respawn:death" then
if fields.btn_ghost_mode then
minetest.display_chat_message("You are in ghost mode. Use .respawn to Respawn.")
else
minetest.send_respawn()
end
end
end)
minetest.register_chatcommand("respawn", {
description = "Respawn when in ghost mode",
func = function()
if minetest.localplayer:get_hp() == 0 then
minetest.send_respawn()
minetest.display_chat_message("Respawned.")
else
minetest.display_chat_message("You are not in ghost mode.")
end
end
})
minetest.register_cheat("AutoRespawn", "Player", "autorespawn")
minetest.register_cheat("DeathWarp", "Player", "deathwarp")

View File

@ -0,0 +1,4 @@
name = respawn
author = Fleckenstein
description = Extended respawn behaviour
optional_depends = warp

View File

@ -0,0 +1 @@
autorespawn (AutoRespawn) bool false

View File

@ -0,0 +1,204 @@
local autoeat = rawget(_G, "autoeat") or {}
local storage = minetest.get_mod_storage()
local pos1, pos2
local min, max = math.min, math.max
local building, build_index, build_data, build_pos, just_placed_node, failed_count, out_of_blocks
minetest.register_chatcommand("pos1", {
description = "Set schematicas position 1 at your current location",
func = function()
pos1 = vector.round(minetest.localplayer:get_pos())
return true, "Position 1 set to " .. minetest.pos_to_string(pos1)
end
})
minetest.register_chatcommand("pos2", {
description = "Set schematicas position 2 at your current location",
func = function()
pos2 = vector.round(minetest.localplayer:get_pos())
return true, "Position 2 set to " .. minetest.pos_to_string(pos2)
end
})
minetest.register_chatcommand("schemesave", {
description = "Save a schematica",
param = "<name>",
func = function(name)
if not pos1 or not pos2 then
return false, "Position 1 or 2 not set."
end
local data = {}
local lx, ly, lz, hx, hy, hz = min(pos1.x, pos2.x), min(pos1.y, pos2.y), min(pos1.z, pos2.z), max(pos1.x, pos2.x), max(pos1.y, pos2.y), max(pos1.z, pos2.z)
for x = lx, hx do
local rx = x - lx
for y = ly, hy do
local ry = y - ly
for z = lz, hz do
local rz = z - lz
local node = minetest.get_node_or_nil({x = x, y = y, z = z})
if node and node.name ~= "air" then
table.insert(data, {pos = {x = rx, y = ry, z = rz}, node = node.name})
end
end
end
end
storage:set_string(name, minetest.serialize(data))
return true, "Scheme saved successfully as '" .. name .. "'."
end
})
minetest.register_chatcommand("schemebuild", {
description = "Build a schematica",
param = "<name>",
func = function(name)
if not pos1 then
return false, "Position 1 not set."
end
if building then
return false, "Still building a scheme. Use .schemeabort to stop it."
end
local rawdata = storage:get(name)
if not rawdata then
return false, "Schematica '" .. name .. "' not found."
end
building, build_index, build_data, build_pos, just_placed_node, failed_count, out_of_blocks = true, 1, minetest.deserialize(rawdata), vector.new(pos1), false, 0, false
end
})
minetest.register_chatcommand("schemerecipe", {
description = "Print the recipe for a schematica",
param = "<name>",
func = function(name)
local rawdata = storage:get(name)
if not rawdata then
return false, "Schematica '" .. name .. "' not found."
end
local data = minetest.deserialize(rawdata)
local sorted = {}
for _, d in ipairs(data) do
end
end
})
minetest.register_chatcommand("schemeresume", {
description = "Resume constructing a schematica",
func = function()
if not build_data then
return false, "Currently not building a scheme."
end
building, out_of_blocks = true, false
return true, "Resumed."
end
})
minetest.register_chatcommand("schemepause", {
description = "Pause constructing a schematica",
func = function()
if not build_data then
return false, "Currently not building a scheme."
end
building = false
return true, "Paused."
end
})
minetest.register_chatcommand("schemeabort", {
description = "Abort constructing a schematica",
param = "<name>",
func = function()
if not build_data then
return false, "Currently not building a scheme."
end
building, build_index, build_data, build_pos, just_placed_node, failed_count, out_of_blocks = nilw
return true, "Aborted."
end
})
minetest.register_chatcommand("schemeskip", {
description = "Skip a step in constructing a schematica",
param = "<name>",
func = function()
if not build_data then
return false, "Currently not building a scheme."
end
building, build_index = true, build_index + 1
return true, "Skipped."
end
})
minetest.register_chatcommand("schemegetindex", {
description = "Output the build index of the schematica",
func = function()
return build_index and true or false, build_index
end
})
minetest.register_chatcommand("schemesetindex", {
description = "Set the build index of the schematica",
param = "<index>",
func = function(param)
local index = tonumber(param)
if not index then return false, "Invalid usage." end
build_index = index
return true, "Index Changed"
end
})
minetest.register_globalstep(function()
if building and not autoeat.eating then
local data = build_data[build_index]
if not data then
building, build_index, build_data, build_pos, just_placed_node, failed_count, out_of_blocks = nil
minetest.display_chat_message("Completed Schematica.")
return
end
local pos, node = vector.add(build_pos, data.pos), data.node
if just_placed_node then
local map_node = minetest.get_node_or_nil(pos)
if map_node and map_node.name == node then
build_index = build_index + 1
just_placed_node = false
else
failed_count = failed_count + 1
end
if failed_count < 10 then
return
end
end
failed_count = 0
local new_index
local inventory = minetest.get_inventory("current_player").main
for index, stack in ipairs(inventory) do
if minetest.get_item_def(stack:get_name()).node_placement_prediction == node then
new_index = index - 1
break
end
end
if not new_index then
if not out_of_blocks then
minetest.display_chat_message("Out of blocks for schematica. Missing ressource: '" .. node .. "'. It will resume as soon as you got it or use .schemeskip to skip it.")
minetest.send_chat_message("[Schematicas] Missing ressource: " .. node)
end
out_of_blocks = true
return
end
if out_of_blocks then
minetest.send_chat_message("[Schematicas] Resuming.")
end
out_of_blocks = false
minetest.localplayer:set_wield_index(new_index)
minetest.localplayer:set_pos(minetest.find_node_near(pos, 5, {"air", "ignore", "mcl_core:water_source", "mcl_core:water_flowing"}, false) or pos)
minetest.place_node(pos)
just_placed_node = true
if build_index % 250 == 0 then
minetest.send_chat_message("[Schematicas] " .. build_index .. " of " .. #build_data .. " blocks placed!")
end
end
end)

View File

@ -0,0 +1,4 @@
name = schematicas
description = Save structures and recreate them automatically in survival.
author = Fleckenstein
optional_depends = autoeat

View File

@ -0,0 +1,94 @@
warp = {}
local storage = minetest.get_mod_storage()
function warp.set(warp, pos)
if warp == "" or not pos then return false, "Missing parameter." end
local posstr = minetest.pos_to_string(pos)
storage:set_string(warp, posstr)
return true, "Warp " .. warp .. " set to " .. posstr .. "."
end
function warp.set_here(param)
local success, message = warp.set(param, vector.round(minetest.localplayer:get_pos()))
return success, message
end
function warp.get(param)
if param == "" then return false, "Missing parameter." end
local pos = storage:get_string(param)
if pos == "" then return false, "Warp " .. param .. " not set." end
return true, "Warp " .. param .. " is set to " .. pos .. ".", minetest.string_to_pos(pos)
end
function warp.delete(param)
if param == "" then return false, "Missing parameter." end
storage:set_string(param, "")
return true, "Deleted warp " .. param .. "."
end
minetest.register_chatcommand("setwarp", {
params = "<warp>",
description = "Set a warp to your current position.",
func = warp.set_here,
})
minetest.register_chatcommand("readwarp", {
params = "<warp>",
description = "Print the coordinates of a warp.",
func = warp.get,
})
minetest.register_chatcommand("deletewarp", {
params = "<warp>",
description = "Delete a warp.",
func = warp.delete,
})
minetest.register_chatcommand("listwarps", {
description = "List all warps.",
func = function()
local warps = storage:to_table().fields
local warplist = {}
for warp in pairs(warps) do
table.insert(warplist, warp)
end
if #warplist > 0 then
return true, table.concat(warplist, ", ")
else
return false, "No warps set."
end
end,
})
local function do_warp(param)
if param == "" then return false, "Missing parameter." end
local success, pos = minetest.parse_pos(param)
if not success then
local msg
success, msg, pos = warp.get(param)
if not success then
return false, msg
end
end
minetest.localplayer:set_pos(pos)
return true, "Warped to " .. minetest.pos_to_string(pos)
end
minetest.register_chatcommand("warp", {
params = "<pos>|<warp>",
description = "Warp to a set warp or a position.",
func = do_warp
})
minetest.register_chatcommand("warpandexit", {
params = "<pos>|<warp>",
description = "Warp to a set warp or a position and exit.",
func = function(param)
local s, m = do_warp(param)
if s then
minetest.disconnect()
end
return s,m
end
})

View File

@ -0,0 +1,3 @@
name = warp
author = Fleckenstein
description = Set custom warps and use the teleport exploit

View File

@ -0,0 +1,48 @@
minetest.register_chatcommand("findnodes", {
description = "Scan for one or multible nodes in a radius around you",
param = "<radius> <node1>[,<node2>...]",
func = function(param)
local radius = tonumber(param:split(" ")[1])
local nodes = param:split(" ")[2]:split(",")
local pos = minetest.localplayer:get_pos()
local fpos = minetest.find_node_near(pos, radius, nodes, true)
if fpos then
return true, "Found " .. table.concat(nodes, " or ") .. " at " .. minetest.pos_to_string(fpos)
end
return false, "None of " .. table.concat(nodes, " or ") .. " found in a radius of " .. tostring(radius)
end,
})
minetest.register_chatcommand("place", {
params = "<X>,<Y>,<Z>",
description = "Place wielded item",
func = function(param)
local success, pos = minetest.parse_relative_pos(param)
if success then
minetest.place_node(pos)
return true, "Node placed at " .. minetest.pos_to_string(pos)
end
return false, pos
end,
})
minetest.register_chatcommand("dig", {
params = "<X>,<Y>,<Z>",
description = "Dig node",
func = function(param)
local success, pos = minetest.parse_relative_pos(param)
if success then
minetest.dig_node(pos)
return true, "Node at " .. minetest.pos_to_string(pos) .. " dug"
end
return false, pos
end,
})
minetest.register_on_dignode(function(pos)
if minetest.settings:get_bool("replace") then
minetest.after(0, minetest.place_node, pos)
end
end)
minetest.register_cheat("Replace", "World", "replace")

View File

@ -0,0 +1,3 @@
name = world
desciption = Adds several world interaction bots to dragonfire.
author = Fleckenstein

View File

@ -0,0 +1,6 @@
scaffold (Scaffold) bool false
highway_z (HighwayZ) bool false
block_water (BlockWater) bool false
autotnt (AutoTNT) bool false
replace (Replace) bool false
nodes_per_tick (Number of nodes to place per tick) int 8

21
clientmods/dte/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Benjamin Fleming
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.

59
clientmods/dte/README.md Normal file
View File

@ -0,0 +1,59 @@
# CSM DTE
## Client Side Mod Designing & Testing Environment
An advanced, ingame lua and formspec editor for minetest.
This can be used for creating, testing and using CSMs without reloading the game, and without the game crashing.
This is also the easiest way to create advanced formspecs.
It was tested with multiple CSMs from the minetest forums, and they all worked. (actually in the current version, a few things might not work, but they will be fixed in the future)
CSMs can be created, or pasted in, and they should work the same as they would normaly, except errors won't crash the game!
functions that are registered with minetest can be put in a function `safe(func)` to output errors to the UI when minetest calls them
scripts can be put in startup to run them automatically when the game loads. errors are also put in the UI
screenshots:
![lua editor](preview_1.png)
![formspec editor](preview_2.png)
## FEATURES:
lua editor:
- print function
- coloured error output
- multiple files
- file creation and deletion
- safe function execution
- automatically run files at startup
formspec editor:
- every widget is available
- widgets are easy to edit
- formspec preview, shows what it will look like
- export as a function with parameters
- export as a string
- and a whole bunch of fancy stuff
## To Use:
- use the command `.dte` to open the editor
- select the `lua editor` tab to run and edit CSMs
- select the `formspec editor` tab to create a formspec
- select the `files` tab to open, create, and delete files
- select the `startup` tab to select lua files to run when the game loads
## How to install
- make sure you have client modding enabled (search for `client modding` in advanced settings, and set it to true)
- download and extract the zip file into `clientmods\csm_dte` or paste the `init.lua` file into it.
- add `load_mod_csm_dte = true` to the `clientmods\mods.conf` file
- join a game or server, and it should work!
## Editing the files
### - if you do not wan't to edit the program, the `development` folder can be deleted!
when editing the program, it is easier to edit the smaller files found inside the `development` folder.
these can be run as seperate CSMs for testing (I recomend disabling `csm_dte`)
to join the together, copy the lua editor into `csm_dte/init.lua` and copy the formspec editor (from EDITOR START to EDITOR END) into the "PASTE FORMSPEC EDITOR HERE" section
### TODO:
- make all functions work after the game has been loaded
- add a UI to unregister functions which were registered from a program
- add a lua console
- import ui strings into the editor (?)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,492 @@
local data = { -- window size
width = 15,
height = 10,
}
local form_esc = minetest.formspec_escape -- shorten the function
local modstorage = core.get_mod_storage()
local function create_tabs(selected)
return "tabheader[0,0;_option_tabs_;" ..
" LUA EDITOR ,FORMSPEC EDITOR, LUA CONSOLE , FILES , STARTUP , FUNCTIONS , HELP ;"..selected..";;]"
end
local function copy_table(table)
local new = {}
for i, v in pairs(table) do
if type(v) == "table" then
v = copy_table(v)
end
new[i] = v
end
return new
end
----------
-- LOAD AND DEFINE STUFF - global stuff is accissible from the UI
----------
local split = function (str, splitter) -- a function to split a string into a list. "\" before the splitter makes it ignore it (usefull for minetests formspecs)
local result = {""}
for i=1, str:len() do
char = string.sub(str, i, i)
if char == splitter and string.sub(str, i-1, i-1) ~= "\\" then
table.insert(result, "")
else
result[#result] = result[#result]..char
end
end
return result
end
local output = {} -- the output for errors, prints, etc
local saved_file = modstorage:get_string("_lua_saved") -- remember what file is currently being edited
if saved_file == "" then
saved_file = false -- if the file had no save name (it was still saved)
end
local lua_startup = split(modstorage:get_string("_lua_startup"), ",") -- the list of scripts to run at startup
local lua_files = split(modstorage:get_string("_lua_files_list"), ",") -- the list of names of all saved files
local ui_files = split(modstorage:get_string("_UI_files_list"), ",") -- UI files list
local reg_funcs = {formspec_input={}, chatcommands={}, on_connect={}, joinplayer={}, sending_chat_message={}, recieving_chat_message={}}
local selected_files = {0, 0}
minetest.register_on_connect(function() -- some functions don't work after startup. this tries to replace them
minetest.get_mod_storage = function()
return modstorage
end
core.get_mod_storage = function()
return modstorage
end
-- show formspec
end) -- add whatever functions don't work after startup to here (if possible)
----------
-- FUNCTIONS FOR UI
----------
function print(...) -- replace print to output into the UI. (doesn't refresh untill the script has ended)
params = {...}
if #params == 1 then
local str = params[1]
if type(str) ~= "string" then
str = dump(str)
end
table.insert(output, "")
for i=1, str:len() do
char = string.sub(str, i, i)
if char == "\n" then
table.insert(output, "") -- split multiple lines over multiple lines. without this, text with line breaks would not display properly
else
output[#output] = output[#output]..char
end
end
else
for i, v in pairs(params) do
print(v)
end
end
end
function safe(func) -- run a function without crashing the game. All errors are displayed in the UI.
f = function(...) -- This can be used for functions being registered with minetest, like "minetest.register_chat_command()"
status, out = pcall(func, ...)
if status then
return out
else
table.insert(output, "#ff0000Error: "..out)
minetest.debug("Error (func): "..out)
return nil
end
end
return f
end
----------
-- CODE EXECUTION
----------
local function run(code, name) -- run a script
if name == nil then
name = saved_file
end
status, err = pcall(loadstring(code)) -- run
if status then
if saved_file == false then
table.insert(output, "#00ff00finished") -- display that the script ran without errors
else
table.insert(output, "#00ff00"..name..": finished") -- display which script, if it was saved
end
else
if err == "attempt to call a nil value" then
err = "Syntax Error"
end
if saved_file == false then
table.insert(output, "#ff0000Error: "..err) -- display errors
minetest.log("Error (unsaved): "..err)
else
table.insert(output, "#ff0000"..name..": Error: "..err)
minetest.log("Error ("..name.."): "..err)
end
end
end
local function on_startup() -- ran on startup. Runs all scripts registered for startup
for i, v in pairs(lua_startup) do
if v ~= "" then
run(modstorage:get_string("_lua_file_"..v, v), v) -- errors still get displayed in the UI
end
end
end
on_startup()
----------
-- FILE READING AND SAVING
----------
local function load_lua() -- returns the contents of the file currently being edited
if saved_file == false then
return modstorage:get_string("_lua_temp") -- unsaved files are remembered (get saved on UI reloads - when clicking on buttons)
else
return modstorage:get_string("_lua_file_"..saved_file)
end
end
local function save_lua(code) -- save a file
if saved_file == false then
modstorage:set_string("_lua_temp", code)
else
modstorage:set_string("_lua_file_"..saved_file, code)
end
end
----------
-- FORM DEFINITIONS
----------
local function startup_form() -- the formspec for adding or removing files for startup
local startup_str = ""
for i, v in pairs(lua_startup) do
if i ~= 1 then startup_str = startup_str.."," end
startup_str = startup_str .. form_esc(v)
end
local files_str = ""
for i, v in pairs(lua_files) do
if i ~= 1 then files_str = files_str.."," end
files_str = files_str .. form_esc(v)
end
local form = ""..
"size["..data.width..","..data.height.."]" ..
"label[0,0.1;Startup Items:]"..
"label["..data.width/2 ..",0.1;File List:]"..
"textlist[0,0.5;"..data.width/2-0.1 ..","..data.height-1 ..";starts;"..startup_str.."]"..
"textlist["..data.width/2 ..",0.5;"..data.width/2-0.1 ..","..data.height-1 ..";chooser;"..files_str.."]"..
"label[0," .. data.height-0.3 .. ";double click items to add or remove from startup]"..
"" .. create_tabs(5)
return form
end
local function lua_editor() -- the main formspec for editing
local output_str = "" -- convert the output to a string
for i, v in pairs(output) do
if output_str:len() > 0 then output_str = output_str .. "," end
output_str = output_str .. form_esc(v)
end
local code = form_esc(load_lua())
-- create the form
local form = ""..
"size["..data.width..","..data.height.."]" ..
"textarea[0.3,0.1;"..data.width ..","..data.height-3 ..";editor;Lua editor;"..code.."]"..
"button[0," .. data.height-3.5 .. ";1,0;run;RUN]"..
"button[1," .. data.height-3.5 .. ";1,0;clear;CLEAR]"..
"button[2," .. data.height-3.5 .. ";1,0;save;SAVE]"..
"textlist[0,"..data.height-3 ..";"..data.width-0.2 ..","..data.height-7 ..";output;"..output_str..";".. #output .."]"..
"" .. create_tabs(1)
return form
end
local function file_viewer() -- created with the formspec editor!
local lua_files_item_str = ""
for i, item in pairs(lua_files) do
if i ~= 1 then lua_files_item_str = lua_files_item_str.."," end
lua_files_item_str = lua_files_item_str .. form_esc(item)
end
local ui_select_item_str = ""
for i, item in pairs(ui_files) do
if i ~= 1 then ui_select_item_str = ui_select_item_str.."," end
ui_select_item_str = ui_select_item_str .. form_esc(item)
end
local form = "" ..
"size["..data.width..","..data.height.."]" ..
"textlist[-0.2,0.2;"..data.width/2.02- -0.2 ..","..data.height- 1 ..";lua_select;"..lua_files_item_str.."]" ..
"label[-0.2,-0.2;LUA FILES]" ..
"field[0.1,"..data.height- 0.2 ..";3,1;new_lua;NEW;]" ..
"field_close_on_enter[new_lua;false]" ..
"button[2.6,"..data.height- 0.5 ..";0.5,1;add_lua;+]" ..
"textlist["..data.width/1.97 ..",0.2;"..data.width- 0-(data.width/1.97) ..","..data.height- 1 ..";ui_select;"..ui_select_item_str.."]" ..
"label["..data.width/1.96 ..",-0.2;FORMSPEC FILES]" ..
"field["..data.width- 2.8 ..","..data.height- 0.2 ..";3,1;new_ui;NEW;]" ..
"field_close_on_enter[new_ui;false]" ..
"button["..data.width- 0.3 ..","..data.height- 0.5 ..";0.5,1;add_ui;+]" ..
"label["..data.width/2.4 ..","..data.height- 0.8 ..";Double click a file to open it]" ..
"button[3.1,"..data.height- 0.5 ..";1.1,1;del_lua;DELETE]" ..
"button["..data.width- 4.2 ..","..data.height- 0.5 ..";1.1,1;del_ui;DELETE]" ..
"" .. create_tabs(4)
return form
end
----------
-- FUNCTIONALITY
----------
minetest.register_on_formspec_input(function(formname, fields)
-- EDITING PAGE
----------
if formname == "lua:editor" then
if fields.run then --[RUN] button
save_lua(fields.editor)
run(fields.editor)
minetest.show_formspec("lua:editor", lua_editor())
elseif fields.save then --[SAVE] button
if saved_file == false then
modstorage:set_string("_lua_temp", fields.editor)
else
modstorage:set_string("_lua_file_"..saved_file, fields.editor)
end
elseif fields.clear then --[CLEAR] button
output = {}
save_lua(fields.editor)
minetest.show_formspec("lua:editor", lua_editor())
end
-- STARTUP EDITOR
----------
elseif formname == "lua:startup" then -- double click a file to remove it from the list
if fields.starts then
local select = {["type"] = string.sub(fields.starts, 1, 3), ["row"] = tonumber(string.sub(fields.starts, 5, 5))}
if select.type == "DCL" then
table.remove(lua_startup, select.row)
local startup_str = ""
for i, v in pairs(lua_startup) do
if v ~= "" then
startup_str = startup_str..v..","
end
end
modstorage:set_string("_lua_startup", startup_str)
minetest.show_formspec("lua:startup", startup_form())
end
elseif fields.chooser then -- double click a file to add it to the list
local select = {["type"] = string.sub(fields.chooser, 1, 3), ["row"] = tonumber(string.sub(fields.chooser, 5, 5))}
if select.type == "DCL" then
table.insert(lua_startup, lua_files[select.row])
local startup_str = ""
for i, v in pairs(lua_startup) do
if v ~= "" then
startup_str = startup_str..v..","
end
end
modstorage:set_string("_lua_startup", startup_str)
minetest.show_formspec("lua:startup", startup_form())
end
end
end
end)
---------- ----------
-- PASTE FORMSPEC EDITOR HERE --
---------- ----------
--
---------- ----------
-- PASTE FORMSPEC EDITOR HERE --
---------- ----------
----------
-- UI FUNCTIONALITY
----------
minetest.register_on_formspec_input(function(formname, fields)
-- FILE VIEWER
----------
if formname == "files:viewer" then
if fields.del_lua then
name = lua_files[selected_files[1] ]
table.remove(lua_files, selected_files[1])
files_str = ""
for i, v in pairs(lua_files) do
if v ~= "" then
files_str = files_str..v.."," -- remove the file from the list
end
end
if name == saved_file then -- clear the editing area if the file was loaded
saved_file = false
modstorage:set_string("_lua_saved", "")
save_lua("")
end
modstorage:set_string("_lua_files_list", files_str)
minetest.show_formspec("files:viewer", file_viewer())
elseif fields.del_ui then
name = ui_files[selected_files[2] ]
table.remove(ui_files, selected_files[2])
files_str = ""
for i, v in pairs(ui_files) do
if v ~= "" then
files_str = files_str..v.."," -- remove the file from the list
end
end
if name == current_ui_file then -- clear the editing area if the file was loaded
load_UI("new")
end
modstorage:set_string("_UI_files_list", files_str)
minetest.show_formspec("files:viewer", file_viewer())
elseif fields.lua_select then -- click on a file to select it, double click to open it
local index = tonumber(string.sub(fields.lua_select, 5))
if string.sub(fields.lua_select, 1, 3) == "DCL" then
saved_file = lua_files[index]
modstorage:set_string("_lua_saved", saved_file)
minetest.show_formspec("lua:editor", lua_editor())
else
selected_files[1] = index
minetest.show_formspec("files:viewer", file_viewer())
end
elseif fields.ui_select then -- click on a file to select it, double click to open it
local index = tonumber(string.sub(fields.ui_select, 5))
if string.sub(fields.ui_select, 1, 3) == "DCL" then
load_UI(ui_files[index])
reload_ui()
else
selected_files[2] = index
minetest.show_formspec("files:viewer", file_viewer())
end
elseif fields.key_enter_field == "new_lua" or fields.add_lua then
local exist = false
for i, v in pairs(lua_files) do
if v == fields.new_lua then
exist = true
selected_files[1] = i
end
end
if not exist then
table.insert(lua_files, fields.new_lua)
selected_files[1] = #lua_files
files_str = ""
for i, v in pairs(lua_files) do
if v ~= "" then
files_str = files_str..v..","
end
end
modstorage:set_string("_lua_files_list", files_str)
saved_file = fields.new_lua
minetest.show_formspec("lua:editor", lua_editor())
end
elseif fields.key_enter_field == "new_ui" or fields.add_ui then
local exist = false
for i, v in pairs(ui_files) do
if v == fields.new_ui then
exist = true
selected_files[2] = i
end
end
if not exist then
table.insert(ui_files, fields.new_ui)
selected_files[2] = #ui_files
files_str = ""
for i, v in pairs(ui_files) do
if v ~= "" then
files_str = files_str..v..","
end
end
modstorage:set_string("_UI_files_list", files_str)
load_UI(fields.new_ui)
reload_ui()
end
end
end
if fields._option_tabs_ then
if fields._option_tabs_ == "1" then
minetest.show_formspec("lua:editor", lua_editor())
elseif fields._option_tabs_ == "2" then
reload_ui()
elseif fields._option_tabs_ == "4" then
minetest.show_formspec("files:viewer", file_viewer())
elseif fields._option_tabs_ == "5" then
minetest.show_formspec("lua:startup", startup_form())
else
minetest.show_formspec("lua:unknown",
"size["..data.width..","..data.height.."]label[1,1;COMING SOON]"..create_tabs(fields._option_tabs_))
end
end
end)
----------
-- REGISTER COMMAND
----------
core.register_chatcommand("dte", { -- register the chat command
description = core.gettext("open a lua IDE"),
func = function(parameter)
minetest.show_formspec("lua:editor", lua_editor())
end,
})

2915
clientmods/dte/init.lua Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

82
clientmods/esp/init.lua Normal file
View File

@ -0,0 +1,82 @@
---
-- coras esp .. indev
esp = {}
local radius=60 -- limit is 4,096,000 nodes (i.e. 160^3 -> a number > 79 won't work)
local esplimit=30; -- display at most this many waypoints
local espinterval=4 --number of seconds to wait between scans (a lower number can induce clientside lag)
local stpos={x=0,y=0,z=0}
local nodes=nlist.get("esp")
local esp_wps={}
local hud2=nil
local hud;
local lastch=0
local wason=false
minetest.register_globalstep(function()
if not nodes then return end
if not minetest.settings:get_bool("espactive") then
if #esp_wps > 0 then
for k,v in pairs(esp_wps) do
minetest.localplayer:hud_remove(v)
table.remove(esp_wps,k)
end
wason=false
nlist.hide()
end
return
end
if not minetest.localplayer then return end
wason=true
if os.time() < lastch + espinterval then return end
lastch=os.time()
if not minetest.settings:get_bool('nlist_edmode') then nlist.show_list("esp") end
local pos = minetest.localplayer:get_pos()
local pos1 = vector.add(pos,{x=radius,y=radius,z=radius})
local pos2 = vector.add(pos,{x=-radius,y=-radius,z=-radius})
local epos=minetest.find_nodes_in_area(pos1, pos2, nodes, true)
for k,v in pairs(esp_wps) do --clear waypoints out of range
local hd=minetest.localplayer:hud_get(v)
if not hd or vector.distance(pos,hd.world_pos) > radius + 50 then
minetest.localplayer:hud_remove(v)
table.remove(esp_wps,k)
end
end
if epos then
local ii=0;
for m,xx in pairs(epos) do -- display found nodes as WPs
for kk,vv in pairs(xx) do
if ( ii > esplimit ) then break end
if vector.distance(stpos,pos) > 200 then
stpos=minetest.localplayer:get_pos()
if minetest.settings:get_bool("espautostop") then
minetest.settings:set("continuous_forward", "false")
autofly.aim(vv)
end
end
ii=ii+1
table.insert(esp_wps,minetest.localplayer:hud_add({
hud_elem_type = 'waypoint',
name = m,
text = "m",
number = 0x00ff00,
world_pos = vv
})
)
end
end
end
end)
if (_G["minetest"]["register_cheat"] ~= nil) then
minetest.register_cheat("NodeESP", "Render", "espactive")
else
minetest.settings:set_bool('espactive',true)
end

3
clientmods/esp/mod.conf Normal file
View File

@ -0,0 +1,3 @@
name = esp
author = cora
description = ESP hack

View File

@ -0,0 +1,2 @@
espactive (Enable ESP) bool false
espautostop (Stop Autoforward) bool true

View File

@ -0,0 +1,488 @@
fren = {}
frenemies = fren
fren.friends = {}
fren.friend_color = "#00FF00"
fren.enemies = {}
fren.enemy_color = "#FF0000"
fren.neutral_color = "#FFFFFF"
fren.groups = {}
--[[
storage:
player is {level = n, color = ""}
group is {name = "", level = n, color = "", members = {"", ""}}
available:
player is {level = n, color = "", groups = {"", ""}}
group is {name = "", level = n, color = "", members = {"", ""}}
qualify(player)
name_of(qualified)
is_enemy(player)
is_friend(player)
is_current_player(player)
is_neutral(player)
player_color(player)
player_groups(player)
player_level(player)
get_online_players()
get_online_friends()
get_online_enemies()
get_online_neutrals()
get_online_group(group)
if a player name contains @ it is a fully qualified name
.friend and .enemy should support level
.friend player <color> -- make player friend or recolor them
.fr player
.unfriend player
.unfr player
.enemy player <color> -- make player enemy or recolor them
.en player
.unenemy player
.unen player
.group name <color> -- create or recolor group
.rm_group name
.gadd group player
.grm group player
.lfriends
.lenemies
.lgroup group
.lfriends_all
.lenemies_all
.lgroup_all group
maybe groups should be qualified per server?
--]]
local storage = minetest.get_mod_storage()
-- should remove groups
function fren.serialize()
return minetest.write_json({
friends = fren.friends,
friend_color = fren.friend_color,
enemies = fren.enemies,
enemy_color = fren.enemy_color,
neutral_color = fren.neutral_color,
groups = fren.groups
})
end
-- should relate groups
function fren.deserialize(str)
local des = minetest.parse_json(str)
if des then
fren.friends = des.friends or {}
fren.friend_color = des.friend_color
fren.enemies = des.enemies or {}
fren.enemy_color = des.enemy_color
fren.neutral_color = des.neutral_color
fren.groups = des.groups or {}
end
end
function fren.store()
storage:set_string("data", fren.serialize())
end
function fren.load()
local d = storage:get("data")
if d then
fren.deserialize(d)
end
end
fren.load()
local server_info = minetest.get_server_info()
function fren.qualify(player)
local name = server_info.ip
if server_info.address ~= "" then
name = server_info.address
end
return player .. "@" .. name .. ":" .. server_info.port
end
function fren.name_of(qualified)
return qualified:match("(.-)@")
end
function fren.on_server(name)
local qname=fren.qualify(name)
for k,v in pairs(fren.friends) do
if k == qname then return true end
end
for k,v in pairs(fren.enemies) do
if k == qname then return true end
end
return false
end
-- player required, color/level optional
function fren.friend(player, color, level)
local n = fren.qualify(player)
fren.friends[n] = {placeholder = true} -- true because the way Minetest serializes Json replaces {} with null
fren.friends[n].color = color
fren.friends[n].level = level
fren.store()
end
function fren.unfriend(player)
fren.friends[fren.qualify(player)] = nil
fren.store()
end
function fren.enemy(player, color, level)
local n = fren.qualify(player)
fren.enemies[n] = {placeholder = true}
fren.enemies[n].color = color
fren.enemies[n].level = level
fren.store()
end
function fren.unenemy(player)
fren.enemies[fren.qualify(player)] = nil
fren.store()
end
function fren.group(name, color, level)
fren.groups[name] = {placeholder = true}
fren.groups[name].color = color
fren.groups[name].level = level
fren.store()
end
function fren.remove_group(name)
fren.groups[name] = nil
fren.store()
end
function fren.group_add_player(group, player, level)
if fren.groups[group] then
local q = fren.qualify(player)
fren.groups[group].members[q] = {placeholder = true}
fren.groups[group].members[q].level = level
fren.store()
end
end
function fren.group_remove_player(group, player)
if fren.groups[group] then
fren.groups[group].members[fren.qualify(player)] = nil
fren.store()
end
end
function fren.is_enemy(player)
return fren.enemies[fren.qualify(player)] ~= nil
end
function fren.is_friend(player)
return fren.friends[fren.qualify(player)] ~= nil
end
function fren.is_neutral(player)
return not fren.is_enemy(player) and not fren.is_friend(player)
end
function fren.is_current_player(player)
return player == minetest.locaplayer:get_name()
end
function fren.in_group(player, group)
if fren.groups[group] then
return fren.groups[group].members[fren.qualify(player)] ~= nil
end
end
-- maybe groups should be involved?
function fren.color(player)
local q = fren.qualify(player)
if fren.is_friend(player) then
return fren.friends[q].color or fren.friend_color
elseif fren.is_enemy(player) then
return fren.enemies[q].color or fren.enemy_color
else
return friend.neutral_color
end
end
-- should be a setting
local check_interval = 1
local online_cached = {}
local online_cached_last = 0
local friend_online_cached = {}
local friend_online_cached_last = 0
local enemy_online_cached = {}
local enemy_online_cached_last = 0
local neutral_online_cached = {}
local neutral_online_cached_last = 0
local group_online_cached = {}
local group_online_cached_last = {}
local function is_time(epoch)
if epoch == nil then
return true
end
return os.clock() - epoch >= check_interval
end
local function uniq(l)
local o = {}
local oi = 1
local last
for i, v in ipairs(l) do
if last ~= v then
o[oi] = v
oi = oi + 1
end
last = v
end
return o
end
function fren.get_online_players()
if is_time(online_cached_last) then
online_cached_last = os.clock()
online_cached = minetest.get_player_names()
table.sort(online_cached)
online_cached = uniq(online_cached)
end
return online_cached
end
local function filter(filter, source)
local o = {}
for k, v in pairs(source) do
if filter(v) then
o[k] = v
end
end
return o
end
function fren.get_online_friends()
if is_time(friend_online_cached_last) then
friend_online_cached_last = os.clock()
friend_online_cached = filter(fren.is_friend, fren.get_online_players())
end
return friend_online_cached
end
function fren.get_all_friends()
if is_time(friend_online_cached_last) then
friend_online_cached_last = os.clock()
friend_online_cached = filter(fren.is_friend, fren.get_online_players())
end
return friend_online_cached
end
function fren.get_online_enemies()
if is_time(enemy_online_cached_last) then
enemy_online_cached_last = os.clock()
enemy_online_cached = filter(fren.is_enemy, fren.get_online_players())
end
return enemy_online_cached
end
function fren.get_online_neutrals()
if is_time(neutral_online_cached_last) then
neutral_online_cached_last = os.clock()
neutral_online_cached = filter(fren.is_neutral, fren.get_online_players())
end
return neutral_online_cached
end
function fren.get_online_group(group)
if is_time(group_online_cached_last[group]) then
group_online_cached_last[group] = os.clock()
group_online_cached[group] = filter(
function(v)
return fren.in_group(v, group)
end, fren.get_online_players())
end
return group_online_cached[group]
end
-- first second [opt_third]
-- converts to
-- {
-- [1] = {name = "first", required = true},
-- [2] = {name = "second", required = true},
-- [3] = {name = "opt_third", required = false}
-- }
local function parse_opts(str)
local o = {}
local opts = string.split(str, " ")
for i, v in ipairs(opts) do
if v:match("%[(.-)%]") then
o[i] = {name = v, required = false}
else
o[i] = {name = v, required = true}
end
end
return o
end
-- first second [opt_third]
-- returns {first = a, second = a, opt_third = a/nil} or nil if parsing failed
local function parse_args(str, args)
local opts = parse_opts(str)
local splargs = string.split(args, " ")
local parsed = {}
for i, v in ipairs(opts) do
if splargs[i] then
parsed[v.name] = splargs[i]
elseif v.required then
minetest.display_chat_message("Error: argument '" .. v.name .. "' is required.")
return nil
else
break
end
end
return parsed
end
minetest.register_chatcommand("friend", {
description = "Add a player as a friend.",
params = "<player> <?color>",
func = function(params)
local args = parse_args("player [color]", params)
if args then
fren.friend(args.player, args.color)
end
end
})
minetest.register_chatcommand("unfriend", {
description = "Remove player from friend list.",
params = "<player>",
func = function(params)
local args = parse_args("player", params)
if args then
fren.unfriend(args.player)
end
end
})
minetest.register_chatcommand("enemy", {
description = "Add player as an enemy.",
params = "<player> <?color>",
func = function(params)
local args = parse_args("player [color]", params)
if args then
fren.enemy(args.player, args.color)
end
end
})
minetest.register_chatcommand("unenemy", {
description = "Remove player from enemy list.",
params = "<player>",
func = function(params)
local args = parse_args("player", params)
if args then
fren.unenemy(args.player)
end
end
})
local function lcat(l)
return table.concat(l, ", ")
end
local function displist(l)
minetest.display_chat_message(lcat(l))
end
minetest.register_chatcommand("lfriends", {
description = "List online friends.",
func = function()
displist(fren.get_online_friends())
end
})
minetest.register_chatcommand("lenemies", {
description = "List online enemies.",
func = function()
displist(fren.get_online_enemies())
end
})
minetest.register_chatcommand("lgroup", {
description = "List online members of a group.",
params = "<group>",
func = function(params)
local args = parse_args("group", params)
if args then
displist(fren.get_online_group(args.group))
end
end
})

View File

@ -0,0 +1,3 @@
name = frenemies
author = emilia
description = Keep track of friends and enemies, as well as groups. Mostly for other mods to use as a unified friend/team list tracker. API methods can be accessed with fren.method or frenemies.method.

View File

@ -0,0 +1,162 @@
--
-- cora's defensive combat hax
local karange=14
local tping=false
local dodged=false
local function checkair(pos)
local n=minetest.get_node_or_nil(pos)
if n==nil or n['name'] == 'air' then return true end
return false
end
local function checkbadblocks(pos)
local n=minetest.find_node_near(pos, 2, {'mcl_core:gravel','mcl_core:sand','mcl_core:lava_source','mcl_core:lava_flowing','mcl_core:water_source','mcl_core:water_flowing',
'mcl_core:obsidian','mcl_core:bedrock'}, true)
if n == nil then return false end
return true
end
local function checktrap()
local lp=minetest.localplayer:get_pos()
local air,nd=minetest.line_of_sight(vector.add(lp,{x=0,y=-2,z=0}), vector.add(lp,{x=0,y=50,z=0}))
if(not air) then
local tn=minetest.get_node_or_nil(nd)
if(tn == nil) then return false end
for k,v in ipairs({'mcl_core:lava_source','mcl_core:lava_flowing','mcl_core:water_source','mcl_core:water_flowing'}) do
if tn.name == v then return true end
end
end
return false
end
local function checkhead()
local ppos=vector.add(minetest.localplayer:get_pos(),{x=0,y=1,z=0})
if (checkair(ppos)) then return true end
return false
end
local function checkprojectile()
for k, v in ipairs(minetest.localplayer.get_nearby_objects(karange)) do
if ( v:get_item_textures():sub(-9) == "arrow_box") or ( v:get_item_textures():sub(-7) == "_splash") or v:get_item_textures():sub(-17) == "shulkerbullet.png" then
local lp=minetest.localplayer:get_pos()
local vel=v:get_velocity()
local dst=vector.distance(lp,v:get_pos())
if dst > 4 then return false end
if (vel.x == 0 and vel.y == 0 and vel.z ==0 ) then return false end
return true
end
end
return false
end
local function amautotool(pos)
local node=minetest.get_node_or_nil(pos)
minetest.select_best_tool(node.name)
end
local function get_2dpos_from_yaw(r,yaw)
local tg={x=0,y=0,z=0}
tg.x= r * math.sin(yaw)
tg.z= r * math.cos(yaw)
return tg
end
local function get_3dpos_from_yaw_and_pitch(r,yaw,pitch)
local tg={x=0,y=0,z=0}
tg.x= r * math.sin(yaw)
tg.y= r * math.sin(pitch)
tg.z= r * math.cos(yaw)
return tg
end
local function dhfree()
if not minetest.localplayer then return end
local n=vector.add(minetest.localplayer:get_pos(),{x=0,y=2,z=0})
local nd=minetest.get_node_or_nil(n)
if nd == nil then return end
while nd.name ~= "air" do
amautotool(n)
minetest.dig_node(n)
minetest.dig_node(vector.add(n,{x=0,y=-1,z=0}))
nd=minetest.get_node_or_nil(n)
end
tping=false
end
local lastwrp=0
local function mwarp(pos)
if tping then return end
--if os.time() < lastwrp+1 then return end
--lastwrp=os.time();
tping=true
minetest.after("0.1",function() dhfree() end)
minetest.localplayer:set_pos(pos)
end
local function get_target(epos)
math.randomseed(os.time())
local t=vector.add(epos,get_3dpos_from_yaw_and_pitch(karange+1,math.random(90,240),math.random(90,135)))
if (checkbadblocks(t)) then
return get_target(epos)
elseif checkair(t) then
return t
else
amautotool(t)
end
return t
end
local function evade(ppos)
mwarp(get_target(ppos))
end
local function dodge()
if dodged then return end
dodged=true
local t=turtle.dircoord(math.random(0,2)-1,0,math.random(0,2)-1)
local opos=minetest.localplayer:get_pos()
mwarp(t)
minetest.after("0.5",function() mwarp(opos) dodged=false end )
end
local function rro() -- reverse restraining order
for k, v in ipairs(minetest.localplayer.get_nearby_objects(karange+5)) do
local name=v:get_name()
if (v:is_player() and name ~= minetest.localplayer:get_name()) then
if fren.is_friend(name) then
return end
local pos = v:get_pos()
pos.y = pos.y - 1
local mpos=minetest.localplayer:get_pos()
local distance=vector.distance(mpos,pos)
if distance < karange then
local trg=get_target(pos)
mwarp(trg)
minetest.after("0.2",function() autofly.aim(pos) end)
return
end
end
end
end
minetest.register_globalstep(function()
if minetest.settings:get_bool("goddess") then
local ppos=minetest.localplayer:get_pos()
--rro()
if(checkprojectile()) then dodge(ppos) end
--if(checktrap()) then evade(ppos) end
if(not checkhead()) then dhfree() end
end
end)
minetest.register_chatcommand("dhf", { description = "", func = dhfree })
-- REG cheats on DF
if (_G["minetest"]["register_cheat"] ~= nil) then
minetest.register_cheat("Goddess Mode", "Combat", "goddess")
else
minetest.settings:set_bool('goddess',true)
end

View File

@ -0,0 +1,3 @@
name = goddessmode
author = cora
description = defensive combat hax

View File

@ -0,0 +1 @@
goddess (Goddess Mode) bool true

View File

@ -0,0 +1,32 @@
local function init_settings(setting_table)
for k, v in pairs(setting_table) do
if minetest.settings:get(k) == nil then
if type(v) == "boolean" then
minetest.settings:set_bool(k, v)
else
minetest.settings:set(k, v)
end
end
end
end
init_settings({
haxnotify_enabled = true,
haxnotify_public = true,
haxnotify_public_message = "Hey guys. I'm using a hacked client. https://repo.or.cz/waspsaliva.git."
})
local function notify_server()
minetest.send_chat_message("/usinghax.banmeifudare.")
end
local function notify_public()
minetest.send_chat_message(minetest.settings:get('haxnotify_public_message'))
end
minetest.register_on_mods_loaded(function()
minetest.after("5.0", function()
if minetest.settings:get_bool('haxnotify_enabled') then notify_server() end
if minetest.settings:get_bool('haxnotify_public') then notify_public() end
end)
end)

View File

@ -0,0 +1,3 @@
haxnotify_enabled (Notify server that you're hacking. If you disable this you're bad.) bool true
haxnotify_public (Notify the public that you're hacking) bool true
haxnotify_public_message (Welcome message for public notification) string TCHAT

256
clientmods/hignore/init.lua Normal file
View File

@ -0,0 +1,256 @@
-- CC0/Unlicense system32 2020
local function init_settings(setting_table)
for k, v in pairs(setting_table) do
if minetest.settings:get(k) == nil then
if type(v) == "boolean" then
minetest.settings:set_bool(k, v)
else
minetest.settings:set(k, v)
end
end
end
end
init_settings({
hignore_ignore_all = false,
hignore_highlight_all = false,
hignore_highlight_all_color = "#FFFFFF",
hignore_strip_all = false,
hignore_log = true
})
local storage = minetest.get_mod_storage()
local function storage_init_table(key)
if storage:get(key) == nil or storage:get(key) == "null" then
storage:set_string(key, "{}")
end
return minetest.parse_json(storage:get_string(key))
end
local function storage_save_json(key, value)
storage:set_string(key, minetest.write_json(value))
end
-- public interface
hignore = {}
-- name: color
hignore.highlight = storage_init_table("hignore_highlight")
-- name: mode
hignore.ignore = storage_init_table("hignore_ignore")
-- strip: mode
hignore.strip = storage_init_table("hignore_strip")
function hignore.save()
storage_save_json("hignore_highlight", hignore.highlight)
storage_save_json("hignore_ignore", hignore.ignore)
storage_save_json("hignore_strip", hignore.strip)
end
local function localize_player(player)
local info = minetest.get_server_info()
local name = info.ip
if info.address ~= "" then
name = info.address
end
return player .. "@" .. name .. ":" .. info.port
end
local playerat
local function log(message)
if minetest.settings:get_bool("hignore_log") then
if playerat == nil then
playerat = localize_player(minetest.localplayer:get_name())
end
minetest.log("action", "[hignore] " .. playerat .. " " .. message)
end
end
local function display(message)
if minetest.settings:get_bool("hignore_strip_colors") then
message = minetest.strip_colors(message)
end
local dm = message:match(".*rom (.-): .*")
local pub = message:match("<(.-)>.*")
local is_dm = false
local player = dm or pub
if dm then
is_dm = true
end
if player then
player = localize_player(dm or pub)
else
return
end
-- ignore and hide
if hignore.ignore[player] or minetest.settings:get_bool("hignore_ignore_all") then
if hignore.ignore[player] == "summarize" then
if dm then
minetest.display_chat_message(player .. " sent you a DM.")
else
minetest.display_chat_message(player .. " sent a message.")
end
end
return true
end
-- strip title
if not is_dm and (hignore.strip[player] or minetest.settings:get_bool("hignore_strip_all")) then
message = message:match(".- (.*)") or message
if hignore.highlight[player] == nil then
minetest.display_chat_message(message)
return true
end
end
-- highlight message
if hignore.highlight[player] then
minetest.display_chat_message(minetest.colorize(hignore.highlight[player], message))
return true
end
if minetest.settings:get_bool("hignore_highlight_all") then
minetest.display_chat_message(
minetest.colorize(
minetest.settings:get("hignore_highlight_all_color"),
message))
return true
end
if minetest.settings:get_bool("hignore_strip_colors") then
minetest.display_chat_message(message)
return true
end
end
minetest.register_on_receiving_chat_message(function(message)
local l = display(message)
if l then
log(message)
end
return l
end)
local function noplayer()
minetest.display_chat_message("No player specified.")
end
local function string_table(t)
local out = ""
for k, v in pairs(t) do
if out ~= "" then
out = out .. ", " .. tostring(k) .. ": " .. tostring(v)
else
out = tostring(k) .. ": " .. tostring(v)
end
end
if out == "" then
return "Empty"
else
return out
end
end
minetest.register_chatcommand("ignore", {
params = "<player> <mode>",
description = "Ignore a player's messages, mode can be omitted (hide) or hide/summarize/none (stops ignoring).",
func = function(params)
local plist = string.split(params, " ")
if plist[1] == nil then
noplayer()
return
end
local player = localize_player(plist[1])
local val = plist[2]
-- hide/summarize are already set
if plist[2] == nil then
val = "hide"
elseif plist[2] == "none" then
val = nil
end
hignore.ignore[player] = val
hignore.save()
end
})
minetest.register_chatcommand("ignore_list", {
description = "List ignored players.",
func = function(params)
minetest.display_chat_message(string_table(hignore.ignore))
end
})
minetest.register_chatcommand("highlight", {
params = "<player> <color>",
description = "Highlight a player's messages, omit color to stop highlighting. Supports CSS and RGBA hex colors.",
func = function(params)
local plist = string.split(params, " ")
if plist[1] == nil then
noplayer()
return
end
local player = localize_player(plist[1])
hignore.highlight[player] = plist[2]
hignore.save()
end
})
minetest.register_chatcommand("highlight_list", {
description = "List highlighted players.",
func = function(params)
minetest.display_chat_message(string_table(hignore.highlight))
end
})
minetest.register_chatcommand("strip", {
params = "<player>",
description = "Toggle stripping of a player's titles.",
func = function(params)
local plist = string.split(params, " ")
if plist[1] == nil then
noplayer()
return
end
local player = localize_player(plist[1])
if hignore.strip[player] then
hignore.strip[player] = nil
else
hignore.strip[player] = "remove"
end
hignore.save()
end
})
minetest.register_chatcommand("strip_list", {
description = "List players with stripped titles.",
func = function(params)
minetest.display_chat_message(string_table(hignore.strip))
end
})

View File

@ -0,0 +1,2 @@
name = hignore
description = Highlight/ignore player chat messages and DMs.

View File

@ -0,0 +1,6 @@
hignore_log (Log highlighted/ignored/stripped messages) bool true
hignore_strip_all (Strip titles of all players) bool false
hignore_highlight_all (Highlight all player messages) bool false
hignore_highlight_all_color (Color to highlight all player messages) string "#FFFFFF"
hignore_ignore_all (Ignore all player messages) bool false
hignore_strip_colors (Strip colors from messages before highlighting) bool false

View File

@ -0,0 +1,85 @@
hpchange = {}
local widget
local last_hp
local dmg
local function show_widget()
widget = minetest.localplayer:hud_add({
hud_elem_type = "text",
name = "HP Change",
text = "Last HP change: ",
number = 0x00FF00,
direction = 0,
position = {x = 0.85, y = 0.8},
scale = {x = 0.9, y = 0.9},
alignment = {x = 1, y = 1},
offset = {x = 0, y = 0}
})
end
local function update_hud(delta, potential)
if minetest.localplayer ~= nil and delta ~= 0 then
if widget == nil then
show_widget()
end
local num = tostring(math.abs(delta))
if delta < 0 then
num = "-" .. num
dmg=true
else
num = "+" .. num
dmg=false
end
if potential then
minetest.localplayer:hud_change(widget, "text", "Last HP change (potential): " .. num)
else
minetest.localplayer:hud_change(widget, "text", "Last HP change: " .. num)
end
if delta > 0 then
minetest.localplayer:hud_change(widget, "number", 0x00FF00)
else
minetest.localplayer:hud_change(widget, "number", 0xFF0000)
end
if last_hp ~= nil then
last_hp = last_hp + delta
end
end
end
local function init_last()
if last_hp == nil and minetest.localplayer ~= nil then
last_hp = minetest.localplayer:get_hp()
end
end
-- health decrease (potential)
minetest.register_on_damage_taken(function(hp)
if minetest.localplayer ~= nil then
-- this should be true if no fall damage is on
-- the localplayer hp is wrong though
if last_hp == minetest.localplayer:get_hp() then
update_hud(-hp, true)
else
update_hud(-hp)
end
init_last()
end
end)
-- health increase
minetest.register_on_hp_modification(function(hp)
init_last()
if last_hp ~= nil and last_hp <= hp then
update_hud(hp - last_hp)
end
end)
function hpchange.get_status()
return dmg
end

View File

@ -0,0 +1,2 @@
name = hpchange
description = Display last hit's damage (in half hearts).

View File

@ -0,0 +1,127 @@
-- CC0/Unlicense Emilia 2021
incremental_tp = {}
incremental_tp.fudge = 0.8 -- cause the tp time isn't synced with the server
incremental_tp.tpactive=false
-- for Clamity
incremental_tp.max_instantaneous_tp = {
x = 5,
y = 45,
z = 5
}
function incremental_tp.get_actual_speed()
local po=minetest.localplayer:get_physics_override()
local rt=vector.new()
rt.x=incremental_tp.max_instantaneous_tp.x * po.speed
rt.y=incremental_tp.max_instantaneous_tp.y * po.speed
rt.z=incremental_tp.max_instantaneous_tp.z * po.speed
return rt
end
local wason=false
local function sign(n)
if n == 0 then
return 0
end
return n / math.abs(n)
end
local function max_dist_per(vec, time)
local mitp = vector.multiply(incremental_tp.get_actual_speed(),
incremental_tp.fudge)
local nvec = {x = 0, y = 0, z = 0}
nvec.x = sign(vec.x) * math.min(math.abs(vec.x), mitp.x * time)
nvec.z = sign(vec.z) * math.min(math.abs(vec.z), mitp.z * time)
-- negative y speed cap is infinity, so if y < 0 it is always allowed
nvec.y = math.min(vec.y, mitp.y * time)
return nvec
end
local function tpstep(target, time, second, variance,sfunc)
local pos = minetest.localplayer:get_pos()
local vec = vector.subtract(target, pos)
minetest.settings:set_bool("free_move",true)
if not incremental_tp.tpactive and wason then
wason=false
return
end
wason=true
incremental_tp.tpactive=true
if vector.distance(pos,target) < 3 then
minetest.localplayer:set_pos(target)
incremental_tp.tpactive=false
--minetest.display_chat_message("Arrived at " .. minetest.pos_to_string(target))
if sfunc then
minetest.after(time, function()
sfunc(target)
end)
end
return
end
if second < 0.001 then
second = 1
end
local intime = math.min(time, second)
if variance then
-- you can't move faster than 1 second of distance instantaneously
intime = math.min(1, math.random() * variance - variance / 2 + intime)
end
local nvec = max_dist_per(vec, intime)
local trg=vector.add(pos, nvec)
--local safe=ws.find_closest_reachable_airpocket(trg)
minetest.localplayer:set_pos(trg)
minetest.after(intime, function()
tpstep(target, time, second - intime, variance,sfunc)
end)
end
function incremental_tp.tpstep(target, time, variance,sfunc)
if incremental_tp.tpactive then return end
tpstep(target, time, 1, variance,sfunc)
end
function incremental_tp.tp(target, time, variance)
incremental_tp.tpactive=false
minetest.after(time,function()
tpstep(target,time,1,variance)
end)
end
function incremental_tp.tpafter(target,time,variance,sfunc)
incremental_tp.tpactive=false
minetest.after(time,function()
tpstep(target,time,1,variance,sfunc)
end)
end
if autofly then autofly.register_transport('itp',function(pos,name) incremental_tp.tp(pos,1) end) end
if autofly then autofly.register_transport('jitp',function(pos,name) incremental_tp.tp(pos,0.5,0.4) end) end
minetest.register_chatcommand("itp", {
description = "Teleport to destination with fixed increments.",
params = "<destination>",
func = function(params)
local pos = minetest.string_to_pos(params)
incremental_tp.tp(pos, 1)
end
})
minetest.register_chatcommand("jittertp", {
description = "Teleport to destination with jittery increments.",
params = "<destination>",
func = function(params)
local pos = minetest.string_to_pos(params)
incremental_tp.tp(pos, 0.5, 0.4)
end
})
-- chunk_rand

View File

@ -0,0 +1,95 @@
-- CC0/Unlicense Emilia 2021
refill = {}
local function nameformat(description)
description = description:gsub(string.char(0x1b) .. "%(.@[^)]+%)", "")
description = description:match("([^\n]*)")
return description
end
function refill.find_named(list, name, test)
for i, v in ipairs(list) do
if (v:get_name():find("shulker_box")
and nameformat(v:get_description()) == name
and (test and test(v))) then
return i
end
end
end
function refill.shulker_has_items(stack)
local list = minetest.deserialize(stack:get_metadata())
for i, v in ipairs(list) do
if not ItemStack(v):is_empty() then
return true
end
end
return false
end
function refill.shulk_switch(name)
local plinv = minetest.get_inventory("current_player")
local pos = refill.find_named(plinv.main, name, refill.shulker_has_items)
if pos then
minetest.log("main " .. tostring(pos))
minetest.localplayer:set_wield_index(pos)
return true
end
local epos = refill.find_named(plinv.enderchest, name, refill.shulker_has_items)
if epos then
minetest.log("enderchest " .. tostring(epos))
local tpos
for i, v in ipairs(plinv.main) do
if v:is_empty() then
tpos = i
break
end
end
if tpos then
local mv = InventoryAction("move")
mv:from("current_player", "enderchest", epos)
mv:to("current_player", "main", tpos)
mv:apply()
minetest.localplayer:set_wield_index(tpos)
return true
end
end
end
local function invposformat(pos)
pos = vector.round(pos)
return string.format("nodemeta:%i,%i,%i", pos.x, pos.y, pos.z)
end
local function do_refill(pos)
local q = quint.invaction_new()
quint.invaction_dump(q,
{location = invposformat(pos), inventory = "main"},
{location = "current_player", inventory = "main"})
quint.invaction_apply(q)
end
function refill.refill_at(pos, name)
if refill.shulk_switch(name) then
minetest.after(1, minetest.place_node, pos)
minetest.after(2, do_refill, pos)
minetest.after(3, minetest.dig_node, pos)
end
end
function refill.refill_here(name)
local pos = vector.round(minetest.localplayer:get_pos())
refill.refill_at(pos, name)
end
minetest.register_chatcommand("refill", {
description = "Refill the inventory with a named shulker.",
params = "<shulker name>",
func = refill.refill_here
})

View File

@ -0,0 +1,80 @@
local last_count
local last_item
local icon_widget
local count_widget
local epoch = 0
local function display_widgets()
if minetest.localplayer ~= nil then
if icon_widget == nil then
icon_widget = minetest.localplayer:hud_add({
hud_elem_type = "image",
name = "Item count icon",
scale = {x = 1, y = 1},
alignment = {x = 0.5, y = 1},
position = {x = 0.85, y = 0.5}
})
end
if count_widget == nil then
count_widget = minetest.localplayer:hud_add({
hud_elem_tyoe = "text",
name = "Item count",
scale = {x = 1, y = 1},
alignment = {x = 0.5, y = 0},
position = {x = 0.85, y = 0.5},
text = "0",
number = 0xFFFFFF
})
end
end
end
local function update_count()
if minetest.localplayer ~= nil then
display_widgets()
local wielded = minetest.localplayer:get_wielded_item()
local texture = "" --wielded:get_definition().inventory_image
local wear = wielded:get_wear()
local count = 0
local num = ""
if wear == 0 then
for k, v in ipairs(minetest.get_inventory("current_player").main) do
if v:get_name() == wielded:get_name() then
count = count + v:get_count()
end
end
num = tostring(count)
else
num = tostring(((65535 - wear) / 65535) * 100) .. "%"
end
last_count = count
last_item = wielded.name
minetest.localplayer:hud_change(icon_widget, "text", texture)
minetest.localplayer:hud_change(count_widget, "text", num)
end
end
minetest.register_on_placenode(function(item, pointed_thing)
update_count()
end)
minetest.register_on_item_use(function(item, pointed_thing)
update_count()
end)
minetest.register_globalstep(function()
if os.time() > epoch then
update_count()
epoch = os.time()
end
end)

View File

@ -0,0 +1,2 @@
name = itemcount
description = Display currently held item's total count.

View File

@ -0,0 +1,192 @@
kamikaze={}
kamikaze.active=false
local fnd=false
local cpos=vector.new(0,0,0)
local hud_wp=nil
local zz=vector.new(42,42,42)
local badnodes={'mcl_tnt:tnt','mcl_fire:basic_flame','mcl_fire:fire','mcl_banners:hanging_banner','mcl_banners:standing_banner','mcl_fire:fire_charge','mcl_sponges:sponge','mcl_sponges:sponge_wet','mcl_nether:soul_sand','mcl_heads:wither_skeleton'}
local badobs={'mcl_end_crystal','arrow_box','mobs_mc_wither.png'}
local searchtxt=nil
local searchheight=64
local tob=nil
local function set_kwp(name,pos)
if hud_wp then
minetest.localplayer:hud_change(hud_wp, 'world_pos', pos)
minetest.localplayer:hud_change(hud_wp, 'name', name)
else
hud_wp = minetest.localplayer:hud_add({
hud_elem_type = 'waypoint',
name = name,
text = 'm',
number = 0x00ff00,
world_pos = pos
})
end
end
local nextzz=0
local function randomzz()
if nextzz > os.clock() then return false end
math.randomseed(os.time())
zz.x=math.random(-128,129)
zz.y=math.random(0,searchheight)
zz.z=math.random(-128,128)
nextzz=os.clock()+ 30
end
local function find_ob(txts)
local odst=500
local rt=nil
local obs=minetest.localplayer.get_nearby_objects(500)
for k, v in ipairs(obs) do
for kk,txt in pairs(txts) do
if ( v:get_item_textures():find(txt) ) then
local npos=v:get_pos()
local dst=vector.distance(npos,minetest.localplayer:get_pos())
if odst > dst then
searchtxt=v:get_item_textures()
cpos=npos
set_kwp(searchtxt,v:get_pos())
odst=dst
fnd=true
tob=v
rt=v
end
end
end
end
return rt
end
local function find_nd(names)
local lp=minetest.localplayer:get_pos()
local epos=minetest.find_nodes_near(lp,60,names,true)
local rt=nil
local odst=500
if epos then
for k,v in pairs(epos) do
local node=minetest.get_node_or_nil(v)
local lp=minetest.localplayer:get_pos()
local dst=vector.distance(lp,v)
if odst > dst then
odst=dst
cpos=vv
rt=vv
fnd=true
end
end
end
return rt
end
local function find_bad_things()
if fnd then return true end
local lp=minetest.localplayer:get_pos()
local ob=find_ob(badobs)
if not ob then ob=find_nd(badnodes) end
if not ob then
set_kwp('nothing found',zz)
randomzz()
fnd=false
return false
end
return true
end
local function flythere()
if not minetest.localplayer then return end
if not cpos then return end
ws.aim(cpos)
minetest.settings:set_bool("killaura",false)
if incremental_tp.tpactive then return end
local lp=minetest.localplayer:get_pos()
local dst=vector.distance(lp,cpos)
if tob and tob:get_item_textures() == searchtxt then
dst=vector.distance(lp,tob:get_pos())
cpos=tob:get_pos()
set_kwp(searchtxt,cpos)
end
minetest.settings:set_bool("continuous_forward",true)
end
local function stopflight()
local lp = minetest.localplayer:get_pos()
local dst=vector.distance(lp,cpos)
minetest.settings:set_bool("continuous_forward",false)
if tob and tob:get_item_textures():find(searchtxt) then
if searchtxt == 'mcl_end_crystal.png' then
minetest.dig_node(cpos)
tob:punch()
minetest.interact('start_digging')
searchtxt=""
tob=nil
else
minetest.settings:set_bool("killaura",true)
end
end
fnd=false
tob=nil
end
ws.rg('Kamikaze','Bots','kamikaze', function()
local lp = minetest.localplayer:get_pos()
local dst=vector.distance(lp,cpos)
if not find_bad_things() then
if vector.distance(lp,zz) < 1 then
stopflight()
else
cpos=zz
flythere()
end
elseif dst < 1 then
stopflight()
else
flythere()
end
-- ws.dignodes(minetest.find_nodes_near(minetest.localplayer:get_pos(),5,badnodes,true))
if cpos then
minetest.dig_node(cpos)
--minetest.interact('start_digging')
end
end,function()
core.set_keypress("special1", true)
kamikaze.active=true
end, function()
kamikaze.active=false
core.set_keypress("special1", false)
fnd=false
if hud_wp then
minetest.localplayer:hud_remove(hud_wp)
hud_wp=nil
end
end,{"noclip","pitch_move","dighead","digbadnodes"})
minetest.register_on_death(function()
if not minetest.settings:get_bool("kamikaze") then return end
tob=nil
-- incremental_tp.tpactive=false
minetest.after("5.0",function()
fnd=false
end)
end)
ws.on_connect(function()
minetest.settings:set_bool("kamikaze",false)
--if minetest.localplayer and minetest.localplayer:get_name():find("kamikaze") then
-- minetest.settings:set_bool("kamikaze",true)
--else minetest.settings:set_bool("kamikaze",false)
--end
end)
minetest.register_cheat('KamiCrystals','Bots','kamikaze_crystals')

146
clientmods/muse/init.lua Normal file
View File

@ -0,0 +1,146 @@
-- CC0/Unlicense Emilia 2020
-- TODO: support noteblocks on different blocktypes
-- denoted with S/W/O/etc. then the tone in a string?
-- TODO: polyphony?
-- TODO: multiple mod support
muse = {}
muse.playing = nil
-- assumes tone in param2
muse.noteblocks = {
"mesecons_noteblock:noteblock"
}
muse.tracks = {
["test"] = {
name = "example",
interval = 0.25, -- in seconds
notes = {
10,
10
}
}
}
local toneblocks = {}
local tones = {}
function muse.find_toneblocks(radius)
radius = radius or 20
local pos = vector.round(minetest.localplayer:get_pos())
local nodes = minetest.find_nodes_near(pos, radius, muse.noteblocks)
for i, v in ipairs(nodes) do
local p2 = minetest.get_node_or_nil(v).param2
local i = tostring(p2)
if not toneblocks[i] then
toneblocks[i] = {}
end
toneblocks[i][#toneblocks[i] + 1] = v
end
end
function muse.play(note)
if not note then
return
end
if note == -1 then
return
end
if type(note) == "string" then
note = tones[note]
end
local poses = toneblocks[tostring(note)]
if poses then
local pos = poses[1]
minetest.localplayer:set_pos(pos)
minetest.interact("start_digging", pos)
minetest.interact("stop_digging", pos)
end
end
function muse.play_track(track)
muse.playing = track
muse.playing.idx = 1
muse.playing.last = 0
end
minetest.register_globalstep(function()
local now = os.clock()
if muse.playing and muse.playing.last + muse.playing.interval <= now then
muse.playing.last = now
local note = muse.playing.notes[muse.playing.idx]
if note then
muse.play(note)
muse.playing.idx = muse.playing.idx + 1
else
muse.playing = nil
end
end
end)
local function timestring(seconds)
seconds = math.floor(seconds + 0.5)
local mins = math.floor(seconds / 60)
local secs = seconds % 60
return string.format("%02d:%02d", mins, secs)
end
function muse.get_playing_string()
if muse.playing then
return string.format("Currently playing: %s, %d/%d (%s/%s)",
muse.playing.name,
muse.playing.idx,
#muse.playing.notes,
timestring(muse.playing.idx * muse.playing.interval),
timestring(#muse.playing.notes * muse.playing.interval))
else
return "Nothing is playing"
end
end
minetest.register_chatcommand("findtones", {
description = "Find tone blocks in the vicinity.",
func = function()
toneblocks = {}
muse.find_toneblocks()
local len = 0
for k, v in pairs(toneblocks) do
len = len + 1
end
minetest.display_chat_message("Found " .. tostring(len) .. " out of 25 tones")
end
})
minetest.register_chatcommand("play", {
description = "Play a musical track",
params = "<track>",
func = function(params)
local track = muse.tracks[params]
if track then
muse.play_track(track)
minetest.display_chat_message("Now playing " .. muse.playing.name)
else
minetest.display_chat_message("Track not found.")
end
end
})
minetest.register_chatcommand("playing", {
description = "Show currently playing track and its progress",
func = function()
minetest.display_chat_message(muse.get_playing_string())
end
})

108
clientmods/nlist/forms.lua Normal file
View File

@ -0,0 +1,108 @@
local selected_name = false
local formspec_list = {}
function ws.display_list_formspec(fname,list,funcs)
funcs={}
local formspec = 'size[6.25,9]' ..
'label[0,0;NodeLists]' ..
'button_exit[0,7.5;1,0.5;display;Show]' ..
'button[3.625,7.5;1.3,0.5;rename;Rename]' ..
'button[4.9375,7.5;1.3,0.5;delete;Delete]'
local sp=0
for k,v in pairs(funcs) do
formspec=formspec..'button_exit['..sp..',8.5;1,0.5;'..v.name..';'..v.name..']'
sp=sp+0.8
end
formspec=formspec..'textlist[0,0.75;6,6;marker;'
local selected = 1
formspec_list = {}
if not list then list={} end
for id, name in ipairs(list) do
if id > 1 then
formspec = formspec .. ','
end
if not selected_name then
selected_name = name
end
if name == selected_name then
selected = id
end
formspec_list[#formspec_list + 1] = name
formspec = formspec .. '##' .. minetest.formspec_escape(name)
end
formspec = formspec .. ';' .. tostring(selected) .. ']'
if selected_name then
local val=list[selected]
if val then
formspec = formspec .. 'label[0,6.75;' .. selected_name .. ']'
end
else
formspec = formspec .. 'button_exit[0,7.5;5.25,0.5;quit;Close dialog]' ..
'label[0,6.75;No Entries.]'
end
return minetest.show_formspec(fname, formspec)
end
minetest.register_on_formspec_input(function(formname, fields)
local fname="NodeLists"
if formname == 'NodeLists-ignore' then
return true
elseif formname ~= "NodeLists" then
return
end
local name = selected_name
if name then
for k,v in pairs(autofly.registered_transports) do
if fields[v.name] then
if not v.func(autofly.get_waypoint(name),name) then
minetest.display_chat_message('Error with '..v.name)
end
end
end
if fields.display then
if not autofly.display_waypoint(name) then
minetest.display_chat_message('Error displaying waypoint!')
end
elseif fields.rename then
minetest.show_formspec(fname, 'size[6,3]' ..
'label[0.35,0.2;Rename waypoint]' ..
'field[0.3,1.3;6,1;new_name;New name;' ..
minetest.formspec_escape(name) .. ']' ..
'button[0,2;3,1;cancel;Cancel]' ..
'button[3,2;3,1;rename_confirm;Rename]')
elseif fields.rename_confirm then
if fields.new_name and #fields.new_name > 0 then
if nlist.rename(name, fields.new_name) then
selected_name = fields.new_name
else
minetest.display_chat_message('Error renaming!')
end
ws.display_list_formspec()
else
minetest.display_chat_message('Please enter a new name for the entry.')
end
elseif fields.delete then
minetest.show_formspec(fname, 'size[6,2]' ..
'label[0.35,0.25;Are you sure you want to delete this waypoint?]' ..
'button[0,1;3,1;cancel;Cancel]' ..
'button[3,1;3,1;delete_confirm;Delete]')
elseif fields.delete_confirm then
autofly.delete_waypoint(name)
selected_name = false
ws.display_list_formspec()
elseif fields.cancel then
ws.display_list_formspec()
elseif name ~= selected_name then
selected_name = name
ws.display_list_formspec()
end
elseif fields.display or fields.delete then
minetest.display_chat_message('Please select a waypoint.')
end
return true
end)

1236
clientmods/nlist/init.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
-- CC0/Unlicense Emilia 2020
-- Optimizes stuff.
woptimize={}
function woptimize.countents()
local obj = minetest.localplayer.get_nearby_objects(10000)
ws.dcm("Entity count: "..#obj)
end
-- texture is a prefix
local function remove_ents(texture)
if not minetest.localplayer then return end
local obj = minetest.localplayer.get_nearby_objects(10000)
for i, v in ipairs(obj) do
-- CAOs with water/lava textures are droplets
--minetest.log("ERROR",v:get_item_textures())
--ws.dcm(v:get_item_textures())
local txt=v:get_item_textures()
if type(txt) == "string" and txt:find(texture) then
v:set_visible(false)
v:remove(true)
end
end
end
local function remove_hud(name)
local player = minetest.localplayer
local def
local i = -1
if not player then return end
repeat
i = i + 1
def = player:hud_get(i)
until not def or def.text:find(name)
if def then
minetest.localplayer:hud_remove(i)
end
end
core.register_on_spawn_particle(function(particle)
if minetest.settings:get_bool("noparticles") then return true end
end)
local epoch = os.clock()
minetest.register_globalstep(function()
if not minetest.localplayer then return end
if os.clock() > epoch + 1 then
if minetest.settings:get_bool("optimize_water_drops") then
remove_ents("default_water_source")
end
if minetest.settings:get_bool("optimize_burning") then
remove_hud('mcl_burning_hud_flame_animated.png')
end
epoch = os.clock()
end
end)
minetest.register_cheat("NoParticles", "Render", "noparticles")
minetest.register_cheat("NoDroplets", "Render", "optimize_water_drops")
minetest.register_cheat("NoBurning", "Render", "optimize_burning")

View File

@ -0,0 +1,3 @@
optimize_water_drops (Hide water drops, improves framerates in watery areas) bool false
optimize_hearts (Hide damage hearts) bool false
noparticles (Do not show particles) bool false

160
clientmods/peek/init.lua Normal file
View File

@ -0,0 +1,160 @@
-- CC0/Unlicense system32 2020
local function parse_coord(c)
c = string.split(c, " ")
return {x = tonumber(c[1] or 0), y = tonumber(c[2] or 0), z = tonumber(c[3] or 0)}
end
minetest.register_chatcommand("cpeek", {
func = function(params)
local oldpos = minetest.localplayer:get_pos()
local c = parse_coord(params)
local dist = vector.distance(c, oldpos)
local d = tostring(c.x) .. "," .. tostring(c.y) .. "," .. tostring(c.z)
local f = "size[10,10]\nlabel[0,0;Can access: " .. tostring(dist < 6) .. "(" .. tostring(dist) .. ")]\nlist[nodemeta:" .. d .. ";main;0,0.5;9,3;]"
minetest.localplayer:set_pos(c)
minetest.show_formspec("ChestPeek", f)
minetest.localplayer:set_pos(oldpos)
end
})
local formspec_template = "size[9,Y]label[0,0;L]button[8,0;1,1;up;^Up^]"
local formspec_base = formspec_template:gsub("Y", "4")
local formspec_base_label = formspec_template:gsub("Y", "4.5")
local formspec_item = "\nitem_image_button[X,Y;1,1;I;N;]"
local formspec_item_label = formspec_item .. "\nlabel[X,Z;T]"
local function map(f, t)
local out = {}
for i, v in ipairs(t) do
out[i] = f(v)
end
return out
end
local inventories = {}
-- include_label because i implemented the label then realized item buttons did it themselves
local function make_formspec(name, items, include_label)
if items == nil then
return nil
end
local form = formspec_base
if include_label then
form = formspec_base_label
end
-- color strip cause yellow is unreadible with default styling
form = form:gsub("L", minetest.formspec_escape(minetest.strip_colors(name)))
for i, v in ipairs(items) do
local x = (i - 1) % 9
local y = 1 + math.floor((i - 1) / 9) -- +1 for the shulker name
if include_label then
y = y + (y * 0.2) -- shifts each layer down a bit
end
local it = formspec_item
if include_label then
it = formspec_item_label
end
it = it:gsub("X", x)
it = it:gsub("Y", y)
if include_label then
it = it:gsub("I", v:get_name())
it = it:gsub("Z", y + 0.8)
it = it:gsub("T", v:get_count())
else
it = it:gsub("I", v:get_name() .. " " .. tostring(v:get_count()))
end
local item_name = "button" .. tostring(i)
it = it:gsub("N", item_name)
if minetest.get_item_def(v:get_name()).description ~= v:get_description() then
it = it .. "tooltip[" .. item_name .. ";" .. v:get_description() .. "]"
end
form = form .. it
end
return form
end
local function get_items(item)
local meta = item:get_metadata()
local list = minetest.deserialize(meta)
if list == nil then
return
end
local items = map(ItemStack, list)
return items
end
local function make_list(name, items, prevent_push)
local fs = make_formspec(name, items)
if not prevent_push then
table.insert(inventories, {name = name, items = items})
end
if fs ~= nil then
minetest.show_formspec("PeekInventory", fs)
end
end
local function show_form(shulker)
make_list(shulker:get_description(), get_items(shulker))
end
local function top(list)
return list[#list]
end
minetest.register_on_formspec_input(function(formname, fields)
if formname == "PeekInventory" then
if fields.quit then
inventories = {}
return true
end
if fields.up and #inventories > 1 then
table.remove(inventories)
local t = top(inventories)
make_list(t.name, t.items, true)
return true
end
for k, v in pairs(fields) do
if k:find("button") then
local idx = tonumber(k:match("([0-9]+)"))
local item = top(inventories).items[idx]
local iname = item:get_name()
if iname:find("mcl_chests:.-_shulker_box") then
show_form(top(inventories).items[idx])
return true
elseif iname:find("mcl_books:.-written_book") then
-- to be implemented with bookbot
-- bookbot.read(item)
end
end
end
end
end)
minetest.register_chatcommand("peek", {
description = "Peek inside a Mineclone Shulker box.",
func = function()
show_form(minetest.localplayer:get_wielded_item())
end
})

288
clientmods/quint/init.lua Normal file
View File

@ -0,0 +1,288 @@
-- CC0/Unlicense Emilia 2020
--[[
invaction
place
dig
interact
--]]
-- invaction stuff needs to change indices to 1 indexed soon(tm)
-- Queue Interact
-- think of it like minetest.after() but with automatic time calculation and a bit more parallel
quint = {}
-- New empty invaction quint
function quint.invaction_new()
return {start = {}, q = {}, current = {}, last = 0, index = 1}
end
-- Get a global index for an inventory location from part of an invaction
local function format_inv(taction)
return taction.location .. ";" .. taction.inventory
end
-- Split a global inventory index
local function parse_inv(inv)
local spl = string.split(inv, ";")
return {location = spl[1], inventory = spl[2]}
end
-- Get some useful things from an invaction
local function parse_invaction(lists, taction)
local idx = format_inv(taction)
local slot = taction.slot
local itemstack = lists[idx][slot]
return idx, slot, itemstack
end
-- Simulates an inventory action, performing the collateral operations
local function simulate_invaction(lists, invaction)
local fidx, fslot, fis = parse_invaction(lists, invaction:to_table().from)
local tidx, tslot, tis = parse_invaction(lists, invaction:to_table().to)
local tcount = invaction:to_table().count
if tcount == 0 then
tcount = fis:get_count()
end
-- can't do anything
if fis:is_empty() then
return
end
-- dump
if tis:is_empty() then
lists[tidx][tslot] = fis
lists[fidx][fslot] = ItemStack()
return
end
-- swap
if ((fis:get_name() ~= tis:get_name())
or (fis:get_name() == tis:get_name()
and tcount > tis:get_free_space())
or (tcount > tis:get_free_space())) then
local t = fis
lists[fidx][fslot] = tis
lists[tidx][tslot] = t
return
end
-- fill
if fis:get_name() == tis:get_name() and tcount <= tis:get_free_space() then
local count = math.min(fis:get_count(), tis:get_free_space(), tcount)
lists[tidx][tslot]:set_count(tis:get_count() + count)
if fis:get_count() - count == 0 then
lists[fidx][fslot] = ItemStack()
else
lists[fidx][fslot]:set_count(fis:get_count() - count)
end
return
end
end
-- Deepcopy an inventory list
local function invlist_copy(list)
local o = {}
for i, v in ipairs(list) do
o[i] = ItemStack(v:to_string())
end
return o
end
-- Add an invlist to a invaction quint if not there already
local function insert_invlist(q, taction)
local idx = format_inv(taction)
if q.start[idx] == nil then
local mdata = minetest.get_inventory(taction.location)
if mdata then
q.start[idx] = mdata[taction.inventory]
q.current[idx] = invlist_copy(mdata[taction.inventory])
end
end
end
-- Enqueue and preview an InventoryAction
function quint.invaction_enqueue(q, invaction)
insert_invlist(q, invaction:to_table().from)
insert_invlist(q, invaction:to_table().to)
table.insert(q.q, invaction)
simulate_invaction(q.current, invaction)
end
-- Dump a slot into destination, perform another dump if there is extra
local function invaction_dump_slot(q, src, dst, srci, dstbounds)
local empty
local matching
local sinv = q.current[format_inv(src)]
local dinv = q.current[format_inv(dst)]
if sinv[srci]:is_empty() then
return true
end
for i = dstbounds.min, dstbounds.max do
if not empty and dinv[i]:is_empty() then
empty = i
end
if not matching and dinv[i]:get_name() == sinv[srci]:get_name() then
if dinv[i]:get_free_space() ~= 0 then
matching = i
end
end
if matching and empty then
break
end
end
if matching then
local free = dinv[matching]:get_free_space()
local scount = sinv[srci]:get_count()
local count = math.min(free, scount)
local act = InventoryAction("move")
act:from(src.location, src.inventory, srci)
act:to(dst.location, dst.inventory, matching)
act:set_count(count)
quint.invaction_enqueue(q, act)
if scount > free then
return invaction_dump_slot(q, src, dst, srci, dstbounds)
end
return true
elseif empty then
local act = InventoryAction("move")
act:from(src.location, src.inventory, srci)
act:to(dst.location, dst.inventory, empty)
quint.invaction_enqueue(q, act)
return true
else
return false
end
end
local function rebind(lists, inv, bounds)
local invlist = lists[format_inv(inv)]
if not bounds then
bounds = {min = 0, max = 0}
end
if bounds.max == 0 then
bounds.max = #invlist
end
bounds.min = math.max(bounds.min, 1)
bounds.max = math.min(bounds.max, #invlist)
return bounds
end
-- Dump from src to dst
-- src and dest are in the format of {location = "", inventory = ""}
-- like {location = "current_player", inventory = "main"}
-- srcbounds and dstbounds are inclusive beginning and ends to the selectible invslots
-- in the form {min = n, max = n}, if max is 0 it is changed to the maximum index
function quint.invaction_dump(q, src, dst, srcbounds, dstbounds)
if src.location .. src.inventory == dst.location .. dst.inventory then
return
end
insert_invlist(q, src)
insert_invlist(q, dst)
srcbounds = rebind(q.current, src, srcbounds)
dstbounds = rebind(q.current, dst, dstbounds)
for i = srcbounds.min, srcbounds.max do
if not invaction_dump_slot(q, src, dst, i, dstbounds) then
return
end
end
return
end
-- Remake a invaction quint up to index, refreshing the starts
function quint.invaction_remake(q, index)
local t = quint.invaction_new()
for i = 1, index do
invaction_enqueue(t, q.q[i])
end
return t
end
-- Preview an invaction quint after the first n (index) actions
function quint.invaction_view_state_at(q, index)
local t = quint.invaction_remake(q, index)
return t.current
end
-- Refresh starts and get new currents
function quint.invaction_refresh(q)
local t = quint.invaction_remake(q, #q.q)
q.start = t.start
q.current = t.current
end
quint.invaction_gsteps = {}
-- Apply an invaction quint, with optional delay
function quint.invaction_apply(q, delay)
q.delay = delay
if not delay or delay == 0 then
for i, v in ipairs(q.q) do
v:apply()
end
return
end
table.insert(quint.invaction_gsteps, q)
end
minetest.register_globalstep(function()
local dead = {}
local ctime = os.clock()
for i, v in ipairs(quint.invaction_gsteps) do
if not v.delay or ctime >= v.last + v.delay then
if v.delay then
v.last = v.last + v.delay
end
if v.q[v.index] then
v.q[v.index]:apply()
v.index = v.index + 1
else
table.insert(dead, 1, i)
end
end
end
for i, v in ipairs(dead) do
table.remove(quint.invaction_gsteps, v)
end
end)

View File

@ -0,0 +1,399 @@
-- CC0/Unlicense system32 2020
--[[
Commands:
.qb_add_commander player
.qb_list_commanders
.qb_del_commander player
.qb_set ID quote
.qb_list
.qb_say <ID>
.qb_direct player <ID>
.qb_chance num/player <num>
.qb_enable
.qb_disable
-- requires unsafe
.qb_export file
.qb_import file
--]]
local storage = minetest.get_mod_storage()
local function storage_init_table(key)
if storage:get(key) == nil or storage:get(key) == "null" then
storage:set_string(key, "{}")
end
return minetest.parse_json(storage:get_string(key))
end
local function storage_save_json(key, value)
storage:set_string(key, minetest.write_json(value))
end
quotebot = {}
quotebot.quotes = storage_init_table("quotebot_quotes")
quotebot.chance = tonumber(storage:get_string("quotebot_chance")) or 0
quotebot.chances = storage_init_table("quotebot_chances")
quotebot.commanders = storage_init_table("quotebot_commanders")
quotebot.enabled = false
function quotebot.save()
storage_save_json("quotebot_quotes", quotebot.quotes)
storage:set_string("quotebot_chance", tostring(quotebot.chance))
storage_save_json("quotebot_chances", quotebot.chances)
storage_save_json("quotebot_commanders", quotebot.commanders)
storage:set_bool("quotebot_enabled", quotebot.enabled)
end
local function localize_player(player)
if player == nil then
return nil
end
local info = minetest.get_server_info()
local name = info.ip
if info.address ~= "" then
name = info.address
end
return player .. "@" .. info.ip .. ":" .. info.port
end
local function say(id, prefix)
if quotebot.enabled then
minetest.send_chat_message((prefix or "") .. quotebot.quotes[id])
end
end
local function index_table(t, ti)
local i = 1
for k, v in pairs(t) do
if i == ti then
return k, v
end
i = i + 1
end
end
local function in_list(list, value)
for i, v in ipairs(list) do
if v == value then
return true
end
end
return false
end
local function table_size(t)
local n = 0
for k, v in pairs(t) do
n = n + 1
end
return n
end
local function rand_say(trigger, id, prefix)
id = id or index_table(quotebot.quotes, math.random(table_size(quotebot.quotes)))
if trigger.type == "message" or trigger.type == "api" then
local chance = quotebot.chances[trigger.player] or quotebot.chance
if math.random() <= chance then
say(id, prefix)
end
elseif trigger.type == "command" or trigger.type == "api_command" then
say(id, prefix)
elseif trigger.type == "dm" and trigger.content == "say" and in_list(trigger.player, quotebot.commanders) then
say(id, prefix)
end
end
function quotebot.force_say(id, prefix)
rand_say({
type = "api_command",
player = "",
content = ""
}, id, prefix)
end
function quotebot.say(id, prefix)
rand_say({
type = "api",
player = "",
content = ""
}, id, prefix)
end
minetest.register_on_mods_loaded(function()
math.randomseed(os.time())
end)
minetest.register_on_receiving_chat_message(function(message)
local dm_player, dm_content = message:match(".*rom (.-): (.*)")
local message_player, message_content= message:match("<(.-)> (.*)")
local mtype = "message"
local player = localize_player(message_player)
local content = message_content
if dm_player then
mtype = "dm"
player = localize_player(dm_player)
content = dm_content
end
rand_say({
type = mtype,
player = player,
content = content
})
end)
local function set_append(set, value)
if not in_list(set, value) then
set[#set + 1] = value
end
end
local function list_remove(list, value)
local out = {}
for i, v in ipairs(list) do
if v ~= value then
out[#out] = value
end
end
return out
end
local function noplayer()
minetest.display_chat_message("No player specified.")
end
local function parse_player(params)
local player = string.split(params, " ")[1]
if player == nil then
noplayer()
return
end
return player
end
local function get_keys(t)
local out = {}
for k, v in pairs(t) do
out[#out + 1] = k
end
return out
end
minetest.register_chatcommand("qb_add_commander", {
params = "<player>",
description = "Add a user who can DM 'say' to the bot to force it to say a random quote.",
func = function(params)
local player = parse_player(params)
if player then
set_append(quotebot.commanders, player)
quotebot.save()
end
end
})
minetest.register_chatcommand("qb_list_commanders", {
description = "List users who can force the bot to say a random quote.",
func = function(params)
minetest.display_chat_message(table.concat(quotebot.commanders, ", "))
end
})
minetest.register_chatcommand("qb_del_commander", {
params = "<player>",
description = "Remove a commander.",
func = function(params)
local player = parse_player(params)
if player then
list_remove(quotebot.commanders, player)
quotebot.save()
end
end
})
minetest.register_chatcommand("qb_set", {
params = "<id> <quote>",
description = "Set a quote ID to a value, omit quote to remove the quote.",
func = function(params)
local id, quote = params:match("(.-) (.*)")
if id == "" then
minetest.display_chat_message("ID not specified.")
return
end
if quote == "" then
quote = nil
end
quotebot.quotes[id] = quote
quotebot.save()
end
})
minetest.register_chatcommand("qb_list", {
description = "List quote IDs.",
func = function(params)
minetest.display_chat_message(table.concat(get_keys(quotebot.quotes), ", "))
end
})
minetest.register_chatcommand("qb_show", {
params = "<ID>",
description = "Show a quote",
func = function(params)
minetest.display_chat_message(quotebot.quotes[params] or "No quote with specified ID.")
end
})
minetest.register_chatcommand("qb_say", {
params = "<ID>",
description = "Say a random quote (or specific if ID is specified).",
func = function(params)
if params ~= "" then
rand_say({
type = "command",
player = "",
content = ""
}, params)
else
rand_say({
type = "command",
player = "",
content = ""
})
end
end
})
minetest.register_chatcommand("qb_direct", {
params = "<player> <ID>",
description = "Say a random quote (or specific if ID is specified) directed towards a player.",
func = function(params)
local player = parse_player(params)
if player then
if params ~= "" then
rand_say({
type = "command",
player = "",
content = ""
}, params, player .. ": ")
else
rand_say({
type = "command",
player = "",
content = ""
}, nil, player .. ": ")
end
end
end
})
minetest.register_chatcommand("qb_chance", {
params = "<num/player> <num>",
description = "Set the say chance to number (0.percentage). If first is num, set global chance. If first is player and second is num then set player's chance. If first is player and second num is not set, clear player chance.",
func = function(params)
local plist = string.split(params, " ")
local first_num = tonumber(plist[1] or "")
local player = plist[1]
local second_num = tonumber(plist[2] or "")
if first_num then
quotebot.chance = first_num
elseif player then
quotebot.chances[player] = second_num
end
quotebot.save()
end
})
minetest.register_chatcommand("qb_enable", {
description = "Enable the quotebot.",
func = function(params)
quotebot.enable = true
quotebot.save()
end
})
minetest.register_chatcommand("qb_disable", {
description = "Disable the quotebot.",
func = function(params)
quotebot.enable = false
quotebot.save()
end
})
if minetest.settings:get_bool("quotebot_export") then
if minetest.request_insecure_environment == nil then
error("Request insecure environment not accessible. Apply patches or disable quotebot_export.")
end
local env = minetest.request_insecure_environment()
if env == nil then
error("Could not get an insecure environment, is quotebot in trusted mods?")
end
function quotebot.export(file)
fdesc = io.open(file, "w")
fdesc:write(minetest.write_json({
quotes = quotebot.quotes,
chance = quotebot.chance,
chances = quotebot.chances,
commanders = quotebot.commanders,
enabled = quotebot.enabled
}))
fdesc:close()
end
function quotebot.import(file)
fdesc = io.open(file, "r")
local t = minetest.parse_json(fdesc:read("*all")) or {}
fdesc:close()
quotebot.quotes = t.quotes or {}
quotebot.chance = t.chance or 0
quotebot.chances = t.chances or {}
quotebot.commanders = t.commanders or {}
quotebot.enabled = t.enabled or false
quotebot.save()
end
minetest.register_chatcommand("qb_export", {
params = "<file>",
description = "Export quote setup to a file.",
func = function(params)
quotebot.export(params)
end
})
minetest.register_chatcommand("qb_import", {
params = "<file>",
description = "Import quote setup from a file.",
func = function(params)
quotebot.import(params)
end
})
end

View File

@ -0,0 +1,2 @@
name = quotebot
description = Randomly say quotes when users send messages.

View File

@ -0,0 +1,45 @@
---
-- random screenshots
randomscreenshot = {}
local function init_settings(setting_table)
for k, v in pairs(setting_table) do
if minetest.settings:get(k) == nil then
if type(v) == "boolean" then
minetest.settings:set_bool(k, v)
else
minetest.settings:set(k, v)
end
end
end
end
init_settings({
randomscreenshot_interval = 10,
randomscreenshot_rnd = 10
})
local nextsc=0
minetest.register_globalstep(function()
if not minetest.settings:get_bool("randomsc") then return end
if os.time() < nextsc then return end
math.randomseed(os.clock())
nextsc=os.time() + ( minetest.settings:get('randomscreenshot_interval') * 60 ) + math.random(minetest.settings:get('randomscreenshot_rnd') * 60)
minetest.after("15.0",function()
minetest.hide_huds()
--minetest.display_chat_message("\n\n\n\n\n\n\n\n\n")
minetest.after("0.05",minetest.take_screenshot)
minetest.after("0.1",function()
minetest.show_huds()
end)
end)
end)
if (_G["minetest"]["register_cheat"] ~= nil) then
minetest.register_cheat("Random Screenshot", "World", "randomsc")
else
minetest.settings:set_bool('randomsc',true)
end

View File

@ -0,0 +1,3 @@
name = randomscreenshot
author = cora
description = take screenshots at random intervals

View File

@ -0,0 +1,4 @@
randomsc (Enable Autoscreenshot) bool false
randomscreenshot_interval ( Minimum time to wait between screenshots in minutes ) int 10
randomscreenshot_rnd (Random time between screenshots in minutes ) int 10