Compare commits

..

1 Commits

60 changed files with 1103 additions and 1788 deletions

View File

@ -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.

View File

@ -1,414 +1,105 @@
# Contributing to MineClone2
So you want to contribute to MineClone2?
# Contributing to MineClone 2
So you want to contribute 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 three persons. Namely, kay27, EliasFleckenstein and jordan4ibanez. You can find us
in the Minetest forums (forums.minetest.net), in IRC in the #mineclone2
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)
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.
## 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.
## 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:
## How you can help as a non-programmer
For small and medium changes:
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.
### 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
### 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:
If your code causes bugs and crashes, it is your responsibility to fix them as soon as possible.
* 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)
We mostly use plain merging rather than rebasing or squash merging.
### Stick to our guidelines
Your commit names should be relatively descriptive, e.g. when saying "Fix #issueid", the commit message should also contain the title of the issue.
#### 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)
Contributors will be credited in `CREDITS.md`.
#### 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.
## Code Style
Each mod must provide `mod.conf`.
Each mod which add API functions should store functions inside a global table named like the mod.
Public functions should not use self references but rather just access the table directly.
Functions should be defined in this way:
```lua
mcl_example = {}
function mcl_example.do_something()
-- ...
end
function mcl_xyz.stuff(param) end
```
Insteed of this way:
```lua
mcl_xyz.stuff = function(param) end
```
Indentation must be unified, more likely with tabs.
Time sensitive mods should make a local copy of most used API functions to improve performances.
```lua
local vector = vector
local get_node = minetest.get_node
```
* Public functions should not use self references but rather just access
the table directly, e.g.
```lua
-- bad
function mcl_example:do_something()
end
## Features > 1.12
-- good
function mcl_example.do_something()
end
```
If you want to make a feature that was added in a Minecraft version later than 1.12, you should fork MineClone5 (mineclone5 branch in the repository) and add your changes to this.
* Use modern Minetest API, e.g. no usage of `minetest.env`
* Tabs should be used for indent, spaces for alignment, e.g.
## What we accept
```lua
* Every MC features up to version 1.12 JE.
* Every already finished and working good features from versions above (only when making a MineClone5 PR / Contribution).
* Except features which couldn't be done easily and bugfree because of Minetest engine limitations. Eg. we CAN extend world boundaries by playing with map chunks, just teleporting player onto next layer after 31000 , but it would cost too much (time, code, bugs, performance, stability, etc).
* Some features, approved by the rest of the community, I mean maybe some voting and really missing any negative feedback.
-- use tabs for indent
## What we reject
for i = 1, 10 do
if i % 3 == 0 then
print(i)
end
end
* Any features which cause critical bugs, sending them to rework/fix or trying to fix immediately.
* Some small portions of big entirely missing features which just definitely break gamplay balance give nothing useful
* Controversial features, which some people support while others do not should be discussed well, with publishing forum announcements, at least during the week. In case if there are still doubts - send them into the mod.
-- use tabs for indent and spaces to align things
## Reporting bugs
Report all bugs and missing Minecraft features here:
some_table = {
{"a string", 5},
{"a very much longer string", 10},
}
```
<https://git.minetest.land/MineClone2/MineClone2/issues>
* 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.
## Direct discussion
We have an IRC channel! Join us on #mineclone2 in freenode.net.
```lua
-- bad
local some_local_func = function()
-- ...
end
<ircs://irc.freenode.net:6697/#mineclone2>
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.

View File

@ -8,8 +8,8 @@
## Maintainers
* Fleckenstein
* Nicu
* kay27
* jordan4ibanez
## Developers
* bzoss
@ -19,11 +19,10 @@
* iliekprogrammar
* MysticTempest
* Rootyjr
* Nicu
* aligator
* Code-Sploit
* NO11
* cora
* jordan4ibanez
## Contributors
* Laurent Rocher
@ -49,25 +48,8 @@
* dBeans
* nickolas360
* yutyo
* Tianyang Zhang
* ztianyang
* 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
@ -92,6 +74,7 @@
* Rochambeau
* rubenwardy
* stu
* jordan4ibanez
* 4aiman
* Kahrl
* Krock
@ -120,7 +103,6 @@
* xMrVizzy
* yutyo
* NO11
* kay27
## Translations
* Wuzzy
@ -129,10 +111,6 @@
* kay27
* pitchum
* todoporlalibertad
* Marcin Serwin
## Funders
* 40W
## Special thanks
* celeron55 for creating Minetest

View File

@ -1,4 +1,4 @@
# 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.
@ -69,9 +69,9 @@ an explanation.
This game requires [Minetest](http://minetest.net) to run (version 5.3.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.
@ -85,24 +85,21 @@ The MineClone2 repository is hosted at Mesehub. To contribute or report issues,
* 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.
## Project description
The main goal of **MineClone 2** is to be a clone of Minecraft and to be released as free software.
* **Target of development: Minecraft, PC Edition, version 1.12** (later known as “Java Edition”)
* MineClone2 also includes Optifine features supported by the Minetest
* In general, Minecraft is aimed to be cloned as good as possible
* Cloning the gameplay has highest priority
* MineClone 2 will use different assets, but with a similar style
* Limitations found in Minetest will be documented in the course of development
* Features of later Minecraft versions are collected in the mineclone5 branch
## Using features from newer versions of Minecraft
For > 1.12 features, checkout MineClone5. It includes features from newer Minecraft versions.
Download it here: https://git.minetest.land/MineClone2/MineClone2/src/branch/mineclone5
## Completion status
This game is currently in **beta** stage.
@ -189,7 +186,7 @@ Technical differences from Minecraft:
* 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”)!
## Other readme files

View File

@ -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
--[[

View File

@ -12,7 +12,7 @@ Params:
* pos: position
## mcl_worlds.y_to_layer(y)
## mcl_worlds.y_to_layer(y)
This function is used to calculate the minetest y layer and dimension of the given <y> minecraft layer.
Mainly used for ore generation.
Takes an Y coordinate as input and returns:
@ -78,4 +78,4 @@ Table containing all function registered with mcl_worlds.register_on_dimension_c
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")
* dimension: string, new dimension ("overworld", "nether", "end", "void")

View File

@ -38,32 +38,18 @@ 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)
.. string.char(2) -- image type (uncompressed true-color image = 2)
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
self.data = self.data
.. string.char(pixel[3], pixel[2], pixel[1])
end
end
packets[#packets +1] = rle_packet
self.data = self.data .. table.concat(packets)
end
function image:encode_footer()

View File

@ -0,0 +1,114 @@
--Dripping Water Mod
--by kddekadenz
local math = math
-- 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,
})

View File

@ -1,4 +1,4 @@
name = mcl_dripping
name = drippingwater
author = kddekadenz
description = Drops are generated rarely under solid nodes
depends = mcl_core

View File

@ -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 :)

View File

@ -115,7 +115,7 @@ local boat = {
collisionbox = {-0.5, -0.35, -0.5, 0.5, 0.3, 0.5},
visual = "mesh",
mesh = "mcl_boats_boat.b3d",
textures = {"mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png"},
textures = {"mcl_boats_texture_oak_boat.png"},
visual_size = boat_visual_size,
hp_max = boat_max_hp,
damage_texture_modifier = "^[colorize:white:0",
@ -148,11 +148,6 @@ function boat.on_activate(self, staticdata, dtime_s)
self._v = data.v
self._last_v = self._v
self._itemstring = data.itemstring
while #data.textures < 5 do
table.insert(data.textures, data.textures[1])
end
self.object:set_properties({textures = data.textures})
end
end
@ -342,8 +337,7 @@ function boat.on_step(self, dtime, moveresult)
self.object:get_velocity().y)
else
p.y = p.y + 1
local is_obsidian_boat = self.object:get_luaentity()._itemstring == "mcl_boats:boat_obsidian"
if is_water(p) or is_obsidian_boat then
if is_water(p) then
-- Inside water: Slowly sink
local y = self.object:get_velocity().y
y = y - 0.01
@ -383,13 +377,13 @@ end
-- Register one entity for all boat types
minetest.register_entity("mcl_boats:boat", boat)
local boat_ids = { "boat", "boat_spruce", "boat_birch", "boat_jungle", "boat_acacia", "boat_dark_oak", "boat_obsidian" }
local names = { S("Oak Boat"), S("Spruce Boat"), S("Birch Boat"), S("Jungle Boat"), S("Acacia Boat"), S("Dark Oak Boat"), S("Obsidian Boat") }
local boat_ids = { "boat", "boat_spruce", "boat_birch", "boat_jungle", "boat_acacia", "boat_dark_oak" }
local names = { S("Oak Boat"), S("Spruce Boat"), S("Birch Boat"), S("Jungle Boat"), S("Acacia Boat"), S("Dark Oak Boat") }
local craftstuffs = {}
if minetest.get_modpath("mcl_core") then
craftstuffs = { "mcl_core:wood", "mcl_core:sprucewood", "mcl_core:birchwood", "mcl_core:junglewood", "mcl_core:acaciawood", "mcl_core:darkwood", "mcl_core:obsidian" }
craftstuffs = { "mcl_core:wood", "mcl_core:sprucewood", "mcl_core:birchwood", "mcl_core:junglewood", "mcl_core:acaciawood", "mcl_core:darkwood" }
end
local images = { "oak", "spruce", "birch", "jungle", "acacia", "dark_oak", "obsidian" }
local images = { "oak", "spruce", "birch", "jungle", "acacia", "dark_oak" }
for b=1, #boat_ids do
local itemstring = "mcl_boats:"..boat_ids[b]
@ -440,9 +434,8 @@ for b=1, #boat_ids do
pos = vector.add(pos, vector.multiply(dir, boat_y_offset_ground))
end
local boat = minetest.add_entity(pos, "mcl_boats:boat")
local texture = "mcl_boats_texture_"..images[b].."_boat.png"
boat:get_luaentity()._itemstring = itemstring
boat:set_properties({textures = { texture, texture, texture, texture, texture }})
boat:set_properties({textures = { "mcl_boats_texture_"..images[b].."_boat.png" }})
boat:set_yaw(placer:get_look_horizontal())
if not minetest.is_creative_enabled(placer:get_player_name()) then
itemstack:take_item()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 535 B

View File

@ -44,18 +44,18 @@ minetest.register_on_respawnplayer(function(player)
mcl_burning.extinguish(player)
end)
function mcl_burning.init_player(player)
local meta = player:get_meta()
-- NOTE: mcl_burning:data may be "return nil" (which deserialize into nil) for reasons unknown.
if meta:get_string("mcl_burning:data"):find("return nil", 1, true) then
minetest.log("warning", "[mcl_burning] 'mcl_burning:data' player meta field is invalid! Please report this bug")
end
mcl_burning.storage[player] = meta:contains("mcl_burning:data") and minetest.deserialize(meta:get_string("mcl_burning:data")) or {}
mcl_burning.channels[player] = minetest.mod_channel_join("mcl_burning:" .. player:get_player_name())
end
minetest.register_on_joinplayer(function(player)
mcl_burning.init_player(player)
local storage
local burn_data = player:get_meta():get_string("mcl_burning:data")
if burn_data == "" then
storage = {}
else
storage = minetest.deserialize(burn_data)
end
mcl_burning.storage[player] = storage
mcl_burning.channels[player] = minetest.mod_channel_join("mcl_burning:" .. player:get_player_name())
end)
minetest.register_on_leaveplayer(function(player)

View File

@ -1,66 +0,0 @@
-- Dripping Water Mod
-- by kddekadenz
local math = math
-- License of code, textures & sounds: CC0
local function register_drop(liquid, glow, sound, nodes)
minetest.register_entity("mcl_dripping:drop_" .. liquid, {
hp_max = 1,
physical = true,
collide_with_objects = false,
collisionbox = {-0.01, 0.01, -0.01, 0.01, 0.01, 0.01},
glow = glow,
pointable = false,
visual = "sprite",
visual_size = {x = 0.1, y = 0.1},
textures = {""},
spritediv = {x = 1, y = 1},
initial_sprite_basepos = {x = 0, y = 0},
static_save = false,
_dropped = false,
on_activate = function(self)
self.object:set_properties({
textures = {"[combine:2x2:" .. -math.random(1, 16) .. "," .. -math.random(1, 16) .. "=default_" .. liquid .. "_source_animated.png"}
})
end,
on_step = function(self, dtime)
local k = math.random(1, 222)
local ownpos = self.object:get_pos()
if k == 1 then
self.object:set_acceleration(vector.new(0, -5, 0))
end
if minetest.get_node(vector.offset(ownpos, 0, 0.5, 0)).name == "air" then
self.object:set_acceleration(vector.new(0, -5, 0))
end
if minetest.get_node(vector.offset(ownpos, 0, -0.1, 0)).name ~= "air" then
local ent = self.object:get_luaentity()
if not ent._dropped then
ent._dropped = true
minetest.sound_play({name = "drippingwater_" .. sound .. "drip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
end
if k < 3 then
self.object:remove()
end
end
end,
})
minetest.register_abm({
label = "Create drops",
nodenames = nodes,
neighbors = {"group:" .. liquid},
interval = 2,
chance = 22,
action = function(pos)
if minetest.get_item_group(minetest.get_node(vector.offset(pos, 0, 1, 0)).name, liquid) ~= 0
and minetest.get_node(vector.offset(pos, 0, -1, 0)).name == "air" then
local x, z = math.random(-45, 45) / 100, math.random(-45, 45) / 100
minetest.add_entity(vector.offset(pos, x, -0.520, z), "mcl_dripping:drop_" .. liquid)
end
end,
})
end
register_drop("water", 1, "", {"group:opaque", "group:leaves"})
register_drop("lava", math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3), "lava", {"group:opaque"})

View File

@ -290,10 +290,10 @@ function minetest.handle_node_drops(pos, drops, digger)
end
end
if digger and mcl_experience.throw_xp and not silk_touch_drop then
if digger and mcl_experience.throw_experience and not silk_touch_drop then
local experience_amount = minetest.get_item_group(dug_node.name,"xp")
if experience_amount > 0 then
mcl_experience.throw_xp(pos, experience_amount)
mcl_experience.throw_experience(pos, experience_amount)
end
end

View File

@ -198,20 +198,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
else
self._last_float_check = self._last_float_check + dtime
end
local pos, rou_pos, node = self.object:get_pos()
local r = 0.6
for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do
if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then
detach_driver(self)
for d = 1, #drop do
minetest.add_item(pos, drop[d])
end
self.object:remove()
return
end
end
local pos, rou_pos, node
-- Drop minecart if it isn't on a rail anymore
if self._last_float_check >= mcl_minecarts.check_float_time then
pos = self.object:get_pos()

View File

@ -122,10 +122,7 @@ mobs.death_logic = function(self, dtime)
if self.death_animation_timer >= 1.25 then
item_drop(self,false,1)
mobs.death_effect(self)
mcl_experience.throw_xp(self.object:get_pos(), math_random(self.xp_min, self.xp_max))
if self.on_die then
self.on_die(self, self.object:get_pos())
end
mcl_experience.throw_experience(self.object:get_pos(), math_random(self.xp_min, self.xp_max))
self.object:remove()
return
end
@ -158,4 +155,4 @@ mobs.death_logic = function(self, dtime)
if self.pause_timer <= 0 then
mobs.set_velocity(self,0)
end
end
end

View File

@ -37,7 +37,7 @@ mobs:register_mob("mobs_mc:creeper", {
},
makes_footstep_sound = false,
walk_velocity = 1.05,
run_velocity = 2.1,
run_velocity = 3.25,
runaway_from = { "mobs_mc:ocelot", "mobs_mc:cat" },
attack_type = "explode",
eye_height = 1.25,
@ -47,8 +47,8 @@ mobs:register_mob("mobs_mc:creeper", {
--explosion_radius = 3,
--explosion_damage_radius = 6,
--explosiontimer_reset_radius = 6,
reach = 3,
defuse_reach = 5.2,
reach = 1.5,
defuse_reach = 4,
explosion_timer = 0.3,
allow_fuse_reset = true,
stop_to_explode = true,
@ -95,8 +95,7 @@ mobs:register_mob("mobs_mc:creeper", {
if self._forced_explosion_countdown_timer then
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
if self._forced_explosion_countdown_timer <= 0 then
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
mcl_explosions.explode(mcl_util.get_object_center(self.object), self.explosion_strength, { griefing = mobs_griefing, drop_chance = 1.0}, self.object)
mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
end
end
end,
@ -152,7 +151,6 @@ mobs:register_mob("mobs_mc:creeper_charged", {
description = S("Charged Creeper"),
type = "monster",
spawn_class = "hostile",
hostile = true,
hp_min = 20,
hp_max = 20,
xp_min = 5,
@ -188,8 +186,8 @@ mobs:register_mob("mobs_mc:creeper_charged", {
--explosion_radius = 3,
--explosion_damage_radius = 6,
--explosiontimer_reset_radius = 3,
reach = 3,
defuse_reach = 5.2,
reach = 1.5,
defuse_reach = 4,
explosion_timer = 0.3,
allow_fuse_reset = true,
stop_to_explode = true,
@ -221,8 +219,7 @@ mobs:register_mob("mobs_mc:creeper_charged", {
if self._forced_explosion_countdown_timer then
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
if self._forced_explosion_countdown_timer <= 0 then
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
mcl_explosions.explode(mcl_util.get_object_center(self.object), self.explosion_strength, { griefing = mobs_griefing, drop_chance = 1.0}, self.object)
mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
end
end
end,

View File

@ -103,7 +103,7 @@ mobs:register_mob("mobs_mc:enderdragon", {
mcl_portals.spawn_gateway_portal()
mcl_structures.call_struct(self._portal_pos, "end_exit_portal_open")
if self._initial then
mcl_experience.throw_xp(pos, 11500) -- 500 + 11500 = 12000
mcl_experience.throw_experience(pos, 11500) -- 500 + 11500 = 12000
minetest.set_node(vector.add(self._portal_pos, vector.new(3, 5, 3)), {name = mobs_mc.items.dragon_egg})
end
end

View File

@ -215,9 +215,7 @@ lightning.register_on_strike(function(pos, pos2, objects)
posadd = { x=math.cos(angle),y=0,z=math.sin(angle) }
posadd = vector.normalize(posadd)
local mob = add_entity(vector.add(pos2, posadd), "mobs_mc:skeleton")
if mob then
mob:set_yaw(angle-math.pi/2)
end
mob:set_yaw(angle-math.pi/2)
angle = angle + (math.pi*2) / 3
end

View File

@ -3,8 +3,123 @@ local S = minetest.get_translator(modname)
mcl_credits = {
players = {},
description = S("A faithful Open Source clone of Minecraft"),
people = dofile(minetest.get_modpath(modname) .. "/people.lua"),
}
mcl_credits.description = S("A faithful Open Source clone of Minecraft")
-- Sub-lists are sorted by number of commits, but the list should not be rearranged (-> new contributors are just added at the end of the list)
mcl_credits.people = {
{ S("Creator of MineClone"), 0x0A9400, {
"davedevils",
}},
{ S("Creator of MineClone2"), 0xFBF837, {
"Wuzzy",
}},
{ S("Maintainers"), 0xFF51D5, {
"Fleckenstein",
"kay27",
"oilboi",
}},
{ S("Developers"), 0xF84355, {
"bzoss",
"AFCMS",
"epCode",
"ryvnf",
"iliekprogrammar",
"MysticTempest",
"Rootyjr",
"Nicu",
"aligator",
"Code-Sploit",
"NO11",
}},
{ S("Contributors"), 0x52FF00, {
"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",
"ztianyang",
"j45",
}},
{"MineClone5", 0xA60014, {
"kay27",
"Debiankaios",
"epCode",
"NO11",
"j45",
}},
{ S("Original Mod Authors"), 0x343434, {
"Wuzzy",
"Fleckenstein",
"BlockMen",
"TenPlus1",
"PilzAdam",
"ryvnf",
"stujones11",
"Arcelmi",
"celeron55",
"maikerumine",
"GunshipPenguin",
"Qwertymine3",
"Rochambeau",
"rubenwardy",
"stu",
"oilboi",
"4aiman",
"Kahrl",
"Krock",
"UgnilJoZ",
"lordfingle",
"22i",
"bzoss",
"kilbith",
"xeranas",
"kddekadenz",
"sofar",
"4Evergreen4",
"jordan4ibanez",
"paramat",
}},
{ S("3D Models"), 0x0019FF, {
"22i",
"tobyplowy",
"epCode",
}},
{ S("Textures"), 0xFF9705, {
"XSSheep",
"Wuzzy",
"kingoscargames",
"leorockway",
"xMrVizzy",
"yutyo",
"NO11",
}},
{ S("Translations"), 0x00FF60, {
"Wuzzy",
"Rocher Laurent",
"wuniversales",
"kay27",
"pitchum",
}},
}
local function add_hud_element(def, huds, y)
@ -128,7 +243,7 @@ minetest.register_globalstep(function(dtime)
y = y - 5
end
end
if y > -100 then
if id == huds.icon then
y = math.max(400, y)

View File

@ -1,144 +0,0 @@
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
return {
{S("Creator of MineClone"), 0x0A9400, {
"davedevils",
}},
{S("Creator of MineClone2"), 0xFBF837, {
"Wuzzy",
}},
{S("Maintainers"), 0xFF51D5, {
"Fleckenstein",
"Nicu",
"kay27",
}},
{S("Developers"), 0xF84355, {
"bzoss",
"AFCMS",
"epCode",
"ryvnf",
"iliekprogrammar",
"MysticTempest",
"Rootyjr",
"aligator",
"Code-Sploit",
"NO11",
"cora",
"jordan4ibanez",
}},
{S("Contributors"), 0x52FF00, {
"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",
}},
{S("MineClone5"), 0xA60014, {
"kay27",
"Debiankaios",
"epCode",
"NO11",
"j45",
}},
{S("Original Mod Authors"), 0x343434, {
"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",
}},
{S("3D Models"), 0x0019FF, {
"22i",
"tobyplowy",
"epCode",
}},
{S("Textures"), 0xFF9705, {
"XSSheep",
"Wuzzy",
"kingoscargames",
"leorockway",
"xMrVizzy",
"yutyo",
"NO11",
"kay27",
}},
{S("Translations"), 0x00FF60, {
"Wuzzy",
"Rocher Laurent",
"wuniversales",
"kay27",
"pitchum",
"todoporlalibertad",
"Marcin Serwin",
}},
{S("Funders"), 0xF7FF00, {
"40W",
}},
{S("Special thanks"), 0x00E9FF, {
"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",
}},
}

View File

@ -1,63 +0,0 @@
local S = minetest.get_translator(minetest.get_current_modname())
minetest.register_entity("mcl_experience:bottle",{
textures = {"mcl_experience_bottle.png"},
hp_max = 1,
visual_size = {x = 0.35, y = 0.35},
collisionbox = {-0.1, -0.1, -0.1, 0.1, 0.1, 0.1},
pointable = false,
on_step = function(self, dtime)
local pos = self.object:get_pos()
local node = minetest.get_node(pos)
local n = node.name
if n ~= "air" and n ~= "mcl_portals:portal" and n ~= "mcl_portals:portal_end" and minetest.get_item_group(n, "liquid") == 0 then
minetest.sound_play("mcl_potions_breaking_glass", {pos = pos, max_hear_distance = 16, gain = 1})
mcl_experience.throw_xp(pos, math.random(3, 11))
minetest.add_particlespawner({
amount = 50,
time = 0.1,
minpos = vector.add(pos, vector.new(-0.1, 0.5, -0.1)),
maxpos = vector.add(pos, vector.new( 0.1, 0.6, 0.1)),
minvel = vector.new(-2, 0, -2),
maxvel = vector.new( 2, 2, 2),
minacc = vector.new(0, 0, 0),
maxacc = vector.new(0, 0, 0),
minexptime = 0.5,
maxexptime = 1.25,
minsize = 1,
maxsize = 2,
collisiondetection = true,
vertical = false,
texture = "mcl_particles_effect.png^[colorize:blue:127",
})
self.object:remove()
end
end,
})
local function throw_xp_bottle(pos, dir, velocity)
minetest.sound_play("mcl_throwing_throw", {pos = pos, gain = 0.4, max_hear_distance = 16}, true)
local obj = minetest.add_entity(pos, "mcl_experience:bottle")
obj:set_velocity(vector.multiply(dir, velocity))
local acceleration = vector.multiply(dir, -3)
acceleration.y = -9.81
obj:set_acceleration(acceleration)
end
minetest.register_craftitem("mcl_experience:bottle", {
description = "Bottle o' Enchanting",
inventory_image = "mcl_experience_bottle.png",
wield_image = "mcl_experience_bottle.png",
stack_max = 64,
on_use = function(itemstack, placer, pointed_thing)
throw_xp_bottle(vector.add(placer:get_pos(), vector.new(0, 1.5, 0)), placer:get_look_dir(), 10)
if not minetest.is_creative_enabled(placer:get_player_name()) then
itemstack:take_item()
end
return itemstack
end,
_on_dispense = function(_, pos, _, _, dir)
throw_xp_bottle(vector.add(pos, vector.multiply(dir, 0.51)), dir, 10)
end
})

View File

@ -1,39 +0,0 @@
local S = minetest.get_translator(minetest.get_current_modname())
minetest.register_chatcommand("xp", {
params = S("[[<player>] <xp>]"),
description = S("Gives a player some XP"),
privs = {server=true},
func = function(name, params)
local player, xp = nil, 1000
local P, i = {}, 0
for str in string.gmatch(params, "([^ ]+)") do
i = i + 1
P[i] = str
end
if i > 2 then
return false, S("Error: Too many parameters!")
end
if i > 0 then
xp = tonumber(P[i])
end
if i < 2 then
player = minetest.get_player_by_name(name)
end
if i == 2 then
player = minetest.get_player_by_name(P[1])
end
if not xp then
return false, S("Error: Incorrect value of XP")
end
if not player then
return false, S("Error: Player not found")
end
mcl_experience.add_xp(player, xp)
return true, S("Added @1 XP to @2, total: @3, experience level: @4", tostring(xp), player:get_player_name(), tostring(mcl_experience.get_xp(player)), tostring(mcl_experience.get_level(player)))
end,
})

View File

@ -1,227 +1,641 @@
mcl_experience = {
on_add_xp = {},
local S = minetest.get_translator(minetest.get_current_modname())
mcl_experience = {}
local vector = vector
local math = math
local string = string
local pool = {}
local registered_nodes
local max_xp = 2^31-1
local max_orb_age = 300 -- seconds
local gravity = {x = 0, y = -((tonumber(minetest.settings:get("movement_gravity"))) or 9.81), z = 0}
local size_min, size_max = 20, 59 -- percents
local delta_size = size_max - size_min
local size_to_xp = {
{-32768, 2}, -- 1
{ 3, 6}, -- 2
{ 7, 16}, -- 3
{ 17, 36}, -- 4
{ 37, 72}, -- 5
{ 73, 148}, -- 6
{ 149, 306}, -- 7
{ 307, 616}, -- 8
{ 617, 1236}, -- 9
{ 1237, 2476}, --10
{ 2477, 32767} --11
}
local modpath = minetest.get_modpath(minetest.get_current_modname())
local function xp_to_size(xp)
local i, l = 1, #size_to_xp
while (xp > size_to_xp[i][1]) and (i < l) do
i = i + 1
end
return ((i-1) / (l-1) * delta_size + size_min)/100
end
dofile(modpath .. "/command.lua")
dofile(modpath .. "/orb.lua")
dofile(modpath .. "/bottle.lua")
minetest.register_on_mods_loaded(function()
registered_nodes = minetest.registered_nodes
end)
-- local storage
local function load_data(player)
local name = player:get_player_name()
pool[name] = {}
local temp_pool = pool[name]
local meta = player:get_meta()
temp_pool.xp = meta:get_int("xp") or 0
temp_pool.level = mcl_experience.xp_to_level(temp_pool.xp)
temp_pool.bar, temp_pool.bar_step, temp_pool.xp_next_level = mcl_experience.xp_to_bar(temp_pool.xp, temp_pool.level)
temp_pool.last_time= minetest.get_us_time()/1000000
end
local hud_bars = {}
local hud_levels = {}
local caches = {}
-- saves data to be utilized on next login
local function save_data(player)
local name = player:get_player_name()
local temp_pool = pool[name]
local meta = player:get_meta()
meta:set_int("xp", temp_pool.xp)
pool[name] = nil
end
-- helpers
local player_huds = {} -- the list of players hud lists (3d array)
hud_manager = {} -- hud manager class
local function xp_to_level(xp)
-- terminate the player's list on leave
minetest.register_on_leaveplayer(function(player)
local name = player:get_player_name()
player_huds[name] = nil
end)
-- create instance of new hud
function hud_manager.add_hud(player,hud_name,def)
local name = player:get_player_name()
if minetest.is_creative_enabled(name) then
return
end
local local_hud = player:hud_add({
hud_elem_type = def.hud_elem_type,
position = def.position,
text = def.text,
text2 = def.text2,
number = def.number,
item = def.item,
direction = def.direction,
size = def.size,
offset = def.offset,
z_index = def.z_index,
alignment = def.alignment,
scale = def.scale,
})
-- create new 3d array here
-- depends.txt is not needed
-- with it here
if not player_huds[name] then
player_huds[name] = {}
end
player_huds[name][hud_name] = local_hud
end
-- delete instance of hud
function hud_manager.remove_hud(player,hud_name)
local name = player:get_player_name()
if player_huds[name] and player_huds[name][hud_name] then
player:hud_remove(player_huds[name][hud_name])
player_huds[name][hud_name] = nil
end
end
-- change element of hud
function hud_manager.change_hud(data)
local name = data.player:get_player_name()
if player_huds[name] and player_huds[name][data.hud_name] then
data.player:hud_change(player_huds[name][data.hud_name], data.element, data.data)
end
end
-- gets if hud exists
function hud_manager.hud_exists(player,hud_name)
local name = player:get_player_name()
if player_huds[name] and player_huds[name][hud_name] then
return true
else
return false
end
end
-------------------
-- saves specific users data for when they relog
minetest.register_on_leaveplayer(function(player)
save_data(player)
end)
-- is used for shutdowns to save all data
local function save_all()
for name,_ in pairs(pool) do
local player = minetest.get_player_by_name(name)
if player then
save_data(player)
end
end
end
-- save all data to mod storage on shutdown
minetest.register_on_shutdown(function()
save_all()
end)
function mcl_experience.get_player_xp_level(player)
local name = player:get_player_name()
return pool[name].level
end
function mcl_experience.set_player_xp_level(player,level)
local name = player:get_player_name()
if level == pool[name].level then
return
end
pool[name].level = level
pool[name].xp, pool[name].bar_step, pool[name].xp_next_level = mcl_experience.bar_to_xp(pool[name].bar, level)
hud_manager.change_hud({player = player, hud_name = "xp_level", element = "text", data = tostring(level)})
-- we may don't update the bar
end
local name
local temp_pool
minetest.register_on_joinplayer(function(player)
load_data(player)
name = player:get_player_name()
temp_pool = pool[name]
hud_manager.add_hud(player,"experience_bar",
{
hud_elem_type = "image",
name = "experience bar",
text = "experience_bar_background.png^[lowpart:" .. math.floor(temp_pool.bar / 36 * 100) .. ":experience_bar.png^[transformR270",
position = {x=0.5, y=1},
offset = {x = (-9 * 28) - 3, y = -(48 + 24 + 16 - 5)},
scale = {x = 2.8, y = 3.0},
alignment = { x = 1, y = 1 },
z_index = 11,
})
hud_manager.add_hud(player,"xp_level",
{
hud_elem_type = "text", position = {x=0.5, y=1},
name = "xp_level", text = tostring(temp_pool.level),
number = 0x80FF20,
offset = {x = 0, y = -(48 + 24 + 24)},
z_index = 12,
})
end)
function mcl_experience.xp_to_level(xp)
local xp = xp or 0
local a, b, c, D
if xp > 1507 then
a, b, c = 4.5, -162.5, 2220 - xp
a, b, c = 4.5, -162.5, 2220-xp
elseif xp > 352 then
a, b, c = 2.5, -40.5, 360 - xp
a, b, c = 2.5, -40.5, 360-xp
else
a, b, c = 1, 6, -xp
end
D = b * b - 4 * a * c
D = b*b-4*a*c
if D == 0 then
return math.floor(-b / 2 / a)
elseif D > 0 then
local v1, v2 = -b / 2 / a, math.sqrt(D) / 2 / a
return math.floor(math.max(v1 - v2, v1 + v2))
return math.floor(-b/2/a)
elseif D > 0 then
local v1, v2 = -b/2/a, math.sqrt(D)/2/a
return math.floor((math.max(v1-v2, v1+v2)))
end
return 0
end
local function level_to_xp(level)
if level >= 1 and level <= 16 then
function mcl_experience.level_to_xp(level)
if (level >= 1 and level <= 16) then
return math.floor(math.pow(level, 2) + 6 * level)
elseif level >= 17 and level <= 31 then
elseif (level >= 17 and level <= 31) then
return math.floor(2.5 * math.pow(level, 2) - 40.5 * level + 360)
elseif level >= 32 then
return math.floor(4.5 * math.pow(level, 2) - 162.5 * level + 2220)
return math.floor(4.5 * math.pow(level, 2) - 162.5 * level + 2220);
end
return 0
end
local function calculate_bounds(level)
return level_to_xp(level), level_to_xp(level + 1)
function mcl_experience.xp_to_bar(xp, level)
local level = level or mcl_experience.xp_to_level(xp)
local xp_this_level = mcl_experience.level_to_xp(level)
local xp_next_level = mcl_experience.level_to_xp(level+1)
local bar_step = 36 / (xp_next_level-xp_this_level)
local bar = (xp-xp_this_level) * bar_step
return bar, bar_step, xp_next_level
end
local function xp_to_bar(xp, level)
local xp_min, xp_max = calculate_bounds(level)
return (xp - xp_min) / (xp_max - xp_min)
function mcl_experience.bar_to_xp(bar, level)
local xp_this_level = mcl_experience.level_to_xp(level)
local xp_next_level = mcl_experience.level_to_xp(level+1)
local bar_step = 36 / (xp_next_level-xp_this_level)
local xp = xp_this_level + math.floor(bar/36*(xp_next_level-xp_this_level))
return xp, bar_step, xp_next_level
end
local function bar_to_xp(bar, level)
local xp_min, xp_max = calculate_bounds(level)
function mcl_experience.add_experience(player, experience)
local name = player:get_player_name()
local temp_pool = pool[name]
return xp_min + bar * (xp_max - xp_min)
end
local function get_time()
return minetest.get_us_time() / 1000000
end
-- api
function mcl_experience.get_level(player)
return caches[player].level
end
function mcl_experience.set_level(player, level)
local cache = caches[player]
if level ~= cache.level then
mcl_experience.set_xp(player, math.floor(bar_to_xp(xp_to_bar(mcl_experience.get_xp(player), cache.level), level)))
end
end
function mcl_experience.get_xp(player)
return player:get_meta():get_int("xp")
end
function mcl_experience.set_xp(player, xp)
player:get_meta():set_int("xp", xp)
mcl_experience.update(player)
end
function mcl_experience.add_xp(player, xp)
for _, cb in ipairs(mcl_experience.on_add_xp) do
xp = cb.func(player, xp) or xp
if xp == 0 then
break
local inv = player:get_inventory()
local candidates = {
{list = "main", index = player:get_wield_index()},
{list = "armor", index = 2},
{list = "armor", index = 3},
{list = "armor", index = 4},
{list = "armor", index = 5},
}
local final_candidates = {}
for _, can in ipairs(candidates) do
local stack = inv:get_stack(can.list, can.index)
local wear = stack:get_wear()
if mcl_enchanting.has_enchantment(stack, "mending") and wear > 0 then
can.stack = stack
can.wear = wear
table.insert(final_candidates, can)
end
end
local cache = caches[player]
local old_level = cache.level
mcl_experience.set_xp(player, mcl_experience.get_xp(player) + xp)
local current_time = get_time()
if current_time - cache.last_time > 0.01 then
local name = player:get_player_name()
if old_level == cache.level then
minetest.sound_play("mcl_experience", {
to_player = name,
gain = 0.1,
pitch = math.random(75, 99) / 100,
})
cache.last_time = current_time
if #final_candidates > 0 then
local can = final_candidates[math.random(#final_candidates)]
local stack, list, index, wear = can.stack, can.list, can.index, can.wear
local uses = mcl_util.calculate_durability(stack)
local multiplier = 2 * 65535 / uses
local repair = experience * multiplier
local new_wear = wear - repair
if new_wear < 0 then
experience = math.floor(-new_wear / multiplier + 0.5)
new_wear = 0
else
minetest.sound_play("mcl_experience_level_up", {
to_player = name,
gain = 0.2,
})
cache.last_time = current_time + 0.2
experience = 0
end
stack:set_wear(math.floor(new_wear))
inv:set_stack(list, index, stack)
end
local old_bar, old_xp, old_level = temp_pool.bar, temp_pool.xp, temp_pool.level
temp_pool.xp = math.min(math.max(temp_pool.xp + experience, 0), max_xp)
if (temp_pool.xp < temp_pool.xp_next_level) and (temp_pool.xp >= old_xp) then
temp_pool.bar = temp_pool.bar + temp_pool.bar_step * experience
else
temp_pool.level = mcl_experience.xp_to_level(temp_pool.xp)
temp_pool.bar, temp_pool.bar_step, temp_pool.xp_next_level = mcl_experience.xp_to_bar(temp_pool.xp, temp_pool.level)
end
if old_bar ~= temp_pool.bar then
hud_manager.change_hud({player = player, hud_name = "experience_bar", element = "text", data = "experience_bar_background.png^[lowpart:" .. math.floor(temp_pool.bar / 36 * 100) .. ":experience_bar.png^[transformR270",})
end
if experience > 0 and minetest.get_us_time()/1000000 - temp_pool.last_time > 0.01 then
if old_level ~= temp_pool.level then
minetest.sound_play("level_up",{gain=0.2,to_player = name})
temp_pool.last_time = minetest.get_us_time()/1000000 + 0.2
else
minetest.sound_play("experience",{gain=0.1,to_player = name,pitch=math.random(75,99)/100})
temp_pool.last_time = minetest.get_us_time()/1000000
end
end
if old_level ~= temp_pool.level then
hud_manager.change_hud({player = player, hud_name = "xp_level", element = "text", data = tostring(temp_pool.level)})
end
end
function mcl_experience.throw_xp(pos, total_xp)
--reset player level
local name
local temp_pool
local xp_amount
minetest.register_on_dieplayer(function(player)
if minetest.settings:get_bool("mcl_keepInventory", false) then
return
end
name = player:get_player_name()
temp_pool = pool[name]
xp_amount = temp_pool.xp
temp_pool.xp = 0
temp_pool.level = 0
temp_pool.bar, temp_pool.bar_step, temp_pool.xp_next_level = mcl_experience.xp_to_bar(temp_pool.xp, temp_pool.level)
hud_manager.change_hud({player = player, hud_name = "xp_level", element = "text", data = tostring(temp_pool.level)})
hud_manager.change_hud({player = player, hud_name = "experience_bar", element = "text", data = "experience_bar_background.png^[lowpart:" .. math.floor(temp_pool.bar / 36 * 100) .. ":experience_bar.png^[transformR270",})
mcl_experience.throw_experience(player:get_pos(), xp_amount)
end)
local collector, pos, pos2
local direction, distance, player_velocity, goal
local currentvel, acceleration, multiplier, velocity
local node, vel, def
local is_moving, is_slippery, slippery, slip_factor
local size
local function xp_step(self, dtime)
--if item set to be collected then only execute go to player
if self.collected == true then
if not self.collector then
self.collected = false
return
end
collector = minetest.get_player_by_name(self.collector)
if collector and collector:get_hp() > 0 and vector.distance(self.object:get_pos(),collector:get_pos()) < 7.25 then
self.object:set_acceleration(vector.new(0,0,0))
self.disable_physics(self)
--get the variables
pos = self.object:get_pos()
pos2 = collector:get_pos()
player_velocity = collector:get_velocity() or collector:get_player_velocity()
pos2.y = pos2.y + 0.8
direction = vector.direction(pos,pos2)
distance = vector.distance(pos2,pos)
multiplier = distance
if multiplier < 1 then
multiplier = 1
end
goal = vector.multiply(direction,multiplier)
currentvel = self.object:get_velocity()
if distance > 1 then
multiplier = 20 - distance
velocity = vector.multiply(direction,multiplier)
goal = velocity
acceleration = vector.new(goal.x-currentvel.x,goal.y-currentvel.y,goal.z-currentvel.z)
self.object:add_velocity(vector.add(acceleration,player_velocity))
elseif distance < 0.8 then
mcl_experience.add_experience(collector, self._xp)
self.object:remove()
end
return
else
self.collector = nil
self.enable_physics(self)
end
end
self.age = self.age + dtime
if self.age > max_orb_age then
self.object:remove()
return
end
pos = self.object:get_pos()
if pos then
node = minetest.get_node_or_nil({
x = pos.x,
y = pos.y -0.25,
z = pos.z
})
else
return
end
-- Remove nodes in 'ignore'
if node and node.name == "ignore" then
self.object:remove()
return
end
if not self.physical_state then
return -- Don't do anything
end
-- Slide on slippery nodes
vel = self.object:get_velocity()
def = node and registered_nodes[node.name]
is_moving = (def and not def.walkable) or
vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
is_slippery = false
if def and def.walkable then
slippery = minetest.get_item_group(node.name, "slippery")
is_slippery = slippery ~= 0
if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then
-- Horizontal deceleration
slip_factor = 4.0 / (slippery + 4)
self.object:set_acceleration({
x = -vel.x * slip_factor,
y = 0,
z = -vel.z * slip_factor
})
elseif vel.y == 0 then
is_moving = false
end
end
if self.moving_state == is_moving and self.slippery_state == is_slippery then
-- Do not update anything until the moving state changes
return
end
self.moving_state = is_moving
self.slippery_state = is_slippery
if is_moving then
self.object:set_acceleration(gravity)
else
self.object:set_acceleration({x = 0, y = 0, z = 0})
self.object:set_velocity({x = 0, y = 0, z = 0})
end
end
minetest.register_entity("mcl_experience:orb", {
initial_properties = {
hp_max = 1,
physical = true,
collide_with_objects = false,
collisionbox = {-0.2, -0.2, -0.2, 0.2, 0.2, 0.2},
visual = "sprite",
visual_size = {x = 0.4, y = 0.4},
textures = {name="experience_orb.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0}},
spritediv = {x = 1, y = 14},
initial_sprite_basepos = {x = 0, y = 0},
is_visible = true,
pointable = false,
static_save = false,
},
moving_state = true,
slippery_state = false,
physical_state = true,
-- Item expiry
age = 0,
-- Pushing item out of solid nodes
force_out = nil,
force_out_start = nil,
--Collection Variables
collectable = false,
try_timer = 0,
collected = false,
delete_timer = 0,
radius = 4,
on_activate = function(self, staticdata, dtime_s)
self.object:set_velocity(vector.new(
math.random(-2,2)*math.random(),
math.random(2,5),
math.random(-2,2)*math.random()
))
self.object:set_armor_groups({immortal = 1})
self.object:set_velocity({x = 0, y = 2, z = 0})
self.object:set_acceleration(gravity)
local xp = tonumber(staticdata)
self._xp = xp
size = xp_to_size(xp)
self.object:set_properties({
visual_size = {x = size, y = size},
glow = 14,
})
self.object:set_sprite({x=1,y=math.random(1,14)}, 14, 0.05, false)
end,
enable_physics = function(self)
if not self.physical_state then
self.physical_state = true
self.object:set_properties({physical = true})
self.object:set_velocity({x=0, y=0, z=0})
self.object:set_acceleration(gravity)
end
end,
disable_physics = function(self)
if self.physical_state then
self.physical_state = false
self.object:set_properties({physical = false})
self.object:set_velocity({x=0, y=0, z=0})
self.object:set_acceleration({x=0, y=0, z=0})
end
end,
on_step = function(self, dtime)
xp_step(self, dtime)
end,
})
minetest.register_chatcommand("xp", {
params = S("[[<player>] <xp>]"),
description = S("Gives a player some XP"),
privs = {server=true},
func = function(name, params)
local player, xp = nil, 1000
local P, i = {}, 0
for str in string.gmatch(params, "([^ ]+)") do
i = i + 1
P[i] = str
end
if i > 2 then
return false, S("Error: Too many parameters!")
end
if i > 0 then
xp = tonumber(P[i])
end
if i < 2 then
player = minetest.get_player_by_name(name)
end
if i == 2 then
player = minetest.get_player_by_name(P[1])
end
if not xp then
return false, S("Error: Incorrect value of XP")
end
if not player then
return false, S("Error: Player not found")
end
mcl_experience.add_experience(player, xp)
local playername = player:get_player_name()
minetest.chat_send_player(name, S("Added @1 XP to @2, total: @3, experience level: @4", tostring(xp), playername, tostring(pool[playername].xp), tostring(pool[playername].level)))
end,
})
function mcl_experience.throw_experience(pos, amount)
local i, j = 0, 0
while i < total_xp and j < 100 do
local xp = math.min(math.random(1, math.min(32767, total_xp - math.floor(i / 2))), total_xp - i)
local obj = minetest.add_entity(pos, "mcl_experience:orb", tostring(xp))
local obj, xp
while i < amount and j < 100 do
xp = math.min(math.random(1, math.min(32767, amount-math.floor(i/2))), amount-i)
obj = minetest.add_entity(pos, "mcl_experience:orb", tostring(xp))
if not obj then
return false
end
obj:set_velocity(vector.new(
math.random(-2, 2) * math.random(),
math.random( 2, 5),
math.random(-2, 2) * math.random()
))
obj:set_velocity({
x=math.random(-2,2)*math.random(),
y=math.random(2,5),
z=math.random(-2,2)*math.random()
})
i = i + xp
j = j + 1
end
end
function mcl_experience.update(player)
local xp = mcl_experience.get_xp(player)
local cache = caches[player]
cache.level = xp_to_level(xp)
if not minetest.is_creative_enabled(player:get_player_name()) then
player:hud_change(hud_bars[player], "text", "mcl_experience_bar_background.png^[lowpart:"
.. math.floor(math.floor(xp_to_bar(xp, cache.level) * 18) / 18 * 100)
.. ":mcl_experience_bar.png^[transformR270"
)
if cache.level == 0 then
player:hud_change(hud_levels[player], "text", "")
else
player:hud_change(hud_levels[player], "text", tostring(cache.level))
minetest.register_entity("mcl_experience:bottle",{
textures = {"mcl_experience_bottle.png"},
hp_max = 1,
visual_size = {x = 0.35, y = 0.35},
collisionbox = {-0.1, -0.1, -0.1, 0.1, 0.1, 0.1},
pointable = false,
on_step = function(self, dtime)
local pos = self.object:get_pos()
local node = minetest.get_node(pos)
local n = node.name
if n ~= "air" and n ~= "mcl_portals:portal" and n ~= "mcl_portals:portal_end" and minetest.get_item_group(n, "liquid") == 0 then
minetest.sound_play("mcl_potions_breaking_glass", {pos = pos, max_hear_distance = 16, gain = 1})
mcl_experience.throw_experience(pos, math.random(3, 11))
minetest.add_particlespawner({
amount = 50,
time = 0.1,
minpos = vector.add(pos, vector.new(-0.1, 0.5, -0.1)),
maxpos = vector.add(pos, vector.new( 0.1, 0.6, 0.1)),
minvel = vector.new(-2, 0, -2),
maxvel = vector.new( 2, 2, 2),
minacc = vector.new(0, 0, 0),
maxacc = vector.new(0, 0, 0),
minexptime = 0.5,
maxexptime = 1.25,
minsize = 1,
maxsize = 2,
collisiondetection = true,
vertical = false,
texture = "mcl_particles_effect.png^[colorize:blue:127",
})
self.object:remove()
end
end
end,
})
local function throw_xp_bottle(pos, dir, velocity)
minetest.sound_play("mcl_throwing_throw", {pos = pos, gain = 0.4, max_hear_distance = 16}, true)
local obj = minetest.add_entity(pos, "mcl_experience:bottle")
obj:set_velocity(vector.multiply(dir, velocity))
local acceleration = vector.multiply(dir, -3)
acceleration.y = -9.81
obj:set_acceleration(acceleration)
end
function mcl_experience.register_on_add_xp(func, priority)
table.insert(mcl_experience.on_add_xp, {func = func, priority = priority or 0})
end
-- callbacks
minetest.register_on_joinplayer(function(player)
caches[player] = {
last_time = get_time(),
}
if not minetest.is_creative_enabled(player:get_player_name()) then
hud_bars[player] = player:hud_add({
hud_elem_type = "image",
position = {x = 0.5, y = 1},
offset = {x = (-9 * 28) - 3, y = -(48 + 24 + 16 - 5)},
scale = {x = 2.8, y = 3.0},
alignment = {x = 1, y = 1},
z_index = 11,
})
hud_levels[player] = player:hud_add({
hud_elem_type = "text",
position = {x = 0.5, y = 1},
number = 0x80FF20,
offset = {x = 0, y = -(48 + 24 + 24)},
z_index = 12,
})
minetest.register_craftitem("mcl_experience:bottle", {
description = "Bottle o' Enchanting",
inventory_image = "mcl_experience_bottle.png",
wield_image = "mcl_experience_bottle.png",
stack_max = 64,
on_use = function(itemstack, placer, pointed_thing)
throw_xp_bottle(vector.add(placer:get_pos(), vector.new(0, 1.5, 0)), placer:get_look_dir(), 10)
if not minetest.is_creative_enabled(placer:get_player_name()) then
itemstack:take_item()
end
return itemstack
end,
_on_dispense = function(_, pos, _, _, dir)
throw_xp_bottle(vector.add(pos, vector.multiply(dir, 0.51)), dir, 10)
end
mcl_experience.update(player)
end)
minetest.register_on_leaveplayer(function(player)
hud_bars[player] = nil
hud_levels[player] = nil
caches[player] = nil
end)
minetest.register_on_dieplayer(function(player)
if not minetest.settings:get_bool("mcl_keepInventory", false) then
mcl_experience.throw_xp(player:get_pos(), mcl_experience.get_xp(player))
mcl_experience.set_xp(player, 0)
end
end)
minetest.register_on_mods_loaded(function()
table.sort(mcl_experience.on_add_xp, function(a, b) return a.priority < b.priority end)
end)
})

View File

@ -1,220 +0,0 @@
local size_min, size_max = 20, 59
local delta_size = size_max - size_min
local size_to_xp = {
{-32768, 2}, -- 1
{ 3, 6}, -- 2
{ 7, 16}, -- 3
{ 17, 36}, -- 4
{ 37, 72}, -- 5
{ 73, 148}, -- 6
{ 149, 306}, -- 7
{ 307, 616}, -- 8
{ 617, 1236}, -- 9
{ 1237, 2476}, -- 10
{ 2477, 32767} -- 11
}
local function xp_to_size(xp)
local i, l = 1, #size_to_xp
while xp > size_to_xp[i][1] and i < l do
i = i + 1
end
return ((i - 1) / (l - 1) * delta_size + size_min) / 100
end
local max_orb_age = 300 -- seconds
local gravity = vector.new(0, -((tonumber(minetest.settings:get("movement_gravity"))) or 9.81), 0)
local collector, pos, pos2
local direction, distance, player_velocity, goal
local currentvel, acceleration, multiplier, velocity
local node, vel, def
local is_moving, is_slippery, slippery, slip_factor
local size
local function xp_step(self, dtime)
--if item set to be collected then only execute go to player
if self.collected == true then
if not self.collector then
self.collected = false
return
end
collector = minetest.get_player_by_name(self.collector)
if collector and collector:get_hp() > 0 and vector.distance(self.object:get_pos(),collector:get_pos()) < 7.25 then
self.object:set_acceleration(vector.new(0,0,0))
self.disable_physics(self)
--get the variables
pos = self.object:get_pos()
pos2 = collector:get_pos()
player_velocity = collector:get_velocity() or collector:get_player_velocity()
pos2.y = pos2.y + 0.8
direction = vector.direction(pos,pos2)
distance = vector.distance(pos2,pos)
multiplier = distance
if multiplier < 1 then
multiplier = 1
end
goal = vector.multiply(direction,multiplier)
currentvel = self.object:get_velocity()
if distance > 1 then
multiplier = 20 - distance
velocity = vector.multiply(direction,multiplier)
goal = velocity
acceleration = vector.new(goal.x-currentvel.x,goal.y-currentvel.y,goal.z-currentvel.z)
self.object:add_velocity(vector.add(acceleration,player_velocity))
elseif distance < 0.8 then
mcl_experience.add_xp(collector, self._xp)
self.object:remove()
end
return
else
self.collector = nil
self.enable_physics(self)
end
end
self.age = self.age + dtime
if self.age > max_orb_age then
self.object:remove()
return
end
pos = self.object:get_pos()
if pos then
node = minetest.get_node_or_nil({
x = pos.x,
y = pos.y -0.25,
z = pos.z
})
else
return
end
-- Remove nodes in 'ignore'
if node and node.name == "ignore" then
self.object:remove()
return
end
if not self.physical_state then
return -- Don't do anything
end
-- Slide on slippery nodes
vel = self.object:get_velocity()
def = node and minetest.registered_nodes[node.name]
is_moving = (def and not def.walkable) or
vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
is_slippery = false
if def and def.walkable then
slippery = minetest.get_item_group(node.name, "slippery")
is_slippery = slippery ~= 0
if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then
-- Horizontal deceleration
slip_factor = 4.0 / (slippery + 4)
self.object:set_acceleration({
x = -vel.x * slip_factor,
y = 0,
z = -vel.z * slip_factor
})
elseif vel.y == 0 then
is_moving = false
end
end
if self.moving_state == is_moving and self.slippery_state == is_slippery then
-- Do not update anything until the moving state changes
return
end
self.moving_state = is_moving
self.slippery_state = is_slippery
if is_moving then
self.object:set_acceleration(gravity)
else
self.object:set_acceleration({x = 0, y = 0, z = 0})
self.object:set_velocity({x = 0, y = 0, z = 0})
end
end
minetest.register_entity("mcl_experience:orb", {
initial_properties = {
hp_max = 1,
physical = true,
collide_with_objects = false,
collisionbox = {-0.2, -0.2, -0.2, 0.2, 0.2, 0.2},
visual = "sprite",
visual_size = {x = 0.4, y = 0.4},
textures = {name="mcl_experience_orb.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0}},
spritediv = {x = 1, y = 14},
initial_sprite_basepos = {x = 0, y = 0},
is_visible = true,
pointable = false,
static_save = false,
},
moving_state = true,
slippery_state = false,
physical_state = true,
-- Item expiry
age = 0,
-- Pushing item out of solid nodes
force_out = nil,
force_out_start = nil,
--Collection Variables
collectable = false,
try_timer = 0,
collected = false,
delete_timer = 0,
radius = 4,
on_activate = function(self, staticdata, dtime_s)
self.object:set_velocity(vector.new(
math.random(-2,2)*math.random(),
math.random(2,5),
math.random(-2,2)*math.random()
))
self.object:set_armor_groups({immortal = 1})
self.object:set_velocity({x = 0, y = 2, z = 0})
self.object:set_acceleration(gravity)
local xp = tonumber(staticdata)
self._xp = xp
size = xp_to_size(xp)
self.object:set_properties({
visual_size = {x = size, y = size},
glow = 14,
})
self.object:set_sprite({x=1,y=math.random(1,14)}, 14, 0.05, false)
end,
enable_physics = function(self)
if not self.physical_state then
self.physical_state = true
self.object:set_properties({physical = true})
self.object:set_velocity({x=0, y=0, z=0})
self.object:set_acceleration(gravity)
end
end,
disable_physics = function(self)
if self.physical_state then
self.physical_state = false
self.object:set_properties({physical = false})
self.object:set_velocity({x=0, y=0, z=0})
self.object:set_acceleration({x=0, y=0, z=0})
end
end,
on_step = function(self, dtime)
xp_step(self, dtime)
end,
})

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 891 B

After

Width:  |  Height:  |  Size: 891 B

View File

@ -1,6 +1,5 @@
local S = minetest.get_translator(minetest.get_current_modname())
local F = minetest.formspec_escape
local C = minetest.colorize
-- Prepare player info table
local players = {}
@ -290,19 +289,6 @@ filtername["inv"] = S("Survival Inventory")
bg["default"] = dark_bg
end]]
local function get_stack_size(player)
return player:get_meta():get_int("mcl_inventory:switch_stack")
end
local function set_stack_size(player, n)
player:get_meta():set_int("mcl_inventory:switch_stack", n)
end
minetest.register_on_joinplayer(function (player)
if get_stack_size(player) == 0 then
set_stack_size(player, 64)
end
end)
function mcl_inventory.set_creative_formspec(player, start_i, pagenum, inv_size, show, page, filter)
--reset_menu_item_bg()
@ -363,8 +349,6 @@ function mcl_inventory.set_creative_formspec(player, start_i, pagenum, inv_size,
armor_slot_imgs = armor_slot_imgs .. "image[5.5,2.75;1,1;mcl_inventory_empty_armor_slot_boots.png]"
end
local stack_size = get_stack_size(player)
-- Survival inventory slots
main_list = "list[current_player;main;0,3.75;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,3.75,9,3)..
@ -392,11 +376,7 @@ function mcl_inventory.set_creative_formspec(player, start_i, pagenum, inv_size,
-- achievements button
"image_button[9,4;1,1;mcl_achievements_button.png;__mcl_achievements;]"..
--"style_type[image_button;border=;bgimg=;bgimg_pressed=]"..
"tooltip[__mcl_achievements;"..F(S("Achievements")).."]"..
-- switch stack size button
"image_button[9,5;1,1;default_apple.png;__switch_stack;]"..
"label[9.4,5.4;".. F(C("#FFFFFF", stack_size ~= 1 and stack_size or "")) .."]"..
"tooltip[__switch_stack;"..F(S("Switch stack size")).."]"
"tooltip[__mcl_achievements;"..F(S("Achievements")).."]"
-- For shortcuts
listrings = listrings ..
@ -564,12 +544,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
elseif fields.search and not fields.creative_next and not fields.creative_prev then
set_inv_search(string.lower(fields.search),player)
page = "nix"
elseif fields.__switch_stack then
local switch = 1
if get_stack_size(player) == 1 then
switch = 64
end
set_stack_size(player, switch)
end
if page then
@ -693,11 +667,3 @@ minetest.register_on_joinplayer(function(player)
init(player)
mcl_inventory.set_creative_formspec(player, 0, 1, nil, false, "nix", "")
end)
minetest.register_on_player_inventory_action(function(player, action, inventory, inventory_info)
if minetest.is_creative_enabled(player:get_player_name()) and get_stack_size(player) == 64 and action == "put" and inventory_info.listname == "main" then
local stack = inventory_info.stack
stack:set_count(stack:get_stack_max())
player:get_inventory():set_stack("main", inventory_info.index, stack)
end
end)

View File

@ -92,7 +92,7 @@ local function get_arrow(player)
return arrow_stack, arrow_stack_id
end
local function player_shoot_arrow(wielditem, player, power, damage, is_critical)
local function player_shoot_arrow(itemstack, player, power, damage, is_critical)
local has_multishot_enchantment = mcl_enchanting.has_enchantment(player:get_wielded_item(), "multishot")
local arrow_itemstring = wielditem:get_meta():get("arrow")
@ -286,7 +286,7 @@ end)
controls.register_on_press(function(player, key, time)
if key~="LMB" then return end
local wielditem = player:get_wielded_item()
wielditem = player:get_wielded_item()
if wielditem:get_name()=="mcl_bows:crossbow_loaded" or wielditem:get_name()=="mcl_bows:crossbow_loaded_enchanted" then
local enchanted = mcl_enchanting.is_enchanted(wielditem:get_name())
local speed, damage

View File

@ -293,7 +293,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile
local function drop_items_chest(pos, oldnode, oldmetadata)
local meta = minetest.get_meta(pos)
local meta2 = meta:to_table()
local meta2 = meta
if oldmetadata then
meta:from_table(oldmetadata)
end
@ -305,7 +305,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile
minetest.add_item(p, stack)
end
end
meta:from_table(meta2)
meta:from_table(meta2:to_table())
end
local function on_chest_blast(pos)
@ -608,12 +608,10 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile
on_rightclick = function(pos, node, clicker)
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left")
local above_def = minetest.registered_nodes[minetest.get_node({x = pos.x, y = pos.y + 1, z = pos.z}).name]
local above_def_other = minetest.registered_nodes[minetest.get_node({x = pos_other.x, y = pos_other.y + 1, z = pos_other.z}).name]
if not above_def or above_def.groups.opaque == 1 or not above_def_other or above_def_other.groups.opaque == 1 then
-- won't open if there is no space from the top
return false
if minetest.registered_nodes[minetest.get_node({x = pos.x, y = pos.y + 1, z = pos.z}).name].groups.opaque == 1
or minetest.registered_nodes[minetest.get_node({x = pos_other.x, y = pos_other.y + 1, z = pos_other.z}).name].groups.opaque == 1 then
-- won't open if there is no space from the top
return false
end
local name = minetest.get_meta(pos):get_string("name")

View File

@ -773,7 +773,8 @@ end
local grass_spread_randomizer = PseudoRandom(minetest.get_mapgen_setting("seed"))
function mcl_core.get_grass_palette_index(pos)
-- Return appropriate grass block node for pos
function mcl_core.get_grass_block_type(pos)
local biome_data = minetest.get_biome_data(pos)
local index = 0
if biome_data then
@ -784,12 +785,7 @@ function mcl_core.get_grass_palette_index(pos)
index = reg_biome._mcl_palette_index
end
end
return index
end
-- Return appropriate grass block node for pos
function mcl_core.get_grass_block_type(pos)
return {name = "mcl_core:dirt_with_grass", param2 = mcl_core.get_grass_palette_index(pos)}
return {name="mcl_core:dirt_with_grass", param2=index}
end
------------------------------

View File

@ -365,7 +365,7 @@ minetest.register_node("mcl_core:dirt_with_grass", {
overlay_tiles = {"mcl_core_grass_block_top.png", "", {name="mcl_core_grass_block_side_overlay.png", tileable_vertical=false}},
palette = "mcl_core_palette_grass.png",
palette_index = 0,
color = "#8EB971",
color = "#55aa60",
is_ground_content = true,
stack_max = 64,
groups = {handy=1,shovely=1,dirt=2,grass_block=1, grass_block_no_snow=1, soil=1, soil_sapling=2, soil_sugarcane=1, cultivatable=2, spreading_dirt_type=1, enderman_takable=1, building_block=1},

View File

@ -53,10 +53,7 @@ minetest.register_node("mcl_core:reeds", {
_doc_items_longdesc = S("Sugar canes are a plant which has some uses in crafting. Sugar canes will slowly grow up to 3 blocks when they are next to water and are placed on a grass block, dirt, sand, red sand, podzol or coarse dirt. When a sugar cane is broken, all sugar canes connected above will break as well."),
_doc_items_usagehelp = S("Sugar canes can only be placed top of other sugar canes and on top of blocks on which they would grow."),
drawtype = "plantlike",
paramtype2 = "color",
tiles = {"default_papyrus.png"},
palette = "mcl_core_palette_grass.png",
palette_index = 0,
inventory_image = "mcl_core_reeds.png",
wield_image = "mcl_core_reeds.png",
paramtype = "light",
@ -82,7 +79,6 @@ minetest.register_node("mcl_core:reeds", {
groups = {dig_immediate=3, craftitem=1, deco_block=1, plant=1, non_mycelium_plant=1, dig_by_piston=1},
sounds = mcl_sounds.node_sound_leaves_defaults(),
node_placement_prediction = "",
drop = "mcl_core:reeds", -- to prevent color inheritation
on_place = mcl_util.generate_on_place_plant_function(function(place_pos, place_node)
local soil_pos = {x=place_pos.x, y=place_pos.y-1, z=place_pos.z}
local soil_node = minetest.get_node_or_nil(soil_pos)
@ -118,15 +114,6 @@ minetest.register_node("mcl_core:reeds", {
return false
end),
on_construct = function(pos)
local node = minetest.get_node(pos)
if node.param2 == 0 then
node.param2 = mcl_core.get_grass_palette_index(pos)
if node.param2 ~= 0 then
minetest.set_node(pos, node)
end
end
end,
_mcl_blast_resistance = 0,
_mcl_hardness = 0,
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 277 B

View File

@ -1,14 +0,0 @@
# mcl_dye
# Bone meal API
Callback and particle functions.
## mcl_dye.add_bone_meal_particle(pos, def)
Spawns standard or custom bone meal particles.
* `pos`: position, is ignored if you define def.minpos and def.maxpos
* `def`: (optional) particle definition
## mcl_dye.register_on_bone_meal_apply(function(pointed_thing, user))
Called when the bone meal is applied anywhere.
* `pointed_thing`: exact pointing location (see Minetest API), where the bone meal is applied
* `user`: ObjectRef of the player who aplied the bone meal, can be nil!

View File

@ -128,35 +128,26 @@ for _, row in ipairs(dyelocal.dyes) do
end
-- Bone Meal
function mcl_dye.add_bone_meal_particle(pos, def)
if not def then
def = {}
end
local function bone_meal_particle(pos)
minetest.add_particlespawner({
amount = def.amount or 10,
time = def.time or 0.1,
minpos = def.minpos or vector.subtract(pos, 0.5),
maxpos = def.maxpos or vector.add(pos, 0.5),
minvel = def.minvel or vector.new(-0.01, 0.01, -0.01),
maxvel = def.maxvel or vector.new(0.01, 0.01, 0.01),
minacc = def.minacc or vector.new(0, 0, 0),
maxacc = def.maxacc or vector.new(0, 0, 0),
minexptime = def.minexptime or 1,
maxexptime = def.maxexptime or 4,
minsize = def.minsize or 0.7,
maxsize = def.maxsize or 2.4,
amount = 10,
time = 0.1,
minpos = { x = pos.x - 0.5, y = pos.y - 0.5, z = pos.z - 0.5 },
maxpos = { x = pos.x + 0.5, y = pos.y + 0.5, z = pos.z + 0.5 },
minvel = { x = 0, y = 0, z = 0},
maxvel = { x = 0, y = 0, z = 0},
minacc = { x = 0, y = 0, z = 0},
maxacc = { x = 0, y = 0, z = 0},
minexptime = 1,
maxexptime = 4,
minsize = 0.7,
maxsize = 2.4,
texture = "mcl_particles_bonemeal.png^[colorize:#00EE00:125", -- TODO: real MC color
glow = def.glow or 1,
glow = 5,
})
end
mcl_dye.bone_meal_callbacks = {}
function mcl_dye.register_on_bone_meal_apply(func)
table.insert(mcl_dye.bone_meal_callbacks, func)
end
local function apply_bone_meal(pointed_thing)
function mcl_dye.apply_bone_meal(pointed_thing)
-- Bone meal currently spawns all flowers found in the plains.
local flowers_table_plains = {
"mcl_flowers:dandelion",
@ -192,21 +183,14 @@ local function apply_bone_meal(pointed_thing)
local pos = pointed_thing.under
local n = minetest.get_node(pos)
if n.name == "" then return false end
for _, func in pairs(mcl_dye.bone_meal_callbacks) do
if func(pointed_thing, user) then
return true
end
end
if minetest.get_item_group(n.name, "sapling") >= 1 then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
-- Saplings: 45% chance to advance growth stage
if math.random(1,100) <= 45 then
return mcl_core.grow_sapling(pos, n)
end
elseif minetest.get_item_group(n.name, "mushroom") == 1 then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
-- Try to grow huge mushroom
-- Must be on a dirt-type block
@ -256,71 +240,71 @@ local function apply_bone_meal(pointed_thing)
return false
-- Wheat, Potato, Carrot, Pumpkin Stem, Melon Stem: Advance by 2-5 stages
elseif string.find(n.name, "mcl_farming:wheat_") then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
local stages = math.random(2, 5)
return mcl_farming:grow_plant("plant_wheat", pos, n, stages, true)
elseif string.find(n.name, "mcl_farming:potato_") then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
local stages = math.random(2, 5)
return mcl_farming:grow_plant("plant_potato", pos, n, stages, true)
elseif string.find(n.name, "mcl_farming:carrot_") then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
local stages = math.random(2, 5)
return mcl_farming:grow_plant("plant_carrot", pos, n, stages, true)
elseif string.find(n.name, "mcl_farming:pumpkin_") then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
local stages = math.random(2, 5)
return mcl_farming:grow_plant("plant_pumpkin_stem", pos, n, stages, true)
elseif string.find(n.name, "mcl_farming:melontige_") then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
local stages = math.random(2, 5)
return mcl_farming:grow_plant("plant_melon_stem", pos, n, stages, true)
elseif string.find(n.name, "mcl_farming:beetroot_") then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
-- Beetroot: 75% chance to advance to next stage
if math.random(1, 100) <= 75 then
return mcl_farming:grow_plant("plant_beetroot", pos, n, 1, true)
end
elseif n.name == "mcl_cocoas:cocoa_1" or n.name == "mcl_cocoas:cocoa_2" then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
-- Cocoa: Advance by 1 stage
mcl_cocoas.grow(pos)
return true
elseif minetest.get_item_group(n.name, "grass_block") == 1 then
local grass_block_pos = {x = pos.x, y = pos.y + 1, z = pos.z}
bone_meal_particle(grass_block_pos)
-- Grass Block: Generate tall grass and random flowers all over the place
for i = -7, 7 do
for j = -7, 7 do
for y = -1, 1 do
pos = vector.offset(pointed_thing.above, i, y, j)
n = minetest.get_node(pos)
local n2 = minetest.get_node(vector.offset(pos, 0, -1, 0))
for i = -2, 2 do
for j = -2, 2 do
pos = pointed_thing.above
pos = {x=pos.x+i, y=pos.y, z=pos.z+j}
n = minetest.get_node(pos)
local n2 = minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z})
if n.name ~= "" and n.name == "air" and (minetest.get_item_group(n2.name, "grass_block_no_snow") == 1) then
-- Randomly generate flowers, tall grass or nothing
if math.random(1, 100) <= 90 / ((math.abs(i) + math.abs(j)) / 2)then
-- 90% tall grass, 10% flower
mcl_dye.add_bone_meal_particle(pos, {amount = 4})
if math.random(1,100) <= 90 then
local col = n2.param2
minetest.add_node(pos, {name="mcl_flowers:tallgrass", param2=col})
if n.name ~= "" and n.name == "air" and (minetest.get_item_group(n2.name, "grass_block_no_snow") == 1) then
-- Randomly generate flowers, tall grass or nothing
if math.random(1,100) <= 90 then
-- 90% tall grass, 10% flower
if math.random(1,100) <= 90 then
local col = n2.param2
minetest.add_node(pos, {name="mcl_flowers:tallgrass", param2=col})
else
local flowers_table
if mg_name == "v6" then
flowers_table = flowers_table_plains
else
local flowers_table
if mg_name == "v6" then
local biome = minetest.get_biome_name(minetest.get_biome_data(pos).biome)
if biome == "Swampland" or biome == "Swampland_shore" or biome == "Swampland_ocean" or biome == "Swampland_deep_ocean" or biome == "Swampland_underground" then
flowers_table = flowers_table_swampland
elseif biome == "FlowerForest" or biome == "FlowerForest_beach" or biome == "FlowerForest_ocean" or biome == "FlowerForest_deep_ocean" or biome == "FlowerForest_underground" then
flowers_table = flowers_table_flower_forest
elseif biome == "Plains" or biome == "Plains_beach" or biome == "Plains_ocean" or biome == "Plains_deep_ocean" or biome == "Plains_underground" or biome == "SunflowerPlains" or biome == "SunflowerPlains_ocean" or biome == "SunflowerPlains_deep_ocean" or biome == "SunflowerPlains_underground" then
flowers_table = flowers_table_plains
else
local biome = minetest.get_biome_name(minetest.get_biome_data(pos).biome)
if biome == "Swampland" or biome == "Swampland_shore" or biome == "Swampland_ocean" or biome == "Swampland_deep_ocean" or biome == "Swampland_underground" then
flowers_table = flowers_table_swampland
elseif biome == "FlowerForest" or biome == "FlowerForest_beach" or biome == "FlowerForest_ocean" or biome == "FlowerForest_deep_ocean" or biome == "FlowerForest_underground" then
flowers_table = flowers_table_flower_forest
elseif biome == "Plains" or biome == "Plains_beach" or biome == "Plains_ocean" or biome == "Plains_deep_ocean" or biome == "Plains_underground" or biome == "SunflowerPlains" or biome == "SunflowerPlains_ocean" or biome == "SunflowerPlains_deep_ocean" or biome == "SunflowerPlains_underground" then
flowers_table = flowers_table_plains
else
flowers_table = flowers_table_simple
end
flowers_table = flowers_table_simple
end
minetest.add_node(pos, {name=flowers_table[math.random(1, #flowers_table)]})
end
minetest.add_node(pos, {name=flowers_table[math.random(1, #flowers_table)]})
end
end
end
@ -330,24 +314,24 @@ local function apply_bone_meal(pointed_thing)
-- Double flowers: Drop corresponding item
elseif n.name == "mcl_flowers:rose_bush" or n.name == "mcl_flowers:rose_bush_top" then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
minetest.add_item(pos, "mcl_flowers:rose_bush")
return true
elseif n.name == "mcl_flowers:peony" or n.name == "mcl_flowers:peony_top" then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
minetest.add_item(pos, "mcl_flowers:peony")
return true
elseif n.name == "mcl_flowers:lilac" or n.name == "mcl_flowers:lilac_top" then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
minetest.add_item(pos, "mcl_flowers:lilac")
return true
elseif n.name == "mcl_flowers:sunflower" or n.name == "mcl_flowers:sunflower_top" then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
minetest.add_item(pos, "mcl_flowers:sunflower")
return true
elseif n.name == "mcl_flowers:tallgrass" then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
-- Tall Grass: Grow into double tallgrass
local toppos = { x=pos.x, y=pos.y+1, z=pos.z }
local topnode = minetest.get_node(toppos)
@ -358,7 +342,7 @@ local function apply_bone_meal(pointed_thing)
end
elseif n.name == "mcl_flowers:fern" then
mcl_dye.add_bone_meal_particle(pos)
bone_meal_particle(pos)
-- Fern: Grow into large fern
local toppos = { x=pos.x, y=pos.y+1, z=pos.z }
local topnode = minetest.get_node(toppos)
@ -390,7 +374,7 @@ minetest.register_craftitem("mcl_dye:white", {
end
-- Use the bone meal on the ground
if (apply_bone_meal(pointed_thing, user) and (not minetest.is_creative_enabled(user:get_player_name()))) then
if(mcl_dye.apply_bone_meal(pointed_thing) and (not minetest.is_creative_enabled(user:get_player_name()))) then
itemstack:take_item()
end
return itemstack
@ -403,7 +387,7 @@ minetest.register_craftitem("mcl_dye:white", {
else
pointed_thing = { above = pos, under = droppos }
end
local success = apply_bone_meal(pointed_thing, nil)
local success = mcl_dye.apply_bone_meal(pointed_thing)
if success then
stack:take_item()
end

View File

@ -379,49 +379,6 @@ mcl_enchanting.enchantments.mending = {
inv_tool_tab = true,
}
mcl_experience.register_on_add_xp(function(player, xp)
local inv = player:get_inventory()
local candidates = {
{list = "main", index = player:get_wield_index()},
{list = "armor", index = 2},
{list = "armor", index = 3},
{list = "armor", index = 4},
{list = "armor", index = 5},
}
local final_candidates = {}
for _, can in ipairs(candidates) do
local stack = inv:get_stack(can.list, can.index)
local wear = stack:get_wear()
if mcl_enchanting.has_enchantment(stack, "mending") and wear > 0 then
can.stack = stack
can.wear = wear
table.insert(final_candidates, can)
end
end
if #final_candidates > 0 then
local can = final_candidates[math.random(#final_candidates)]
local stack, list, index, wear = can.stack, can.list, can.index, can.wear
local uses = mcl_util.calculate_durability(stack)
local multiplier = 2 * 65535 / uses
local repair = xp * multiplier
local new_wear = wear - repair
if new_wear < 0 then
xp = math.floor(-new_wear / multiplier + 0.5)
new_wear = 0
else
xp = 0
end
stack:set_wear(math.floor(new_wear))
inv:set_stack(list, index, stack)
end
return xp
end, 0)
mcl_enchanting.enchantments.multishot = {
name = S("Multishot"),

View File

@ -499,7 +499,7 @@ function mcl_enchanting.show_enchanting_formspec(player)
.. "real_coordinates[true]"
.. "image[3.15,0.6;7.6,4.1;mcl_enchanting_button_background.png]"
local itemstack = inv:get_stack("enchanting_item", 1)
local player_levels = mcl_experience.get_level(player)
local player_levels = mcl_experience.get_player_xp_level(player)
local y = 0.65
local any_enchantment = false
local table_slots = mcl_enchanting.get_table_slots(player, itemstack, num_bookshelves)
@ -549,11 +549,11 @@ function mcl_enchanting.handle_formspec_fields(player, formname, fields)
if not slot then
return
end
local player_level = mcl_experience.get_level(player)
local player_level = mcl_experience.get_player_xp_level(player)
if player_level < slot.level_requirement then
return
end
mcl_experience.set_level(player, player_level - button_pressed)
mcl_experience.set_player_xp_level(player, player_level - button_pressed)
inv:remove_item("enchanting_lapis", cost)
mcl_enchanting.set_enchanted_itemstring(itemstack)
mcl_enchanting.set_enchantments(itemstack, slot.enchantments)

View File

@ -183,7 +183,7 @@ minetest.register_entity("mcl_enchanting:book", {
collisionbox = {0, 0, 0},
pointable = false,
physical = false,
textures = {"mcl_enchanting_book_entity.png", "mcl_enchanting_book_entity.png", "mcl_enchanting_book_entity.png", "mcl_enchanting_book_entity.png", "mcl_enchanting_book_entity.png"},
textures = {"mcl_enchanting_book_entity.png"},
static_save = false,
},
_player_near = false,

View File

@ -37,7 +37,7 @@ local fish = function(itemstack, player, pointed_thing)
local num = 0
local ent = nil
local noent = true
local durability = 65
local unbreaking = mcl_enchanting.get_enchantment(itemstack, "unbreaking")
if unbreaking > 0 then
@ -117,8 +117,8 @@ local fish = function(itemstack, player, pointed_thing)
else
minetest.add_item(pos, item)
end
if mcl_experience.throw_xp then
mcl_experience.throw_xp(pos, math.random(1,6))
if mcl_experience.throw_experience then
mcl_experience.throw_experience(pos, math.random(1,6))
end
if not minetest.is_creative_enabled(player:get_player_name()) then

View File

@ -75,9 +75,9 @@ local function give_xp(pos, player)
local xp = meta:get_int("xp")
if xp > 0 then
if player then
mcl_experience.add_xp(player, xp)
mcl_experience.add_experience(player, xp)
else
mcl_experience.throw_xp(vector.add(pos, dir), xp)
mcl_experience.throw_experience(vector.add(pos, dir), xp)
end
meta:set_int("xp", 0)
end
@ -461,7 +461,7 @@ minetest.register_node("mcl_furnaces:furnace", {
on_timer = furnace_node_timer,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
local meta = minetest.get_meta(pos)
local meta2 = meta:to_table()
local meta2 = meta
meta:from_table(oldmetadata)
local inv = meta:get_inventory()
for _, listname in ipairs({"src", "dst", "fuel"}) do
@ -471,7 +471,7 @@ minetest.register_node("mcl_furnaces:furnace", {
minetest.add_item(p, stack)
end
end
meta:from_table(meta2)
meta:from_table(meta2:to_table())
end,
on_construct = function(pos)

View File

@ -61,7 +61,7 @@ local def_hopper = {
after_dig_node = function(pos, oldnode, oldmetadata, digger)
local meta = minetest.get_meta(pos)
local meta2 = meta:to_table()
local meta2 = meta
meta:from_table(oldmetadata)
local inv = meta:get_inventory()
for i=1,inv:get_size("main") do
@ -71,7 +71,7 @@ local def_hopper = {
minetest.add_item(p, stack)
end
end
meta:from_table(meta2)
meta:from_table(meta2:to_table())
end,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
local name = player:get_player_name()

View File

@ -317,7 +317,7 @@ minetest.register_node("mcl_mobspawners:spawner", {
if obj then
obj:remove()
end
mcl_experience.throw_xp(pos, math.random(15, 43))
mcl_experience.throw_experience(pos, math.random(15, 43))
end,
on_punch = function(pos)

View File

@ -115,22 +115,16 @@ function place_wet_sponge(itemstack, placer, pointed_thing)
if mcl_worlds.pos_to_dimension(pointed_thing.above) == "nether" then
minetest.item_place_node(ItemStack("mcl_sponges:sponge"), placer, pointed_thing)
local pos = pointed_thing.above
for n = 1, 5 do
minetest.add_particlespawner({
amount = 5,
time = 0.1,
minpos = vector.offset(pos, -0.5, 0.6, -0.5),
maxpos = vector.offset(pos, 0.5, 0.6, 0.5),
minvel = vector.new(0, 0.1, 0),
maxvel = vector.new(0, 1, 0),
minexptime = 0.1,
maxexptime = 1,
minsize = 2,
maxsize = 5,
for n = 0, 25 do
minetest.add_particle({
pos = {x = pos.x + math.random(-1, 1)*math.random()/2, y = pos.y + 0.6, z = pos.z + math.random(-1, 1)*math.random()/2},
velocity = {x = 0, y = math.random(), z = 0},
acceleration = {x=0, y=0, z=0},
expirationtime = math.random(),
collisiondetection = false,
vertical = false,
texture = "mcl_particles_sponge" .. n .. ".png",
size = math.random(2, 5),
texture = "mcl_particles_sponge"..math.random(1, 5)..".png",
})
end
if not minetest.is_creative_enabled(name) then

View File

@ -1496,7 +1496,7 @@ local function register_dimension_biomes()
heat_point = 100,
humidity_point = 0,
_mcl_biome_type = "hot",
_mcl_palette_index = 17,
_mcl_palette_index = 19,
})
--[[ THE END ]]

View File

@ -91,8 +91,8 @@ end
-- register saturation hudbar
hb.register_hudbar("hunger", 0xFFFFFF, S("Food"), { icon = "hbhunger_icon.png", bgicon = "hbhunger_bgicon.png", bar = "hbhunger_bar.png" }, 1, 20, 20, false)
if mcl_hunger.debug then
hb.register_hudbar("saturation", 0xFFFFFF, S("Saturation"), { icon = "mcl_hunger_icon_saturation.png", bgicon = "mcl_hunger_bgicon_saturation.png", bar = "mcl_hunger_bar_saturation.png" }, 1, mcl_hunger.SATURATION_INIT, 200, false)
hb.register_hudbar("exhaustion", 0xFFFFFF, S("Exhaust."), { icon = "mcl_hunger_icon_exhaustion.png", bgicon = "mcl_hunger_bgicon_exhaustion.png", bar = "mcl_hunger_bar_exhaustion.png" }, 1, 0, mcl_hunger.EXHAUST_LVL, false)
hb.register_hudbar("saturation", 0xFFFFFF, S("Saturation"), { icon = "mcl_hunger_icon_saturation.png", bgicon = "mcl_hunger_bgicon_saturation.png", bar = "mcl_hunger_bar_saturation.png" }, 1, mcl_hunger.SATURATION_INIT, 200, false, S("%s: %.1f/%d"))
hb.register_hudbar("exhaustion", 0xFFFFFF, S("Exhaust."), { icon = "mcl_hunger_icon_exhaustion.png", bgicon = "mcl_hunger_bgicon_exhaustion.png", bar = "mcl_hunger_bar_exhaustion.png" }, 1, 0, mcl_hunger.EXHAUST_LVL, false, S("%s: %d/%d"))
end
minetest.register_on_joinplayer(function(player)
@ -134,46 +134,47 @@ minetest.register_on_player_hpchange(function(player, hp_change)
end
end)
local food_tick_timers = {} -- one food_tick_timer per player, keys are the player-objects
local main_timer = 0
local timer = 0 -- Half second timer
local timerMult = 1 -- Cycles from 0 to 7, each time when timer hits half a second
minetest.register_globalstep(function(dtime)
for _,player in pairs(minetest.get_connected_players()) do
main_timer = main_timer + dtime
timer = timer + dtime
if main_timer > mcl_hunger.HUD_TICK or timer > 0.25 then
if main_timer > mcl_hunger.HUD_TICK then main_timer = 0 end
for _,player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
local food_tick_timer = food_tick_timers[player] and food_tick_timers[player] + dtime or 0
local player_name = player:get_player_name()
local food_level = mcl_hunger.get_hunger(player)
local food_saturation_level = mcl_hunger.get_saturation(player)
local player_health = player:get_hp()
if food_tick_timer > 4.0 then
food_tick_timer = 0
if food_level >= 18 then -- slow regenration
if player_health > 0 and player_health < 20 then
player:set_hp(player_health+1)
mcl_hunger.exhaust(player_name, mcl_hunger.EXHAUST_REGEN)
local h = tonumber(mcl_hunger.get_hunger(player))
local hp = player:get_hp()
if timer > 0.25 then
-- Slow health regeneration, and hunger damage (every 4s).
-- Regeneration rate based on tutorial video <https://www.youtube.com/watch?v=zs2t-xCVHBo>.
-- Minecraft Wiki seems to be wrong in claiming that full hunger gives 0.5s regen rate.
if timerMult == 0 then
if h >= 18 and hp > 0 and hp < 20 then
-- +1 HP, +exhaustion
player:set_hp(hp+1)
mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_REGEN)
mcl_hunger.update_exhaustion_hud(player, mcl_hunger.get_exhaustion(player))
end
elseif food_level == 0 then -- starvation
-- the amount of health at which a player will stop to get
-- harmed by starvation (10 for Easy, 1 for Normal, 0 for Hard)
local maximum_starvation = 1
-- TODO: implement Minecraft-like difficulty modes and the update maximumStarvation here
if player_health > maximum_starvation then
mcl_util.deal_damage(player, 1, {type = "starve"})
elseif h == 0 then
-- Damage hungry player down to 1 HP
-- TODO: Allow starvation at higher difficulty levels
if hp-1 > 0 then
mcl_util.deal_damage(player, 1, {type = "starve"})
end
end
end
elseif food_tick_timer > 0.5 and food_level == 20 and food_saturation_level >= 6 then -- fast regeneration
if player_health > 0 and player_health < 20 then
food_tick_timer = 0
player:set_hp(player_health+1)
mcl_hunger.exhaust(player_name, mcl_hunger.EXHAUST_REGEN)
mcl_hunger.update_exhaustion_hud(player, mcl_hunger.get_exhaustion(player))
end
end
food_tick_timers[player] = food_tick_timer -- update food_tick_timer table
end
end
if timer > 0.25 then
timer = 0
timerMult = timerMult + 2
if timerMult > 7 then
timerMult = 0
end
end
end)

View File

@ -118,6 +118,17 @@ def colorize_alpha(colormap, source, colormap_pixel, texture_size, destination):
colorize(colormap, source, colormap_pixel, texture_size, tempfile2.name)
os.system("composite -compose Dst_In "+source+" "+tempfile2.name+" -alpha Set "+destination)
# This function is unused atm.
# TODO: Implemnt colormap extraction
def extract_colormap(colormap, colormap_pixel, positions):
os.system("convert -size 16x16 canvas:black "+tempfile1.name)
x=0
y=0
for p in positions:
os.system("convert "+colormap+" -crop 1x1+"+colormap_pixel+" -depth 8 "+tempfile2.name)
os.system("composite -geometry 16x16+"+x+"+"+y+" "+tempfile2.name)
x = x+1
def target_dir(directory):
if make_texture_pack:
return output_dir + "/" + output_dir_name
@ -386,60 +397,20 @@ def convert_textures():
colorize_alpha(FOLIAG, tex_dir+"/blocks/vine.png", "16+39", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/mcl_core_vine.png")
# Tall grass, fern (inventory images)
pcol = "50+173" # Plains grass color
pcol = "49+172" # Plains grass color
colorize_alpha(GRASS, tex_dir+"/blocks/tallgrass.png", pcol, str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/mcl_flowers_tallgrass_inv.png")
colorize_alpha(GRASS, tex_dir+"/blocks/fern.png", pcol, str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/mcl_flowers_fern_inv.png")
colorize_alpha(GRASS, tex_dir+"/blocks/double_plant_fern_top.png", pcol, str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/mcl_flowers_double_plant_fern_inv.png")
colorize_alpha(GRASS, tex_dir+"/blocks/double_plant_grass_top.png", pcol, str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/mcl_flowers_double_plant_grass_inv.png")
# Convert grass palette: https://minecraft.fandom.com/wiki/Tint
grass_colors = [
# [Coords or #Color, AdditionalTint], # Index - Minecraft biome name (MineClone2 biome names)
["50+173"], # 0 - Plains (flat, Plains, Plains_beach, Plains_ocean, End)
["0+255"], # 1 - Savanna (Savanna, Savanna_beach, Savanna_ocean)
["255+255"], # 2 - Ice Spikes (IcePlainsSpikes, IcePlainsSpikes_ocean)
["255+255"], # 3 - Snowy Taiga (ColdTaiga, ColdTaiga_beach, ColdTaiga_beach_water, ColdTaiga_ocean)
["178+193"], # 4 - Giant Tree Taiga (MegaTaiga, MegaTaiga_ocean)
["178+193"], # 5 - Giant Tree Taiga (MegaSpruceTaiga, MegaSpruceTaiga_ocean)
["203+239"], # 6 - Montains (ExtremeHills, ExtremeHills_beach, ExtremeHills_ocean)
["203+239"], # 7 - Montains (ExtremeHillsM, ExtremeHillsM_ocean)
["203+239"], # 8 - Montains (ExtremeHills+, ExtremeHills+_snowtop, ExtremeHills+_ocean)
["50+173"], # 9 - Beach (StoneBeach, StoneBeach_ocean)
["255+255"], # 10 - Snowy Tundra (IcePlains, IcePlains_ocean)
["50+173"], # 11 - Sunflower Plains (SunflowerPlains, SunflowerPlains_ocean)
["191+203"], # 12 - Taiga (Taiga, Taiga_beach, Taiga_ocean)
["76+112"], # 13 - Forest (Forest, Forest_beach, Forest_ocean)
["76+112"], # 14 - Flower Forest (FlowerForest, FlowerForest_beach, FlowerForest_ocean)
["101+163"], # 15 - Birch Forest (BirchForest, BirchForest_ocean)
["101+163"], # 16 - Birch Forest Hills (BirchForestM, BirchForestM_ocean)
["0+255"], # 17 - Desert and Nether (Desert, Desert_ocean, Nether)
["76+112", "#28340A"], # 18 - Dark Forest (RoofedForest, RoofedForest_ocean)
["#90814d"], # 19 - Mesa (Mesa, Mesa_sandlevel, Mesa_ocean, )
["#90814d"], # 20 - Mesa (MesaBryce, MesaBryce_sandlevel, MesaBryce_ocean)
["#90814d"], # 21 - Mesa (MesaPlateauF, MesaPlateauF_grasstop, MesaPlateauF_sandlevel, MesaPlateauF_ocean)
["#90814d"], # 22 - Mesa (MesaPlateauFM, MesaPlateauFM_grasstop, MesaPlateauFM_sandlevel, MesaPlateauFM_ocean)
["0+255"], # 23 - Shattered Savanna (or Savanna Plateau ?) (SavannaM, SavannaM_ocean)
["12+36"], # 24 - Jungle (Jungle, Jungle_shore, Jungle_ocean)
["12+36"], # 25 - Modified Jungle (JungleM, JungleM_shore, JungleM_ocean)
["12+61"], # 26 - Jungle Edge (JungleEdge, JungleEdge_ocean)
["12+61"], # 27 - Modified Jungle Edge (JungleEdgeM, JungleEdgeM_ocean)
["#6A7039"], # 28 - Swamp (Swampland, Swampland_shore, Swampland_ocean)
["25+25"], # 29 - Mushroom Fields and Mushroom Field Shore (MushroomIsland, MushroomIslandShore, MushroomIsland_ocean)
# TODO: Convert grass palette
offset = [
[ pcol, "", "grass" ], # Default grass: Plains
]
grass_palette_file = target_dir("/mods/ITEMS/mcl_core/textures") + "/mcl_core_palette_grass.png"
os.system("convert -size 16x16 canvas:transparent " + grass_palette_file)
for i, color in enumerate(grass_colors):
if color[0][0] == "#":
os.system("convert -size 1x1 xc:\"" + color[0] + "\" " + tempfile1.name + ".png")
else:
os.system("convert " + GRASS + " -crop 1x1+" + color[0] + " " + tempfile1.name + ".png")
if len(color) > 1:
os.system("convert " + tempfile1.name + ".png \\( -size 1x1 xc:\"" + color[1] + "\" \\) -compose blend -define compose:args=50,50 -composite " + tempfile1.name + ".png")
os.system("convert " + grass_palette_file + " \\( " + tempfile1.name + ".png -geometry +" + str(i % 16) + "+" + str(int(i / 16)) + " \\) -composite " + grass_palette_file)
for o in offset:
colorize(GRASS, tex_dir+"/blocks/grass_top.png", o[0], str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/default_"+o[2]+".png")
colorize_alpha(GRASS, tex_dir+"/blocks/grass_side_overlay.png", o[0], str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/default_"+o[2]+"_side.png")
# Metadata
if make_texture_pack:

View File

@ -1,60 +0,0 @@
#!/bin/sh -eu
# analyze-packet-spam show minetest client packet count per second
# Copyright © 2021 Nils Dagsson Moskopp (erlehmann)
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
# Dieses Programm hat das Ziel, die Medienkompetenz der Leser zu
# steigern. Gelegentlich packe ich sogar einen handfesten Buffer
# Overflow oder eine Format String Vulnerability zwischen die anderen
# Codezeilen und schreibe das auch nicht dran.
# This script takes a minetest log with at least INFO log level and
# outputs the MINETEST network protocol packet count per second.
# To collect such a log file of minetest running for 10 minutes, run:
# timeout 600 minetest --info >log.txt 2>&1 >/dev/null
# To get packet counts from that file, run:
# ./analyze-packet-spam <log.txt
TEMPFILE=$(mktemp /tmp/minetest.analyze-packet-spam.XXXXXXXX)
grep -F 'INFO[Main]: cmd' \
|while read DATE TIME _ _ PACKET_ID PACKET_NAME _ PACKET_COUNT; do
TIMESTAMP=$(date +%s --date "${DATE} ${TIME%:}")
PACKET_NAME=${PACKET_NAME#(}
PACKET_NAME=${PACKET_NAME%)}
VARIABLE=PACKET_COUNT_"${PACKET_NAME}"
eval "$( echo $VARIABLE=\$\(\( \${$VARIABLE:-0} + ${PACKET_COUNT} \)\) )"
printf '%s ' \
"${TIMESTAMP}" \
"${VARIABLE}"
eval echo \$${VARIABLE}
done >"${TEMPFILE}"
TIMESTAMP_START=$( <"${TEMPFILE}" head -n1 |cut -d' ' -f1 )
TIMESTAMP_END=$( <"${TEMPFILE}" tail -n1 |cut -d' ' -f1 )
DURATION=$(( 30 + ${TIMESTAMP_END} - ${TIMESTAMP_START} ))
PACKET_NAME_SEEN=''
<"${TEMPFILE}" tac \
|while read _ PACKET_NAME PACKET_COUNT; do
case "${PACKET_NAME_SEEN}" in
*"${PACKET_NAME}"*)
;;
*)
PACKET_COUNT_PER_SECOND=$(
printf '1k %s %s /p' "${PACKET_COUNT}" "${DURATION}" \
|dc
)
printf '%s\t%s\n' "${PACKET_COUNT_PER_SECOND}" "${PACKET_NAME}"
PACKET_NAME_SEEN="${PACKET_NAME_SEEN} ${PACKET_NAME}"
;;
esac
done
unlink "${TEMPFILE}"

View File

@ -1,50 +0,0 @@
#! /usr/bin/env lua
-- Script to automatically generate mods/HUD/mcl_credits/people.lua from CREDITS.md
-- Run from MCL2 root folder
local colors = {
["Creator of MineClone"] = "0x0A9400",
["Creator of MineClone2"] = "0xFBF837",
["Maintainers"] = "0xFF51D5",
["Developers"] = "0xF84355",
["Contributors"] = "0x52FF00",
["MineClone5"] = "0xA60014",
["Original Mod Authors"] = "0x343434",
["3D Models"] = "0x0019FF",
["Textures"] = "0xFF9705",
["Translations"] = "0x00FF60",
["Funders"] = "0xF7FF00",
["Special thanks"] = "0x00E9FF",
}
local from = io.open("CREDITS.md", "r")
local to = io.open("mods/HUD/mcl_credits/people.lua", "w")
to:write([[
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
]])
to:write("return {\n")
local started_block = false
for line in from:lines() do
if line:find("## ") == 1 then
if started_block then
to:write("\t}},\n")
end
local title = line:sub(4, #line)
to:write("\t{S(\"" .. title .. "\"), " .. (colors[title] or "0xFFFFFF") .. ", {\n")
started_block = true
elseif line:find("*") == 1 then
to:write("\t\t\"" .. line:sub(3, #line) .. "\",\n")
end
end
if started_block then
to:write("\t}},\n")
end
to:write("}\n")