Compare commits
15 Commits
Author | SHA1 | Date |
---|---|---|
Lizzy Fleckenstein | 87462ee853 | |
Lizzy Fleckenstein | 8f735cc644 | |
Lizzy Fleckenstein | 422076f761 | |
Lizzy Fleckenstein | 9ce59d4438 | |
Lizzy Fleckenstein | 99357d467c | |
Lizzy Fleckenstein | 018fc64a27 | |
Lizzy Fleckenstein | b53329b452 | |
Lizzy Fleckenstein | c27dea59b0 | |
Lizzy Fleckenstein | 80c6b547e5 | |
Lizzy Fleckenstein | 0de53a9dcb | |
Lizzy Fleckenstein | 67dd48b06c | |
Lizzy Fleckenstein | 34c31cac1f | |
Lizzy Fleckenstein | 788b6dc080 | |
Lizzy Fleckenstein | 200ce76c70 | |
Lizzy Fleckenstein | ca94a1c354 |
|
@ -1,2 +0,0 @@
|
|||
# Text Editor TMP Files
|
||||
*.swp
|
55
.luacheckrc
|
@ -1,55 +0,0 @@
|
|||
unused_args = false
|
||||
allow_defined_top = true
|
||||
max_line_length = false
|
||||
redefined = false
|
||||
|
||||
globals = {
|
||||
"minetest", "core",
|
||||
}
|
||||
|
||||
read_globals = {
|
||||
"DIR_DELIM",
|
||||
"dump", "dump2",
|
||||
"vector",
|
||||
"VoxelManip", "VoxelArea",
|
||||
"PseudoRandom", "PcgRandom", "PerlinNoise", "PerlinNoiseMap",
|
||||
"ItemStack",
|
||||
"Settings",
|
||||
"unpack",
|
||||
|
||||
table = {
|
||||
fields = {
|
||||
"copy",
|
||||
"indexof",
|
||||
"insert_all",
|
||||
"key_value_swap",
|
||||
}
|
||||
},
|
||||
|
||||
string = {
|
||||
fields = {
|
||||
"split",
|
||||
"trim",
|
||||
}
|
||||
},
|
||||
|
||||
math = {
|
||||
fields = {
|
||||
"hypot",
|
||||
"sign",
|
||||
"factorial"
|
||||
}
|
||||
},
|
||||
------
|
||||
--MODS
|
||||
------
|
||||
|
||||
--GENERAL
|
||||
"default",
|
||||
|
||||
--ENTITIES
|
||||
"cmi",
|
||||
|
||||
--HUD
|
||||
"sfinv", "sfinv_buttons", "unified_inventory", "cmsg", "inventory_plus",
|
||||
}
|
4
API.md
|
@ -17,10 +17,6 @@ Items can have these fields:
|
|||
anvil.
|
||||
See `mcl_banners` for an example.
|
||||
|
||||
Tools can have these fields:
|
||||
* `_mcl_diggroups`: Specifies the digging groups that a tool can dig and how
|
||||
efficiently. See `_mcl_autogroup` for more information.
|
||||
|
||||
All nodes can have these fields:
|
||||
|
||||
* `_mcl_hardness`: Hardness of the block, ranges from 0 to infinity (represented by -1). Determines digging times. Default: 0
|
||||
|
|
|
@ -1,128 +0,0 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
eliasfleckenstein@web.de.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
452
CONTRIBUTING.md
|
@ -1,414 +1,96 @@
|
|||
# Contributing to MineClone2
|
||||
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`.
|
||||
|
||||
* Minecraft code (Name the source file and line, however DONT post any
|
||||
proprietary code). You can use
|
||||
[MCP](https://minecraft.fandom.com/wiki/Programs_and_editors/Mod_Coder_Pack)
|
||||
to decompile Minecraft or look at
|
||||
[Minestorm](https://github.com/Minestom/Minestom) code.
|
||||
* Testing things inside of Minecraft (Attach screenshots / video footage
|
||||
of the results)
|
||||
* [Official Minecraft Wiki](https://minecraft.fandom.com/wiki/Minecraft_Wiki)
|
||||
(Include a link to the specific page you used)
|
||||
## 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.
|
||||
|
|
141
CREDITS.md
|
@ -1,141 +0,0 @@
|
|||
# Credits
|
||||
|
||||
## Creator of MineClone
|
||||
* davedevils
|
||||
|
||||
## Creator of MineClone2
|
||||
* Wuzzy
|
||||
|
||||
## Maintainers
|
||||
* Fleckenstein
|
||||
* Nicu
|
||||
* kay27
|
||||
|
||||
## Developers
|
||||
* bzoss
|
||||
* AFCMS
|
||||
* epCode
|
||||
* ryvnf
|
||||
* iliekprogrammar
|
||||
* MysticTempest
|
||||
* Rootyjr
|
||||
* aligator
|
||||
* Code-Sploit
|
||||
* NO11
|
||||
* cora
|
||||
* jordan4ibanez
|
||||
|
||||
## Contributors
|
||||
* Laurent Rocher
|
||||
* HimbeerserverDE
|
||||
* TechDudie
|
||||
* Alexander Minges
|
||||
* ArTee3
|
||||
* ZeDique la Ruleta
|
||||
* pitchum
|
||||
* wuniversales
|
||||
* Bu-Gee
|
||||
* David McMackins II
|
||||
* Nicholas Niro
|
||||
* Wouters Dorian
|
||||
* Blue Blancmange
|
||||
* Jared Moody
|
||||
* Li0n
|
||||
* Midgard
|
||||
* Saku Laesvuori
|
||||
* Yukitty
|
||||
* ZedekThePD
|
||||
* aldum
|
||||
* dBeans
|
||||
* nickolas360
|
||||
* yutyo
|
||||
* Tianyang Zhang
|
||||
* j45
|
||||
* Marcin Serwin
|
||||
* erlehmann
|
||||
* E
|
||||
* Benjamin Schötz
|
||||
* Doloment
|
||||
* Sydney Gems
|
||||
* talamh
|
||||
* Emily2255
|
||||
* Emojigit
|
||||
* FinishedFragment
|
||||
* sfan5
|
||||
* Blue Blancmange
|
||||
* Jared Moody
|
||||
* SmallJoker
|
||||
* Sven792
|
||||
* aldum
|
||||
* Dieter44
|
||||
|
||||
## MineClone5
|
||||
* kay27
|
||||
* Debiankaios
|
||||
* epCode
|
||||
* NO11
|
||||
* j45
|
||||
|
||||
## Original Mod Authors
|
||||
* Wuzzy
|
||||
* Fleckenstein
|
||||
* BlockMen
|
||||
* TenPlus1
|
||||
* PilzAdam
|
||||
* ryvnf
|
||||
* stujones11
|
||||
* Arcelmi
|
||||
* celeron55
|
||||
* maikerumine
|
||||
* GunshipPenguin
|
||||
* Qwertymine3
|
||||
* Rochambeau
|
||||
* rubenwardy
|
||||
* stu
|
||||
* 4aiman
|
||||
* Kahrl
|
||||
* Krock
|
||||
* UgnilJoZ
|
||||
* lordfingle
|
||||
* 22i
|
||||
* bzoss
|
||||
* kilbith
|
||||
* xeranas
|
||||
* kddekadenz
|
||||
* sofar
|
||||
* 4Evergreen4
|
||||
* jordan4ibanez
|
||||
* paramat
|
||||
|
||||
## 3D Models
|
||||
* 22i
|
||||
* tobyplowy
|
||||
* epCode
|
||||
|
||||
## Textures
|
||||
* XSSheep
|
||||
* Wuzzy
|
||||
* kingoscargames
|
||||
* leorockway
|
||||
* xMrVizzy
|
||||
* yutyo
|
||||
* NO11
|
||||
* kay27
|
||||
|
||||
## Translations
|
||||
* Wuzzy
|
||||
* Rocher Laurent
|
||||
* wuniversales
|
||||
* kay27
|
||||
* pitchum
|
||||
* todoporlalibertad
|
||||
* Marcin Serwin
|
||||
|
||||
## Funders
|
||||
* 40W
|
||||
|
||||
## Special thanks
|
||||
* celeron55 for creating Minetest
|
||||
* Jordach for the jukebox music compilation from Big Freaking Dig
|
||||
* The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game
|
||||
* Notch and Jeb for being the major forces behind Minecraft
|
|
@ -149,7 +149,7 @@ These groups are used mostly for informational purposes
|
|||
* `trapdoor=2`: Open trapdoor
|
||||
* `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 (in development)
|
||||
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.
|
||||
|
|
BIN
menu/Logo.blend
BIN
menu/icon.png
Before Width: | Height: | Size: 547 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 419 KiB |
|
@ -1,8 +1,5 @@
|
|||
# This is a game specific minetest.conf file, do not edit
|
||||
|
||||
# If any of these settings are set in your minetest.conf file in ~/.minetest (Linux) or in the root directory of the game (Run in place/Windows)
|
||||
# They will override these settings
|
||||
|
||||
# Basic game rules
|
||||
time_speed = 72
|
||||
|
||||
|
@ -36,10 +33,10 @@ mgvalleys_spflags = noaltitude_chill,noaltitude_dry,nohumid_rivers,vary_river_de
|
|||
keepInventory = false
|
||||
|
||||
# Performance settings
|
||||
dedicated_server_step = 0.05 #tick rate
|
||||
# abm_interval = 0.25
|
||||
# max_objects_per_block = 4096
|
||||
# max_packets_per_iteration = 10096
|
||||
dedicated_server_step = 0.001
|
||||
abm_interval = 0.25
|
||||
max_objects_per_block = 4096
|
||||
max_packets_per_iteration = 10096
|
||||
|
||||
# Clientmodding to support official client
|
||||
enable_client_modding = true
|
||||
|
|
|
@ -4,11 +4,6 @@ Specifically, this mod has 2 purposes:
|
|||
1) Automatically adding the group “solid” for blocks considered “solid” in Minecraft.
|
||||
2) Generating digging time group for all nodes based on node metadata (it's complicated)
|
||||
|
||||
This mod also requires another mod called “mcl_autogroup” to function properly.
|
||||
“mcl_autogroup” exposes the API used to register digging groups, while this mod
|
||||
uses those digging groups to set the digging time groups for all the nodes and
|
||||
tools.
|
||||
|
||||
See init.lua for more infos.
|
||||
|
||||
The leading underscore in the name “_mcl_autogroup” was added to force Minetest to load this mod as late as possible.
|
||||
|
|
|
@ -1,365 +1,169 @@
|
|||
--[[
|
||||
This mod implements a HACK to make 100% sure the digging times of all tools
|
||||
match Minecraft's perfectly. The digging times system of Minetest is very
|
||||
different, so this weird group trickery has to be used. In Minecraft, each
|
||||
block has a hardness and the actual Minecraft digging time is determined by
|
||||
this:
|
||||
--[[ Mining times. Yeah, mining times … Alright, this is going to be FUN!
|
||||
|
||||
This mod does include a HACK to make 100% sure the digging times of all tools match Minecraft's perfectly.
|
||||
The digging times system of Minetest is very different, so this weird group trickery has to be used.
|
||||
In Minecraft, each block has a hardness and the actual Minecraft digging time is determined by this:
|
||||
1) The block's hardness
|
||||
2) The tool being used (the tool speed and its efficiency level)
|
||||
3) Whether the tool is considered as "eligible" for the block
|
||||
2) The tool being used
|
||||
3) Whether the tool is considered as “eligible” for the block
|
||||
(e.g. only diamond pick eligible for obsidian)
|
||||
See Minecraft Wiki <http://minecraft.gamepedia.com/Minecraft_Wiki> for more information.
|
||||
|
||||
See Minecraft Wiki <http://minecraft.gamepedia.com/Minecraft_Wiki> for more
|
||||
information.
|
||||
In MineClone 2, all diggable node have the hardness set in the custom field “_mcl_hardness” (0 by default).
|
||||
The nodes are also required to specify the “eligible” tools in groups like “pickaxey”, “shovely”, etc.
|
||||
This mod then calculates the real digging time based on the node meta data. The real digging times
|
||||
are then added into mcl_autogroup.digtimes where the table indices are group rating and the values are the
|
||||
digging times in seconds. These digging times can be then added verbatim into the tool definitions.
|
||||
|
||||
How the mod is used
|
||||
===================
|
||||
Example:
|
||||
mcl_autogroup.digtimes.pickaxey_dig_diamond[1] = 0.2
|
||||
|
||||
In MineClone 2, all diggable nodes have the hardness set in the custom field
|
||||
"_mcl_hardness" (0 by default). These values are used together with digging
|
||||
groups by this mod to create the correct digging times for nodes. Digging
|
||||
groups are registered using the following code:
|
||||
→ This means that when a node has been assigned the group “pickaxey_dig_diamond=1”, it can be dug by the
|
||||
diamond pickaxe in 0.2 seconds.
|
||||
|
||||
mcl_autogroup.register_diggroup("shovely")
|
||||
mcl_autogroup.register_diggroup("pickaxey", {
|
||||
levels = { "wood", "gold", "stone", "iron", "diamond" }
|
||||
})
|
||||
|
||||
The first line registers a simple digging group. The second line registers a
|
||||
digging group with 5 different levels (in this case one for each material of a
|
||||
pickaxes).
|
||||
|
||||
Nodes indicate that they belong to a particular digging group by being member of
|
||||
the digging group in their node definition. "mcl_core:dirt" for example has
|
||||
shovely=1 in its groups. If the digging group has multiple levels the value of
|
||||
the group indicates which digging level the node requires.
|
||||
"mcl_core:stone_with_gold" for example has pickaxey=4 because it requires a
|
||||
pickaxe of level 4 be mined.
|
||||
This strange setup with mcl_autogroup has been done to minimize the amount of required digging times
|
||||
a single tool needs to use. If this is not being done, the loading time will increase considerably
|
||||
(>10s).
|
||||
|
||||
For tools to be able to dig nodes of digging groups they need to use the have
|
||||
the custom field "_mcl_diggroups" function to get the groupcaps. The value of
|
||||
this field is a table which defines which groups the tool can dig and how
|
||||
efficiently.
|
||||
]]
|
||||
|
||||
_mcl_diggroups = {
|
||||
handy = { speed = 1, level = 1, uses = 0 },
|
||||
pickaxey = { speed = 1, level = 0, uses = 0 },
|
||||
}
|
||||
local materials = { "wood", "gold", "stone", "iron", "diamond" }
|
||||
local basegroups = { "pickaxey", "axey", "shovely" }
|
||||
local minigroups = { "handy", "shearsy", "swordy", "shearsy_wool", "swordy_cobweb" }
|
||||
local divisors = {
|
||||
["wood"] = 2,
|
||||
["gold"] = 12,
|
||||
["stone"] = 4,
|
||||
["iron"] = 6,
|
||||
["diamond"] = 8,
|
||||
["handy"] = 1,
|
||||
["shearsy"] = 15,
|
||||
["swordy"] = 1.5,
|
||||
["shearsy_wool"] = 5,
|
||||
["swordy_cobweb"] = 15,
|
||||
}
|
||||
local max_efficiency_level = 5
|
||||
|
||||
The "uses" field indicate how many uses (0 for infinite) a tool has when used on
|
||||
the specified digging group. The "speed" field is a multiplier to the dig speed
|
||||
on that digging group.
|
||||
mcl_autogroup = {}
|
||||
mcl_autogroup.digtimes = {}
|
||||
mcl_autogroup.creativetimes = {} -- Copy of digtimes, except that all values are 0. Used for creative mode
|
||||
|
||||
The "level" field indicates which levels of the group the tool can harvest. A
|
||||
level of 0 means that the tool cannot harvest blocks of that node. A level of 1
|
||||
or above means that the tool can harvest nodes with that level or below. See
|
||||
"mcl_tools/init.lua" for examples on how "_mcl_diggroups" is used in practice.
|
||||
|
||||
Information about the mod
|
||||
=========================
|
||||
|
||||
The mod is split up into two parts, mcl_autogroup and _mcl_autogroup.
|
||||
mcl_autogroup contains the API functions used to register custom digging groups.
|
||||
_mcl_autogroup contains most of the code. The leading underscore in the name
|
||||
"_mcl_autogroup" is used to force Minetest to load that part of the mod as late
|
||||
as possible. Minetest loads mods in reverse alphabetical order.
|
||||
|
||||
This also means that it is very important that no mod adds _mcl_autogroup as a
|
||||
dependency.
|
||||
--]]
|
||||
|
||||
assert(minetest.get_modpath("mcl_autogroup"), "This mod requires the mod mcl_autogroup to function")
|
||||
|
||||
-- Returns a table containing the unique "_mcl_hardness" for nodes belonging to
|
||||
-- each diggroup.
|
||||
local function get_hardness_values_for_groups()
|
||||
local maps = {}
|
||||
local values = {}
|
||||
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
|
||||
maps[g] = {}
|
||||
values[g] = {}
|
||||
end
|
||||
|
||||
for _, ndef in pairs(minetest.registered_nodes) do
|
||||
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
|
||||
if ndef.groups[g] then
|
||||
maps[g][ndef._mcl_hardness or 0] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for g, map in pairs(maps) do
|
||||
for k, _ in pairs(map) do
|
||||
table.insert(values[g], k)
|
||||
end
|
||||
end
|
||||
|
||||
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
|
||||
table.sort(values[g])
|
||||
end
|
||||
return values
|
||||
end
|
||||
|
||||
-- Returns a table containing a table indexed by "_mcl_hardness_value" to get
|
||||
-- its index in the list of unique hardnesses for each diggroup.
|
||||
local function get_hardness_lookup_for_groups(hardness_values)
|
||||
local map = {}
|
||||
for g, values in pairs(hardness_values) do
|
||||
map[g] = {}
|
||||
for k, v in pairs(values) do
|
||||
map[g][v] = k
|
||||
end
|
||||
end
|
||||
return map
|
||||
end
|
||||
|
||||
-- Array of unique hardness values for each group which affects dig time.
|
||||
local hardness_values = get_hardness_values_for_groups()
|
||||
|
||||
-- Map indexed by hardness values which return the index of that value in
|
||||
-- hardness_value. Used for quick lookup.
|
||||
local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
|
||||
|
||||
--[[local function compute_creativetimes(group)
|
||||
local creativetimes = {}
|
||||
|
||||
for index, hardness in pairs(hardness_values[group]) do
|
||||
table.insert(creativetimes, 0)
|
||||
end
|
||||
|
||||
return creativetimes
|
||||
end]]
|
||||
|
||||
-- Get the list of digging times for using a specific tool on a specific
|
||||
-- diggroup.
|
||||
--
|
||||
-- Parameters:
|
||||
-- group - the group which it is digging
|
||||
-- can_harvest - if the tool can harvest the block
|
||||
-- speed - dig speed multiplier for tool (default 1)
|
||||
-- efficiency - efficiency level for the tool if applicable
|
||||
local function get_digtimes(group, can_harvest, speed, efficiency)
|
||||
local speed = speed or 1
|
||||
if efficiency then
|
||||
speed = speed + efficiency * efficiency + 1
|
||||
end
|
||||
|
||||
local digtimes = {}
|
||||
|
||||
for index, hardness in pairs(hardness_values[group]) do
|
||||
local digtime = (hardness or 0) / speed
|
||||
if can_harvest then
|
||||
digtime = digtime * 1.5
|
||||
else
|
||||
digtime = digtime * 5
|
||||
end
|
||||
|
||||
if digtime <= 0.05 then
|
||||
digtime = 0
|
||||
else
|
||||
digtime = math.ceil(digtime * 20) / 20
|
||||
end
|
||||
table.insert(digtimes, digtime)
|
||||
end
|
||||
|
||||
return digtimes
|
||||
end
|
||||
|
||||
-- Get one groupcap field for using a specific tool on a specific group.
|
||||
local function get_groupcap(group, can_harvest, multiplier, efficiency, uses)
|
||||
return {
|
||||
times = get_digtimes(group, can_harvest, multiplier, efficiency),
|
||||
uses = uses,
|
||||
maxlevel = 0,
|
||||
}
|
||||
end
|
||||
|
||||
-- Add the groupcaps from a field in "_mcl_diggroups" to the groupcaps of a
|
||||
-- tool.
|
||||
local function add_groupcaps(toolname, groupcaps, groupcaps_def, efficiency)
|
||||
if not groupcaps_def then
|
||||
return
|
||||
end
|
||||
|
||||
for g, capsdef in pairs(groupcaps_def) do
|
||||
local mult = capsdef.speed or 1
|
||||
local uses = capsdef.uses
|
||||
local def = mcl_autogroup.registered_diggroups[g]
|
||||
local max_level = def.levels and #def.levels or 1
|
||||
|
||||
assert(capsdef.level, toolname .. ' is missing level for ' .. g)
|
||||
local level = math.min(capsdef.level, max_level)
|
||||
|
||||
if def.levels then
|
||||
groupcaps[g .. "_dig_default"] = get_groupcap(g, false, mult, efficiency, uses)
|
||||
if level > 0 then
|
||||
groupcaps[g .. "_dig_" .. def.levels[level]] = get_groupcap(g, true, mult, efficiency, uses)
|
||||
end
|
||||
else
|
||||
groupcaps[g .. "_dig"] = get_groupcap(g, level > 0, mult, efficiency, uses)
|
||||
for m=1, #materials do
|
||||
for g=1, #basegroups do
|
||||
mcl_autogroup.digtimes[basegroups[g].."_dig_"..materials[m]] = {}
|
||||
mcl_autogroup.creativetimes[basegroups[g].."_dig_"..materials[m]] = {}
|
||||
for e=1, max_efficiency_level do
|
||||
mcl_autogroup.digtimes[basegroups[g].."_dig_"..materials[m].."_efficiency_"..e] = {}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Checks if the given node would drop its useful drop if dug by a given tool.
|
||||
-- Returns true if it will yield its useful drop, false otherwise.
|
||||
function mcl_autogroup.can_harvest(nodename, toolname)
|
||||
local ndef = minetest.registered_nodes[nodename]
|
||||
|
||||
if not ndef then
|
||||
return false
|
||||
for g=1, #minigroups do
|
||||
mcl_autogroup.digtimes[minigroups[g].."_dig"] = {}
|
||||
mcl_autogroup.creativetimes[minigroups[g].."_dig"] = {}
|
||||
for e=1, max_efficiency_level do
|
||||
mcl_autogroup.digtimes[minigroups[g].."_dig_efficiency_"..e] = {}
|
||||
mcl_autogroup.creativetimes[minigroups[g].."_dig_efficiency_"..e] = {}
|
||||
end
|
||||
|
||||
if minetest.get_item_group(nodename, "dig_immediate") >= 2 then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Check if it can be dug by tool
|
||||
local tdef = minetest.registered_tools[toolname]
|
||||
if tdef and tdef._mcl_diggroups then
|
||||
for g, gdef in pairs(tdef._mcl_diggroups) do
|
||||
if ndef.groups[g] then
|
||||
if ndef.groups[g] <= gdef.level then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if it can be dug by hand
|
||||
local tdef = minetest.registered_tools[""]
|
||||
if tdef then
|
||||
for g, gdef in pairs(tdef._mcl_diggroups) do
|
||||
if ndef.groups[g] then
|
||||
if ndef.groups[g] <= gdef.level then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- Get one groupcap field for using a specific tool on a specific group.
|
||||
--[[local function get_groupcap(group, can_harvest, multiplier, efficiency, uses)
|
||||
return {
|
||||
times = get_digtimes(group, can_harvest, multiplier, efficiency),
|
||||
uses = uses,
|
||||
maxlevel = 0,
|
||||
}
|
||||
end]]
|
||||
|
||||
-- Returns the tool_capabilities from a tool definition or a default set of
|
||||
-- tool_capabilities
|
||||
local function get_tool_capabilities(tdef)
|
||||
if tdef.tool_capabilities then
|
||||
return tdef.tool_capabilities
|
||||
end
|
||||
|
||||
-- If the damage group and punch interval from hand is not included,
|
||||
-- then the user will not be able to attack with the tool.
|
||||
local hand_toolcaps = minetest.registered_tools[""].tool_capabilities
|
||||
return {
|
||||
full_punch_interval = hand_toolcaps.full_punch_interval,
|
||||
damage_groups = hand_toolcaps.damage_groups
|
||||
}
|
||||
end
|
||||
|
||||
-- Get the groupcaps for a tool. This function returns "groupcaps" table of
|
||||
-- digging which should be put in the "tool_capabilities" of the tool definition
|
||||
-- or in the metadata of an enchanted tool.
|
||||
--
|
||||
-- Parameters:
|
||||
-- toolname - Name of the tool being enchanted (like "mcl_tools:diamond_pickaxe")
|
||||
-- efficiency - The efficiency level the tool is enchanted with (default 0)
|
||||
--
|
||||
-- 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.
|
||||
function mcl_autogroup.get_groupcaps(toolname, efficiency)
|
||||
local tdef = minetest.registered_tools[toolname]
|
||||
local groupcaps = table.copy(get_tool_capabilities(tdef).groupcaps or {})
|
||||
add_groupcaps(toolname, groupcaps, tdef._mcl_diggroups, efficiency)
|
||||
return groupcaps
|
||||
end
|
||||
|
||||
-- Get the wear from using a tool on a digging group.
|
||||
--
|
||||
-- Parameters
|
||||
-- toolname - Name of the tool used
|
||||
-- diggroup - The name of the diggroup the tool is used on
|
||||
--
|
||||
-- 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.
|
||||
function mcl_autogroup.get_wear(toolname, diggroup)
|
||||
local tdef = minetest.registered_tools[toolname]
|
||||
local uses = tdef._mcl_diggroups[diggroup].uses
|
||||
return math.ceil(65535 / uses)
|
||||
end
|
||||
|
||||
local function overwrite()
|
||||
local overwrite = function()
|
||||
for nname, ndef in pairs(minetest.registered_nodes) do
|
||||
local groups_changed = false
|
||||
local newgroups = table.copy(ndef.groups)
|
||||
if (nname ~= "ignore" and ndef.diggable) then
|
||||
-- Automatically assign the "solid" group for solid nodes
|
||||
-- Automatically assign the “solid” group for solid nodes
|
||||
if (ndef.walkable == nil or ndef.walkable == true)
|
||||
and (ndef.collision_box == nil or ndef.collision_box.type == "regular")
|
||||
and (ndef.node_box == nil or ndef.node_box.type == "regular")
|
||||
and (ndef.groups.not_solid == 0 or ndef.groups.not_solid == nil) then
|
||||
newgroups.solid = 1
|
||||
groups_changed = true
|
||||
end
|
||||
-- Automatically assign the "opaque" group for opaque nodes
|
||||
-- Automatically assign the “opaque” group for opaque nodes
|
||||
if (not (ndef.paramtype == "light" or ndef.sunlight_propagates)) and
|
||||
(ndef.groups.not_opaque == 0 or ndef.groups.not_opaque == nil) then
|
||||
newgroups.opaque = 1
|
||||
groups_changed = true
|
||||
end
|
||||
|
||||
--local creative_breakable = false
|
||||
local function calculate_group(hardness, material, diggroup, newgroups, actual_rating, expected_rating, efficiency)
|
||||
local time, validity_factor
|
||||
if actual_rating >= expected_rating then
|
||||
-- Valid tool
|
||||
validity_factor = 1.5
|
||||
else
|
||||
-- Wrong tool (higher digging time)
|
||||
validity_factor = 5
|
||||
end
|
||||
local speed_multiplier = divisors[material]
|
||||
if efficiency then
|
||||
speed_multiplier = speed_multiplier + efficiency * efficiency + 1
|
||||
end
|
||||
time = (hardness * validity_factor) / speed_multiplier
|
||||
if time <= 0.05 then
|
||||
time = 0
|
||||
else
|
||||
time = math.ceil(time * 20) / 20
|
||||
end
|
||||
table.insert(mcl_autogroup.digtimes[diggroup], time)
|
||||
if not efficiency then
|
||||
table.insert(mcl_autogroup.creativetimes[diggroup], 0)
|
||||
end
|
||||
newgroups[diggroup] = #mcl_autogroup.digtimes[diggroup]
|
||||
return newgroups
|
||||
end
|
||||
|
||||
-- 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
|
||||
local index = hardness_lookup[g][ndef._mcl_hardness or 0]
|
||||
if ndef.groups[g] then
|
||||
if gdef.levels then
|
||||
newgroups[g .. "_dig_default"] = index
|
||||
-- Hack in digging times
|
||||
local hardness = ndef._mcl_hardness
|
||||
if not hardness then
|
||||
hardness = 0
|
||||
end
|
||||
|
||||
for i = ndef.groups[g], #gdef.levels do
|
||||
newgroups[g .. "_dig_" .. gdef.levels[i]] = index
|
||||
-- Handle pickaxey, axey and shovely
|
||||
for _, basegroup in pairs(basegroups) do
|
||||
if (hardness ~= -1 and ndef.groups[basegroup]) then
|
||||
for g=1,#materials do
|
||||
local diggroup = basegroup.."_dig_"..materials[g]
|
||||
newgroups = calculate_group(hardness, materials[g], diggroup, newgroups, g, ndef.groups[basegroup])
|
||||
for e=1,max_efficiency_level do
|
||||
newgroups = calculate_group(hardness, materials[g], diggroup .. "_efficiency_" .. e, newgroups, g, ndef.groups[basegroup], e)
|
||||
end
|
||||
else
|
||||
newgroups[g .. "_dig"] = index
|
||||
groups_changed = true
|
||||
end
|
||||
end
|
||||
end
|
||||
for m=1, #minigroups do
|
||||
local minigroup = minigroups[m]
|
||||
if hardness ~= -1 then
|
||||
local diggroup = minigroup.."_dig"
|
||||
-- actual rating
|
||||
local ar = ndef.groups[minigroup]
|
||||
if ar == nil then
|
||||
ar = 0
|
||||
end
|
||||
if (minigroup == "handy")
|
||||
or
|
||||
(ndef.groups.shearsy_wool and minigroup == "shearsy_wool" and ndef.groups.wool)
|
||||
or
|
||||
(ndef.groups.swordy_cobweb and minigroup == "swordy_cobweb" and nname == "mcl_core:cobweb")
|
||||
or
|
||||
(ndef.groups[minigroup] and minigroup ~= "swordy_cobweb" and minigroup ~= "shearsy_wool") then
|
||||
newgroups = calculate_group(hardness, minigroup, diggroup, newgroups, ar, 1)
|
||||
for e=1,max_efficiency_level do
|
||||
newgroups = calculate_group(hardness, minigroup, diggroup .. "_efficiency_" .. e, newgroups, ar, 1, e)
|
||||
end
|
||||
groups_changed = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Automatically assign the node to the
|
||||
-- creative_breakable group if it belongs to any digging
|
||||
-- group.
|
||||
newgroups["creative_breakable"] = 1
|
||||
|
||||
minetest.override_item(nname, {
|
||||
groups = newgroups
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
for tname, tdef in pairs(minetest.registered_tools) do
|
||||
-- Assign groupcaps for digging the registered digging groups
|
||||
-- depending on the _mcl_diggroups in the tool definition
|
||||
if tdef._mcl_diggroups then
|
||||
local toolcaps = table.copy(get_tool_capabilities(tdef))
|
||||
toolcaps.groupcaps = mcl_autogroup.get_groupcaps(tname)
|
||||
|
||||
minetest.override_item(tname, {
|
||||
tool_capabilities = toolcaps
|
||||
})
|
||||
if groups_changed then
|
||||
minetest.override_item(nname, {
|
||||
groups = newgroups
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
name = _mcl_autogroup
|
||||
author = ryvnf
|
||||
author = Wuzzy
|
||||
description = MineClone 2 core mod which automatically adds groups to all items. Very important for digging times.
|
||||
|
|
|
@ -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
|
||||
--[[
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
local Object = {}
|
||||
|
||||
-- Define a getter that caches the result for the next time it is called
|
||||
-- This is a static method (self = the class); in this class system static methods start with __ by convention
|
||||
function Object:__cache_getter(name, func)
|
||||
-- cache key: prevent overriding the getter function itself
|
||||
local key = "_" .. name
|
||||
-- add a function to the class
|
||||
self[name] = function(self)
|
||||
-- check if the value is present in the cache
|
||||
local value = self[key]
|
||||
|
||||
-- `== nil` instead of `not value` to allow caching boolean values
|
||||
if value == nil then
|
||||
-- call the getter function
|
||||
value = func(self)
|
||||
end
|
||||
|
||||
-- store result in cache
|
||||
self[key] = value
|
||||
|
||||
-- return result
|
||||
return value
|
||||
end
|
||||
end
|
||||
|
||||
-- Define a getter / setter
|
||||
-- If no argument is specified, it will act as a getter, else as a setter
|
||||
-- The specified function MUST return the new value, if it returns nil, nil will be used as new value
|
||||
-- Optionally works in combination with a previously defined cache getter and only really makes sense in that context
|
||||
function Object:__setter(name, func)
|
||||
-- since the function is overridden, we need to store the old one in case a cache getter is defined
|
||||
local cache_getter = self[name]
|
||||
-- use same key as cache getter to modify getter cache if present
|
||||
local key = "_" .. name
|
||||
|
||||
self[name] = function(self, new)
|
||||
-- check whether an argument was specified
|
||||
if new == nil then
|
||||
if cache_getter then
|
||||
-- call the cache getter if present
|
||||
return cache_getter(self)
|
||||
else
|
||||
-- return the value else
|
||||
return self[key]
|
||||
end
|
||||
end
|
||||
|
||||
-- call the setter and set the new value to the result
|
||||
self[key] = func(self, new)
|
||||
end
|
||||
end
|
||||
|
||||
-- Define a comparator function
|
||||
-- Acts like a setter, except that it does not set the new value but rather compares the present and specified values and returns whether they are equal or not
|
||||
-- Incompatible with setter
|
||||
-- The function is optional. The == operator is used else.
|
||||
function Object:__comparator(name, func)
|
||||
local cache_getter = self[name]
|
||||
local key = "_" .. name
|
||||
|
||||
self[name] = function(self, expected)
|
||||
-- the current value is needed everytime, no matter whether there is an argument or not
|
||||
local actual
|
||||
|
||||
if cache_getter then
|
||||
-- call the cache getter if present
|
||||
actual = cache_getter(self)
|
||||
else
|
||||
-- use the value else
|
||||
actual = self[key]
|
||||
end
|
||||
|
||||
-- act as a getter if there is no argument
|
||||
if expected == nil then
|
||||
return actual
|
||||
end
|
||||
|
||||
if func then
|
||||
-- if a function as specified, call it
|
||||
return func(actual, expected)
|
||||
else
|
||||
-- else, use the == operator to compare the expected value to the actual
|
||||
return actual == expected
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Override an already existing function in a way that the old function is called
|
||||
-- If nil is returned, the old function is called. Else the return value is returned. (Only the first return value is taken into concern here, multiple are supported tho)
|
||||
-- This works even if it is applied to the instance of a class when the function is defined by the class
|
||||
-- It also works with overriding functions that are located in superclasses
|
||||
function Object:__override(name, func)
|
||||
-- store the old function
|
||||
local old_func = self[name]
|
||||
|
||||
-- redefine the function with variable arguments
|
||||
self[name] = function(...)
|
||||
-- call the new function and store the return values in a table
|
||||
local rvals = {func(...)}
|
||||
-- if nil was returned, fall back to the old function
|
||||
if rvals[1] == nil then
|
||||
-- if present, call the return function with the values the new function returned (converted back to a tuple)
|
||||
return old_func(...)
|
||||
else
|
||||
-- return the values from the new function else
|
||||
return unpack(rvals)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Works like override except that the new function does not modify the output of the old function but rather the input
|
||||
-- The new function can decide with what arguments by returing them, including the `self` reference
|
||||
-- If the "self" arg is not returned the old function is not called
|
||||
-- Note that this way the new function cannot change the return value of the old function
|
||||
function Object:__pipe(name, func)
|
||||
local old_func = self[name]
|
||||
|
||||
self[name] = function(self, ...)
|
||||
local rvals = {func(self, ...)}
|
||||
-- check if self was returned properly
|
||||
if rvals[1] then
|
||||
-- if present, call the return function with the values the new function returned (converted back to a tuple)
|
||||
return old_func(unpack(rvals))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Make class available as table to distribute the Object table
|
||||
class = setmetatable({Object = Object}, {
|
||||
-- Create a new class by calling class() with an optional superclass argument
|
||||
__call = function(super)
|
||||
return setmetatable({}, {
|
||||
-- Create a new instance of the class when the class is called
|
||||
__call = function(_class, ...)
|
||||
-- Check whether the first argument is an instance of the class
|
||||
-- If that is the case, just return it - this is to allow "making sure something is the instance of a class" by calling the constructor
|
||||
local argtbl = {...}
|
||||
local first_arg = args[1]
|
||||
if first_arg and type(first_arg) == "table" and inst.CLASS = _class then
|
||||
return inst
|
||||
end
|
||||
-- set the metatable and remember which class the object belongs to
|
||||
local instance = setmetatable({CLASS = _class}, {
|
||||
__index = _class,
|
||||
})
|
||||
-- call the constructor if present
|
||||
if instance.constructor then
|
||||
instance:constructor(...)
|
||||
end
|
||||
-- return the created instance
|
||||
return instance
|
||||
end,
|
||||
-- Object as superclass of all classes that dont have a different one
|
||||
__index = super or Object,
|
||||
})
|
||||
end
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
name = class
|
||||
author = Fleckenstein
|
||||
description = A class system for MineClone2
|
|
@ -1,23 +0,0 @@
|
|||
# controls
|
||||
|
||||
## controls.players
|
||||
Table containing player controls at runtime.
|
||||
WARNING: Never use this table in writing
|
||||
|
||||
## controls.register_on_press(func)
|
||||
Register a function that will be executed with (player, keyname) every time a player press a key.
|
||||
|
||||
## controls.registered_on_press
|
||||
Table containing functions registered with controls.register_on_press().
|
||||
|
||||
## controls.register_on_release(func)
|
||||
Register a function that will be executed with (player, keyname, clock_from_last_press) every time a player release a key.
|
||||
|
||||
## controls.registered_on_release
|
||||
Table containing functions registered with controls.register_on_release().
|
||||
|
||||
## controls.register_on_hold(func)
|
||||
Register a function that will be executed with (player, keyname, clock_from_start_hold) every time a player hold a key.
|
||||
|
||||
## controls.registered_on_hold
|
||||
Table containing functions registered with controls.register_on_hold().
|
|
@ -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,45 +0,0 @@
|
|||
# flowlib
|
||||
Simple flow functions.
|
||||
|
||||
## flowlib.is_touching(realpos, nodepos, radius)
|
||||
Return true if a sphere of <radius> at <realpos> collide with node at <nodepos>.
|
||||
* realpos: position
|
||||
* nodepos: position
|
||||
* radius: number
|
||||
|
||||
## flowlib.is_water(pos)
|
||||
Return true if node at <pos> is water, false overwise.
|
||||
* pos: position
|
||||
|
||||
## flowlib.node_is_water(node)
|
||||
Return true if <node> is water, false overwise.
|
||||
* node: node
|
||||
|
||||
## flowlib.is_lava(pos)
|
||||
Return true if node at <pos> is lava, false overwise.
|
||||
* pos: position
|
||||
|
||||
## flowlib.node_is_lava(node)
|
||||
Return true if <node> is lava, false overwise.
|
||||
* node: node
|
||||
|
||||
## flowlib.is_liquid(pos)
|
||||
Return true if node at <pos> is liquid, false overwise.
|
||||
* pos: position
|
||||
|
||||
## flowlib.node_is_liquid(node)
|
||||
Return true if <node> is liquid, false overwise.
|
||||
* node: node
|
||||
|
||||
## flowlib.quick_flow(pos, node)
|
||||
Return direction where the water is flowing (to be use to push mobs, items...).
|
||||
* pos: position
|
||||
* node: node
|
||||
|
||||
## flowlib.move_centre(pos, realpos, node, radius)
|
||||
Return the pos of the nearest not water block near from <pos> in a sphere of <radius> at <realpos>.
|
||||
WARNING: This function is never used in mcl2, use at your own risk. The informations described here may be wrong.
|
||||
* pos: position
|
||||
* realpos: position, position of the entity
|
||||
* node: node
|
||||
* radius: number
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
# mcl_autogroup
|
||||
This mod emulate digging times from mc.
|
||||
|
||||
## mcl_autogroup.can_harvest(nodename, toolname)
|
||||
Return true if <nodename> can be dig with <toolname>.
|
||||
* nodename: string, valid nodename
|
||||
* toolname: (optional) string, valid toolname
|
||||
|
||||
## mcl_autogroup.get_groupcaps(toolname, efficiency)
|
||||
This function is used to calculate diggroups for tools.
|
||||
WARNING: This function can only be called after mod initialization.
|
||||
* toolname: string, name of the tool being enchanted (like "mcl_tools:diamond_pickaxe")
|
||||
* efficiency: (optional) integer, the efficiency level the tool is enchanted with (default 0)
|
||||
|
||||
## mcl_autogroup.get_wear(toolname, diggroup)
|
||||
Return the max wear of <toolname> with <diggroup>
|
||||
WARNING: This function can only be called after mod initialization.
|
||||
* toolname: string, name of the tool used
|
||||
* diggroup: string, the name of the diggroup the tool is used on
|
||||
|
||||
## mcl_autogroup.register_diggroup(group, def)
|
||||
* group: string, name of the group to register as a digging group
|
||||
* def: (optional) table, table with information about the diggroup (defaults to {} if unspecified)
|
||||
* level: (optional) string, if specified it is an array containing the names of the different digging levels the digging group supports
|
||||
|
||||
## mcl_autogroup.registered_diggroups
|
||||
List of registered diggroups, indexed by name.
|
|
@ -1,28 +0,0 @@
|
|||
--[[
|
||||
This is one part of a mod to replicate the digging times from Minecraft. This
|
||||
part only exposes a function to register digging groups. The rest of the mod is
|
||||
implemented and documented in the _mcl_autogroup.
|
||||
|
||||
The mod is split up into two parts, mcl_autogroup and _mcl_autogroup.
|
||||
mcl_autogroup contains the API functions used to register custom digging groups.
|
||||
_mcl_autogroup contains most of the code. The leading underscore in the name
|
||||
"_mcl_autogroup" is used to force Minetest to load that part of the mod as late
|
||||
as possible. Minetest loads mods in reverse alphabetical order.
|
||||
--]]
|
||||
mcl_autogroup = {}
|
||||
mcl_autogroup.registered_diggroups = {}
|
||||
|
||||
assert(minetest.get_modpath("_mcl_autogroup"), "This mod requires the mod _mcl_autogroup to function")
|
||||
|
||||
-- Register a group as a digging group.
|
||||
--
|
||||
-- Parameters:
|
||||
-- group - Name of the group to register as a digging group
|
||||
-- def - Table with information about the diggroup (defaults to {} if unspecified)
|
||||
--
|
||||
-- Values in def:
|
||||
-- level - If specified it is an array containing the names of the different
|
||||
-- digging levels the digging group supports.
|
||||
function mcl_autogroup.register_diggroup(group, def)
|
||||
mcl_autogroup.registered_diggroups[group] = def or {}
|
||||
end
|
|
@ -1,3 +0,0 @@
|
|||
name = mcl_autogroup
|
||||
author = ryvnf
|
||||
description = MineClone 2 core mod which automatically adds groups to all items. Very important for digging times.
|
|
@ -0,0 +1,298 @@
|
|||
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(obj, "float", "burn_time") > 0
|
||||
end
|
||||
|
||||
function mcl_burning.is_affected_by_rain(obj)
|
||||
return mcl_weather and mcl_weather.get_weather() == "rain" and mcl_weather.is_outdoor(obj:get_pos())
|
||||
end
|
||||
|
||||
function mcl_burning.get_collisionbox(obj, smaller)
|
||||
local box = obj:get_properties().collisionbox
|
||||
local minp, maxp = vector.new(box[1], box[2], box[3]), vector.new(box[4], box[5], box[6])
|
||||
if smaller then
|
||||
local s_vec = vector.new(0.1, 0.1, 0.1)
|
||||
minp = vector.add(minp, s_vec)
|
||||
maxp = vector.subtract(maxp, s_vec)
|
||||
end
|
||||
return minp, maxp
|
||||
end
|
||||
|
||||
function mcl_burning.get_touching_nodes(obj, nodenames)
|
||||
local pos = obj:get_pos()
|
||||
local 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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_burning.set_on_fire(obj, burn_time, reason)
|
||||
local luaentity = obj:get_luaentity()
|
||||
if luaentity and luaentity.fire_resistant then
|
||||
return
|
||||
end
|
||||
|
||||
local old_burn_time = mcl_burning.get(obj, "float", "burn_time")
|
||||
local max_fire_prot_lvl = 0
|
||||
|
||||
if obj:is_player() then
|
||||
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
|
||||
|
||||
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 = "fire_basic_flame.png",
|
||||
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 sound_id = mcl_burning.get(obj, "int", "sound_id") - 1
|
||||
minetest.sound_stop(sound_id)
|
||||
|
||||
if obj:is_player() then
|
||||
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)
|
||||
local burn_time = mcl_burning.get(obj, "float", "burn_time") - dtime
|
||||
|
||||
if burn_time <= 0 then
|
||||
mcl_burning.extinguish(obj)
|
||||
else
|
||||
mcl_burning.set(obj, "float", "burn_time", burn_time)
|
||||
|
||||
local damage_timer = mcl_burning.get(obj, "float", "damage_timer") + dtime
|
||||
|
||||
if damage_timer >= 1 then
|
||||
damage_timer = 0
|
||||
mcl_burning.damage(obj)
|
||||
end
|
||||
|
||||
mcl_burning.set(obj, "float", "damage_timer", damage_timer)
|
||||
end
|
||||
|
||||
mcl_burning.catch_fire_tick(obj, dtime)
|
||||
end
|
||||
|
||||
function mcl_burning.update_animation_frame(obj, fire_entity, animation_frame)
|
||||
local fire_texture = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. animation_frame
|
||||
local fire_HUD_texture = "mcl_burning_hud_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. animation_frame
|
||||
fire_entity:set_properties({textures = {"blank.png", "blank.png", fire_texture, fire_texture, fire_texture, fire_texture}})
|
||||
if obj:is_player() then
|
||||
local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1
|
||||
obj:hud_change(hud_id, "text", fire_HUD_texture)
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_burning.fire_entity_step(self, dtime)
|
||||
if self.removed then
|
||||
return
|
||||
end
|
||||
|
||||
local obj = self.object
|
||||
local parent = obj:get_attach()
|
||||
local do_remove
|
||||
|
||||
self.doing_step = true
|
||||
|
||||
if not parent or not mcl_burning.is_burning(parent) then
|
||||
do_remove = true
|
||||
else
|
||||
for _, other in pairs(minetest.get_objects_inside_radius(obj:get_pos(), 0)) do
|
||||
local luaentity = obj:get_luaentity()
|
||||
if luaentity and luaentity.name == "mcl_burning:fire" and not luaentity.doing_step and not luaentity.removed then
|
||||
do_remove = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.doing_step = false
|
||||
|
||||
if do_remove then
|
||||
self.removed = true
|
||||
obj:remove()
|
||||
return
|
||||
end
|
||||
|
||||
local animation_timer = self.animation_timer + dtime
|
||||
if animation_timer >= 0.015 then
|
||||
animation_timer = 0
|
||||
local animation_frame = self.animation_frame + 1
|
||||
if animation_frame > mcl_burning.animation_frames - 1 then
|
||||
animation_frame = 0
|
||||
end
|
||||
mcl_burning.update_animation_frame(parent, obj, animation_frame)
|
||||
self.animation_frame = animation_frame
|
||||
end
|
||||
self.animation_timer = animation_timer
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
local S = minetest.get_translator("mcl_burning")
|
||||
local modpath = minetest.get_modpath("mcl_burning")
|
||||
|
||||
mcl_burning = {
|
||||
animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8
|
||||
}
|
||||
|
||||
dofile(modpath .. "/api.lua")
|
||||
|
||||
minetest.register_entity("mcl_burning:fire", {
|
||||
initial_properties = {
|
||||
physical = false,
|
||||
collisionbox = {0, 0, 0, 0, 0, 0},
|
||||
visual = "cube",
|
||||
pointable = false,
|
||||
glow = -1,
|
||||
},
|
||||
|
||||
animation_frame = 0,
|
||||
animation_timer = 0,
|
||||
on_step = mcl_burning.fire_entity_step,
|
||||
})
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
for _, player in pairs(minetest.get_connected_players()) do
|
||||
mcl_burning.tick(player, dtime)
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_respawnplayer(function(player)
|
||||
mcl_burning.extinguish(player)
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
mcl_burning.set(player, "int", "hud_id")
|
||||
end)
|
|
@ -1,8 +0,0 @@
|
|||
# mcl_colors
|
||||
Mod providing global table containing legacity minecraft colors to be used in mods.
|
||||
|
||||
## mcl_colors.*
|
||||
Colors by upper name, in hex value.
|
||||
|
||||
## mcl_colors.background.*
|
||||
Background colors by upper name, in hex value.
|
|
@ -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
|
|
@ -0,0 +1,29 @@
|
|||
MCLDamageSource = class()
|
||||
|
||||
function MCLDamageSource:constructor(tbl)
|
||||
if tbl then
|
||||
for k, v in pairs(tbl) do
|
||||
self[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MCLDamageSource:from_mt(reason)
|
||||
|
||||
end
|
||||
|
||||
MCLDamageSource:__getter("direct_object", function(self)
|
||||
local obj = self.raw_source_object
|
||||
if not obj then
|
||||
return
|
||||
end
|
||||
return mcl_object_mgr.get(obj)
|
||||
end)
|
||||
|
||||
MCLDamageSource:__getter("source_object", function(self)
|
||||
local direct = self:direct_object()
|
||||
if not direct then
|
||||
return
|
||||
end
|
||||
return direct.source_object or direct
|
||||
end)
|
|
@ -0,0 +1,44 @@
|
|||
MCLEntity = class(MCLObject)
|
||||
|
||||
MCLEntity:__getter("meta", MCLMetadata)
|
||||
|
||||
local last_inv_id = 0
|
||||
|
||||
MCLEntity:__getter("inventory", function(self)
|
||||
local info = self.inventory_info
|
||||
if not info then
|
||||
return
|
||||
end
|
||||
self.inventory_id = "mcl_entity:" .. last_inv_id
|
||||
last_inv_id = last_inv_id + 1
|
||||
local inv = minetest.create_detached_inventory(self.inventory_id, self.inventory_callbacks)
|
||||
for list, size in pairs(data.sizes) do
|
||||
inv:set_size(list, size)
|
||||
end
|
||||
for list, liststr in pairs(data.lists) do
|
||||
inv:set_list(list, liststr)
|
||||
end
|
||||
return inv
|
||||
end)
|
||||
|
||||
function MCLEntity:on_activate(staticdata)
|
||||
local data = minetest.deserialize(staticdata)
|
||||
if data then
|
||||
self:meta():from_table(data)
|
||||
self.inventory_info = data.inventory
|
||||
end
|
||||
end
|
||||
|
||||
function MCLEntity:get_staticdata()
|
||||
local data = self:meta():to_table()
|
||||
local inventory_info = self.inventory_info
|
||||
|
||||
if inventory_info then
|
||||
data.inventory = {
|
||||
sizes = inventory_info.sizes,
|
||||
lists = self:inventory():get_lists()
|
||||
}
|
||||
end
|
||||
|
||||
return minetest.serialize(data)
|
||||
end
|
|
@ -0,0 +1,103 @@
|
|||
MCLEquipment = class()
|
||||
|
||||
function MCLEquipment:constructor(inv, idx)
|
||||
self.inv = inv
|
||||
self.idx = idx
|
||||
end
|
||||
|
||||
MCLEquipment:__cache_getter("has_main", function(self)
|
||||
return self.inv and self.idx and self.inv:get_list("main") and true or false
|
||||
end)
|
||||
|
||||
MCLEquipment:__cache_getter("has_right", function(self)
|
||||
return self.inv and self.inv:get_list("right_hand") and true or false
|
||||
end)
|
||||
|
||||
MCLEquipment:__cache_getter("has_left", function(self)
|
||||
return self.inv and self.inv:get_list("left_hand") and true or false
|
||||
end)
|
||||
|
||||
MCLEquipment:__cache_getter("has_armor", function(self)
|
||||
return self.inv and self.inv:get_list("armor") and true or false
|
||||
end)
|
||||
|
||||
function MCLEquipment:mainhand()
|
||||
if self:has_main() then
|
||||
return self.inv:get_stack("main", self.idx)
|
||||
elseif self:has_right() then
|
||||
return self.inv:get_stack("right_hand", 1)
|
||||
else
|
||||
return ItemStack()
|
||||
end
|
||||
end
|
||||
|
||||
MCLEquipment:__setter("mainhand", function(self, new)
|
||||
if self:has_main() then
|
||||
self.inv:set_stack("main", self.idx, stack)
|
||||
elseif self:has_right() then
|
||||
self.inv:set_stack("right_hand", 1, stack)
|
||||
end
|
||||
end)
|
||||
|
||||
function MCLEquipment:offhand()
|
||||
if self:has_left() then
|
||||
return self.inv:get_stack("left_hand", 1)
|
||||
else
|
||||
return ItemStack()
|
||||
end
|
||||
end
|
||||
|
||||
MCLEquipment:__setter("offhand", function(self, new)
|
||||
if self:has_left() then
|
||||
self.inv:set_stack("left_hand", 1, new)
|
||||
end
|
||||
end)
|
||||
|
||||
function MCLEquipment:__armor(idx, name)
|
||||
self[name] = function(self)
|
||||
if self:has_armor() then
|
||||
return self.inv:get_stack("armor", idx)
|
||||
else
|
||||
return ItemStack()
|
||||
end
|
||||
end
|
||||
|
||||
self:__setter(name, function(self, new)
|
||||
if self:has_armor() then
|
||||
self.inv:set_stack("armor", idx, new)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local armor_slots = {"helmet", "chestplace", "leggings", "boots"}
|
||||
|
||||
for i, name in ipairs(armor_slots) do
|
||||
MCLEquipment:__armor(idx, name)
|
||||
end
|
||||
|
||||
local function insert(tbl, key, stack)
|
||||
if stack:get_name() ~= "" then
|
||||
tbl[key] = stack
|
||||
end
|
||||
end
|
||||
|
||||
function MCLEquipment:get_armor()
|
||||
local tbl = {}
|
||||
if self:has_armor() then
|
||||
for i, name in ipairs(armor_slots) do
|
||||
insert(tbl, name, self.inv:get_stack("armor", i))
|
||||
end
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
|
||||
function MCLEquipment:get_all()
|
||||
local tbl = {}
|
||||
insert(tbl, "mainhand", self:mainhand())
|
||||
insert(tbl, "offhand", self:offhand())
|
||||
for k, v in pairs(self:get_armor()) do
|
||||
tbl[k] = v
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
# mcl_explosions
|
||||
This mod provide helper functions to create explosions.
|
||||
|
||||
## mcl_explosions.explode(pos, strength, info, puncher)
|
||||
* pos: position, initial position of the explosion
|
||||
* strenght: number, radius of the explosion
|
||||
* info: table, explosion informations:
|
||||
* drop_chance: number, if specified becomes the drop chance of all nodes in the explosion (default: 1.0 / strength)
|
||||
* max_blast_resistance: int, if specified the explosion will treat all non-indestructible nodes as having a blast resistance of no more than this value
|
||||
* sound: bool, if true, the explosion will play a sound (default: true)
|
||||
* particles: bool, if true, the explosion will create particles (default: true)
|
||||
* fire: bool, if true, 1/3 nodes become fire (default: false)
|
||||
* griefing: bool, if true, the explosion will destroy nodes (default: true)
|
||||
* grief_protected: bool, if true, the explosion will also destroy nodes which have been protected (default: false)
|
||||
* puncher: (optional) entity, will be used as source for damage done by the explosion
|
|
@ -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 не удалось пережить взрыва.
|
|
@ -0,0 +1,2 @@
|
|||
# textdomain:mcl_explosions
|
||||
@1 was caught in an explosion.=
|
|
@ -0,0 +1,66 @@
|
|||
mcl_gamerules = {
|
||||
__defaults = {},
|
||||
__rules = {},
|
||||
}
|
||||
|
||||
setmetatable(mcl_gamerules, {__index = mcl_gamerules.__rules})
|
||||
|
||||
local worldpath = minetest.get_worldpath()
|
||||
|
||||
function mcl_gamerules.__load()
|
||||
local file = io.open(worldpath .. "gamerules.json", "r")
|
||||
if file then
|
||||
local contents = file:read("*all")
|
||||
file:close()
|
||||
local data = minetest.parse_json(contents)
|
||||
local rules = mcl_gamerules.__rules
|
||||
for rule, default in pairs(mcl_gamerules.__defaults) do
|
||||
local value = data[rule]
|
||||
if value == nil then
|
||||
value = default
|
||||
end
|
||||
rules[rule] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_gamerules.__save()
|
||||
local file = io.open(worldpath .. "gamerules.json", "w")
|
||||
file:write(minetest.write_json(mcl_gamerules.__rules, true))
|
||||
file:close()
|
||||
end
|
||||
|
||||
function mcl_gamerules.__set(rule, value)
|
||||
if not mcl_gamerules.__defaults[rule] then
|
||||
return false
|
||||
end
|
||||
mcl_gamerules.__rules[rule] = value
|
||||
mcl_gamerules.__save()
|
||||
return true
|
||||
end
|
||||
|
||||
function mcl_gamerules.__register(rule, default)
|
||||
mcl_gamerules.__defaults[rule] = default
|
||||
end
|
||||
|
||||
mcl_gamerules.__register("announceAdvancements", true)
|
||||
mcl_gamerules.__register("commandBlockOutput", true)
|
||||
mcl_gamerules.__register("doDaylightCycle", true)
|
||||
mcl_gamerules.__register("doFireTick", true)
|
||||
mcl_gamerules.__register("doImmediateRespawn", false)
|
||||
mcl_gamerules.__register("doMobLoot", true)
|
||||
mcl_gamerules.__register("doMobSpawning", true)
|
||||
mcl_gamerules.__register("doTileDrops", true)
|
||||
mcl_gamerules.__register("doWeatherCycle", true)
|
||||
mcl_gamerules.__register("drowningDamage", true)
|
||||
mcl_gamerules.__register("fallDamage", true)
|
||||
mcl_gamerules.__register("fireDamage", true)
|
||||
mcl_gamerules.__register("keepInventory", false)
|
||||
mcl_gamerules.__register("logAdminCommands", true)
|
||||
mcl_gamerules.__register("mobGriefing", true)
|
||||
mcl_gamerules.__register("naturalRegeneration", true)
|
||||
mcl_gamerules.__register("pvp", true)
|
||||
mcl_gamerules.__register("showDeathMessages", true)
|
||||
mcl_gamerules.__register("tntExplodes", true)
|
||||
|
||||
minetest.register_on_mods_loaded(mcl_gamerules.__load)
|
|
@ -0,0 +1,32 @@
|
|||
mcl_groupcache = {
|
||||
cache = {},
|
||||
}
|
||||
|
||||
local function check_insert(item, group, cache)
|
||||
if minetest.get_item_group(item, group) ~= 0 then
|
||||
table.insert(cache, item)
|
||||
end
|
||||
end
|
||||
|
||||
local old_register_item = minetest.register_item
|
||||
|
||||
function minetest.register_item(name, def)
|
||||
old_register_item(name, def)
|
||||
for group, cache in pairs(mcl_groupcache.cache) do
|
||||
check_insert(item, group, cache)
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_groupcache.init_cache(group)
|
||||
local cache = {}
|
||||
for item in pairs(minetest.registered_items) do
|
||||
check_insert(item, group, cache)
|
||||
end
|
||||
return cache
|
||||
end
|
||||
|
||||
function mcl_groupcache.get_items_in_group(group)
|
||||
local cache = mcl_groupcache.cache[group] or mcl_groupcache.init_cache(group)
|
||||
mcl_groupcache.cache[group] = cache
|
||||
return cache
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
name = mcl_groupcache
|
||||
author = Fleckenstein
|
||||
description = Keep track of items with certain groups
|
|
@ -21,9 +21,6 @@ mcl_vars.gui_bg_img = "background9[1,1;1,1;mcl_base_textures_background9.png;tru
|
|||
-- Legacy
|
||||
mcl_vars.inventory_header = ""
|
||||
|
||||
-- Tool wield size
|
||||
mcl_vars.tool_wield_scale = { x = 1.8, y = 1.8, z = 1 }
|
||||
|
||||
-- Mapgen variables
|
||||
local mg_name = minetest.get_mapgen_setting("mg_name")
|
||||
local minecraft_height_limit = 256
|
||||
|
@ -32,30 +29,29 @@ 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
|
||||
local chunk_size_in_nodes = mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE
|
||||
local central_chunk_min_pos = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
|
||||
local central_chunk_max_pos = central_chunk_min_pos + mcl_vars.chunk_size_in_nodes - 1
|
||||
local central_chunk_max_pos = central_chunk_min_pos + chunk_size_in_nodes - 1
|
||||
local ccfmin = central_chunk_min_pos - mcl_vars.MAP_BLOCKSIZE -- Fullminp/fullmaxp of central chunk, in nodes
|
||||
local ccfmax = central_chunk_max_pos + mcl_vars.MAP_BLOCKSIZE
|
||||
local mapgen_limit_b = math.floor(math.min(mcl_vars.mapgen_limit, mcl_vars.MAX_MAP_GENERATION_LIMIT) / mcl_vars.MAP_BLOCKSIZE)
|
||||
local mapgen_limit_min = -mapgen_limit_b * mcl_vars.MAP_BLOCKSIZE
|
||||
local mapgen_limit_max = (mapgen_limit_b + 1) * mcl_vars.MAP_BLOCKSIZE - 1
|
||||
local numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / mcl_vars.chunk_size_in_nodes), 0) -- Number of complete chunks from central chunk
|
||||
local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / mcl_vars.chunk_size_in_nodes), 0) -- fullminp/fullmaxp to effective mapgen limits.
|
||||
mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * mcl_vars.chunk_size_in_nodes
|
||||
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * mcl_vars.chunk_size_in_nodes
|
||||
local numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / chunk_size_in_nodes), 0) -- Number of complete chunks from central chunk
|
||||
local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / chunk_size_in_nodes), 0) -- fullminp/fullmaxp to effective mapgen limits.
|
||||
mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * chunk_size_in_nodes
|
||||
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * chunk_size_in_nodes
|
||||
|
||||
local function coordinate_to_block(x)
|
||||
return math.floor(x / mcl_vars.MAP_BLOCKSIZE)
|
||||
end
|
||||
|
||||
local function coordinate_to_chunk(x)
|
||||
return math.floor((coordinate_to_block(x) - central_chunk_offset) / mcl_vars.chunksize)
|
||||
return math.floor((coordinate_to_block(x) + central_chunk_offset) / mcl_vars.chunksize)
|
||||
end
|
||||
|
||||
function mcl_vars.pos_to_block(pos)
|
||||
|
@ -74,7 +70,7 @@ function mcl_vars.pos_to_chunk(pos)
|
|||
}
|
||||
end
|
||||
|
||||
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / mcl_vars.chunk_size_in_nodes)
|
||||
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / chunk_size_in_nodes)
|
||||
local k_positive_z = k_positive * 2
|
||||
local k_positive_y = k_positive_z * k_positive_z
|
||||
|
||||
|
@ -178,86 +174,3 @@ minetest.craftitemdef_default.stack_max = 64
|
|||
-- Set random seed for all other mods (Remember to make sure no other mod calls this function)
|
||||
math.randomseed(os.time())
|
||||
|
||||
local chunks = {} -- intervals of chunks generated
|
||||
function mcl_vars.add_chunk(pos)
|
||||
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
|
||||
local prev
|
||||
for i, d in pairs(chunks) do
|
||||
if n <= d[2] then -- we've found it
|
||||
if (n == d[2]) or (n >= d[1]) then return end -- already here
|
||||
if n == d[1]-1 then -- right before:
|
||||
if prev and (prev[2] == n-1) then
|
||||
prev[2] = d[2]
|
||||
table.remove(chunks, i)
|
||||
return
|
||||
end
|
||||
d[1] = n
|
||||
return
|
||||
end
|
||||
if prev and (prev[2] == n-1) then --join to previous
|
||||
prev[2] = n
|
||||
return
|
||||
end
|
||||
table.insert(chunks, i, {n, n}) -- insert new interval before i
|
||||
return
|
||||
end
|
||||
prev = d
|
||||
end
|
||||
chunks[#chunks+1] = {n, n}
|
||||
end
|
||||
function mcl_vars.is_generated(pos)
|
||||
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
|
||||
for i, d in pairs(chunks) do
|
||||
if n <= d[2] then
|
||||
return (n >= d[1])
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- "Trivial" (actually NOT) function to just read the node and some stuff to not just return "ignore", like mt 5.4 does.
|
||||
-- p: Position, if it's wrong, {name="error"} node will return.
|
||||
-- force: optional (default: false) - Do the maximum to still read the node within us_timeout.
|
||||
-- us_timeout: optional (default: 244 = 0.000244 s = 1/80/80/80), set it at least to 3000000 to let mapgen to finish its job.
|
||||
--
|
||||
-- returns node definition, eg. {name="air"}. Unfortunately still can return {name="ignore"}.
|
||||
function mcl_vars.get_node(p, force, us_timeout)
|
||||
-- check initial circumstances
|
||||
if not p or not p.x or not p.y or not p.z then return {name="error"} end
|
||||
|
||||
-- try common way
|
||||
local node = minetest.get_node(p)
|
||||
if node.name ~= "ignore" then
|
||||
return node
|
||||
end
|
||||
|
||||
-- copy table to get sure it won't changed by other threads
|
||||
local pos = {x=p.x,y=p.y,z=p.z}
|
||||
|
||||
-- try LVM
|
||||
minetest.get_voxel_manip():read_from_map(pos, pos)
|
||||
node = minetest.get_node(pos)
|
||||
if node.name ~= "ignore" or not force then
|
||||
return node
|
||||
end
|
||||
|
||||
-- all ways failed - need to emerge (or forceload if generated)
|
||||
local us_timeout = us_timeout or 244
|
||||
if mcl_vars.is_generated(pos) then
|
||||
minetest.chat_send_all("IMPOSSIBLE! Please report this to MCL2 issue tracker!")
|
||||
minetest.forceload_block(pos)
|
||||
else
|
||||
minetest.emerge_area(pos, pos)
|
||||
end
|
||||
|
||||
local t = minetest.get_us_time()
|
||||
|
||||
node = minetest.get_node(pos)
|
||||
|
||||
while (not node or node.name == "ignore") and (minetest.get_us_time() - t < us_timeout) do
|
||||
node = minetest.get_node(pos)
|
||||
end
|
||||
|
||||
return node
|
||||
-- it still can return "ignore", LOL, even if force = true, but only after time out
|
||||
end
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
MCLItemStack = class()
|
||||
|
||||
function MCLItemStack:constructor(stack)
|
||||
self.stack = stack
|
||||
end
|
||||
|
||||
MCLItemStack:__getter("enchantments", function(self)
|
||||
return mcl_enchanting.get_enchantments(self.stack)
|
||||
end)
|
||||
MCLItemStack:__comparator("enchantments", mcl_types.match_enchantments)
|
||||
|
||||
function MCLItemStack:meta()
|
||||
return self.stack:get_meta()
|
||||
end
|
||||
MCLItemStack:__comparator("meta", mcl_types.match_meta)
|
||||
|
||||
function MCLItemStack:get_enchantment(name)
|
||||
return self:enchantments()[name] or 0
|
||||
end
|
||||
|
||||
function MCLItemStack:has_enchantment(name)
|
||||
return self:get_enchantment(name) > 0
|
||||
end
|
||||
|
||||
function MCLItemStack:total_durability()
|
||||
end
|
||||
|
||||
function MCLItemStack:durability()
|
||||
local def = self.stack:get_definition()
|
||||
if def then
|
||||
local base_uses = def._durability
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function MCLItemStack:use_durability()
|
||||
end
|
||||
|
||||
function MCLItemStack:restore_durability()
|
||||
end
|
||||
|
||||
function MCLItemStack:get_group()
|
||||
end
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
MCLMetadata = class()
|
||||
|
||||
function MCLMetadata:constructor()
|
||||
self.fields = {}
|
||||
end
|
||||
|
||||
for _type, default in pairs({string = "", float = 0.0, int = 0}) do
|
||||
MCLMetadata["set_" .. _type] = function(name, value) do
|
||||
if value == default then
|
||||
value = nil
|
||||
end
|
||||
self.fields[name] = value
|
||||
end
|
||||
MCLMetadata["get_" .. _type] = function(name) do
|
||||
return self.fields[name] or default
|
||||
end
|
||||
end
|
||||
|
||||
function MCLMetadata:to_table()
|
||||
return table.copy(self)
|
||||
end
|
||||
|
||||
function MCLMetadata:from_table(tbl)
|
||||
self.fields = table.copy(tbl.fields)
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
MCLMob = class(MCLEntity)
|
||||
|
||||
function MCLMob:get_hp()
|
||||
self:meta():get_float("hp")
|
||||
end
|
||||
|
||||
function MCLMob:set_hp()
|
||||
self:meta():set_float("hp", hp)
|
||||
end
|
||||
|
||||
function MCLMob:on_damage(damage, source)
|
||||
MCLEntity.on_damage(self, damage, source)
|
||||
|
||||
local new_hp = self:get_hp()
|
||||
if new_hp <= 0 and new_hp + damage > 0 then
|
||||
self:on_death(source)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,121 @@
|
|||
MCLObject = class()
|
||||
|
||||
function MCLObject:constructor(obj)
|
||||
self.object = obj.object or obj
|
||||
self.IS_MCL_OBJECT = true
|
||||
end
|
||||
|
||||
function MCLObject:on_punch(hitter, time_from_last_punch, tool_capabilities, dir, damage)
|
||||
local source = MCLDamageSource({is_punch = true, raw_source_object = hitter})
|
||||
|
||||
local knockback = {
|
||||
hitter = hitter,
|
||||
time_from_last_punch = time_from_last_punch,
|
||||
tool_capabilities = tool_capabilities,
|
||||
dir = dir,
|
||||
}
|
||||
|
||||
self:damage(damage, source, knockback)
|
||||
return true
|
||||
end
|
||||
|
||||
function MCLObject:apply_knockback(strength, dir)
|
||||
local oldvel = self.object:get_velocity()
|
||||
local vel = vector.multiply(vector.normalize(vector.new(dir.x, 0, dir.z)), strength)
|
||||
|
||||
if self:is_on_ground() then
|
||||
local old_y = oldvel.y / 2
|
||||
|
||||
y = math.min(0.4, old_y / 2 + strenth)
|
||||
vel.y
|
||||
end
|
||||
|
||||
vel = vector.subtract(vel, vector.divide(vector.new(oldvel.x, 0, oldvel.z), 2))
|
||||
|
||||
self.object:add_velocity(vel)
|
||||
end
|
||||
|
||||
-- use this function to deal regular damage to an object (do NOT use :punch() unless toolcaps need to be handled)
|
||||
function MCLObject:damage(damage, source, knockback)
|
||||
damage = self:damage_modifier(damage, source) or damage
|
||||
self:set_hp(self:get_hp() - damage)
|
||||
|
||||
if type(knockback) == "table" then
|
||||
knockback = self:calculate_knockback(
|
||||
knockback.hitter,
|
||||
knockback.time_from_last_punch,
|
||||
knockback.tool_capabilities,
|
||||
knockback.dir or vector.direction(knockback.hitter:get_pos(), self.object:get_pos(),
|
||||
knockback.distance or vector.distance(knockback.hitter:get_pos(), self.object:get_pos(),
|
||||
damage,
|
||||
source)
|
||||
end
|
||||
|
||||
table.insert(self.damage_info, {
|
||||
damage = damage,
|
||||
source = source,
|
||||
knockback = knockback,
|
||||
})
|
||||
|
||||
return damage
|
||||
end
|
||||
|
||||
function MCLObject:wield_index()
|
||||
end
|
||||
|
||||
MCLObject:__getter("equipment", function(self)
|
||||
return MCLEquipment(self:inventory(), self:wield_index())
|
||||
end)
|
||||
|
||||
function MCLObject:get_hp()
|
||||
return self.object:get_hp()
|
||||
end
|
||||
|
||||
function MCLObject:set_hp(hp)
|
||||
self.object:set_hp(hp)
|
||||
end
|
||||
|
||||
function MCLObject:death_drop(inventory, listname, index, stack)
|
||||
minetest.add_item(self.object:get_pos(), stack)
|
||||
inventory:set_stack(listname, index, nil)
|
||||
end
|
||||
|
||||
function MCLObject:on_death(source)
|
||||
local inventory = self:inventory()
|
||||
if inventory then
|
||||
for listname, list in pairs(inventory:get_lists()) do
|
||||
for index, stack in pairs(list) do
|
||||
if stack:get_name() ~= "" and then
|
||||
self:death_drop(inventory, listname, index, stack)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MCLObject:damage_modifier(damage, source)
|
||||
if self.invulnerable and not source.bypasses_invulnerability then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
function MCLObject:on_damage(damage, source, knockback)
|
||||
end
|
||||
|
||||
MCLObject.calculate_knockback = minetest.calculate_knockback
|
||||
|
||||
function minetest.calculate_knockback()
|
||||
return 0
|
||||
end
|
||||
|
||||
function MCLObject:on_step()
|
||||
local damage_info = self.damage_info
|
||||
if damage_info then
|
||||
self.damage_info = nil
|
||||
self:on_damage(damage_info.damage, damage_info.source)
|
||||
|
||||
if damage_info.knockback then
|
||||
self.object:add_velocity(damage_info.knockback)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,121 @@
|
|||
mcl_object_mgr = {
|
||||
players = {}
|
||||
}
|
||||
|
||||
-- functions
|
||||
|
||||
function mcl_object_mgr.get(obj)
|
||||
local rval
|
||||
|
||||
if mcl_object_mgr.is_mcl_object(obj) then
|
||||
rval = obj
|
||||
elseif mcl_object_mgr.is_player(obj) then
|
||||
rval = mcl_object_mgr.get_player(obj)
|
||||
elseif mcl_object_mgr.is_entity(obj) then
|
||||
rval = mcl_object_mgr.get_entity(obj)
|
||||
end
|
||||
|
||||
return assert(rval, "No matching MCLObject found. This is most likely an error caused by custom mods.")
|
||||
end
|
||||
|
||||
function mcl_object_mgr.is_mcl_object(obj)
|
||||
return type(obj) == "table" and obj.IS_MCL_OBJECT
|
||||
end
|
||||
|
||||
function mcl_object_mgr.is_player(obj)
|
||||
return type(obj) == "string" or type(obj) == "userdata" and obj:is_player()
|
||||
end
|
||||
|
||||
function mcl_object_mgr.is_is_entity(obj)
|
||||
return type(obj) == "table" and obj.object or type(obj) == "userdata" and obj:get_luaentity()
|
||||
end
|
||||
|
||||
function mcl_object_mgr.get_entity(ent)
|
||||
if type(ent) == "userdata" then
|
||||
ent = ent:get_luaentity()
|
||||
end
|
||||
return ent.mcl_entity
|
||||
end
|
||||
|
||||
function mcl_object_mgr.get_player(name)
|
||||
if type(name) == "userdata" then
|
||||
name = name:get_player_name()
|
||||
end
|
||||
return mcl_player_mgr.players[name]
|
||||
end
|
||||
|
||||
-- entity wrappers
|
||||
|
||||
local function add_entity_wrapper(def, name)
|
||||
def[name] = function(luaentity, ...)
|
||||
local func = self.mcl_entity[name]
|
||||
if func then
|
||||
return func(self.mcl_entity, ...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_object_mgr.register_entity(name, initial_properties, base_class)
|
||||
local def = {
|
||||
initial_properties = initial_properties,
|
||||
|
||||
on_activate = function(self, ...)
|
||||
local entity = base_class(self.object)
|
||||
self.mcl_entity = entity
|
||||
if entity.on_activate then
|
||||
entity:on_activate(...)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
add_entity_wrapper(def, "on_deactivate")
|
||||
add_entity_wrapper(def, "on_step")
|
||||
add_entity_wrapper(def, "on_punch")
|
||||
add_entity_wrapper(def, "on_death")
|
||||
add_entity_wrapper(def, "on_rightclick")
|
||||
add_entity_wrapper(def, "on_attach_child")
|
||||
add_entity_wrapper(def, "on_detach_child")
|
||||
add_entity_wrapper(def, "on_detach")
|
||||
add_entity_wrapper(def, "get_staticdata")
|
||||
|
||||
minetest.register_entity(name, def)
|
||||
end
|
||||
|
||||
-- player wrappers
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local name = player:get_player_name()
|
||||
mcl_player_mgr.players[name] = MCLPlayer(player)
|
||||
mcl_player_mgr.players[name]:on_join()
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local name = player:get_player_name()
|
||||
mcl_player_mgr.players[name]:on_leave()
|
||||
mcl_player_mgr.players[name] = nil
|
||||
end)
|
||||
|
||||
local function add_player_wrapper(wrapper, regfunc)
|
||||
minetest[regfunc or "register_" .. wrapper .. "player"](function(player, ...)
|
||||
local mclplayer = mcl_player_mgr.players[player:get_player_name()]
|
||||
local func = mclplayer[funcname or wrapper]
|
||||
if func then
|
||||
func(mclplayer, ...)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
add_player_wrapper("on_punch")
|
||||
add_player_wrapper("on_rightclick")
|
||||
add_player_wrapper("on_death", "register_on_dieplayer")
|
||||
add_player_wrapper("on_respawn")
|
||||
|
||||
minetest.register_on_player_hpchange(function(player, hp_change, reason)
|
||||
if not reason.auto then
|
||||
local mclplayer = mcl_object_mgr.get(player)
|
||||
local source = MCLDamageSource():from_mt(reason)
|
||||
|
||||
mclplayer:damage(hp_change, source)
|
||||
return true
|
||||
end
|
||||
end, true)
|
|
@ -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,19 @@
|
|||
MCLPlayer = class(MCLObject)
|
||||
|
||||
MCLPlayer:__cache_getter("meta", function(self)
|
||||
return self.object:get_meta()
|
||||
end)
|
||||
|
||||
MCLPlayer:__cache_getter("inventory", function(self)
|
||||
return self.object:get_inventory()
|
||||
end)
|
||||
|
||||
MCLPlayer:__override_pipe("death_drop", function(self, inventory, listname, index, stack)
|
||||
if not mcl_gamerules.keepInventory then
|
||||
return self, inventory, listname, index, stack
|
||||
end
|
||||
end)
|
||||
|
||||
function MCLPlayer:on_damage(damage, source, knockback)
|
||||
MCLObject.on_damage(self, damage, source, knockback)
|
||||
end
|
|
@ -1,27 +1,5 @@
|
|||
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 +150,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 +212,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
|
||||
|
@ -432,7 +410,7 @@ function mcl_util.get_color(colorstr)
|
|||
local mc_color = mcl_colors[colorstr:upper()]
|
||||
if mc_color then
|
||||
colorstr = mc_color
|
||||
elseif #colorstr ~= 7 or colorstr:sub(1, 1) ~= "#" then
|
||||
elseif #colorstr ~= 7 or colorstr:sub(1, 1) ~= "#"then
|
||||
return
|
||||
end
|
||||
local hex = tonumber(colorstr:sub(2, 7), 16)
|
||||
|
@ -441,133 +419,30 @@ 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
|
||||
function mcl_util.registration_function(tbl, func)
|
||||
return function(name, def)
|
||||
if func then
|
||||
local res = func(name, def)
|
||||
if res == false then
|
||||
return
|
||||
elseif res ~= nil then
|
||||
def = res
|
||||
end
|
||||
end
|
||||
tbl[name] = def
|
||||
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")
|
||||
function mcl_util.rand(pr, ...)
|
||||
return pr and pr:next(...) or math.random(...)
|
||||
end
|
||||
|
||||
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
|
||||
function mcl_util.rand_bool(probability, pr)
|
||||
if probability >= 1 then
|
||||
return true
|
||||
elseif probability <= 0 then
|
||||
return false
|
||||
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})
|
||||
return mcl_util.rand(pr, 0, 32767) < probability * 32768
|
||||
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
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
# mcl_worlds
|
||||
This mod provides utility functions about positions and dimensions.
|
||||
|
||||
## mcl_worlds.is_in_void(pos)
|
||||
This function returns:
|
||||
|
||||
* true, true: if pos is in deep void (deadly)
|
||||
* true, false: if the pos is in void (non deadly)
|
||||
* false, false: owerwise
|
||||
|
||||
Params:
|
||||
|
||||
* pos: position
|
||||
|
||||
## 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:
|
||||
|
||||
* The corresponding Minecraft layer (can be nil if void)
|
||||
* The corresponding Minecraft dimension ("overworld", "nether" or "end") or "void" if <y> is in the void
|
||||
If the Y coordinate is not located in any dimension, it will return: nil, "void"
|
||||
|
||||
Params:
|
||||
|
||||
* y: int
|
||||
|
||||
## mcl_worlds.pos_to_dimension(pos)
|
||||
This function return the Minecraft dimension of <pos> ("overworld", "nether" or "end") or "void" if <y> is in the void.
|
||||
|
||||
* pos: position
|
||||
|
||||
## mcl_worlds.layer_to_y(layer, mc_dimension)
|
||||
Takes a Minecraft layer and a “dimension” name and returns the corresponding Y coordinate for MineClone 2.
|
||||
mc_dimension can be "overworld", "nether", "end" (default: "overworld").
|
||||
|
||||
* layer: int
|
||||
* mc_dimension: string
|
||||
|
||||
## mcl_worlds.has_weather(pos)
|
||||
Returns true if <pos> can have weather, false owerwise.
|
||||
Weather can be only in the overworld.
|
||||
|
||||
* pos: position
|
||||
|
||||
## mcl_worlds.has_dust(pos)
|
||||
Returns true if <pos> can have nether dust, false owerwise.
|
||||
Nether dust can be only in the nether.
|
||||
|
||||
* pos: position
|
||||
|
||||
## mcl_worlds.compass_works(pos)
|
||||
Returns true if compasses are working at <pos>, false owerwise.
|
||||
In mc, you cant use compass in the nether and the end.
|
||||
|
||||
* pos: position
|
||||
|
||||
## mcl_worlds.compass_works(pos)
|
||||
Returns true if clock are working at <pos>, false owerwise.
|
||||
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))
|
||||
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").
|
||||
|
||||
|
||||
## 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>
|
||||
|
||||
* player: player, player who changed the dimension
|
||||
* dimension: string, new dimension ("overworld", "nether", "end", "void")
|
|
@ -1,14 +1,12 @@
|
|||
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
|
||||
function mcl_worlds.is_in_void(pos)
|
||||
local void =
|
||||
not ((pos.y < mcl_vars.mg_overworld_max and pos.y > mcl_vars.mg_overworld_min) or
|
||||
(pos.y < mcl_vars.mg_nether_max+128 and pos.y > mcl_vars.mg_nether_min) or
|
||||
(pos.y < mcl_vars.mg_nether_max and pos.y > mcl_vars.mg_nether_min) or
|
||||
(pos.y < mcl_vars.mg_end_max and pos.y > mcl_vars.mg_end_min))
|
||||
|
||||
local void_deadly = false
|
||||
|
@ -17,11 +15,11 @@ function mcl_worlds.is_in_void(pos)
|
|||
-- Overworld → Void → End → Void → Nether → Void
|
||||
if pos.y < mcl_vars.mg_overworld_min and pos.y > mcl_vars.mg_end_max then
|
||||
void_deadly = pos.y < mcl_vars.mg_overworld_min - deadly_tolerance
|
||||
elseif pos.y < mcl_vars.mg_end_min and pos.y > mcl_vars.mg_nether_max+128 then
|
||||
elseif pos.y < mcl_vars.mg_end_min and pos.y > mcl_vars.mg_nether_max then
|
||||
-- The void between End and Nether. Like usual, but here, the void
|
||||
-- *above* the Nether also has a small tolerance area, so player
|
||||
-- can fly above the Nether without getting hurt instantly.
|
||||
void_deadly = (pos.y < mcl_vars.mg_end_min - deadly_tolerance) and (pos.y > mcl_vars.mg_nether_max+128 + deadly_tolerance)
|
||||
void_deadly = (pos.y < mcl_vars.mg_end_min - deadly_tolerance) and (pos.y > mcl_vars.mg_nether_max + deadly_tolerance)
|
||||
elseif pos.y < mcl_vars.mg_nether_min then
|
||||
void_deadly = pos.y < mcl_vars.mg_nether_min - deadly_tolerance
|
||||
end
|
||||
|
@ -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 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 + 64 and pos.y >= mcl_vars.mg_nether_min - 64
|
||||
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, "w"))
|
||||
f:write(self.data)
|
||||
f:close()
|
||||
end
|
||||
|
||||
tga_encoder.image = image
|
|
@ -1,3 +0,0 @@
|
|||
name = tga_encoder
|
||||
author = Fleckenstein
|
||||
description = A TGA Encoder written in Lua without the use of external Libraries.
|
|
@ -4,7 +4,6 @@ local get_connected_players = minetest.get_connected_players
|
|||
local get_node = minetest.get_node
|
||||
local vector_add = vector.add
|
||||
local ceil = math.ceil
|
||||
local pairs = pairs
|
||||
|
||||
walkover = {}
|
||||
walkover.registered_globals = {}
|
||||
|
@ -32,21 +31,24 @@ minetest.register_globalstep(function(dtime)
|
|||
timer = timer + dtime;
|
||||
if timer >= 0.3 then
|
||||
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 then
|
||||
local nodeiamon = get_node(loc)
|
||||
if nodeiamon then
|
||||
if on_walk[nodeiamon.name] then
|
||||
on_walk[nodeiamon.name](loc, nodeiamon, player)
|
||||
end
|
||||
for i = 1, #registered_globals 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
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
timer = 0
|
||||
end
|
||||
end)
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
mcl_loottables.register_entry = mcl_util.registration_function(mcl_loottables.entries)
|
||||
mcl_loottables.register_table = mcl_util.registration_function(mcl_loottables.tables, function(name, def)
|
||||
local function set_parents(parent)
|
||||
for _, child in ipairs(parent.children or parent.entries or parent.pools or {}) do
|
||||
child.parent = parent
|
||||
set_parents(child)
|
||||
end
|
||||
end
|
||||
set_parents(def)
|
||||
end)
|
||||
|
||||
function mcl_loottables.get_entry_type(entry)
|
||||
return mcl_loottables.entries[entry.type]
|
||||
end
|
||||
|
||||
function mcl_loottables.get_candidates(entries, data, func)
|
||||
local candidates = {}
|
||||
for _, entry in ipairs(entries) do
|
||||
local success = mcl_predicates.do_predicates(entry.conditions, data)
|
||||
|
||||
if success then
|
||||
local children = entry.children
|
||||
|
||||
if children then
|
||||
table.insert_all(candidates, mcl_loottables.get_candidates(children, data, mcl_loottables.get_entry_type(entry).preprocess))
|
||||
else
|
||||
table.insert(candidates, entry)
|
||||
end
|
||||
end
|
||||
|
||||
if func and func(success, data) then
|
||||
break
|
||||
end
|
||||
end
|
||||
return candidates
|
||||
end
|
||||
|
||||
function mcl_loottables.do_item_modifiers(itemstack, node, data)
|
||||
if node then
|
||||
mcl_item_modifiers.do_item_modifiers(itemstack, node.functions, data)
|
||||
mcl_loottables.do_item_modifiers(itemstack, node.parent, data)
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_loottables.do_pools(pools, functions, data)
|
||||
local luck = data.luck or 0
|
||||
local stacks = {}
|
||||
for _, pool in ipairs(pools or {}) do
|
||||
if mcl_predicates.do_predicates(pool.conditions, data) do
|
||||
local rolls = mcl_numbers.get_number(pool.rolls, data) + mcl_numbers.get_number(pool.bonus_rolls, data) * luck
|
||||
for i = 1, rolls do
|
||||
local candidates = mcl_loottables.get_candidates(pool.entries, data)
|
||||
|
||||
if #candidates > 0 then
|
||||
local total_weight = 0
|
||||
local weights = {}
|
||||
for _, candidate in ipairs(candidates)
|
||||
total_weight = total_weight + math.floor((candidate.weight or 1) + (candidate.quality or 0) * luck)
|
||||
table.insert(weights, total_weight)
|
||||
end
|
||||
|
||||
local selected
|
||||
local rnd = mcl_util.rand(data.pr, 0, weight - 1)
|
||||
for i, w in ipairs(weights) do
|
||||
if rnd < w then
|
||||
selected = candidates[i]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local pool_stacks = mcl_loottables.get_entry_type(entry).process(selected, data)
|
||||
|
||||
for _, stack in ipairs(pool_stacks) do
|
||||
mcl_item_modifiers.do_item_modifiers(stack, selected, data)
|
||||
end
|
||||
table.insert_all(stacks, pool_stacks)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return stacks
|
||||
end
|
||||
|
||||
function mcl_loottables.get_loot(def, data)
|
||||
if type(def) == "string" then
|
||||
def = mcl_loottables.tables[def]
|
||||
end
|
||||
return mcl_loottables.do_pools(def.pools)
|
||||
end
|
||||
|
||||
function mcl_loottables.drop_loot(def, data)
|
||||
local loot = mcl_loottables.get_loot(def, data)
|
||||
local old_loot = table.copy(loot)
|
||||
for _, stack in ipairs(old_loot) do
|
||||
local max_stack = stack:get_stack_max()
|
||||
while max_stack < stack:get_count() do
|
||||
table.insert(loot, stack:take_items(max_stack))
|
||||
end
|
||||
end
|
||||
return loot
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
mcl_loottables.register_entry("mcl_loottables:alternatives", {
|
||||
preprocess = function(success, data)
|
||||
return success
|
||||
end,
|
||||
})
|
||||
|
||||
mcl_loottables.register_entry("mcl_loottables:group", {
|
||||
preprocess = function(success, data)
|
||||
return false
|
||||
end,
|
||||
})
|
||||
|
||||
mcl_loottables.register_entry("mcl_loottables:sequence", {
|
||||
preprocess = function(success, data)
|
||||
return not success
|
||||
end,
|
||||
})
|
||||
|
||||
mcl_loottables.register_entry("mcl_loottables:tag", function(entry, data)
|
||||
local stacks = mcl_groupcache.get_items_in_group(entry.name)
|
||||
if entry.expand then
|
||||
stacks = {stacks[pr:next(1, #stacks)]}
|
||||
end
|
||||
return stacks
|
||||
end)
|
||||
|
||||
mcl_loottables.register_entry("mcl_loottables:loot_table", {
|
||||
process = function(entry, data)
|
||||
return mcl_loottables.get_loot(entry.name, data)
|
||||
end,
|
||||
})
|
||||
|
||||
mcl_loottables.register_entry("mcl_loottables:empty", {
|
||||
process = function(entry, data)
|
||||
return {}
|
||||
end,
|
||||
})
|
||||
|
||||
mcl_loottables.register_entry("mcl_loottables:item", {
|
||||
process = function(entry, data)
|
||||
return {item = ItemStack(entry.name)}
|
||||
end,
|
||||
})
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
mcl_loottables = {
|
||||
tables = {},
|
||||
entries = {},
|
||||
}
|
||||
|
||||
local modpath = minetest.get_modpath("mcl_loottables")
|
||||
|
||||
dofile(modpath .. "/api.lua")
|
||||
dofile(modpath .. "/entries.lua")
|
|
@ -0,0 +1,4 @@
|
|||
name = mcl_loottables
|
||||
author = Fleckenstein
|
||||
description = Provides Minecraft-like loot table definitions
|
||||
depends = mcl_util, mcl_predicates, mcl_item_modifiers, mcl_groupcache
|
|
@ -0,0 +1,19 @@
|
|||
mcl_numbers.register_provider = mcl_util.registration_function(mcl_numbers.providers)
|
||||
|
||||
function mcl_numbers.get_number(provider, data)
|
||||
if type(provider) == "number" then
|
||||
return provider
|
||||
else
|
||||
mcl_numbers.providers[data.type](provider, data)
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_numbers.match_bounds(actual, expected, data)
|
||||
if type(expected) == "table" then
|
||||
expected = {
|
||||
min = mcl_numbers.get_number(expected.min, data),
|
||||
max = mcl_numbers.get_number(expected.max, data)
|
||||
}
|
||||
end
|
||||
return mcl_util.match_bounds(actual, expected)
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
mcl_numbers = {
|
||||
providers = {},
|
||||
}
|
||||
|
||||
dofile(minetest.get_modpath("mcl_numbers") .. "/api.lua")
|
||||
|
||||
mcl_numbers.register_provider("mcl_numbers:constant", function(provider)
|
||||
return provider.value
|
||||
end)
|
||||
|
||||
mcl_numbers.register_provider("mcl_numbers:uniform", function(provider, data)
|
||||
return mcl_util.rand(data.pr, mcl_numbers.get_number(provider.min, data), mcl_numbers.get_number(provider.max, data))
|
||||
end)
|
||||
|
||||
mcl_numbers.register_provider("mcl_numbers:binomial", function(provider, data)
|
||||
local n = mcl_numbers.get_number(provider.n, data)
|
||||
local num = 0
|
||||
for i = 1, n do
|
||||
if mcl_util.rand_bool(mcl_numbers.get_number(provider.p, data), data.pr) then
|
||||
num = num + 1
|
||||
end
|
||||
end
|
||||
return num
|
||||
end)
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
name = mcl_numbers
|
||||
author = Fleckenstein
|
||||
description = Minecraft-like number providers
|
||||
depends = mcl_util
|
|
@ -0,0 +1,15 @@
|
|||
mcl_predicates.register_predicate = mcl_util.registration_function(mcl_predicates.predicates)
|
||||
|
||||
function mcl_predicates.do_predicates(predicates, data, or_mode)
|
||||
or_mode = or_mode or false
|
||||
for _, func in ipairs(predicates) do
|
||||
if type(func) == "string" then
|
||||
func = mcl_predicates.predicates[func]
|
||||
end
|
||||
local failure = func and not func(def, data) or false
|
||||
if or_mode ~= failure then
|
||||
return or_mode
|
||||
end
|
||||
end
|
||||
return not or_mode or #predicates == 0
|
||||
end
|
|
@ -0,0 +1,104 @@
|
|||
mcl_predicates = {
|
||||
predicates = {},
|
||||
}
|
||||
|
||||
local modpath = minetest.get_modpath("mcl_predicates")
|
||||
|
||||
dofile(modpath .. "/api.lua")
|
||||
|
||||
mcl_predicates.register_predicate("alternative", function(predicate, data)
|
||||
return mcl_predicates.do_predicates(predicate.terms, data, true)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("match_node", function(predicate, data)
|
||||
return mcl_types.match_node(data.node or minetest.get_node(data.pos), predicate, data.pos, data.nodemeta)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("damage_source_properties", function(predicate, data)
|
||||
-- ToDo: damage source abstraction
|
||||
return nil
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("entity_properties", function(predicate, data)
|
||||
local entity = predicate.entity
|
||||
if not entity or (entity ~= "this" and entity ~= "killer" and entity ~= "killer_player") then
|
||||
return false
|
||||
end
|
||||
|
||||
local ref = data[entity]
|
||||
if not ref then
|
||||
return false
|
||||
end
|
||||
|
||||
-- ToDo: entity / player abstraction
|
||||
return nil
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("entity_scores", function(predicate, data)
|
||||
-- ToDo: scoreboards
|
||||
return nil
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("inverted", function(predicate, data)
|
||||
return not mcl_predicates.do_predicates({predicate.term}, data)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("killed_by_player", function(predicate, data)
|
||||
return not predicate.inverse ~= not data.killer_player
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("location_check", function(predicate, data)
|
||||
local pos = vector.add(data.pos, vector.new(predicate.offset_x or 0, predicate.offset_y or 0, predicate.offset_z or 0))
|
||||
return mcl_location(pos, data.nodemeta):match(predicate.predicate)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("match_tool", function(predicate, data)
|
||||
return mcl_predicates.match_tool(data.tool, predicate.predicate)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("random_chance", function(predicate, data)
|
||||
return mcl_util.rand_bool(predicate.chance, data.pr)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("random_chance_with_looting", function(predicate, data)
|
||||
local chance = predicate.chance -- + (looting_level * looting_multiplier)
|
||||
|
||||
-- ToDo: entity / player abstraction
|
||||
return mcl_util.rand_bool(chance, data.pr)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("reference", function(predicate, data)
|
||||
-- ToDo: needs research
|
||||
return nil
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("survives_explosion", function(predicate, data)
|
||||
return mcl_util.rand_bool(data.drop_chance or 1, data.pr)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("table_bonus", function(predicate, data)
|
||||
-- ToDo: entity / player abstraction
|
||||
return nil
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("time_check", function(predicate, data)
|
||||
-- ToDo: needs research
|
||||
return nil
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("weather_check", function(predicate, data)
|
||||
local weather = mcl_weather.get_weather()
|
||||
|
||||
if predicate.thundering then
|
||||
return weather == "thunder"
|
||||
elseif predicate.raining then
|
||||
return weather == "rain"
|
||||
else
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("value_check", function(predicate, data)
|
||||
local value = mcl_numbers.get_number(predicate.value, data)
|
||||
return mcl_numbers.check_in_bounds(value, predicate.range, data)
|
||||
end)
|
|
@ -0,0 +1,4 @@
|
|||
name = mcl_predicates
|
||||
author = Fleckenstein
|
||||
description = Provides MC-like predicates
|
||||
depends = mcl_util, mcl_numbers
|
|
@ -0,0 +1,88 @@
|
|||
mcl_types = {}
|
||||
|
||||
function mcl_types.match_bounds(actual, expected)
|
||||
if type(expected) == "table" then
|
||||
return actual <= expected.max and actual >= expected.min
|
||||
else
|
||||
return actual == expected
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_types.match_meta(actual, expected)
|
||||
for k, v in pairs(expected) do
|
||||
if actual:get_string(k) ~= v then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function mcl_types.match_vector(actual, expected)
|
||||
for k, v in pairs(expected) do
|
||||
if not mcl_types.match_bounds(actual[k], expected[k]) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function mcl_types.match_enchantments(actual, expected)
|
||||
for _, v in ipairs(expected) do
|
||||
if not mcl_types.match_bounds(actual[v.enchantment] or 0, v.levels or {min = 1, max = math.huge}) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function mcl_types.match_item(actual, expected)
|
||||
actual = actual or ItemStack()
|
||||
|
||||
if expected.item and actual:get_name() ~= expected.item then
|
||||
return false
|
||||
elseif expected.group and minetest.get_item_group(actual:get_name(), expected.group) == 0 then
|
||||
return false
|
||||
elseif expected.count and not mcl_types.match_bounds(actual:get_count(), expected.count) then
|
||||
return false
|
||||
elseif expected.wear and not mcl_types.match_bounds(actual:get_wear(), expected.wear) then
|
||||
return false
|
||||
elseif expected.enchantments and not mcl_types.match_enchantments(mcl_enchanting.get_enchantments(actual), expected.enchantments) then
|
||||
return false
|
||||
elseif expected.meta and not mcl_types.match_meta(actual:get_meta(), expected.meta) then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_types.match_node(actual, expected, pos, meta)
|
||||
if expected.node and actual.name ~= expected.node then
|
||||
return false
|
||||
elseif expected.group and minetest.get_item_group(actual.name, expected.group) == 0 then
|
||||
return false
|
||||
elseif expected.param1 and actual.param1 ~= compare.param1 then
|
||||
return false
|
||||
elseif expected.param2 and actual.param2 ~= compare.param2 then
|
||||
return false
|
||||
elseif expected.meta and not mcl_types.match_meta(meta or minetest.get_meta(pos), expected.meta) then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_types.match_pos(actual, expected, meta)
|
||||
if expected.pos and not mcl_types.match_vector(actual, expected.pos) then
|
||||
return false
|
||||
elseif expected.dimension and mcl_worlds.pos_to_dimension(actual) ~= expected.dimension then
|
||||
return false
|
||||
elseif expected.biome and minetest.get_biome_name(minetest.get_biome_data(actual).biome) ~= expected.biome then
|
||||
return false
|
||||
elseif expected.node and not mcl_types.match_node(minetest.get_node(actual), expected.node, actual, meta) then
|
||||
return false
|
||||
elseif expected.light and not mcl_types.match_bounds(minetest.get_node_light(actual), expected.light) then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
|
@ -0,0 +1,4 @@
|
|||
name = mcl_types
|
||||
author = Fleckenstein
|
||||
description = Offers compare functions for many types, used for MineClone2 datapacks
|
||||
depends =
|
|
@ -0,0 +1,2 @@
|
|||
name = DATA
|
||||
description = Meta-modpack containing the datapack system for MineClone 2
|
|
@ -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,6 +1,6 @@
|
|||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
local S = minetest.get_translator("mcl_boats")
|
||||
|
||||
local boat_visual_size = {x = 1, y = 1, z = 1}
|
||||
local boat_visual_size = {x = 3, y = 3, z = 3}
|
||||
local paddling_speed = 22
|
||||
local boat_y_offset = 0.35
|
||||
local boat_y_offset_ground = boat_y_offset + 0.6
|
||||
|
@ -12,7 +12,9 @@ local function is_group(pos, group)
|
|||
return minetest.get_item_group(nn, group) ~= 0
|
||||
end
|
||||
|
||||
local is_water = flowlib.is_water
|
||||
local function is_water(pos)
|
||||
return is_group(pos, "water")
|
||||
end
|
||||
|
||||
local function is_ice(pos)
|
||||
return is_group(pos, "ice")
|
||||
|
@ -84,7 +86,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 +117,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 +150,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 +190,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
|
||||
|
@ -250,7 +247,7 @@ function boat.on_step(self, dtime, moveresult)
|
|||
else
|
||||
local ctrl = self._passenger:get_player_control()
|
||||
if ctrl and ctrl.sneak then
|
||||
detach_object(self._passenger, true)
|
||||
detach_player(self._passenger, true)
|
||||
self._passenger = nil
|
||||
end
|
||||
end
|
||||
|
@ -333,17 +330,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 +379,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 +396,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 +436,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 +472,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ź
|