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,5 +0,0 @@
|
|||
# Text Editor TMP Files
|
||||
*.swp
|
||||
*.blend1
|
||||
*.blend2
|
||||
*.blend3
|
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.
|
448
CONTRIBUTING.md
|
@ -1,410 +1,96 @@
|
|||
# Contributing to MineClone2
|
||||
So you want to contribute to MineClone2?
|
||||
# Contributing to MineClone 2
|
||||
So you want to MineClone 2?
|
||||
Wow, thank you! :-)
|
||||
|
||||
MineClone2 is maintained by Nicu and Fleckenstein. If you have any
|
||||
problems or questions, contact us (See Links section below).
|
||||
But first, some things to note:
|
||||
|
||||
You can help with MineClone2's development in many different ways,
|
||||
whether you're a programmer or not.
|
||||
MineClone 2's development target is to make a free software clone of Minecraft,
|
||||
***version 1.12***, ***PC edition***, *** + Optifine features supported by the Minetest Engine ***.
|
||||
|
||||
## MineClone2's development target is to...
|
||||
- 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.
|
||||
MineClone 2 is maintained by two persons. Namely, kay27 and EliasFleckenstein. You can find us
|
||||
in the Minetest forums (forums.minetest.net), in IRC in the #minetest
|
||||
channel on irc.freenode.net. And finally, you can send e-mails to
|
||||
<eliasfleckenstein@web.de> or <kay27@bk.ru>.
|
||||
|
||||
## Links
|
||||
* [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)
|
||||
There is **no** guarantee we will accept anything from anybody.
|
||||
|
||||
## Using git
|
||||
MineClone2 is developed using the version control system
|
||||
[git](https://git-scm.com/). If you want to contribute code to the
|
||||
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.
|
||||
By sending us patches or asking us to include your changes in this game,
|
||||
you agree that they fall under the terms of the LGPLv2.1, which basically
|
||||
means they will become part of a free software.
|
||||
|
||||
## 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
|
||||
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.
|
||||
For small and medium changes:
|
||||
|
||||
### Rules about both bugs and feature requests
|
||||
* 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)
|
||||
* Fork the repository
|
||||
* Do your change in a new branch
|
||||
* Create a pull request to get your changes merged into master
|
||||
* Keep your pull request up to date by regularly merging upstream. It is
|
||||
imperative that conflicts are resolved prior to merging the pull
|
||||
request.
|
||||
* After the pull request got merged, you can delete the branch
|
||||
* Upload the repository somewhere where it can be accessed from the Internet and
|
||||
notify us
|
||||
|
||||
### Discuss first
|
||||
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.
|
||||
For small changes, sending us a patch is also good.
|
||||
|
||||
### Don't hesitate to ask for help
|
||||
We appreciate any contributing effort to MineClone2. If you are a
|
||||
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.
|
||||
For big changes: Same as above, but consider notifying us first to avoid
|
||||
duplicate work and possible tears of rejection. ;-)
|
||||
|
||||
### Maintain your own code, even if already got merged
|
||||
Sometimes, your code may cause crashes or bugs - we try to avoid such
|
||||
scenarios by testing every time before merging it, but if your merged
|
||||
work causes problems, we ask you fix the issues as soon as possible.
|
||||
For trusted people, we might give them direct commit access to this
|
||||
repository. In this case, you obviously don't need to fork, but you still
|
||||
need to show your contributions align with the project goals. We still
|
||||
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
|
||||
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:
|
||||
Contributors will be credited in `README.md`.
|
||||
|
||||
* Testing things inside of Minecraft (Attach screenshots / video footage
|
||||
of the results)
|
||||
* Looking at [Minestom](https://github.com/Minestom/Minestom) code. An open source Minecraft Server implementation
|
||||
* [Official Minecraft Wiki](https://minecraft.fandom.com/wiki/Minecraft_Wiki)
|
||||
(Include a link to the specific page you used)
|
||||
## Quality remarks
|
||||
Again: There is ***no*** guarantee we will accept anything from anybody.
|
||||
But we will gladly take in code from others when we feel it saves us work
|
||||
in the long run.
|
||||
|
||||
### Stick to our guidelines
|
||||
### Inclusion criteria
|
||||
Depending on what you add, the chances for inclusion vary:
|
||||
|
||||
#### Git Guidelines
|
||||
* We use merge rather than rebase or squash merge
|
||||
* 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)
|
||||
### High chance for inclusion
|
||||
* Gameplay features in Minecraft which are missing in MineClone 2
|
||||
|
||||
#### Code Guidelines
|
||||
* Each mod must provide `mod.conf`.
|
||||
* Mod names are snake case, and newly added mods start with `mcl_`, e.g.
|
||||
`mcl_core`, `mcl_farming`, `mcl_monster_eggs`. Keep in mind Minetest
|
||||
does not support capital letters in mod names.
|
||||
* To export functions, store them inside a global table named like the
|
||||
mod, e.g.
|
||||
### Medium chance for inclusion (discuss first)
|
||||
* Features which don't a impact on gameplay
|
||||
* GUI improvement
|
||||
* Features from pocket or console edition
|
||||
|
||||
```lua
|
||||
mcl_example = {}
|
||||
### Low chance for inclusion (discuss/optimize first)
|
||||
* 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()
|
||||
-- ...
|
||||
end
|
||||
### Instant rejection
|
||||
* Proprietary **anything**
|
||||
* 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
|
||||
the table directly, e.g.
|
||||
## Reporting bugs
|
||||
Report all bugs and missing Minecraft features here:
|
||||
|
||||
```lua
|
||||
-- bad
|
||||
function mcl_example:do_something()
|
||||
end
|
||||
<https://git.minetest.land/MineClone2/MineClone2/issues>
|
||||
|
||||
-- good
|
||||
function mcl_example.do_something()
|
||||
end
|
||||
```
|
||||
## Direct discussion
|
||||
We have an IRC channel! Join us on #mineclone2 in freenode.net.
|
||||
|
||||
* Use modern Minetest API, e.g. no usage of `minetest.env`
|
||||
* Tabs should be used for indent, spaces for alignment, e.g.
|
||||
<ircs://irc.freenode.net:6697/#mineclone2>
|
||||
|
||||
```lua
|
||||
|
||||
-- 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)
|
||||
## Creating releases
|
||||
* Launch MineClone2 to make sure it still runs
|
||||
* Update the version number in README.md
|
||||
* Use `git tag <version number>` to tag the latest commit with the
|
||||
version number
|
||||
* Push to repository (don't forget `--tags`!)
|
||||
* Update ContentDB
|
||||
(https://content.minetest.net/packages/Wuzzy/mineclone2/)
|
||||
* Update first post in forum thread
|
||||
(https://forum.minetest.net/viewtopic.php?f=50&t=16407)
|
||||
* Use `git tag <version number>` to tag the latest commit with the version number
|
||||
* Push to repo (don't forget `--tags`!)
|
||||
* Update ContentDB (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
|
||||
|
||||
### 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.
|
||||
|
|
154
CREDITS.md
|
@ -1,154 +0,0 @@
|
|||
# Credits
|
||||
|
||||
## Creator of MineClone
|
||||
* davedevils
|
||||
|
||||
## Creator of MineClone2
|
||||
* Wuzzy
|
||||
|
||||
|
||||
## Maintainers
|
||||
* Nicu
|
||||
* cora
|
||||
|
||||
## Previous Maintainers
|
||||
* Fleckenstein
|
||||
* jordan4ibanez
|
||||
|
||||
## Developers
|
||||
* bzoss
|
||||
* AFCMS
|
||||
* epCode
|
||||
* ryvnf
|
||||
* iliekprogrammar
|
||||
* MysticTempest
|
||||
* Rootyjr
|
||||
* aligator
|
||||
* Code-Sploit
|
||||
* NO11
|
||||
* kabou
|
||||
|
||||
## 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
|
||||
* 3raven
|
||||
* PrarieWind
|
||||
* Gustavo1
|
||||
* CableGuy67
|
||||
|
||||
## Mineclonia
|
||||
* erlehmann
|
||||
* Li0n
|
||||
* E
|
||||
* n_to
|
||||
|
||||
## 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
|
||||
* `glass=1`: Glass (full cubes only)
|
||||
* `rail=1`: Rail
|
||||
* `music_record`: Item is Music Disc
|
||||
* `music_record`: Music Disc (rating is track ID)
|
||||
* `tnt=1`: Block is TNT
|
||||
* `boat=1`: Boat
|
||||
* `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.
|
||||
Developed by many people. Not developed or endorsed by Mojang AB.
|
||||
|
||||
Version: 0.72.0
|
||||
Version: 0.71.0
|
||||
|
||||
### Gameplay
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
Minetest to learn more.
|
||||
|
||||
## Useful links
|
||||
The MineClone2 repository is hosted at Mesehub. To contribute or report issues, head there.
|
||||
## Project description
|
||||
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>
|
||||
* 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>
|
||||
|
||||
## 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.
|
||||
* **Target of development: Minecraft, PC Edition, version 1.12** (later known as “Java Edition”)
|
||||
* MineClone2 also includes Optifine features supported by the Minetest
|
||||
* Features of later Minecraft versions might sneak in, but they have a low priority
|
||||
* In general, Minecraft is aimed to be cloned as good as Minetest currently permits (no hacks)
|
||||
* Cloning the gameplay has highest priority
|
||||
* MineClone 2 will use different graphics and sounds, but with a similar style
|
||||
* Cloning the interface has no priority. It will only be roughly imitated
|
||||
* Limitations found in Minetest will be written down and reported in the course of development
|
||||
|
||||
## Completion status
|
||||
This game is currently in **beta** stage.
|
||||
It is playable, but not yet feature-complete.
|
||||
Backwards-compability is not entirely guaranteed, updating your world might cause small bugs.
|
||||
If you want to use the git version of MineClone2 in production, consider using the production branch.
|
||||
It is updated weekly and contains relatively stable code for servers.
|
||||
This game is currently in **alpha** stage.
|
||||
It is playable, but unfinished, many bugs are to be expected.
|
||||
Backwards-compability is *not* guaranteed, updating your world might cause small and
|
||||
big bugs (such as “missing node” errors or even crashes).
|
||||
|
||||
The following main features are available:
|
||||
|
||||
|
@ -138,7 +128,7 @@ The following main features are available:
|
|||
* Clock
|
||||
* Compass
|
||||
* Sponge
|
||||
* Slime block
|
||||
* Slime block (does not interact with redstone)
|
||||
* Small plants and saplings
|
||||
* Dyes
|
||||
* Banners
|
||||
|
@ -150,19 +140,19 @@ The following main features are available:
|
|||
* Creative inventory
|
||||
* Farming
|
||||
* Writable books
|
||||
* Commands
|
||||
* Villages
|
||||
* The End
|
||||
* A few server commands
|
||||
* And more!
|
||||
|
||||
The following features are incomplete:
|
||||
|
||||
* Generated structures (especially villages)
|
||||
* Some monsters and animals
|
||||
* Redstone-related things
|
||||
* The End
|
||||
* Special minecarts
|
||||
* 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
|
||||
* 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 sounds (various sources)
|
||||
* 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
|
||||
|
||||
* `LICENSE.txt`: The GPLv3 license text
|
||||
* `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
|
||||
* `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.
|
||||
|
|
|
@ -1,324 +0,0 @@
|
|||
# MineClone 2
|
||||
一個非官方的Minetest遊戲,遊玩方式和Minecraft類似。由davedevils從MineClone分拆。
|
||||
由許多人開發。並非由Mojang Studios開發。<!-- "Mojang AB"'s Name changed at 2020/05, main README should change too -->
|
||||
|
||||
版本:0.71.0
|
||||
|
||||
### 遊玩
|
||||
你開始在一個完全由方塊隨機生成的世界裡。你可以探索這個世界,挖掘和建造世界上幾乎所有的方塊,以創造新的結構。你可以選擇在「生存模式」中進行遊戲,在這個模式中,你必須與怪物戰鬥,飢餓求生,並在遊戲的其他各個環節中慢慢進步,如採礦、養殖、建造機器等等。
|
||||
|
||||
或者你也可以在「創造模式」中玩,在這個模式中,你可以瞬間建造大部分東西。
|
||||
#### Gameplay summary
|
||||
|
||||
* 沙盒式遊戲,沒有明確目標
|
||||
* 生存:與怪物和飢餓搏鬥
|
||||
* 挖礦來獲得礦物和寶物
|
||||
* 附魔:獲得經驗值並以附魔強化你的工具
|
||||
* 使用收集的方塊來創造偉大的建築
|
||||
* 收集鮮花(和其他染料來源),令世界多姿多彩
|
||||
* 找些種子並開始耕種
|
||||
* 尋找或合成數百個物品之一
|
||||
* 建立一個鐵路系統,並從礦車中得到樂趣
|
||||
* 用紅石電路建造複雜的機器
|
||||
* 在創造模式下,你幾乎可以免費建造任何東西,而且沒有限制。
|
||||
|
||||
## 如何開始
|
||||
### 開始生存
|
||||
* **挖樹幹**直到其破裂並收集木材
|
||||
* 將木頭**放入2×2的格子中**(你的物品欄中的「合成格子」),然後製作4塊木材。
|
||||
* 將4塊木材按2×2的形狀擺放在合成格子裡,製作成合成臺。
|
||||
* **右鍵單擊製作臺**以獲得3×3製作網格,製作更複雜的東西
|
||||
* 使用**合成指南**(書形圖標)了解所有可能的合成方式
|
||||
* **製作一個木鎬**,這樣你就可以挖石頭了。
|
||||
* 不同的工具可以打破不同種類的方塊。試試吧!
|
||||
* 繼續玩你想玩的。盡情享受吧!
|
||||
|
||||
### 耕種
|
||||
* 找到種子
|
||||
* 合成鋤頭
|
||||
* 用鋤頭右鍵點擊泥土或類似的方塊,創建農田
|
||||
* 將種子放在農田上,看著它們長出來
|
||||
* Collect plant when fully grown
|
||||
* If near water, farmland becomes wet and speeds up growth
|
||||
|
||||
### Furnace
|
||||
* Craft furnace
|
||||
* Furnace allows you to obtain more items
|
||||
* Upper slot must contain a smeltable item (example: iron ore)
|
||||
* Lower slot must contain a fuel item (example: coal)
|
||||
* See tooltips in crafting guide to learn about fuels and smeltable items
|
||||
|
||||
### Additional help
|
||||
More help about the gameplay, blocks items and much more can be found from inside
|
||||
the game. You can access the help from your inventory menu.
|
||||
|
||||
### Special items
|
||||
The following items are interesting for Creative Mode and for adventure
|
||||
map builders. They can not be obtained in-game or in the creative inventory.
|
||||
|
||||
* Barrier: `mcl_core:barrier`
|
||||
|
||||
Use the `/giveme` chat command to obtain them. See the in-game help for
|
||||
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
|
||||
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
|
||||
are officially supported.
|
||||
There is no support for running MineClone 2 in development versions of Minetest.
|
||||
|
||||
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
|
||||
Minetest to learn more.
|
||||
|
||||
## Project description
|
||||
The main goal of **MineClone 2** is to be a clone of Minecraft and to be released as free software.
|
||||
|
||||
* **開發目標:我的世界, Java版, 版本 1.12**
|
||||
* MineClone2還包括Minetest支持的Optifine功能。
|
||||
* 後期Minecraft版本的功能可能會偷偷加入,但它們的優先級較低。
|
||||
* 總的來說,Minecraft的目標是在Minetest目前允許的情況下進行克隆。
|
||||
* 克隆Minecraft是最優先的。
|
||||
* MineClone2將使用不同的圖形和聲音,但風格相似。
|
||||
* 克隆界面沒有優先權。只會被粗略地模仿。
|
||||
* 在Minetest中發現的局限性將在開發過程中被記錄和報告。
|
||||
|
||||
## 完成程度
|
||||
該遊戲目前處於**alpha**階段。
|
||||
它是可玩的,但尚未完成,預計會出現許多錯誤。
|
||||
向後兼容性是**不能保證的**,更新你的世界可能會造成大大小小的bug(比如「缺少節點」的錯誤甚至崩潰)。
|
||||
|
||||
已經實現以下功能:
|
||||
|
||||
* 工具,武器
|
||||
* 盔甲
|
||||
* 合成和熔煉系統:2×2 合成格, 合成臺 (3×3 合成格), 熔爐, 合成教學
|
||||
* 儲物箱,大型儲物箱,終界箱和界伏盒
|
||||
* 熔爐, 漏斗
|
||||
* 飢餓和飽食
|
||||
* 大多數怪物和動物
|
||||
* Minecraft 1.12中的所有礦物<!-- Minecraft 1.17 added copper, so here must mark the version is 1.12, then main README should also add this -->
|
||||
* 主世界的大部分方塊
|
||||
* 水和岩漿
|
||||
* 天氣
|
||||
* 28個生態域
|
||||
* 地獄,熾熱的維度
|
||||
* 紅石電路(部分)
|
||||
* 礦車(部分)
|
||||
* 狀態效果(部分)
|
||||
* 經驗系統
|
||||
* 附魔
|
||||
* 釀造,藥水,藥水箭(部分)
|
||||
* 船
|
||||
* 火
|
||||
* 建築方塊:樓梯、半磚、門、地板門、柵欄、柵欄門、牆。
|
||||
* 時鐘
|
||||
* 指南針
|
||||
* 海綿
|
||||
* 史萊姆方塊(不與紅石互動)
|
||||
* 小植物和樹苗
|
||||
* 染料
|
||||
* 旗幟
|
||||
* 裝飾方塊:玻璃、染色玻璃、玻璃片、鐵柵欄、陶土(和染色版本)、頭顱等
|
||||
* 物品展示框
|
||||
* 唱片機
|
||||
* 床
|
||||
* 物品欄
|
||||
* 創造模式物品欄
|
||||
* 生產
|
||||
* 書和羽毛筆
|
||||
* 一些服務器命令
|
||||
* 還有更多!
|
||||
|
||||
以下是不完整的特性:
|
||||
|
||||
* 生成結構(特別是村莊)
|
||||
* 一些怪物和動物
|
||||
* 紅石系統
|
||||
* 終界
|
||||
* 特殊的礦車
|
||||
* 一些不簡單的方塊和物品。
|
||||
|
||||
額外功能(在Minecraft 1.11中沒有)。
|
||||
|
||||
* 內置合成指南,向你展示製作和熔煉的配方
|
||||
* 遊戲中的幫助系統包含了大量關於遊戲基礎知識、方塊、物品等方面的幫助。
|
||||
* 臨時製作配方。它們的存在只是為了在你不在創造模式下時,提供一些其他無法獲得的物品。這些配方將隨著開發的進行和更多功能的出現而被移除。
|
||||
* v6地圖生成器中箱子裡的樹苗。
|
||||
* 完全可修改(得益於Minetest強大的Lua API)。
|
||||
* 新的方塊和物品:
|
||||
* 查找工具,顯示觸及事物的幫助
|
||||
* 更多的半磚和樓梯
|
||||
* 地獄磚柵欄門
|
||||
* 紅地獄磚柵欄
|
||||
* 紅地獄磚柵欄門
|
||||
|
||||
與Minecraft的技性術差異:
|
||||
|
||||
* 高度限制為31000格(遠高於Minecraft)
|
||||
* 水平世界大小約為62000×62000格(比Minecraft中的小得多,但仍然非常大)。
|
||||
* 仍然非常不完整和有問題
|
||||
* 塊、物品、敵人和其他功能缺失。
|
||||
* 一些項目的名稱略有不同,以便於區分。
|
||||
* 唱片機的音樂不同
|
||||
* 不同的材質(像素完美)
|
||||
* 不同的聲音(各種來源)
|
||||
* 不同的引擎(Minetest)
|
||||
|
||||
...最後,MineClone2是自由軟件!
|
||||
|
||||
## 錯誤報告
|
||||
請在此處報告所有錯誤和缺少的功能:
|
||||
|
||||
<https://git.minetest.land/MineClone2/MineClone2/issues>
|
||||
|
||||
## Chating with the community
|
||||
我們有Discord交流羣:
|
||||
|
||||
<https://discord.gg/84GKcxczG3>
|
||||
|
||||
|
||||
## Other readme files
|
||||
|
||||
* `LICENSE.txt`:GPLv3許可文本
|
||||
* `CONTRIBUTING.md`: 為那些想參與貢獻的人提供資訊
|
||||
* `MISSING_ENGINE_FEATURES.md`: MineClone2需要改进,Minetest中缺失的功能列表。
|
||||
* `API.md`: 關於MineClone2的API
|
||||
|
||||
## 參與者
|
||||
有這麼多人要列出(抱歉)。詳情請查看各mod目錄。本節只是粗略地介紹了本遊戲的核心作者。
|
||||
|
||||
### 程式碼
|
||||
* [Wuzzy](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=3082):大多數mod的主要程序員(已退休)
|
||||
* davedevils:MineClone 2的原型——「MineClone」的創造者
|
||||
* [ex-bart](https://github.com/ex-bart):紅石比較器
|
||||
* [Rootyjr](https://github.com/Rootyjr):釣竿和錯誤修復
|
||||
* [aligator](https://github.com/aligator):改進門
|
||||
* [ryvnf](https://github.com/ryvnf):爆炸物理
|
||||
* MysticTempest:錯誤修復
|
||||
* [bzoss](https://github.com/bzoss):狀態效果,釀造,藥水
|
||||
* kay27 <kay27@bk.ru>:經驗系統,錯誤修復和優化(當前維護者)
|
||||
* [EliasFleckenstein03](https://github.com/EliasFleckenstein03):終界水晶,附魔,燃燒的怪物/玩家,箱子的動畫和錯誤修復(當前維護者)
|
||||
* epCode:更好的玩家動畫,新徽標
|
||||
* 2mac:修復動力鐵軌的錯誤
|
||||
* 更多:待篇寫 (請查看各mod目錄)
|
||||
|
||||
#### Mod(概括)
|
||||
|
||||
* `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
|
||||
* 大多其他的Mod: Wuzzy
|
||||
|
||||
每个mod的详细參與者可以在各个mod目录中找到。
|
||||
|
||||
### 圖形
|
||||
* [XSSheep](http://www.minecraftforum.net/members/XSSheep):主要作者;Minecraft 1.11的Pixel Perfection资源包的制作者
|
||||
* [Wuzzy](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=3082):主菜單圖像和各種編輯和添加的材質包
|
||||
* [kingoscargames](https://github.com/kingoscargames):現有材質的各種編輯和添加
|
||||
* [leorockway](https://github.com/leorockway):怪物紋理的一些編輯
|
||||
* [xMrVizzy](https://minecraft.curseforge.com/members/xMrVizzy):釉陶(材質以後會被替換)
|
||||
* yutyo <tanakinci2002@gmail.com>:MineClone2標志
|
||||
* 其他:GUI圖片
|
||||
|
||||
### 翻譯
|
||||
* Wuzzy:德語
|
||||
* Rocher Laurent <rocherl@club-internet.fr>:法語
|
||||
* wuniversales:西班牙語
|
||||
* kay27 <kay27@bk.ru>:俄語
|
||||
* [Emoji](https://toyshost2.ddns.net):繁體中文<!-- Hi, after the translate finish, this name should add to the main README too! -->
|
||||
|
||||
### 模型
|
||||
* [22i](https://github.com/22i):所有模型的作者
|
||||
* [tobyplowy](https://github.com/tobyplowy):對上述模型進行UV映射修復
|
||||
|
||||
### 聲音和音樂
|
||||
多種來源。 有關詳細信息,請參見相應的mod目錄。
|
||||
|
||||
### 特殊感謝
|
||||
|
||||
* Wuzzy,感謝他啟動和維護MineClone2多年。
|
||||
* celeron55,創建Minetest。
|
||||
* Minetest的社區提供了大量的mods選擇,其中一些最終被納入MineClone 2。
|
||||
* Jordach,為《Big Freaking Dig》的唱片機音樂合輯而來
|
||||
* 花了太多時間為Minecraft Wiki寫作的工作狂。它是創建這個遊戲的寶貴資源。
|
||||
* Notch和Jeb是Minecraft背后的主要力量
|
||||
* XSSheep用於創建Pixel Perfection資源包。
|
||||
* [22i](https://github.com/22i) 提供出色的模型和支持
|
||||
* [maikerumine](http://github.com/maikerumine) 揭開生物和生物群落的序幕
|
||||
|
||||
## 給程序員的信息
|
||||
你可以在「API.md」中找到有趣和有用的信息。
|
||||
|
||||
## 法律信息
|
||||
這是一款粉絲開發的遊戲,並非由Mojang AB開發或認可。
|
||||
|
||||
複製是一種愛的行為。請複制和分享! <3
|
||||
下面是詳細的法律條文,有需要的朋友可以參考。
|
||||
|
||||
### 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.
|
||||
```
|
BIN
menu/Logo.blend
BIN
menu/icon.png
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 1.5 MiB |
Before Width: | Height: | Size: 830 KiB |
|
@ -83,7 +83,7 @@ local function get_hardness_values_for_groups()
|
|||
|
||||
for _, ndef in pairs(minetest.registered_nodes) 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
|
||||
end
|
||||
end
|
||||
|
@ -121,7 +121,7 @@ local hardness_values = get_hardness_values_for_groups()
|
|||
-- hardness_value. Used for quick lookup.
|
||||
local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
|
||||
|
||||
--[[local function compute_creativetimes(group)
|
||||
local function compute_creativetimes(group)
|
||||
local creativetimes = {}
|
||||
|
||||
for index, hardness in pairs(hardness_values[group]) do
|
||||
|
@ -129,7 +129,7 @@ local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
|
|||
end
|
||||
|
||||
return creativetimes
|
||||
end]]
|
||||
end
|
||||
|
||||
-- Get the list of digging times for using a specific tool on a specific
|
||||
-- diggroup.
|
||||
|
@ -207,10 +207,6 @@ end
|
|||
function mcl_autogroup.can_harvest(nodename, toolname)
|
||||
local ndef = minetest.registered_nodes[nodename]
|
||||
|
||||
if not ndef then
|
||||
return false
|
||||
end
|
||||
|
||||
if minetest.get_item_group(nodename, "dig_immediate") >= 2 then
|
||||
return true
|
||||
end
|
||||
|
@ -243,13 +239,13 @@ function mcl_autogroup.can_harvest(nodename, toolname)
|
|||
end
|
||||
|
||||
-- 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 {
|
||||
times = get_digtimes(group, can_harvest, multiplier, efficiency),
|
||||
uses = uses,
|
||||
maxlevel = 0,
|
||||
}
|
||||
end]]
|
||||
end
|
||||
|
||||
-- Returns the tool_capabilities from a tool definition or a default set of
|
||||
-- tool_capabilities
|
||||
|
@ -275,7 +271,7 @@ end
|
|||
-- toolname - Name of the tool being enchanted (like "mcl_tools:diamond_pickaxe")
|
||||
-- efficiency - The efficiency level the tool is enchanted with (default 0)
|
||||
--
|
||||
-- NOTE:
|
||||
-- NOTE:
|
||||
-- This function can only be called after mod initialization. Otherwise a mod
|
||||
-- would have to add _mcl_autogroup as a dependency which would break the mod
|
||||
-- loading order.
|
||||
|
@ -292,7 +288,7 @@ end
|
|||
-- toolname - Name of the tool used
|
||||
-- diggroup - The name of the diggroup the tool is used on
|
||||
--
|
||||
-- NOTE:
|
||||
-- NOTE:
|
||||
-- This function can only be called after mod initialization. Otherwise a mod
|
||||
-- would have to add _mcl_autogroup as a dependency which would break the mod
|
||||
-- loading order.
|
||||
|
@ -302,7 +298,7 @@ function mcl_autogroup.get_wear(toolname, diggroup)
|
|||
return math.ceil(65535 / uses)
|
||||
end
|
||||
|
||||
local function overwrite()
|
||||
local overwrite = function()
|
||||
for nname, ndef in pairs(minetest.registered_nodes) do
|
||||
local newgroups = table.copy(ndef.groups)
|
||||
if (nname ~= "ignore" and ndef.diggable) then
|
||||
|
@ -319,12 +315,12 @@ local function overwrite()
|
|||
newgroups.opaque = 1
|
||||
end
|
||||
|
||||
--local creative_breakable = false
|
||||
local creative_breakable = false
|
||||
|
||||
-- Assign groups used for digging this node depending on
|
||||
-- the registered digging groups
|
||||
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]
|
||||
if ndef.groups[g] then
|
||||
if gdef.levels then
|
||||
|
|
|
@ -81,11 +81,11 @@ if v6_use_snow_biomes then
|
|||
end
|
||||
local v6_freq_desert = tonumber(minetest.get_mapgen_setting("mgv6_freq_desert") or 0.45)
|
||||
|
||||
--local NOISE_MAGIC_X = 1619
|
||||
--local NOISE_MAGIC_Y = 31337
|
||||
--local NOISE_MAGIC_Z = 52591
|
||||
--local NOISE_MAGIC_SEED = 1013
|
||||
local function noise2d(x, y, seed)
|
||||
local NOISE_MAGIC_X = 1619
|
||||
local NOISE_MAGIC_Y = 31337
|
||||
local NOISE_MAGIC_Z = 52591
|
||||
local NOISE_MAGIC_SEED = 1013
|
||||
local noise2d = function(x, y, seed)
|
||||
-- TODO: implement noise2d function for biome blend
|
||||
return 0
|
||||
--[[
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
local get_connected_players = minetest.get_connected_players
|
||||
local clock = os.clock
|
||||
|
||||
local pairs = pairs
|
||||
|
||||
controls = {}
|
||||
controls.players = {}
|
||||
|
||||
|
@ -22,15 +20,15 @@ function controls.register_on_hold(func)
|
|||
end
|
||||
|
||||
local known_controls = {
|
||||
jump = true,
|
||||
right = true,
|
||||
left = true,
|
||||
LMB = true,
|
||||
RMB = true,
|
||||
sneak = true,
|
||||
aux1 = true,
|
||||
down = true,
|
||||
up = true,
|
||||
jump=true,
|
||||
right=true,
|
||||
left=true,
|
||||
LMB=true,
|
||||
RMB=true,
|
||||
sneak=true,
|
||||
aux1=true,
|
||||
down=true,
|
||||
up=true,
|
||||
}
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
|
@ -51,27 +49,27 @@ minetest.register_globalstep(function(dtime)
|
|||
local player_name = player:get_player_name()
|
||||
local player_controls = player:get_player_control()
|
||||
if controls.players[player_name] then
|
||||
for cname, cbool in pairs(player_controls) do
|
||||
if known_controls[cname] == true then
|
||||
--Press a key
|
||||
if cbool == true and controls.players[player_name][cname][1] == false then
|
||||
for _, func in pairs(controls.registered_on_press) do
|
||||
func(player, cname)
|
||||
end
|
||||
controls.players[player_name][cname] = {true, clock()}
|
||||
elseif cbool == true and controls.players[player_name][cname][1] == true then
|
||||
for _, func in pairs(controls.registered_on_hold) do
|
||||
func(player, cname, clock()-controls.players[player_name][cname][2])
|
||||
end
|
||||
--Release a key
|
||||
elseif cbool == false and controls.players[player_name][cname][1] == true then
|
||||
for _, func in pairs(controls.registered_on_release) do
|
||||
func(player, cname, clock()-controls.players[player_name][cname][2])
|
||||
end
|
||||
controls.players[player_name][cname] = {false}
|
||||
end
|
||||
for cname, cbool in pairs(player_controls) do
|
||||
if known_controls[cname] == true then
|
||||
--Press a key
|
||||
if cbool==true and controls.players[player_name][cname][1]==false then
|
||||
for _, func in pairs(controls.registered_on_press) do
|
||||
func(player, cname)
|
||||
end
|
||||
controls.players[player_name][cname] = {true, clock()}
|
||||
elseif cbool==true and controls.players[player_name][cname][1]==true then
|
||||
for _, func in pairs(controls.registered_on_hold) do
|
||||
func(player, cname, clock()-controls.players[player_name][cname][2])
|
||||
end
|
||||
--Release a key
|
||||
elseif cbool==false and controls.players[player_name][cname][1]==true then
|
||||
for _, func in pairs(controls.registered_on_release) do
|
||||
func(player, cname, clock()-controls.players[player_name][cname][2])
|
||||
end
|
||||
controls.players[player_name][cname] = {false}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
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 = {}
|
||||
|
||||
--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 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]}
|
||||
--(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 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
|
||||
|
||||
local function is_touching(realpos,nodepos,radius)
|
||||
local is_touching = function(realpos,nodepos,radius)
|
||||
local boarder = 0.5 - radius
|
||||
return math.abs(realpos - nodepos) > (boarder)
|
||||
return (math.abs(realpos - nodepos) > (boarder))
|
||||
end
|
||||
|
||||
flowlib.is_touching = is_touching
|
||||
|
||||
local function is_water(pos)
|
||||
return get_item_group(get_node(pos).name, "water") ~= 0
|
||||
local is_water = function(pos)
|
||||
return (minetest.get_item_group(minetest.get_node(
|
||||
{x=pos.x,y=pos.y,z=pos.z}).name
|
||||
, "water") ~= 0)
|
||||
end
|
||||
|
||||
flowlib.is_water = is_water
|
||||
|
||||
local function node_is_water(node)
|
||||
return get_item_group(node.name, "water") ~= 0
|
||||
local node_is_water = function(node)
|
||||
return (minetest.get_item_group(node.name, "water") ~= 0)
|
||||
end
|
||||
|
||||
flowlib.node_is_water = node_is_water
|
||||
|
||||
local function is_lava(pos)
|
||||
return get_item_group(get_node(pos).name, "lava") ~= 0
|
||||
local is_lava = function(pos)
|
||||
return (minetest.get_item_group(minetest.get_node(
|
||||
{x=pos.x,y=pos.y,z=pos.z}).name
|
||||
, "lava") ~= 0)
|
||||
end
|
||||
|
||||
flowlib.is_lava = is_lava
|
||||
|
||||
local function node_is_lava(node)
|
||||
return get_item_group(node.name, "lava") ~= 0
|
||||
local node_is_lava = function(node)
|
||||
return (minetest.get_item_group(node.name, "lava") ~= 0)
|
||||
end
|
||||
|
||||
flowlib.node_is_lava = node_is_lava
|
||||
|
||||
|
||||
local function is_liquid(pos)
|
||||
return get_item_group(get_node(pos).name, "liquid") ~= 0
|
||||
local is_liquid = function(pos)
|
||||
return (minetest.get_item_group(minetest.get_node(
|
||||
{x=pos.x,y=pos.y,z=pos.z}).name
|
||||
, "liquid") ~= 0)
|
||||
end
|
||||
|
||||
flowlib.is_liquid = is_liquid
|
||||
|
||||
local function node_is_liquid(node)
|
||||
return minetest.get_item_group(node.name, "liquid") ~= 0
|
||||
local node_is_liquid = function(node)
|
||||
return (minetest.get_item_group(node.name, "liquid") ~= 0)
|
||||
end
|
||||
|
||||
flowlib.node_is_liquid = node_is_liquid
|
||||
|
||||
--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
|
||||
if not registered_nodes[name] then
|
||||
if not minetest.registered_nodes[name] then
|
||||
return 0
|
||||
end
|
||||
if registered_nodes[name].liquidtype == "source" then
|
||||
local node_testing = get_node(pos_testing)
|
||||
if not registered_nodes[node_testing.name] then
|
||||
if minetest.registered_nodes[name].liquidtype == "source" then
|
||||
local node_testing = minetest.get_node(pos_testing)
|
||||
local param2_testing = node_testing.param2
|
||||
if not minetest.registered_nodes[node_testing.name] then
|
||||
return 0
|
||||
end
|
||||
if registered_nodes[node_testing.name].liquidtype ~= "flowing" then
|
||||
if minetest.registered_nodes[node_testing.name].liquidtype
|
||||
~= "flowing" then
|
||||
return 0
|
||||
else
|
||||
return direction
|
||||
end
|
||||
elseif registered_nodes[name].liquidtype == "flowing" then
|
||||
local node_testing = get_node(pos_testing)
|
||||
elseif minetest.registered_nodes[name].liquidtype == "flowing" then
|
||||
local node_testing = minetest.get_node(pos_testing)
|
||||
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
|
||||
end
|
||||
if registered_nodes[node_testing.name].liquidtype == "source" then
|
||||
if minetest.registered_nodes[node_testing.name].liquidtype
|
||||
== "source" then
|
||||
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 (node.param2 - param2_testing) > 6 then
|
||||
return -direction
|
||||
|
@ -113,41 +108,48 @@ local function quick_flow_logic(node, pos_testing, direction)
|
|||
return 0
|
||||
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
|
||||
return {x = 0, y = 0, z = 0}
|
||||
return {x=0,y=0,z=0}
|
||||
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)
|
||||
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)
|
||||
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
|
||||
|
||||
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 is_touching(realpos.x, pos.x, radius) 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})
|
||||
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 = get_node({x = pos.x+1, y = pos.y, z = pos.z})
|
||||
pos = {x = pos.x+1, y = pos.y, z = pos.z}
|
||||
--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 move_centre = function(pos,realpos,node,radius)
|
||||
if is_touching(realpos.x,pos.x,radius) then
|
||||
if 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}
|
||||
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
|
||||
if is_touching(realpos.z, pos.z, radius) 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})
|
||||
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
|
||||
node = get_node({x = pos.x, y = pos.y, z = pos.z + 1})
|
||||
pos = {x = pos.x, y = pos.y, z = pos.z + 1}
|
||||
if is_touching(realpos.z,pos.z,radius) then
|
||||
if is_liquid({x=pos.x,y=pos.y,z=pos.z-1}) then
|
||||
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}
|
||||
elseif is_liquid({x=pos.x,y=pos.y,z=pos.z+1}) then
|
||||
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}
|
||||
end
|
||||
end
|
||||
return pos, node
|
||||
return pos,node
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
function minetest.check_single_for_falling(pos)
|
||||
minetest.check_single_for_falling = function(pos)
|
||||
local ret_o = original_function(pos)
|
||||
|
||||
local ret = false
|
||||
local node = minetest.get_node(pos)
|
||||
if get_item_group(node.name, "attached_node_facedir") ~= 0 then
|
||||
local dir = facedir_to_dir(node.param2)
|
||||
if minetest.get_item_group(node.name, "attached_node_facedir") ~= 0 then
|
||||
local dir = minetest.facedir_to_dir(node.param2)
|
||||
if dir then
|
||||
if get_item_group(get_node(vector.add(pos, dir)).name, "solid") == 0 then
|
||||
remove_node(pos)
|
||||
local cpos = vector.add(pos, dir)
|
||||
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, "")
|
||||
for dr=1, #drops do
|
||||
minetest.add_item(pos, drops[dr])
|
||||
|
@ -24,6 +20,7 @@ function minetest.check_single_for_falling(pos)
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ret_o or ret
|
||||
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,15 +0,0 @@
|
|||
# mcl_damage
|
||||
|
||||
This mod is intended to overall minetest's native damage system, to provide a better integration between features that deals with entities' health.
|
||||
|
||||
WARNING: Not using it inside your mods may cause strange bugs (using the native damage system may cause conflicts with this system).
|
||||
|
||||
## Callbacks
|
||||
|
||||
To modify the amount of damage made by something:
|
||||
|
||||
```lua
|
||||
--obj: an ObjectRef
|
||||
mcl_damage.register_modifier(function(obj, damage, reason)
|
||||
end, 0)
|
||||
```
|
|
@ -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 = {}
|
||||
|
||||
local mod_fire = minetest.get_modpath("mcl_fire")
|
||||
--local CONTENT_FIRE = minetest.get_content_id("mcl_fire:fire")
|
||||
local mod_death_messages = minetest.get_modpath("mcl_death_messages") ~= nil
|
||||
local mod_fire = minetest.get_modpath("mcl_fire") ~= nil
|
||||
local CONTENT_FIRE = minetest.get_content_id("mcl_fire:fire")
|
||||
|
||||
local math = math
|
||||
local vector = vector
|
||||
local table = table
|
||||
local S = minetest.get_translator("mcl_explosions")
|
||||
|
||||
local hash_node_position = minetest.hash_node_position
|
||||
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 check_for_falling = minetest.check_for_falling
|
||||
local add_item = minetest.add_item
|
||||
local pos_to_string = minetest.pos_to_string
|
||||
|
||||
-- Saved sphere explosion shapes for various radiuses
|
||||
local sphere_shapes = {}
|
||||
|
@ -69,44 +67,46 @@ local function compute_sphere_rays(radius)
|
|||
local rays = {}
|
||||
local sphere = {}
|
||||
|
||||
local function add_ray(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
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for x = -radius, radius do
|
||||
for z = -radius, radius do
|
||||
for y = -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
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for x = -radius, radius do
|
||||
for i=1, 2 do
|
||||
for y = -radius, radius do
|
||||
for z = -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
|
||||
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
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i=1,2 do
|
||||
for x = -radius, radius do
|
||||
for z = -radius, radius do
|
||||
for y = -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
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i=1,2 do
|
||||
for x = -radius, radius do
|
||||
for y = -radius, radius do
|
||||
for z = -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
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -150,8 +150,7 @@ end
|
|||
-- raydirs - The directions for each ray
|
||||
-- radius - The maximum distance each ray will go
|
||||
-- info - Table containing information about explosion
|
||||
-- direct - direct source object of the damage (optional)
|
||||
-- source - indirect source object of the damage (optional)
|
||||
-- puncher - object that punches other objects (optional)
|
||||
--
|
||||
-- Values in info:
|
||||
-- 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
|
||||
-- inlined to avoid function calls and unnecessary table creation. This was
|
||||
-- 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 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 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,
|
||||
MaxEdge = emax
|
||||
}]]
|
||||
}
|
||||
local data = vm:get_data()
|
||||
local destroy = {}
|
||||
|
||||
|
@ -210,7 +212,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
|||
npos_x - emin_x + 1
|
||||
|
||||
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
|
||||
br = max_blast_resistance
|
||||
end
|
||||
|
@ -245,7 +247,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
|||
local ent = obj:get_luaentity()
|
||||
|
||||
-- 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 collisionbox = nil
|
||||
|
||||
|
@ -258,12 +260,12 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
|||
|
||||
if collisionbox then
|
||||
-- Create rays from random points in the collision box
|
||||
local x1 = collisionbox[1]
|
||||
local y1 = collisionbox[2]
|
||||
local z1 = collisionbox[3]
|
||||
local x2 = collisionbox[4]
|
||||
local y2 = collisionbox[5]
|
||||
local z2 = collisionbox[6]
|
||||
local x1 = collisionbox[1] * 2
|
||||
local y1 = collisionbox[2] * 2
|
||||
local z1 = collisionbox[3] * 2
|
||||
local x2 = collisionbox[4] * 2
|
||||
local y2 = collisionbox[5] * 2
|
||||
local z2 = collisionbox[6] * 2
|
||||
local x_len = math.abs(x2 - x1)
|
||||
local y_len = math.abs(y2 - y1)
|
||||
local z_len = math.abs(z2 - z1)
|
||||
|
@ -319,6 +321,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
|||
impact = 0
|
||||
end
|
||||
local damage = math.floor((impact * impact + impact) * 7 * strength + 1)
|
||||
local source = puncher or obj
|
||||
|
||||
local sleep_formspec_doesnt_close_mt53 = false
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
if not obj:is_player() then
|
||||
return
|
||||
end
|
||||
|
||||
mcl_util.deal_damage(obj, damage, {type = "explosion", direct = direct, source = source})
|
||||
|
||||
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 then return end
|
||||
obj:punch(obj, 10, { damage_groups = { full_punch_interval = 1, fleshy = damage, knockback = impact * 20.0 } }, punch_dir)
|
||||
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
||||
end)
|
||||
end, obj, damage, impact, vector.new(punch_dir))
|
||||
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))
|
||||
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 remove = true
|
||||
|
||||
if do_drop or on_blast then
|
||||
if do_drop or on_blast ~= nil then
|
||||
local npos = get_position_from_hash(hash)
|
||||
if on_blast then
|
||||
if on_blast ~= nil then
|
||||
on_blast(npos, 1.0, do_drop)
|
||||
remove = false
|
||||
else
|
||||
|
@ -405,7 +412,8 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
|||
end
|
||||
|
||||
-- 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
|
||||
|
||||
-- Create an explosion with strength at pos.
|
||||
|
@ -414,8 +422,7 @@ end
|
|||
-- pos - The position where the explosion originates from
|
||||
-- strength - The blast strength of the explosion (a TNT explosion uses 4)
|
||||
-- info - Table containing information about explosion
|
||||
-- direct - direct source object of the damage (optional)
|
||||
-- source - indirect source object of the damage (optional)
|
||||
-- puncher - object that is reported as source of punches/damage (optional)
|
||||
--
|
||||
-- Values in info:
|
||||
-- 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)
|
||||
-- grief_protected - If true, the explosion will also destroy nodes which have
|
||||
-- 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
|
||||
info = {}
|
||||
end
|
||||
|
@ -458,7 +465,7 @@ function mcl_explosions.explode(pos, strength, info, direct, source)
|
|||
info.drop_chance = 0
|
||||
end
|
||||
|
||||
trace_explode(pos, strength, shape, radius, info, direct, source)
|
||||
trace_explode(pos, strength, shape, radius, info, puncher)
|
||||
|
||||
if info.particles then
|
||||
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 не удалось пережить взрыва.
|
|
@ -1,2 +0,0 @@
|
|||
# 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
|
||||
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.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)
|
||||
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
|
||||
|
|
|
@ -40,9 +40,10 @@ function mcl_loot.get_loot(loot_definitions, pr)
|
|||
total_weight = total_weight + (loot_definitions.items[i].weight or 1)
|
||||
end
|
||||
|
||||
--local stacks_min = loot_definitions.stacks_min or 1
|
||||
--local stacks_max = loot_definitions.stacks_max or 1
|
||||
|
||||
local stacks_min = loot_definitions.stacks_min
|
||||
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)
|
||||
for s=1, stacks do
|
||||
local r = pr:next(1, total_weight)
|
||||
|
@ -58,27 +59,26 @@ function mcl_loot.get_loot(loot_definitions, pr)
|
|||
end
|
||||
if item then
|
||||
local itemstring = item.itemstring
|
||||
|
||||
local itemstack = item.itemstack
|
||||
if itemstring then
|
||||
local stack = ItemStack(itemstring)
|
||||
|
||||
if item.amount_min and item.amount_max then
|
||||
stack:set_count(pr:next(item.amount_min, item.amount_max))
|
||||
itemstring = itemstring .. " " .. pr:next(item.amount_min, item.amount_max)
|
||||
end
|
||||
|
||||
if item.wear_min and item.wear_max then
|
||||
-- Sadly, PseudoRandom only allows very narrow ranges, so we set wear in steps of 10
|
||||
local wear_min = math.floor(item.wear_min / 10)
|
||||
local wear_max = math.floor(item.wear_max / 10)
|
||||
local wear = pr:next(wear_min, wear_max) * 10
|
||||
|
||||
stack:set_wear(pr:next(wear_min, wear_max) * 10)
|
||||
if not item.amount_min and not item.amount_max then
|
||||
itemstring = itemstring .. " 1"
|
||||
end
|
||||
|
||||
itemstring = itemstring .. " " .. tostring(wear)
|
||||
end
|
||||
|
||||
if item.func then
|
||||
item.func(stack, pr)
|
||||
end
|
||||
|
||||
table.insert(items, stack)
|
||||
table.insert(items, itemstring)
|
||||
elseif itemstack then
|
||||
table.insert(items, itemstack)
|
||||
else
|
||||
minetest.log("error", "[mcl_loot] INTERNAL ERROR! Failed to select random loot item!")
|
||||
end
|
||||
|
|
|
@ -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 = {}
|
||||
|
||||
-- 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
|
||||
return
|
||||
end
|
||||
local poshash = hash_node_position(pos)
|
||||
local poshash = minetest.hash_node_position(pos)
|
||||
if not poshash then
|
||||
return
|
||||
end
|
||||
local id = add_particlespawner(particlespawner_definition)
|
||||
local id = minetest.add_particlespawner(particlespawner_definition)
|
||||
if id == -1 then
|
||||
return
|
||||
end
|
||||
|
@ -56,8 +47,6 @@ function mcl_particles.add_node_particlespawner(pos, particlespawner_definition,
|
|||
return id
|
||||
end
|
||||
|
||||
local add_node_particlespawner = mcl_particles.add_node_particlespawner
|
||||
|
||||
-- Deletes all particlespawners that are assigned to a node position.
|
||||
-- If no particlespawners exist for this position, nothing happens.
|
||||
-- pos: Node positon. MUST use integer values!
|
||||
|
@ -66,66 +55,14 @@ function mcl_particles.delete_node_particlespawners(pos)
|
|||
if allowed_level == 0 then
|
||||
return false
|
||||
end
|
||||
local poshash = hash_node_position(pos)
|
||||
local poshash = minetest.hash_node_position(pos)
|
||||
local ids = particle_nodes[poshash]
|
||||
if ids then
|
||||
for i=1, #ids do
|
||||
delete_particlespawner(ids[i])
|
||||
minetest.delete_particlespawner(ids[i])
|
||||
end
|
||||
particle_nodes[poshash] = nil
|
||||
return true
|
||||
end
|
||||
return false
|
||||
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 = {}
|
||||
|
||||
-- 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
|
||||
|
||||
--[[
|
||||
|
@ -172,7 +152,7 @@ function mcl_util.get_eligible_transfer_item_slot(src_inventory, src_list, dst_i
|
|||
end
|
||||
|
||||
-- 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")
|
||||
return g == 0 or g == nil
|
||||
end
|
||||
|
@ -234,7 +214,7 @@ function mcl_util.move_item_container(source_pos, destination_pos, source_list,
|
|||
end
|
||||
|
||||
-- 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
|
||||
pos = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right")
|
||||
if not pos then
|
||||
|
@ -357,32 +337,6 @@ function mcl_util.get_first_occupied_inventory_slot(inventory, listname)
|
|||
return mcl_util.get_eligible_transfer_item_slot(inventory, listname)
|
||||
end
|
||||
|
||||
local function drop_item_stack(pos, stack)
|
||||
if not stack or stack:is_empty() then return end
|
||||
local drop_offset = vector.new(math.random() - 0.5, 0, math.random() - 0.5)
|
||||
minetest.add_item(vector.add(pos, drop_offset), stack)
|
||||
end
|
||||
|
||||
function mcl_util.drop_items_from_meta_container(listname)
|
||||
return function(pos, oldnode, oldmetadata)
|
||||
if oldmetadata and oldmetadata.inventory then
|
||||
-- process in after_dig_node callback
|
||||
local main = oldmetadata.inventory.main
|
||||
if not main then return end
|
||||
for _, stack in pairs(main) do
|
||||
drop_item_stack(pos, stack)
|
||||
end
|
||||
else
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
for i = 1, inv:get_size("main") do
|
||||
drop_item_stack(pos, inv:get_stack("main", i))
|
||||
end
|
||||
meta:from_table()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns true if item (itemstring or ItemStack) can be used as a furnace fuel.
|
||||
-- Returns false otherwise
|
||||
function mcl_util.is_fuel(item)
|
||||
|
@ -467,146 +421,4 @@ function mcl_util.get_color(colorstr)
|
|||
end
|
||||
end
|
||||
|
||||
function mcl_util.call_on_rightclick(itemstack, player, pointed_thing)
|
||||
-- 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
|
||||
|
||||
local _, groupcap = next(itemstack:get_tool_capabilities().groupcaps)
|
||||
uses = uses or (groupcap 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
|
||||
|
||||
function mcl_util.get_pointed_thing(player, liquid)
|
||||
local pos = vector.offset(player:get_pos(), 0, player:get_properties().eye_height, 0)
|
||||
local look_dir = vector.multiply(player:get_look_dir(), 5)
|
||||
local pos2 = vector.add(pos, look_dir)
|
||||
local ray = minetest.raycast(pos, pos2, false, liquid)
|
||||
|
||||
if ray then
|
||||
for pointed_thing in ray do
|
||||
return pointed_thing
|
||||
end
|
||||
end
|
||||
end
|
||||
dofile(modpath.."/entities.lua")
|
|
@ -12,7 +12,7 @@ Params:
|
|||
|
||||
* 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.
|
||||
Mainly used for ore generation.
|
||||
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
|
||||
|
||||
## 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).
|
||||
It will be called whenever a player changes between dimensions.
|
||||
The void counts as dimension.
|
||||
|
||||
* player: player, the player who changed of dimension
|
||||
* dimension: string, The new dimension of the player ("overworld", "nether", "end", "void").
|
||||
* last_dimension: string, The dimension where the player was ("overworld", "nether", "end", "void").
|
||||
* player: player, the player who changed the dimension
|
||||
* dimension: position, The new dimension of the player ("overworld", "nether", "end", "void").
|
||||
|
||||
|
||||
## mcl_worlds.registered_on_dimension_change
|
||||
Table containing all function registered with mcl_worlds.register_on_dimension_change()
|
||||
|
||||
## 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
|
||||
* dimension: string, new dimension ("overworld", "nether", "end", "void")
|
||||
* dimension: string, new dimension ("overworld", "nether", "end", "void")
|
|
@ -1,7 +1,5 @@
|
|||
mcl_worlds = {}
|
||||
|
||||
local get_connected_players = minetest.get_connected_players
|
||||
|
||||
-- For a given position, returns a 2-tuple:
|
||||
-- 1st return value: true if pos is in void
|
||||
-- 2nd return value: true if it is in the deadly part of the void
|
||||
|
@ -35,64 +33,60 @@ end
|
|||
-- If the Y coordinate is not located in any dimension, it will return:
|
||||
-- nil, "void"
|
||||
function mcl_worlds.y_to_layer(y)
|
||||
if y >= mcl_vars.mg_overworld_min then
|
||||
return y - mcl_vars.mg_overworld_min, "overworld"
|
||||
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max+128 then
|
||||
return y - mcl_vars.mg_nether_min, "nether"
|
||||
elseif y >= mcl_vars.mg_end_min and y <= mcl_vars.mg_end_max then
|
||||
return y - mcl_vars.mg_end_min, "end"
|
||||
else
|
||||
return nil, "void"
|
||||
end
|
||||
if y >= mcl_vars.mg_overworld_min then
|
||||
return y - mcl_vars.mg_overworld_min, "overworld"
|
||||
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max+128 then
|
||||
return y - mcl_vars.mg_nether_min, "nether"
|
||||
elseif y >= mcl_vars.mg_end_min and y <= mcl_vars.mg_end_max then
|
||||
return y - mcl_vars.mg_end_min, "end"
|
||||
else
|
||||
return nil, "void"
|
||||
end
|
||||
end
|
||||
|
||||
local y_to_layer = mcl_worlds.y_to_layer
|
||||
|
||||
-- Takes a pos and returns the dimension it belongs to (same as above)
|
||||
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
|
||||
end
|
||||
|
||||
local pos_to_dimension = mcl_worlds.pos_to_dimension
|
||||
|
||||
-- Takes a Minecraft layer and a “dimension” name
|
||||
-- and returns the corresponding Y coordinate for
|
||||
-- MineClone 2.
|
||||
-- mc_dimension is one of "overworld", "nether", "end" (default: "overworld").
|
||||
function mcl_worlds.layer_to_y(layer, mc_dimension)
|
||||
if mc_dimension == "overworld" or mc_dimension == nil then
|
||||
return layer + mcl_vars.mg_overworld_min
|
||||
elseif mc_dimension == "nether" then
|
||||
return layer + mcl_vars.mg_nether_min
|
||||
elseif mc_dimension == "end" then
|
||||
return layer + mcl_vars.mg_end_min
|
||||
end
|
||||
if mc_dimension == "overworld" or mc_dimension == nil then
|
||||
return layer + mcl_vars.mg_overworld_min
|
||||
elseif mc_dimension == "nether" then
|
||||
return layer + mcl_vars.mg_nether_min
|
||||
elseif mc_dimension == "end" then
|
||||
return layer + mcl_vars.mg_end_min
|
||||
end
|
||||
end
|
||||
|
||||
-- Takes a position and returns true if this position can have weather
|
||||
function mcl_worlds.has_weather(pos)
|
||||
-- Weather in the Overworld and the high part of the void below
|
||||
return pos.y <= mcl_vars.mg_overworld_max and pos.y >= mcl_vars.mg_overworld_min - 64
|
||||
-- Weather in the Overworld and the high part of the void below
|
||||
return pos.y <= mcl_vars.mg_overworld_max and pos.y >= mcl_vars.mg_overworld_min - 64
|
||||
end
|
||||
|
||||
-- Takes a position and returns true if this position can have Nether dust
|
||||
function mcl_worlds.has_dust(pos)
|
||||
-- Weather in the Overworld and the high part of the void below
|
||||
return pos.y <= mcl_vars.mg_nether_max + 138 and pos.y >= mcl_vars.mg_nether_min - 10
|
||||
-- Weather in the Overworld and the high part of the void below
|
||||
return pos.y <= mcl_vars.mg_nether_max + 138 and pos.y >= mcl_vars.mg_nether_min - 10
|
||||
end
|
||||
|
||||
-- Takes a position (pos) and returns true if compasses are working here
|
||||
function mcl_worlds.compass_works(pos)
|
||||
-- It doesn't work in Nether and the End, but it works in the Overworld and in the high part of the void below
|
||||
local _, dim = mcl_worlds.y_to_layer(pos.y)
|
||||
if dim == "nether" or dim == "end" then
|
||||
return false
|
||||
elseif dim == "void" then
|
||||
return pos.y <= mcl_vars.mg_overworld_max and pos.y >= mcl_vars.mg_overworld_min - 64
|
||||
else
|
||||
return true
|
||||
end
|
||||
-- It doesn't work in Nether and the End, but it works in the Overworld and in the high part of the void below
|
||||
local _, dim = mcl_worlds.y_to_layer(pos.y)
|
||||
if dim == "nether" or dim == "end" then
|
||||
return false
|
||||
elseif dim == "void" then
|
||||
return pos.y <= mcl_vars.mg_overworld_max and pos.y >= mcl_vars.mg_overworld_min - 64
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- Takes a position (pos) and returns true if clocks are working here
|
||||
|
@ -118,15 +112,12 @@ local last_dimension = {}
|
|||
-- * player: Player who changed the dimension
|
||||
-- * dimension: New dimension ("overworld", "nether", "end", "void")
|
||||
function mcl_worlds.dimension_change(player, dimension)
|
||||
local playername = player:get_player_name()
|
||||
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
|
||||
last_dimension[playername] = dimension
|
||||
end
|
||||
|
||||
local dimension_change = mcl_worlds.dimension_change
|
||||
|
||||
----------------------- INTERNAL STUFF ----------------------
|
||||
|
||||
-- Update the dimension callbacks every DIM_UPDATE seconds
|
||||
|
@ -134,19 +125,19 @@ local DIM_UPDATE = 1
|
|||
local dimtimer = 0
|
||||
|
||||
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)
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
-- regular updates based on iterval
|
||||
dimtimer = dimtimer + dtime;
|
||||
if dimtimer >= DIM_UPDATE then
|
||||
local players = get_connected_players()
|
||||
for p = 1, #players do
|
||||
local dim = pos_to_dimension(players[p]:get_pos())
|
||||
local players = minetest.get_connected_players()
|
||||
for p=1, #players do
|
||||
local dim = mcl_worlds.pos_to_dimension(players[p]:get_pos())
|
||||
local name = players[p]:get_player_name()
|
||||
if dim ~= last_dimension[name] then
|
||||
dimension_change(players[p], dim)
|
||||
mcl_worlds.dimension_change(players[p], dim)
|
||||
end
|
||||
end
|
||||
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, "wb"))
|
||||
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.
|
|
@ -2,49 +2,53 @@
|
|||
|
||||
local get_connected_players = minetest.get_connected_players
|
||||
local get_node = minetest.get_node
|
||||
local vector = vector
|
||||
local vector_add = vector.add
|
||||
local ceil = math.ceil
|
||||
local pairs = pairs
|
||||
|
||||
walkover = {}
|
||||
walkover.registered_globals = {}
|
||||
|
||||
function walkover.register_global(func)
|
||||
table.insert(walkover.registered_globals, func)
|
||||
end
|
||||
|
||||
local on_walk = {}
|
||||
local registered_globals = {}
|
||||
|
||||
walkover.registered_globals = registered_globals
|
||||
|
||||
function walkover.register_global(func)
|
||||
table.insert(registered_globals, func)
|
||||
end
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
for name,def in pairs(minetest.registered_nodes) do
|
||||
if def.on_walk_over then
|
||||
on_walk[name] = def.on_walk_over
|
||||
end
|
||||
end
|
||||
for _,func in ipairs(walkover.registered_globals) do --cache registered globals
|
||||
table.insert(registered_globals, func)
|
||||
end
|
||||
end)
|
||||
|
||||
local timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
timer = timer + dtime
|
||||
timer = timer + dtime;
|
||||
if timer >= 0.3 then
|
||||
for _, player in pairs(get_connected_players()) do
|
||||
local ppos = player:get_pos()
|
||||
ppos.y = ceil(ppos.y)
|
||||
local npos = vector.add(ppos, vector.new(0, -1, 0))
|
||||
if npos then
|
||||
local node = get_node(npos)
|
||||
if node then
|
||||
if on_walk[node.name] then
|
||||
on_walk[node.name](npos, node, player)
|
||||
end
|
||||
for i = 1, #registered_globals do
|
||||
registered_globals[i](npos, node, player)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for _,player in pairs(get_connected_players()) do
|
||||
local pp = player:get_pos()
|
||||
pp.y = ceil(pp.y)
|
||||
local loc = vector_add(pp, {x=0,y=-1,z=0})
|
||||
if loc ~= nil then
|
||||
|
||||
local nodeiamon = get_node(loc)
|
||||
|
||||
if nodeiamon ~= nil then
|
||||
if on_walk[nodeiamon.name] then
|
||||
on_walk[nodeiamon.name](loc, nodeiamon, player)
|
||||
end
|
||||
for i = 1, #registered_globals do
|
||||
registered_globals[i](loc, nodeiamon, player)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
timer = 0
|
||||
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
|
||||
description = Drops are generated rarely under solid nodes
|
||||
depends = mcl_core
|
|
@ -1,12 +1,12 @@
|
|||
Dripping Mod
|
||||
Dripping Water Mod
|
||||
by kddekadenz
|
||||
|
||||
modified for MineClone 2 by Wuzzy and NO11
|
||||
modified for MineClone 2 by Wuzzy
|
||||
|
||||
|
||||
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 :)
|
||||
|
|
@ -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 paddling_speed = 22
|
||||
|
@ -84,7 +84,7 @@ local function attach_object(self, obj)
|
|||
end
|
||||
end, name)
|
||||
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
|
||||
obj:get_luaentity()._old_visual_size = visual_size
|
||||
end
|
||||
|
@ -115,7 +115,7 @@ local boat = {
|
|||
collisionbox = {-0.5, -0.35, -0.5, 0.5, 0.3, 0.5},
|
||||
visual = "mesh",
|
||||
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,
|
||||
hp_max = boat_max_hp,
|
||||
damage_texture_modifier = "^[colorize:white:0",
|
||||
|
@ -148,11 +148,6 @@ function boat.on_activate(self, staticdata, dtime_s)
|
|||
self._v = data.v
|
||||
self._last_v = self._v
|
||||
self._itemstring = data.itemstring
|
||||
|
||||
while #data.textures < 5 do
|
||||
table.insert(data.textures, data.textures[1])
|
||||
end
|
||||
|
||||
self.object:set_properties({textures = data.textures})
|
||||
end
|
||||
end
|
||||
|
@ -193,7 +188,7 @@ function boat.on_punch(self, puncher, time_from_last_punch, tool_capabilities, d
|
|||
end
|
||||
|
||||
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)
|
||||
local v_factor = 1
|
||||
|
@ -267,7 +262,7 @@ function boat.on_step(self, dtime, moveresult)
|
|||
return
|
||||
end
|
||||
local yaw = self.object:get_yaw()
|
||||
if ctrl and ctrl.up then
|
||||
if ctrl.up then
|
||||
-- Forwards
|
||||
self._v = self._v + 0.1 * v_factor
|
||||
|
||||
|
@ -276,7 +271,7 @@ function boat.on_step(self, dtime, moveresult)
|
|||
self.object:set_animation({x=0, y=40}, paddling_speed, 0, true)
|
||||
self._animation = 1
|
||||
end
|
||||
elseif ctrl and ctrl.down then
|
||||
elseif ctrl.down then
|
||||
-- Backwards
|
||||
self._v = self._v - 0.1 * v_factor
|
||||
|
||||
|
@ -333,17 +328,16 @@ function boat.on_step(self, dtime, moveresult)
|
|||
|
||||
p.y = p.y - boat_y_offset
|
||||
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
|
||||
-- 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_velo = get_velocity(self._v, self.object:get_yaw(),
|
||||
self.object:get_velocity().y)
|
||||
else
|
||||
p.y = p.y + 1
|
||||
local is_obsidian_boat = self.object:get_luaentity()._itemstring == "mcl_boats:boat_obsidian"
|
||||
if is_water(p) or is_obsidian_boat then
|
||||
if is_water(p) then
|
||||
-- Inside water: Slowly sink
|
||||
local y = self.object:get_velocity().y
|
||||
y = y - 0.01
|
||||
|
@ -383,13 +377,13 @@ end
|
|||
-- Register one entity for all boat types
|
||||
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 names = { S("Oak Boat"), S("Spruce Boat"), S("Birch Boat"), S("Jungle Boat"), S("Acacia Boat"), S("Dark Oak Boat"), S("Obsidian Boat") }
|
||||
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") }
|
||||
local craftstuffs = {}
|
||||
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
|
||||
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
|
||||
local itemstring = "mcl_boats:"..boat_ids[b]
|
||||
|
@ -400,7 +394,7 @@ for b=1, #boat_ids do
|
|||
if b == 1 then
|
||||
help = true
|
||||
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")
|
||||
end
|
||||
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))
|
||||
end
|
||||
local boat = minetest.add_entity(pos, "mcl_boats:boat")
|
||||
local texture = "mcl_boats_texture_"..images[b].."_boat.png"
|
||||
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())
|
||||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||
itemstack:take_item()
|
||||
|
@ -477,6 +470,6 @@ minetest.register_craft({
|
|||
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")
|
||||
end
|
||||
|
|
|
@ -6,7 +6,6 @@ Boats are used to travel on the surface of water.=Boote werden benutzt, um sich
|
|||
Dark Oak Boat=Schwarzeichenboot
|
||||
Jungle Boat=Dschungelboot
|
||||
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
|
||||
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
|
||||
Jungle Boat=Bateau en Acajou
|
||||
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
|
||||
Water vehicle=Véhicule aquatique
|
||||
Sneak to dismount=
|
||||
Water vehicle=Véhicule aquatique
|
|
@ -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ź
|
|
@ -1,11 +0,0 @@
|
|||
# textdomain: mcl_boats
|
||||
Acacia Boat=相思木船
|
||||
Birch Boat=白樺木船
|
||||
Boat=船
|
||||
Boats are used to travel on the surface of water.=船是用來在水上行走的交通工具。
|
||||
Dark Oak Boat=黑橡木船
|
||||
Jungle 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. Rightclick the boat again to leave it, punch the boat to make it drop as an item.=右鍵單擊水源以放置船。右鍵單擊船以搭乘它。使用[左]和[右]進行轉向,[向前]加快速度,[向後]減速或向後移動。再次右鍵單擊船以離開它,打擊船以使其掉落為物品。
|
||||
Spruce Boat=杉木船
|
||||
Water vehicle=水上交通工具
|
|
@ -6,7 +6,6 @@ Boats are used to travel on the surface of water.=
|
|||
Dark Oak Boat=
|
||||
Jungle 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=
|
||||
Water vehicle=
|
||||
Sneak to dismount=
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
name = mcl_boats
|
||||
author = PilzAdam
|
||||
description = Adds drivable boats.
|
||||
depends = mcl_player, flowlib, mcl_title
|
||||
depends = mcl_player, flowlib
|
||||
optional_depends = mcl_core, doc_identifier
|
||||
|
||||
|
||||
|
|
Before Width: | Height: | Size: 264 B |
Before Width: | Height: | Size: 535 B |
|
@ -1,85 +1,122 @@
|
|||
function mcl_burning.get_storage(obj)
|
||||
return obj:is_player() and mcl_burning.storage[obj] or obj:get_luaentity()
|
||||
local S = minetest.get_translator("mcl_burning")
|
||||
|
||||
function mcl_burning.get_default(datatype)
|
||||
local default_table = {string = "", float = 0.0, int = 0, bool = false}
|
||||
return default_table[datatype]
|
||||
end
|
||||
|
||||
function mcl_burning.get(obj, datatype, name)
|
||||
local key
|
||||
if obj:is_player() then
|
||||
local meta = obj:get_meta()
|
||||
return meta["get_" .. datatype](meta, "mcl_burning:" .. name)
|
||||
else
|
||||
local luaentity = obj:get_luaentity()
|
||||
return luaentity and luaentity["mcl_burning_" .. name] or mcl_burning.get_default(datatype)
|
||||
end
|
||||
end
|
||||
|
||||
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_storage(obj).burn_time
|
||||
return mcl_burning.get(obj, "float", "burn_time") > 0
|
||||
end
|
||||
|
||||
function mcl_burning.is_affected_by_rain(obj)
|
||||
return mcl_weather.get_weather() == "rain" and mcl_weather.is_outdoor(obj:get_pos())
|
||||
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, storage)
|
||||
local cache = storage.collisionbox_cache
|
||||
if cache then
|
||||
local box = cache[smaller and 2 or 1]
|
||||
return box[1], box[2]
|
||||
else
|
||||
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])
|
||||
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)
|
||||
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
|
||||
minp = vector.add(minp, s_vec)
|
||||
maxp = vector.subtract(maxp, s_vec)
|
||||
end
|
||||
return minp, maxp
|
||||
end
|
||||
|
||||
local find_nodes_in_area = minetest.find_nodes_in_area
|
||||
|
||||
function mcl_burning.get_touching_nodes(obj, nodenames, storage)
|
||||
function mcl_burning.get_touching_nodes(obj, nodenames)
|
||||
local pos = obj:get_pos()
|
||||
local minp, maxp = mcl_burning.get_collisionbox(obj, true, storage)
|
||||
local nodes = find_nodes_in_area(vector.add(pos, minp), vector.add(pos, maxp), nodenames)
|
||||
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)
|
||||
return nodes
|
||||
end
|
||||
|
||||
-- Manages the fire animation on a burning player's HUD
|
||||
--
|
||||
-- Parameters:
|
||||
-- player - a valid player object;
|
||||
--
|
||||
-- If the player already has a fire HUD, updates the burning animation.
|
||||
-- If the fire does not have a fire HUD, initializes the HUD.
|
||||
--
|
||||
function mcl_burning.update_hud(player)
|
||||
local animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8
|
||||
local hud_flame_animated = "mcl_burning_hud_flame_animated.png^[opacity:180^[verticalframe:" .. animation_frames .. ":"
|
||||
function mcl_burning.get_highest_group_value(obj, groupname)
|
||||
local nodes = mcl_burning.get_touching_nodes(obj, "group:" .. groupname, true)
|
||||
local highest_group_value = 0
|
||||
|
||||
local storage = mcl_burning.get_storage(player)
|
||||
if not storage.fire_hud_id then
|
||||
storage.animation_frame = 1
|
||||
storage.fire_hud_id = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0.5, y = 0.5},
|
||||
scale = {x = -100, y = -100},
|
||||
text = hud_flame_animated .. storage.animation_frame,
|
||||
z_index = 1000,
|
||||
})
|
||||
else
|
||||
storage.animation_frame = storage.animation_frame + 1
|
||||
if storage.animation_frame > animation_frames - 1 then
|
||||
storage.animation_frame = 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
|
||||
end
|
||||
|
||||
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
|
||||
player:hud_change(storage.fire_hud_id, "text", hud_flame_animated .. storage.animation_frame)
|
||||
end
|
||||
end
|
||||
|
||||
-- Sets and object state as burning and adds a fire animation to the object.
|
||||
--
|
||||
-- Parameters:
|
||||
-- obj - may be a player or a lua_entity;
|
||||
-- burn_time - sets the object's burn duration;
|
||||
--
|
||||
-- If obj is a player, adds a fire animation to the HUD, if obj is a
|
||||
-- lua_entity, adds an animated fire entity to obj.
|
||||
-- The effective burn duration is modified by obj's armor protection.
|
||||
-- If obj was already burning, its burn duration is updated if the current
|
||||
-- duration is less than burn_time.
|
||||
-- If obj is dead, fireproof or a creative player, this function does nothing.
|
||||
--
|
||||
function mcl_burning.set_on_fire(obj, burn_time)
|
||||
function mcl_burning.set_on_fire(obj, burn_time, reason)
|
||||
if obj:get_hp() < 0 then
|
||||
return
|
||||
end
|
||||
|
@ -89,95 +126,177 @@ function mcl_burning.set_on_fire(obj, burn_time)
|
|||
return
|
||||
end
|
||||
|
||||
if obj:is_player() and minetest.is_creative_enabled(obj:get_player_name()) then
|
||||
burn_time = 0
|
||||
else
|
||||
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
|
||||
for _, stack in pairs(armor_list) do
|
||||
local fire_prot_lvl = mcl_enchanting.get_enchantment(stack, "fire_protection")
|
||||
if fire_prot_lvl > max_fire_prot_lvl then
|
||||
max_fire_prot_lvl = fire_prot_lvl
|
||||
end
|
||||
end
|
||||
end
|
||||
if max_fire_prot_lvl > 0 then
|
||||
burn_time = burn_time - math.floor(burn_time * max_fire_prot_lvl * 0.15)
|
||||
end
|
||||
end
|
||||
|
||||
local storage = mcl_burning.get_storage(obj)
|
||||
if storage.burn_time then
|
||||
if burn_time > storage.burn_time then
|
||||
storage.burn_time = burn_time
|
||||
end
|
||||
return
|
||||
end
|
||||
storage.burn_time = burn_time
|
||||
storage.fire_damage_timer = 0
|
||||
|
||||
local minp, maxp = mcl_burning.get_collisionbox(obj, false, storage)
|
||||
local size = vector.subtract(maxp, minp)
|
||||
size = vector.multiply(size, vector.new(1.1, 1.2, 1.1))
|
||||
size = vector.divide(size, obj:get_properties().visual_size)
|
||||
|
||||
local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire")
|
||||
fire_entity:set_properties({visual_size = size})
|
||||
fire_entity:set_attach(obj, "", vector.new(0, size.y * 5, 0), vector.new(0, 0, 0))
|
||||
local old_burn_time = mcl_burning.get(obj, "float", "burn_time")
|
||||
local max_fire_prot_lvl = 0
|
||||
|
||||
if obj:is_player() then
|
||||
mcl_burning.update_hud(obj)
|
||||
if minetest.is_creative_enabled(obj:get_player_name()) then
|
||||
burn_time = burn_time / 100
|
||||
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
|
||||
|
||||
-- FIXME: does this code make sense? It removes attached fire luaentities from
|
||||
-- another object that happen to be at the same position.
|
||||
local fire_luaentity = fire_entity:get_luaentity()
|
||||
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
|
||||
if max_fire_prot_lvl > 0 then
|
||||
burn_time = burn_time - math.floor(burn_time * max_fire_prot_lvl * 0.15)
|
||||
end
|
||||
|
||||
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
|
||||
hud_id = mcl_burning.get(obj, "int", "hud_id")
|
||||
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
|
||||
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 minp, maxp = mcl_burning.get_collisionbox(obj)
|
||||
local obj_size = obj:get_properties().visual_size
|
||||
|
||||
local vertical_grow_factor = 1.2
|
||||
local horizontal_grow_factor = 1.1
|
||||
local grow_vector = vector.new(horizontal_grow_factor, vertical_grow_factor, horizontal_grow_factor)
|
||||
|
||||
local size = vector.subtract(maxp, minp)
|
||||
size = vector.multiply(size, grow_vector)
|
||||
size = vector.divide(size, obj_size)
|
||||
local offset = vector.new(0, size.y * 10 / 2, 0)
|
||||
|
||||
fire_entity:set_properties({visual_size = size})
|
||||
fire_entity:set_attach(obj, "", offset, {x = 0, y = 0, z = 0})
|
||||
mcl_burning.update_animation_frame(obj, fire_entity, 0)
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_burning.extinguish(obj)
|
||||
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 storage.fire_hud_id then
|
||||
obj:hud_remove(storage.fire_hud_id)
|
||||
end
|
||||
mcl_burning.storage[obj] = {}
|
||||
else
|
||||
storage.burn_time = nil
|
||||
storage.fire_damage_timer = nil
|
||||
local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1
|
||||
obj:hud_remove(hud_id)
|
||||
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
|
||||
|
||||
function mcl_burning.catch_fire_tick(obj, dtime)
|
||||
if mcl_burning.is_affected_by_rain(obj) or #mcl_burning.get_touching_nodes(obj, "group:puts_out_fire") > 0 then
|
||||
mcl_burning.extinguish(obj)
|
||||
else
|
||||
local set_on_fire_value = mcl_burning.get_highest_group_value(obj, "set_on_fire")
|
||||
|
||||
if set_on_fire_value > 0 then
|
||||
mcl_burning.set_on_fire(obj, set_on_fire_value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_burning.tick(obj, dtime, storage)
|
||||
if storage.burn_time then
|
||||
storage.burn_time = storage.burn_time - dtime
|
||||
function mcl_burning.tick(obj, dtime)
|
||||
local burn_time = mcl_burning.get(obj, "float", "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)
|
||||
return true
|
||||
else
|
||||
storage.fire_damage_timer = storage.fire_damage_timer + dtime
|
||||
if burn_time <= 0 then
|
||||
mcl_burning.extinguish(obj)
|
||||
else
|
||||
mcl_burning.set(obj, "float", "burn_time", burn_time)
|
||||
|
||||
if storage.fire_damage_timer >= 1 then
|
||||
storage.fire_damage_timer = 0
|
||||
local damage_timer = mcl_burning.get(obj, "float", "damage_timer") + dtime
|
||||
|
||||
local luaentity = obj:get_luaentity()
|
||||
if damage_timer >= 1 then
|
||||
damage_timer = 0
|
||||
mcl_burning.damage(obj)
|
||||
end
|
||||
|
||||
if not luaentity or not luaentity.fire_damage_resistant then
|
||||
mcl_util.deal_damage(obj, 1, {type = "on_fire"})
|
||||
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,51 +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")
|
||||
|
||||
mcl_burning = {
|
||||
-- the storage table holds a list of objects (players,luaentities) and tables
|
||||
-- associated with these objects. These tables have the following attributes:
|
||||
-- burn_time:
|
||||
-- Remaining time that object will burn.
|
||||
-- fire_damage_timer:
|
||||
-- Timer for dealing damage every second while burning.
|
||||
-- fire_hud_id:
|
||||
-- HUD id of the flames animation on a burning player's HUD.
|
||||
-- animation_frame:
|
||||
-- The HUD's current animation frame, used by update_hud().
|
||||
-- collisionbox_cache:
|
||||
-- Used by mcl_burning.get_collisionbox() to avoid recalculations.
|
||||
storage = {}
|
||||
animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8
|
||||
}
|
||||
|
||||
dofile(modpath .. "/api.lua")
|
||||
|
||||
local pairs = pairs
|
||||
local get_connected_players = minetest.get_connected_players
|
||||
local get_item_group = minetest.get_item_group
|
||||
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)
|
||||
for _, player in pairs(get_connected_players()) do
|
||||
local storage = mcl_burning.storage[player]
|
||||
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
|
||||
for _, player in pairs(minetest.get_connected_players()) do
|
||||
mcl_burning.tick(player, dtime)
|
||||
end
|
||||
end)
|
||||
|
||||
|
@ -53,73 +31,6 @@ minetest.register_on_respawnplayer(function(player)
|
|||
mcl_burning.extinguish(player)
|
||||
end)
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local storage = {}
|
||||
local burn_data = player:get_meta():get_string("mcl_burning:data")
|
||||
if burn_data ~= "" then
|
||||
storage = minetest.deserialize(burn_data)
|
||||
end
|
||||
mcl_burning.storage[player] = storage
|
||||
if storage.burn_time and storage.burn_time > 0 then
|
||||
mcl_burning.update_hud(player)
|
||||
end
|
||||
end)
|
||||
|
||||
local function on_leaveplayer(player)
|
||||
local storage = mcl_burning.storage[player]
|
||||
storage.fire_hud_id = nil
|
||||
player:get_meta():set_string("mcl_burning:data", minetest.serialize(storage))
|
||||
mcl_burning.storage[player] = nil
|
||||
end
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
on_leaveplayer(player)
|
||||
mcl_burning.set(player, "int", "hud_id")
|
||||
end)
|
||||
|
||||
minetest.register_on_shutdown(function()
|
||||
for _,player in ipairs(minetest.get_connected_players()) do
|
||||
on_leaveplayer(player)
|
||||
end
|
||||
end)
|
||||
|
||||
local animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8
|
||||
|
||||
minetest.register_entity("mcl_burning:fire", {
|
||||
initial_properties = {
|
||||
physical = false,
|
||||
collisionbox = {0, 0, 0, 0, 0, 0},
|
||||
visual = "upright_sprite",
|
||||
textures = {
|
||||
"mcl_burning_entity_flame_animated.png",
|
||||
"mcl_burning_entity_flame_animated.png"
|
||||
},
|
||||
spritediv = {x = 1, y = 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}, animation_frames, 1.0 / animation_frames)
|
||||
end,
|
||||
on_step = function(self, dtime)
|
||||
local parent = self.object:get_attach()
|
||||
if not parent then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
local storage = mcl_burning.get_storage(parent)
|
||||
if not storage or not storage.burn_time then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
if parent:is_player() then
|
||||
self.animation_timer = self.animation_timer + dtime
|
||||
if self.animation_timer >= 0.1 then
|
||||
self.animation_timer = 0
|
||||
mcl_burning.update_hud(parent)
|
||||
end
|
||||
end
|
||||
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
|
||||
-- Fallback
|
||||
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
|
||||
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
|
||||
return
|
||||
end
|
||||
|
@ -17,34 +23,80 @@ local function deal_falling_damage(self, dtime)
|
|||
-- Fallback
|
||||
self._startpos = pos
|
||||
end
|
||||
self._hit = self._hit or {}
|
||||
for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
|
||||
local entity = obj:get_luaentity()
|
||||
if entity and entity.name == "__builtin:item" then
|
||||
obj:remove()
|
||||
elseif mcl_util.get_hp(obj) > 0 and not self._hit[obj] then
|
||||
self._hit[obj] = true
|
||||
local way = self._startpos.y - pos.y
|
||||
local damage = (way - 1) * 2
|
||||
damage = math.min(40, math.max(0, damage))
|
||||
if damage >= 1 then
|
||||
-- Reduce damage if wearing a helmet
|
||||
local inv = mcl_util.get_inventory(obj)
|
||||
if inv then
|
||||
local helmet = inv:get_stack("armor", 2)
|
||||
if minetest.get_item_group(helmet:get_name(), "combat_armor") > 0 then
|
||||
damage = damage / 4 * 3
|
||||
mcl_util.use_item_durability(helmet, 1)
|
||||
inv:set_stack("armor", 2, helmet)
|
||||
local objs = minetest.get_objects_inside_radius(pos, 1)
|
||||
for _,v in ipairs(objs) do
|
||||
if v:is_player() then
|
||||
local hp = v:get_hp()
|
||||
local name = v:get_player_name()
|
||||
if hp ~= 0 then
|
||||
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
|
||||
local dmg_type
|
||||
if minetest.get_item_group(self.node.name, "anvil") ~= 0 then
|
||||
dmg_type = "anvil"
|
||||
else
|
||||
dmg_type = "falling_node"
|
||||
if not hit then
|
||||
table.insert(self._hit_players, name)
|
||||
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
|
||||
-- Reduce damage if wearing a helmet
|
||||
local inv = v:get_inventory()
|
||||
local helmet = inv:get_stack("armor", 2)
|
||||
if has_mcl_armor and not helmet:is_empty() then
|
||||
hp = hp/4*3
|
||||
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)
|
||||
end
|
||||
end
|
||||
local msg
|
||||
if minetest.get_item_group(self.node.name, "anvil") ~= 0 then
|
||||
msg = S("@1 was smashed by a falling anvil.", v:get_player_name())
|
||||
else
|
||||
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
|
||||
mcl_util.deal_damage(obj, damage, {type = dmg_type})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -60,8 +112,10 @@ minetest.register_entity(":__builtin:falling_node", {
|
|||
collide_with_objects = false,
|
||||
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||
},
|
||||
|
||||
node = {},
|
||||
meta = {},
|
||||
|
||||
set_node = function(self, node, meta)
|
||||
local def = minetest.registered_nodes[node.name]
|
||||
-- Change falling node if definition tells us to
|
||||
|
@ -88,6 +142,7 @@ minetest.register_entity(":__builtin:falling_node", {
|
|||
glow = glow,
|
||||
})
|
||||
end,
|
||||
|
||||
get_staticdata = function(self)
|
||||
local meta = self.meta
|
||||
-- Workaround: Save inventory seperately from metadata.
|
||||
|
@ -108,9 +163,10 @@ minetest.register_entity(":__builtin:falling_node", {
|
|||
}
|
||||
return minetest.serialize(ds)
|
||||
end,
|
||||
|
||||
on_activate = function(self, staticdata)
|
||||
self.object:set_armor_groups({immortal = 1})
|
||||
|
||||
|
||||
local ds = minetest.deserialize(staticdata)
|
||||
if ds then
|
||||
self._startpos = ds._startpos
|
||||
|
@ -130,6 +186,7 @@ minetest.register_entity(":__builtin:falling_node", {
|
|||
end
|
||||
self._startpos = vector.round(self._startpos)
|
||||
end,
|
||||
|
||||
on_step = function(self, dtime)
|
||||
-- Set gravity
|
||||
local acceleration = self.object:get_acceleration()
|
||||
|
@ -143,7 +200,7 @@ minetest.register_entity(":__builtin:falling_node", {
|
|||
local np = {x = pos.x, y = pos.y + 0.3, z = pos.z}
|
||||
local n2 = minetest.get_node(np)
|
||||
if n2.name == "mcl_portals:portal_end" then
|
||||
-- TODO: Teleport falling node.
|
||||
-- TODO: Teleport falling node.
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
@ -181,9 +238,10 @@ minetest.register_entity(":__builtin:falling_node", {
|
|||
return
|
||||
end
|
||||
local nd = minetest.registered_nodes[n2.name]
|
||||
--if n2.name == "mcl_portals:portal_end" then
|
||||
-- TODO: Teleport falling node.
|
||||
if (nd and nd.buildable_to == true) or minetest.get_item_group(self.node.name, "crush_after_fall") ~= 0 then
|
||||
if n2.name == "mcl_portals:portal_end" then
|
||||
-- TODO: Teleport falling node.
|
||||
|
||||
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
|
||||
minetest.remove_node(np)
|
||||
-- Run script hook
|
||||
|
@ -250,6 +308,7 @@ minetest.register_entity(":__builtin:falling_node", {
|
|||
self.object:set_pos(npos)
|
||||
end
|
||||
end
|
||||
|
||||
deal_falling_damage(self, dtime)
|
||||
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 раздавило падающим блоком.
|
|
@ -1,3 +0,0 @@
|
|||
# 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
|
||||
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
|
||||
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
|
||||
|
||||
local function get_gravity()
|
||||
local get_gravity = function()
|
||||
return tonumber(minetest.settings:get("movement_gravity")) or 9.81
|
||||
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_core:diamond", "mcl:diamonds")
|
||||
|
||||
local function check_pickup_achievements(object, player)
|
||||
local check_pickup_achievements = function(object, player)
|
||||
if has_awards then
|
||||
local itemname = ItemStack(object:get_luaentity().itemstring):get_name()
|
||||
local playername = player:get_player_name()
|
||||
|
@ -72,7 +72,7 @@ local function check_pickup_achievements(object, player)
|
|||
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
|
||||
luaentity.physical_state = true
|
||||
object:set_properties({
|
||||
|
@ -83,7 +83,7 @@ local function enable_physics(object, luaentity, ignore_check)
|
|||
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
|
||||
luaentity.physical_state = false
|
||||
object:set_properties({
|
||||
|
@ -98,19 +98,21 @@ end
|
|||
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
|
||||
tick = not tick
|
||||
|
||||
for _,player in pairs(minetest.get_connected_players()) do
|
||||
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()
|
||||
|
||||
if tick == true and pool[name] > 0 then
|
||||
minetest.sound_play("item_drop_pickup", {
|
||||
pos = pos,
|
||||
gain = 0.3,
|
||||
gain = 0.7,
|
||||
max_hear_distance = 16,
|
||||
pitch = math.random(70,110)/100
|
||||
})
|
||||
|
@ -122,7 +124,7 @@ minetest.register_globalstep(function(dtime)
|
|||
end
|
||||
|
||||
|
||||
|
||||
|
||||
local inv = player:get_inventory()
|
||||
local checkpos = {x=pos.x,y=pos.y + item_drop_settings.player_collect_height,z=pos.z}
|
||||
|
||||
|
@ -233,7 +235,7 @@ function minetest.handle_node_drops(pos, drops, digger)
|
|||
local dug_node = minetest.get_node(pos)
|
||||
local tooldef
|
||||
local tool
|
||||
if digger then
|
||||
if digger ~= nil then
|
||||
tool = digger:get_wielded_item()
|
||||
tooldef = minetest.registered_tools[tool:get_name()]
|
||||
|
||||
|
@ -256,8 +258,6 @@ function minetest.handle_node_drops(pos, drops, digger)
|
|||
|
||||
local silk_touch_drop = false
|
||||
local nodedef = minetest.registered_nodes[dug_node.name]
|
||||
if not nodedef then return end
|
||||
|
||||
if shearsy_level and shearsy_level > 0 and nodedef._mcl_shears_drop then
|
||||
if nodedef._mcl_shears_drop == true then
|
||||
drops = { dug_node.name }
|
||||
|
@ -292,10 +292,10 @@ function minetest.handle_node_drops(pos, drops, digger)
|
|||
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")
|
||||
if experience_amount > 0 then
|
||||
mcl_experience.throw_xp(pos, experience_amount)
|
||||
mcl_experience.throw_experience(pos, experience_amount)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -316,7 +316,7 @@ function minetest.handle_node_drops(pos, drops, digger)
|
|||
end
|
||||
-- Spawn item and apply random speed
|
||||
local obj = minetest.add_item(dpos, drop_item)
|
||||
if obj then
|
||||
if obj ~= nil then
|
||||
local x = math.random(1, 5)
|
||||
if math.random(1,2) == 1 then
|
||||
x = -x
|
||||
|
@ -365,17 +365,6 @@ if not time_to_live then
|
|||
time_to_live = 300
|
||||
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", {
|
||||
initial_properties = {
|
||||
hp_max = 1,
|
||||
|
@ -396,7 +385,7 @@ minetest.register_entity(":__builtin:item", {
|
|||
-- The itemstring MUST be set immediately to a non-empty string after creating the entity.
|
||||
-- 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.
|
||||
itemstring = "",
|
||||
itemstring = '',
|
||||
|
||||
-- If true, item will fall
|
||||
physical_state = true,
|
||||
|
@ -417,14 +406,6 @@ minetest.register_entity(":__builtin:item", {
|
|||
return
|
||||
end
|
||||
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 max_count = stack:get_stack_max()
|
||||
if count > max_count then
|
||||
|
@ -437,9 +418,13 @@ minetest.register_entity(":__builtin:item", {
|
|||
if itemtable then
|
||||
itemname = stack:to_table().name
|
||||
end
|
||||
local item_texture = nil
|
||||
local item_type = ""
|
||||
local glow
|
||||
local def = minetest.registered_items[itemname]
|
||||
if def then
|
||||
item_texture = def.inventory_image
|
||||
item_type = def.type
|
||||
description = def.description
|
||||
glow = def.light_source
|
||||
end
|
||||
|
@ -482,7 +467,7 @@ minetest.register_entity(":__builtin:item", {
|
|||
end,
|
||||
|
||||
get_staticdata = function(self)
|
||||
local data = minetest.serialize({
|
||||
return minetest.serialize({
|
||||
itemstring = self.itemstring,
|
||||
always_collect = self.always_collect,
|
||||
age = self.age,
|
||||
|
@ -490,39 +475,6 @@ minetest.register_entity(":__builtin:item", {
|
|||
_flowing = self._flowing,
|
||||
_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,
|
||||
|
||||
on_activate = function(self, staticdata, dtime_s)
|
||||
|
@ -610,7 +562,7 @@ minetest.register_entity(":__builtin:item", {
|
|||
return true
|
||||
end,
|
||||
|
||||
on_step = function(self, dtime, moveresult)
|
||||
on_step = function(self, dtime)
|
||||
if self._removed then
|
||||
self.object:set_properties({
|
||||
physical = false
|
||||
|
@ -620,7 +572,7 @@ minetest.register_entity(":__builtin:item", {
|
|||
return
|
||||
end
|
||||
self.age = self.age + dtime
|
||||
if self._collector_timer then
|
||||
if self._collector_timer ~= nil then
|
||||
self._collector_timer = self._collector_timer + dtime
|
||||
end
|
||||
if time_to_live > 0 and self.age > time_to_live then
|
||||
|
@ -641,12 +593,6 @@ minetest.register_entity(":__builtin:item", {
|
|||
local node = minetest.get_node_or_nil(p)
|
||||
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 self._magnet_active and (self._collector_timer == nil or (self._collector_timer > item_drop_settings.magnet_time)) then
|
||||
self._magnet_active = false
|
||||
|
@ -677,18 +623,6 @@ minetest.register_entity(":__builtin:item", {
|
|||
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
|
||||
if def and def.walkable and def.groups and def.groups.opaque == 1 then
|
||||
local shootdir
|
||||
|
@ -700,6 +634,16 @@ minetest.register_entity(":__builtin:item", {
|
|||
-- 1st: closest
|
||||
-- 2nd: other direction
|
||||
-- 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
|
||||
order = cxcz(order, cx, "x", "z")
|
||||
order = cxcz(order, cz, "z", "x")
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
local vector = vector
|
||||
|
||||
function mcl_minecarts:get_sign(z)
|
||||
if z == 0 then
|
||||
return 0
|
||||
|
@ -40,9 +38,11 @@ end
|
|||
|
||||
function mcl_minecarts:check_front_up_down(pos, dir_, check_down, railtype)
|
||||
local dir = vector.new(dir_)
|
||||
local cur = nil
|
||||
|
||||
-- Front
|
||||
dir.y = 0
|
||||
local cur = vector.add(pos, dir)
|
||||
cur = vector.add(pos, dir)
|
||||
if mcl_minecarts:is_rail(cur, railtype) then
|
||||
return dir
|
||||
end
|
||||
|
@ -65,9 +65,9 @@ end
|
|||
|
||||
function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
||||
local pos = vector.round(pos_)
|
||||
local cur
|
||||
local cur = nil
|
||||
local left_check, right_check = true, true
|
||||
|
||||
|
||||
-- Check left and right
|
||||
local left = {x=0, y=0, z=0}
|
||||
local right = {x=0, y=0, z=0}
|
||||
|
@ -78,7 +78,7 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
|||
left.z = dir.x
|
||||
right.z = -dir.x
|
||||
end
|
||||
|
||||
|
||||
if ctrl then
|
||||
if old_switch == 1 then
|
||||
left_check = false
|
||||
|
@ -100,13 +100,13 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
|||
right_check = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Normal
|
||||
cur = mcl_minecarts:check_front_up_down(pos, dir, true, railtype)
|
||||
if cur then
|
||||
return cur
|
||||
end
|
||||
|
||||
|
||||
-- Left, if not already checked
|
||||
if left_check then
|
||||
cur = mcl_minecarts:check_front_up_down(pos, left, false, railtype)
|
||||
|
@ -114,7 +114,7 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
|||
return cur
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Right, if not already checked
|
||||
if right_check then
|
||||
cur = mcl_minecarts:check_front_up_down(pos, right, false, railtype)
|
||||
|
@ -122,6 +122,7 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
|||
return cur
|
||||
end
|
||||
end
|
||||
|
||||
-- Backwards
|
||||
if not old_switch then
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
return {x=0, y=0, z=0}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
local modname = minetest.get_current_modname()
|
||||
local S = minetest.get_translator(modname)
|
||||
local S = minetest.get_translator("mcl_minecarts")
|
||||
|
||||
local has_mcl_wip = minetest.get_modpath("mcl_wip")
|
||||
|
||||
mcl_minecarts = {}
|
||||
mcl_minecarts.modpath = minetest.get_modpath(modname)
|
||||
mcl_minecarts.modpath = minetest.get_modpath("mcl_minecarts")
|
||||
mcl_minecarts.speed_max = 10
|
||||
mcl_minecarts.check_float_time = 15
|
||||
|
||||
|
@ -198,27 +197,14 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
|
|||
else
|
||||
self._last_float_check = self._last_float_check + dtime
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
local pos, rou_pos, node
|
||||
-- Drop minecart if it isn't on a rail anymore
|
||||
if self._last_float_check >= mcl_minecarts.check_float_time then
|
||||
pos = self.object:get_pos()
|
||||
rou_pos = vector.round(pos)
|
||||
node = minetest.get_node(rou_pos)
|
||||
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
|
||||
if player then
|
||||
if self._old_pos then
|
||||
|
@ -432,8 +418,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
|
|||
-- Slow down or speed up
|
||||
local acc = dir.y * -1.8
|
||||
local friction = 0.4
|
||||
local ndef = minetest.registered_nodes[minetest.get_node(pos).name]
|
||||
local speed_mod = ndef and ndef._rail_acceleration
|
||||
local speed_mod = minetest.registered_nodes[minetest.get_node(pos).name]._rail_acceleration
|
||||
|
||||
acc = acc - friction
|
||||
|
||||
|
@ -501,6 +486,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
|
|||
if update.pos then
|
||||
self.object:set_pos(pos)
|
||||
end
|
||||
update = nil
|
||||
end
|
||||
|
||||
function cart:get_staticdata()
|
||||
|
@ -511,7 +497,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
|
|||
end
|
||||
|
||||
-- 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
|
||||
return
|
||||
end
|
||||
|
@ -538,7 +524,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer)
|
|||
local cart = minetest.add_entity(railpos, entity_id)
|
||||
local railtype = minetest.get_item_group(node.name, "connect_to_raillike")
|
||||
local le = cart:get_luaentity()
|
||||
if le then
|
||||
if le ~= nil then
|
||||
le._railtype = railtype
|
||||
end
|
||||
local cart_dir = mcl_minecarts:get_rail_direction(railpos, {x=1, y=0, z=0}, nil, nil, railtype)
|
||||
|
@ -555,7 +541,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer)
|
|||
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
|
||||
|
||||
local groups = { minecart = 1, transport = 1 }
|
||||
|
@ -621,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)
|
||||
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)
|
||||
if minetest.get_modpath("doc_identifier") then
|
||||
if minetest.get_modpath("doc_identifier") ~= nil then
|
||||
doc.sub.identifier.register_object(entity_id, "craftitems", itemstring)
|
||||
end
|
||||
end
|
||||
|
@ -660,7 +646,7 @@ register_minecart(
|
|||
if player then
|
||||
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})
|
||||
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, name)
|
||||
end
|
||||
|
@ -831,34 +817,35 @@ minetest.register_craft({
|
|||
})
|
||||
|
||||
-- TODO: Re-enable crafting of special minecarts when they have been implemented
|
||||
--[[minetest.register_craft({
|
||||
output = "mcl_minecarts:furnace_minecart",
|
||||
recipe = {
|
||||
{"mcl_furnaces:furnace"},
|
||||
{"mcl_minecarts:minecart"},
|
||||
},
|
||||
})
|
||||
if false then
|
||||
minetest.register_craft({
|
||||
output = "mcl_minecarts:furnace_minecart",
|
||||
recipe = {
|
||||
{"mcl_furnaces:furnace"},
|
||||
{"mcl_minecarts:minecart"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "mcl_minecarts:hopper_minecart",
|
||||
recipe = {
|
||||
{"mcl_hoppers:hopper"},
|
||||
{"mcl_minecarts:minecart"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "mcl_minecarts:chest_minecart",
|
||||
recipe = {
|
||||
{"mcl_chests:chest"},
|
||||
{"mcl_minecarts:minecart"},
|
||||
},
|
||||
})]]
|
||||
minetest.register_craft({
|
||||
output = "mcl_minecarts:hopper_minecart",
|
||||
recipe = {
|
||||
{"mcl_hoppers:hopper"},
|
||||
{"mcl_minecarts:minecart"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "mcl_minecarts:chest_minecart",
|
||||
recipe = {
|
||||
{"mcl_chests:chest"},
|
||||
{"mcl_minecarts:minecart"},
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
if has_mcl_wip then
|
||||
mcl_wip.register_wip_item("mcl_minecarts:chest_minecart")
|
||||
mcl_wip.register_wip_item("mcl_minecarts:furnace_minecart")
|
||||
mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart")
|
||||
mcl_wip.register_wip_item("mcl_minecarts:hopper_minecart")
|
||||
end
|
||||
end
|
|
@ -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
|
||||
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
|
||||
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é
|
||||
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é
|
||||
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
|
||||
author = Krock
|
||||
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
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
local S = minetest.get_translator("mcl_minecarts")
|
||||
|
||||
-- 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}
|
||||
if creative == false then
|
||||
groups.not_in_creative_inventory = 1
|
||||
|
@ -206,11 +206,11 @@ register_rail("mcl_minecarts:detector_rail_on",
|
|||
|
||||
-- Crafting
|
||||
minetest.register_craft({
|
||||
output = "mcl_minecarts:rail 16",
|
||||
output = 'mcl_minecarts:rail 16',
|
||||
recipe = {
|
||||
{"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: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:iron_ingot'},
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -177,8 +177,7 @@ local function object_in_range(self, object)
|
|||
local factor
|
||||
-- Apply view range reduction for special player armor
|
||||
if object:is_player() and mod_armor then
|
||||
local factors = mcl_armor.player_view_range_factors[object]
|
||||
factor = factors and factors[self.name]
|
||||
factor = armor:get_mob_view_range_factor(object, self.name)
|
||||
end
|
||||
-- Distance check
|
||||
local dist
|
||||
|
@ -820,7 +819,7 @@ local check_for_death = function(self, cause, cmi_cause)
|
|||
item_drop(self, cooked, looting)
|
||||
|
||||
if mod_experience and ((not self.child) or self.type ~= "animal") and (minetest.get_us_time() - self.xp_timestamp <= 5000000) then
|
||||
mcl_experience.throw_xp(self.object:get_pos(), math.random(self.xp_min, self.xp_max))
|
||||
mcl_experience.throw_experience(self.object:get_pos(), math.random(self.xp_min, self.xp_max))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1030,14 +1029,6 @@ local node_ok = function(pos, fallback)
|
|||
return minetest.registered_nodes[fallback]
|
||||
end
|
||||
|
||||
local function get_light(pos, tod)
|
||||
if minetest.get_node_or_nil(pos) then
|
||||
local lightfunc = minetest.get_natural_light or minetest.get_node_light
|
||||
return lightfunc(pos, tod)
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
-- environmental damage (water, lava, fire, light etc.)
|
||||
local do_env_damage = function(self)
|
||||
|
@ -1083,6 +1074,7 @@ local do_env_damage = function(self)
|
|||
|
||||
-- Use get_node_light for Minetest version 5.3 where get_natural_light
|
||||
-- does not exist yet.
|
||||
local get_light = minetest.get_natural_light or minetest.get_node_light
|
||||
local sunlight = get_light(pos, self.time_of_day)
|
||||
|
||||
-- bright light harms mob
|
||||
|
@ -1328,8 +1320,8 @@ local do_jump = function(self)
|
|||
return false
|
||||
end
|
||||
|
||||
local ndef = minetest.registered_nodes[nod.name]
|
||||
if self.walk_chance == 0 or ndef and ndef.walkable then
|
||||
if self.walk_chance == 0
|
||||
or minetest.registered_items[nod.name].walkable then
|
||||
|
||||
if minetest.get_item_group(nod.name, "fence") == 0
|
||||
and minetest.get_item_group(nod.name, "fence_gate") == 0
|
||||
|
@ -1556,7 +1548,7 @@ local breed = function(self)
|
|||
|
||||
-- Give XP
|
||||
if mod_experience then
|
||||
mcl_experience.throw_xp(pos, math.random(1, 7))
|
||||
mcl_experience.throw_experience(pos, math.random(1, 7))
|
||||
end
|
||||
|
||||
-- custom breed function
|
||||
|
@ -3456,7 +3448,7 @@ end
|
|||
local mob_step = function(self, dtime)
|
||||
|
||||
if not self.fire_resistant then
|
||||
mcl_burning.tick(self.object, dtime, self)
|
||||
mcl_burning.tick(self.object, dtime)
|
||||
end
|
||||
|
||||
if use_cmi then
|
||||
|
@ -3771,7 +3763,6 @@ minetest.register_entity(name, {
|
|||
use_texture_alpha = def.use_texture_alpha,
|
||||
stepheight = def.stepheight or 0.6,
|
||||
name = name,
|
||||
description = def.description,
|
||||
type = def.type,
|
||||
attack_type = def.attack_type,
|
||||
fly = def.fly,
|
||||
|
@ -3915,7 +3906,7 @@ minetest.register_entity(name, {
|
|||
--default built in engine collision detection
|
||||
self.object:set_properties({
|
||||
collide_with_objects = false,
|
||||
})
|
||||
})
|
||||
return mob_activate(self, staticdata, def, dtime)
|
||||
end,
|
||||
|
||||
|
@ -4359,7 +4350,7 @@ function mobs:alias_mob(old_name, new_name)
|
|||
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
local timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
timer = timer + dtime
|
||||
|
@ -4376,3 +4367,4 @@ minetest.register_globalstep(function(dtime)
|
|||
end
|
||||
timer = 0
|
||||
end)
|
||||
]]--
|