Compare commits
55 Commits
master
...
chat-comma
Author | SHA1 | Date |
---|---|---|
AFCMS | 2c82629cf6 | |
AFCMS | b891a13787 | |
AFCMS | 0c32c94f73 | |
AFCMS | e463d16298 | |
AFCMS | 0a35714019 | |
AFCMS | 0a802a2cc1 | |
AFCMS | 5ade58f919 | |
AFCMS | 25495d2dcd | |
AFCMS | e0fcfc1888 | |
AFCMS | 698b107cb3 | |
AFCMS | b9c0dc80f3 | |
AFCMS | 43d398b0b2 | |
AFCMS | 163aa720c2 | |
AFCMS | e7d3939e28 | |
AFCMS | 6f0777d36d | |
AFCMS | 9b0f2c4cbf | |
AFCMS | c307b5304f | |
AFCMS | 16529330dd | |
AFCMS | 8eebdd3461 | |
AFCMS | 073707f53c | |
AFCMS | d7ac1b7cb5 | |
AFCMS | d00a5c74d2 | |
AFCMS | f9a03a3c98 | |
AFCMS | 0c96ff672d | |
AFCMS | 46677a38ae | |
AFCMS | f8804c5e56 | |
AFCMS | 3137c86f67 | |
AFCMS | 2a28bf215d | |
AFCMS | 86f01eed75 | |
AFCMS | a51bbb6974 | |
AFCMS | 11384bd73c | |
AFCMS | 669a9ff0a4 | |
AFCMS | 783146f32e | |
AFCMS | 31e256e1e2 | |
AFCMS | 5f00d47ec2 | |
AFCMS | 0d3147b13d | |
AFCMS | a22188ccf4 | |
AFCMS | f90243f6e5 | |
AFCMS | 884097a8e5 | |
AFCMS | 72ddaf33f6 | |
AFCMS | 19e83fc2fb | |
AFCMS | 43c641f84f | |
AFCMS | 155548f384 | |
AFCMS | f7b832508f | |
AFCMS | 9e7ec24c0e | |
AFCMS | 5b5b525d32 | |
AFCMS | e334665365 | |
AFCMS | 88a971fe6f | |
AFCMS | 5425a01097 | |
AFCMS | 1f5076cfd0 | |
AFCMS | 7139ca1395 | |
AFCMS | 84de4ea728 | |
AFCMS | 59ab7e6ae6 | |
AFCMS | d5874b4062 | |
AFCMS | d72fa76757 |
|
@ -1,2 +0,0 @@
|
||||||
# Text Editor TMP Files
|
|
||||||
*.swp
|
|
55
.luacheckrc
|
@ -1,55 +0,0 @@
|
||||||
unused_args = false
|
|
||||||
allow_defined_top = true
|
|
||||||
max_line_length = false
|
|
||||||
redefined = false
|
|
||||||
|
|
||||||
globals = {
|
|
||||||
"minetest", "core",
|
|
||||||
}
|
|
||||||
|
|
||||||
read_globals = {
|
|
||||||
"DIR_DELIM",
|
|
||||||
"dump", "dump2",
|
|
||||||
"vector",
|
|
||||||
"VoxelManip", "VoxelArea",
|
|
||||||
"PseudoRandom", "PcgRandom", "PerlinNoise", "PerlinNoiseMap",
|
|
||||||
"ItemStack",
|
|
||||||
"Settings",
|
|
||||||
"unpack",
|
|
||||||
|
|
||||||
table = {
|
|
||||||
fields = {
|
|
||||||
"copy",
|
|
||||||
"indexof",
|
|
||||||
"insert_all",
|
|
||||||
"key_value_swap",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
string = {
|
|
||||||
fields = {
|
|
||||||
"split",
|
|
||||||
"trim",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
math = {
|
|
||||||
fields = {
|
|
||||||
"hypot",
|
|
||||||
"sign",
|
|
||||||
"factorial"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
------
|
|
||||||
--MODS
|
|
||||||
------
|
|
||||||
|
|
||||||
--GENERAL
|
|
||||||
"default",
|
|
||||||
|
|
||||||
--ENTITIES
|
|
||||||
"cmi",
|
|
||||||
|
|
||||||
--HUD
|
|
||||||
"sfinv", "sfinv_buttons", "unified_inventory", "cmsg", "inventory_plus",
|
|
||||||
}
|
|
|
@ -1,128 +0,0 @@
|
||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
We as members, contributors, and leaders pledge to make participation in our
|
|
||||||
community a harassment-free experience for everyone, regardless of age, body
|
|
||||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
||||||
identity and expression, level of experience, education, socio-economic status,
|
|
||||||
nationality, personal appearance, race, religion, or sexual identity
|
|
||||||
and orientation.
|
|
||||||
|
|
||||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
||||||
diverse, inclusive, and healthy community.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to a positive environment for our
|
|
||||||
community include:
|
|
||||||
|
|
||||||
* Demonstrating empathy and kindness toward other people
|
|
||||||
* Being respectful of differing opinions, viewpoints, and experiences
|
|
||||||
* Giving and gracefully accepting constructive feedback
|
|
||||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
||||||
and learning from the experience
|
|
||||||
* Focusing on what is best not just for us as individuals, but for the
|
|
||||||
overall community
|
|
||||||
|
|
||||||
Examples of unacceptable behavior include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery, and sexual attention or
|
|
||||||
advances of any kind
|
|
||||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or email
|
|
||||||
address, without their explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
|
||||||
professional setting
|
|
||||||
|
|
||||||
## Enforcement Responsibilities
|
|
||||||
|
|
||||||
Community leaders are responsible for clarifying and enforcing our standards of
|
|
||||||
acceptable behavior and will take appropriate and fair corrective action in
|
|
||||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
||||||
or harmful.
|
|
||||||
|
|
||||||
Community leaders have the right and responsibility to remove, edit, or reject
|
|
||||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
||||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
||||||
decisions when appropriate.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies within all community spaces, and also applies when
|
|
||||||
an individual is officially representing the community in public spaces.
|
|
||||||
Examples of representing our community include using an official e-mail address,
|
|
||||||
posting via an official social media account, or acting as an appointed
|
|
||||||
representative at an online or offline event.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
||||||
reported to the community leaders responsible for enforcement at
|
|
||||||
eliasfleckenstein@web.de.
|
|
||||||
All complaints will be reviewed and investigated promptly and fairly.
|
|
||||||
|
|
||||||
All community leaders are obligated to respect the privacy and security of the
|
|
||||||
reporter of any incident.
|
|
||||||
|
|
||||||
## Enforcement Guidelines
|
|
||||||
|
|
||||||
Community leaders will follow these Community Impact Guidelines in determining
|
|
||||||
the consequences for any action they deem in violation of this Code of Conduct:
|
|
||||||
|
|
||||||
### 1. Correction
|
|
||||||
|
|
||||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
||||||
unprofessional or unwelcome in the community.
|
|
||||||
|
|
||||||
**Consequence**: A private, written warning from community leaders, providing
|
|
||||||
clarity around the nature of the violation and an explanation of why the
|
|
||||||
behavior was inappropriate. A public apology may be requested.
|
|
||||||
|
|
||||||
### 2. Warning
|
|
||||||
|
|
||||||
**Community Impact**: A violation through a single incident or series
|
|
||||||
of actions.
|
|
||||||
|
|
||||||
**Consequence**: A warning with consequences for continued behavior. No
|
|
||||||
interaction with the people involved, including unsolicited interaction with
|
|
||||||
those enforcing the Code of Conduct, for a specified period of time. This
|
|
||||||
includes avoiding interactions in community spaces as well as external channels
|
|
||||||
like social media. Violating these terms may lead to a temporary or
|
|
||||||
permanent ban.
|
|
||||||
|
|
||||||
### 3. Temporary Ban
|
|
||||||
|
|
||||||
**Community Impact**: A serious violation of community standards, including
|
|
||||||
sustained inappropriate behavior.
|
|
||||||
|
|
||||||
**Consequence**: A temporary ban from any sort of interaction or public
|
|
||||||
communication with the community for a specified period of time. No public or
|
|
||||||
private interaction with the people involved, including unsolicited interaction
|
|
||||||
with those enforcing the Code of Conduct, is allowed during this period.
|
|
||||||
Violating these terms may lead to a permanent ban.
|
|
||||||
|
|
||||||
### 4. Permanent Ban
|
|
||||||
|
|
||||||
**Community Impact**: Demonstrating a pattern of violation of community
|
|
||||||
standards, including sustained inappropriate behavior, harassment of an
|
|
||||||
individual, or aggression toward or disparagement of classes of individuals.
|
|
||||||
|
|
||||||
**Consequence**: A permanent ban from any sort of public interaction within
|
|
||||||
the community.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
||||||
version 2.0, available at
|
|
||||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
|
||||||
|
|
||||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
|
||||||
enforcement ladder](https://github.com/mozilla/diversity).
|
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see the FAQ at
|
|
||||||
https://www.contributor-covenant.org/faq. Translations are available at
|
|
||||||
https://www.contributor-covenant.org/translations.
|
|
452
CONTRIBUTING.md
|
@ -1,414 +1,96 @@
|
||||||
# Contributing to MineClone2
|
# Contributing to MineClone 2
|
||||||
So you want to contribute to MineClone2?
|
So you want to MineClone 2?
|
||||||
Wow, thank you! :-)
|
Wow, thank you! :-)
|
||||||
|
|
||||||
MineClone2 is maintained by Nicu and Fleckenstein. If you have any
|
But first, some things to note:
|
||||||
problems or questions, contact us (See Links section below).
|
|
||||||
|
|
||||||
You can help with MineClone2's development in many different ways,
|
MineClone 2's development target is to make a free software clone of Minecraft,
|
||||||
whether you're a programmer or not.
|
***version 1.12***, ***PC edition***, *** + Optifine features supported by the Minetest Engine ***.
|
||||||
|
|
||||||
## MineClone2's development target is to...
|
MineClone 2 is maintained by two persons. Namely, kay27 and EliasFleckenstein. You can find us
|
||||||
- Crucially, create a stable, moddable, free/libre clone of Minecraft
|
in the Minetest forums (forums.minetest.net), in IRC in the #minetest
|
||||||
based on the Minetest engine with polished features, usable in both
|
channel on irc.freenode.net. And finally, you can send e-mails to
|
||||||
singleplayer and multiplayer. Currently, most of **Minecraft Java
|
<eliasfleckenstein@web.de> or <kay27@bk.ru>.
|
||||||
Edition 1.12.2** features are already implemented and polishing existing
|
|
||||||
features are prioritized over new feature requests.
|
|
||||||
- With lessened priority yet strictly, implement features targetting
|
|
||||||
**Minecraft version 1.17 + OptiFine** (OptiFine only as far as supported
|
|
||||||
by the Minetest Engine). This means features in parity with the listed
|
|
||||||
Minecraft experiences are prioritized over those that don't fulfill this
|
|
||||||
scope.
|
|
||||||
- Optionally, create a performant experience that will run relatively
|
|
||||||
well on really low spec computers. Unfortunately, due to Minecraft's
|
|
||||||
mechanisms and Minetest engine's limitations along with a very small
|
|
||||||
playerbase on low spec computers, optimizations are hard to investigate.
|
|
||||||
|
|
||||||
## Links
|
There is **no** guarantee we will accept anything from anybody.
|
||||||
* [Mesehub](https://git.minetest.land/MineClone2/MineClone2)
|
|
||||||
* [Discord](https://discord.gg/xE4z8EEpDC)
|
|
||||||
* [YouTube](https://www.youtube.com/channel/UClI_YcsXMF3KNeJtoBfnk9A)
|
|
||||||
* [IRC](https://web.libera.chat/#mineclone2)
|
|
||||||
* [Matrix](https://app.element.io/#/room/#mc2:matrix.org)
|
|
||||||
* [Reddit](https://www.reddit.com/r/MineClone2/)
|
|
||||||
* [Minetest forums](https://forum.minetest.net/viewtopic.php?f=50&t=16407)
|
|
||||||
* [ContentDB](https://content.minetest.net/packages/wuzzy/mineclone2/)
|
|
||||||
* [OpenCollective](https://opencollective.com/mineclone2)
|
|
||||||
|
|
||||||
## Using git
|
By sending us patches or asking us to include your changes in this game,
|
||||||
MineClone2 is developed using the version control system
|
you agree that they fall under the terms of the LGPLv2.1, which basically
|
||||||
[git](https://git-scm.com/). If you want to contribute code to the
|
means they will become part of a free software.
|
||||||
project, it is **highly recommended** that you learn the git basics.
|
|
||||||
For non-programmers and people who do not plan to contribute code to
|
|
||||||
MineClone2, git is not required. However, git is a tool that will be
|
|
||||||
referenced frequently because of its usefulness. As such, it is valuable
|
|
||||||
in learning how git works and its terminology. It can also help you
|
|
||||||
keeping your game updated, and easily test pull requests.
|
|
||||||
|
|
||||||
## How you can help as a non-programmer
|
## The suggested workflow
|
||||||
|
We don't **dictate** your workflow, but in order to work with us in an efficient
|
||||||
|
way, you can follow these suggestions:
|
||||||
|
|
||||||
As someone who does not know how to write programs in Lua or does not
|
For small and medium changes:
|
||||||
know how to use the Minetest API, you can still help us out a lot. For
|
|
||||||
example, by opening an issue in the
|
|
||||||
[Issue tracker](https://git.minetest.land/MineClone2/MineClone2/issues),
|
|
||||||
you can report a bug or request a feature.
|
|
||||||
|
|
||||||
### Rules about both bugs and feature requests
|
* Fork the repository
|
||||||
* Stay polite towards the developers and anyone else involved in the
|
|
||||||
discussion.
|
|
||||||
* Choose a descriptive title (e.g. not just "crash", "bug" or "question"
|
|
||||||
).
|
|
||||||
* Please write in plain, understandable English. It will be easier to
|
|
||||||
communicate.
|
|
||||||
* Please start the issue title with a capital letter.
|
|
||||||
* Always check the currently opened issues before creating a new one.
|
|
||||||
Don't report bugs that have already been reported or request features
|
|
||||||
that already have been requested.
|
|
||||||
* If you know about Minetest's inner workings, please think about
|
|
||||||
whether the bug / the feature that you are reporting / requesting is
|
|
||||||
actually an issue with Minetest itself, and if it is, head to the
|
|
||||||
[Minetest issue tracker](https://github.com/minetest/minetest/issues)
|
|
||||||
instead.
|
|
||||||
* If you need any help regarding creating a Mesehub account or opening
|
|
||||||
an issue, feel free to ask on the Discord / Matrix server or the IRC
|
|
||||||
channel.
|
|
||||||
|
|
||||||
### Reporting bugs
|
|
||||||
* A bug is an unintended behavior or, in the worst case, a crash.
|
|
||||||
However, it is not a bug if you believe something is missing in the
|
|
||||||
game. In this case, please read "Requesting features"
|
|
||||||
* If you report a crash, always include the error message. If you play
|
|
||||||
in singleplayer, post a screenshot of the message that Minetest showed
|
|
||||||
when the crash happened (or copy the message into your issue). If you
|
|
||||||
are a server admin, you can find error messages in the log file of the
|
|
||||||
server.
|
|
||||||
* Tell us which MineClone2 and Minetest versions you are using.
|
|
||||||
* Tell us how to reproduce the problem: What you were doing to trigger
|
|
||||||
the bug, e.g. before the crash happened or what causes the faulty
|
|
||||||
behavior.
|
|
||||||
|
|
||||||
### Requesting features
|
|
||||||
* Ensure the requested feature fulfills our development targets and
|
|
||||||
goals.
|
|
||||||
* Begging or excessive attention seeking does not help us in the
|
|
||||||
slightest, and may very well disrupt MineClone2 development. It's better
|
|
||||||
to put that energy into helping or researching the feature in question.
|
|
||||||
After all, we're just volunteers working on our spare time.
|
|
||||||
* Ensure the requested feature has not been implemented in MineClone2
|
|
||||||
latest or development versions.
|
|
||||||
|
|
||||||
### Testing code
|
|
||||||
If you want to help us with speeding up MineClone2 development and
|
|
||||||
making the game more stable, a great way to do that is by testing out
|
|
||||||
new features from contributors. For most new things that get into the
|
|
||||||
game, a pull request is created. A pull request is essentially a
|
|
||||||
programmer saying "Look, I modified the game, please apply my changes
|
|
||||||
to the upstream version of the game". However, every programmer makes
|
|
||||||
mistakes sometimes, some of which are hard to spot. You can help by
|
|
||||||
downloading this modified version of the game and trying it out - then
|
|
||||||
tell us if the code works as expected without any issues. Ideally, you
|
|
||||||
would report issues will pull requests similar to when you were
|
|
||||||
reporting bugs that are the mainline (See Reporting bugs section). You
|
|
||||||
can find currently open pull requests here:
|
|
||||||
<https://git.minetest.land/MineClone2/MineClone2/pulls>. Note that pull
|
|
||||||
requests that start with a `WIP:` are not done yet, and therefore might
|
|
||||||
not work, so it's not very useful to try them out yet.
|
|
||||||
|
|
||||||
### Contributing assets
|
|
||||||
Due to license problems, MineClone2 unfortunately cannot use
|
|
||||||
Minecraft's assets, therefore we are always looking for asset
|
|
||||||
contributions. To contribute assets, it can be useful to learn git
|
|
||||||
basics and read the section for Programmers of this document, however
|
|
||||||
this is not required. It's also a good idea to join the Discord server
|
|
||||||
(or alternatively IRC or Matrix).
|
|
||||||
|
|
||||||
#### Textures
|
|
||||||
For textures we use the Pixel Perfection texture pack. This is mostly
|
|
||||||
enough; however in some cases - e.g. for newer Minecraft features, it's
|
|
||||||
useful to have texture artists around. If you want to make such
|
|
||||||
contributions, join our Discord server. Demands for textures will be
|
|
||||||
communicated there.
|
|
||||||
|
|
||||||
#### Sounds
|
|
||||||
MineClone2 currently does not have a consistent way to handle sounds.
|
|
||||||
The sounds in the game come from different sources, like the SnowZone
|
|
||||||
resource pack or minetest_game. Unfortunately, MineClone2 does not play
|
|
||||||
a sound in every situation you would get one in Minecraft. Any help with
|
|
||||||
sounds is greatly appreciated, however if you add new sounds you should
|
|
||||||
probably work together with a programmer, to write the code to actually
|
|
||||||
play these sounds in game.
|
|
||||||
|
|
||||||
#### 3D Models
|
|
||||||
Most of the 3D Models in MineClone2 come from
|
|
||||||
[22i's repository](https://github.com/22i/minecraft-voxel-blender-models).
|
|
||||||
Similar to the textures, we need people that can make 3D Models with
|
|
||||||
Blender on demand. Many of the models have to be patched, some new
|
|
||||||
animations have to be added etc.
|
|
||||||
|
|
||||||
#### Crediting
|
|
||||||
Asset contributions will be credited in their own respective sections in
|
|
||||||
CREDITS.md. If you have commited the results yourself, you will also be
|
|
||||||
credited in the Contributors section.
|
|
||||||
|
|
||||||
### Contributing Translations
|
|
||||||
|
|
||||||
#### Workflow
|
|
||||||
To add/update support for your language to MineClone2, you should take
|
|
||||||
the steps documented in the section for Programmers, add/update the
|
|
||||||
translation files of the mods that you want to update. You can add
|
|
||||||
support for all mods, just some of them or only one mod; you can update
|
|
||||||
the translation file entirely or only partly; basically any effort is
|
|
||||||
valued. If your changes are small, you can also send them to developers
|
|
||||||
via E-Mail, Discord, IRC or Matrix - they will credit you appropriately.
|
|
||||||
|
|
||||||
#### Things to note
|
|
||||||
You can use the script at `tools/check_translate_files.py` to compare
|
|
||||||
the translation files for the language you are working on with the
|
|
||||||
template files, to see what is missing and what is out of date with
|
|
||||||
the template file. However, template files are often incomplete and/or
|
|
||||||
out of date, sometimes they don't match the code. You can update the
|
|
||||||
translation files if that is required, you can also modify the code in
|
|
||||||
your translation PR if it's related to translation. You can also work on
|
|
||||||
multiple languages at the same time in one PR.
|
|
||||||
|
|
||||||
#### Crediting
|
|
||||||
Translation contributions will be credited in their own in CREDITS.md.
|
|
||||||
If you have commited the results yourself, you will also be credited in
|
|
||||||
the Contributors section.
|
|
||||||
|
|
||||||
### Profiling
|
|
||||||
If you own a server, a great way to help us improve MineClone2's code
|
|
||||||
is by giving us profiler results. Profiler results give us detailed
|
|
||||||
information about the game's performance and let us know places to
|
|
||||||
investigate optimization issues. This way we can make the game faster.
|
|
||||||
|
|
||||||
#### Using Minetest's profiler
|
|
||||||
Minetest has a built in profiler. Simply set `profiler.load = true` in
|
|
||||||
your configuration file and restart the server. After running the server
|
|
||||||
for some time, just run `/profiler save` in chat - then you will find a
|
|
||||||
file in the world directory containing the results. Open a new issue and
|
|
||||||
upload the file. You can name the issue "<Server name> profiler
|
|
||||||
results".
|
|
||||||
|
|
||||||
### Let us know your opinion
|
|
||||||
It is always encouraged to actively contribute to issue discussions on
|
|
||||||
MeseHub, let us know what you think about a topic and help us make
|
|
||||||
decisions. Also, note that a lot of discussion takes place on the
|
|
||||||
Discord server, so it's definitely worth checking it out.
|
|
||||||
|
|
||||||
### Funding
|
|
||||||
You can help pay for our infrastructure (Mesehub) by donating to our
|
|
||||||
OpenCollective link (See Links section).
|
|
||||||
|
|
||||||
### Crediting
|
|
||||||
If you opened or have contributed to an issue, you receive the
|
|
||||||
`Community` role on our Discord (after asking for it).
|
|
||||||
OpenCollective Funders are credited in their own section in
|
|
||||||
`CREDITS.md` and receive a special role "Funder" on our discord (unless
|
|
||||||
they have made their donation Incognito).
|
|
||||||
|
|
||||||
## How you can help as a programmer
|
|
||||||
(Almost) all the MineClone2 development is done using pull requests.
|
|
||||||
|
|
||||||
### Recommended workflow
|
|
||||||
* Fork the repository (in case you have not already)
|
|
||||||
* Do your change in a new branch
|
* Do your change in a new branch
|
||||||
* Create a pull request to get your changes merged into master
|
* Upload the repository somewhere where it can be accessed from the Internet and
|
||||||
* Keep your pull request up to date by regularly merging upstream. It is
|
notify us
|
||||||
imperative that conflicts are resolved prior to merging the pull
|
|
||||||
request.
|
|
||||||
* After the pull request got merged, you can delete the branch
|
|
||||||
|
|
||||||
### Discuss first
|
For small changes, sending us a patch is also good.
|
||||||
If you feel like a problem needs to fixed or you want to make a new
|
|
||||||
feature, you could start writing the code right away and notifying us
|
|
||||||
when you're done, but it never hurts to discuss things first. If there
|
|
||||||
is no issue on the topic, open one. If there is an issue, tell us that
|
|
||||||
you'd like to take care of it, to avoid duplicate work.
|
|
||||||
|
|
||||||
### Don't hesitate to ask for help
|
For big changes: Same as above, but consider notifying us first to avoid
|
||||||
We appreciate any contributing effort to MineClone2. If you are a
|
duplicate work and possible tears of rejection. ;-)
|
||||||
relatively new programmer, you can reach us on Discord, Matrix or IRC
|
|
||||||
for questions about git, Lua, Minetest API, MineClone2 codebase or
|
|
||||||
anything related to MineClone2. We can help you avoid writing code that
|
|
||||||
would be deemed inadequate, or help you become familiar with MineClone2
|
|
||||||
better, or assist you use development tools.
|
|
||||||
|
|
||||||
### Maintain your own code, even if already got merged
|
For trusted people, we might give them direct commit access to this
|
||||||
Sometimes, your code may cause crashes or bugs - we try to avoid such
|
repository. In this case, you obviously don't need to fork, but you still
|
||||||
scenarios by testing every time before merging it, but if your merged
|
need to show your contributions align with the project goals. We still
|
||||||
work causes problems, we ask you fix the issues as soon as possible.
|
reserve the right to revert everything that we don't like.
|
||||||
|
For bigger changes, we strongly recommend to use feature branches and
|
||||||
|
discuss with me first.
|
||||||
|
|
||||||
### Changing Gameplay
|
Contributors will be credited in `README.md`.
|
||||||
Pull Requests that change gameplay have to be properly researched and
|
|
||||||
need to state their sources. These PRs also need Fleckenstein's approval
|
|
||||||
before they are merged.
|
|
||||||
You can use these sources:
|
|
||||||
|
|
||||||
* Minecraft code (Name the source file and line, however DONT post any
|
## Quality remarks
|
||||||
proprietary code). You can use
|
Again: There is ***no*** guarantee we will accept anything from anybody.
|
||||||
[MCP](https://minecraft.fandom.com/wiki/Programs_and_editors/Mod_Coder_Pack)
|
But we will gladly take in code from others when we feel it saves us work
|
||||||
to decompile Minecraft or look at
|
in the long run.
|
||||||
[Minestorm](https://github.com/Minestom/Minestom) code.
|
|
||||||
* Testing things inside of Minecraft (Attach screenshots / video footage
|
|
||||||
of the results)
|
|
||||||
* [Official Minecraft Wiki](https://minecraft.fandom.com/wiki/Minecraft_Wiki)
|
|
||||||
(Include a link to the specific page you used)
|
|
||||||
|
|
||||||
### Stick to our guidelines
|
### Inclusion criteria
|
||||||
|
Depending on what you add, the chances for inclusion vary:
|
||||||
|
|
||||||
#### Git Guidelines
|
### High chance for inclusion
|
||||||
* We use merge rather than rebase or squash merge
|
* Gameplay features in Minecraft which are missing in MineClone 2
|
||||||
* We don't use git submodules.
|
|
||||||
* Your commit names should be relatively descriptive, e.g. when saying
|
|
||||||
"Fix #issueid", the commit message should also contain the title of the
|
|
||||||
issue.
|
|
||||||
* Try to keep your commits as atomic as possible (advise, but completely
|
|
||||||
optional)
|
|
||||||
|
|
||||||
#### Code Guidelines
|
### Medium chance for inclusion (discuss first)
|
||||||
* Each mod must provide `mod.conf`.
|
* Features which don't a impact on gameplay
|
||||||
* Mod names are snake case, and newly added mods start with `mcl_`, e.g.
|
* GUI improvement
|
||||||
`mcl_core`, `mcl_farming`, `mcl_monster_eggs`. Keep in mind Minetest
|
* Features from pocket or console edition
|
||||||
does not support capital letters in mod names.
|
|
||||||
* To export functions, store them inside a global table named like the
|
|
||||||
mod, e.g.
|
|
||||||
|
|
||||||
```lua
|
### Low chance for inclusion (discuss/optimize first)
|
||||||
mcl_example = {}
|
* Overhaul of architecture / mod structure
|
||||||
|
* Mass-itemstring changes all over the place
|
||||||
|
* Added files have a unusual high file size
|
||||||
|
* Indentation looks like crazy
|
||||||
|
* Single commits which add several unrelated things
|
||||||
|
* Gameplay features which don't exist in Minecraft
|
||||||
|
|
||||||
function mcl_example.do_something()
|
### Instant rejection
|
||||||
-- ...
|
* Proprietary **anything**
|
||||||
end
|
* Code contains `minetest.env` anywhere
|
||||||
|
|
||||||
```
|
## Coding style guide
|
||||||
|
* Indentations should reflect the code flow
|
||||||
|
* Use tabs, not spaces for indentation (tab size = 8)
|
||||||
|
* Never use `minetest.env`
|
||||||
|
|
||||||
* Public functions should not use self references but rather just access
|
## Reporting bugs
|
||||||
the table directly, e.g.
|
Report all bugs and missing Minecraft features here:
|
||||||
|
|
||||||
```lua
|
<https://git.minetest.land/MineClone2/MineClone2/issues>
|
||||||
-- bad
|
|
||||||
function mcl_example:do_something()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- good
|
## Direct discussion
|
||||||
function mcl_example.do_something()
|
We have an IRC channel! Join us on #mineclone2 in freenode.net.
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
* Use modern Minetest API, e.g. no usage of `minetest.env`
|
<ircs://irc.freenode.net:6697/#mineclone2>
|
||||||
* Tabs should be used for indent, spaces for alignment, e.g.
|
|
||||||
|
|
||||||
```lua
|
## Creating releases
|
||||||
|
|
||||||
-- use tabs for indent
|
|
||||||
|
|
||||||
for i = 1, 10 do
|
|
||||||
if i % 3 == 0 then
|
|
||||||
print(i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- use tabs for indent and spaces to align things
|
|
||||||
|
|
||||||
some_table = {
|
|
||||||
{"a string", 5},
|
|
||||||
{"a very much longer string", 10},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* Use double quotes for strings, e.g. `"asdf"` rather than `'asdf'`
|
|
||||||
* Use snake_case rather than CamelCase, e.g. `my_function` rather than
|
|
||||||
`MyFunction`
|
|
||||||
* Don't declare functions as an assignment, e.g.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
-- bad
|
|
||||||
local some_local_func = function()
|
|
||||||
-- ...
|
|
||||||
end
|
|
||||||
|
|
||||||
my_mod.some_func = function()
|
|
||||||
-- ...
|
|
||||||
end
|
|
||||||
|
|
||||||
-- good
|
|
||||||
local function some_local_func()
|
|
||||||
-- ...
|
|
||||||
end
|
|
||||||
|
|
||||||
function my_mod.some_func()
|
|
||||||
-- ...
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### Developer status
|
|
||||||
Active and trusted contributors are often granted write access to the
|
|
||||||
MineClone2 repository.
|
|
||||||
|
|
||||||
#### Developer responsibilities
|
|
||||||
- You should not push things directly to
|
|
||||||
MineClone2 master - rather, do your work on a branch on your private
|
|
||||||
repository, then create a pull request. This way other people can review
|
|
||||||
your changes and make sure they work before they get merged.
|
|
||||||
- Merge PRs only when they have recieved the necessary feedback and have
|
|
||||||
been tested by at least two different people (including the author of
|
|
||||||
the pull request), to avoid crashes or the introduction of new bugs.
|
|
||||||
- You may also be assigned to issues or pull
|
|
||||||
requests as a developer. In this case it is your responsibility to fix
|
|
||||||
the issue / review and merge the pull request when it is ready. You can
|
|
||||||
also unassign yourself from the issue / PR if you have no time or don't
|
|
||||||
want to take care of it for some other reason. After all, everyone is a
|
|
||||||
volunteer and we can't expect you to do work that you are not interested
|
|
||||||
in. **The important thing is that you make sure to inform us if you
|
|
||||||
won't take care of something that has been assigned to you.**
|
|
||||||
- Please assign yourself to something that you want to work on to avoid
|
|
||||||
duplicate work.
|
|
||||||
- As a developer, it should be easy to reach you about your work. You
|
|
||||||
should be in at least one of the public MineClone2 discussion rooms -
|
|
||||||
preferrably Discord, but if you really don't like Discord, Matrix
|
|
||||||
or IRC are fine too.
|
|
||||||
|
|
||||||
### Maintainer status
|
|
||||||
Maintainers carry the main responsibility for the project.
|
|
||||||
|
|
||||||
#### Maintainer responsibilities
|
|
||||||
- Making sure issues are addressed and pull requests are reviewed and
|
|
||||||
merged, by assigning either themselves or Developers to issues / PRs
|
|
||||||
- Making releases
|
|
||||||
- Making sure guidelines are kept
|
|
||||||
- Making project decisions based on community feedback
|
|
||||||
- Granting/revoking developer access
|
|
||||||
- Enforcing the code of conduct (See CODE_OF_CONDUCT.md)
|
|
||||||
- Moderating official community spaces (See Links section)
|
|
||||||
- Resolving conflicts and problems within the community
|
|
||||||
|
|
||||||
#### Current maintainers
|
|
||||||
* Fleckenstein - responsible for gameplay review, publishing releases,
|
|
||||||
technical guidelines and issue/PR delegation
|
|
||||||
* Nicu - responsible for community related issues
|
|
||||||
|
|
||||||
#### Release process
|
|
||||||
* Run `tools/generate_ingame_credits.lua` to update the ingame credits
|
|
||||||
from `CREDITS.md` and commit the result (if anything changed)
|
|
||||||
* Launch MineClone2 to make sure it still runs
|
* Launch MineClone2 to make sure it still runs
|
||||||
* Update the version number in README.md
|
* Update the version number in README.md
|
||||||
* Use `git tag <version number>` to tag the latest commit with the
|
* Use `git tag <version number>` to tag the latest commit with the version number
|
||||||
version number
|
* Push to repo (don't forget `--tags`!)
|
||||||
* Push to repository (don't forget `--tags`!)
|
* Update ContentDB (https://content.minetest.net/packages/Wuzzy/mineclone2/)
|
||||||
* Update ContentDB
|
* Update first post in forum thread (https://forum.minetest.net/viewtopic.php?f=50&t=16407)
|
||||||
(https://content.minetest.net/packages/Wuzzy/mineclone2/)
|
|
||||||
* Update first post in forum thread
|
|
||||||
(https://forum.minetest.net/viewtopic.php?f=50&t=16407)
|
|
||||||
* Post release announcement and changelog in forums
|
* Post release announcement and changelog in forums
|
||||||
|
|
||||||
### Licensing
|
|
||||||
By asking us to include your changes in this game, you agree that they
|
|
||||||
fall under the terms of the GPLv3, which basically means they will
|
|
||||||
become part of a free/libre software.
|
|
||||||
|
|
||||||
### Crediting
|
|
||||||
Contributors, Developers and Maintainers will be credited in
|
|
||||||
`CREDITS.md`. If you make your first time contribution, please add
|
|
||||||
yourself to this file. There are also Discord roles for Contributors,
|
|
||||||
Developers and Maintainers.
|
|
||||||
|
|
141
CREDITS.md
|
@ -1,141 +0,0 @@
|
||||||
# Credits
|
|
||||||
|
|
||||||
## Creator of MineClone
|
|
||||||
* davedevils
|
|
||||||
|
|
||||||
## Creator of MineClone2
|
|
||||||
* Wuzzy
|
|
||||||
|
|
||||||
## Maintainers
|
|
||||||
* Fleckenstein
|
|
||||||
* Nicu
|
|
||||||
* kay27
|
|
||||||
|
|
||||||
## Developers
|
|
||||||
* bzoss
|
|
||||||
* AFCMS
|
|
||||||
* epCode
|
|
||||||
* ryvnf
|
|
||||||
* iliekprogrammar
|
|
||||||
* MysticTempest
|
|
||||||
* Rootyjr
|
|
||||||
* aligator
|
|
||||||
* Code-Sploit
|
|
||||||
* NO11
|
|
||||||
* cora
|
|
||||||
* jordan4ibanez
|
|
||||||
|
|
||||||
## Contributors
|
|
||||||
* Laurent Rocher
|
|
||||||
* HimbeerserverDE
|
|
||||||
* TechDudie
|
|
||||||
* Alexander Minges
|
|
||||||
* ArTee3
|
|
||||||
* ZeDique la Ruleta
|
|
||||||
* pitchum
|
|
||||||
* wuniversales
|
|
||||||
* Bu-Gee
|
|
||||||
* David McMackins II
|
|
||||||
* Nicholas Niro
|
|
||||||
* Wouters Dorian
|
|
||||||
* Blue Blancmange
|
|
||||||
* Jared Moody
|
|
||||||
* Li0n
|
|
||||||
* Midgard
|
|
||||||
* Saku Laesvuori
|
|
||||||
* Yukitty
|
|
||||||
* ZedekThePD
|
|
||||||
* aldum
|
|
||||||
* dBeans
|
|
||||||
* nickolas360
|
|
||||||
* yutyo
|
|
||||||
* Tianyang Zhang
|
|
||||||
* j45
|
|
||||||
* Marcin Serwin
|
|
||||||
* erlehmann
|
|
||||||
* E
|
|
||||||
* Benjamin Schötz
|
|
||||||
* Doloment
|
|
||||||
* Sydney Gems
|
|
||||||
* talamh
|
|
||||||
* Emily2255
|
|
||||||
* Emojigit
|
|
||||||
* FinishedFragment
|
|
||||||
* sfan5
|
|
||||||
* Blue Blancmange
|
|
||||||
* Jared Moody
|
|
||||||
* SmallJoker
|
|
||||||
* Sven792
|
|
||||||
* aldum
|
|
||||||
* Dieter44
|
|
||||||
|
|
||||||
## MineClone5
|
|
||||||
* kay27
|
|
||||||
* Debiankaios
|
|
||||||
* epCode
|
|
||||||
* NO11
|
|
||||||
* j45
|
|
||||||
|
|
||||||
## Original Mod Authors
|
|
||||||
* Wuzzy
|
|
||||||
* Fleckenstein
|
|
||||||
* BlockMen
|
|
||||||
* TenPlus1
|
|
||||||
* PilzAdam
|
|
||||||
* ryvnf
|
|
||||||
* stujones11
|
|
||||||
* Arcelmi
|
|
||||||
* celeron55
|
|
||||||
* maikerumine
|
|
||||||
* GunshipPenguin
|
|
||||||
* Qwertymine3
|
|
||||||
* Rochambeau
|
|
||||||
* rubenwardy
|
|
||||||
* stu
|
|
||||||
* 4aiman
|
|
||||||
* Kahrl
|
|
||||||
* Krock
|
|
||||||
* UgnilJoZ
|
|
||||||
* lordfingle
|
|
||||||
* 22i
|
|
||||||
* bzoss
|
|
||||||
* kilbith
|
|
||||||
* xeranas
|
|
||||||
* kddekadenz
|
|
||||||
* sofar
|
|
||||||
* 4Evergreen4
|
|
||||||
* jordan4ibanez
|
|
||||||
* paramat
|
|
||||||
|
|
||||||
## 3D Models
|
|
||||||
* 22i
|
|
||||||
* tobyplowy
|
|
||||||
* epCode
|
|
||||||
|
|
||||||
## Textures
|
|
||||||
* XSSheep
|
|
||||||
* Wuzzy
|
|
||||||
* kingoscargames
|
|
||||||
* leorockway
|
|
||||||
* xMrVizzy
|
|
||||||
* yutyo
|
|
||||||
* NO11
|
|
||||||
* kay27
|
|
||||||
|
|
||||||
## Translations
|
|
||||||
* Wuzzy
|
|
||||||
* Rocher Laurent
|
|
||||||
* wuniversales
|
|
||||||
* kay27
|
|
||||||
* pitchum
|
|
||||||
* todoporlalibertad
|
|
||||||
* Marcin Serwin
|
|
||||||
|
|
||||||
## Funders
|
|
||||||
* 40W
|
|
||||||
|
|
||||||
## Special thanks
|
|
||||||
* celeron55 for creating Minetest
|
|
||||||
* Jordach for the jukebox music compilation from Big Freaking Dig
|
|
||||||
* The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game
|
|
||||||
* Notch and Jeb for being the major forces behind Minecraft
|
|
|
@ -149,7 +149,7 @@ These groups are used mostly for informational purposes
|
||||||
* `trapdoor=2`: Open trapdoor
|
* `trapdoor=2`: Open trapdoor
|
||||||
* `glass=1`: Glass (full cubes only)
|
* `glass=1`: Glass (full cubes only)
|
||||||
* `rail=1`: Rail
|
* `rail=1`: Rail
|
||||||
* `music_record`: Item is Music Disc
|
* `music_record`: Music Disc (rating is track ID)
|
||||||
* `tnt=1`: Block is TNT
|
* `tnt=1`: Block is TNT
|
||||||
* `boat=1`: Boat
|
* `boat=1`: Boat
|
||||||
* `minecart=1`: Minecart
|
* `minecart=1`: Minecart
|
||||||
|
|
52
LEGAL.md
|
@ -1,52 +0,0 @@
|
||||||
# Legal information
|
|
||||||
This is a fan game, not developed or endorsed by Mojang AB.
|
|
||||||
|
|
||||||
Copying is an act of love. Please copy and share! <3
|
|
||||||
Here's the detailed legalese for those who need it:
|
|
||||||
|
|
||||||
## License of source code
|
|
||||||
MineClone 2 (by kay27, EliasFleckenstein, Wuzzy, davedevils and countless others)
|
|
||||||
is an imitation of Minecraft.
|
|
||||||
|
|
||||||
MineClone 2 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 (in the LICENSE.txt file) for more
|
|
||||||
details.
|
|
||||||
|
|
||||||
In the mods you might find in the read-me or license
|
|
||||||
text files a different license. This counts as dual-licensing.
|
|
||||||
You can choose which license applies to you: Either the
|
|
||||||
license of MineClone 2 (GNU GPLv3) or the mod's license.
|
|
||||||
|
|
||||||
MineClone 2 is a direct continuation of the discontinued MineClone
|
|
||||||
project by davedevils.
|
|
||||||
|
|
||||||
Mod credits:
|
|
||||||
See `README.txt` or `README.md` in each mod directory for information about other authors.
|
|
||||||
For mods that do not have such a file, the license is the source code license
|
|
||||||
of MineClone 2 and the author is Wuzzy.
|
|
||||||
|
|
||||||
## License of media (textures and sounds)
|
|
||||||
No non-free licenses are used anywhere.
|
|
||||||
|
|
||||||
The textures, unless otherwise noted, are based on the Pixel Perfection resource pack for Minecraft 1.11,
|
|
||||||
authored by XSSheep. Most textures are verbatim copies, while some textures have been changed or redone
|
|
||||||
from scratch.
|
|
||||||
The glazed terracotta textures have been created by (MysticTempest)[https://github.com/MysticTempest].
|
|
||||||
Source: <https://www.planetminecraft.com/texture_pack/131pixel-perfection/>
|
|
||||||
License: [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/)
|
|
||||||
|
|
||||||
The main menu images are release under: [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
|
|
||||||
|
|
||||||
All other files, unless mentioned otherwise, fall under:
|
|
||||||
Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
|
||||||
http://creativecommons.org/licenses/by-sa/3.0/
|
|
||||||
|
|
||||||
See README.txt in each mod directory for detailed information about other authors.
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Missing features in Minetest to recreate Minecraft features
|
||||||
|
|
||||||
|
A side goal of the MineClone 2 project is to find any shortcomings of Minetest which make it impossible to recreate a Minecraft feature exactly.
|
||||||
|
This file lists some of the missing features in Minetest which MineClone 2 would require.
|
||||||
|
|
||||||
|
## No workaround possible
|
||||||
|
For these features, no easy Lua workaround could be found.
|
||||||
|
|
||||||
|
### Lua API
|
||||||
|
#### Tools/wielded item
|
||||||
|
- “Lock” hotbar for a brief time after using an item, making it impossible to switch item or to attach/mine/build until the delay is over (For eating with delay)
|
||||||
|
- Tool charging: Holding down the mouse and releasing it, applying a “power level” (For bow and arrows, more charge = higher arrow range) ([issue 5212](https://github.com/minetest/minetest/issues/5212))
|
||||||
|
- [Dual Wielding](http://minecraft.gamepedia.com/Dual_wield)
|
||||||
|
- Eating/drinking animation ([issue 2811](https://github.com/minetest/minetest/issues/2811))
|
||||||
|
|
||||||
|
#### Nodes
|
||||||
|
- Light level 15 for nodes (not sunlight)
|
||||||
|
- Nodes makes light level drop by 2 or or more per node ([issue 5209](https://github.com/minetest/minetest/issues/5209))
|
||||||
|
|
||||||
|
## Interface
|
||||||
|
- Inventory: Hold down right mouse button while holding an item stack to drop items into the slots as you move the mouse. Makes crafting MUCH faster
|
||||||
|
- Sneak+Leftclick on crafting output crafts as many items as possible and immediately puts it into the player inventory ([issue 5211](https://github.com/minetest/minetest/issues/5211))
|
||||||
|
- Sneak+click puts items in different inventories depending on the item type (maybe group-based)? Required for sneak-clicking to armor slots
|
||||||
|
|
||||||
|
## Workaround theoretically possible
|
||||||
|
For these features, a workaround (or hack ;-)) by using Lua is theoretically possible. But engine support would be clearly better, more performant, more reliable, etc.
|
||||||
|
|
||||||
|
### Lua API
|
||||||
|
#### Nodes
|
||||||
|
- Change walking speed on block (soul sand)
|
||||||
|
- Change jumping height on block (soul sand),
|
||||||
|
- Change object movement speed *through* a block, but for non-liquids (for cobweb)
|
||||||
|
- Add `on_walk_over` event
|
||||||
|
- Set frequency in which players lose breath. 2 seconds are hardcoded in Minetest, in Minecraft it's 1 second
|
||||||
|
- Set damage frequency of `damage_per_second`. In Minecraft many things damage players every half-second rather than every second
|
||||||
|
- Possible to damage players directly when they are with the head inside. This allows to add Minecraft-like suffocation
|
||||||
|
- Sneak+click on inventory slot should be able to put items into additional “fallback inventories” if the first inventory is full. Useful for large chests
|
||||||
|
|
||||||
|
#### Nice-to-haye
|
||||||
|
- Utility function to rotate pillar-like nodes, requiring only 3 possible orientations (X, Y, Z). Basically this is `minetest.rotate_node` but with less orientations; the purpur pillar would mess up if a mirrored rotation would be possible. This is already implemented in MCL2, See `mcl_util` for more infos
|
216
README.md
|
@ -1,8 +1,8 @@
|
||||||
# MineClone2
|
# MineClone 2
|
||||||
An unofficial Minecraft-like game for Minetest. Forked from MineClone by davedevils.
|
An unofficial Minecraft-like game for Minetest. Forked from MineClone by davedevils.
|
||||||
Developed by many people. Not developed or endorsed by Mojang AB.
|
Developed by many people. Not developed or endorsed by Mojang AB.
|
||||||
|
|
||||||
Version: 0.72.0 (in development)
|
Version: 0.71.0
|
||||||
|
|
||||||
### Gameplay
|
### Gameplay
|
||||||
You start in a randomly-generated world made entirely of cubes. You can explore
|
You start in a randomly-generated world made entirely of cubes. You can explore
|
||||||
|
@ -65,51 +65,41 @@ map builders. They can not be obtained in-game or in the creative inventory.
|
||||||
Use the `/giveme` chat command to obtain them. See the in-game help for
|
Use the `/giveme` chat command to obtain them. See the in-game help for
|
||||||
an explanation.
|
an explanation.
|
||||||
|
|
||||||
|
#### Incomplete items
|
||||||
|
These items do not work yet, but you can get them with `/giveme` for testing:
|
||||||
|
|
||||||
|
* Minecart with Chest: `mcl_minecarts:chest_minecart`
|
||||||
|
* Minecart with Furnace: `mcl_minecarts:furnace_minecart`
|
||||||
|
* Minecart with Hopper: `mcl_minecarts:hopper_minecart`
|
||||||
|
* Minecart with Command Block: `mcl_minecarts:command_block_minecart`
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
This game requires [Minetest](http://minetest.net) to run (version 5.3.0 or
|
This game requires [Minetest](http://minetest.net) to run (version 5.0.0 or
|
||||||
later). So you need to install Minetest first. Only stable versions of Minetest
|
later). So you need to install Minetest first. Only stable versions of Minetest
|
||||||
are officially supported.
|
are officially supported.
|
||||||
There is no support for running MineClone2 in development versions of Minetest.
|
There is no support for running MineClone 2 in development versions of Minetest.
|
||||||
|
|
||||||
To install MineClone2 (if you haven't already), move this directory into the
|
To install MineClone 2 (if you haven't already), move this directory into the
|
||||||
“games” directory of your Minetest data directory. Consult the help of
|
“games” directory of your Minetest data directory. Consult the help of
|
||||||
Minetest to learn more.
|
Minetest to learn more.
|
||||||
|
|
||||||
## Useful links
|
## Project description
|
||||||
The MineClone2 repository is hosted at Mesehub. To contribute or report issues, head there.
|
The main goal of **MineClone 2** is to be a clone of Minecraft and to be released as free software.
|
||||||
|
|
||||||
* Mesehub: <https://git.minetest.land/MineClone2/MineClone2>
|
* **Target of development: Minecraft, PC Edition, version 1.12** (later known as “Java Edition”)
|
||||||
* Discord: <https://discord.gg/xE4z8EEpDC>
|
* MineClone2 also includes Optifine features supported by the Minetest
|
||||||
* YouTube <https://www.youtube.com/channel/UClI_YcsXMF3KNeJtoBfnk9A>
|
* Features of later Minecraft versions might sneak in, but they have a low priority
|
||||||
* IRC: <https://web.libera.chat/#mineclone2>
|
* In general, Minecraft is aimed to be cloned as good as Minetest currently permits (no hacks)
|
||||||
* Matrix: <https://app.element.io/#/room/#mc2:matrix.org>
|
* Cloning the gameplay has highest priority
|
||||||
* Reddit: <https://www.reddit.com/r/MineClone2/>
|
* MineClone 2 will use different graphics and sounds, but with a similar style
|
||||||
* Minetest forums: <https://forum.minetest.net/viewtopic.php?f=50&t=16407>
|
* Cloning the interface has no priority. It will only be roughly imitated
|
||||||
* ContentDB: <https://content.minetest.net/packages/wuzzy/mineclone2/>
|
* Limitations found in Minetest will be written down and reported in the course of development
|
||||||
* OpenCollective: <https://opencollective.com/mineclone2>
|
|
||||||
|
|
||||||
## Target
|
|
||||||
- Crucially, create a stable, moddable, free/libre clone of Minecraft
|
|
||||||
based on the Minetest engine with polished features, usable in both
|
|
||||||
singleplayer and multiplayer. Currently, most of **Minecraft Java
|
|
||||||
Edition 1.12.2** features are already implemented and polishing existing
|
|
||||||
features are prioritized over new feature requests.
|
|
||||||
- With lessened priority yet strictly, implement features targetting
|
|
||||||
**Minecraft version 1.17 + OptiFine** (OptiFine only as far as supported
|
|
||||||
by the Minetest Engine). This means features in parity with the listed
|
|
||||||
Minecraft experiences are prioritized over those that don't fulfill this
|
|
||||||
scope.
|
|
||||||
- Optionally, create a performant experience that will run relatively
|
|
||||||
well on really low spec computers. Unfortunately, due to Minecraft's
|
|
||||||
mechanisms and Minetest engine's limitations along with a very small
|
|
||||||
playerbase on low spec computers, optimizations are hard to investigate.
|
|
||||||
|
|
||||||
## Completion status
|
## Completion status
|
||||||
This game is currently in **beta** stage.
|
This game is currently in **alpha** stage.
|
||||||
It is playable, but not yet feature-complete.
|
It is playable, but unfinished, many bugs are to be expected.
|
||||||
Backwards-compability is not entirely guaranteed, updating your world might cause small bugs.
|
Backwards-compability is *not* guaranteed, updating your world might cause small and
|
||||||
If you want to use the git version of MineClone2 in production, consider using the production branch.
|
big bugs (such as “missing node” errors or even crashes).
|
||||||
It is updated weekly and contains relatively stable code for servers.
|
|
||||||
|
|
||||||
The following main features are available:
|
The following main features are available:
|
||||||
|
|
||||||
|
@ -138,7 +128,7 @@ The following main features are available:
|
||||||
* Clock
|
* Clock
|
||||||
* Compass
|
* Compass
|
||||||
* Sponge
|
* Sponge
|
||||||
* Slime block
|
* Slime block (does not interact with redstone)
|
||||||
* Small plants and saplings
|
* Small plants and saplings
|
||||||
* Dyes
|
* Dyes
|
||||||
* Banners
|
* Banners
|
||||||
|
@ -150,19 +140,19 @@ The following main features are available:
|
||||||
* Creative inventory
|
* Creative inventory
|
||||||
* Farming
|
* Farming
|
||||||
* Writable books
|
* Writable books
|
||||||
* Commands
|
* A few server commands
|
||||||
* Villages
|
|
||||||
* The End
|
|
||||||
* And more!
|
* And more!
|
||||||
|
|
||||||
The following features are incomplete:
|
The following features are incomplete:
|
||||||
|
|
||||||
|
* Generated structures (especially villages)
|
||||||
* Some monsters and animals
|
* Some monsters and animals
|
||||||
* Redstone-related things
|
* Redstone-related things
|
||||||
|
* The End
|
||||||
* Special minecarts
|
* Special minecarts
|
||||||
* A couple of non-trivial blocks and items
|
* A couple of non-trivial blocks and items
|
||||||
|
|
||||||
Bonus features (not found in Minecraft 1.12):
|
Bonus features (not found in Minecraft 1.11):
|
||||||
|
|
||||||
* Built-in crafting guide which shows you crafting and smelting recipes
|
* Built-in crafting guide which shows you crafting and smelting recipes
|
||||||
* In-game help system containing extensive help about gameplay basics, blocks, items and more
|
* In-game help system containing extensive help about gameplay basics, blocks, items and more
|
||||||
|
@ -187,14 +177,148 @@ Technical differences from Minecraft:
|
||||||
* Different textures (Pixel Perfection)
|
* Different textures (Pixel Perfection)
|
||||||
* Different sounds (various sources)
|
* Different sounds (various sources)
|
||||||
* Different engine (Minetest)
|
* Different engine (Minetest)
|
||||||
* Different easter eggs
|
|
||||||
|
|
||||||
… and finally, MineClone2 is free software (“free” as in “freedom”)!
|
… and finally, MineClone 2 is free software (“free” as in “freedom”)!
|
||||||
|
|
||||||
|
## Reporting bugs
|
||||||
|
Please report all bugs and missing Minecraft features here:
|
||||||
|
|
||||||
|
<https://git.minetest.land/MineClone2/MineClone2/issues>
|
||||||
|
|
||||||
|
## Chating with the community
|
||||||
|
Join our discord server at:
|
||||||
|
|
||||||
|
<https://discord.gg/84GKcxczG3>
|
||||||
|
|
||||||
## Other readme files
|
## Other readme files
|
||||||
|
|
||||||
* `LICENSE.txt`: The GPLv3 license text
|
* `LICENSE.txt`: The GPLv3 license text
|
||||||
* `CONTRIBUTING.md`: Information for those who want to contribute
|
* `CONTRIBUTING.md`: Information for those who want to contribute
|
||||||
|
* `MISSING_ENGINE_FEATURES.md`: List of missing features in Minetest which MineClone 2 would need for improvement
|
||||||
* `API.md`: For Minetest modders who want to mod this game
|
* `API.md`: For Minetest modders who want to mod this game
|
||||||
* `LEGAL.md`: Legal information
|
|
||||||
* `CREDITS.md`: List of everyone who contributed
|
## Credits
|
||||||
|
There are so many people to list (sorry). Check out the respective mod directories for details. This section is only a rough overview of the core authors of this game.
|
||||||
|
|
||||||
|
### Coding
|
||||||
|
* [Wuzzy](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=3082): Main programmer of most mods (retired)
|
||||||
|
* davedevils: Creator of MineClone on which MineClone 2 is based on
|
||||||
|
* [ex-bart](https://github.com/ex-bart): Redstone comparators
|
||||||
|
* [Rootyjr](https://github.com/Rootyjr): Fishing rod and bugfixes
|
||||||
|
* [aligator](https://github.com/aligator): Improvement of doors
|
||||||
|
* [ryvnf](https://github.com/ryvnf): Explosion mechanics
|
||||||
|
* MysticTempest: Bugfixes
|
||||||
|
* [bzoss](https://github.com/bzoss): Status effects, potions, brewing stand
|
||||||
|
* kay27 <kay27@bk.ru>: Experience system, bugfixes, optimizations (Current maintainer)
|
||||||
|
* [EliasFleckenstein03](https://github.com/EliasFleckenstein03): End crystals, enchanting, burning mobs / players, animated chests, bugfixes (Current maintainer)
|
||||||
|
* epCode: Better player animations, new logo
|
||||||
|
* 2mac: Fix bug with powered rail
|
||||||
|
* Lots of other people: TO BE WRITTEN (see mod directories for details)
|
||||||
|
|
||||||
|
#### Mod credits (summary)
|
||||||
|
|
||||||
|
* `controls`: Arcelmi
|
||||||
|
* `flowlib`: Qwertymine13
|
||||||
|
* `walkover`: lordfingle
|
||||||
|
* `drippingwater`: kddekadenz
|
||||||
|
* `mobs_mc`: maikerumine, 22i and others
|
||||||
|
* `awards`: rubenwardy
|
||||||
|
* `screwdriver`: RealBadAngel, Maciej Kastakin, Minetest contributors
|
||||||
|
* `xpanes`: Minetest contributors
|
||||||
|
* `mesecons` mods: Jeija and contributors
|
||||||
|
* `wieldview`: Stuart Jones
|
||||||
|
* `mcl_meshhand`: Based on `newhand` by jordan4ibanez
|
||||||
|
* `mcl_mobs`: Based on Mobs Redo [`mobs`] by TenPlus1 and contributors
|
||||||
|
* Most other mods: Wuzzy
|
||||||
|
|
||||||
|
Detailed credits for each mod can be found in the individual mod directories.
|
||||||
|
|
||||||
|
### Graphics
|
||||||
|
* [XSSheep](http://www.minecraftforum.net/members/XSSheep): Main author; creator of the Pixel Perfection resource pack of Minecraft 1.11
|
||||||
|
* [Wuzzy](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=3082): Main menu imagery and various edits and additions of texture pack
|
||||||
|
* [kingoscargames](https://github.com/kingoscargames): Various edits and additions of existing textures
|
||||||
|
* [leorockway](https://github.com/leorockway): Some edits of mob textures
|
||||||
|
* [xMrVizzy](https://minecraft.curseforge.com/members/xMrVizzy): Glazed terracotta (textures are subject to be replaced later)
|
||||||
|
* yutyo <tanakinci2002@gmail.com>: MineClone 2 logo
|
||||||
|
* Other authors: GUI images
|
||||||
|
|
||||||
|
### Translations
|
||||||
|
* Wuzzy: German
|
||||||
|
* Rocher Laurent <rocherl@club-internet.fr>: French
|
||||||
|
* wuniversales: Spanish
|
||||||
|
* kay27 <kay27@bk.ru>: Russian
|
||||||
|
|
||||||
|
### Models
|
||||||
|
* [22i](https://github.com/22i): Creator of all models
|
||||||
|
* [tobyplowy](https://github.com/tobyplowy): UV-mapping fixes to said models
|
||||||
|
|
||||||
|
### Sounds and music
|
||||||
|
Various sources. See the respective mod directories for details.
|
||||||
|
|
||||||
|
### Special thanks
|
||||||
|
|
||||||
|
* davedevils for starting MineClone, the original version of this game
|
||||||
|
* Wuzzy for starting and maintaining MineClone2 for several years
|
||||||
|
* celeron55 for creating Minetest
|
||||||
|
* Minetest's modding community for providing a huge selection of mods, some of which ended up in MineClone 2
|
||||||
|
* Jordach for the jukebox music compilation from Big Freaking Dig
|
||||||
|
* The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game
|
||||||
|
* Notch and Jeb for being the major forces behind Minecraft
|
||||||
|
* XSSheep for creating the Pixel Perfection resource pack
|
||||||
|
* [22i](https://github.com/22i) for providing great models and support
|
||||||
|
* [maikerumine](http://github.com/maikerumine) for kicking off mobs and biomes
|
||||||
|
|
||||||
|
## Info for programmers
|
||||||
|
You find interesting and useful infos in `API.md`.
|
||||||
|
|
||||||
|
## Legal information
|
||||||
|
This is a fan game, not developed or endorsed by Mojang AB.
|
||||||
|
|
||||||
|
Copying is an act of love. Please copy and share! <3
|
||||||
|
Here's the detailed legalese for those who need it:
|
||||||
|
|
||||||
|
### License of source code
|
||||||
|
MineClone 2 (by kay27, EliasFleckenstein, Wuzzy, davedevils and countless others)
|
||||||
|
is an imitation of Minecraft.
|
||||||
|
|
||||||
|
MineClone 2 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 (in the LICENSE.txt file) for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
In the mods you might find in the read-me or license
|
||||||
|
text files a different license. This counts as dual-licensing.
|
||||||
|
You can choose which license applies to you: Either the
|
||||||
|
license of MineClone 2 (GNU GPLv3) or the mod's license.
|
||||||
|
|
||||||
|
MineClone 2 is a direct continuation of the discontinued MineClone
|
||||||
|
project by davedevils.
|
||||||
|
|
||||||
|
Mod credits:
|
||||||
|
See `README.txt` or `README.md` in each mod directory for information about other authors.
|
||||||
|
For mods that do not have such a file, the license is the source code license
|
||||||
|
of MineClone 2 and the author is Wuzzy.
|
||||||
|
|
||||||
|
### License of media (textures and sounds)
|
||||||
|
No non-free licenses are used anywhere.
|
||||||
|
|
||||||
|
The textures, unless otherwise noted, are based on the Pixel Perfection resource pack for Minecraft 1.11,
|
||||||
|
authored by XSSheep. Most textures are verbatim copies, while some textures have been changed or redone
|
||||||
|
from scratch.
|
||||||
|
The glazed terracotta textures have been created by (MysticTempest)[https://github.com/MysticTempest].
|
||||||
|
Source: <https://www.planetminecraft.com/texture_pack/131pixel-perfection/>
|
||||||
|
License: [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||||
|
|
||||||
|
The main menu images are release under: [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
|
||||||
|
|
||||||
|
All other files, unless mentioned otherwise, fall under:
|
||||||
|
Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
||||||
|
http://creativecommons.org/licenses/by-sa/3.0/
|
||||||
|
|
||||||
|
See README.txt in each mod directory for detailed information about other authors.
|
||||||
|
|
|
@ -83,7 +83,7 @@ local function get_hardness_values_for_groups()
|
||||||
|
|
||||||
for _, ndef in pairs(minetest.registered_nodes) do
|
for _, ndef in pairs(minetest.registered_nodes) do
|
||||||
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
|
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
|
||||||
if ndef.groups[g] then
|
if ndef.groups[g] ~= nil then
|
||||||
maps[g][ndef._mcl_hardness or 0] = true
|
maps[g][ndef._mcl_hardness or 0] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -121,7 +121,7 @@ local hardness_values = get_hardness_values_for_groups()
|
||||||
-- hardness_value. Used for quick lookup.
|
-- hardness_value. Used for quick lookup.
|
||||||
local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
|
local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
|
||||||
|
|
||||||
--[[local function compute_creativetimes(group)
|
local function compute_creativetimes(group)
|
||||||
local creativetimes = {}
|
local creativetimes = {}
|
||||||
|
|
||||||
for index, hardness in pairs(hardness_values[group]) do
|
for index, hardness in pairs(hardness_values[group]) do
|
||||||
|
@ -129,7 +129,7 @@ local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
|
||||||
end
|
end
|
||||||
|
|
||||||
return creativetimes
|
return creativetimes
|
||||||
end]]
|
end
|
||||||
|
|
||||||
-- Get the list of digging times for using a specific tool on a specific
|
-- Get the list of digging times for using a specific tool on a specific
|
||||||
-- diggroup.
|
-- diggroup.
|
||||||
|
@ -207,10 +207,6 @@ end
|
||||||
function mcl_autogroup.can_harvest(nodename, toolname)
|
function mcl_autogroup.can_harvest(nodename, toolname)
|
||||||
local ndef = minetest.registered_nodes[nodename]
|
local ndef = minetest.registered_nodes[nodename]
|
||||||
|
|
||||||
if not ndef then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
if minetest.get_item_group(nodename, "dig_immediate") >= 2 then
|
if minetest.get_item_group(nodename, "dig_immediate") >= 2 then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -243,13 +239,13 @@ function mcl_autogroup.can_harvest(nodename, toolname)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get one groupcap field for using a specific tool on a specific group.
|
-- Get one groupcap field for using a specific tool on a specific group.
|
||||||
--[[local function get_groupcap(group, can_harvest, multiplier, efficiency, uses)
|
local function get_groupcap(group, can_harvest, multiplier, efficiency, uses)
|
||||||
return {
|
return {
|
||||||
times = get_digtimes(group, can_harvest, multiplier, efficiency),
|
times = get_digtimes(group, can_harvest, multiplier, efficiency),
|
||||||
uses = uses,
|
uses = uses,
|
||||||
maxlevel = 0,
|
maxlevel = 0,
|
||||||
}
|
}
|
||||||
end]]
|
end
|
||||||
|
|
||||||
-- Returns the tool_capabilities from a tool definition or a default set of
|
-- Returns the tool_capabilities from a tool definition or a default set of
|
||||||
-- tool_capabilities
|
-- tool_capabilities
|
||||||
|
@ -302,7 +298,7 @@ function mcl_autogroup.get_wear(toolname, diggroup)
|
||||||
return math.ceil(65535 / uses)
|
return math.ceil(65535 / uses)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function overwrite()
|
local overwrite = function()
|
||||||
for nname, ndef in pairs(minetest.registered_nodes) do
|
for nname, ndef in pairs(minetest.registered_nodes) do
|
||||||
local newgroups = table.copy(ndef.groups)
|
local newgroups = table.copy(ndef.groups)
|
||||||
if (nname ~= "ignore" and ndef.diggable) then
|
if (nname ~= "ignore" and ndef.diggable) then
|
||||||
|
@ -319,12 +315,12 @@ local function overwrite()
|
||||||
newgroups.opaque = 1
|
newgroups.opaque = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
--local creative_breakable = false
|
local creative_breakable = false
|
||||||
|
|
||||||
-- Assign groups used for digging this node depending on
|
-- Assign groups used for digging this node depending on
|
||||||
-- the registered digging groups
|
-- the registered digging groups
|
||||||
for g, gdef in pairs(mcl_autogroup.registered_diggroups) do
|
for g, gdef in pairs(mcl_autogroup.registered_diggroups) do
|
||||||
--creative_breakable = true
|
creative_breakable = true
|
||||||
local index = hardness_lookup[g][ndef._mcl_hardness or 0]
|
local index = hardness_lookup[g][ndef._mcl_hardness or 0]
|
||||||
if ndef.groups[g] then
|
if ndef.groups[g] then
|
||||||
if gdef.levels then
|
if gdef.levels then
|
||||||
|
|
|
@ -81,11 +81,11 @@ if v6_use_snow_biomes then
|
||||||
end
|
end
|
||||||
local v6_freq_desert = tonumber(minetest.get_mapgen_setting("mgv6_freq_desert") or 0.45)
|
local v6_freq_desert = tonumber(minetest.get_mapgen_setting("mgv6_freq_desert") or 0.45)
|
||||||
|
|
||||||
--local NOISE_MAGIC_X = 1619
|
local NOISE_MAGIC_X = 1619
|
||||||
--local NOISE_MAGIC_Y = 31337
|
local NOISE_MAGIC_Y = 31337
|
||||||
--local NOISE_MAGIC_Z = 52591
|
local NOISE_MAGIC_Z = 52591
|
||||||
--local NOISE_MAGIC_SEED = 1013
|
local NOISE_MAGIC_SEED = 1013
|
||||||
local function noise2d(x, y, seed)
|
local noise2d = function(x, y, seed)
|
||||||
-- TODO: implement noise2d function for biome blend
|
-- TODO: implement noise2d function for biome blend
|
||||||
return 0
|
return 0
|
||||||
--[[
|
--[[
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
local get_connected_players = minetest.get_connected_players
|
local get_connected_players = minetest.get_connected_players
|
||||||
local clock = os.clock
|
local clock = os.clock
|
||||||
|
|
||||||
local pairs = pairs
|
|
||||||
|
|
||||||
controls = {}
|
controls = {}
|
||||||
controls.players = {}
|
controls.players = {}
|
||||||
|
|
||||||
|
@ -22,15 +20,15 @@ function controls.register_on_hold(func)
|
||||||
end
|
end
|
||||||
|
|
||||||
local known_controls = {
|
local known_controls = {
|
||||||
jump = true,
|
jump=true,
|
||||||
right = true,
|
right=true,
|
||||||
left = true,
|
left=true,
|
||||||
LMB = true,
|
LMB=true,
|
||||||
RMB = true,
|
RMB=true,
|
||||||
sneak = true,
|
sneak=true,
|
||||||
aux1 = true,
|
aux1=true,
|
||||||
down = true,
|
down=true,
|
||||||
up = true,
|
up=true,
|
||||||
}
|
}
|
||||||
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
minetest.register_on_joinplayer(function(player)
|
||||||
|
@ -54,17 +52,17 @@ minetest.register_globalstep(function(dtime)
|
||||||
for cname, cbool in pairs(player_controls) do
|
for cname, cbool in pairs(player_controls) do
|
||||||
if known_controls[cname] == true then
|
if known_controls[cname] == true then
|
||||||
--Press a key
|
--Press a key
|
||||||
if cbool == true and controls.players[player_name][cname][1] == false then
|
if cbool==true and controls.players[player_name][cname][1]==false then
|
||||||
for _, func in pairs(controls.registered_on_press) do
|
for _, func in pairs(controls.registered_on_press) do
|
||||||
func(player, cname)
|
func(player, cname)
|
||||||
end
|
end
|
||||||
controls.players[player_name][cname] = {true, clock()}
|
controls.players[player_name][cname] = {true, clock()}
|
||||||
elseif cbool == true and controls.players[player_name][cname][1] == true then
|
elseif cbool==true and controls.players[player_name][cname][1]==true then
|
||||||
for _, func in pairs(controls.registered_on_hold) do
|
for _, func in pairs(controls.registered_on_hold) do
|
||||||
func(player, cname, clock()-controls.players[player_name][cname][2])
|
func(player, cname, clock()-controls.players[player_name][cname][2])
|
||||||
end
|
end
|
||||||
--Release a key
|
--Release a key
|
||||||
elseif cbool == false and controls.players[player_name][cname][1] == true then
|
elseif cbool==false and controls.players[player_name][cname][1]==true then
|
||||||
for _, func in pairs(controls.registered_on_release) do
|
for _, func in pairs(controls.registered_on_release) do
|
||||||
func(player, cname, clock()-controls.players[player_name][cname][2])
|
func(player, cname, clock()-controls.players[player_name][cname][2])
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,100 +1,95 @@
|
||||||
local math = math
|
|
||||||
|
|
||||||
local get_node = minetest.get_node
|
|
||||||
local get_item_group = minetest.get_item_group
|
|
||||||
|
|
||||||
local registered_nodes = minetest.registered_nodes
|
|
||||||
|
|
||||||
flowlib = {}
|
flowlib = {}
|
||||||
|
|
||||||
--sum of direction vectors must match an array index
|
--sum of direction vectors must match an array index
|
||||||
|
|
||||||
--(sum,root)
|
|
||||||
--(0,1), (1,1+0=1), (2,1+1=2), (3,1+2^2=5), (4,2^2+2^2=8)
|
|
||||||
|
|
||||||
local inv_roots = {
|
|
||||||
[0] = 1,
|
|
||||||
[1] = 1,
|
|
||||||
[2] = 0.70710678118655,
|
|
||||||
[4] = 0.5,
|
|
||||||
[5] = 0.44721359549996,
|
|
||||||
[8] = 0.35355339059327,
|
|
||||||
}
|
|
||||||
|
|
||||||
local function to_unit_vector(dir_vector)
|
local function to_unit_vector(dir_vector)
|
||||||
local sum = dir_vector.x * dir_vector.x + dir_vector.z * dir_vector.z
|
--(sum,root)
|
||||||
return {x = dir_vector.x * inv_roots[sum], y = dir_vector.y, z = dir_vector.z * inv_roots[sum]}
|
-- (0,1), (1,1+0=1), (2,1+1=2), (3,1+2^2=5), (4,2^2+2^2=8)
|
||||||
|
local inv_roots = {[0] = 1, [1] = 1, [2] = 0.70710678118655, [4] = 0.5
|
||||||
|
, [5] = 0.44721359549996, [8] = 0.35355339059327}
|
||||||
|
local sum = dir_vector.x*dir_vector.x + dir_vector.z*dir_vector.z
|
||||||
|
return {x=dir_vector.x*inv_roots[sum],y=dir_vector.y
|
||||||
|
,z=dir_vector.z*inv_roots[sum]}
|
||||||
end
|
end
|
||||||
|
|
||||||
local function is_touching(realpos,nodepos,radius)
|
local is_touching = function(realpos,nodepos,radius)
|
||||||
local boarder = 0.5 - radius
|
local boarder = 0.5 - radius
|
||||||
return math.abs(realpos - nodepos) > (boarder)
|
return (math.abs(realpos - nodepos) > (boarder))
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.is_touching = is_touching
|
flowlib.is_touching = is_touching
|
||||||
|
|
||||||
local function is_water(pos)
|
local is_water = function(pos)
|
||||||
return get_item_group(get_node(pos).name, "water") ~= 0
|
return (minetest.get_item_group(minetest.get_node(
|
||||||
|
{x=pos.x,y=pos.y,z=pos.z}).name
|
||||||
|
, "water") ~= 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.is_water = is_water
|
flowlib.is_water = is_water
|
||||||
|
|
||||||
local function node_is_water(node)
|
local node_is_water = function(node)
|
||||||
return get_item_group(node.name, "water") ~= 0
|
return (minetest.get_item_group(node.name, "water") ~= 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.node_is_water = node_is_water
|
flowlib.node_is_water = node_is_water
|
||||||
|
|
||||||
local function is_lava(pos)
|
local is_lava = function(pos)
|
||||||
return get_item_group(get_node(pos).name, "lava") ~= 0
|
return (minetest.get_item_group(minetest.get_node(
|
||||||
|
{x=pos.x,y=pos.y,z=pos.z}).name
|
||||||
|
, "lava") ~= 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.is_lava = is_lava
|
flowlib.is_lava = is_lava
|
||||||
|
|
||||||
local function node_is_lava(node)
|
local node_is_lava = function(node)
|
||||||
return get_item_group(node.name, "lava") ~= 0
|
return (minetest.get_item_group(node.name, "lava") ~= 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.node_is_lava = node_is_lava
|
flowlib.node_is_lava = node_is_lava
|
||||||
|
|
||||||
|
|
||||||
local function is_liquid(pos)
|
local is_liquid = function(pos)
|
||||||
return get_item_group(get_node(pos).name, "liquid") ~= 0
|
return (minetest.get_item_group(minetest.get_node(
|
||||||
|
{x=pos.x,y=pos.y,z=pos.z}).name
|
||||||
|
, "liquid") ~= 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.is_liquid = is_liquid
|
flowlib.is_liquid = is_liquid
|
||||||
|
|
||||||
local function node_is_liquid(node)
|
local node_is_liquid = function(node)
|
||||||
return minetest.get_item_group(node.name, "liquid") ~= 0
|
return (minetest.get_item_group(node.name, "liquid") ~= 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.node_is_liquid = node_is_liquid
|
flowlib.node_is_liquid = node_is_liquid
|
||||||
|
|
||||||
--This code is more efficient
|
--This code is more efficient
|
||||||
local function quick_flow_logic(node, pos_testing, direction)
|
local function quick_flow_logic(node,pos_testing,direction)
|
||||||
local name = node.name
|
local name = node.name
|
||||||
if not registered_nodes[name] then
|
if not minetest.registered_nodes[name] then
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if registered_nodes[name].liquidtype == "source" then
|
if minetest.registered_nodes[name].liquidtype == "source" then
|
||||||
local node_testing = get_node(pos_testing)
|
local node_testing = minetest.get_node(pos_testing)
|
||||||
if not registered_nodes[node_testing.name] then
|
local param2_testing = node_testing.param2
|
||||||
|
if not minetest.registered_nodes[node_testing.name] then
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if registered_nodes[node_testing.name].liquidtype ~= "flowing" then
|
if minetest.registered_nodes[node_testing.name].liquidtype
|
||||||
|
~= "flowing" then
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
return direction
|
return direction
|
||||||
end
|
end
|
||||||
elseif registered_nodes[name].liquidtype == "flowing" then
|
elseif minetest.registered_nodes[name].liquidtype == "flowing" then
|
||||||
local node_testing = get_node(pos_testing)
|
local node_testing = minetest.get_node(pos_testing)
|
||||||
local param2_testing = node_testing.param2
|
local param2_testing = node_testing.param2
|
||||||
if not registered_nodes[node_testing.name] then
|
if not minetest.registered_nodes[node_testing.name] then
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if registered_nodes[node_testing.name].liquidtype == "source" then
|
if minetest.registered_nodes[node_testing.name].liquidtype
|
||||||
|
== "source" then
|
||||||
return -direction
|
return -direction
|
||||||
elseif registered_nodes[node_testing.name].liquidtype == "flowing" then
|
elseif minetest.registered_nodes[node_testing.name].liquidtype
|
||||||
|
== "flowing" then
|
||||||
if param2_testing < node.param2 then
|
if param2_testing < node.param2 then
|
||||||
if (node.param2 - param2_testing) > 6 then
|
if (node.param2 - param2_testing) > 6 then
|
||||||
return -direction
|
return -direction
|
||||||
|
@ -113,41 +108,48 @@ local function quick_flow_logic(node, pos_testing, direction)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local function quick_flow(pos, node)
|
local quick_flow = function(pos,node)
|
||||||
|
local x = 0
|
||||||
|
local z = 0
|
||||||
|
|
||||||
if not node_is_liquid(node) then
|
if not node_is_liquid(node) then
|
||||||
return {x = 0, y = 0, z = 0}
|
return {x=0,y=0,z=0}
|
||||||
end
|
end
|
||||||
local x = quick_flow_logic(node,{x = pos.x-1, y = pos.y, z = pos.z},-1) + quick_flow_logic(node,{x = pos.x+1, y = pos.y, z = pos.z}, 1)
|
|
||||||
local z = quick_flow_logic(node,{x = pos.x, y = pos.y, z = pos.z-1},-1) + quick_flow_logic(node,{x = pos.x, y = pos.y, z = pos.z+1}, 1)
|
x = x + quick_flow_logic(node,{x=pos.x-1,y=pos.y,z=pos.z},-1)
|
||||||
return to_unit_vector({x = x, y = 0, z = z})
|
x = x + quick_flow_logic(node,{x=pos.x+1,y=pos.y,z=pos.z}, 1)
|
||||||
|
z = z + quick_flow_logic(node,{x=pos.x,y=pos.y,z=pos.z-1},-1)
|
||||||
|
z = z + quick_flow_logic(node,{x=pos.x,y=pos.y,z=pos.z+1}, 1)
|
||||||
|
|
||||||
|
return to_unit_vector({x=x,y=0,z=z})
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.quick_flow = quick_flow
|
flowlib.quick_flow = quick_flow
|
||||||
|
|
||||||
--if not in water but touching, move centre to touching block
|
|
||||||
--x has higher precedence than z
|
|
||||||
--if pos changes with x, it affects z
|
|
||||||
|
|
||||||
local function move_centre(pos, realpos, node, radius)
|
--if not in water but touching, move centre to touching block
|
||||||
if is_touching(realpos.x, pos.x, radius) then
|
--x has higher precedence than z
|
||||||
if is_liquid({x = pos.x-1, y = pos.y, z = pos.z}) then
|
--if pos changes with x, it affects z
|
||||||
node = get_node({x=pos.x-1, y = pos.y, z = pos.z})
|
local move_centre = function(pos,realpos,node,radius)
|
||||||
pos = {x = pos.x-1, y = pos.y, z = pos.z}
|
if is_touching(realpos.x,pos.x,radius) then
|
||||||
elseif is_liquid({x = pos.x+1, y = pos.y, z = pos.z}) then
|
if is_liquid({x=pos.x-1,y=pos.y,z=pos.z}) then
|
||||||
node = get_node({x = pos.x+1, y = pos.y, z = pos.z})
|
node = minetest.get_node({x=pos.x-1,y=pos.y,z=pos.z})
|
||||||
pos = {x = pos.x+1, y = pos.y, z = pos.z}
|
pos = {x=pos.x-1,y=pos.y,z=pos.z}
|
||||||
|
elseif is_liquid({x=pos.x+1,y=pos.y,z=pos.z}) then
|
||||||
|
node = minetest.get_node({x=pos.x+1,y=pos.y,z=pos.z})
|
||||||
|
pos = {x=pos.x+1,y=pos.y,z=pos.z}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if is_touching(realpos.z, pos.z, radius) then
|
if is_touching(realpos.z,pos.z,radius) then
|
||||||
if is_liquid({x = pos.x, y = pos.y, z = pos.z - 1}) then
|
if is_liquid({x=pos.x,y=pos.y,z=pos.z-1}) then
|
||||||
node = get_node({x = pos.x, y = pos.y, z = pos.z - 1})
|
node = minetest.get_node({x=pos.x,y=pos.y,z=pos.z-1})
|
||||||
pos = {x = pos.x, y = pos.y, z = pos.z - 1}
|
pos = {x=pos.x,y=pos.y,z=pos.z-1}
|
||||||
elseif is_liquid({x = pos.x, y = pos.y, z = pos.z + 1}) then
|
elseif is_liquid({x=pos.x,y=pos.y,z=pos.z+1}) then
|
||||||
node = get_node({x = pos.x, y = pos.y, z = pos.z + 1})
|
node = minetest.get_node({x=pos.x,y=pos.y,z=pos.z+1})
|
||||||
pos = {x = pos.x, y = pos.y, z = pos.z + 1}
|
pos = {x=pos.x,y=pos.y,z=pos.z+1}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return pos, node
|
return pos,node
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.move_centre = move_centre
|
flowlib.move_centre = move_centre
|
||||||
|
|
|
@ -1,21 +1,17 @@
|
||||||
local vector = vector
|
|
||||||
|
|
||||||
local facedir_to_dir = minetest.facedir_to_dir
|
|
||||||
local get_item_group = minetest.get_item_group
|
|
||||||
local remove_node = minetest.remove_node
|
|
||||||
local get_node = minetest.get_node
|
|
||||||
|
|
||||||
local original_function = minetest.check_single_for_falling
|
local original_function = minetest.check_single_for_falling
|
||||||
|
|
||||||
function minetest.check_single_for_falling(pos)
|
minetest.check_single_for_falling = function(pos)
|
||||||
local ret_o = original_function(pos)
|
local ret_o = original_function(pos)
|
||||||
|
|
||||||
local ret = false
|
local ret = false
|
||||||
local node = minetest.get_node(pos)
|
local node = minetest.get_node(pos)
|
||||||
if get_item_group(node.name, "attached_node_facedir") ~= 0 then
|
if minetest.get_item_group(node.name, "attached_node_facedir") ~= 0 then
|
||||||
local dir = facedir_to_dir(node.param2)
|
local dir = minetest.facedir_to_dir(node.param2)
|
||||||
if dir then
|
if dir then
|
||||||
if get_item_group(get_node(vector.add(pos, dir)).name, "solid") == 0 then
|
local cpos = vector.add(pos, dir)
|
||||||
remove_node(pos)
|
local cnode = minetest.get_node(cpos)
|
||||||
|
if minetest.get_item_group(cnode.name, "solid") == 0 then
|
||||||
|
minetest.remove_node(pos)
|
||||||
local drops = minetest.get_node_drops(node.name, "")
|
local drops = minetest.get_node_drops(node.name, "")
|
||||||
for dr=1, #drops do
|
for dr=1, #drops do
|
||||||
minetest.add_item(pos, drops[dr])
|
minetest.add_item(pos, drops[dr])
|
||||||
|
@ -24,6 +20,7 @@ function minetest.check_single_for_falling(pos)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return ret_o or ret
|
return ret_o or ret
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
# API documentation of mcl_commands
|
||||||
|
|
||||||
|
The mcl_commands API allows you to register and overide complex commands.
|
||||||
|
This mod is derivated from ChatCommandBuilder by rubenwardy
|
||||||
|
|
||||||
|
Some public functions are not documented here but they are for internal use only.
|
||||||
|
|
||||||
|
## Technical differences from ChatCommandBuilder
|
||||||
|
|
||||||
|
* subcommand aditional specific privs
|
||||||
|
* new types: `json`, `color`, `nodename` an maybe more in the future
|
||||||
|
|
||||||
|
|
||||||
|
## Public Functions
|
||||||
|
|
||||||
|
### `mcl_commands.register_command("exemple", def)`
|
||||||
|
|
||||||
|
This is a function which is called when an item is dispensed by the dispenser.
|
||||||
|
These are the parameters:
|
||||||
|
|
||||||
|
```
|
||||||
|
mcl_commands.register_command("exemple", {
|
||||||
|
func = function(cmd) --function executed on registration
|
||||||
|
cmd:sub(":name:username title :params:json", { --create a new subcommand called "title" with defined patterns
|
||||||
|
func = function(name, target, json)
|
||||||
|
return a_cool_function(target, json) --function executed if the params match the patterns
|
||||||
|
end,
|
||||||
|
privs = {settime = true}, --subcommand aditional specific privs
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
description = "Controls text displayed on the screen.", --the description of the command
|
||||||
|
params = "<target> command <params>", --very basic explaination of the syntax of the command (will be semi-automatic in the future)
|
||||||
|
privs = {server = true}, --global privs
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Register a complex chatcommand. If a chat command with the same name is already registered, the program will fail.
|
||||||
|
|
||||||
|
### `mcl_commands.overide_command("exemple", def)`
|
||||||
|
|
||||||
|
Same as above but will overide existing command.
|
||||||
|
|
||||||
|
### `mcl_commands.register_chatcommand_alias(alias, cmd, [bypass])`
|
||||||
|
|
||||||
|
Register an alias called `alias` of the `cmd` command.
|
||||||
|
If the setting `mcl_builtin_commands_overide` is set to `false`, the function will silently fail.
|
||||||
|
If `bypass` is set to `true` the function will not take care of the above setting.
|
||||||
|
Will warn if trying to alias to already existing command.
|
||||||
|
|
||||||
|
### `mcl_commands.rename_chatcommand(newname, cmd, [bypass])`
|
||||||
|
|
||||||
|
Rename `cmd` command to `newname`.
|
||||||
|
If the setting `mcl_builtin_commands_overide` is set to `false`, the function will silently fail.
|
||||||
|
If `bypass` is set to `true` the function will not take care of the above setting.
|
||||||
|
Will warn if trying to rename to already existing command.
|
||||||
|
|
||||||
|
### paterns
|
||||||
|
|
||||||
|
mcl_commands adds many types for patterns
|
||||||
|
If not specified, a value will be by default with the word pattern.
|
||||||
|
|
||||||
|
* pos value must be pos
|
||||||
|
* text value must be text WARNING: this pattern must be the last pattern of the subcommand!!
|
||||||
|
* number value must be number
|
||||||
|
* int value must be integer
|
||||||
|
* word value must be word
|
||||||
|
* alpha value must be alphanumeric
|
||||||
|
* modname value must be a valid modname
|
||||||
|
* alphascore
|
||||||
|
* alphanumeric
|
||||||
|
* username: value must be a valid username
|
||||||
|
* json: value must be a json string (will be parsed automaticaly)
|
||||||
|
* color value must be a color string or a valid named color
|
||||||
|
* nodename value must be a valid (existing) node or item name
|
|
@ -0,0 +1,374 @@
|
||||||
|
local S = minetest.get_translator("mcl_commands")
|
||||||
|
local C = minetest.colorize
|
||||||
|
|
||||||
|
local mod_death_messages = minetest.get_modpath("mcl_death_messages")
|
||||||
|
|
||||||
|
local parse_json = minetest.parse_json
|
||||||
|
local get_modpath = minetest.get_modpath
|
||||||
|
|
||||||
|
mcl_commands.types = {
|
||||||
|
pos = {"%(? *(%-?[%d.]+) *,? *(%-?[%d.]+) *,? *(%-?[%d.]+) *%)?",
|
||||||
|
function(res, pointer)
|
||||||
|
local pos = {
|
||||||
|
x = tonumber(res[pointer]),
|
||||||
|
y = tonumber(res[pointer + 1]),
|
||||||
|
z = tonumber(res[pointer + 2])
|
||||||
|
}
|
||||||
|
if pos.x and pos.y and pos.z then
|
||||||
|
return nil, pos, pointer+3
|
||||||
|
else
|
||||||
|
return S("Pos is invalid!")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
text = {"(.+)",
|
||||||
|
function(res, pointer)
|
||||||
|
if res[pointer] == tostring(res[pointer]) then
|
||||||
|
return nil, res[pointer], pointer+1
|
||||||
|
else
|
||||||
|
return S("Text is invalid!")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
number = {"(%-?[%d.]+)}",
|
||||||
|
function(res, pointer)
|
||||||
|
if res[pointer] == tonumber(res[pointer]) then
|
||||||
|
return nil, tonumber(res[pointer]), pointer+1
|
||||||
|
else
|
||||||
|
return S("Number is invalid!")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
int = {"(%-?[%d]+)}",
|
||||||
|
function(res, pointer)
|
||||||
|
if res[pointer] == math.floor(tonumber(res[pointer])) then
|
||||||
|
return nil, tonumber(res[pointer]), pointer+1
|
||||||
|
else
|
||||||
|
return S("Int is invalid!")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
word = {"([^ ]+)",
|
||||||
|
function(res, pointer)
|
||||||
|
if res[pointer] == tostring(res[pointer]) then
|
||||||
|
return nil, tostring(res[pointer]), pointer+1
|
||||||
|
else
|
||||||
|
return S("Word is invalid!")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
quotestring = {'"+(.-)"+',
|
||||||
|
function(res, pointer)
|
||||||
|
if res[pointer] == tostring(res[pointer]) then
|
||||||
|
return nil, string.sub(tostring(res[pointer]), 2, #res[pointer]-1), pointer+1
|
||||||
|
else
|
||||||
|
return S("String is invalid!")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
alpha = {"([A-Za-z]+)}",
|
||||||
|
function(res, pointer)
|
||||||
|
if res[pointer] then
|
||||||
|
return nil, res[pointer], pointer+1
|
||||||
|
else
|
||||||
|
return S("Alpha is invalid!")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
modname = {"([a-z0-9_]+)}",
|
||||||
|
function(res, pointer)
|
||||||
|
if get_modpath(res[pointer]) then
|
||||||
|
return nil, res[pointer], pointer+1
|
||||||
|
else
|
||||||
|
return S("Modname is invalid!")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
alphascore = {"([A-Za-z_]+)",
|
||||||
|
function(res, pointer)
|
||||||
|
if res[pointer] then --What is alphascore?
|
||||||
|
return nil, res[pointer], pointer+1
|
||||||
|
else
|
||||||
|
return S("Alphascore is invalid!")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
alphanumeric = {"([A-Za-z0-9]+)}",
|
||||||
|
function(res, pointer)
|
||||||
|
if res[pointer] then --What is alphanumerical?
|
||||||
|
return nil, res[pointer], pointer+1
|
||||||
|
else
|
||||||
|
return S("Alphanumerical is invalid!")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
username = {"([A-Za-z0-9-_]+)",
|
||||||
|
function(res, pointer)
|
||||||
|
--if minetest.player_exists(res[pointer]) then
|
||||||
|
if res[pointer] then
|
||||||
|
return nil, res[pointer], pointer+1
|
||||||
|
else
|
||||||
|
return S("Player doesn't exist.")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
target = {"([A-Za-z0-9-_]+)",
|
||||||
|
function(res, pointer)
|
||||||
|
--if minetest.player_exists(res[pointer]) then
|
||||||
|
if res[pointer] then
|
||||||
|
return nil, res[pointer], pointer+1
|
||||||
|
else
|
||||||
|
return S("Player doesn't exist.")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
json = {"(.+)", --FIXME
|
||||||
|
function(res, pointer)
|
||||||
|
local parsed = parse_json(res[pointer])
|
||||||
|
if parsed then
|
||||||
|
return nil, parsed, pointer+1
|
||||||
|
else
|
||||||
|
return S("Json failed to parse!")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
color = {"([^ ]+)", --FIXME
|
||||||
|
function(res, pointer)
|
||||||
|
local color = mcl_util.get_color(res[pointer])
|
||||||
|
if color then
|
||||||
|
return nil, color, pointer+1
|
||||||
|
else
|
||||||
|
return S("Color is not a valid color name or hexadecimal!")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
nodename = {"(%l+[%w_]+%:?[_%l]+[%w_]*)",
|
||||||
|
function(res, pointer)
|
||||||
|
if minetest.registered_items[res[pointer]] then
|
||||||
|
return nil, res[pointer], pointer+1
|
||||||
|
else
|
||||||
|
return S("Nodename is invalid")
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
}
|
||||||
|
|
||||||
|
function mcl_commands.register_command(name, def)
|
||||||
|
def = def or {}
|
||||||
|
local cmd = mcl_commands.build(name, def)
|
||||||
|
if minetest.registered_chatcommands[name] then
|
||||||
|
error("[mcl_commands] Failed to register command: ["..name.."] command already existing! Use mcl_commands.overide_command() if you want to overide existing command")
|
||||||
|
end
|
||||||
|
minetest.register_chatcommand(name, cmd)
|
||||||
|
minetest.log("action", "[mcl_commands] ["..name.."] command registered successfully")
|
||||||
|
return cmd
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_commands.override_command(name, def)
|
||||||
|
def = def or {}
|
||||||
|
local cmd = mcl_commands.build(name, def)
|
||||||
|
if minetest.registered_chatcommands[name] then
|
||||||
|
minetest.unregister_chatcommand(name)
|
||||||
|
end
|
||||||
|
minetest.register_chatcommand(name, cmd)
|
||||||
|
minetest.log("action", "[mcl_commands] ["..name.."] command overridden successfully")
|
||||||
|
return cmd
|
||||||
|
end
|
||||||
|
|
||||||
|
local STATE_READY = 1
|
||||||
|
local STATE_PARAM = 2
|
||||||
|
local STATE_PARAM_TYPE = 3
|
||||||
|
local bad_chars = {"(", ")", ".", "%", "+", "-", "*", "?", "[", "^", "$"}
|
||||||
|
local function escape(char)
|
||||||
|
if bad_chars[char] then
|
||||||
|
return "%" .. char
|
||||||
|
else
|
||||||
|
return char
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local dprint = function() end
|
||||||
|
|
||||||
|
function mcl_commands.build(name, chat_def)
|
||||||
|
local cmd = {
|
||||||
|
_subs = {}
|
||||||
|
}
|
||||||
|
function cmd:sub(route, def)
|
||||||
|
dprint("Parsing " .. route)
|
||||||
|
|
||||||
|
if string.trim then
|
||||||
|
route = string.trim(route)
|
||||||
|
end
|
||||||
|
|
||||||
|
local sub = {
|
||||||
|
pattern = "^",
|
||||||
|
params = {},
|
||||||
|
func = def.func,
|
||||||
|
privs = def.privs or {},
|
||||||
|
desc = def.desc,
|
||||||
|
params_desc = def.params or "",
|
||||||
|
}
|
||||||
|
|
||||||
|
-- End of param reached: add it to the pattern
|
||||||
|
local param = ""
|
||||||
|
local param_type = ""
|
||||||
|
local should_be_eos = false
|
||||||
|
local function finishParam()
|
||||||
|
if param ~= "" and param_type ~= "" then
|
||||||
|
dprint(" - Found param " .. param .. " type " .. param_type)
|
||||||
|
|
||||||
|
local pattern = mcl_commands.types[param_type][1]
|
||||||
|
if not pattern then
|
||||||
|
error("Unrecognised param_type=" .. param_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
sub.pattern = sub.pattern .. pattern
|
||||||
|
|
||||||
|
table.insert(sub.params, param_type)
|
||||||
|
|
||||||
|
param = ""
|
||||||
|
param_type = ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Iterate through the route to find params
|
||||||
|
local state = STATE_READY
|
||||||
|
local catching_space = false
|
||||||
|
local match_space = " " -- change to "%s" to also catch tabs and newlines
|
||||||
|
local catch_space = match_space.."+"
|
||||||
|
for i = 1, #route do
|
||||||
|
local c = route:sub(i, i)
|
||||||
|
if should_be_eos then
|
||||||
|
error("Should be end of string. Nothing is allowed after a param of type text.")
|
||||||
|
end
|
||||||
|
|
||||||
|
if state == STATE_READY then
|
||||||
|
if c == ":" then
|
||||||
|
dprint(" - Found :, entering param")
|
||||||
|
state = STATE_PARAM
|
||||||
|
param_type = "word"
|
||||||
|
catching_space = false
|
||||||
|
elseif c:match(match_space) then
|
||||||
|
print(" - Found space")
|
||||||
|
if not catching_space then
|
||||||
|
catching_space = true
|
||||||
|
sub.pattern = sub.pattern .. catch_space
|
||||||
|
end
|
||||||
|
else
|
||||||
|
catching_space = false
|
||||||
|
sub.pattern = sub.pattern .. escape(c)
|
||||||
|
end
|
||||||
|
elseif state == STATE_PARAM then
|
||||||
|
if c == ":" then
|
||||||
|
dprint(" - Found :, entering param type")
|
||||||
|
state = STATE_PARAM_TYPE
|
||||||
|
param_type = ""
|
||||||
|
elseif c:match(match_space) then
|
||||||
|
print(" - Found whitespace, leaving param")
|
||||||
|
state = STATE_READY
|
||||||
|
finishParam()
|
||||||
|
catching_space = true
|
||||||
|
sub.pattern = sub.pattern .. catch_space
|
||||||
|
elseif c:match("%W") then
|
||||||
|
dprint(" - Found nonalphanum, leaving param")
|
||||||
|
state = STATE_READY
|
||||||
|
finishParam()
|
||||||
|
sub.pattern = sub.pattern .. escape(c)
|
||||||
|
else
|
||||||
|
param = param .. c
|
||||||
|
end
|
||||||
|
elseif state == STATE_PARAM_TYPE then
|
||||||
|
if c:match(match_space) then
|
||||||
|
print(" - Found space, leaving param type")
|
||||||
|
state = STATE_READY
|
||||||
|
finishParam()
|
||||||
|
catching_space = true
|
||||||
|
sub.pattern = sub.pattern .. catch_space
|
||||||
|
elseif c:match("%W") then
|
||||||
|
dprint(" - Found nonalphanum, leaving param type")
|
||||||
|
state = STATE_READY
|
||||||
|
finishParam()
|
||||||
|
sub.pattern = sub.pattern .. escape(c)
|
||||||
|
else
|
||||||
|
param_type = param_type .. c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dprint(" - End of route")
|
||||||
|
finishParam()
|
||||||
|
sub.pattern = sub.pattern .. "$"
|
||||||
|
dprint("Pattern: " .. sub.pattern)
|
||||||
|
|
||||||
|
table.insert(self._subs, sub)
|
||||||
|
end
|
||||||
|
|
||||||
|
if chat_def.func then
|
||||||
|
chat_def.func(cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd.func = function(name, param)
|
||||||
|
local msg
|
||||||
|
for i = 1, #cmd._subs do
|
||||||
|
local sub = cmd._subs[i]
|
||||||
|
local res = { string.match(param, sub.pattern) }
|
||||||
|
if #res > 0 then
|
||||||
|
local pointer = 1
|
||||||
|
local params = { name }
|
||||||
|
for j = 1, #sub.params do
|
||||||
|
local param = sub.params[j]
|
||||||
|
local value
|
||||||
|
if mcl_commands.types[param] then
|
||||||
|
msg, value, pointer = mcl_commands.check_type(param, res, pointer)
|
||||||
|
table.insert(params, value)
|
||||||
|
else
|
||||||
|
table.insert(params, res[pointer])
|
||||||
|
pointer = pointer + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local can_execute, missing_privs = minetest.check_player_privs(name, sub.privs)
|
||||||
|
if can_execute then
|
||||||
|
if table.unpack then
|
||||||
|
-- lua 5.2 or later
|
||||||
|
return sub.func(table.unpack(params))
|
||||||
|
else
|
||||||
|
-- lua 5.1 or earlier
|
||||||
|
return sub.func(unpack(params))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local missing_privs_str = ""
|
||||||
|
for i = 1, #missing_privs do
|
||||||
|
if not i == #missing_privs then
|
||||||
|
missing_privs_str = missing_privs_str..missing_privs[i].." "
|
||||||
|
else
|
||||||
|
missing_privs_str = missing_privs_str..missing_privs[i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false, C(mcl_colors.RED, S("You don't have permission to run this command (missing privilege: "..missing_privs_str..")"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false, C(mcl_colors.RED, msg)
|
||||||
|
end
|
||||||
|
if chat_def.params then
|
||||||
|
cmd.params = chat_def.params
|
||||||
|
else
|
||||||
|
cmd.params = ""
|
||||||
|
end
|
||||||
|
cmd.privs = chat_def.privs
|
||||||
|
cmd.description = chat_def.description
|
||||||
|
return cmd
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_commands.check_type(type, res, pointer)
|
||||||
|
return mcl_commands.types[type][2](res, pointer)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_commands.register_chatcommand_alias(alias, cmd, bypass)
|
||||||
|
if not bypass then bypass = false end
|
||||||
|
if minetest.registered_chatcommands[alias] then
|
||||||
|
minetest.log("warning", "[mcl_commands] trying to alias ["..cmd.."] to already existing ["..alias.."] command")
|
||||||
|
elseif minetest.settings:get_bool("mcl_builtin_commands_overide", true) or bypass then
|
||||||
|
minetest.register_chatcommand(alias, minetest.chatcommands[cmd])
|
||||||
|
minetest.log("action", "[mcl_commands] ["..cmd.."] command aliased successfully to ["..alias.."]")
|
||||||
|
else
|
||||||
|
minetest.log("action", "[mcl_commands] ["..cmd.."] command not aliased to ["..alias.."]")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_commands.rename_chatcommand(newname, cmd, bypass)
|
||||||
|
if not bypass then bypass = false end
|
||||||
|
if minetest.registered_chatcommands[newname] then
|
||||||
|
minetest.log("warning", "[mcl_commands] trying to rename ["..cmd.."] to already existing ["..alias.."] command")
|
||||||
|
elseif minetest.settings:get_bool("mcl_builtin_commands_overide", true) or bypass then
|
||||||
|
minetest.register_chatcommand(newname, minetest.chatcommands[cmd])
|
||||||
|
minetest.unregister_chatcommand(cmd)
|
||||||
|
minetest.log("action", "[mcl_commands] ["..cmd.."] command renamed successfully to ["..newname.."]")
|
||||||
|
else
|
||||||
|
minetest.log("action", "[mcl_commands] ["..cmd.."] command not renamed to ["..newname.."]")
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,13 @@
|
||||||
|
--mcl_commands
|
||||||
|
--derivated from ChatCommandBuilder by @rubenwardy
|
||||||
|
|
||||||
|
local S = minetest.get_translator("mcl_commands")
|
||||||
|
|
||||||
|
local mod_death_messages = minetest.get_modpath("mcl_death_messages")
|
||||||
|
|
||||||
|
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||||
|
|
||||||
|
mcl_commands = {}
|
||||||
|
|
||||||
|
dofile(modpath.."/api.lua")
|
||||||
|
dofile(modpath.."/utils.lua")
|
|
@ -0,0 +1,6 @@
|
||||||
|
name = mcl_commands
|
||||||
|
author = AFCMS
|
||||||
|
description = MCL2 commands API
|
||||||
|
depends = mcl_colors, mcl_util
|
||||||
|
optional_depends =
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
local S = minetest.get_translator("mcl_commands")
|
||||||
|
|
||||||
|
function mcl_commands.get_target_selector(target, pos, name)
|
||||||
|
if minetest.player_exists(target) then
|
||||||
|
return nil, minetest.get_player_by_name(target)
|
||||||
|
elseif target == "@a" then
|
||||||
|
return nil, minetest.get_connected_players()
|
||||||
|
elseif target == "@r" then
|
||||||
|
local connected = minetest.get_connected_players()
|
||||||
|
return nil, connected[math.random(1, #connected)]
|
||||||
|
elseif target == "@s" then
|
||||||
|
if name then
|
||||||
|
local player = minetest.get_player_by_name(name)
|
||||||
|
end
|
||||||
|
if player then
|
||||||
|
return nil, player
|
||||||
|
else
|
||||||
|
return S("Not a valid player")
|
||||||
|
end
|
||||||
|
elseif target == "@p" then
|
||||||
|
local smallest = math.huge
|
||||||
|
local nearest
|
||||||
|
for _,player in pairs(minetest.get_connected_players()) do
|
||||||
|
local distance = vector.distance(pos, player:get_pos())
|
||||||
|
if distance < min_distance then
|
||||||
|
min_distance = distance
|
||||||
|
nearest = player
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if nearest then
|
||||||
|
return nil, nearest
|
||||||
|
else
|
||||||
|
return S("No player online")
|
||||||
|
end
|
||||||
|
elseif target == "@e" then
|
||||||
|
return minetest.luaentities --TODO: add filtering of not valid entities.
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,169 +0,0 @@
|
||||||
mcl_damage = {
|
|
||||||
modifiers = {},
|
|
||||||
damage_callbacks = {},
|
|
||||||
death_callbacks = {},
|
|
||||||
types = {
|
|
||||||
in_fire = {is_fire = true},
|
|
||||||
lightning_bolt = {is_lightning = true},
|
|
||||||
on_fire = {is_fire = true, bypasses_armor = true},
|
|
||||||
lava = {is_fire = true},
|
|
||||||
hot_floor = {is_fire = true},
|
|
||||||
in_wall = {bypasses_armor = true},
|
|
||||||
drown = {bypasses_armor = true},
|
|
||||||
starve = {bypasses_armor = true, bypasses_magic = true},
|
|
||||||
cactus = {},
|
|
||||||
fall = {bypasses_armor = true},
|
|
||||||
fly_into_wall = {bypasses_armor = true}, -- unused
|
|
||||||
out_of_world = {bypasses_armor = true, bypasses_magic = true, bypasses_invulnerability = true},
|
|
||||||
generic = {bypasses_armor = true},
|
|
||||||
magic = {is_magic = true, bypasses_armor = true},
|
|
||||||
dragon_breath = {is_magic = true, bypasses_armor = true}, -- this is only used for dragon fireball; dragon fireball does not actually deal impact damage tho, so this is unreachable
|
|
||||||
wither = {bypasses_armor = true}, -- unused
|
|
||||||
wither_skull = {is_magic = true, is_explosion = true}, -- this is non-MC but a workaround to get the proper death message
|
|
||||||
anvil = {},
|
|
||||||
falling_node = {}, -- this is falling_block in MC
|
|
||||||
mob = {},
|
|
||||||
player = {},
|
|
||||||
arrow = {is_projectile = true},
|
|
||||||
fireball = {is_projectile = true, is_fire = true},
|
|
||||||
thorns = {is_magic = true},
|
|
||||||
explosion = {is_explosion = true},
|
|
||||||
cramming = {bypasses_armor = true}, -- unused
|
|
||||||
fireworks = {is_explosion = true}, -- unused
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mcl_damage.register_modifier(func, priority)
|
|
||||||
table.insert(mcl_damage.modifiers, {func = func, priority = priority or 0})
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_damage.register_on_damage(func)
|
|
||||||
table.insert(mcl_damage.damage_callbacks, func)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_damage.register_on_death(func)
|
|
||||||
table.insert(mcl_damage.death_callbacks, func)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_damage.run_modifiers(obj, damage, reason)
|
|
||||||
for _, modf in ipairs(mcl_damage.modifiers) do
|
|
||||||
damage = modf.func(obj, damage, reason) or damage
|
|
||||||
if damage == 0 then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return damage
|
|
||||||
end
|
|
||||||
|
|
||||||
local function run_callbacks(funcs, ...)
|
|
||||||
for _, func in pairs(funcs) do
|
|
||||||
func(...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_damage.run_damage_callbacks(obj, damage, reason)
|
|
||||||
run_callbacks(mcl_damage.damage_callbacks, obj, damage, reason)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_damage.run_death_callbacks(obj, reason)
|
|
||||||
run_callbacks(mcl_damage.death_callbacks, obj, reason)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_damage.from_punch(mcl_reason, object)
|
|
||||||
mcl_reason.direct = object
|
|
||||||
local luaentity = mcl_reason.direct:get_luaentity()
|
|
||||||
if luaentity then
|
|
||||||
if luaentity._is_arrow then
|
|
||||||
mcl_reason.type = "arrow"
|
|
||||||
elseif luaentity._is_fireball then
|
|
||||||
mcl_reason.type = "fireball"
|
|
||||||
elseif luaentity._cmi_is_mob then
|
|
||||||
mcl_reason.type = "mob"
|
|
||||||
end
|
|
||||||
mcl_reason.source = mcl_reason.source or luaentity._source_object
|
|
||||||
else
|
|
||||||
mcl_reason.type = "player"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_damage.finish_reason(mcl_reason)
|
|
||||||
mcl_reason.source = mcl_reason.source or mcl_reason.direct
|
|
||||||
mcl_reason.flags = mcl_damage.types[mcl_reason.type]
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_damage.from_mt(mt_reason)
|
|
||||||
if mt_reason._mcl_chached_reason then
|
|
||||||
return mt_reason._mcl_chached_reason
|
|
||||||
end
|
|
||||||
|
|
||||||
local mcl_reason
|
|
||||||
|
|
||||||
if mt_reason._mcl_reason then
|
|
||||||
mcl_reason = mt_reason._mcl_reason
|
|
||||||
else
|
|
||||||
mcl_reason = {type = "generic"}
|
|
||||||
|
|
||||||
if mt_reason._mcl_type then
|
|
||||||
mcl_reason.type = mt_reason._mcl_type
|
|
||||||
elseif mt_reason.type == "fall" then
|
|
||||||
mcl_reason.type = "fall"
|
|
||||||
elseif mt_reason.type == "drown" then
|
|
||||||
mcl_reason.type = "drown"
|
|
||||||
elseif mt_reason.type == "punch" then
|
|
||||||
mcl_damage.from_punch(mcl_reason, mt_reason.object)
|
|
||||||
elseif mt_reason.type == "node_damage" and mt_reason.node then
|
|
||||||
if minetest.get_item_group(mt_reason.node, "fire") > 0 then
|
|
||||||
mcl_reason.type = "in_fire"
|
|
||||||
end
|
|
||||||
if minetest.get_item_group(mt_reason.node, "lava") > 0 then
|
|
||||||
mcl_reason.type = "lava"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for key, value in pairs(mt_reason) do
|
|
||||||
if key:find("_mcl_") == 1 then
|
|
||||||
mcl_reason[key:sub(6, #key)] = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_damage.finish_reason(mcl_reason)
|
|
||||||
mt_reason._mcl_cached_reason = mcl_reason
|
|
||||||
|
|
||||||
return mcl_reason
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_damage.register_type(name, def)
|
|
||||||
mcl_damage.types[name] = def
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
|
||||||
if hp_change < 0 then
|
|
||||||
if player:get_hp() <= 0 then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
hp_change = -mcl_damage.run_modifiers(player, -hp_change, mcl_damage.from_mt(mt_reason))
|
|
||||||
end
|
|
||||||
return hp_change
|
|
||||||
end, true)
|
|
||||||
|
|
||||||
minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
|
||||||
if player:get_hp() > 0 then
|
|
||||||
mt_reason.approved = true
|
|
||||||
if hp_change < 0 then
|
|
||||||
mcl_damage.run_damage_callbacks(player, -hp_change, mcl_damage.from_mt(mt_reason))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end, false)
|
|
||||||
|
|
||||||
minetest.register_on_dieplayer(function(player, mt_reason)
|
|
||||||
if mt_reason.approved then
|
|
||||||
mcl_damage.run_death_callbacks(player, mcl_damage.from_mt(mt_reason))
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
minetest.register_on_mods_loaded(function()
|
|
||||||
table.sort(mcl_damage.modifiers, function(a, b) return a.priority < b.priority end)
|
|
||||||
end)
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
name = mcl_damage
|
|
||||||
author = Fleckenstein
|
|
||||||
description = Minecraft-like damage reason system
|
|
|
@ -12,12 +12,11 @@ under the LGPLv2.1 license.
|
||||||
|
|
||||||
mcl_explosions = {}
|
mcl_explosions = {}
|
||||||
|
|
||||||
local mod_fire = minetest.get_modpath("mcl_fire")
|
local mod_death_messages = minetest.get_modpath("mcl_death_messages") ~= nil
|
||||||
--local CONTENT_FIRE = minetest.get_content_id("mcl_fire:fire")
|
local mod_fire = minetest.get_modpath("mcl_fire") ~= nil
|
||||||
|
local CONTENT_FIRE = minetest.get_content_id("mcl_fire:fire")
|
||||||
|
|
||||||
local math = math
|
local S = minetest.get_translator("mcl_explosions")
|
||||||
local vector = vector
|
|
||||||
local table = table
|
|
||||||
|
|
||||||
local hash_node_position = minetest.hash_node_position
|
local hash_node_position = minetest.hash_node_position
|
||||||
local get_objects_inside_radius = minetest.get_objects_inside_radius
|
local get_objects_inside_radius = minetest.get_objects_inside_radius
|
||||||
|
@ -28,7 +27,6 @@ local get_voxel_manip = minetest.get_voxel_manip
|
||||||
local bulk_set_node = minetest.bulk_set_node
|
local bulk_set_node = minetest.bulk_set_node
|
||||||
local check_for_falling = minetest.check_for_falling
|
local check_for_falling = minetest.check_for_falling
|
||||||
local add_item = minetest.add_item
|
local add_item = minetest.add_item
|
||||||
local pos_to_string = minetest.pos_to_string
|
|
||||||
|
|
||||||
-- Saved sphere explosion shapes for various radiuses
|
-- Saved sphere explosion shapes for various radiuses
|
||||||
local sphere_shapes = {}
|
local sphere_shapes = {}
|
||||||
|
@ -69,48 +67,50 @@ local function compute_sphere_rays(radius)
|
||||||
local rays = {}
|
local rays = {}
|
||||||
local sphere = {}
|
local sphere = {}
|
||||||
|
|
||||||
local function add_ray(pos)
|
for i=1, 2 do
|
||||||
|
for y = -radius, radius do
|
||||||
|
for z = -radius, radius do
|
||||||
|
for x = -radius, 0, 1 do
|
||||||
|
local d = x * x + y * y + z * z
|
||||||
|
if d <= radius * radius then
|
||||||
|
local pos = { x = x, y = y, z = z }
|
||||||
sphere[hash_node_position(pos)] = pos
|
sphere[hash_node_position(pos)] = pos
|
||||||
end
|
|
||||||
|
|
||||||
for y = -radius, radius do
|
|
||||||
for z = -radius, radius do
|
|
||||||
for x = -radius, 0 do
|
|
||||||
local d = x * x + y * y + z * z
|
|
||||||
if d <= radius * radius then
|
|
||||||
add_ray(vector.new(x, y, z))
|
|
||||||
add_ray(vector.new(-x, y, z))
|
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i=1,2 do
|
||||||
for x = -radius, radius do
|
for x = -radius, radius do
|
||||||
for z = -radius, radius do
|
for z = -radius, radius do
|
||||||
for y = -radius, 0 do
|
for y = -radius, 0, 1 do
|
||||||
local d = x * x + y * y + z * z
|
local d = x * x + y * y + z * z
|
||||||
if d <= radius * radius then
|
if d <= radius * radius then
|
||||||
add_ray(vector.new(x, y, z))
|
local pos = { x = x, y = y, z = z }
|
||||||
add_ray(vector.new(x, -y, z))
|
sphere[hash_node_position(pos)] = pos
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i=1,2 do
|
||||||
for x = -radius, radius do
|
for x = -radius, radius do
|
||||||
for y = -radius, radius do
|
for y = -radius, radius do
|
||||||
for z = -radius, 0 do
|
for z = -radius, 0, 1 do
|
||||||
local d = x * x + y * y + z * z
|
local d = x * x + y * y + z * z
|
||||||
if d <= radius * radius then
|
if d <= radius * radius then
|
||||||
add_ray(vector.new(x, y, z))
|
local pos = { x = x, y = y, z = z }
|
||||||
add_ray(vector.new(x, y, -z))
|
sphere[hash_node_position(pos)] = pos
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
for _, pos in pairs(sphere) do
|
for _, pos in pairs(sphere) do
|
||||||
rays[#rays + 1] = vector.normalize(pos)
|
rays[#rays + 1] = vector.normalize(pos)
|
||||||
|
@ -150,8 +150,7 @@ end
|
||||||
-- raydirs - The directions for each ray
|
-- raydirs - The directions for each ray
|
||||||
-- radius - The maximum distance each ray will go
|
-- radius - The maximum distance each ray will go
|
||||||
-- info - Table containing information about explosion
|
-- info - Table containing information about explosion
|
||||||
-- direct - direct source object of the damage (optional)
|
-- puncher - object that punches other objects (optional)
|
||||||
-- source - indirect source object of the damage (optional)
|
|
||||||
--
|
--
|
||||||
-- Values in info:
|
-- Values in info:
|
||||||
-- drop_chance - The chance that destroyed nodes will drop their items
|
-- drop_chance - The chance that destroyed nodes will drop their items
|
||||||
|
@ -166,7 +165,7 @@ end
|
||||||
-- Note that this function has been optimized, it contains code which has been
|
-- Note that this function has been optimized, it contains code which has been
|
||||||
-- inlined to avoid function calls and unnecessary table creation. This was
|
-- inlined to avoid function calls and unnecessary table creation. This was
|
||||||
-- measured to give a significant performance increase.
|
-- measured to give a significant performance increase.
|
||||||
local function trace_explode(pos, strength, raydirs, radius, info, direct, source)
|
local function trace_explode(pos, strength, raydirs, radius, info, puncher)
|
||||||
local vm = get_voxel_manip()
|
local vm = get_voxel_manip()
|
||||||
|
|
||||||
local emin, emax = vm:read_from_map(vector.subtract(pos, radius),
|
local emin, emax = vm:read_from_map(vector.subtract(pos, radius),
|
||||||
|
@ -177,11 +176,14 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
|
|
||||||
local ystride = (emax.x - emin_x + 1)
|
local ystride = (emax.x - emin_x + 1)
|
||||||
local zstride = ystride * (emax.y - emin_y + 1)
|
local zstride = ystride * (emax.y - emin_y + 1)
|
||||||
|
local pos_x = pos.x
|
||||||
|
local pos_y = pos.y
|
||||||
|
local pos_z = pos.z
|
||||||
|
|
||||||
--[[local area = VoxelArea:new {
|
local area = VoxelArea:new {
|
||||||
MinEdge = emin,
|
MinEdge = emin,
|
||||||
MaxEdge = emax
|
MaxEdge = emax
|
||||||
}]]
|
}
|
||||||
local data = vm:get_data()
|
local data = vm:get_data()
|
||||||
local destroy = {}
|
local destroy = {}
|
||||||
|
|
||||||
|
@ -210,7 +212,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
npos_x - emin_x + 1
|
npos_x - emin_x + 1
|
||||||
|
|
||||||
local cid = data[idx]
|
local cid = data[idx]
|
||||||
local br = node_blastres[cid] or INDESTRUCT_BLASTRES
|
local br = node_blastres[cid]
|
||||||
if br < INDESTRUCT_BLASTRES and br > max_blast_resistance then
|
if br < INDESTRUCT_BLASTRES and br > max_blast_resistance then
|
||||||
br = max_blast_resistance
|
br = max_blast_resistance
|
||||||
end
|
end
|
||||||
|
@ -245,7 +247,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
local ent = obj:get_luaentity()
|
local ent = obj:get_luaentity()
|
||||||
|
|
||||||
-- Ignore items to lower lag
|
-- Ignore items to lower lag
|
||||||
if (obj:is_player() or (ent and ent.name ~= "__builtin.item")) and obj:get_hp() > 0 then
|
if obj:is_player() or (ent and ent.name ~= '__builtin.item') then
|
||||||
local opos = obj:get_pos()
|
local opos = obj:get_pos()
|
||||||
local collisionbox = nil
|
local collisionbox = nil
|
||||||
|
|
||||||
|
@ -258,12 +260,12 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
|
|
||||||
if collisionbox then
|
if collisionbox then
|
||||||
-- Create rays from random points in the collision box
|
-- Create rays from random points in the collision box
|
||||||
local x1 = collisionbox[1]
|
local x1 = collisionbox[1] * 2
|
||||||
local y1 = collisionbox[2]
|
local y1 = collisionbox[2] * 2
|
||||||
local z1 = collisionbox[3]
|
local z1 = collisionbox[3] * 2
|
||||||
local x2 = collisionbox[4]
|
local x2 = collisionbox[4] * 2
|
||||||
local y2 = collisionbox[5]
|
local y2 = collisionbox[5] * 2
|
||||||
local z2 = collisionbox[6]
|
local z2 = collisionbox[6] * 2
|
||||||
local x_len = math.abs(x2 - x1)
|
local x_len = math.abs(x2 - x1)
|
||||||
local y_len = math.abs(y2 - y1)
|
local y_len = math.abs(y2 - y1)
|
||||||
local z_len = math.abs(z2 - z1)
|
local z_len = math.abs(z2 - z1)
|
||||||
|
@ -319,6 +321,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
impact = 0
|
impact = 0
|
||||||
end
|
end
|
||||||
local damage = math.floor((impact * impact + impact) * 7 * strength + 1)
|
local damage = math.floor((impact * impact + impact) * 7 * strength + 1)
|
||||||
|
local source = puncher or obj
|
||||||
|
|
||||||
local sleep_formspec_doesnt_close_mt53 = false
|
local sleep_formspec_doesnt_close_mt53 = false
|
||||||
if obj:is_player() then
|
if obj:is_player() then
|
||||||
|
@ -330,22 +333,26 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
sleep_formspec_doesnt_close_mt53 = true
|
sleep_formspec_doesnt_close_mt53 = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if mod_death_messages then
|
||||||
|
mcl_death_messages.player_damage(obj, S("@1 was caught in an explosion.", name))
|
||||||
|
end
|
||||||
|
if rawget(_G, "armor") and armor.last_damage_types then
|
||||||
|
armor.last_damage_types[name] = "explosion"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if sleep_formspec_doesnt_close_mt53 then
|
if sleep_formspec_doesnt_close_mt53 then
|
||||||
minetest.after(0.3, function() -- 0.2 is minimum delay for closing old formspec and open died formspec -- TODO: REMOVE THIS IN THE FUTURE
|
minetest.after(0.3, function(obj, damage, impact, punch_dir) -- 0.2 is minimum delay for closing old formspec and open died formspec -- TODO: REMOVE THIS IN THE FUTURE
|
||||||
if not obj:is_player() then
|
if not obj then return end
|
||||||
return
|
obj:punch(obj, 10, { damage_groups = { full_punch_interval = 1, fleshy = damage, knockback = impact * 20.0 } }, punch_dir)
|
||||||
end
|
|
||||||
|
|
||||||
mcl_util.deal_damage(obj, damage, {type = "explosion", direct = direct, source = source})
|
|
||||||
|
|
||||||
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
||||||
end)
|
end, obj, damage, impact, vector.new(punch_dir))
|
||||||
else
|
else
|
||||||
mcl_util.deal_damage(obj, damage, {type = "explosion", direct = direct, source = source})
|
obj:punch(source, 10, { damage_groups = { full_punch_interval = 1, fleshy = damage, knockback = impact * 20.0 } }, punch_dir)
|
||||||
|
|
||||||
if obj:is_player() or ent.tnt_knockback then
|
if obj:is_player() then
|
||||||
|
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
||||||
|
elseif ent.tnt_knockback then
|
||||||
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -361,9 +368,9 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
local on_blast = node_on_blast[data[idx]]
|
local on_blast = node_on_blast[data[idx]]
|
||||||
local remove = true
|
local remove = true
|
||||||
|
|
||||||
if do_drop or on_blast then
|
if do_drop or on_blast ~= nil then
|
||||||
local npos = get_position_from_hash(hash)
|
local npos = get_position_from_hash(hash)
|
||||||
if on_blast then
|
if on_blast ~= nil then
|
||||||
on_blast(npos, 1.0, do_drop)
|
on_blast(npos, 1.0, do_drop)
|
||||||
remove = false
|
remove = false
|
||||||
else
|
else
|
||||||
|
@ -405,7 +412,8 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Log explosion
|
-- Log explosion
|
||||||
minetest.log("action", "Explosion at "..pos_to_string(pos).." with strength "..strength.." and radius "..radius)
|
minetest.log('action', 'Explosion at ' .. minetest.pos_to_string(pos) ..
|
||||||
|
' with strength ' .. strength .. ' and radius ' .. radius)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create an explosion with strength at pos.
|
-- Create an explosion with strength at pos.
|
||||||
|
@ -414,8 +422,7 @@ end
|
||||||
-- pos - The position where the explosion originates from
|
-- pos - The position where the explosion originates from
|
||||||
-- strength - The blast strength of the explosion (a TNT explosion uses 4)
|
-- strength - The blast strength of the explosion (a TNT explosion uses 4)
|
||||||
-- info - Table containing information about explosion
|
-- info - Table containing information about explosion
|
||||||
-- direct - direct source object of the damage (optional)
|
-- puncher - object that is reported as source of punches/damage (optional)
|
||||||
-- source - indirect source object of the damage (optional)
|
|
||||||
--
|
--
|
||||||
-- Values in info:
|
-- Values in info:
|
||||||
-- drop_chance - If specified becomes the drop chance of all nodes in the
|
-- drop_chance - If specified becomes the drop chance of all nodes in the
|
||||||
|
@ -429,7 +436,7 @@ end
|
||||||
-- griefing - If true, the explosion will destroy nodes (default: true)
|
-- griefing - If true, the explosion will destroy nodes (default: true)
|
||||||
-- grief_protected - If true, the explosion will also destroy nodes which have
|
-- grief_protected - If true, the explosion will also destroy nodes which have
|
||||||
-- been protected (default: false)
|
-- been protected (default: false)
|
||||||
function mcl_explosions.explode(pos, strength, info, direct, source)
|
function mcl_explosions.explode(pos, strength, info, puncher)
|
||||||
if info == nil then
|
if info == nil then
|
||||||
info = {}
|
info = {}
|
||||||
end
|
end
|
||||||
|
@ -458,7 +465,7 @@ function mcl_explosions.explode(pos, strength, info, direct, source)
|
||||||
info.drop_chance = 0
|
info.drop_chance = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
trace_explode(pos, strength, shape, radius, info, direct, source)
|
trace_explode(pos, strength, shape, radius, info, puncher)
|
||||||
|
|
||||||
if info.particles then
|
if info.particles then
|
||||||
add_particles(pos, radius)
|
add_particles(pos, radius)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
# textdomain:mcl_explosions
|
||||||
|
@1 was caught in an explosion.=@1 wurde Opfer einer Explosion.
|
|
@ -0,0 +1,2 @@
|
||||||
|
# textdomain:mcl_explosions
|
||||||
|
@1 was caught in an explosion.=@1 a été pris dans une explosion.
|
|
@ -1,2 +0,0 @@
|
||||||
# textdomain:mcl_explosions
|
|
||||||
@1 was caught in an explosion.=@1 została wysadzona.
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
# textdomain:mcl_explosions
|
||||||
|
@1 was caught in an explosion.=@1 не удалось пережить взрыва.
|
|
@ -0,0 +1,2 @@
|
||||||
|
# textdomain:mcl_explosions
|
||||||
|
@1 was caught in an explosion.=
|
|
@ -32,9 +32,9 @@ local singlenode = mg_name == "singlenode"
|
||||||
|
|
||||||
-- Calculate mapgen_edge_min/mapgen_edge_max
|
-- Calculate mapgen_edge_min/mapgen_edge_max
|
||||||
mcl_vars.chunksize = math.max(1, tonumber(minetest.get_mapgen_setting("chunksize")) or 5)
|
mcl_vars.chunksize = math.max(1, tonumber(minetest.get_mapgen_setting("chunksize")) or 5)
|
||||||
mcl_vars.MAP_BLOCKSIZE = math.max(1, minetest.MAP_BLOCKSIZE or 16)
|
mcl_vars.MAP_BLOCKSIZE = math.max(1, core.MAP_BLOCKSIZE or 16)
|
||||||
mcl_vars.mapgen_limit = math.max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000)
|
mcl_vars.mapgen_limit = math.max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000)
|
||||||
mcl_vars.MAX_MAP_GENERATION_LIMIT = math.max(1, minetest.MAX_MAP_GENERATION_LIMIT or 31000)
|
mcl_vars.MAX_MAP_GENERATION_LIMIT = math.max(1, core.MAX_MAP_GENERATION_LIMIT or 31000)
|
||||||
local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2)
|
local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2)
|
||||||
mcl_vars.central_chunk_offset_in_nodes = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
|
mcl_vars.central_chunk_offset_in_nodes = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
|
||||||
mcl_vars.chunk_size_in_nodes = mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE
|
mcl_vars.chunk_size_in_nodes = mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE
|
||||||
|
|
|
@ -40,9 +40,10 @@ function mcl_loot.get_loot(loot_definitions, pr)
|
||||||
total_weight = total_weight + (loot_definitions.items[i].weight or 1)
|
total_weight = total_weight + (loot_definitions.items[i].weight or 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
--local stacks_min = loot_definitions.stacks_min or 1
|
local stacks_min = loot_definitions.stacks_min
|
||||||
--local stacks_max = loot_definitions.stacks_max or 1
|
local stacks_max = loot_definitions.stacks_max
|
||||||
|
if not stacks_min then stacks_min = 1 end
|
||||||
|
if not stacks_max then stacks_max = 1 end
|
||||||
local stacks = pr:next(loot_definitions.stacks_min, loot_definitions.stacks_max)
|
local stacks = pr:next(loot_definitions.stacks_min, loot_definitions.stacks_max)
|
||||||
for s=1, stacks do
|
for s=1, stacks do
|
||||||
local r = pr:next(1, total_weight)
|
local r = pr:next(1, total_weight)
|
||||||
|
|
|
@ -1,12 +1,3 @@
|
||||||
local vector = vector
|
|
||||||
local table = table
|
|
||||||
|
|
||||||
local hash_node_position = minetest.hash_node_position
|
|
||||||
local add_particlespawner = minetest.add_particlespawner
|
|
||||||
local delete_particlespawner = minetest.delete_particlespawner
|
|
||||||
|
|
||||||
local ipairs = ipairs
|
|
||||||
|
|
||||||
mcl_particles = {}
|
mcl_particles = {}
|
||||||
|
|
||||||
-- Table of particlespawner IDs on a per-node hash basis
|
-- Table of particlespawner IDs on a per-node hash basis
|
||||||
|
@ -41,11 +32,11 @@ function mcl_particles.add_node_particlespawner(pos, particlespawner_definition,
|
||||||
if allowed_level == 0 or levels[level] > allowed_level then
|
if allowed_level == 0 or levels[level] > allowed_level then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local poshash = hash_node_position(pos)
|
local poshash = minetest.hash_node_position(pos)
|
||||||
if not poshash then
|
if not poshash then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local id = add_particlespawner(particlespawner_definition)
|
local id = minetest.add_particlespawner(particlespawner_definition)
|
||||||
if id == -1 then
|
if id == -1 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -56,8 +47,6 @@ function mcl_particles.add_node_particlespawner(pos, particlespawner_definition,
|
||||||
return id
|
return id
|
||||||
end
|
end
|
||||||
|
|
||||||
local add_node_particlespawner = mcl_particles.add_node_particlespawner
|
|
||||||
|
|
||||||
-- Deletes all particlespawners that are assigned to a node position.
|
-- Deletes all particlespawners that are assigned to a node position.
|
||||||
-- If no particlespawners exist for this position, nothing happens.
|
-- If no particlespawners exist for this position, nothing happens.
|
||||||
-- pos: Node positon. MUST use integer values!
|
-- pos: Node positon. MUST use integer values!
|
||||||
|
@ -66,66 +55,14 @@ function mcl_particles.delete_node_particlespawners(pos)
|
||||||
if allowed_level == 0 then
|
if allowed_level == 0 then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local poshash = hash_node_position(pos)
|
local poshash = minetest.hash_node_position(pos)
|
||||||
local ids = particle_nodes[poshash]
|
local ids = particle_nodes[poshash]
|
||||||
if ids then
|
if ids then
|
||||||
for i=1, #ids do
|
for i=1, #ids do
|
||||||
delete_particlespawner(ids[i])
|
minetest.delete_particlespawner(ids[i])
|
||||||
end
|
end
|
||||||
particle_nodes[poshash] = nil
|
particle_nodes[poshash] = nil
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 3 exptime variants because the animation is not tied to particle expiration time.
|
|
||||||
-- 3 colorized variants to imitate minecraft's
|
|
||||||
local smoke_pdef_cached = {}
|
|
||||||
|
|
||||||
function mcl_particles.spawn_smoke(pos, name, smoke_pdef_base)
|
|
||||||
local new_minpos = vector.add(pos, smoke_pdef_base.minrelpos)
|
|
||||||
local new_maxpos = vector.add(pos, smoke_pdef_base.maxrelpos)
|
|
||||||
|
|
||||||
-- populate the cache
|
|
||||||
if smoke_pdef_cached[name] then
|
|
||||||
for i, smoke_pdef in ipairs(smoke_pdef_cached[name]) do
|
|
||||||
smoke_pdef.minpos = new_minpos
|
|
||||||
smoke_pdef.maxpos = new_maxpos
|
|
||||||
add_node_particlespawner(pos, smoke_pdef, "high")
|
|
||||||
end
|
|
||||||
-- cache already populated
|
|
||||||
else
|
|
||||||
smoke_pdef_cached[name] = {}
|
|
||||||
|
|
||||||
local smoke_pdef = table.copy(smoke_pdef_base)
|
|
||||||
smoke_pdef.amount = smoke_pdef_base.amount / 9
|
|
||||||
smoke_pdef.time = 0
|
|
||||||
smoke_pdef.animation = {
|
|
||||||
type = "vertical_frames",
|
|
||||||
aspect_w = 8,
|
|
||||||
aspect_h = 8,
|
|
||||||
-- length = 3 exptime variants
|
|
||||||
}
|
|
||||||
smoke_pdef.collisiondetection = true
|
|
||||||
smoke_pdef.minpos = new_minpos
|
|
||||||
smoke_pdef.maxpos = new_maxpos
|
|
||||||
|
|
||||||
-- the last frame plays for 1/8 * N seconds, so we can take advantage of it
|
|
||||||
-- to have varying exptime for each variant.
|
|
||||||
local exptimes = { 0.175, 0.375, 1.0 }
|
|
||||||
local colorizes = { "199", "209", "243" } -- round(78%, 82%, 90% of 256) - 1
|
|
||||||
|
|
||||||
for _,exptime in ipairs(exptimes) do
|
|
||||||
for _,colorize in ipairs(colorizes) do
|
|
||||||
smoke_pdef.maxexptime = exptime * smoke_pdef_base.maxexptime
|
|
||||||
smoke_pdef.animation.length = exptime + 0.1
|
|
||||||
-- minexptime must be set such that the last frame is actully rendered,
|
|
||||||
-- even if its very short. Larger exptime -> larger range
|
|
||||||
smoke_pdef.minexptime = math.min(exptime, (7.0/8.0 * (exptime + 0.1) + 0.1))
|
|
||||||
smoke_pdef.texture = "mcl_particles_smoke_anim.png^[colorize:#000000:" ..colorize
|
|
||||||
add_node_particlespawner(pos, smoke_pdef, "high")
|
|
||||||
table.insert(smoke_pdef_cached[name], table.copy(smoke_pdef))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 148 B |
Before Width: | Height: | Size: 154 B |
Before Width: | Height: | Size: 155 B |
Before Width: | Height: | Size: 165 B |
|
@ -0,0 +1,11 @@
|
||||||
|
mcl_util.registered_blacklisted_entities = {}
|
||||||
|
function mcl_util.register_blacklisted_entities(type)
|
||||||
|
mcl_util.registered_blacklisted_entities[type] = true
|
||||||
|
end
|
||||||
|
function mcl_util.get_real_entities()
|
||||||
|
for key, val in pairs(minetest.luaentities) do
|
||||||
|
local def = minetest.registered_entities[minetest.luaentities[key].name]
|
||||||
|
if not mcl_util.registered_blacklisted_entities[def.type] then
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,27 +1,7 @@
|
||||||
|
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||||
|
|
||||||
mcl_util = {}
|
mcl_util = {}
|
||||||
|
|
||||||
-- Updates all values in t using values from to*.
|
|
||||||
function table.update(t, ...)
|
|
||||||
for _, to in ipairs{...} do
|
|
||||||
for k,v in pairs(to) do
|
|
||||||
t[k] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Updates nil values in t using values from to*.
|
|
||||||
function table.update_nil(t, ...)
|
|
||||||
for _, to in ipairs{...} do
|
|
||||||
for k,v in pairs(to) do
|
|
||||||
if t[k] == nil then
|
|
||||||
t[k] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Based on minetest.rotate_and_place
|
-- Based on minetest.rotate_and_place
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
|
@ -172,7 +152,7 @@ function mcl_util.get_eligible_transfer_item_slot(src_inventory, src_list, dst_i
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns true if itemstack is a shulker box
|
-- Returns true if itemstack is a shulker box
|
||||||
local function is_not_shulker_box(itemstack)
|
local is_not_shulker_box = function(itemstack)
|
||||||
local g = minetest.get_item_group(itemstack:get_name(), "shulker_box")
|
local g = minetest.get_item_group(itemstack:get_name(), "shulker_box")
|
||||||
return g == 0 or g == nil
|
return g == 0 or g == nil
|
||||||
end
|
end
|
||||||
|
@ -234,7 +214,7 @@ function mcl_util.move_item_container(source_pos, destination_pos, source_list,
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Normalize double container by forcing to always use the left segment first
|
-- Normalize double container by forcing to always use the left segment first
|
||||||
local function normalize_double_container(pos, node, ctype)
|
local normalize_double_container = function(pos, node, ctype)
|
||||||
if ctype == 6 then
|
if ctype == 6 then
|
||||||
pos = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right")
|
pos = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right")
|
||||||
if not pos then
|
if not pos then
|
||||||
|
@ -441,131 +421,4 @@ function mcl_util.get_color(colorstr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_util.call_on_rightclick(itemstack, player, pointed_thing)
|
dofile(modpath.."/entities.lua")
|
||||||
-- Call on_rightclick if the pointed node defines it
|
|
||||||
if pointed_thing and pointed_thing.type == "node" then
|
|
||||||
local pos = pointed_thing.under
|
|
||||||
local node = minetest.get_node(pos)
|
|
||||||
if player and not player:get_player_control().sneak then
|
|
||||||
local nodedef = minetest.registered_nodes[node.name]
|
|
||||||
local on_rightclick = nodedef and nodedef.on_rightclick
|
|
||||||
if on_rightclick then
|
|
||||||
return on_rightclick(pos, node, player, itemstack, pointed_thing) or itemstack
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_util.calculate_durability(itemstack)
|
|
||||||
local unbreaking_level = mcl_enchanting.get_enchantment(itemstack, "unbreaking")
|
|
||||||
local armor_uses = minetest.get_item_group(itemstack:get_name(), "mcl_armor_uses")
|
|
||||||
|
|
||||||
local uses
|
|
||||||
|
|
||||||
if armor_uses > 0 then
|
|
||||||
uses = armor_uses
|
|
||||||
if unbreaking_level > 0 then
|
|
||||||
uses = uses / (0.6 + 0.4 / (unbreaking_level + 1))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local def = itemstack:get_definition()
|
|
||||||
if def then
|
|
||||||
local fixed_uses = def._mcl_uses
|
|
||||||
if fixed_uses then
|
|
||||||
uses = fixed_uses
|
|
||||||
if unbreaking_level > 0 then
|
|
||||||
uses = uses * (unbreaking_level + 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
uses = uses or (next(itemstack:get_tool_capabilities().groupcaps) or {}).uses
|
|
||||||
end
|
|
||||||
|
|
||||||
return uses or 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_util.use_item_durability(itemstack, n)
|
|
||||||
local uses = mcl_util.calculate_durability(itemstack)
|
|
||||||
itemstack:add_wear(65535 / uses * n)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_util.deal_damage(target, damage, mcl_reason)
|
|
||||||
local luaentity = target:get_luaentity()
|
|
||||||
|
|
||||||
if luaentity then
|
|
||||||
if luaentity.deal_damage then
|
|
||||||
luaentity:deal_damage(damage, mcl_reason or {type = "generic"})
|
|
||||||
return
|
|
||||||
elseif luaentity._cmi_is_mob then
|
|
||||||
-- local puncher = mcl_reason and mcl_reason.direct or target
|
|
||||||
-- target:punch(puncher, 1.0, {full_punch_interval = 1.0, damage_groups = {fleshy = damage}}, vector.direction(puncher:get_pos(), target:get_pos()), damage)
|
|
||||||
if luaentity.health > 0 then
|
|
||||||
luaentity.health = luaentity.health - damage
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local hp = target:get_hp()
|
|
||||||
|
|
||||||
if hp > 0 then
|
|
||||||
target:set_hp(hp - damage, {_mcl_reason = mcl_reason})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_util.get_hp(obj)
|
|
||||||
local luaentity = obj:get_luaentity()
|
|
||||||
|
|
||||||
if luaentity and luaentity._cmi_is_mob then
|
|
||||||
return luaentity.health
|
|
||||||
else
|
|
||||||
return obj:get_hp()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_util.get_inventory(object, create)
|
|
||||||
if object:is_player() then
|
|
||||||
return object:get_inventory()
|
|
||||||
else
|
|
||||||
local luaentity = object:get_luaentity()
|
|
||||||
local inventory = luaentity.inventory
|
|
||||||
|
|
||||||
if create and not inventory and luaentity.create_inventory then
|
|
||||||
inventory = luaentity:create_inventory()
|
|
||||||
end
|
|
||||||
|
|
||||||
return inventory
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_util.get_wielded_item(object)
|
|
||||||
if object:is_player() then
|
|
||||||
return object:get_wielded_item()
|
|
||||||
else
|
|
||||||
-- ToDo: implement getting wielditems from mobs as soon as mobs have wielditems
|
|
||||||
return ItemStack()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_util.get_object_name(object)
|
|
||||||
if object:is_player() then
|
|
||||||
return object:get_player_name()
|
|
||||||
else
|
|
||||||
local luaentity = object:get_luaentity()
|
|
||||||
|
|
||||||
if not luaentity then
|
|
||||||
return tostring(object)
|
|
||||||
end
|
|
||||||
|
|
||||||
return luaentity.nametag and luaentity.nametag ~= "" and luaentity.nametag or luaentity.description or luaentity.name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_util.replace_mob(obj, mob)
|
|
||||||
local rot = obj:get_yaw()
|
|
||||||
local pos = obj:get_pos()
|
|
||||||
obj:remove()
|
|
||||||
obj = minetest.add_entity(pos, mob)
|
|
||||||
obj:set_yaw(rot)
|
|
||||||
return obj
|
|
||||||
end
|
|
|
@ -12,7 +12,7 @@ Params:
|
||||||
|
|
||||||
* pos: position
|
* pos: position
|
||||||
|
|
||||||
## mcl_worlds.y_to_layer(y)
|
## mcl_worlds.y_to_layer(y)
|
||||||
This function is used to calculate the minetest y layer and dimension of the given <y> minecraft layer.
|
This function is used to calculate the minetest y layer and dimension of the given <y> minecraft layer.
|
||||||
Mainly used for ore generation.
|
Mainly used for ore generation.
|
||||||
Takes an Y coordinate as input and returns:
|
Takes an Y coordinate as input and returns:
|
||||||
|
@ -61,21 +61,20 @@ In mc, you cant use clock in the nether and the end.
|
||||||
|
|
||||||
* pos: position
|
* pos: position
|
||||||
|
|
||||||
## mcl_worlds.register_on_dimension_change(function(player, dimension, last_dimension))
|
## mcl_worlds.register_on_dimension_change(function(player, dimension))
|
||||||
Register a callback function func(player, dimension).
|
Register a callback function func(player, dimension).
|
||||||
It will be called whenever a player changes between dimensions.
|
It will be called whenever a player changes between dimensions.
|
||||||
The void counts as dimension.
|
The void counts as dimension.
|
||||||
|
|
||||||
* player: player, the player who changed of dimension
|
* player: player, the player who changed the dimension
|
||||||
* dimension: string, The new dimension of the player ("overworld", "nether", "end", "void").
|
* dimension: position, The new dimension of the player ("overworld", "nether", "end", "void").
|
||||||
* last_dimension: string, The dimension where the player was ("overworld", "nether", "end", "void").
|
|
||||||
|
|
||||||
|
|
||||||
## mcl_worlds.registered_on_dimension_change
|
## mcl_worlds.registered_on_dimension_change
|
||||||
Table containing all function registered with mcl_worlds.register_on_dimension_change()
|
Table containing all function registered with mcl_worlds.register_on_dimension_change()
|
||||||
|
|
||||||
## mcl_worlds.dimension_change(player, dimension)
|
## mcl_worlds.dimension_change(player, dimension)
|
||||||
Notify this mod of a dimension change of <player> to <dimension>
|
Notify this mod of a dimmension change of <player> to <dimension>
|
||||||
|
|
||||||
* player: player, player who changed the dimension
|
* player: player, player who changed the dimension
|
||||||
* dimension: string, new dimension ("overworld", "nether", "end", "void")
|
* dimension: string, new dimension ("overworld", "nether", "end", "void")
|
|
@ -1,7 +1,5 @@
|
||||||
mcl_worlds = {}
|
mcl_worlds = {}
|
||||||
|
|
||||||
local get_connected_players = minetest.get_connected_players
|
|
||||||
|
|
||||||
-- For a given position, returns a 2-tuple:
|
-- For a given position, returns a 2-tuple:
|
||||||
-- 1st return value: true if pos is in void
|
-- 1st return value: true if pos is in void
|
||||||
-- 2nd return value: true if it is in the deadly part of the void
|
-- 2nd return value: true if it is in the deadly part of the void
|
||||||
|
@ -46,16 +44,12 @@ function mcl_worlds.y_to_layer(y)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local y_to_layer = mcl_worlds.y_to_layer
|
|
||||||
|
|
||||||
-- Takes a pos and returns the dimension it belongs to (same as above)
|
-- Takes a pos and returns the dimension it belongs to (same as above)
|
||||||
function mcl_worlds.pos_to_dimension(pos)
|
function mcl_worlds.pos_to_dimension(pos)
|
||||||
local _, dim = y_to_layer(pos.y)
|
local _, dim = mcl_worlds.y_to_layer(pos.y)
|
||||||
return dim
|
return dim
|
||||||
end
|
end
|
||||||
|
|
||||||
local pos_to_dimension = mcl_worlds.pos_to_dimension
|
|
||||||
|
|
||||||
-- Takes a Minecraft layer and a “dimension” name
|
-- Takes a Minecraft layer and a “dimension” name
|
||||||
-- and returns the corresponding Y coordinate for
|
-- and returns the corresponding Y coordinate for
|
||||||
-- MineClone 2.
|
-- MineClone 2.
|
||||||
|
@ -118,15 +112,12 @@ local last_dimension = {}
|
||||||
-- * player: Player who changed the dimension
|
-- * player: Player who changed the dimension
|
||||||
-- * dimension: New dimension ("overworld", "nether", "end", "void")
|
-- * dimension: New dimension ("overworld", "nether", "end", "void")
|
||||||
function mcl_worlds.dimension_change(player, dimension)
|
function mcl_worlds.dimension_change(player, dimension)
|
||||||
local playername = player:get_player_name()
|
|
||||||
for i=1, #mcl_worlds.registered_on_dimension_change do
|
for i=1, #mcl_worlds.registered_on_dimension_change do
|
||||||
mcl_worlds.registered_on_dimension_change[i](player, dimension, last_dimension[playername])
|
mcl_worlds.registered_on_dimension_change[i](player, dimension)
|
||||||
|
last_dimension[player:get_player_name()] = dimension
|
||||||
end
|
end
|
||||||
last_dimension[playername] = dimension
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local dimension_change = mcl_worlds.dimension_change
|
|
||||||
|
|
||||||
----------------------- INTERNAL STUFF ----------------------
|
----------------------- INTERNAL STUFF ----------------------
|
||||||
|
|
||||||
-- Update the dimension callbacks every DIM_UPDATE seconds
|
-- Update the dimension callbacks every DIM_UPDATE seconds
|
||||||
|
@ -134,19 +125,19 @@ local DIM_UPDATE = 1
|
||||||
local dimtimer = 0
|
local dimtimer = 0
|
||||||
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
minetest.register_on_joinplayer(function(player)
|
||||||
last_dimension[player:get_player_name()] = pos_to_dimension(player:get_pos())
|
last_dimension[player:get_player_name()] = mcl_worlds.pos_to_dimension(player:get_pos())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
minetest.register_globalstep(function(dtime)
|
minetest.register_globalstep(function(dtime)
|
||||||
-- regular updates based on iterval
|
-- regular updates based on iterval
|
||||||
dimtimer = dimtimer + dtime;
|
dimtimer = dimtimer + dtime;
|
||||||
if dimtimer >= DIM_UPDATE then
|
if dimtimer >= DIM_UPDATE then
|
||||||
local players = get_connected_players()
|
local players = minetest.get_connected_players()
|
||||||
for p = 1, #players do
|
for p=1, #players do
|
||||||
local dim = pos_to_dimension(players[p]:get_pos())
|
local dim = mcl_worlds.pos_to_dimension(players[p]:get_pos())
|
||||||
local name = players[p]:get_player_name()
|
local name = players[p]:get_player_name()
|
||||||
if dim ~= last_dimension[name] then
|
if dim ~= last_dimension[name] then
|
||||||
dimension_change(players[p], dim)
|
mcl_worlds.dimension_change(players[p], dim)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
dimtimer = 0
|
dimtimer = 0
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
# tga_encoder
|
|
||||||
A TGA Encoder written in Lua without the use of external Libraries.
|
|
||||||
|
|
||||||
May be used as a Minetest mod.
|
|
|
@ -1,92 +0,0 @@
|
||||||
tga_encoder = {}
|
|
||||||
|
|
||||||
local image = setmetatable({}, {
|
|
||||||
__call = function(self, ...)
|
|
||||||
local t = setmetatable({}, {__index = self})
|
|
||||||
t:constructor(...)
|
|
||||||
return t
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
function image:constructor(pixels)
|
|
||||||
self.data = ""
|
|
||||||
self.pixels = pixels
|
|
||||||
self.width = #pixels[1]
|
|
||||||
self.height = #pixels
|
|
||||||
|
|
||||||
self:encode()
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode_colormap_spec()
|
|
||||||
self.data = self.data
|
|
||||||
.. string.char(0, 0) -- first entry index
|
|
||||||
.. string.char(0, 0) -- number of entries
|
|
||||||
.. string.char(0) -- bits per pixel
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode_image_spec()
|
|
||||||
self.data = self.data
|
|
||||||
.. string.char(0, 0) -- X-origin
|
|
||||||
.. string.char(0, 0) -- Y-origin
|
|
||||||
.. string.char(self.width % 256, math.floor(self.width / 256)) -- width
|
|
||||||
.. string.char(self.height % 256, math.floor(self.height / 256)) -- height
|
|
||||||
.. string.char(24) -- pixel depth (RGB = 3 bytes = 24 bits)
|
|
||||||
.. string.char(0) -- image descriptor
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode_header()
|
|
||||||
self.data = self.data
|
|
||||||
.. string.char(0) -- image id
|
|
||||||
.. string.char(0) -- color map type
|
|
||||||
.. string.char(10) -- image type (RLE RGB = 10)
|
|
||||||
self:encode_colormap_spec() -- color map specification
|
|
||||||
self:encode_image_spec() -- image specification
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode_data()
|
|
||||||
local current_pixel = ''
|
|
||||||
local previous_pixel = ''
|
|
||||||
local count = 1
|
|
||||||
local packets = {}
|
|
||||||
local rle_packet = ''
|
|
||||||
for _, row in ipairs(self.pixels) do
|
|
||||||
for _, pixel in ipairs(row) do
|
|
||||||
current_pixel = string.char(pixel[3], pixel[2], pixel[1])
|
|
||||||
if current_pixel ~= previous_pixel or count == 128 then
|
|
||||||
packets[#packets +1] = rle_packet
|
|
||||||
count = 1
|
|
||||||
previous_pixel = current_pixel
|
|
||||||
else
|
|
||||||
count = count + 1
|
|
||||||
end
|
|
||||||
rle_packet = string.char(128 + count - 1) .. current_pixel
|
|
||||||
end
|
|
||||||
end
|
|
||||||
packets[#packets +1] = rle_packet
|
|
||||||
self.data = self.data .. table.concat(packets)
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode_footer()
|
|
||||||
self.data = self.data
|
|
||||||
.. string.char(0, 0, 0, 0) -- extension area offset
|
|
||||||
.. string.char(0, 0, 0, 0) -- developer area offset
|
|
||||||
.. "TRUEVISION-XFILE"
|
|
||||||
.. "."
|
|
||||||
.. string.char(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode()
|
|
||||||
self:encode_header() -- header
|
|
||||||
-- no color map and image id data
|
|
||||||
self:encode_data() -- encode data
|
|
||||||
-- no extension or developer area
|
|
||||||
self:encode_footer() -- footer
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:save(filename)
|
|
||||||
local f = assert(io.open(filename, "w"))
|
|
||||||
f:write(self.data)
|
|
||||||
f:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
tga_encoder.image = image
|
|
|
@ -1,3 +0,0 @@
|
||||||
name = tga_encoder
|
|
||||||
author = Fleckenstein
|
|
||||||
description = A TGA Encoder written in Lua without the use of external Libraries.
|
|
|
@ -4,7 +4,6 @@ local get_connected_players = minetest.get_connected_players
|
||||||
local get_node = minetest.get_node
|
local get_node = minetest.get_node
|
||||||
local vector_add = vector.add
|
local vector_add = vector.add
|
||||||
local ceil = math.ceil
|
local ceil = math.ceil
|
||||||
local pairs = pairs
|
|
||||||
|
|
||||||
walkover = {}
|
walkover = {}
|
||||||
walkover.registered_globals = {}
|
walkover.registered_globals = {}
|
||||||
|
@ -35,9 +34,11 @@ minetest.register_globalstep(function(dtime)
|
||||||
local pp = player:get_pos()
|
local pp = player:get_pos()
|
||||||
pp.y = ceil(pp.y)
|
pp.y = ceil(pp.y)
|
||||||
local loc = vector_add(pp, {x=0,y=-1,z=0})
|
local loc = vector_add(pp, {x=0,y=-1,z=0})
|
||||||
if loc then
|
if loc ~= nil then
|
||||||
|
|
||||||
local nodeiamon = get_node(loc)
|
local nodeiamon = get_node(loc)
|
||||||
if nodeiamon then
|
|
||||||
|
if nodeiamon ~= nil then
|
||||||
if on_walk[nodeiamon.name] then
|
if on_walk[nodeiamon.name] then
|
||||||
on_walk[nodeiamon.name](loc, nodeiamon, player)
|
on_walk[nodeiamon.name](loc, nodeiamon, player)
|
||||||
end
|
end
|
||||||
|
@ -47,6 +48,7 @@ minetest.register_globalstep(function(dtime)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
timer = 0
|
timer = 0
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
--Dripping Water Mod
|
||||||
|
--by kddekadenz
|
||||||
|
|
||||||
|
-- License of code, textures & sounds: CC0
|
||||||
|
|
||||||
|
--Drop entities
|
||||||
|
|
||||||
|
--water
|
||||||
|
|
||||||
|
local water_tex = "default_water_source_animated.png^[verticalframe:16:0"
|
||||||
|
minetest.register_entity("drippingwater:drop_water", {
|
||||||
|
hp_max = 1,
|
||||||
|
physical = true,
|
||||||
|
collide_with_objects = false,
|
||||||
|
collisionbox = {-0.025,-0.05,-0.025,0.025,-0.01,0.025},
|
||||||
|
pointable = false,
|
||||||
|
visual = "cube",
|
||||||
|
visual_size = {x=0.05, y=0.1},
|
||||||
|
textures = {water_tex, water_tex, water_tex, water_tex, water_tex, water_tex},
|
||||||
|
spritediv = {x=1, y=1},
|
||||||
|
initial_sprite_basepos = {x=0, y=0},
|
||||||
|
static_save = false,
|
||||||
|
|
||||||
|
on_activate = function(self, staticdata)
|
||||||
|
self.object:set_sprite({x=0,y=0}, 1, 1, true)
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_step = function(self, dtime)
|
||||||
|
local k = math.random(1,222)
|
||||||
|
local ownpos = self.object:get_pos()
|
||||||
|
|
||||||
|
if k==1 then
|
||||||
|
self.object:set_acceleration({x=0, y=-5, z=0})
|
||||||
|
end
|
||||||
|
|
||||||
|
if minetest.get_node({x=ownpos.x, y=ownpos.y +0.5, z=ownpos.z}).name == "air" then
|
||||||
|
self.object:set_acceleration({x=0, y=-5, z=0})
|
||||||
|
end
|
||||||
|
|
||||||
|
if minetest.get_node({x=ownpos.x, y=ownpos.y -0.5, z=ownpos.z}).name ~= "air" then
|
||||||
|
self.object:remove()
|
||||||
|
minetest.sound_play({name="drippingwater_drip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
--lava
|
||||||
|
|
||||||
|
local lava_tex = "default_lava_source_animated.png^[verticalframe:16:0"
|
||||||
|
minetest.register_entity("drippingwater:drop_lava", {
|
||||||
|
hp_max = 1,
|
||||||
|
physical = true,
|
||||||
|
collide_with_objects = false,
|
||||||
|
collisionbox = {-0.025,-0.05,-0.025,0.025,-0.01,0.025},
|
||||||
|
glow = math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3),
|
||||||
|
pointable = false,
|
||||||
|
visual = "cube",
|
||||||
|
visual_size = {x=0.05, y=0.1},
|
||||||
|
textures = {lava_tex, lava_tex, lava_tex, lava_tex, lava_tex, lava_tex},
|
||||||
|
spritediv = {x=1, y=1},
|
||||||
|
initial_sprite_basepos = {x=0, y=0},
|
||||||
|
static_save = false,
|
||||||
|
|
||||||
|
on_activate = function(self, staticdata)
|
||||||
|
self.object:set_sprite({x=0,y=0}, 1, 0, true)
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_step = function(self, dtime)
|
||||||
|
local k = math.random(1,222)
|
||||||
|
local ownpos = self.object:get_pos()
|
||||||
|
|
||||||
|
if k==1 then
|
||||||
|
self.object:set_acceleration({x=0, y=-5, z=0})
|
||||||
|
end
|
||||||
|
|
||||||
|
if minetest.get_node({x=ownpos.x, y=ownpos.y +0.5, z=ownpos.z}).name == "air" then
|
||||||
|
self.object:set_acceleration({x=0, y=-5, z=0})
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if minetest.get_node({x=ownpos.x, y=ownpos.y -0.5, z=ownpos.z}).name ~= "air" then
|
||||||
|
self.object:remove()
|
||||||
|
minetest.sound_play({name="drippingwater_lavadrip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--Create drop
|
||||||
|
|
||||||
|
minetest.register_abm(
|
||||||
|
{
|
||||||
|
label = "Create water drops",
|
||||||
|
nodenames = {"group:opaque", "group:leaves"},
|
||||||
|
neighbors = {"group:water"},
|
||||||
|
interval = 2,
|
||||||
|
chance = 22,
|
||||||
|
action = function(pos)
|
||||||
|
if minetest.get_item_group(minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}).name, "water") ~= 0 and
|
||||||
|
minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == "air" then
|
||||||
|
local i = math.random(-45,45) / 100
|
||||||
|
minetest.add_entity({x=pos.x + i, y=pos.y - 0.501, z=pos.z + i}, "drippingwater:drop_water")
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
--Create lava drop
|
||||||
|
|
||||||
|
minetest.register_abm(
|
||||||
|
{
|
||||||
|
label = "Create lava drops",
|
||||||
|
nodenames = {"group:opaque"},
|
||||||
|
neighbors = {"group:lava"},
|
||||||
|
interval = 2,
|
||||||
|
chance = 22,
|
||||||
|
action = function(pos)
|
||||||
|
if minetest.get_item_group(minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}).name, "lava") ~= 0 and
|
||||||
|
minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == "air" then
|
||||||
|
local i = math.random(-45,45) / 100
|
||||||
|
minetest.add_entity({x=pos.x + i, y=pos.y - 0.501, z=pos.z + i}, "drippingwater:drop_lava")
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
|
@ -1,4 +1,4 @@
|
||||||
name = mcl_dripping
|
name = drippingwater
|
||||||
author = kddekadenz
|
author = kddekadenz
|
||||||
description = Drops are generated rarely under solid nodes
|
description = Drops are generated rarely under solid nodes
|
||||||
depends = mcl_core
|
depends = mcl_core
|
|
@ -1,12 +1,12 @@
|
||||||
Dripping Mod
|
Dripping Water Mod
|
||||||
by kddekadenz
|
by kddekadenz
|
||||||
|
|
||||||
modified for MineClone 2 by Wuzzy and NO11
|
modified for MineClone 2 by Wuzzy
|
||||||
|
|
||||||
|
|
||||||
Installing instructions:
|
Installing instructions:
|
||||||
|
|
||||||
1. Copy the mcl_dripping mod folder into games/gamemode/mods
|
1. Copy the drippingwater mod folder into games/gamemode/mods
|
||||||
|
|
||||||
2. Start game and enjoy :)
|
2. Start game and enjoy :)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
local S = minetest.get_translator(minetest.get_current_modname())
|
local S = minetest.get_translator("mcl_boats")
|
||||||
|
|
||||||
local boat_visual_size = {x = 1, y = 1, z = 1}
|
local boat_visual_size = {x = 1, y = 1, z = 1}
|
||||||
local paddling_speed = 22
|
local paddling_speed = 22
|
||||||
|
@ -84,7 +84,7 @@ local function attach_object(self, obj)
|
||||||
end
|
end
|
||||||
end, name)
|
end, name)
|
||||||
obj:set_look_horizontal(yaw)
|
obj:set_look_horizontal(yaw)
|
||||||
mcl_title.set(obj, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60})
|
mcl_tmp_message.message(obj, S("Sneak to dismount"))
|
||||||
else
|
else
|
||||||
obj:get_luaentity()._old_visual_size = visual_size
|
obj:get_luaentity()._old_visual_size = visual_size
|
||||||
end
|
end
|
||||||
|
@ -115,7 +115,7 @@ local boat = {
|
||||||
collisionbox = {-0.5, -0.35, -0.5, 0.5, 0.3, 0.5},
|
collisionbox = {-0.5, -0.35, -0.5, 0.5, 0.3, 0.5},
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mcl_boats_boat.b3d",
|
mesh = "mcl_boats_boat.b3d",
|
||||||
textures = {"mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png"},
|
textures = {"mcl_boats_texture_oak_boat.png"},
|
||||||
visual_size = boat_visual_size,
|
visual_size = boat_visual_size,
|
||||||
hp_max = boat_max_hp,
|
hp_max = boat_max_hp,
|
||||||
damage_texture_modifier = "^[colorize:white:0",
|
damage_texture_modifier = "^[colorize:white:0",
|
||||||
|
@ -148,11 +148,6 @@ function boat.on_activate(self, staticdata, dtime_s)
|
||||||
self._v = data.v
|
self._v = data.v
|
||||||
self._last_v = self._v
|
self._last_v = self._v
|
||||||
self._itemstring = data.itemstring
|
self._itemstring = data.itemstring
|
||||||
|
|
||||||
while #data.textures < 5 do
|
|
||||||
table.insert(data.textures, data.textures[1])
|
|
||||||
end
|
|
||||||
|
|
||||||
self.object:set_properties({textures = data.textures})
|
self.object:set_properties({textures = data.textures})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -193,7 +188,7 @@ function boat.on_punch(self, puncher, time_from_last_punch, tool_capabilities, d
|
||||||
end
|
end
|
||||||
|
|
||||||
function boat.on_step(self, dtime, moveresult)
|
function boat.on_step(self, dtime, moveresult)
|
||||||
mcl_burning.tick(self.object, dtime, self)
|
mcl_burning.tick(self.object, dtime)
|
||||||
|
|
||||||
self._v = get_v(self.object:get_velocity()) * get_sign(self._v)
|
self._v = get_v(self.object:get_velocity()) * get_sign(self._v)
|
||||||
local v_factor = 1
|
local v_factor = 1
|
||||||
|
@ -333,17 +328,16 @@ function boat.on_step(self, dtime, moveresult)
|
||||||
|
|
||||||
p.y = p.y - boat_y_offset
|
p.y = p.y - boat_y_offset
|
||||||
local new_velo
|
local new_velo
|
||||||
local new_acce
|
local new_acce = {x = 0, y = 0, z = 0}
|
||||||
if not is_water(p) and not on_ice then
|
if not is_water(p) and not on_ice then
|
||||||
-- Not on water or inside water: Free fall
|
-- Not on water or inside water: Free fall
|
||||||
--local nodedef = minetest.registered_nodes[minetest.get_node(p).name]
|
local nodedef = minetest.registered_nodes[minetest.get_node(p).name]
|
||||||
new_acce = {x = 0, y = -9.8, z = 0}
|
new_acce = {x = 0, y = -9.8, z = 0}
|
||||||
new_velo = get_velocity(self._v, self.object:get_yaw(),
|
new_velo = get_velocity(self._v, self.object:get_yaw(),
|
||||||
self.object:get_velocity().y)
|
self.object:get_velocity().y)
|
||||||
else
|
else
|
||||||
p.y = p.y + 1
|
p.y = p.y + 1
|
||||||
local is_obsidian_boat = self.object:get_luaentity()._itemstring == "mcl_boats:boat_obsidian"
|
if is_water(p) then
|
||||||
if is_water(p) or is_obsidian_boat then
|
|
||||||
-- Inside water: Slowly sink
|
-- Inside water: Slowly sink
|
||||||
local y = self.object:get_velocity().y
|
local y = self.object:get_velocity().y
|
||||||
y = y - 0.01
|
y = y - 0.01
|
||||||
|
@ -383,13 +377,13 @@ end
|
||||||
-- Register one entity for all boat types
|
-- Register one entity for all boat types
|
||||||
minetest.register_entity("mcl_boats:boat", boat)
|
minetest.register_entity("mcl_boats:boat", boat)
|
||||||
|
|
||||||
local boat_ids = { "boat", "boat_spruce", "boat_birch", "boat_jungle", "boat_acacia", "boat_dark_oak", "boat_obsidian" }
|
local boat_ids = { "boat", "boat_spruce", "boat_birch", "boat_jungle", "boat_acacia", "boat_dark_oak" }
|
||||||
local names = { S("Oak Boat"), S("Spruce Boat"), S("Birch Boat"), S("Jungle Boat"), S("Acacia Boat"), S("Dark Oak Boat"), S("Obsidian Boat") }
|
local names = { S("Oak Boat"), S("Spruce Boat"), S("Birch Boat"), S("Jungle Boat"), S("Acacia Boat"), S("Dark Oak Boat") }
|
||||||
local craftstuffs = {}
|
local craftstuffs = {}
|
||||||
if minetest.get_modpath("mcl_core") then
|
if minetest.get_modpath("mcl_core") then
|
||||||
craftstuffs = { "mcl_core:wood", "mcl_core:sprucewood", "mcl_core:birchwood", "mcl_core:junglewood", "mcl_core:acaciawood", "mcl_core:darkwood", "mcl_core:obsidian" }
|
craftstuffs = { "mcl_core:wood", "mcl_core:sprucewood", "mcl_core:birchwood", "mcl_core:junglewood", "mcl_core:acaciawood", "mcl_core:darkwood" }
|
||||||
end
|
end
|
||||||
local images = { "oak", "spruce", "birch", "jungle", "acacia", "dark_oak", "obsidian" }
|
local images = { "oak", "spruce", "birch", "jungle", "acacia", "dark_oak" }
|
||||||
|
|
||||||
for b=1, #boat_ids do
|
for b=1, #boat_ids do
|
||||||
local itemstring = "mcl_boats:"..boat_ids[b]
|
local itemstring = "mcl_boats:"..boat_ids[b]
|
||||||
|
@ -400,7 +394,7 @@ for b=1, #boat_ids do
|
||||||
if b == 1 then
|
if b == 1 then
|
||||||
help = true
|
help = true
|
||||||
longdesc = S("Boats are used to travel on the surface of water.")
|
longdesc = S("Boats are used to travel on the surface of water.")
|
||||||
usagehelp = S("Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.")
|
usagehelp = S("Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Rightclick the boat again to leave it, punch the boat to make it drop as an item.")
|
||||||
helpname = S("Boat")
|
helpname = S("Boat")
|
||||||
end
|
end
|
||||||
tt_help = S("Water vehicle")
|
tt_help = S("Water vehicle")
|
||||||
|
@ -440,9 +434,8 @@ for b=1, #boat_ids do
|
||||||
pos = vector.add(pos, vector.multiply(dir, boat_y_offset_ground))
|
pos = vector.add(pos, vector.multiply(dir, boat_y_offset_ground))
|
||||||
end
|
end
|
||||||
local boat = minetest.add_entity(pos, "mcl_boats:boat")
|
local boat = minetest.add_entity(pos, "mcl_boats:boat")
|
||||||
local texture = "mcl_boats_texture_"..images[b].."_boat.png"
|
|
||||||
boat:get_luaentity()._itemstring = itemstring
|
boat:get_luaentity()._itemstring = itemstring
|
||||||
boat:set_properties({textures = { texture, texture, texture, texture, texture }})
|
boat:set_properties({textures = { "mcl_boats_texture_"..images[b].."_boat.png" }})
|
||||||
boat:set_yaw(placer:get_look_horizontal())
|
boat:set_yaw(placer:get_look_horizontal())
|
||||||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||||
itemstack:take_item()
|
itemstack:take_item()
|
||||||
|
@ -477,6 +470,6 @@ minetest.register_craft({
|
||||||
burntime = 20,
|
burntime = 20,
|
||||||
})
|
})
|
||||||
|
|
||||||
if minetest.get_modpath("doc_identifier") then
|
if minetest.get_modpath("doc_identifier") ~= nil then
|
||||||
doc.sub.identifier.register_object("mcl_boats:boat", "craftitems", "mcl_boats:boat")
|
doc.sub.identifier.register_object("mcl_boats:boat", "craftitems", "mcl_boats:boat")
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,6 @@ Boats are used to travel on the surface of water.=Boote werden benutzt, um sich
|
||||||
Dark Oak Boat=Schwarzeichenboot
|
Dark Oak Boat=Schwarzeichenboot
|
||||||
Jungle Boat=Dschungelboot
|
Jungle Boat=Dschungelboot
|
||||||
Oak Boat=Eichenboot
|
Oak Boat=Eichenboot
|
||||||
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=Rechtsklicken Sie auf eine Wasserquelle, um das Boot zu platzieren. Rechtsklicken Sie auf das Boot, um es zu betreten. Mit [Links] und [Rechts] lenken, mit [Vorwärts] und [Rückwärts] Geschwindigkeit regeln oder rückwärts fahren. Nutzen sie [Schleichen], um das Boot zu verlassen, schlagen Sie das Boot, um es als Gegenstand fallen zu lassen.
|
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Rightclick the boat again to leave it, punch the boat to make it drop as an item.=Rechtsklicken Sie auf eine Wasserquelle, um das Boot zu platzieren. Rechtsklicken Sie auf das Boot, um es zu betreten. Mit [Links] und [Rechts] lenken, mit [Vorwärts] und [Rückwärts] Geschwindigkeit regeln oder rückwärts fahren. Rechtsklicken Sie erneut auf das Boot, um es zu verlassen, schlagen Sie das Boot, um es als Gegenstand fallen zu lassen.
|
||||||
Spruce Boat=Fichtenboot
|
Spruce Boat=Fichtenboot
|
||||||
Water vehicle=Wasserfahrzeug
|
Water vehicle=Wasserfahrzeug
|
||||||
Sneak to dismount=Zum Aussteigen schleichen
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ Boats are used to travel on the surface of water.=Les bateaux sont utilisés pou
|
||||||
Dark Oak Boat=Bateau en Chêne Noir
|
Dark Oak Boat=Bateau en Chêne Noir
|
||||||
Jungle Boat=Bateau en Acajou
|
Jungle Boat=Bateau en Acajou
|
||||||
Oak Boat=Bateau en Chêne
|
Oak Boat=Bateau en Chêne
|
||||||
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=Faites un clic droit sur une source d'eau pour placer le bateau. Faites un clic droit sur le bateau pour y entrer. Utilisez [Gauche] et [Droite] pour diriger, [Avant] pour accélérer et [Arrière] pour ralentir ou reculer. Utilisez [Sneak] pour le quitter, frappez le bateau pour le faire tomber en tant qu'objet.
|
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Rightclick the boat again to leave it, punch the boat to make it drop as an item.=Faites un clic droit sur une source d'eau pour placer le bateau. Faites un clic droit sur le bateau pour y entrer. Utilisez [Gauche] et [Droite] pour diriger, [Avant] pour accélérer et [Arrière] pour ralentir ou reculer. Cliquez de nouveau avec le bouton droit sur le bateau pour le quitter, frappez le bateau pour le faire tomber en tant qu'objet.
|
||||||
Spruce Boat=Bateau en Sapin
|
Spruce Boat=Bateau en Sapin
|
||||||
Water vehicle=Véhicule aquatique
|
Water vehicle=Véhicule aquatique
|
||||||
Sneak to dismount=
|
|
|
@ -1,12 +0,0 @@
|
||||||
# textdomain: mcl_boats
|
|
||||||
Acacia Boat=Akacjowa łódź
|
|
||||||
Birch Boat=Brzozowa łódź
|
|
||||||
Boat=Łódź
|
|
||||||
Boats are used to travel on the surface of water.=Łodzie są wykorzystywane do podróżowania po powierzchni wody.
|
|
||||||
Dark Oak Boat=Ciemno-dębowa łódź
|
|
||||||
Jungle Boat=Tropikalna łódź
|
|
||||||
Oak Boat=Dębowa łódź
|
|
||||||
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=Kliknij prawym przyciskiem myszy na źródło wody by postawić łódź. Kliknij prawym przyciskiem myszy by w nią wsiąść. Użyj przycisków [Lewy] oraz [Prawy] by sterować, [Naprzód] by przyspieszyć i [W tył] by zwolnić lub się cofać. Kliknij [Skradanie] by z niej wyjść, uderz ją by wziąć ją jako przedmiot.
|
|
||||||
Spruce Boat=Świerkowa łódź
|
|
||||||
Water vehicle=Pojazd wodny
|
|
||||||
Sneak to dismount=Skradaj się by opuścić łódź
|
|
|
@ -6,7 +6,6 @@ Boats are used to travel on the surface of water.=
|
||||||
Dark Oak Boat=
|
Dark Oak Boat=
|
||||||
Jungle Boat=
|
Jungle Boat=
|
||||||
Oak Boat=
|
Oak Boat=
|
||||||
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=
|
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Rightclick the boat again to leave it, punch the boat to make it drop as an item.=
|
||||||
Spruce Boat=
|
Spruce Boat=
|
||||||
Water vehicle=
|
Water vehicle=
|
||||||
Sneak to dismount=
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
name = mcl_boats
|
name = mcl_boats
|
||||||
author = PilzAdam
|
author = PilzAdam
|
||||||
description = Adds drivable boats.
|
description = Adds drivable boats.
|
||||||
depends = mcl_player, flowlib, mcl_title
|
depends = mcl_player, flowlib
|
||||||
optional_depends = mcl_core, doc_identifier
|
optional_depends = mcl_core, doc_identifier
|
||||||
|
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 264 B |
Before Width: | Height: | Size: 535 B |
|
@ -1,81 +1,184 @@
|
||||||
function mcl_burning.get_storage(obj)
|
local S = minetest.get_translator("mcl_burning")
|
||||||
return obj:is_player() and mcl_burning.storage[obj] or obj:get_luaentity()
|
|
||||||
|
function mcl_burning.get_default(datatype)
|
||||||
|
local default_table = {string = "", float = 0.0, int = 0, bool = false}
|
||||||
|
return default_table[datatype]
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_burning.is_burning(obj)
|
function mcl_burning.get(obj, datatype, name)
|
||||||
return mcl_burning.get_storage(obj).burn_time
|
local key
|
||||||
end
|
if obj:is_player() then
|
||||||
|
local meta = obj:get_meta()
|
||||||
function mcl_burning.is_affected_by_rain(obj)
|
return meta["get_" .. datatype](meta, "mcl_burning:" .. name)
|
||||||
return mcl_weather.get_weather() == "rain" and mcl_weather.is_outdoor(obj:get_pos())
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_burning.get_collisionbox(obj, smaller, storage)
|
|
||||||
local cache = storage.collisionbox_cache
|
|
||||||
if cache then
|
|
||||||
local box = cache[smaller and 2 or 1]
|
|
||||||
return box[1], box[2]
|
|
||||||
else
|
else
|
||||||
local box = obj:get_properties().collisionbox
|
local luaentity = obj:get_luaentity()
|
||||||
local minp, maxp = vector.new(box[1], box[2], box[3]), vector.new(box[4], box[5], box[6])
|
return luaentity and luaentity["mcl_burning_" .. name] or mcl_burning.get_default(datatype)
|
||||||
local s_vec = vector.new(0.1, 0.1, 0.1)
|
|
||||||
local s_minp = vector.add(minp, s_vec)
|
|
||||||
local s_maxp = vector.subtract(maxp, s_vec)
|
|
||||||
storage.collisionbox_cache = {{minp, maxp}, {s_minp, s_maxp}}
|
|
||||||
return minp, maxp
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_burning.get_touching_nodes(obj, nodenames, storage)
|
function mcl_burning.set(obj, datatype, name, value)
|
||||||
|
if obj:is_player() then
|
||||||
|
local meta = obj:get_meta()
|
||||||
|
meta["set_" .. datatype](meta, "mcl_burning:" .. name, value or mcl_burning.get_default(datatype))
|
||||||
|
else
|
||||||
|
local luaentity = obj:get_luaentity()
|
||||||
|
if mcl_burning.get_default(datatype) == value then
|
||||||
|
value = nil
|
||||||
|
end
|
||||||
|
luaentity["mcl_burning_" .. name] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_burning.is_burning(obj)
|
||||||
|
return mcl_burning.get(obj, "float", "burn_time") > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_burning.is_affected_by_rain(obj)
|
||||||
|
return mcl_weather and mcl_weather.get_weather() == "rain" and mcl_weather.is_outdoor(obj:get_pos())
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_burning.get_collisionbox(obj, smaller)
|
||||||
|
local box = obj:get_properties().collisionbox
|
||||||
|
local minp, maxp = vector.new(box[1], box[2], box[3]), vector.new(box[4], box[5], box[6])
|
||||||
|
if smaller then
|
||||||
|
local s_vec = vector.new(0.1, 0.1, 0.1)
|
||||||
|
minp = vector.add(minp, s_vec)
|
||||||
|
maxp = vector.subtract(maxp, s_vec)
|
||||||
|
end
|
||||||
|
return minp, maxp
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_burning.get_touching_nodes(obj, nodenames)
|
||||||
local pos = obj:get_pos()
|
local pos = obj:get_pos()
|
||||||
local minp, maxp = mcl_burning.get_collisionbox(obj, true, storage)
|
local box = obj:get_properties().collisionbox
|
||||||
|
local minp, maxp = mcl_burning.get_collisionbox(obj, true)
|
||||||
local nodes = minetest.find_nodes_in_area(vector.add(pos, minp), vector.add(pos, maxp), nodenames)
|
local nodes = minetest.find_nodes_in_area(vector.add(pos, minp), vector.add(pos, maxp), nodenames)
|
||||||
return nodes
|
return nodes
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_burning.set_on_fire(obj, burn_time)
|
function mcl_burning.get_highest_group_value(obj, groupname)
|
||||||
if obj:get_hp() < 0 then
|
local nodes = mcl_burning.get_touching_nodes(obj, "group:" .. groupname, true)
|
||||||
|
local highest_group_value = 0
|
||||||
|
|
||||||
|
for _, pos in pairs(nodes) do
|
||||||
|
local node = minetest.get_node(pos)
|
||||||
|
local group_value = minetest.get_item_group(node.name, groupname)
|
||||||
|
if group_value > highest_group_value then
|
||||||
|
highest_group_value = group_value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return highest_group_value
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_burning.damage(obj)
|
||||||
|
local luaentity = obj:get_luaentity()
|
||||||
|
local health
|
||||||
|
|
||||||
|
if luaentity then
|
||||||
|
health = luaentity.health
|
||||||
|
end
|
||||||
|
|
||||||
|
local hp = health or obj:get_hp()
|
||||||
|
|
||||||
|
if hp <= 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local storage = mcl_burning.get_storage(obj)
|
local do_damage = true
|
||||||
|
|
||||||
|
if obj:is_player() then
|
||||||
|
if mcl_potions.player_has_effect(obj, "fire_proof") then
|
||||||
|
do_damage = false
|
||||||
|
else
|
||||||
|
local name = obj:get_player_name()
|
||||||
|
armor.last_damage_types[name] = "fire"
|
||||||
|
local deathmsg = S("@1 burned to death.", name)
|
||||||
|
local reason = mcl_burning.get(obj, "string", "reason")
|
||||||
|
if reason ~= "" then
|
||||||
|
deathmsg = S("@1 was burned by @2.", name, reason)
|
||||||
|
end
|
||||||
|
mcl_death_messages.player_damage(obj, deathmsg)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if luaentity.fire_damage_resistant then
|
||||||
|
do_damage = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if do_damage then
|
||||||
|
local new_hp = hp - 1
|
||||||
|
if health then
|
||||||
|
luaentity.health = new_hp
|
||||||
|
else
|
||||||
|
obj:set_hp(new_hp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_burning.set_on_fire(obj, burn_time, reason)
|
||||||
|
if obj:get_hp() < 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local luaentity = obj:get_luaentity()
|
local luaentity = obj:get_luaentity()
|
||||||
if luaentity and luaentity.fire_resistant then
|
if luaentity and luaentity.fire_resistant then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if obj:is_player() and minetest.is_creative_enabled(obj:get_player_name()) then
|
local old_burn_time = mcl_burning.get(obj, "float", "burn_time")
|
||||||
burn_time = 0
|
|
||||||
else
|
|
||||||
local max_fire_prot_lvl = 0
|
local max_fire_prot_lvl = 0
|
||||||
local inv = mcl_util.get_inventory(obj)
|
|
||||||
local armor_list = inv and inv:get_list("armor")
|
|
||||||
|
|
||||||
if armor_list then
|
if obj:is_player() then
|
||||||
for _, stack in pairs(armor_list) do
|
if minetest.is_creative_enabled(obj:get_player_name()) then
|
||||||
local fire_prot_lvl = mcl_enchanting.get_enchantment(stack, "fire_protection")
|
burn_time = burn_time / 100
|
||||||
if fire_prot_lvl > max_fire_prot_lvl then
|
|
||||||
max_fire_prot_lvl = fire_prot_lvl
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local inv = obj:get_inventory()
|
||||||
|
|
||||||
|
for i = 2, 5 do
|
||||||
|
local stack = inv:get_stack("armor", i)
|
||||||
|
|
||||||
|
local fire_prot_lvl = mcl_enchanting.get_enchantment(stack, "fire_protection")
|
||||||
|
max_fire_prot_lvl = math.max(max_fire_prot_lvl, fire_prot_lvl)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if max_fire_prot_lvl > 0 then
|
if max_fire_prot_lvl > 0 then
|
||||||
burn_time = burn_time - math.floor(burn_time * max_fire_prot_lvl * 0.15)
|
burn_time = burn_time - math.floor(burn_time * max_fire_prot_lvl * 0.15)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if not storage.burn_time or burn_time >= storage.burn_time then
|
if old_burn_time <= burn_time then
|
||||||
|
--[[local sound_id = mcl_burning.get(obj, "int", "sound_id")
|
||||||
|
if sound_id == 0 then
|
||||||
|
sound_id = minetest.sound_play("fire_fire", {
|
||||||
|
object = obj,
|
||||||
|
gain = 0.18,
|
||||||
|
max_hear_distance = 16,
|
||||||
|
loop = true,
|
||||||
|
}) + 1
|
||||||
|
end]]--
|
||||||
|
|
||||||
|
local hud_id
|
||||||
if obj:is_player() then
|
if obj:is_player() then
|
||||||
mcl_burning.channels[obj]:send_all(tostring(mcl_burning.animation_frames))
|
hud_id = mcl_burning.get(obj, "int", "hud_id")
|
||||||
mcl_burning.channels[obj]:send_all("start")
|
if hud_id == 0 then
|
||||||
|
hud_id = obj:hud_add({
|
||||||
|
hud_elem_type = "image",
|
||||||
|
position = {x = 0.5, y = 0.5},
|
||||||
|
scale = {x = -100, y = -100},
|
||||||
|
text = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. 1,
|
||||||
|
z_index = 1000,
|
||||||
|
}) + 1
|
||||||
end
|
end
|
||||||
storage.burn_time = burn_time
|
end
|
||||||
storage.fire_damage_timer = 0
|
mcl_burning.set(obj, "float", "burn_time", burn_time)
|
||||||
|
mcl_burning.set(obj, "string", "reason", reason)
|
||||||
|
mcl_burning.set(obj, "int", "hud_id", hud_id)
|
||||||
|
--mcl_burning.set(obj, "int", "sound_id", sound_id)
|
||||||
|
|
||||||
local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire")
|
local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire")
|
||||||
local minp, maxp = mcl_burning.get_collisionbox(obj, false, storage)
|
local minp, maxp = mcl_burning.get_collisionbox(obj)
|
||||||
local obj_size = obj:get_properties().visual_size
|
local obj_size = obj:get_properties().visual_size
|
||||||
|
|
||||||
local vertical_grow_factor = 1.2
|
local vertical_grow_factor = 1.2
|
||||||
|
@ -89,50 +192,111 @@ function mcl_burning.set_on_fire(obj, burn_time)
|
||||||
|
|
||||||
fire_entity:set_properties({visual_size = size})
|
fire_entity:set_properties({visual_size = size})
|
||||||
fire_entity:set_attach(obj, "", offset, {x = 0, y = 0, z = 0})
|
fire_entity:set_attach(obj, "", offset, {x = 0, y = 0, z = 0})
|
||||||
local fire_luaentity = fire_entity:get_luaentity()
|
mcl_burning.update_animation_frame(obj, fire_entity, 0)
|
||||||
|
|
||||||
for _, other in pairs(minetest.get_objects_inside_radius(fire_entity:get_pos(), 0)) do
|
|
||||||
local other_luaentity = other:get_luaentity()
|
|
||||||
if other_luaentity and other_luaentity.name == "mcl_burning:fire" and other_luaentity ~= fire_luaentity then
|
|
||||||
other:remove()
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_burning.extinguish(obj)
|
function mcl_burning.extinguish(obj)
|
||||||
if mcl_burning.is_burning(obj) then
|
if mcl_burning.is_burning(obj) then
|
||||||
local storage = mcl_burning.get_storage(obj)
|
--local sound_id = mcl_burning.get(obj, "int", "sound_id") - 1
|
||||||
|
--minetest.sound_stop(sound_id)
|
||||||
|
|
||||||
if obj:is_player() then
|
if obj:is_player() then
|
||||||
mcl_burning.channels[obj]:send_all("stop")
|
local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1
|
||||||
mcl_burning.storage[obj] = {}
|
obj:hud_remove(hud_id)
|
||||||
else
|
|
||||||
storage.burn_time = nil
|
|
||||||
storage.fire_damage_timer = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
mcl_burning.set(obj, "string", "reason")
|
||||||
|
mcl_burning.set(obj, "float", "burn_time")
|
||||||
|
mcl_burning.set(obj, "float", "damage_timer")
|
||||||
|
mcl_burning.set(obj, "int", "hud_id")
|
||||||
|
--mcl_burning.set(obj, "int", "sound_id")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_burning.tick(obj, dtime, storage)
|
function mcl_burning.catch_fire_tick(obj, dtime)
|
||||||
if storage.burn_time then
|
if mcl_burning.is_affected_by_rain(obj) or #mcl_burning.get_touching_nodes(obj, "group:puts_out_fire") > 0 then
|
||||||
storage.burn_time = storage.burn_time - dtime
|
|
||||||
|
|
||||||
if storage.burn_time <= 0 or mcl_burning.is_affected_by_rain(obj) or #mcl_burning.get_touching_nodes(obj, "group:puts_out_fire", storage) > 0 then
|
|
||||||
mcl_burning.extinguish(obj)
|
mcl_burning.extinguish(obj)
|
||||||
return true
|
|
||||||
else
|
else
|
||||||
storage.fire_damage_timer = storage.fire_damage_timer + dtime
|
local set_on_fire_value = mcl_burning.get_highest_group_value(obj, "set_on_fire")
|
||||||
|
|
||||||
if storage.fire_damage_timer >= 1 then
|
if set_on_fire_value > 0 then
|
||||||
storage.fire_damage_timer = 0
|
mcl_burning.set_on_fire(obj, set_on_fire_value)
|
||||||
|
|
||||||
local luaentity = obj:get_luaentity()
|
|
||||||
|
|
||||||
if not luaentity or not luaentity.fire_damage_resistant then
|
|
||||||
mcl_util.deal_damage(obj, 1, {type = "on_fire"})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function mcl_burning.tick(obj, dtime)
|
||||||
|
local burn_time = mcl_burning.get(obj, "float", "burn_time") - dtime
|
||||||
|
|
||||||
|
if burn_time <= 0 then
|
||||||
|
mcl_burning.extinguish(obj)
|
||||||
|
else
|
||||||
|
mcl_burning.set(obj, "float", "burn_time", burn_time)
|
||||||
|
|
||||||
|
local damage_timer = mcl_burning.get(obj, "float", "damage_timer") + dtime
|
||||||
|
|
||||||
|
if damage_timer >= 1 then
|
||||||
|
damage_timer = 0
|
||||||
|
mcl_burning.damage(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_burning.set(obj, "float", "damage_timer", damage_timer)
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_burning.catch_fire_tick(obj, dtime)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_burning.update_animation_frame(obj, fire_entity, animation_frame)
|
||||||
|
local fire_texture = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. animation_frame
|
||||||
|
local fire_HUD_texture = "mcl_burning_hud_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. animation_frame
|
||||||
|
fire_entity:set_properties({textures = {"blank.png", "blank.png", fire_texture, fire_texture, fire_texture, fire_texture}})
|
||||||
|
if obj:is_player() then
|
||||||
|
local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1
|
||||||
|
obj:hud_change(hud_id, "text", fire_HUD_texture)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_burning.fire_entity_step(self, dtime)
|
||||||
|
if self.removed then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local obj = self.object
|
||||||
|
local parent = obj:get_attach()
|
||||||
|
local do_remove
|
||||||
|
|
||||||
|
self.doing_step = true
|
||||||
|
|
||||||
|
if not parent or not mcl_burning.is_burning(parent) then
|
||||||
|
do_remove = true
|
||||||
|
else
|
||||||
|
for _, other in pairs(minetest.get_objects_inside_radius(obj:get_pos(), 0)) do
|
||||||
|
local luaentity = obj:get_luaentity()
|
||||||
|
if luaentity and luaentity.name == "mcl_burning:fire" and not luaentity.doing_step and not luaentity.removed then
|
||||||
|
do_remove = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.doing_step = false
|
||||||
|
|
||||||
|
if do_remove then
|
||||||
|
self.removed = true
|
||||||
|
obj:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local animation_timer = self.animation_timer + dtime
|
||||||
|
if animation_timer >= 0.015 then
|
||||||
|
animation_timer = 0
|
||||||
|
local animation_frame = self.animation_frame + 1
|
||||||
|
if animation_frame > mcl_burning.animation_frames - 1 then
|
||||||
|
animation_frame = 0
|
||||||
|
end
|
||||||
|
mcl_burning.update_animation_frame(parent, obj, animation_frame)
|
||||||
|
self.animation_frame = animation_frame
|
||||||
|
end
|
||||||
|
self.animation_timer = animation_timer
|
||||||
|
end
|
||||||
|
|
|
@ -1,42 +1,29 @@
|
||||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
local S = minetest.get_translator("mcl_burning")
|
||||||
|
local modpath = minetest.get_modpath("mcl_burning")
|
||||||
local pairs = pairs
|
|
||||||
|
|
||||||
local get_connected_players = minetest.get_connected_players
|
|
||||||
local get_item_group = minetest.get_item_group
|
|
||||||
|
|
||||||
mcl_burning = {
|
mcl_burning = {
|
||||||
storage = {},
|
|
||||||
channels = {},
|
|
||||||
animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8
|
animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8
|
||||||
}
|
}
|
||||||
|
|
||||||
dofile(modpath .. "/api.lua")
|
dofile(modpath .. "/api.lua")
|
||||||
|
|
||||||
|
minetest.register_entity("mcl_burning:fire", {
|
||||||
|
initial_properties = {
|
||||||
|
physical = false,
|
||||||
|
collisionbox = {0, 0, 0, 0, 0, 0},
|
||||||
|
visual = "cube",
|
||||||
|
pointable = false,
|
||||||
|
glow = -1,
|
||||||
|
},
|
||||||
|
|
||||||
|
animation_frame = 0,
|
||||||
|
animation_timer = 0,
|
||||||
|
on_step = mcl_burning.fire_entity_step,
|
||||||
|
})
|
||||||
|
|
||||||
minetest.register_globalstep(function(dtime)
|
minetest.register_globalstep(function(dtime)
|
||||||
for _, player in pairs(get_connected_players()) do
|
for _, player in pairs(minetest.get_connected_players()) do
|
||||||
local storage = mcl_burning.storage[player]
|
mcl_burning.tick(player, dtime)
|
||||||
if not mcl_burning.tick(player, dtime, storage) and not mcl_burning.is_affected_by_rain(player) then
|
|
||||||
local nodes = mcl_burning.get_touching_nodes(player, {"group:puts_out_fire", "group:set_on_fire"}, storage)
|
|
||||||
local burn_time = 0
|
|
||||||
|
|
||||||
for _, pos in pairs(nodes) do
|
|
||||||
local node = minetest.get_node(pos)
|
|
||||||
if get_item_group(node.name, "puts_out_fire") > 0 then
|
|
||||||
burn_time = 0
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
local value = get_item_group(node.name, "set_on_fire")
|
|
||||||
if value > burn_time then
|
|
||||||
burn_time = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if burn_time > 0 then
|
|
||||||
mcl_burning.set_on_fire(player, burn_time)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -44,68 +31,6 @@ minetest.register_on_respawnplayer(function(player)
|
||||||
mcl_burning.extinguish(player)
|
mcl_burning.extinguish(player)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
function mcl_burning.init_player(player)
|
|
||||||
local meta = player:get_meta()
|
|
||||||
-- NOTE: mcl_burning:data may be "return nil" (which deserialize into nil) for reasons unknown.
|
|
||||||
if meta:get_string("mcl_burning:data"):find("return nil", 1, true) then
|
|
||||||
minetest.log("warning", "[mcl_burning] 'mcl_burning:data' player meta field is invalid! Please report this bug")
|
|
||||||
end
|
|
||||||
mcl_burning.storage[player] = meta:contains("mcl_burning:data") and minetest.deserialize(meta:get_string("mcl_burning:data")) or {}
|
|
||||||
mcl_burning.channels[player] = minetest.mod_channel_join("mcl_burning:" .. player:get_player_name())
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
|
||||||
mcl_burning.init_player(player)
|
|
||||||
end)
|
|
||||||
|
|
||||||
minetest.register_on_leaveplayer(function(player)
|
minetest.register_on_leaveplayer(function(player)
|
||||||
player:get_meta():set_string("mcl_burning:data", minetest.serialize(mcl_burning.storage[player]))
|
mcl_burning.set(player, "int", "hud_id")
|
||||||
mcl_burning.storage[player] = nil
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
||||||
minetest.register_entity("mcl_burning:fire", {
|
|
||||||
initial_properties = {
|
|
||||||
physical = false,
|
|
||||||
collisionbox = {0, 0, 0, 0, 0, 0},
|
|
||||||
visual = "upright_sprite",
|
|
||||||
textures = {
|
|
||||||
name = "mcl_burning_entity_flame_animated.png",
|
|
||||||
animation = {
|
|
||||||
type = "vertical_frames",
|
|
||||||
aspect_w = 16,
|
|
||||||
aspect_h = 16,
|
|
||||||
length = 1.0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
spritediv = {x = 1, y = mcl_burning.animation_frames},
|
|
||||||
pointable = false,
|
|
||||||
glow = -1,
|
|
||||||
backface_culling = false,
|
|
||||||
},
|
|
||||||
animation_frame = 0,
|
|
||||||
animation_timer = 0,
|
|
||||||
on_activate = function(self)
|
|
||||||
self.object:set_sprite({x = 0, y = 0}, mcl_burning.animation_frames, 1.0 / mcl_burning.animation_frames)
|
|
||||||
end,
|
|
||||||
on_step = function(self)
|
|
||||||
if not self:sanity_check() then
|
|
||||||
self.object:remove()
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
sanity_check = function(self)
|
|
||||||
local parent = self.object:get_attach()
|
|
||||||
|
|
||||||
if not parent then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local storage = mcl_burning.get_storage(parent)
|
|
||||||
|
|
||||||
if not storage or not storage.burn_time then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
-- Dripping Water Mod
|
|
||||||
-- by kddekadenz
|
|
||||||
|
|
||||||
local math = math
|
|
||||||
|
|
||||||
-- License of code, textures & sounds: CC0
|
|
||||||
|
|
||||||
local function register_drop(liquid, glow, sound, nodes)
|
|
||||||
minetest.register_entity("mcl_dripping:drop_" .. liquid, {
|
|
||||||
hp_max = 1,
|
|
||||||
physical = true,
|
|
||||||
collide_with_objects = false,
|
|
||||||
collisionbox = {-0.01, 0.01, -0.01, 0.01, 0.01, 0.01},
|
|
||||||
glow = glow,
|
|
||||||
pointable = false,
|
|
||||||
visual = "sprite",
|
|
||||||
visual_size = {x = 0.1, y = 0.1},
|
|
||||||
textures = {""},
|
|
||||||
spritediv = {x = 1, y = 1},
|
|
||||||
initial_sprite_basepos = {x = 0, y = 0},
|
|
||||||
static_save = false,
|
|
||||||
_dropped = false,
|
|
||||||
on_activate = function(self)
|
|
||||||
self.object:set_properties({
|
|
||||||
textures = {"[combine:2x2:" .. -math.random(1, 16) .. "," .. -math.random(1, 16) .. "=default_" .. liquid .. "_source_animated.png"}
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
on_step = function(self, dtime)
|
|
||||||
local k = math.random(1, 222)
|
|
||||||
local ownpos = self.object:get_pos()
|
|
||||||
if k == 1 then
|
|
||||||
self.object:set_acceleration(vector.new(0, -5, 0))
|
|
||||||
end
|
|
||||||
if minetest.get_node(vector.offset(ownpos, 0, 0.5, 0)).name == "air" then
|
|
||||||
self.object:set_acceleration(vector.new(0, -5, 0))
|
|
||||||
end
|
|
||||||
if minetest.get_node(vector.offset(ownpos, 0, -0.1, 0)).name ~= "air" then
|
|
||||||
local ent = self.object:get_luaentity()
|
|
||||||
if not ent._dropped then
|
|
||||||
ent._dropped = true
|
|
||||||
minetest.sound_play({name = "drippingwater_" .. sound .. "drip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
|
|
||||||
end
|
|
||||||
if k < 3 then
|
|
||||||
self.object:remove()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
minetest.register_abm({
|
|
||||||
label = "Create drops",
|
|
||||||
nodenames = nodes,
|
|
||||||
neighbors = {"group:" .. liquid},
|
|
||||||
interval = 2,
|
|
||||||
chance = 22,
|
|
||||||
action = function(pos)
|
|
||||||
if minetest.get_item_group(minetest.get_node(vector.offset(pos, 0, 1, 0)).name, liquid) ~= 0
|
|
||||||
and minetest.get_node(vector.offset(pos, 0, -1, 0)).name == "air" then
|
|
||||||
local x, z = math.random(-45, 45) / 100, math.random(-45, 45) / 100
|
|
||||||
minetest.add_entity(vector.offset(pos, x, -0.520, z), "mcl_dripping:drop_" .. liquid)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
register_drop("water", 1, "", {"group:opaque", "group:leaves"})
|
|
||||||
register_drop("lava", math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3), "lava", {"group:opaque"})
|
|
|
@ -1,4 +1,10 @@
|
||||||
local function get_falling_depth(self)
|
local S = minetest.get_translator("mcl_falling_nodes")
|
||||||
|
local dmes = minetest.get_modpath("mcl_death_messages") ~= nil
|
||||||
|
local has_mcl_armor = minetest.get_modpath("mcl_armor")
|
||||||
|
|
||||||
|
local is_creative_enabled = minetest.is_creative_enabled
|
||||||
|
|
||||||
|
local get_falling_depth = function(self)
|
||||||
if not self._startpos then
|
if not self._startpos then
|
||||||
-- Fallback
|
-- Fallback
|
||||||
self._startpos = self.object:get_pos()
|
self._startpos = self.object:get_pos()
|
||||||
|
@ -6,7 +12,7 @@ local function get_falling_depth(self)
|
||||||
return self._startpos.y - vector.round(self.object:get_pos()).y
|
return self._startpos.y - vector.round(self.object:get_pos()).y
|
||||||
end
|
end
|
||||||
|
|
||||||
local function deal_falling_damage(self, dtime)
|
local deal_falling_damage = function(self, dtime)
|
||||||
if minetest.get_item_group(self.node.name, "falling_node_damage") == 0 then
|
if minetest.get_item_group(self.node.name, "falling_node_damage") == 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -17,34 +23,80 @@ local function deal_falling_damage(self, dtime)
|
||||||
-- Fallback
|
-- Fallback
|
||||||
self._startpos = pos
|
self._startpos = pos
|
||||||
end
|
end
|
||||||
self._hit = self._hit or {}
|
local objs = minetest.get_objects_inside_radius(pos, 1)
|
||||||
for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
|
for _,v in ipairs(objs) do
|
||||||
local entity = obj:get_luaentity()
|
if v:is_player() then
|
||||||
if entity and entity.name == "__builtin:item" then
|
local hp = v:get_hp()
|
||||||
obj:remove()
|
local name = v:get_player_name()
|
||||||
elseif mcl_util.get_hp(obj) > 0 and not self._hit[obj] then
|
if hp ~= 0 then
|
||||||
self._hit[obj] = true
|
if not self._hit_players then
|
||||||
|
self._hit_players = {}
|
||||||
|
end
|
||||||
|
local hit = false
|
||||||
|
for _,v in ipairs(self._hit_players) do
|
||||||
|
if name == v then
|
||||||
|
hit = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not hit then
|
||||||
|
table.insert(self._hit_players, name)
|
||||||
local way = self._startpos.y - pos.y
|
local way = self._startpos.y - pos.y
|
||||||
local damage = (way - 1) * 2
|
local damage = (way - 1) * 2
|
||||||
damage = math.min(40, math.max(0, damage))
|
damage = math.min(40, math.max(0, damage))
|
||||||
if damage >= 1 then
|
if damage >= 1 then
|
||||||
|
hp = hp - damage
|
||||||
|
if hp < 0 then
|
||||||
|
hp = 0
|
||||||
|
end
|
||||||
-- Reduce damage if wearing a helmet
|
-- Reduce damage if wearing a helmet
|
||||||
local inv = mcl_util.get_inventory(obj)
|
local inv = v:get_inventory()
|
||||||
if inv then
|
|
||||||
local helmet = inv:get_stack("armor", 2)
|
local helmet = inv:get_stack("armor", 2)
|
||||||
if minetest.get_item_group(helmet:get_name(), "combat_armor") > 0 then
|
if has_mcl_armor and not helmet:is_empty() then
|
||||||
damage = damage / 4 * 3
|
hp = hp/4*3
|
||||||
mcl_util.use_item_durability(helmet, 1)
|
if not is_creative_enabled(name) then
|
||||||
|
helmet:add_wear(65535/helmet:get_definition().groups.mcl_armor_uses) --TODO: be sure damage is exactly like mc (informations are missing in the mc wiki)
|
||||||
inv:set_stack("armor", 2, helmet)
|
inv:set_stack("armor", 2, helmet)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local dmg_type
|
local msg
|
||||||
if minetest.get_item_group(self.node.name, "anvil") ~= 0 then
|
if minetest.get_item_group(self.node.name, "anvil") ~= 0 then
|
||||||
dmg_type = "anvil"
|
msg = S("@1 was smashed by a falling anvil.", v:get_player_name())
|
||||||
else
|
else
|
||||||
dmg_type = "falling_node"
|
msg = S("@1 was smashed by a falling block.", v:get_player_name())
|
||||||
|
end
|
||||||
|
if dmes then
|
||||||
|
mcl_death_messages.player_damage(v, msg)
|
||||||
|
end
|
||||||
|
v:set_hp(hp, { type = "punch", from = "mod" })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local hp = v:get_luaentity().health
|
||||||
|
if hp and hp ~= 0 then
|
||||||
|
if not self._hit_mobs then
|
||||||
|
self._hit_mobs = {}
|
||||||
|
end
|
||||||
|
local hit = false
|
||||||
|
for _,mob in ipairs(self._hit_mobs) do
|
||||||
|
if v == mob then
|
||||||
|
hit = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--TODO: reduce damage for mobs then they will be able to wear armor
|
||||||
|
if not hit then
|
||||||
|
table.insert(self._hit_mobs, v)
|
||||||
|
local way = self._startpos.y - pos.y
|
||||||
|
local damage = (way - 1) * 2
|
||||||
|
damage = math.min(40, math.max(0, damage))
|
||||||
|
if damage >= 1 then
|
||||||
|
hp = hp - damage
|
||||||
|
if hp < 0 then
|
||||||
|
hp = 0
|
||||||
|
end
|
||||||
|
v:get_luaentity().health = hp
|
||||||
|
end
|
||||||
end
|
end
|
||||||
mcl_util.deal_damage(obj, damage, {type = dmg_type})
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -60,8 +112,10 @@ minetest.register_entity(":__builtin:falling_node", {
|
||||||
collide_with_objects = false,
|
collide_with_objects = false,
|
||||||
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||||
},
|
},
|
||||||
|
|
||||||
node = {},
|
node = {},
|
||||||
meta = {},
|
meta = {},
|
||||||
|
|
||||||
set_node = function(self, node, meta)
|
set_node = function(self, node, meta)
|
||||||
local def = minetest.registered_nodes[node.name]
|
local def = minetest.registered_nodes[node.name]
|
||||||
-- Change falling node if definition tells us to
|
-- Change falling node if definition tells us to
|
||||||
|
@ -88,6 +142,7 @@ minetest.register_entity(":__builtin:falling_node", {
|
||||||
glow = glow,
|
glow = glow,
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_staticdata = function(self)
|
get_staticdata = function(self)
|
||||||
local meta = self.meta
|
local meta = self.meta
|
||||||
-- Workaround: Save inventory seperately from metadata.
|
-- Workaround: Save inventory seperately from metadata.
|
||||||
|
@ -108,6 +163,7 @@ minetest.register_entity(":__builtin:falling_node", {
|
||||||
}
|
}
|
||||||
return minetest.serialize(ds)
|
return minetest.serialize(ds)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_activate = function(self, staticdata)
|
on_activate = function(self, staticdata)
|
||||||
self.object:set_armor_groups({immortal = 1})
|
self.object:set_armor_groups({immortal = 1})
|
||||||
|
|
||||||
|
@ -130,6 +186,7 @@ minetest.register_entity(":__builtin:falling_node", {
|
||||||
end
|
end
|
||||||
self._startpos = vector.round(self._startpos)
|
self._startpos = vector.round(self._startpos)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_step = function(self, dtime)
|
on_step = function(self, dtime)
|
||||||
-- Set gravity
|
-- Set gravity
|
||||||
local acceleration = self.object:get_acceleration()
|
local acceleration = self.object:get_acceleration()
|
||||||
|
@ -181,9 +238,10 @@ minetest.register_entity(":__builtin:falling_node", {
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local nd = minetest.registered_nodes[n2.name]
|
local nd = minetest.registered_nodes[n2.name]
|
||||||
--if n2.name == "mcl_portals:portal_end" then
|
if n2.name == "mcl_portals:portal_end" then
|
||||||
-- TODO: Teleport falling node.
|
-- TODO: Teleport falling node.
|
||||||
if (nd and nd.buildable_to == true) or minetest.get_item_group(self.node.name, "crush_after_fall") ~= 0 then
|
|
||||||
|
elseif (nd and nd.buildable_to == true) or minetest.get_item_group(self.node.name, "crush_after_fall") ~= 0 then
|
||||||
-- Replace destination node if it's buildable to
|
-- Replace destination node if it's buildable to
|
||||||
minetest.remove_node(np)
|
minetest.remove_node(np)
|
||||||
-- Run script hook
|
-- Run script hook
|
||||||
|
@ -250,6 +308,7 @@ minetest.register_entity(":__builtin:falling_node", {
|
||||||
self.object:set_pos(npos)
|
self.object:set_pos(npos)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
deal_falling_damage(self, dtime)
|
deal_falling_damage(self, dtime)
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# textdomain: mcl_falling_nodes
|
||||||
|
@1 was smashed by a falling anvil.=@1 wurde von einem fallenden Amboss zerschmettert.
|
||||||
|
@1 was smashed by a falling block.=@1 wurde von einem fallenden Block zerschmettert.
|
|
@ -0,0 +1,3 @@
|
||||||
|
# textdomain: mcl_falling_nodes
|
||||||
|
@1 was smashed by a falling anvil.=@1 fue aplastado por la caída de un yunque.
|
||||||
|
@1 was smashed by a falling block.=@1 fue aplastado por la caída de un bloque.
|
|
@ -0,0 +1,3 @@
|
||||||
|
# textdomain: mcl_falling_nodes
|
||||||
|
@1 was smashed by a falling anvil.=@1 a été écrasé par une enclume qui tombait.
|
||||||
|
@1 was smashed by a falling block.=@1 a été écrasé par un bloc qui tombait.
|
|
@ -1,3 +0,0 @@
|
||||||
# textdomain: mcl_falling_nodes
|
|
||||||
@1 was smashed by a falling anvil.=@1 została zmiażdżona przez spadające kowadło.
|
|
||||||
@1 was smashed by a falling block.=@1 została zmiażdżona przez spadający blok.
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# textdomain: mcl_falling_nodes
|
||||||
|
@1 was smashed by a falling anvil.=@1 придавило падающей наковальней.
|
||||||
|
@1 was smashed by a falling block.=@1 раздавило падающим блоком.
|
|
@ -0,0 +1,3 @@
|
||||||
|
# textdomain: mcl_falling_nodes
|
||||||
|
@1 was smashed by a falling anvil.=
|
||||||
|
@1 was smashed by a falling block.=
|
|
@ -1,5 +1,5 @@
|
||||||
--these are lua locals, used for higher performance
|
--these are lua locals, used for higher performance
|
||||||
local minetest, math, vector, ipairs, pairs = minetest, math, vector, ipairs, pairs
|
local minetest,math,vector,ipairs = minetest,math,vector,ipairs
|
||||||
|
|
||||||
--this is used for the player pool in the sound buffer
|
--this is used for the player pool in the sound buffer
|
||||||
local pool = {}
|
local pool = {}
|
||||||
|
@ -38,7 +38,7 @@ item_drop_settings.drop_single_item = false --if true, the drop control dro
|
||||||
|
|
||||||
item_drop_settings.magnet_time = 0.75 -- how many seconds an item follows the player before giving up
|
item_drop_settings.magnet_time = 0.75 -- how many seconds an item follows the player before giving up
|
||||||
|
|
||||||
local function get_gravity()
|
local get_gravity = function()
|
||||||
return tonumber(minetest.settings:get("movement_gravity")) or 9.81
|
return tonumber(minetest.settings:get("movement_gravity")) or 9.81
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ mcl_item_entity.register_pickup_achievement("mcl_mobitems:blaze_rod", "mcl:blaze
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_mobitems:leather", "mcl:killCow")
|
mcl_item_entity.register_pickup_achievement("mcl_mobitems:leather", "mcl:killCow")
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_core:diamond", "mcl:diamonds")
|
mcl_item_entity.register_pickup_achievement("mcl_core:diamond", "mcl:diamonds")
|
||||||
|
|
||||||
local function check_pickup_achievements(object, player)
|
local check_pickup_achievements = function(object, player)
|
||||||
if has_awards then
|
if has_awards then
|
||||||
local itemname = ItemStack(object:get_luaentity().itemstring):get_name()
|
local itemname = ItemStack(object:get_luaentity().itemstring):get_name()
|
||||||
local playername = player:get_player_name()
|
local playername = player:get_player_name()
|
||||||
|
@ -72,7 +72,7 @@ local function check_pickup_achievements(object, player)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function enable_physics(object, luaentity, ignore_check)
|
local enable_physics = function(object, luaentity, ignore_check)
|
||||||
if luaentity.physical_state == false or ignore_check == true then
|
if luaentity.physical_state == false or ignore_check == true then
|
||||||
luaentity.physical_state = true
|
luaentity.physical_state = true
|
||||||
object:set_properties({
|
object:set_properties({
|
||||||
|
@ -83,7 +83,7 @@ local function enable_physics(object, luaentity, ignore_check)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function disable_physics(object, luaentity, ignore_check, reset_movement)
|
local disable_physics = function(object, luaentity, ignore_check, reset_movement)
|
||||||
if luaentity.physical_state == true or ignore_check == true then
|
if luaentity.physical_state == true or ignore_check == true then
|
||||||
luaentity.physical_state = false
|
luaentity.physical_state = false
|
||||||
object:set_properties({
|
object:set_properties({
|
||||||
|
@ -98,11 +98,13 @@ end
|
||||||
|
|
||||||
|
|
||||||
minetest.register_globalstep(function(dtime)
|
minetest.register_globalstep(function(dtime)
|
||||||
|
|
||||||
tick = not tick
|
tick = not tick
|
||||||
|
|
||||||
for _,player in pairs(minetest.get_connected_players()) do
|
for _,player in pairs(minetest.get_connected_players()) do
|
||||||
if player:get_hp() > 0 or not minetest.settings:get_bool("enable_damage") then
|
if player:get_hp() > 0 or not minetest.settings:get_bool("enable_damage") then
|
||||||
|
|
||||||
|
|
||||||
local name = player:get_player_name()
|
local name = player:get_player_name()
|
||||||
|
|
||||||
local pos = player:get_pos()
|
local pos = player:get_pos()
|
||||||
|
@ -233,7 +235,7 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
local dug_node = minetest.get_node(pos)
|
local dug_node = minetest.get_node(pos)
|
||||||
local tooldef
|
local tooldef
|
||||||
local tool
|
local tool
|
||||||
if digger then
|
if digger ~= nil then
|
||||||
tool = digger:get_wielded_item()
|
tool = digger:get_wielded_item()
|
||||||
tooldef = minetest.registered_tools[tool:get_name()]
|
tooldef = minetest.registered_tools[tool:get_name()]
|
||||||
|
|
||||||
|
@ -290,10 +292,10 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if digger and mcl_experience.throw_xp and not silk_touch_drop then
|
if digger and mcl_experience.throw_experience and not silk_touch_drop then
|
||||||
local experience_amount = minetest.get_item_group(dug_node.name,"xp")
|
local experience_amount = minetest.get_item_group(dug_node.name,"xp")
|
||||||
if experience_amount > 0 then
|
if experience_amount > 0 then
|
||||||
mcl_experience.throw_xp(pos, experience_amount)
|
mcl_experience.throw_experience(pos, experience_amount)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -314,7 +316,7 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
end
|
end
|
||||||
-- Spawn item and apply random speed
|
-- Spawn item and apply random speed
|
||||||
local obj = minetest.add_item(dpos, drop_item)
|
local obj = minetest.add_item(dpos, drop_item)
|
||||||
if obj then
|
if obj ~= nil then
|
||||||
local x = math.random(1, 5)
|
local x = math.random(1, 5)
|
||||||
if math.random(1,2) == 1 then
|
if math.random(1,2) == 1 then
|
||||||
x = -x
|
x = -x
|
||||||
|
@ -363,17 +365,6 @@ if not time_to_live then
|
||||||
time_to_live = 300
|
time_to_live = 300
|
||||||
end
|
end
|
||||||
|
|
||||||
local function cxcz(o, cw, one, zero)
|
|
||||||
if cw < 0 then
|
|
||||||
table.insert(o, { [one]=1, y=0, [zero]=0 })
|
|
||||||
table.insert(o, { [one]=-1, y=0, [zero]=0 })
|
|
||||||
else
|
|
||||||
table.insert(o, { [one]=-1, y=0, [zero]=0 })
|
|
||||||
table.insert(o, { [one]=1, y=0, [zero]=0 })
|
|
||||||
end
|
|
||||||
return o
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_entity(":__builtin:item", {
|
minetest.register_entity(":__builtin:item", {
|
||||||
initial_properties = {
|
initial_properties = {
|
||||||
hp_max = 1,
|
hp_max = 1,
|
||||||
|
@ -394,7 +385,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
-- The itemstring MUST be set immediately to a non-empty string after creating the entity.
|
-- The itemstring MUST be set immediately to a non-empty string after creating the entity.
|
||||||
-- The hand is NOT permitted as dropped item. ;-)
|
-- The hand is NOT permitted as dropped item. ;-)
|
||||||
-- Item entities will be deleted if they still have an empty itemstring on their first on_step tick.
|
-- Item entities will be deleted if they still have an empty itemstring on their first on_step tick.
|
||||||
itemstring = "",
|
itemstring = '',
|
||||||
|
|
||||||
-- If true, item will fall
|
-- If true, item will fall
|
||||||
physical_state = true,
|
physical_state = true,
|
||||||
|
@ -415,14 +406,6 @@ minetest.register_entity(":__builtin:item", {
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local stack = ItemStack(itemstring)
|
local stack = ItemStack(itemstring)
|
||||||
if minetest.get_item_group(stack:get_name(), "compass") > 0 then
|
|
||||||
stack:set_name("mcl_compass:16")
|
|
||||||
itemstring = stack:to_string()
|
|
||||||
self.itemstring = itemstring
|
|
||||||
end
|
|
||||||
if minetest.get_item_group(stack:get_name(), "clock") > 0 then
|
|
||||||
self.is_clock = true
|
|
||||||
end
|
|
||||||
local count = stack:get_count()
|
local count = stack:get_count()
|
||||||
local max_count = stack:get_stack_max()
|
local max_count = stack:get_stack_max()
|
||||||
if count > max_count then
|
if count > max_count then
|
||||||
|
@ -435,9 +418,13 @@ minetest.register_entity(":__builtin:item", {
|
||||||
if itemtable then
|
if itemtable then
|
||||||
itemname = stack:to_table().name
|
itemname = stack:to_table().name
|
||||||
end
|
end
|
||||||
|
local item_texture = nil
|
||||||
|
local item_type = ""
|
||||||
local glow
|
local glow
|
||||||
local def = minetest.registered_items[itemname]
|
local def = minetest.registered_items[itemname]
|
||||||
if def then
|
if def then
|
||||||
|
item_texture = def.inventory_image
|
||||||
|
item_type = def.type
|
||||||
description = def.description
|
description = def.description
|
||||||
glow = def.light_source
|
glow = def.light_source
|
||||||
end
|
end
|
||||||
|
@ -480,7 +467,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_staticdata = function(self)
|
get_staticdata = function(self)
|
||||||
local data = minetest.serialize({
|
return minetest.serialize({
|
||||||
itemstring = self.itemstring,
|
itemstring = self.itemstring,
|
||||||
always_collect = self.always_collect,
|
always_collect = self.always_collect,
|
||||||
age = self.age,
|
age = self.age,
|
||||||
|
@ -488,39 +475,6 @@ minetest.register_entity(":__builtin:item", {
|
||||||
_flowing = self._flowing,
|
_flowing = self._flowing,
|
||||||
_removed = self._removed,
|
_removed = self._removed,
|
||||||
})
|
})
|
||||||
-- sfan5 guessed that the biggest serializable item
|
|
||||||
-- entity would have a size of 65530 bytes. This has
|
|
||||||
-- been experimentally verified to be still too large.
|
|
||||||
--
|
|
||||||
-- anon5 has calculated that the biggest serializable
|
|
||||||
-- item entity has a size of exactly 65487 bytes:
|
|
||||||
--
|
|
||||||
-- 1. serializeString16 can handle max. 65535 bytes.
|
|
||||||
-- 2. The following engine metadata is always saved:
|
|
||||||
-- • 1 byte (version)
|
|
||||||
-- • 2 byte (length prefix)
|
|
||||||
-- • 14 byte “__builtin:item”
|
|
||||||
-- • 4 byte (length prefix)
|
|
||||||
-- • 2 byte (health)
|
|
||||||
-- • 3 × 4 byte = 12 byte (position)
|
|
||||||
-- • 4 byte (yaw)
|
|
||||||
-- • 1 byte (version 2)
|
|
||||||
-- • 2 × 4 byte = 8 byte (pitch and roll)
|
|
||||||
-- 3. This leaves 65487 bytes for the serialization.
|
|
||||||
if #data > 65487 then -- would crash the engine
|
|
||||||
local stack = ItemStack(self.itemstring)
|
|
||||||
stack:get_meta():from_table(nil)
|
|
||||||
self.itemstring = stack:to_string()
|
|
||||||
minetest.log(
|
|
||||||
"warning",
|
|
||||||
"Overlong item entity metadata removed: “" ..
|
|
||||||
self.itemstring ..
|
|
||||||
"” had serialized length of " ..
|
|
||||||
#data
|
|
||||||
)
|
|
||||||
return self:get_staticdata()
|
|
||||||
end
|
|
||||||
return data
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_activate = function(self, staticdata, dtime_s)
|
on_activate = function(self, staticdata, dtime_s)
|
||||||
|
@ -608,7 +562,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_step = function(self, dtime, moveresult)
|
on_step = function(self, dtime)
|
||||||
if self._removed then
|
if self._removed then
|
||||||
self.object:set_properties({
|
self.object:set_properties({
|
||||||
physical = false
|
physical = false
|
||||||
|
@ -618,7 +572,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self.age = self.age + dtime
|
self.age = self.age + dtime
|
||||||
if self._collector_timer then
|
if self._collector_timer ~= nil then
|
||||||
self._collector_timer = self._collector_timer + dtime
|
self._collector_timer = self._collector_timer + dtime
|
||||||
end
|
end
|
||||||
if time_to_live > 0 and self.age > time_to_live then
|
if time_to_live > 0 and self.age > time_to_live then
|
||||||
|
@ -639,12 +593,6 @@ minetest.register_entity(":__builtin:item", {
|
||||||
local node = minetest.get_node_or_nil(p)
|
local node = minetest.get_node_or_nil(p)
|
||||||
local in_unloaded = (node == nil)
|
local in_unloaded = (node == nil)
|
||||||
|
|
||||||
if self.is_clock then
|
|
||||||
self.object:set_properties({
|
|
||||||
textures = {"mcl_clock:clock_" .. (mcl_worlds.clock_works(p) and mcl_clock.old_time or mcl_clock.random_frame)}
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If no collector was found for a long enough time, declare the magnet as disabled
|
-- If no collector was found for a long enough time, declare the magnet as disabled
|
||||||
if self._magnet_active and (self._collector_timer == nil or (self._collector_timer > item_drop_settings.magnet_time)) then
|
if self._magnet_active and (self._collector_timer == nil or (self._collector_timer > item_drop_settings.magnet_time)) then
|
||||||
self._magnet_active = false
|
self._magnet_active = false
|
||||||
|
@ -675,18 +623,6 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Destroy item when it collides with a cactus
|
|
||||||
if moveresult and moveresult.collides then
|
|
||||||
for _, collision in pairs(moveresult.collisions) do
|
|
||||||
local pos = collision.node_pos
|
|
||||||
if collision.type == "node" and minetest.get_node(pos).name == "mcl_core:cactus" then
|
|
||||||
self._removed = true
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Push item out when stuck inside solid opaque node
|
-- Push item out when stuck inside solid opaque node
|
||||||
if def and def.walkable and def.groups and def.groups.opaque == 1 then
|
if def and def.walkable and def.groups and def.groups.opaque == 1 then
|
||||||
local shootdir
|
local shootdir
|
||||||
|
@ -698,6 +634,16 @@ minetest.register_entity(":__builtin:item", {
|
||||||
-- 1st: closest
|
-- 1st: closest
|
||||||
-- 2nd: other direction
|
-- 2nd: other direction
|
||||||
-- 3rd and 4th: other axis
|
-- 3rd and 4th: other axis
|
||||||
|
local cxcz = function(o, cw, one, zero)
|
||||||
|
if cw < 0 then
|
||||||
|
table.insert(o, { [one]=1, y=0, [zero]=0 })
|
||||||
|
table.insert(o, { [one]=-1, y=0, [zero]=0 })
|
||||||
|
else
|
||||||
|
table.insert(o, { [one]=-1, y=0, [zero]=0 })
|
||||||
|
table.insert(o, { [one]=1, y=0, [zero]=0 })
|
||||||
|
end
|
||||||
|
return o
|
||||||
|
end
|
||||||
if math.abs(cx) < math.abs(cz) then
|
if math.abs(cx) < math.abs(cz) then
|
||||||
order = cxcz(order, cx, "x", "z")
|
order = cxcz(order, cx, "x", "z")
|
||||||
order = cxcz(order, cz, "z", "x")
|
order = cxcz(order, cz, "z", "x")
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
local vector = vector
|
|
||||||
|
|
||||||
function mcl_minecarts:get_sign(z)
|
function mcl_minecarts:get_sign(z)
|
||||||
if z == 0 then
|
if z == 0 then
|
||||||
return 0
|
return 0
|
||||||
|
@ -40,9 +38,11 @@ end
|
||||||
|
|
||||||
function mcl_minecarts:check_front_up_down(pos, dir_, check_down, railtype)
|
function mcl_minecarts:check_front_up_down(pos, dir_, check_down, railtype)
|
||||||
local dir = vector.new(dir_)
|
local dir = vector.new(dir_)
|
||||||
|
local cur = nil
|
||||||
|
|
||||||
-- Front
|
-- Front
|
||||||
dir.y = 0
|
dir.y = 0
|
||||||
local cur = vector.add(pos, dir)
|
cur = vector.add(pos, dir)
|
||||||
if mcl_minecarts:is_rail(cur, railtype) then
|
if mcl_minecarts:is_rail(cur, railtype) then
|
||||||
return dir
|
return dir
|
||||||
end
|
end
|
||||||
|
@ -65,7 +65,7 @@ end
|
||||||
|
|
||||||
function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
||||||
local pos = vector.round(pos_)
|
local pos = vector.round(pos_)
|
||||||
local cur
|
local cur = nil
|
||||||
local left_check, right_check = true, true
|
local left_check, right_check = true, true
|
||||||
|
|
||||||
-- Check left and right
|
-- Check left and right
|
||||||
|
@ -122,6 +122,7 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
||||||
return cur
|
return cur
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Backwards
|
-- Backwards
|
||||||
if not old_switch then
|
if not old_switch then
|
||||||
cur = mcl_minecarts:check_front_up_down(pos, {
|
cur = mcl_minecarts:check_front_up_down(pos, {
|
||||||
|
@ -133,5 +134,7 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
||||||
return cur
|
return cur
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return {x=0, y=0, z=0}
|
return {x=0, y=0, z=0}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
local modname = minetest.get_current_modname()
|
local S = minetest.get_translator("mcl_minecarts")
|
||||||
local S = minetest.get_translator(modname)
|
|
||||||
|
|
||||||
local has_mcl_wip = minetest.get_modpath("mcl_wip")
|
local has_mcl_wip = minetest.get_modpath("mcl_wip")
|
||||||
|
|
||||||
mcl_minecarts = {}
|
mcl_minecarts = {}
|
||||||
mcl_minecarts.modpath = minetest.get_modpath(modname)
|
mcl_minecarts.modpath = minetest.get_modpath("mcl_minecarts")
|
||||||
mcl_minecarts.speed_max = 10
|
mcl_minecarts.speed_max = 10
|
||||||
mcl_minecarts.check_float_time = 15
|
mcl_minecarts.check_float_time = 15
|
||||||
|
|
||||||
|
@ -198,27 +197,14 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
|
||||||
else
|
else
|
||||||
self._last_float_check = self._last_float_check + dtime
|
self._last_float_check = self._last_float_check + dtime
|
||||||
end
|
end
|
||||||
|
local pos, rou_pos, node
|
||||||
local pos, rou_pos, node = self.object:get_pos()
|
|
||||||
local r = 0.6
|
|
||||||
for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do
|
|
||||||
if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then
|
|
||||||
detach_driver(self)
|
|
||||||
for d = 1, #drop do
|
|
||||||
minetest.add_item(pos, drop[d])
|
|
||||||
end
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Drop minecart if it isn't on a rail anymore
|
-- Drop minecart if it isn't on a rail anymore
|
||||||
if self._last_float_check >= mcl_minecarts.check_float_time then
|
if self._last_float_check >= mcl_minecarts.check_float_time then
|
||||||
pos = self.object:get_pos()
|
pos = self.object:get_pos()
|
||||||
rou_pos = vector.round(pos)
|
rou_pos = vector.round(pos)
|
||||||
node = minetest.get_node(rou_pos)
|
node = minetest.get_node(rou_pos)
|
||||||
local g = minetest.get_item_group(node.name, "connect_to_raillike")
|
local g = minetest.get_item_group(node.name, "connect_to_raillike")
|
||||||
if g ~= self._railtype and self._railtype then
|
if g ~= self._railtype and self._railtype ~= nil then
|
||||||
-- Detach driver
|
-- Detach driver
|
||||||
if player then
|
if player then
|
||||||
if self._old_pos then
|
if self._old_pos then
|
||||||
|
@ -500,6 +486,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
|
||||||
if update.pos then
|
if update.pos then
|
||||||
self.object:set_pos(pos)
|
self.object:set_pos(pos)
|
||||||
end
|
end
|
||||||
|
update = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function cart:get_staticdata()
|
function cart:get_staticdata()
|
||||||
|
@ -510,7 +497,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Place a minecart at pointed_thing
|
-- Place a minecart at pointed_thing
|
||||||
function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer)
|
mcl_minecarts.place_minecart = function(itemstack, pointed_thing, placer)
|
||||||
if not pointed_thing.type == "node" then
|
if not pointed_thing.type == "node" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -537,7 +524,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer)
|
||||||
local cart = minetest.add_entity(railpos, entity_id)
|
local cart = minetest.add_entity(railpos, entity_id)
|
||||||
local railtype = minetest.get_item_group(node.name, "connect_to_raillike")
|
local railtype = minetest.get_item_group(node.name, "connect_to_raillike")
|
||||||
local le = cart:get_luaentity()
|
local le = cart:get_luaentity()
|
||||||
if le then
|
if le ~= nil then
|
||||||
le._railtype = railtype
|
le._railtype = railtype
|
||||||
end
|
end
|
||||||
local cart_dir = mcl_minecarts:get_rail_direction(railpos, {x=1, y=0, z=0}, nil, nil, railtype)
|
local cart_dir = mcl_minecarts:get_rail_direction(railpos, {x=1, y=0, z=0}, nil, nil, railtype)
|
||||||
|
@ -554,7 +541,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function register_craftitem(itemstring, entity_id, description, tt_help, longdesc, usagehelp, icon, creative)
|
local register_craftitem = function(itemstring, entity_id, description, tt_help, longdesc, usagehelp, icon, creative)
|
||||||
entity_mapping[itemstring] = entity_id
|
entity_mapping[itemstring] = entity_id
|
||||||
|
|
||||||
local groups = { minecart = 1, transport = 1 }
|
local groups = { minecart = 1, transport = 1 }
|
||||||
|
@ -620,7 +607,7 @@ Register a minecart
|
||||||
local function register_minecart(itemstring, entity_id, description, tt_help, longdesc, usagehelp, mesh, textures, icon, drop, on_rightclick, on_activate_by_rail, creative)
|
local function register_minecart(itemstring, entity_id, description, tt_help, longdesc, usagehelp, mesh, textures, icon, drop, on_rightclick, on_activate_by_rail, creative)
|
||||||
register_entity(entity_id, mesh, textures, drop, on_rightclick, on_activate_by_rail)
|
register_entity(entity_id, mesh, textures, drop, on_rightclick, on_activate_by_rail)
|
||||||
register_craftitem(itemstring, entity_id, description, tt_help, longdesc, usagehelp, icon, creative)
|
register_craftitem(itemstring, entity_id, description, tt_help, longdesc, usagehelp, icon, creative)
|
||||||
if minetest.get_modpath("doc_identifier") then
|
if minetest.get_modpath("doc_identifier") ~= nil then
|
||||||
doc.sub.identifier.register_object(entity_id, "craftitems", itemstring)
|
doc.sub.identifier.register_object(entity_id, "craftitems", itemstring)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -659,7 +646,7 @@ register_minecart(
|
||||||
if player then
|
if player then
|
||||||
mcl_player.player_set_animation(player, "sit" , 30)
|
mcl_player.player_set_animation(player, "sit" , 30)
|
||||||
player:set_eye_offset({x=0, y=-5.5, z=0},{x=0, y=-4, z=0})
|
player:set_eye_offset({x=0, y=-5.5, z=0},{x=0, y=-4, z=0})
|
||||||
mcl_title.set(clicker, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60})
|
mcl_tmp_message.message(clicker, S("Sneak to dismount"))
|
||||||
end
|
end
|
||||||
end, name)
|
end, name)
|
||||||
end
|
end
|
||||||
|
@ -830,30 +817,31 @@ minetest.register_craft({
|
||||||
})
|
})
|
||||||
|
|
||||||
-- TODO: Re-enable crafting of special minecarts when they have been implemented
|
-- TODO: Re-enable crafting of special minecarts when they have been implemented
|
||||||
--[[minetest.register_craft({
|
if false then
|
||||||
|
minetest.register_craft({
|
||||||
output = "mcl_minecarts:furnace_minecart",
|
output = "mcl_minecarts:furnace_minecart",
|
||||||
recipe = {
|
recipe = {
|
||||||
{"mcl_furnaces:furnace"},
|
{"mcl_furnaces:furnace"},
|
||||||
{"mcl_minecarts:minecart"},
|
{"mcl_minecarts:minecart"},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = "mcl_minecarts:hopper_minecart",
|
output = "mcl_minecarts:hopper_minecart",
|
||||||
recipe = {
|
recipe = {
|
||||||
{"mcl_hoppers:hopper"},
|
{"mcl_hoppers:hopper"},
|
||||||
{"mcl_minecarts:minecart"},
|
{"mcl_minecarts:minecart"},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = "mcl_minecarts:chest_minecart",
|
output = "mcl_minecarts:chest_minecart",
|
||||||
recipe = {
|
recipe = {
|
||||||
{"mcl_chests:chest"},
|
{"mcl_chests:chest"},
|
||||||
{"mcl_minecarts:minecart"},
|
{"mcl_minecarts:minecart"},
|
||||||
},
|
},
|
||||||
})]]
|
})
|
||||||
|
end
|
||||||
|
|
||||||
if has_mcl_wip then
|
if has_mcl_wip then
|
||||||
mcl_wip.register_wip_item("mcl_minecarts:chest_minecart")
|
mcl_wip.register_wip_item("mcl_minecarts:chest_minecart")
|
||||||
|
|
|
@ -33,4 +33,3 @@ Activates minecarts when powered=Aktiviert Loren, wenn bestromt
|
||||||
Emits redstone power when a minecart is detected=Gibt ein Redstonesignal aus, wenn eine Lore erfasst wird
|
Emits redstone power when a minecart is detected=Gibt ein Redstonesignal aus, wenn eine Lore erfasst wird
|
||||||
Vehicle for fast travel on rails=Fahrzeug zum schnellen Transport auf Schienen
|
Vehicle for fast travel on rails=Fahrzeug zum schnellen Transport auf Schienen
|
||||||
Can be ignited by tools or powered activator rail=Kann mit Werkzeugen oder bestromten Aktivierungsschienen angezündet werden
|
Can be ignited by tools or powered activator rail=Kann mit Werkzeugen oder bestromten Aktivierungsschienen angezündet werden
|
||||||
Sneak to dismount=Zum Aussteigen schleichen
|
|
||||||
|
|
|
@ -33,4 +33,3 @@ Activates minecarts when powered=Active les wagonnets lorsqu'il est alimenté
|
||||||
Emits redstone power when a minecart is detected=Émet de l'énergie redstone lorsqu'un wagonnet est détecté
|
Emits redstone power when a minecart is detected=Émet de l'énergie redstone lorsqu'un wagonnet est détecté
|
||||||
Vehicle for fast travel on rails=Véhicule pour voyager rapidement sur rails
|
Vehicle for fast travel on rails=Véhicule pour voyager rapidement sur rails
|
||||||
Can be ignited by tools or powered activator rail=Peut être allumé par des outils ou un rail d'activation motorisé
|
Can be ignited by tools or powered activator rail=Peut être allumé par des outils ou un rail d'activation motorisé
|
||||||
Sneak to dismount=
|
|
|
@ -1,36 +0,0 @@
|
||||||
# textdomain: mcl_minecarts
|
|
||||||
Minecart=Wagonik
|
|
||||||
Minecarts can be used for a quick transportion on rails.=Wagoniki mogą być użyte do szybkiego transportu po torach.
|
|
||||||
Minecarts only ride on rails and always follow the tracks. At a T-junction with no straight way ahead, they turn left. The speed is affected by the rail type.=Wagoniki mogą jeździć tylko po torach i zawsze podążają za wytyczoną ścieżką. W przypadku skrzyżowań typu T, gdzie nie ma prostej ścieżki, skręcają w lew. Ich szybkość zależy od typu torów.
|
|
||||||
You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving.=Możesz postawić wagonik na torach. Kliknij prawym przyciskiem myszy aby do niego wejść. Uderz go by zaczął się poruszać.
|
|
||||||
To obtain the minecart, punch it while holding down the sneak key.=Aby odzyskać wagonik uderz go podczas skradania.
|
|
||||||
A minecart with TNT is an explosive vehicle that travels on rail.=Wagonik z TNT jest wybuchowym pojazdem podróżującym po torach.
|
|
||||||
Place it on rails. Punch it to move it. The TNT is ignited with a flint and steel or when the minecart is on an powered activator rail.=Postaw go na torach. Uderz by zaczął się poruszać. TNT zapala się krzesiwem lub gdy wagonik jest na zasilonych torach aktywacyjnych.
|
|
||||||
To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited.=Aby odzyskać wagonik z TNT uderz go podczas skradania. Nie możesz tego zrobić gdy TNT jest zapalone.
|
|
||||||
A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel.=Wagonik z piecem jest pojazdem podróżującym na torach. Napędza on samego siebie za pomocą paliwa.
|
|
||||||
Place it on rails. If you give it some coal, the furnace will start burning for a long time and the minecart will be able to move itself. Punch it to get it moving.=Postaw go na torach. Jeśli dasz mu nieco węgla piec zacznie palić przez długi czas, a wagonik będzie się sam poruszał. Uderz go by zaczął się poruszać.
|
|
||||||
To obtain the minecart and furnace, punch them while holding down the sneak key.=Aby odzyskać wagonik z piecem uderz go podczas skradania.
|
|
||||||
Minecart with Chest=Wagonik ze skrzynią
|
|
||||||
Minecart with Furnace=Wagonik z piecem
|
|
||||||
Minecart with Command Block=Wagonik z blokiem poleceń
|
|
||||||
Minecart with Hopper=Wagonik z lejem
|
|
||||||
Minecart with TNT=Wagonik z TNT
|
|
||||||
Place them on the ground to build your railway, the rails will automatically connect to each other and will turn into curves, T-junctions, crossings and slopes as needed.=Postaw je na ziemi by zbudować ścieżkę z torów. Tory automatycznie połączą się ze sobą i zamienią się w zakręty, skrzyżowania typu T, skrzyżowania i równie w zależności od potrzeb.
|
|
||||||
Rail=Tor
|
|
||||||
Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction.=Tory mogą być wykorzystane do zbudowania torów dla wagoników. Zwyczajne tory nieco spowalniają wagoniki ze względu na tarcie.
|
|
||||||
Powered Rail=Zasilane tory
|
|
||||||
Rails can be used to build transport tracks for minecarts. Powered rails are able to accelerate and brake minecarts.=Tory mogą być wykorzystane do zbudowania torów dla wagoników. Zasilane tory mogą przyspieszać lub spowalniać wagoniki.
|
|
||||||
Without redstone power, the rail will brake minecarts. To make this rail accelerate minecarts, power it with redstone power.=Bez zasilania czerwienitem tory będą spowalniać wagoniki. Aby sprawić by je przyspieszały zasil je czerwienitem.
|
|
||||||
Activator Rail=Tory aktywacyjne
|
|
||||||
Rails can be used to build transport tracks for minecarts. Activator rails are used to activate special minecarts.=Tory mogą być wykorzystane do zbudowania torów dla wagoników. Tory aktywacyjne są wykorzystywane do aktywacji specjalnych wagoników.
|
|
||||||
To make this rail activate minecarts, power it with redstone power and send a minecart over this piece of rail.=Aby ten tor aktywował wagonik, zasil go czerwienitem i spraw by wagonik po nim przejechał.
|
|
||||||
Detector Rail=Tory z czujnikiem
|
|
||||||
Rails can be used to build transport tracks for minecarts. A detector rail is able to detect a minecart above it and powers redstone mechanisms.=Tory mogą być wykorzystane do zbudowania torów dla wagoników. Tory z czujnikiem są w stanie wykryć kiedy wagonik po nich przejeżdża i wysłać sygnał do czerwienitowych mechanizmów.
|
|
||||||
To detect a minecart and provide redstone power, connect it to redstone trails or redstone mechanisms and send any minecart over the rail.=Aby wykryć wagonik i dostarczyć zasilanie czerwienitem podłącz go czerwienitem to mechanizmu i spraw by wagonik po nim przejechał.
|
|
||||||
Track for minecarts=Tor dla wagoników
|
|
||||||
Speed up when powered, slow down when not powered=Przyspiesza gdy zasilane, spowalnia gdy nie
|
|
||||||
Activates minecarts when powered=Aktywuje wagoniki gdy zasilane
|
|
||||||
Emits redstone power when a minecart is detected=Emituje zasilanie czerwienitem gdy wagonik jest wykryty
|
|
||||||
Vehicle for fast travel on rails=Pojazd do szybkiej podróży na torach
|
|
||||||
Can be ignited by tools or powered activator rail=Może być zapalony przez narzędzia, lub zasilane tor aktywacyjne
|
|
||||||
Sneak to dismount=Zacznij się skradać by zejść
|
|
|
@ -1,6 +1,6 @@
|
||||||
name = mcl_minecarts
|
name = mcl_minecarts
|
||||||
author = Krock
|
author = Krock
|
||||||
description = Minecarts are vehicles to move players quickly on rails.
|
description = Minecarts are vehicles to move players quickly on rails.
|
||||||
depends = mcl_title, mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons
|
depends = mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons
|
||||||
optional_depends = doc_identifier, mcl_wip
|
optional_depends = doc_identifier, mcl_wip
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
local S = minetest.get_translator(minetest.get_current_modname())
|
local S = minetest.get_translator("mcl_minecarts")
|
||||||
|
|
||||||
-- Template rail function
|
-- Template rail function
|
||||||
local function register_rail(itemstring, tiles, def_extras, creative)
|
local register_rail = function(itemstring, tiles, def_extras, creative)
|
||||||
local groups = {handy=1,pickaxey=1, attached_node=1,rail=1,connect_to_raillike=minetest.raillike_group("rail"),dig_by_water=1,destroy_by_lava_flow=1, transport=1}
|
local groups = {handy=1,pickaxey=1, attached_node=1,rail=1,connect_to_raillike=minetest.raillike_group("rail"),dig_by_water=1,destroy_by_lava_flow=1, transport=1}
|
||||||
if creative == false then
|
if creative == false then
|
||||||
groups.not_in_creative_inventory = 1
|
groups.not_in_creative_inventory = 1
|
||||||
|
@ -206,11 +206,11 @@ register_rail("mcl_minecarts:detector_rail_on",
|
||||||
|
|
||||||
-- Crafting
|
-- Crafting
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = "mcl_minecarts:rail 16",
|
output = 'mcl_minecarts:rail 16',
|
||||||
recipe = {
|
recipe = {
|
||||||
{"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"},
|
{'mcl_core:iron_ingot', '', 'mcl_core:iron_ingot'},
|
||||||
{"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"},
|
{'mcl_core:iron_ingot', 'mcl_core:stick', 'mcl_core:iron_ingot'},
|
||||||
{"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"},
|
{'mcl_core:iron_ingot', '', 'mcl_core:iron_ingot'},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -502,6 +502,20 @@ and damages any entity caught inside the blast radius. Protection will limit
|
||||||
node destruction but not entity damage.
|
node destruction but not entity damage.
|
||||||
|
|
||||||
|
|
||||||
|
mobs:capture_mob
|
||||||
|
----------------
|
||||||
|
|
||||||
|
mobs:capture_mob(...)
|
||||||
|
|
||||||
|
Does nothing and returns false.
|
||||||
|
|
||||||
|
This function is provided for compability with Mobs Redo for an attempt to
|
||||||
|
capture a mob.
|
||||||
|
Mobs cannot be captured in MineClone 2.
|
||||||
|
|
||||||
|
In Mobs Redo, this is generally called inside the on_rightclick section of the mob
|
||||||
|
api code, it provides a chance of capturing the mob. See Mobs Redo documentation
|
||||||
|
of parameters.
|
||||||
|
|
||||||
Feeding and Taming/Breeding
|
Feeding and Taming/Breeding
|
||||||
---------------------------
|
---------------------------
|
||||||
|
@ -521,6 +535,19 @@ Will return true when mob is fed with item it likes.
|
||||||
them up
|
them up
|
||||||
|
|
||||||
|
|
||||||
|
Protecting Mobs
|
||||||
|
---------------
|
||||||
|
|
||||||
|
mobs:protect(self, clicker)
|
||||||
|
|
||||||
|
This function can be used to right-click any tamed mob with mobs:protector item,
|
||||||
|
this will protect the mob from harm inside of a protected area from other
|
||||||
|
players. Will return true when mob right-clicked with mobs:protector item.
|
||||||
|
|
||||||
|
'self' mob information
|
||||||
|
'clicker' player information
|
||||||
|
|
||||||
|
|
||||||
Riding Mobs
|
Riding Mobs
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
@ -578,7 +605,7 @@ Note: animation names above are from the pre-defined animation lists inside mob
|
||||||
registry without extensions.
|
registry without extensions.
|
||||||
|
|
||||||
|
|
||||||
mobs.set_mob_animation(self, name)
|
mobs:set_animation(self, name)
|
||||||
|
|
||||||
This function sets the current animation for mob, defaulting to "stand" if not
|
This function sets the current animation for mob, defaulting to "stand" if not
|
||||||
found.
|
found.
|
||||||
|
@ -754,5 +781,8 @@ mobs:register_mob("mob_horse:horse", {
|
||||||
inv:remove_item("main", "mobs:saddle")
|
inv:remove_item("main", "mobs:saddle")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- used to capture horse with magic lasso
|
||||||
|
mobs:capture_mob(self, clicker, 0, 0, 80, false, nil)
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,736 +0,0 @@
|
||||||
-- API for Mobs Redo: MineClone 2 Delux 2.0 DRM Free Early Access Super Extreme Edition
|
|
||||||
|
|
||||||
-- mobs library
|
|
||||||
mobs = {}
|
|
||||||
|
|
||||||
-- lua locals - can grab from this to easily plop them into the api lua files
|
|
||||||
|
|
||||||
--localize minetest functions
|
|
||||||
local minetest_settings = minetest.settings
|
|
||||||
local minetest_get_objects_inside_radius = minetest.get_objects_inside_radius
|
|
||||||
local minetest_get_modpath = minetest.get_modpath
|
|
||||||
local minetest_registered_nodes = minetest.registered_nodes
|
|
||||||
local minetest_get_node = minetest.get_node
|
|
||||||
--local minetest_get_item_group = minetest.get_item_group
|
|
||||||
local minetest_registered_entities = minetest.registered_entities
|
|
||||||
--local minetest_line_of_sight = minetest.line_of_sight
|
|
||||||
--local minetest_after = minetest.after
|
|
||||||
--local minetest_sound_play = minetest.sound_play
|
|
||||||
--local minetest_add_particlespawner = minetest.add_particlespawner
|
|
||||||
--local minetest_registered_items = minetest.registered_items
|
|
||||||
--local minetest_set_node = minetest.set_node
|
|
||||||
local minetest_add_item = minetest.add_item
|
|
||||||
--local minetest_get_craft_result = minetest.get_craft_result
|
|
||||||
--local minetest_find_path = minetest.find_path
|
|
||||||
local minetest_is_creative_enabled = minetest.is_creative_enabled
|
|
||||||
--local minetest_find_node_near = minetest.find_node_near
|
|
||||||
--local minetest_find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air
|
|
||||||
--local minetest_raycast = minetest.raycast
|
|
||||||
--local minetest_get_us_time = minetest.get_us_time
|
|
||||||
local minetest_add_entity = minetest.add_entity
|
|
||||||
--local minetest_get_natural_light = minetest.get_natural_light
|
|
||||||
--local minetest_get_node_or_nil = minetest.get_node_or_nil
|
|
||||||
|
|
||||||
-- localize math functions
|
|
||||||
local math = math
|
|
||||||
|
|
||||||
-- localize vector functions
|
|
||||||
local vector = vector
|
|
||||||
|
|
||||||
local string = string
|
|
||||||
|
|
||||||
-- mob constants
|
|
||||||
--local BREED_TIME = 30
|
|
||||||
--local BREED_TIME_AGAIN = 300
|
|
||||||
--local CHILD_GROW_TIME = 60*20
|
|
||||||
--local DEATH_DELAY = 0.5
|
|
||||||
local DEFAULT_FALL_SPEED = -10
|
|
||||||
--local FLOP_HEIGHT = 5.0
|
|
||||||
--local FLOP_HOR_SPEED = 1.5
|
|
||||||
local GRAVITY = minetest_settings:get("movement_gravity")-- + 9.81
|
|
||||||
|
|
||||||
local MAX_MOB_NAME_LENGTH = 30
|
|
||||||
|
|
||||||
|
|
||||||
--[[local MOB_CAP = {}
|
|
||||||
MOB_CAP.hostile = 70
|
|
||||||
MOB_CAP.passive = 10
|
|
||||||
MOB_CAP.ambient = 15
|
|
||||||
MOB_CAP.water = 15
|
|
||||||
]]
|
|
||||||
|
|
||||||
-- Load main settings
|
|
||||||
--local damage_enabled = minetest_settings:get_bool("enable_damage")
|
|
||||||
--local disable_blood = minetest_settings:get_bool("mobs_disable_blood")
|
|
||||||
--local mobs_drop_items = minetest_settings:get_bool("mobs_drop_items") ~= false
|
|
||||||
--local mobs_griefing = minetest_settings:get_bool("mobs_griefing") ~= false
|
|
||||||
--local spawn_protected = minetest_settings:get_bool("mobs_spawn_protected") ~= false
|
|
||||||
--local remove_far = true
|
|
||||||
local difficulty = tonumber(minetest_settings:get("mob_difficulty")) or 1.0
|
|
||||||
--local show_health = false
|
|
||||||
--local max_per_block = tonumber(minetest_settings:get("max_objects_per_block") or 64)
|
|
||||||
---local mobs_spawn_chance = tonumber(minetest_settings:get("mobs_spawn_chance") or 2.5)
|
|
||||||
|
|
||||||
-- pathfinding settings
|
|
||||||
--local enable_pathfinding = true
|
|
||||||
--local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching
|
|
||||||
--local stuck_path_timeout = 10 -- how long will mob follow path before giving up
|
|
||||||
|
|
||||||
-- default nodes
|
|
||||||
--local node_ice = "mcl_core:ice"
|
|
||||||
--local node_snowblock = "mcl_core:snowblock"
|
|
||||||
--local node_snow = "mcl_core:snow"
|
|
||||||
mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "mcl_core:dirt"
|
|
||||||
|
|
||||||
--local mod_weather = minetest_get_modpath("mcl_weather")
|
|
||||||
--local mod_explosions = minetest_get_modpath("mcl_explosions")
|
|
||||||
local mod_mobspawners = minetest_get_modpath("mcl_mobspawners")
|
|
||||||
--local mod_hunger = minetest_get_modpath("mcl_hunger")
|
|
||||||
--local mod_worlds = minetest_get_modpath("mcl_worlds")
|
|
||||||
--local mod_armor = minetest_get_modpath("mcl_armor")
|
|
||||||
--local mod_experience = minetest_get_modpath("mcl_experience")
|
|
||||||
|
|
||||||
|
|
||||||
-- random locals I found
|
|
||||||
--local los_switcher = false
|
|
||||||
--local height_switcher = false
|
|
||||||
|
|
||||||
-- Get translator
|
|
||||||
local S = minetest.get_translator(minetest.get_current_modname())
|
|
||||||
|
|
||||||
-- CMI support check
|
|
||||||
--local use_cmi = minetest.global_exists("cmi")
|
|
||||||
|
|
||||||
-- creative check
|
|
||||||
function mobs.is_creative(name)
|
|
||||||
return minetest_is_creative_enabled(name)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[local function atan(x)
|
|
||||||
if not x or x ~= x then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return math.atan(x)
|
|
||||||
end
|
|
||||||
end]]
|
|
||||||
|
|
||||||
-- Shows helpful debug info above each mob
|
|
||||||
--local mobs_debug = minetest_settings:get_bool("mobs_debug", false)
|
|
||||||
|
|
||||||
-- Peaceful mode message so players will know there are no monsters
|
|
||||||
if minetest_settings:get_bool("only_peaceful_mobs", false) then
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
|
||||||
minetest.chat_send_player(player:get_player_name(),
|
|
||||||
S("Peaceful mode active! No monsters will spawn."))
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local api_path = minetest.get_modpath(minetest.get_current_modname()).."/api/mob_functions/"
|
|
||||||
|
|
||||||
--ignite all parts of the api
|
|
||||||
dofile(api_path .. "flow_lib.lua")
|
|
||||||
dofile(api_path .. "ai.lua")
|
|
||||||
dofile(api_path .. "animation.lua")
|
|
||||||
dofile(api_path .. "collision.lua")
|
|
||||||
dofile(api_path .. "environment.lua")
|
|
||||||
dofile(api_path .. "interaction.lua")
|
|
||||||
dofile(api_path .. "movement.lua")
|
|
||||||
dofile(api_path .. "set_up.lua")
|
|
||||||
dofile(api_path .. "attack_type_instructions.lua")
|
|
||||||
dofile(api_path .. "sound_handling.lua")
|
|
||||||
dofile(api_path .. "death_logic.lua")
|
|
||||||
dofile(api_path .. "mob_effects.lua")
|
|
||||||
dofile(api_path .. "projectile_handling.lua")
|
|
||||||
dofile(api_path .. "breeding.lua")
|
|
||||||
dofile(api_path .. "head_logic.lua")
|
|
||||||
|
|
||||||
|
|
||||||
mobs.spawning_mobs = {}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- register mob entity
|
|
||||||
function mobs:register_mob(name, def)
|
|
||||||
|
|
||||||
local collisionbox = def.collisionbox or {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}
|
|
||||||
|
|
||||||
-- Workaround for <https://github.com/minetest/minetest/issues/5966>:
|
|
||||||
-- Increase upper Y limit to avoid mobs glitching through solid nodes.
|
|
||||||
-- FIXME: Remove workaround if it's no longer needed.
|
|
||||||
|
|
||||||
if collisionbox[5] < 0.79 then
|
|
||||||
collisionbox[5] = 0.79
|
|
||||||
end
|
|
||||||
|
|
||||||
mobs.spawning_mobs[name] = true
|
|
||||||
|
|
||||||
local function scale_difficulty(value, default, min, special)
|
|
||||||
if (not value) or (value == default) or (value == special) then
|
|
||||||
return default
|
|
||||||
else
|
|
||||||
return math.max(min, value * difficulty)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_entity(name, {
|
|
||||||
description = def.description,
|
|
||||||
use_texture_alpha = def.use_texture_alpha,
|
|
||||||
stepheight = def.stepheight or 0.6,
|
|
||||||
stepheight_backup = def.stepheight or 0.6,
|
|
||||||
name = name,
|
|
||||||
type = def.type,
|
|
||||||
attack_type = def.attack_type,
|
|
||||||
fly = def.fly,
|
|
||||||
fly_in = def.fly_in or {"air", "__airlike"},
|
|
||||||
owner = def.owner or "",
|
|
||||||
order = def.order or "",
|
|
||||||
on_die = def.on_die,
|
|
||||||
spawn_small_alternative = def.spawn_small_alternative,
|
|
||||||
do_custom = def.do_custom,
|
|
||||||
jump_height = def.jump_height or 4, -- was 6
|
|
||||||
rotate = def.rotate or 0, -- 0=front, 90=side, 180=back, 270=side2
|
|
||||||
hp_min = scale_difficulty(def.hp_min, 5, 1),
|
|
||||||
hp_max = scale_difficulty(def.hp_max, 10, 1),
|
|
||||||
xp_min = def.xp_min or 1,
|
|
||||||
xp_max = def.xp_max or 5,
|
|
||||||
breath_max = def.breath_max or 6,
|
|
||||||
breathes_in_water = def.breathes_in_water or false,
|
|
||||||
physical = true,
|
|
||||||
collisionbox = collisionbox,
|
|
||||||
collide_with_objects = def.collide_with_objects or false,
|
|
||||||
selectionbox = def.selectionbox or def.collisionbox,
|
|
||||||
visual = def.visual,
|
|
||||||
visual_size = def.visual_size or {x = 1, y = 1},
|
|
||||||
mesh = def.mesh,
|
|
||||||
makes_footstep_sound = def.makes_footstep_sound or false,
|
|
||||||
view_range = def.view_range or 16,
|
|
||||||
walk_velocity = def.walk_velocity or 1,
|
|
||||||
run_velocity = def.run_velocity or 2,
|
|
||||||
damage = scale_difficulty(def.damage, 0, 0),
|
|
||||||
light_damage = def.light_damage or 0,
|
|
||||||
sunlight_damage = def.sunlight_damage or 0,
|
|
||||||
water_damage = def.water_damage or 0,
|
|
||||||
lava_damage = def.lava_damage or 8,
|
|
||||||
fire_damage = def.fire_damage or 1,
|
|
||||||
suffocation = def.suffocation or true,
|
|
||||||
fall_damage = def.fall_damage or 1,
|
|
||||||
fall_speed = def.fall_speed or DEFAULT_FALL_SPEED, -- must be lower than -2
|
|
||||||
drops = def.drops or {},
|
|
||||||
armor = def.armor or 100,
|
|
||||||
on_rightclick = mobs.create_mob_on_rightclick(def.on_rightclick),
|
|
||||||
arrow = def.arrow,
|
|
||||||
shoot_interval = def.shoot_interval,
|
|
||||||
sounds = def.sounds or {},
|
|
||||||
animation = def.animation,
|
|
||||||
jump = def.jump ~= false,
|
|
||||||
walk_chance = def.walk_chance or 50,
|
|
||||||
attacks_monsters = def.attacks_monsters or false,
|
|
||||||
group_attack = def.group_attack or false,
|
|
||||||
passive = def.passive or false,
|
|
||||||
knock_back = def.knock_back ~= false,
|
|
||||||
shoot_offset = def.shoot_offset or 0,
|
|
||||||
floats = def.floats or 1, -- floats in water by default
|
|
||||||
floats_on_lava = def.floats_on_lava or 0,
|
|
||||||
replace_rate = def.replace_rate,
|
|
||||||
replace_what = def.replace_what,
|
|
||||||
replace_with = def.replace_with,
|
|
||||||
replace_offset = def.replace_offset or 0,
|
|
||||||
on_replace = def.on_replace,
|
|
||||||
timer = 0,
|
|
||||||
state_timer = 0,
|
|
||||||
env_damage_timer = 0,
|
|
||||||
tamed = false,
|
|
||||||
pause_timer = 0,
|
|
||||||
gotten = false,
|
|
||||||
reach = def.reach or 3,
|
|
||||||
htimer = 0,
|
|
||||||
texture_list = def.textures,
|
|
||||||
child_texture = def.child_texture,
|
|
||||||
docile_by_day = def.docile_by_day or false,
|
|
||||||
time_of_day = 0.5,
|
|
||||||
fear_height = def.fear_height or 0,
|
|
||||||
runaway = def.runaway,
|
|
||||||
runaway_timer = 0,
|
|
||||||
pathfinding = def.pathfinding,
|
|
||||||
immune_to = def.immune_to or {},
|
|
||||||
explosion_radius = def.explosion_radius, -- LEGACY
|
|
||||||
explosion_damage_radius = def.explosion_damage_radius, -- LEGACY
|
|
||||||
explosiontimer_reset_radius = def.explosiontimer_reset_radius,
|
|
||||||
explosion_timer = def.explosion_timer or 3,
|
|
||||||
allow_fuse_reset = def.allow_fuse_reset ~= false,
|
|
||||||
stop_to_explode = def.stop_to_explode ~= false,
|
|
||||||
custom_attack = def.custom_attack,
|
|
||||||
double_melee_attack = def.double_melee_attack,
|
|
||||||
dogshoot_switch = def.dogshoot_switch,
|
|
||||||
dogshoot_count = 0,
|
|
||||||
dogshoot_count_max = def.dogshoot_count_max or 5,
|
|
||||||
dogshoot_count2_max = def.dogshoot_count2_max or (def.dogshoot_count_max or 5),
|
|
||||||
attack_animals = def.attack_animals or false,
|
|
||||||
specific_attack = def.specific_attack,
|
|
||||||
runaway_from = def.runaway_from,
|
|
||||||
owner_loyal = def.owner_loyal,
|
|
||||||
facing_fence = false,
|
|
||||||
|
|
||||||
_cmi_is_mob = true,
|
|
||||||
|
|
||||||
pushable = def.pushable or true,
|
|
||||||
|
|
||||||
--j4i stuff
|
|
||||||
yaw = 0,
|
|
||||||
automatic_face_movement_dir = def.rotate or 0, -- 0=front, 90=side, 180=back, 270=side2
|
|
||||||
automatic_face_movement_max_rotation_per_sec = 360, --degrees
|
|
||||||
backface_culling = true,
|
|
||||||
walk_timer = 0,
|
|
||||||
stand_timer = 0,
|
|
||||||
current_animation = "",
|
|
||||||
gravity = GRAVITY,
|
|
||||||
swim = def.swim,
|
|
||||||
swim_in = def.swim_in or {mobs_mc.items.water_source, "mcl_core:water_flowing", mobs_mc.items.river_water_source},
|
|
||||||
pitch_switch = "static",
|
|
||||||
jump_only = def.jump_only,
|
|
||||||
hostile = def.hostile,
|
|
||||||
neutral = def.neutral,
|
|
||||||
attacking = nil,
|
|
||||||
visual_size_origin = def.visual_size or {x = 1, y = 1, z = 1},
|
|
||||||
punch_timer_cooloff = def.punch_timer_cooloff or 0.5,
|
|
||||||
death_animation_timer = 0,
|
|
||||||
hostile_cooldown = def.hostile_cooldown or 15,
|
|
||||||
tilt_fly = def.tilt_fly,
|
|
||||||
tilt_swim = def.tilt_swim,
|
|
||||||
fall_slow = def.fall_slow,
|
|
||||||
projectile_cooldown_min = def.projectile_cooldown_min or 2,
|
|
||||||
projectile_cooldown_max = def.projectile_cooldown_max or 6,
|
|
||||||
skittish = def.skittish,
|
|
||||||
|
|
||||||
minimum_follow_distance = def.minimum_follow_distance or 0.5, --make mobs not freak out when underneath
|
|
||||||
|
|
||||||
memory = 0, -- memory timer if chasing/following
|
|
||||||
fly_random_while_attack = def.fly_random_while_attack,
|
|
||||||
|
|
||||||
--for spiders
|
|
||||||
always_climb = def.always_climb,
|
|
||||||
|
|
||||||
--despawn mechanic variables
|
|
||||||
lifetimer_reset = 30, --30 seconds
|
|
||||||
lifetimer = 30, --30 seconds
|
|
||||||
|
|
||||||
--breeding stuff
|
|
||||||
breed_timer = 0,
|
|
||||||
breed_lookout_timer = 0,
|
|
||||||
breed_distance = def.breed_distance or 1.5, --how far away mobs have to be to begin actual breeding
|
|
||||||
breed_lookout_timer_goal = 30, --30 seconds (this timer is for how long the mob looks for a mate)
|
|
||||||
breed_timer_cooloff = 5*60, -- 5 minutes (this timer is for how long the mob has to wait before being bred again)
|
|
||||||
bred = false,
|
|
||||||
follow = def.follow, --this item is also used for the breeding mechanism
|
|
||||||
follow_distance = def.follow_distance or 2,
|
|
||||||
baby_size = def.baby_size or 0.5,
|
|
||||||
baby = false,
|
|
||||||
grow_up_timer = 0,
|
|
||||||
grow_up_goal = 20*60, --in 20 minutes the mob grows up
|
|
||||||
special_breed_timer = 0, --this is used for the AHEM AHEM part of breeding
|
|
||||||
|
|
||||||
backup_visual_size = def.visual_size,
|
|
||||||
backup_collisionbox = collisionbox,
|
|
||||||
backup_selectionbox = def.selectionbox or def.collisionbox,
|
|
||||||
|
|
||||||
|
|
||||||
--fire timer
|
|
||||||
burn_timer = 0,
|
|
||||||
|
|
||||||
ignores_cobwebs = def.ignores_cobwebs,
|
|
||||||
breath = def.breath_max or 6,
|
|
||||||
|
|
||||||
random_sound_timer_min = 3,
|
|
||||||
random_sound_timer_max = 10,
|
|
||||||
|
|
||||||
--head code variables
|
|
||||||
--defaults are for the cow's default
|
|
||||||
--because I don't know what else to set them
|
|
||||||
--to :P
|
|
||||||
|
|
||||||
--you must use these to adjust the mob's head positions
|
|
||||||
|
|
||||||
--has_head is used as a logic gate (quick easy check)
|
|
||||||
has_head = def.has_head or false,
|
|
||||||
--head_bone is the actual bone in the model which the head
|
|
||||||
--is attached to for animation
|
|
||||||
head_bone = def.head_bone or "head",
|
|
||||||
|
|
||||||
--this part controls the base position of the head calculations
|
|
||||||
--localized to the mob's visual yaw when gotten (self.object:get_yaw())
|
|
||||||
--you can enable the debug in /mob_functions/head_logic.lua by uncommenting the
|
|
||||||
--particle spawner code
|
|
||||||
head_height_offset = def.head_height_offset or 1.0525,
|
|
||||||
head_direction_offset = def.head_direction_offset or 0.5,
|
|
||||||
|
|
||||||
--this part controls the visual of the head
|
|
||||||
head_bone_pos_y = def.head_bone_pos_y or 3.6,
|
|
||||||
head_bone_pos_z = def.head_bone_pos_z or -0.6,
|
|
||||||
head_pitch_modifier = def.head_pitch_modifier or 0,
|
|
||||||
|
|
||||||
--these variables are switches in case the model
|
|
||||||
--moves the wrong way
|
|
||||||
swap_y_with_x = def.swap_y_with_x or false,
|
|
||||||
reverse_head_yaw = def.reverse_head_yaw or false,
|
|
||||||
|
|
||||||
--END HEAD CODE VARIABLES
|
|
||||||
|
|
||||||
--end j4i stuff
|
|
||||||
|
|
||||||
-- MCL2 extensions
|
|
||||||
teleport = mobs.teleport,
|
|
||||||
do_teleport = def.do_teleport,
|
|
||||||
spawn_class = def.spawn_class,
|
|
||||||
ignores_nametag = def.ignores_nametag or false,
|
|
||||||
rain_damage = def.rain_damage or 0,
|
|
||||||
glow = def.glow,
|
|
||||||
--can_despawn = can_despawn,
|
|
||||||
child = def.child or false,
|
|
||||||
texture_mods = {},
|
|
||||||
shoot_arrow = def.shoot_arrow,
|
|
||||||
sounds_child = def.sounds_child,
|
|
||||||
explosion_strength = def.explosion_strength,
|
|
||||||
suffocation_timer = 0,
|
|
||||||
follow_velocity = def.follow_velocity or 2.4,
|
|
||||||
instant_death = def.instant_death or false,
|
|
||||||
fire_resistant = def.fire_resistant or false,
|
|
||||||
fire_damage_resistant = def.fire_damage_resistant or false,
|
|
||||||
ignited_by_sunlight = def.ignited_by_sunlight or false,
|
|
||||||
eye_height = def.eye_height or 1.5,
|
|
||||||
defuse_reach = def.defuse_reach or 4,
|
|
||||||
-- End of MCL2 extensions
|
|
||||||
|
|
||||||
on_spawn = def.on_spawn,
|
|
||||||
|
|
||||||
--on_blast = def.on_blast or do_tnt,
|
|
||||||
|
|
||||||
on_step = mobs.mob_step,
|
|
||||||
|
|
||||||
--do_punch = def.do_punch,
|
|
||||||
|
|
||||||
on_punch = mobs.mob_punch,
|
|
||||||
|
|
||||||
--on_breed = def.on_breed,
|
|
||||||
|
|
||||||
--on_grown = def.on_grown,
|
|
||||||
|
|
||||||
--on_detach_child = mob_detach_child,
|
|
||||||
|
|
||||||
on_activate = function(self, staticdata, dtime)
|
|
||||||
self.object:set_acceleration(vector.new(0,-GRAVITY, 0))
|
|
||||||
return mobs.mob_activate(self, staticdata, def, dtime)
|
|
||||||
end,
|
|
||||||
|
|
||||||
get_staticdata = function(self)
|
|
||||||
return mobs.mob_staticdata(self)
|
|
||||||
end,
|
|
||||||
|
|
||||||
--harmed_by_heal = def.harmed_by_heal,
|
|
||||||
})
|
|
||||||
|
|
||||||
if minetest_get_modpath("doc_identifier") then
|
|
||||||
doc.sub.identifier.register_object(name, "basics", "mobs")
|
|
||||||
end
|
|
||||||
|
|
||||||
end -- END mobs:register_mob function
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- register arrow for shoot attack
|
|
||||||
function mobs:register_arrow(name, def)
|
|
||||||
|
|
||||||
-- errorcheck
|
|
||||||
if not name or not def then
|
|
||||||
print("failed to register arrow entity")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_entity(name.."_entity", {
|
|
||||||
|
|
||||||
physical = false,
|
|
||||||
visual = def.visual,
|
|
||||||
visual_size = def.visual_size,
|
|
||||||
textures = def.textures,
|
|
||||||
velocity = def.velocity,
|
|
||||||
hit_player = def.hit_player,
|
|
||||||
hit_node = def.hit_node,
|
|
||||||
hit_mob = def.hit_mob,
|
|
||||||
hit_object = def.hit_object,
|
|
||||||
drop = def.drop or false, -- drops arrow as registered item when true
|
|
||||||
collisionbox = {0, 0, 0, 0, 0, 0}, -- remove box around arrows
|
|
||||||
timer = 0,
|
|
||||||
switch = 0,
|
|
||||||
owner_id = def.owner_id,
|
|
||||||
rotate = def.rotate,
|
|
||||||
speed = def.speed or nil,
|
|
||||||
on_step = function(self)
|
|
||||||
|
|
||||||
local vel = self.object:get_velocity()
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
|
|
||||||
if self.timer > 150
|
|
||||||
or not mobs.within_limits(pos, 0) then
|
|
||||||
mcl_burning.extinguish(self.object)
|
|
||||||
self.object:remove();
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- does arrow have a tail (fireball)
|
|
||||||
if def.tail
|
|
||||||
and def.tail == 1
|
|
||||||
and def.tail_texture then
|
|
||||||
|
|
||||||
--do this to prevent clipping through main entity sprite
|
|
||||||
local pos_adjustment = vector.multiply(vector.normalize(vel), -1)
|
|
||||||
local divider = def.tail_distance_divider or 1
|
|
||||||
pos_adjustment = vector.divide(pos_adjustment, divider)
|
|
||||||
local new_pos = vector.add(pos, pos_adjustment)
|
|
||||||
minetest.add_particle({
|
|
||||||
pos = new_pos,
|
|
||||||
velocity = {x = 0, y = 0, z = 0},
|
|
||||||
acceleration = {x = 0, y = 0, z = 0},
|
|
||||||
expirationtime = def.expire or 0.25,
|
|
||||||
collisiondetection = false,
|
|
||||||
texture = def.tail_texture,
|
|
||||||
size = def.tail_size or 5,
|
|
||||||
glow = def.glow or 0,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.hit_node then
|
|
||||||
|
|
||||||
local node = minetest_get_node(pos).name
|
|
||||||
|
|
||||||
if minetest_registered_nodes[node].walkable then
|
|
||||||
|
|
||||||
self.hit_node(self, pos, node)
|
|
||||||
|
|
||||||
if self.drop == true then
|
|
||||||
|
|
||||||
pos.y = pos.y + 1
|
|
||||||
|
|
||||||
self.lastpos = (self.lastpos or pos)
|
|
||||||
|
|
||||||
minetest_add_item(self.lastpos, self.object:get_luaentity().name)
|
|
||||||
end
|
|
||||||
|
|
||||||
self.object:remove();
|
|
||||||
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.hit_player or self.hit_mob or self.hit_object then
|
|
||||||
|
|
||||||
for _,player in pairs(minetest_get_objects_inside_radius(pos, 1.5)) do
|
|
||||||
|
|
||||||
if self.hit_player
|
|
||||||
and player:is_player() then
|
|
||||||
|
|
||||||
if self.hit_player then
|
|
||||||
self.hit_player(self, player)
|
|
||||||
else
|
|
||||||
mobs.arrow_hit(self, player)
|
|
||||||
end
|
|
||||||
|
|
||||||
self.object:remove();
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
local entity = player:get_luaentity()
|
|
||||||
|
|
||||||
if entity
|
|
||||||
and self.hit_mob
|
|
||||||
and entity._cmi_is_mob == true
|
|
||||||
and tostring(player) ~= self.owner_id
|
|
||||||
and entity.name ~= self.object:get_luaentity().name
|
|
||||||
and (self._shooter and entity.name ~= self._shooter:get_luaentity().name) then
|
|
||||||
|
|
||||||
--self.hit_mob(self, player)
|
|
||||||
self.object:remove();
|
|
||||||
return
|
|
||||||
end
|
|
||||||
]]--
|
|
||||||
|
|
||||||
--[[
|
|
||||||
if entity
|
|
||||||
and self.hit_object
|
|
||||||
and (not entity._cmi_is_mob)
|
|
||||||
and tostring(player) ~= self.owner_id
|
|
||||||
and entity.name ~= self.object:get_luaentity().name
|
|
||||||
and (self._shooter and entity.name ~= self._shooter:get_luaentity().name) then
|
|
||||||
|
|
||||||
--self.hit_object(self, player)
|
|
||||||
self.object:remove();
|
|
||||||
return
|
|
||||||
end
|
|
||||||
]]--
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self.lastpos = pos
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Register spawn eggs
|
|
||||||
|
|
||||||
-- Note: This also introduces the “spawn_egg” group:
|
|
||||||
-- * spawn_egg=1: Spawn egg (generic mob, no metadata)
|
|
||||||
-- * spawn_egg=2: Spawn egg (captured/tamed mob, metadata)
|
|
||||||
function mobs:register_egg(mob, desc, background, addegg, no_creative)
|
|
||||||
|
|
||||||
local grp = {spawn_egg = 1}
|
|
||||||
|
|
||||||
-- do NOT add this egg to creative inventory (e.g. dungeon master)
|
|
||||||
if no_creative == true then
|
|
||||||
grp.not_in_creative_inventory = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local invimg = background
|
|
||||||
|
|
||||||
if addegg == 1 then
|
|
||||||
invimg = "mobs_chicken_egg.png^(" .. invimg ..
|
|
||||||
"^[mask:mobs_chicken_egg_overlay.png)"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- register old stackable mob egg
|
|
||||||
minetest.register_craftitem(mob, {
|
|
||||||
|
|
||||||
description = desc,
|
|
||||||
inventory_image = invimg,
|
|
||||||
groups = grp,
|
|
||||||
|
|
||||||
_doc_items_longdesc = S("This allows you to place a single mob."),
|
|
||||||
_doc_items_usagehelp = S("Just place it where you want the mob to appear. Animals will spawn tamed, unless you hold down the sneak key while placing. If you place this on a mob spawner, you change the mob it spawns."),
|
|
||||||
|
|
||||||
on_place = function(itemstack, placer, pointed_thing)
|
|
||||||
|
|
||||||
local pos = pointed_thing.above
|
|
||||||
|
|
||||||
-- am I clicking on something with existing on_rightclick function?
|
|
||||||
local under = minetest_get_node(pointed_thing.under)
|
|
||||||
local def = minetest_registered_nodes[under.name]
|
|
||||||
if def and def.on_rightclick then
|
|
||||||
return def.on_rightclick(pointed_thing.under, under, placer, itemstack)
|
|
||||||
end
|
|
||||||
|
|
||||||
if pos
|
|
||||||
--and within_limits(pos, 0)
|
|
||||||
and not minetest.is_protected(pos, placer:get_player_name()) then
|
|
||||||
|
|
||||||
local name = placer:get_player_name()
|
|
||||||
local privs = minetest.get_player_privs(name)
|
|
||||||
if mod_mobspawners and under.name == "mcl_mobspawners:spawner" then
|
|
||||||
if minetest.is_protected(pointed_thing.under, name) then
|
|
||||||
minetest.record_protection_violation(pointed_thing.under, name)
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
if not privs.maphack then
|
|
||||||
minetest.chat_send_player(name, S("You need the “maphack” privilege to change the mob spawner."))
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
mcl_mobspawners.setup_spawner(pointed_thing.under, itemstack:get_name())
|
|
||||||
if not mobs.is_creative(name) then
|
|
||||||
itemstack:take_item()
|
|
||||||
end
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
if not minetest_registered_entities[mob] then
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
if minetest_settings:get_bool("only_peaceful_mobs", false)
|
|
||||||
and minetest_registered_entities[mob].type == "monster" then
|
|
||||||
minetest.chat_send_player(name, S("Only peaceful mobs allowed!"))
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
local mob = minetest_add_entity(pos, mob)
|
|
||||||
minetest.log("action", "Mob spawned: "..name.." at "..minetest.pos_to_string(pos))
|
|
||||||
local ent = mob:get_luaentity()
|
|
||||||
|
|
||||||
-- don't set owner if monster or sneak pressed
|
|
||||||
--[[
|
|
||||||
if ent.type ~= "monster"
|
|
||||||
and not placer:get_player_control().sneak then
|
|
||||||
ent.owner = placer:get_player_name()
|
|
||||||
ent.tamed = true
|
|
||||||
end
|
|
||||||
]]--
|
|
||||||
|
|
||||||
-- set nametag
|
|
||||||
local nametag = itemstack:get_meta():get_string("name")
|
|
||||||
if nametag ~= "" then
|
|
||||||
if string.len(nametag) > MAX_MOB_NAME_LENGTH then
|
|
||||||
nametag = string.sub(nametag, 1, MAX_MOB_NAME_LENGTH)
|
|
||||||
end
|
|
||||||
ent.nametag = nametag
|
|
||||||
--update_tag(ent)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if not in creative then take item
|
|
||||||
if not mobs.is_creative(placer:get_player_name()) then
|
|
||||||
itemstack:take_item()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return itemstack
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
|
@ -1,257 +0,0 @@
|
||||||
local math = math
|
|
||||||
local vector = vector
|
|
||||||
|
|
||||||
local HALF_PI = math.pi/2
|
|
||||||
|
|
||||||
|
|
||||||
local vector_direction = vector.direction
|
|
||||||
local vector_distance = vector.distance
|
|
||||||
local vector_new = vector.new
|
|
||||||
|
|
||||||
local minetest_dir_to_yaw = minetest.dir_to_yaw
|
|
||||||
|
|
||||||
-- set defined animation
|
|
||||||
mobs.set_mob_animation = function(self, anim, fixed_frame)
|
|
||||||
|
|
||||||
if not self.animation or not anim then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.state == "die" and anim ~= "die" and anim ~= "stand" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
if (not self.animation[anim .. "_start"] or not self.animation[anim .. "_end"]) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
--animations break if they are constantly set
|
|
||||||
--so we put this return gate to check if it is
|
|
||||||
--already at the animation we are trying to implement
|
|
||||||
if self.current_animation == anim then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local a_start = self.animation[anim .. "_start"]
|
|
||||||
local a_end
|
|
||||||
|
|
||||||
if fixed_frame then
|
|
||||||
a_end = a_start
|
|
||||||
else
|
|
||||||
a_end = self.animation[anim .. "_end"]
|
|
||||||
end
|
|
||||||
|
|
||||||
self.object:set_animation({
|
|
||||||
x = a_start,
|
|
||||||
y = a_end},
|
|
||||||
self.animation[anim .. "_speed"] or self.animation.speed_normal or 15,
|
|
||||||
0, self.animation[anim .. "_loop"] ~= false)
|
|
||||||
|
|
||||||
self.current_animation = anim
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mobs.death_effect = function(pos, yaw, collisionbox, rotate)
|
|
||||||
local min, max
|
|
||||||
if collisionbox then
|
|
||||||
min = {x=collisionbox[1], y=collisionbox[2], z=collisionbox[3]}
|
|
||||||
max = {x=collisionbox[4], y=collisionbox[5], z=collisionbox[6]}
|
|
||||||
else
|
|
||||||
min = { x = -0.5, y = 0, z = -0.5 }
|
|
||||||
max = { x = 0.5, y = 0.5, z = 0.5 }
|
|
||||||
end
|
|
||||||
if rotate then
|
|
||||||
min = vector.rotate(min, {x=0, y=yaw, z=math.pi/2})
|
|
||||||
max = vector.rotate(max, {x=0, y=yaw, z=math.pi/2})
|
|
||||||
min, max = vector.sort(min, max)
|
|
||||||
min = vector.multiply(min, 0.5)
|
|
||||||
max = vector.multiply(max, 0.5)
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.add_particlespawner({
|
|
||||||
amount = 50,
|
|
||||||
time = 0.001,
|
|
||||||
minpos = vector.add(pos, min),
|
|
||||||
maxpos = vector.add(pos, max),
|
|
||||||
minvel = vector_new(-5,-5,-5),
|
|
||||||
maxvel = vector_new(5,5,5),
|
|
||||||
minexptime = 1.1,
|
|
||||||
maxexptime = 1.5,
|
|
||||||
minsize = 1,
|
|
||||||
maxsize = 2,
|
|
||||||
collisiondetection = false,
|
|
||||||
vertical = false,
|
|
||||||
texture = "mcl_particles_mob_death.png^[colorize:#000000:255",
|
|
||||||
})
|
|
||||||
|
|
||||||
minetest.sound_play("mcl_mobs_mob_poof", {
|
|
||||||
pos = pos,
|
|
||||||
gain = 1.0,
|
|
||||||
max_hear_distance = 8,
|
|
||||||
}, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--this allows auto facedir rotation while making it so mobs
|
|
||||||
--don't look like wet noodles flopping around
|
|
||||||
mobs.movement_rotation_lock = function(self)
|
|
||||||
local current_engine_yaw = self.object:get_yaw()
|
|
||||||
local current_lua_yaw = self.yaw
|
|
||||||
|
|
||||||
if current_engine_yaw > math.pi * 2 then
|
|
||||||
current_engine_yaw = current_engine_yaw - (math.pi * 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
if math.abs(current_engine_yaw - current_lua_yaw) <= 0.05 and self.object:get_properties().automatic_face_movement_dir then
|
|
||||||
self.object:set_properties{automatic_face_movement_dir = false}
|
|
||||||
elseif math.abs(current_engine_yaw - current_lua_yaw) > 0.05 and self.object:get_properties().automatic_face_movement_dir == false then
|
|
||||||
self.object:set_properties{automatic_face_movement_dir = self.rotate}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--this is used when a mob is chasing a player
|
|
||||||
mobs.set_yaw_while_attacking = function(self)
|
|
||||||
|
|
||||||
if self.object:get_properties().automatic_face_movement_dir then
|
|
||||||
self.object:set_properties{automatic_face_movement_dir = false}
|
|
||||||
end
|
|
||||||
|
|
||||||
--turn positions into pseudo 2d vectors
|
|
||||||
local pos1 = self.object:get_pos()
|
|
||||||
pos1.y = 0
|
|
||||||
|
|
||||||
local pos2 = self.attacking:get_pos()
|
|
||||||
pos2.y = 0
|
|
||||||
|
|
||||||
local new_direction = vector_direction(pos1,pos2)
|
|
||||||
local new_yaw = minetest_dir_to_yaw(new_direction)
|
|
||||||
|
|
||||||
self.object:set_yaw(new_yaw)
|
|
||||||
self.yaw = new_yaw
|
|
||||||
end
|
|
||||||
|
|
||||||
--this is used to unlock a mob's yaw after attacking
|
|
||||||
mobs.unlock_yaw = function(self)
|
|
||||||
if self.object:get_properties().automatic_face_movement_dir == false then
|
|
||||||
self.object:set_properties{automatic_face_movement_dir = self.rotate}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--this is used to lock a mob's yaw when they're standing
|
|
||||||
mobs.lock_yaw = function(self)
|
|
||||||
if self.object:get_properties().automatic_face_movement_dir then
|
|
||||||
self.object:set_properties{automatic_face_movement_dir = false}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local calculate_pitch = function(self)
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local pos2 = self.old_pos
|
|
||||||
|
|
||||||
if pos == nil or pos2 == nil then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
return minetest_dir_to_yaw(vector_new(vector_distance(vector_new(pos.x,0,pos.z),vector_new(pos2.x,0,pos2.z)),0,pos.y - pos2.y)) + HALF_PI
|
|
||||||
end
|
|
||||||
|
|
||||||
--this is a helper function used to make mobs pitch rotation dynamically flow when flying/swimming
|
|
||||||
mobs.set_dynamic_pitch = function(self)
|
|
||||||
local pitch = calculate_pitch(self)
|
|
||||||
|
|
||||||
if not pitch then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local current_rotation = self.object:get_rotation()
|
|
||||||
|
|
||||||
current_rotation.x = pitch
|
|
||||||
|
|
||||||
self.object:set_rotation(current_rotation)
|
|
||||||
|
|
||||||
self.pitch_switch = "dynamic"
|
|
||||||
end
|
|
||||||
|
|
||||||
--this is a helper function used to make mobs pitch rotation reset when flying/swimming
|
|
||||||
mobs.set_static_pitch = function(self)
|
|
||||||
|
|
||||||
if self.pitch_switch == "static" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local current_rotation = self.object:get_rotation()
|
|
||||||
|
|
||||||
current_rotation.x = 0
|
|
||||||
|
|
||||||
self.object:set_rotation(current_rotation)
|
|
||||||
self.pitch_switch = "static"
|
|
||||||
end
|
|
||||||
|
|
||||||
--this is a helper function for mobs explosion animation
|
|
||||||
mobs.handle_explosion_animation = function(self)
|
|
||||||
|
|
||||||
--secondary catch-all
|
|
||||||
if not self.explosion_animation then
|
|
||||||
self.explosion_animation = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
--the timer works from 0 for sense of a 0 based counting
|
|
||||||
--but this just bumps it up so it's usable in here
|
|
||||||
local explosion_timer_adjust = self.explosion_animation + 1
|
|
||||||
|
|
||||||
|
|
||||||
local visual_size_modified = table.copy(self.visual_size_origin)
|
|
||||||
|
|
||||||
visual_size_modified.x = visual_size_modified.x * (explosion_timer_adjust ^ 3)
|
|
||||||
visual_size_modified.y = visual_size_modified.y * explosion_timer_adjust
|
|
||||||
|
|
||||||
self.object:set_properties({visual_size = visual_size_modified})
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--this is used when a mob is following player
|
|
||||||
mobs.set_yaw_while_following = function(self)
|
|
||||||
|
|
||||||
if self.object:get_properties().automatic_face_movement_dir then
|
|
||||||
self.object:set_properties{automatic_face_movement_dir = false}
|
|
||||||
end
|
|
||||||
|
|
||||||
--turn positions into pseudo 2d vectors
|
|
||||||
local pos1 = self.object:get_pos()
|
|
||||||
pos1.y = 0
|
|
||||||
|
|
||||||
local pos2 = self.following_person:get_pos()
|
|
||||||
pos2.y = 0
|
|
||||||
|
|
||||||
local new_direction = vector_direction(pos1,pos2)
|
|
||||||
local new_yaw = minetest_dir_to_yaw(new_direction)
|
|
||||||
|
|
||||||
self.object:set_yaw(new_yaw)
|
|
||||||
self.yaw = new_yaw
|
|
||||||
end
|
|
||||||
|
|
||||||
--this is used for when mobs breed
|
|
||||||
mobs.set_yaw_while_breeding = function(self, mate)
|
|
||||||
|
|
||||||
if self.object:get_properties().automatic_face_movement_dir then
|
|
||||||
self.object:set_properties{automatic_face_movement_dir = false}
|
|
||||||
end
|
|
||||||
|
|
||||||
--turn positions into pseudo 2d vectors
|
|
||||||
local pos1 = self.object:get_pos()
|
|
||||||
pos1.y = 0
|
|
||||||
|
|
||||||
local pos2 = mate:get_pos()
|
|
||||||
pos2.y = 0
|
|
||||||
|
|
||||||
local new_direction = vector_direction(pos1,pos2)
|
|
||||||
local new_yaw = minetest_dir_to_yaw(new_direction)
|
|
||||||
|
|
||||||
self.object:set_yaw(new_yaw)
|
|
||||||
self.yaw = new_yaw
|
|
||||||
end
|
|
|
@ -1,347 +0,0 @@
|
||||||
local vector_direction = vector.direction
|
|
||||||
--local minetest_dir_to_yaw = minetest.dir_to_yaw
|
|
||||||
local vector_distance = vector.distance
|
|
||||||
local vector_multiply = vector.multiply
|
|
||||||
local math_random = math.random
|
|
||||||
|
|
||||||
--[[
|
|
||||||
_ _ _ _
|
|
||||||
| | | | | | | |
|
|
||||||
| | | | __ _ _ __ __| | | |
|
|
||||||
| | | | / _` | '_ \ / _` | | |
|
|
||||||
|_| | |___| (_| | | | | (_| | |_|
|
|
||||||
(_) \_____/\__,_|_| |_|\__,_| (_)
|
|
||||||
]]--
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--[[
|
|
||||||
_____ _ _
|
|
||||||
| ___| | | | |
|
|
||||||
| |____ ___ __ | | ___ __| | ___
|
|
||||||
| __\ \/ / '_ \| |/ _ \ / _` |/ _ \
|
|
||||||
| |___> <| |_) | | (_) | (_| | __/
|
|
||||||
\____/_/\_\ .__/|_|\___/ \__,_|\___|
|
|
||||||
| |
|
|
||||||
|_|
|
|
||||||
]]--
|
|
||||||
|
|
||||||
mobs.explode_attack_walk = function(self,dtime)
|
|
||||||
|
|
||||||
--this needs an exception
|
|
||||||
if self.attacking == nil or not self.attacking:is_player() then
|
|
||||||
self.attacking = nil
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
mobs.set_yaw_while_attacking(self)
|
|
||||||
|
|
||||||
local distance_from_attacking = vector_distance(self.object:get_pos(), self.attacking:get_pos())
|
|
||||||
|
|
||||||
--make mob walk up to player within 2 nodes distance then start exploding
|
|
||||||
if distance_from_attacking >= self.reach and
|
|
||||||
--don't allow explosion to cancel unless out of the reach boundary
|
|
||||||
not (self.explosion_animation and self.explosion_animation > 0 and distance_from_attacking <= self.defuse_reach) then
|
|
||||||
|
|
||||||
mobs.set_velocity(self, self.run_velocity)
|
|
||||||
mobs.set_mob_animation(self,"run")
|
|
||||||
|
|
||||||
mobs.reverse_explosion_animation(self,dtime)
|
|
||||||
else
|
|
||||||
mobs.set_velocity(self,0)
|
|
||||||
|
|
||||||
--this is the only way I can reference this without dumping extra data on all mobs
|
|
||||||
if not self.explosion_animation then
|
|
||||||
self.explosion_animation = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
--play ignite sound
|
|
||||||
if self.explosion_animation == 0 then
|
|
||||||
mobs.play_sound(self,"attack")
|
|
||||||
end
|
|
||||||
|
|
||||||
mobs.set_mob_animation(self,"stand")
|
|
||||||
|
|
||||||
mobs.handle_explosion_animation(self)
|
|
||||||
|
|
||||||
self.explosion_animation = self.explosion_animation + (dtime/2.5)
|
|
||||||
end
|
|
||||||
|
|
||||||
--make explosive mobs jump
|
|
||||||
--check for nodes to jump over
|
|
||||||
--explosive mobs will just ride against walls for now
|
|
||||||
local node_in_front_of = mobs.jump_check(self)
|
|
||||||
if node_in_front_of == 1 then
|
|
||||||
mobs.jump(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--do biggening explosion thing
|
|
||||||
if self.explosion_animation and self.explosion_animation > self.explosion_timer then
|
|
||||||
mcl_explosions.explode(self.object:get_pos(), self.explosion_strength,{ drop_chance = 1.0 })
|
|
||||||
self.object:remove()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--this is a small helper function to make working with explosion animations easier
|
|
||||||
mobs.reverse_explosion_animation = function(self,dtime)
|
|
||||||
--if explosion animation was greater than 0 then reverse it
|
|
||||||
if self.explosion_animation and self.explosion_animation > 0 then
|
|
||||||
self.explosion_animation = self.explosion_animation - dtime
|
|
||||||
if self.explosion_animation < 0 then
|
|
||||||
self.explosion_animation = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
mobs.handle_explosion_animation(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--[[
|
|
||||||
______ _
|
|
||||||
| ___ \ | |
|
|
||||||
| |_/ / _ _ __ ___| |__
|
|
||||||
| __/ | | | '_ \ / __| '_ \
|
|
||||||
| | | |_| | | | | (__| | | |
|
|
||||||
\_| \__,_|_| |_|\___|_| |_|
|
|
||||||
]]--
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mobs.punch_attack_walk = function(self,dtime)
|
|
||||||
--this needs an exception
|
|
||||||
if self.attacking == nil or not self.attacking:is_player() then
|
|
||||||
self.attacking = nil
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local distance_from_attacking = mobs.get_2d_distance(self.object:get_pos(), self.attacking:get_pos())
|
|
||||||
|
|
||||||
if distance_from_attacking >= self.minimum_follow_distance then
|
|
||||||
mobs.set_velocity(self, self.run_velocity)
|
|
||||||
mobs.set_mob_animation(self, "run")
|
|
||||||
else
|
|
||||||
mobs.set_velocity(self, 0)
|
|
||||||
mobs.set_mob_animation(self, "stand")
|
|
||||||
end
|
|
||||||
|
|
||||||
mobs.set_yaw_while_attacking(self)
|
|
||||||
|
|
||||||
--make punchy mobs jump
|
|
||||||
--check for nodes to jump over
|
|
||||||
--explosive mobs will just ride against walls for now
|
|
||||||
local node_in_front_of = mobs.jump_check(self)
|
|
||||||
|
|
||||||
if node_in_front_of == 1 then
|
|
||||||
mobs.jump(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--mobs that can climb over stuff
|
|
||||||
if self.always_climb and node_in_front_of > 0 then
|
|
||||||
mobs.climb(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--auto reset punch_timer
|
|
||||||
if not self.punch_timer then
|
|
||||||
self.punch_timer = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.punch_timer > 0 then
|
|
||||||
self.punch_timer = self.punch_timer - dtime
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
mobs.punch_attack = function(self)
|
|
||||||
|
|
||||||
self.attacking:punch(self.object, 1.0, {
|
|
||||||
full_punch_interval = 1.0,
|
|
||||||
damage_groups = {fleshy = self.damage}
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
self.punch_timer = self.punch_timer_cooloff
|
|
||||||
|
|
||||||
|
|
||||||
--knockback
|
|
||||||
local pos1 = self.object:get_pos()
|
|
||||||
pos1.y = 0
|
|
||||||
local pos2 = self.attacking:get_pos()
|
|
||||||
pos2.y = 0
|
|
||||||
local dir = vector_direction(pos1,pos2)
|
|
||||||
|
|
||||||
dir = vector_multiply(dir,3)
|
|
||||||
|
|
||||||
if self.attacking:get_velocity().y <= 1 then
|
|
||||||
dir.y = 5
|
|
||||||
end
|
|
||||||
|
|
||||||
self.attacking:add_velocity(dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--[[
|
|
||||||
______ _ _ _ _
|
|
||||||
| ___ \ (_) | | (_) |
|
|
||||||
| |_/ / __ ___ _ ___ ___| |_ _| | ___
|
|
||||||
| __/ '__/ _ \| |/ _ \/ __| __| | |/ _ \
|
|
||||||
| | | | | (_) | | __/ (__| |_| | | __/
|
|
||||||
\_| |_| \___/| |\___|\___|\__|_|_|\___|
|
|
||||||
_/ |
|
|
||||||
|__/
|
|
||||||
]]--
|
|
||||||
|
|
||||||
|
|
||||||
mobs.projectile_attack_walk = function(self,dtime)
|
|
||||||
|
|
||||||
--this needs an exception
|
|
||||||
if self.attacking == nil or not self.attacking:is_player() then
|
|
||||||
self.attacking = nil
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
mobs.set_yaw_while_attacking(self)
|
|
||||||
|
|
||||||
local distance_from_attacking = vector_distance(self.object:get_pos(), self.attacking:get_pos())
|
|
||||||
|
|
||||||
|
|
||||||
if distance_from_attacking >= self.reach then
|
|
||||||
mobs.set_velocity(self, self.run_velocity)
|
|
||||||
mobs.set_mob_animation(self,"run")
|
|
||||||
else
|
|
||||||
mobs.set_velocity(self,0)
|
|
||||||
mobs.set_mob_animation(self,"stand")
|
|
||||||
end
|
|
||||||
|
|
||||||
--do this to not load data into other mobs
|
|
||||||
if not self.projectile_timer then
|
|
||||||
self.projectile_timer = math_random(self.projectile_cooldown_min, self.projectile_cooldown_max)
|
|
||||||
end
|
|
||||||
|
|
||||||
--run projectile timer
|
|
||||||
if self.projectile_timer > 0 then
|
|
||||||
self.projectile_timer = self.projectile_timer - dtime
|
|
||||||
|
|
||||||
--shoot
|
|
||||||
if self.projectile_timer <= 0 then
|
|
||||||
--reset timer
|
|
||||||
self.projectile_timer = math_random(self.projectile_cooldown_min, self.projectile_cooldown_max)
|
|
||||||
mobs.shoot_projectile(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--make shooty mobs jump
|
|
||||||
--check for nodes to jump over
|
|
||||||
--explosive mobs will just ride against walls for now
|
|
||||||
local node_in_front_of = mobs.jump_check(self)
|
|
||||||
if node_in_front_of == 1 then
|
|
||||||
mobs.jump(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--[[
|
|
||||||
_ ______ _ _
|
|
||||||
| | | ___| | | |
|
|
||||||
| | | |_ | |_ _ | |
|
|
||||||
| | | _| | | | | | | |
|
|
||||||
|_| | | | | |_| | |_|
|
|
||||||
(_) \_| |_|\__, | (_)
|
|
||||||
__/ |
|
|
||||||
|___/
|
|
||||||
]]--
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--[[
|
|
||||||
______ _ _ _ _
|
|
||||||
| ___ \ (_) | | (_) |
|
|
||||||
| |_/ / __ ___ _ ___ ___| |_ _| | ___
|
|
||||||
| __/ '__/ _ \| |/ _ \/ __| __| | |/ _ \
|
|
||||||
| | | | | (_) | | __/ (__| |_| | | __/
|
|
||||||
\_| |_| \___/| |\___|\___|\__|_|_|\___|
|
|
||||||
_/ |
|
|
||||||
|__/
|
|
||||||
]]--
|
|
||||||
|
|
||||||
local random_pitch_multiplier = {-1,1}
|
|
||||||
|
|
||||||
mobs.projectile_attack_fly = function(self, dtime)
|
|
||||||
--this needs an exception
|
|
||||||
if self.attacking == nil or not self.attacking:is_player() then
|
|
||||||
self.attacking = nil
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
--this is specifically for random ghast movement
|
|
||||||
if self.fly_random_while_attack then
|
|
||||||
|
|
||||||
--enable rotation locking
|
|
||||||
mobs.movement_rotation_lock(self)
|
|
||||||
|
|
||||||
self.walk_timer = self.walk_timer - dtime
|
|
||||||
|
|
||||||
--reset the walk timer
|
|
||||||
if self.walk_timer <= 0 then
|
|
||||||
--re-randomize the walk timer
|
|
||||||
self.walk_timer = math.random(1,6) + math.random()
|
|
||||||
--set the mob into a random direction
|
|
||||||
self.yaw = (math_random() * (math.pi * 2))
|
|
||||||
--create a truly random pitch, since there is no easy access to pitch math that I can find
|
|
||||||
self.pitch = math_random() * math.random(1,3) * random_pitch_multiplier[math_random(1,2)]
|
|
||||||
end
|
|
||||||
|
|
||||||
mobs.set_fly_velocity(self, self.run_velocity)
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
mobs.set_yaw_while_attacking(self)
|
|
||||||
|
|
||||||
local distance_from_attacking = vector_distance(self.object:get_pos(), self.attacking:get_pos())
|
|
||||||
|
|
||||||
if distance_from_attacking >= self.reach then
|
|
||||||
mobs.set_pitch_while_attacking(self)
|
|
||||||
mobs.set_fly_velocity(self, self.run_velocity)
|
|
||||||
mobs.set_mob_animation(self,"run")
|
|
||||||
else
|
|
||||||
mobs.set_pitch_while_attacking(self)
|
|
||||||
mobs.set_fly_velocity(self, 0)
|
|
||||||
mobs.set_mob_animation(self,"stand")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--do this to not load data into other mobs
|
|
||||||
if not self.projectile_timer then
|
|
||||||
self.projectile_timer = math_random(self.projectile_cooldown_min, self.projectile_cooldown_max)
|
|
||||||
end
|
|
||||||
|
|
||||||
--run projectile timer
|
|
||||||
if self.projectile_timer > 0 then
|
|
||||||
self.projectile_timer = self.projectile_timer - dtime
|
|
||||||
|
|
||||||
--shoot
|
|
||||||
if self.projectile_timer <= 0 then
|
|
||||||
|
|
||||||
if self.fly_random_while_attack then
|
|
||||||
mobs.set_yaw_while_attacking(self)
|
|
||||||
self.walk_timer = 0
|
|
||||||
end
|
|
||||||
--reset timer
|
|
||||||
self.projectile_timer = math_random(self.projectile_cooldown_min, self.projectile_cooldown_max)
|
|
||||||
mobs.shoot_projectile(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,179 +0,0 @@
|
||||||
local minetest_get_objects_inside_radius = minetest.get_objects_inside_radius
|
|
||||||
|
|
||||||
local vector = vector
|
|
||||||
|
|
||||||
--check to see if someone nearby has some tasty food
|
|
||||||
mobs.check_following = function(self) -- returns true or false
|
|
||||||
--ignore
|
|
||||||
if not self.follow then
|
|
||||||
self.following_person = nil
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--hey look, this thing works for passive mobs too!
|
|
||||||
local follower = mobs.detect_closest_player_within_radius(self,true,self.view_range,self.eye_height)
|
|
||||||
|
|
||||||
--check if the follower is a player incase they log out
|
|
||||||
if follower and follower:is_player() then
|
|
||||||
local stack = follower:get_wielded_item()
|
|
||||||
--safety check
|
|
||||||
if not stack then
|
|
||||||
self.following_person = nil
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local item_name = stack:get_name()
|
|
||||||
--all checks have passed, that guy has some good looking food
|
|
||||||
if item_name == self.follow then
|
|
||||||
self.following_person = follower
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--everything failed
|
|
||||||
self.following_person = nil
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--a function which attempts to make mobs enter
|
|
||||||
--the breeding state
|
|
||||||
mobs.enter_breed_state = function(self,clicker)
|
|
||||||
|
|
||||||
--do not breed if baby
|
|
||||||
if self.baby then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--do not do anything if looking for mate or
|
|
||||||
--if cooling off from breeding
|
|
||||||
if self.breed_lookout_timer > 0 or self.breed_timer > 0 then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--if this is caught, that means something has gone
|
|
||||||
--seriously wrong
|
|
||||||
if not clicker or not clicker:is_player() then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local stack = clicker:get_wielded_item()
|
|
||||||
--safety check
|
|
||||||
if not stack then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local item_name = stack:get_name()
|
|
||||||
--all checks have passed, that guy has some good looking food
|
|
||||||
if item_name == self.follow then
|
|
||||||
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
|
||||||
stack:take_item()
|
|
||||||
clicker:set_wielded_item(stack)
|
|
||||||
end
|
|
||||||
self.breed_lookout_timer = self.breed_lookout_timer_goal
|
|
||||||
self.bred = true
|
|
||||||
mobs.play_sound_specific(self,"mobs_mc_animal_eat_generic")
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
--everything failed
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--find the closest mate in the area
|
|
||||||
mobs.look_for_mate = function(self)
|
|
||||||
|
|
||||||
local pos1 = self.object:get_pos()
|
|
||||||
pos1.y = pos1.y + self.eye_height
|
|
||||||
|
|
||||||
local mates_in_area = {}
|
|
||||||
local winner_mate = nil
|
|
||||||
local mates_detected = 0
|
|
||||||
local radius = self.view_range
|
|
||||||
|
|
||||||
--get mates in radius
|
|
||||||
for _,mate in pairs(minetest_get_objects_inside_radius(pos1, radius)) do
|
|
||||||
|
|
||||||
--look for a breeding mate
|
|
||||||
if mate and mate:get_luaentity()
|
|
||||||
and mate:get_luaentity()._cmi_is_mob
|
|
||||||
and mate:get_luaentity().name == self.name
|
|
||||||
and mate:get_luaentity().breed_lookout_timer > 0
|
|
||||||
and mate:get_luaentity() ~= self then
|
|
||||||
|
|
||||||
local pos2 = mate:get_pos()
|
|
||||||
|
|
||||||
local distance = vector.distance(pos1,pos2)
|
|
||||||
|
|
||||||
if distance <= radius then
|
|
||||||
if minetest.line_of_sight then
|
|
||||||
--must add eye height or stuff breaks randomly because of
|
|
||||||
--seethrough nodes being a blocker (like grass)
|
|
||||||
if minetest.line_of_sight(
|
|
||||||
vector.new(pos1.x, pos1.y, pos1.z),
|
|
||||||
vector.new(pos2.x, pos2.y + mate:get_properties().eye_height, pos2.z)
|
|
||||||
) then
|
|
||||||
mates_detected = mates_detected + 1
|
|
||||||
mates_in_area[mate] = distance
|
|
||||||
end
|
|
||||||
else
|
|
||||||
mates_detected = mates_detected + 1
|
|
||||||
mates_in_area[mate] = distance
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--return if there's no one near by
|
|
||||||
if mates_detected <= 0 then --handle negative numbers for some crazy error that could possibly happen
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
--do a default radius max
|
|
||||||
local shortest_distance = radius + 1
|
|
||||||
|
|
||||||
--sort through mates and find the closest mate
|
|
||||||
for mate,distance in pairs(mates_in_area) do
|
|
||||||
if distance < shortest_distance then
|
|
||||||
shortest_distance = distance
|
|
||||||
winner_mate = mate
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return winner_mate
|
|
||||||
end
|
|
||||||
|
|
||||||
--make the baby grow up
|
|
||||||
mobs.baby_grow_up = function(self)
|
|
||||||
self.baby = nil
|
|
||||||
self.visual_size = self.backup_visual_size
|
|
||||||
self.collisionbox = self.backup_collisionbox
|
|
||||||
self.selectionbox = self.backup_selectionbox
|
|
||||||
self.object:set_properties(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--makes the baby grow up faster with diminishing returns
|
|
||||||
mobs.make_baby_grow_faster = function(self,clicker)
|
|
||||||
if clicker and clicker:is_player() then
|
|
||||||
local stack = clicker:get_wielded_item()
|
|
||||||
--safety check
|
|
||||||
if not stack then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local item_name = stack:get_name()
|
|
||||||
--all checks have passed, that guy has some good looking food
|
|
||||||
if item_name == self.follow then
|
|
||||||
self.grow_up_timer = self.grow_up_timer - (self.grow_up_timer * 0.10) --take 10 percent off - diminishing returns
|
|
||||||
|
|
||||||
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
|
||||||
stack:take_item()
|
|
||||||
clicker:set_wielded_item(stack)
|
|
||||||
end
|
|
||||||
|
|
||||||
mobs.play_sound_specific(self,"mobs_mc_animal_eat_generic")
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
|
@ -1,135 +0,0 @@
|
||||||
local minetest_get_objects_inside_radius = minetest.get_objects_inside_radius
|
|
||||||
|
|
||||||
local math_random = math.random
|
|
||||||
local vector_multiply = vector.multiply
|
|
||||||
|
|
||||||
local vector_direction = vector.direction
|
|
||||||
|
|
||||||
local integer_test = {-1,1}
|
|
||||||
|
|
||||||
mobs.collision = function(self)
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
|
|
||||||
if not self or not self.object or not self.object:get_luaentity() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
--do collision detection from the base of the mob
|
|
||||||
local collisionbox = self.object:get_properties().collisionbox
|
|
||||||
|
|
||||||
pos.y = pos.y + collisionbox[2]
|
|
||||||
|
|
||||||
local collision_boundary = collisionbox[4]
|
|
||||||
|
|
||||||
local radius = collision_boundary
|
|
||||||
|
|
||||||
if collisionbox[5] > collision_boundary then
|
|
||||||
radius = collisionbox[5]
|
|
||||||
end
|
|
||||||
|
|
||||||
local collision_count = 0
|
|
||||||
|
|
||||||
|
|
||||||
local check_for_attack = false
|
|
||||||
|
|
||||||
if self.attack_type == "punch" and self.hostile and self.attacking then
|
|
||||||
check_for_attack = true
|
|
||||||
end
|
|
||||||
|
|
||||||
for _,object in ipairs(minetest_get_objects_inside_radius(pos, radius*1.25)) do
|
|
||||||
if object and object ~= self.object and (object:is_player() or (object:get_luaentity() and object:get_luaentity()._cmi_is_mob == true and object:get_luaentity().health > 0)) and
|
|
||||||
--don't collide with rider, rider don't collide with thing
|
|
||||||
(not object:get_attach() or (object:get_attach() and object:get_attach() ~= self.object)) and
|
|
||||||
(not self.object:get_attach() or (self.object:get_attach() and self.object:get_attach() ~= object)) then
|
|
||||||
--stop infinite loop
|
|
||||||
collision_count = collision_count + 1
|
|
||||||
--mob cramming
|
|
||||||
if collision_count > 30 then
|
|
||||||
self.health = -20
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
local pos2 = object:get_pos()
|
|
||||||
|
|
||||||
local object_collisionbox = object:get_properties().collisionbox
|
|
||||||
|
|
||||||
pos2.y = pos2.y + object_collisionbox[2]
|
|
||||||
|
|
||||||
local object_collision_boundary = object_collisionbox[4]
|
|
||||||
|
|
||||||
|
|
||||||
--this is checking the difference of the object collided with's possision
|
|
||||||
--if positive top of other object is inside (y axis) of current object
|
|
||||||
local y_base_diff = (pos2.y + object_collisionbox[5]) - pos.y
|
|
||||||
|
|
||||||
local y_top_diff = (pos.y + collisionbox[5]) - pos2.y
|
|
||||||
|
|
||||||
|
|
||||||
local distance = vector.distance(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z))
|
|
||||||
|
|
||||||
if distance <= collision_boundary + object_collision_boundary and y_base_diff >= 0 and y_top_diff >= 0 then
|
|
||||||
|
|
||||||
local dir = vector.direction(pos,pos2)
|
|
||||||
|
|
||||||
dir.y = 0
|
|
||||||
|
|
||||||
--eliminate mob being stuck in corners
|
|
||||||
if dir.x == 0 and dir.z == 0 then
|
|
||||||
--slightly adjust mob position to prevent equal length
|
|
||||||
--corner/wall sticking
|
|
||||||
dir.x = dir.x + ((math_random()/10)*integer_test[math.random(1,2)])
|
|
||||||
dir.z = dir.z + ((math_random()/10)*integer_test[math.random(1,2)])
|
|
||||||
end
|
|
||||||
|
|
||||||
local velocity = dir
|
|
||||||
|
|
||||||
--0.5 is the max force multiplier
|
|
||||||
local force = 0.5 - (0.5 * distance / (collision_boundary + object_collision_boundary))
|
|
||||||
|
|
||||||
local vel1 = vector.multiply(velocity, -1.5)
|
|
||||||
local vel2 = vector.multiply(velocity, 1.5)
|
|
||||||
|
|
||||||
vel1 = vector.multiply(vel1, force * 10)
|
|
||||||
vel2 = vector.multiply(vel2, force)
|
|
||||||
|
|
||||||
if object:is_player() then
|
|
||||||
vel2 = vector_multiply(vel2, 2.5)
|
|
||||||
|
|
||||||
--integrate mob punching into collision detection
|
|
||||||
if check_for_attack and self.punch_timer <= 0 then
|
|
||||||
if object == self.attacking then
|
|
||||||
mobs.punch_attack(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.object:add_velocity(vel1)
|
|
||||||
object:add_velocity(vel2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--this is used for arrow collisions
|
|
||||||
mobs.arrow_hit = function(self, player)
|
|
||||||
player:punch(self.object, 1.0, {
|
|
||||||
full_punch_interval = 1.0,
|
|
||||||
damage_groups = {fleshy = self._damage}
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
|
|
||||||
--knockback
|
|
||||||
local pos1 = self.object:get_pos()
|
|
||||||
pos1.y = 0
|
|
||||||
local pos2 = player:get_pos()
|
|
||||||
pos2.y = 0
|
|
||||||
local dir = vector_direction(pos1,pos2)
|
|
||||||
|
|
||||||
dir = vector_multiply(dir,3)
|
|
||||||
|
|
||||||
if player:get_velocity().y <= 1 then
|
|
||||||
dir.y = 5
|
|
||||||
end
|
|
||||||
|
|
||||||
player:add_velocity(dir)
|
|
||||||
end
|
|
|
@ -1,161 +0,0 @@
|
||||||
local minetest_add_item = minetest.add_item
|
|
||||||
--local minetest_sound_play = minetest.sound_play
|
|
||||||
|
|
||||||
local math_pi = math.pi
|
|
||||||
local math_random = math.random
|
|
||||||
local math_floor = math.floor
|
|
||||||
local HALF_PI = math_pi / 2
|
|
||||||
|
|
||||||
local vector_new = vector.new
|
|
||||||
|
|
||||||
|
|
||||||
-- drop items
|
|
||||||
local item_drop = function(self, cooked, looting_level)
|
|
||||||
|
|
||||||
looting_level = looting_level or 0
|
|
||||||
|
|
||||||
-- no drops for child mobs (except monster)
|
|
||||||
if (self.child and self.type ~= "monster") then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local obj, item
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
|
|
||||||
self.drops = self.drops or {} -- nil check
|
|
||||||
|
|
||||||
for n = 1, #self.drops do
|
|
||||||
local dropdef = self.drops[n]
|
|
||||||
local chance = 1 / dropdef.chance
|
|
||||||
local looting_type = dropdef.looting
|
|
||||||
|
|
||||||
if looting_level > 0 then
|
|
||||||
local chance_function = dropdef.looting_chance_function
|
|
||||||
if chance_function then
|
|
||||||
chance = chance_function(looting_level)
|
|
||||||
elseif looting_type == "rare" then
|
|
||||||
chance = chance + (dropdef.looting_factor or 0.01) * looting_level
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local num = 0
|
|
||||||
local do_common_looting = (looting_level > 0 and looting_type == "common")
|
|
||||||
if math_random() < chance then
|
|
||||||
num = math_random(dropdef.min or 1, dropdef.max or 1)
|
|
||||||
elseif not dropdef.looting_ignore_chance then
|
|
||||||
do_common_looting = false
|
|
||||||
end
|
|
||||||
|
|
||||||
if do_common_looting then
|
|
||||||
num = num + math_floor(math_random(0, looting_level) + 0.5)
|
|
||||||
end
|
|
||||||
|
|
||||||
if num > 0 then
|
|
||||||
item = dropdef.name
|
|
||||||
|
|
||||||
-- cook items when true
|
|
||||||
if cooked then
|
|
||||||
|
|
||||||
local output = minetest.get_craft_result({
|
|
||||||
method = "cooking",
|
|
||||||
width = 1,
|
|
||||||
items = {item},
|
|
||||||
})
|
|
||||||
|
|
||||||
if output and output.item and not output.item:is_empty() then
|
|
||||||
item = output.item:get_name()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- add item if it exists
|
|
||||||
for x = 1, num do
|
|
||||||
obj = minetest_add_item(pos, ItemStack(item .. " " .. 1))
|
|
||||||
end
|
|
||||||
|
|
||||||
if obj and obj:get_luaentity() then
|
|
||||||
|
|
||||||
obj:set_velocity({
|
|
||||||
x = math_random(-10, 10) / 9,
|
|
||||||
y = 6,
|
|
||||||
z = math_random(-10, 10) / 9,
|
|
||||||
})
|
|
||||||
elseif obj then
|
|
||||||
obj:remove() -- item does not exist
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self.drops = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
mobs.death_logic = function(self, dtime)
|
|
||||||
|
|
||||||
--stop crashing game when object is nil
|
|
||||||
if not self or not self.object or not self.object:get_luaentity() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
self.death_animation_timer = self.death_animation_timer + dtime
|
|
||||||
|
|
||||||
--get all attached entities and sort through them
|
|
||||||
local attached_entities = self.object:get_children()
|
|
||||||
if #attached_entities > 0 then
|
|
||||||
for _,entity in pairs(attached_entities) do
|
|
||||||
--kick the player off
|
|
||||||
if entity:is_player() then
|
|
||||||
mobs.detach(entity)
|
|
||||||
--kick mobs off
|
|
||||||
--if there is scaling issues, this needs an additional check
|
|
||||||
else
|
|
||||||
entity:set_detach()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--stop mob from getting in the way of other mobs you're fighting
|
|
||||||
if self.object:get_properties().pointable then
|
|
||||||
self.object:set_properties({pointable = false})
|
|
||||||
end
|
|
||||||
|
|
||||||
--the final POOF of a mob despawning
|
|
||||||
if self.death_animation_timer >= 1.25 then
|
|
||||||
item_drop(self,false,1)
|
|
||||||
mobs.death_effect(self)
|
|
||||||
mcl_experience.throw_xp(self.object:get_pos(), math_random(self.xp_min, self.xp_max))
|
|
||||||
if self.on_die then
|
|
||||||
self.on_die(self, self.object:get_pos())
|
|
||||||
end
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
--I'm sure there's a more efficient way to do this
|
|
||||||
--but this is the easiest, easier to work with 1 variable synced
|
|
||||||
--this is also not smooth
|
|
||||||
local death_animation_roll = self.death_animation_timer * 2 -- * 2 to make it faster
|
|
||||||
if death_animation_roll > 1 then
|
|
||||||
death_animation_roll = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local rot = self.object:get_rotation() --(no pun intended)
|
|
||||||
|
|
||||||
rot.z = death_animation_roll * HALF_PI
|
|
||||||
|
|
||||||
self.object:set_rotation(rot)
|
|
||||||
|
|
||||||
mobs.set_mob_animation(self,"stand", true)
|
|
||||||
|
|
||||||
|
|
||||||
--flying and swimming mobs just fall down
|
|
||||||
if self.fly or self.swim then
|
|
||||||
if self.object:get_acceleration().y ~= -self.gravity then
|
|
||||||
self.object:set_acceleration(vector_new(0,-self.gravity,0))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--when landing allow mob to slow down and just fall if in air
|
|
||||||
if self.pause_timer <= 0 then
|
|
||||||
mobs.set_velocity(self,0)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,250 +0,0 @@
|
||||||
local minetest_line_of_sight = minetest.line_of_sight
|
|
||||||
--local minetest_dir_to_yaw = minetest.dir_to_yaw
|
|
||||||
local minetest_yaw_to_dir = minetest.yaw_to_dir
|
|
||||||
local minetest_get_node = minetest.get_node
|
|
||||||
local minetest_get_item_group = minetest.get_item_group
|
|
||||||
local minetest_get_objects_inside_radius = minetest.get_objects_inside_radius
|
|
||||||
local minetest_get_node_or_nil = minetest.get_node_or_nil
|
|
||||||
local minetest_registered_nodes = minetest.registered_nodes
|
|
||||||
local minetest_get_connected_players = minetest.get_connected_players
|
|
||||||
|
|
||||||
local vector_new = vector.new
|
|
||||||
local vector_add = vector.add
|
|
||||||
local vector_multiply = vector.multiply
|
|
||||||
local vector_distance = vector.distance
|
|
||||||
|
|
||||||
local table_copy = table.copy
|
|
||||||
|
|
||||||
local math_abs = math.abs
|
|
||||||
|
|
||||||
-- default function when mobs are blown up with TNT
|
|
||||||
--[[local function do_tnt(obj, damage)
|
|
||||||
obj.object:punch(obj.object, 1.0, {
|
|
||||||
full_punch_interval = 1.0,
|
|
||||||
damage_groups = {fleshy = damage},
|
|
||||||
}, nil)
|
|
||||||
return false, true, {}
|
|
||||||
end]]
|
|
||||||
|
|
||||||
--a fast function to be able to detect only players without using objects_in_radius
|
|
||||||
mobs.detect_closest_player_within_radius = function(self, line_of_sight, radius, object_height_adder)
|
|
||||||
local pos1 = self.object:get_pos()
|
|
||||||
local players_in_area = {}
|
|
||||||
local winner_player = nil
|
|
||||||
local players_detected = 0
|
|
||||||
|
|
||||||
--get players in radius
|
|
||||||
for _,player in pairs(minetest.get_connected_players()) do
|
|
||||||
if player and player:get_hp() > 0 then
|
|
||||||
|
|
||||||
local pos2 = player:get_pos()
|
|
||||||
|
|
||||||
local distance = vector_distance(pos1,pos2)
|
|
||||||
|
|
||||||
if distance <= radius then
|
|
||||||
if line_of_sight then
|
|
||||||
--must add eye height or stuff breaks randomly because of
|
|
||||||
--seethrough nodes being a blocker (like grass)
|
|
||||||
if minetest_line_of_sight(
|
|
||||||
vector_new(pos1.x, pos1.y + object_height_adder, pos1.z),
|
|
||||||
vector_new(pos2.x, pos2.y + player:get_properties().eye_height, pos2.z)
|
|
||||||
) then
|
|
||||||
players_detected = players_detected + 1
|
|
||||||
players_in_area[player] = distance
|
|
||||||
end
|
|
||||||
else
|
|
||||||
players_detected = players_detected + 1
|
|
||||||
players_in_area[player] = distance
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--return if there's no one near by
|
|
||||||
if players_detected <= 0 then --handle negative numbers for some crazy error that could possibly happen
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
--do a default radius max
|
|
||||||
local shortest_distance = radius + 1
|
|
||||||
|
|
||||||
--sort through players and find the closest player
|
|
||||||
for player,distance in pairs(players_in_area) do
|
|
||||||
if distance < shortest_distance then
|
|
||||||
shortest_distance = distance
|
|
||||||
winner_player = player
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return winner_player
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--check if a mob needs to jump
|
|
||||||
mobs.jump_check = function(self,dtime)
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
pos.y = pos.y + 0.1
|
|
||||||
local dir = minetest_yaw_to_dir(self.yaw)
|
|
||||||
|
|
||||||
local collisionbox = self.object:get_properties().collisionbox
|
|
||||||
local radius = collisionbox[4] + 0.5
|
|
||||||
|
|
||||||
vector_multiply(dir, radius)
|
|
||||||
|
|
||||||
--only jump if there's a node and a non-solid node above it
|
|
||||||
local test_dir = vector_add(pos,dir)
|
|
||||||
|
|
||||||
local green_flag_1 = minetest_get_item_group(minetest_get_node(test_dir).name, "solid") ~= 0
|
|
||||||
|
|
||||||
test_dir.y = test_dir.y + 1
|
|
||||||
|
|
||||||
local green_flag_2 = minetest_get_item_group(minetest_get_node(test_dir).name, "solid") == 0
|
|
||||||
|
|
||||||
if green_flag_1 and green_flag_2 then
|
|
||||||
--can jump over node
|
|
||||||
return 1
|
|
||||||
elseif green_flag_1 and not green_flag_2 then
|
|
||||||
--wall in front of mob
|
|
||||||
return 2
|
|
||||||
end
|
|
||||||
--nothing to jump over
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
-- a helper function to quickly turn neutral passive mobs hostile
|
|
||||||
local turn_hostile = function(self,detected_mob)
|
|
||||||
--drop in variables for attacking (stops crash)
|
|
||||||
detected_mob.punch_timer = 0
|
|
||||||
--set to hostile
|
|
||||||
detected_mob.hostile = true
|
|
||||||
--hostile_cooldown timer is initialized here
|
|
||||||
detected_mob.hostile_cooldown_timer = detected_mob.hostile_cooldown
|
|
||||||
--set target to the same
|
|
||||||
detected_mob.attacking = self.attacking
|
|
||||||
end
|
|
||||||
|
|
||||||
--allow hostile mobs to signal to other mobs
|
|
||||||
--to switch from neutal passive to neutral hostile
|
|
||||||
mobs.group_attack_initialization = function(self)
|
|
||||||
|
|
||||||
--get basic data
|
|
||||||
local friends_list
|
|
||||||
|
|
||||||
if self.group_attack == true then
|
|
||||||
friends_list = {self.name}
|
|
||||||
else
|
|
||||||
friends_list = table_copy(self.group_attack)
|
|
||||||
end
|
|
||||||
|
|
||||||
local objects_in_area = minetest_get_objects_inside_radius(self.object:get_pos(), self.view_range)
|
|
||||||
|
|
||||||
--get the player's name
|
|
||||||
local name = self.attacking:get_player_name()
|
|
||||||
|
|
||||||
--re-use local variable
|
|
||||||
local detected_mob
|
|
||||||
|
|
||||||
--run through mobs in viewing distance
|
|
||||||
for _,object in pairs(objects_in_area) do
|
|
||||||
if object and object:get_luaentity() then
|
|
||||||
detected_mob = object:get_luaentity()
|
|
||||||
-- only alert members of same mob or friends
|
|
||||||
if detected_mob._cmi_is_mob and detected_mob.state ~= "attack" and detected_mob.owner ~= name then
|
|
||||||
if detected_mob.name == self.name then
|
|
||||||
turn_hostile(self,detected_mob)
|
|
||||||
else
|
|
||||||
for _,id in pairs(friends_list) do
|
|
||||||
if detected_mob.name == id then
|
|
||||||
turn_hostile(self,detected_mob)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--THIS NEEDS TO BE RE-IMPLEMENTED AS A GLOBAL HIT IN MOB_PUNCH!!
|
|
||||||
-- have owned mobs attack player threat
|
|
||||||
--if obj.owner == name and obj.owner_loyal then
|
|
||||||
-- do_attack(obj, self.object)
|
|
||||||
--end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check if within physical map limits (-30911 to 30927)
|
|
||||||
-- within_limits, wmin, wmax = nil, -30913, 30928
|
|
||||||
mobs.within_limits = function(pos, radius)
|
|
||||||
local wmin, wmax
|
|
||||||
if mcl_vars then
|
|
||||||
if mcl_vars.mapgen_edge_min and mcl_vars.mapgen_edge_max then
|
|
||||||
wmin, wmax = mcl_vars.mapgen_edge_min, mcl_vars.mapgen_edge_max
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return pos
|
|
||||||
and (pos.x - radius) > wmin and (pos.x + radius) < wmax
|
|
||||||
and (pos.y - radius) > wmin and (pos.y + radius) < wmax
|
|
||||||
and (pos.z - radius) > wmin and (pos.z + radius) < wmax
|
|
||||||
end
|
|
||||||
|
|
||||||
-- get node but use fallback for nil or unknown
|
|
||||||
mobs.node_ok = function(pos, fallback)
|
|
||||||
|
|
||||||
fallback = fallback or mobs.fallback_node
|
|
||||||
|
|
||||||
local node = minetest_get_node_or_nil(pos)
|
|
||||||
|
|
||||||
if node and minetest_registered_nodes[node.name] then
|
|
||||||
return node
|
|
||||||
end
|
|
||||||
|
|
||||||
return minetest_registered_nodes[fallback]
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--a teleport functoin
|
|
||||||
mobs.teleport = function(self, target)
|
|
||||||
if self.do_teleport then
|
|
||||||
if self.do_teleport(self, target) == false then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--a function used for despawning mobs
|
|
||||||
mobs.check_for_player_within_area = function(self, radius)
|
|
||||||
local pos1 = self.object:get_pos()
|
|
||||||
--get players in radius
|
|
||||||
for _,player in pairs(minetest_get_connected_players()) do
|
|
||||||
if player and player:get_hp() > 0 then
|
|
||||||
local pos2 = player:get_pos()
|
|
||||||
local distance = vector_distance(pos1,pos2)
|
|
||||||
if distance < radius then
|
|
||||||
--found a player
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--did not find a player
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--a simple helper function for mobs following
|
|
||||||
mobs.get_2d_distance = function(pos1,pos2)
|
|
||||||
pos1.y = 0
|
|
||||||
pos2.y = 0
|
|
||||||
return vector_distance(pos1, pos2)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- fall damage onto solid ground
|
|
||||||
mobs.calculate_fall_damage = function(self)
|
|
||||||
if self.old_velocity and self.old_velocity.y < -7 and self.object:get_velocity().y == 0 then
|
|
||||||
local vel = self.object:get_velocity()
|
|
||||||
if vel then
|
|
||||||
local damage = math_abs(self.old_velocity.y + 7) * 2
|
|
||||||
self.pause_timer = 0.4
|
|
||||||
self.health = self.health - damage
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|