Compare commits

..

23 Commits

Author SHA1 Message Date
AFCMS 2d7ee83d3e Merge branch 'master' into chat-command-builder-2 2021-07-12 20:54:54 +02:00
AFCMS a65acd3ab8 Merge branch 'master' into chat-command-builder-2 2021-07-06 11:07:18 +02:00
AFCMS 3718bb6f80 mcl_death_drop: allow mods to overide default behaviour 2021-07-02 18:07:29 +02:00
AFCMS 19745cafbc fix wrong API def 2021-07-02 01:06:01 +02:00
AFCMS bc2e122652 Merge branch 'master' into chat-command-builder-2 2021-07-02 01:03:45 +02:00
AFCMS b0ca506688 basic proof of concept 2021-07-02 01:03:25 +02:00
AFCMS 0a57666771 performance improvement 2021-06-25 10:22:12 +02:00
AFCMS 07513d8e52 fixes 2021-06-25 10:09:52 +02:00
AFCMS a6e0605b7c testing stuff 2021-06-24 23:58:17 +02:00
AFCMS cc0ed855a9 fixes 2021-06-24 23:45:47 +02:00
AFCMS 658028e90a fixes 2021-06-24 23:39:15 +02:00
AFCMS 3f1b12e327 fixes and testing stuff 2021-06-24 22:50:44 +02:00
AFCMS e436f1a6af remove a stupic crash xD 2021-06-24 18:04:20 +02:00
AFCMS d19f88d226 add some other types 2021-06-24 14:12:37 +02:00
AFCMS 6d1bc8f865 add float type 2021-06-24 14:04:15 +02:00
AFCMS 102f45336b move mcl_privs to CORE 2021-06-24 12:35:40 +02:00
AFCMS be75150725 add help formspec refactoring to the TODO list 2021-06-24 12:15:29 +02:00
AFCMS 0619d480dd integrate translation system 2021-06-24 12:07:13 +02:00
AFCMS 0129fdafef basic target selector system 2021-06-24 12:03:56 +02:00
AFCMS 5ddc3cf99a add some type definitions 2021-06-24 11:54:15 +02:00
AFCMS 84900b39ad basic API setup 2021-06-24 11:40:35 +02:00
AFCMS 528f1cb81f move some files to the right location 2021-06-24 10:03:05 +02:00
AFCMS bb576148f4 move mcl_commands to CORE folder 2021-06-24 10:00:52 +02:00
170 changed files with 2490 additions and 5807 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

@ -2,404 +2,104 @@
So you want to contribute to MineClone 2? So you want to contribute to MineClone 2?
Wow, thank you! :-) Wow, thank you! :-)
MineClone2 is maintained by Nicu and Fleckenstein. If you have any But first, some things to note:
problems or questions, contact us (See Links section below).
You can help with MineClone2's development in many different ways, MineClone 2's development target is to make a free software clone of Minecraft,
whether you're a programmer or not. ***version 1.12***, ***PC edition***, *** + Optifine features supported by the Minetest Engine ***.
## MineClone2's development target is to... MineClone 2 is maintained by three persons. Namely, kay27, EliasFleckenstein and jordan4ibanez. You can find us
- Crucially, create a stable, moddable, free/libre clone of Minecraft in the Minetest forums (forums.minetest.net), in IRC in the #mineclone2
based on the Minetest engine with polished features, usable in both channel on irc.freenode.net. And finally, you can send e-mails to
singleplayer and multiplayer. Currently, most of **Minecraft Java <eliasfleckenstein@web.de> or <kay27@bk.ru>.
Edition 1.12.2** features are already implemented and polishing existing
features are prioritized over new feature requests.
- With lessened priority yet strictly, implement features targetting
**Minecraft version 1.17 + OptiFine** (OptiFine only as far as supported
by the Minetest Engine). This means features in parity with the listed
Minecraft experiences are prioritized over those that don't fulfill this
scope.
- Optionally, create a performant experience that will run relatively
well on really low spec computers. Unfortunately, due to Minecraft's
mechanisms and Minetest engine's limitations along with a very small
playerbase on low spec computers, optimizations are hard to investigate.
## Links By sending us patches or asking us to include your changes in this game,
* [Mesehub](https://git.minetest.land/MineClone2/MineClone2) you agree that they fall under the terms of the LGPLv2.1, which basically
* [Discord](https://discord.gg/xE4z8EEpDC) means they will become part of a free software.
* [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)
## Using git ## The suggested workflow
MineClone2 is developed using the version control system We don't **dictate** your workflow, but in order to work with us in an efficient
[git](https://git-scm.com/). If you want to contribute code to the way, you can follow these suggestions:
project, it is **highly recommended** that you learn the git basics.
For non-programmers and people who do not plan to contribute code to
MineClone2, git is not required. However, git is a tool that will be
referenced frequently because of its usefulness. As such, it is valuable
in learning how git works and its terminology. It can also help you
keeping your game updated, and easily test pull requests.
## How you can help as a non-programmer For small and medium changes:
As someone who does not know how to write programs in Lua or does not * Fork the repository
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.
### Crediting
If you opened or have contributed to an issue, you receive the
`Community` role on our Discord (after asking for it).
## How you can help as a programmer
(Almost) all the MineClone2 development is done using pull requests.
### Recommended workflow
* Fork the repository (in case you have not already)
* Do your change in a new branch * Do your change in a new branch
* Create a pull request to get your changes merged into master * 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 For small changes, sending us a patch is also good.
If you feel like a problem needs to fixed or you want to make a new
feature, you could start writing the code right away and notifying us
when you're done, but it never hurts to discuss things first. If there
is no issue on the topic, open one. If there is an issue, tell us that
you'd like to take care of it, to avoid duplicate work.
### Don't hesitate to ask for help For big changes: Same as above, but consider notifying us first to avoid
We appreciate any contributing effort to MineClone2. If you are a duplicate work and possible tears of rejection. ;-)
relatively new programmer, you can reach us on Discord, Matrix or IRC
for questions about git, Lua, Minetest API, MineClone2 codebase or
anything related to MineClone2. We can help you avoid writing code that
would be deemed inadequate, or help you become familiar with MineClone2
better, or assist you use development tools.
### Maintain your own code, even if already got merged For trusted people, we might give them direct commit access to this
Sometimes, your code may cause crashes or bugs - we try to avoid such repository. In this case, you obviously don't need to fork, but you still
scenarios by testing every time before merging it, but if your merged need to show your contributions align with the project goals. We still
work causes problems, we ask you fix the issues as soon as possible. reserve the right to revert everything that we don't like.
For bigger changes, we strongly recommend to use feature branches and
discuss with me first.
### Changing Gameplay If your code causes bugs and crashes, it is your responsibility to fix them as soon as possible.
Pull Requests that change gameplay have to be properly researched and
need to state their sources. These PRs also need Fleckenstein's approval
before they are merged.
You can use these sources:
* Minecraft code (Name the source file and line, however DONT post any We mostly use plain merging rather than rebasing or squash merging.
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)
### 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 Contributors will be credited in `CREDITS.md`.
* 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)
#### Code Guidelines ## Code Style
* 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.
```lua Each mod must provide `mod.conf`.
mcl_example = {} 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.
function mcl_example.do_something() Functions should be defined in this way:
-- ... ```
end function mcl_xyz.stuff(param) end
```
Insteed of this way:
```
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.
```
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 ## Features > 1.12
-- bad
function mcl_example:do_something()
end
-- good 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.
function mcl_example.do_something()
end
```
* Use modern Minetest API, e.g. no usage of `minetest.env` ## What we accept
* Tabs should be used for indent, spaces for alignment, e.g.
```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 * Any features which cause critical bugs, sending them to rework/fix or trying to fix immediately.
if i % 3 == 0 then * Some small portions of big entirely missing features which just definitely break gamplay balance give nothing useful
print(i) * 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.
end
end
-- use tabs for indent and spaces to align things ## Reporting bugs
Report all bugs and missing Minecraft features here:
some_table = { <https://git.minetest.land/MineClone2/MineClone2/issues>
{"a string", 5},
{"a very much longer string", 10},
}
```
* Use double quotes for strings, e.g. `"asdf"` rather than `'asdf'` ## Direct discussion
* Use snake_case rather than CamelCase, e.g. `my_function` rather than We have an IRC channel! Join us on #mineclone2 in freenode.net.
`MyFunction`
* Don't declare functions as an assignment, e.g.
```lua <ircs://irc.freenode.net:6697/#mineclone2>
-- bad
local some_local_func = function()
-- ...
end
my_mod.some_func = function() ## Creating releases
-- ...
end
-- good
local function some_local_func()
-- ...
end
function my_mod.some_func()
-- ...
end
```
### Developer status
Active and trusted contributors are often granted write access to the
MineClone2 repository.
#### Developer responsibilities
- You should not push things directly to
MineClone2 master - rather, do your work on a branch on your private
repository, then create a pull request. This way other people can review
your changes and make sure they work before they get merged.
- Merge PRs only when they have recieved the necessary feedback and have
been tested by at least two different people (including the author of
the pull request), to avoid crashes or the introduction of new bugs.
- You may also be assigned to issues or pull
requests as a developer. In this case it is your responsibility to fix
the issue / review and merge the pull request when it is ready. You can
also unassign yourself from the issue / PR if you have no time or don't
want to take care of it for some other reason. After all, everyone is a
volunteer and we can't expect you to do work that you are not interested
in. **The important thing is that you make sure to inform us if you
won't take care of something that has been assigned to you.**
- Please assign yourself to something that you want to work on to avoid
duplicate work.
- As a developer, it should be easy to reach you about your work. You
should be in at least one of the public MineClone2 discussion rooms -
preferrably Discord, but if you really don't like Discord, Matrix
or IRC are fine too.
### Maintainer status
Maintainers carry the main responsibility for the project.
#### Maintainer responsibilities
- Making sure issues are addressed and pull requests are reviewed and
merged, by assigning either themselves or Developers to issues / PRs
- Making releases
- Making sure guidelines are kept
- Making project decisions based on community feedback
- Granting/revoking developer access
- Enforcing the code of conduct (See CODE_OF_CONDUCT.md)
- Moderating official community spaces (See Links section)
- Resolving conflicts and problems within the community
#### Current maintainers
* Fleckenstein - responsible for gameplay review, publishing releases,
technical guidelines and issue/PR delegation
* Nicu - responsible for community related issues
#### Release process
* Run `tools/generate_ingame_credits.lua` to update the ingame credits
from `CREDITS.md` and commit the result (if anything changed)
* Launch MineClone2 to make sure it still runs * Launch MineClone2 to make sure it still runs
* Update the version number in README.md * Update the version number in README.md
* Use `git tag <version number>` to tag the latest commit with the * Use `git tag <version number>` to tag the latest commit with the version number
version number * Push to repo (don't forget `--tags`!)
* Push to repository (don't forget `--tags`!) * Update ContentDB (https://content.minetest.net/packages/Wuzzy/mineclone2/)
* Update ContentDB * Update first post in forum thread (https://forum.minetest.net/viewtopic.php?f=50&t=16407)
(https://content.minetest.net/packages/Wuzzy/mineclone2/)
* Update first post in forum thread
(https://forum.minetest.net/viewtopic.php?f=50&t=16407)
* Post release announcement and changelog in forums * Post release announcement and changelog in forums
### Licensing
By asking us to include your changes in this game, you agree that they
fall under the terms of the GPLv3, which basically means they will
become part of a free/libre software.
### Crediting
Contributors, Developers and Maintainers will be credited in
`CREDITS.md`. If you make your first time contribution, please add
yourself to this file. There are also Discord roles for Contributors,
Developers and Maintainers.

View File

@ -8,8 +8,8 @@
## Maintainers ## Maintainers
* Fleckenstein * Fleckenstein
* Nicu
* kay27 * kay27
* jordan4ibanez
## Developers ## Developers
* bzoss * bzoss
@ -19,11 +19,10 @@
* iliekprogrammar * iliekprogrammar
* MysticTempest * MysticTempest
* Rootyjr * Rootyjr
* Nicu
* aligator * aligator
* Code-Sploit * Code-Sploit
* NO11 * NO11
* cora
* jordan4ibanez
## Contributors ## Contributors
* Laurent Rocher * Laurent Rocher
@ -49,24 +48,8 @@
* dBeans * dBeans
* nickolas360 * nickolas360
* yutyo * yutyo
* Tianyang Zhang * ztianyang
* j45 * j45
* Marcin Serwin
* erlehmann
* E
* Benjamin Schötz
* Doloment
* Sydney Gems
* talamh
* Emily2255
* Emojigit
* FinishedFragment
* sfan5
* Blue Blancmange
* Jared Moody
* SmallJoker
* Sven792
* aldum
## MineClone5 ## MineClone5
* kay27 * kay27
@ -91,6 +74,7 @@
* Rochambeau * Rochambeau
* rubenwardy * rubenwardy
* stu * stu
* jordan4ibanez
* 4aiman * 4aiman
* Kahrl * Kahrl
* Krock * Krock
@ -119,7 +103,6 @@
* xMrVizzy * xMrVizzy
* yutyo * yutyo
* NO11 * NO11
* kay27
## Translations ## Translations
* Wuzzy * Wuzzy
@ -127,8 +110,6 @@
* wuniversales * wuniversales
* kay27 * kay27
* pitchum * pitchum
* todoporlalibertad
* Marcin Serwin
## Special thanks ## Special thanks
* celeron55 for creating Minetest * celeron55 for creating Minetest

View File

@ -86,21 +86,20 @@ The MineClone2 repository is hosted at Mesehub. To contribute or report issues,
* Reddit: <https://www.reddit.com/r/MineClone2/> * Reddit: <https://www.reddit.com/r/MineClone2/>
* Minetest forums: <https://forum.minetest.net/viewtopic.php?f=50&t=16407> * Minetest forums: <https://forum.minetest.net/viewtopic.php?f=50&t=16407>
## Target ## Project description
- Crucially, create a stable, moddable, free/libre clone of Minecraft The main goal of **MineClone 2** is to be a clone of Minecraft and to be released as free software.
based on the Minetest engine with polished features, usable in both
singleplayer and multiplayer. Currently, most of **Minecraft Java * **Target of development: Minecraft, PC Edition, version 1.12** (later known as “Java Edition”)
Edition 1.12.2** features are already implemented and polishing existing * MineClone2 also includes Optifine features supported by the Minetest
features are prioritized over new feature requests. * In general, Minecraft is aimed to be cloned as good as possible
- With lessened priority yet strictly, implement features targetting * Cloning the gameplay has highest priority
**Minecraft version 1.17 + OptiFine** (OptiFine only as far as supported * MineClone 2 will use different assets, but with a similar style
by the Minetest Engine). This means features in parity with the listed * Limitations found in Minetest will be documented in the course of development
Minecraft experiences are prioritized over those that don't fulfill this * Features of later Minecraft versions are collected in the mineclone5 branch
scope.
- Optionally, create a performant experience that will run relatively ## Using features from newer versions of Minecraft
well on really low spec computers. Unfortunately, due to Minecraft's For > 1.12 features, checkout MineClone5. It includes features from newer Minecraft versions.
mechanisms and Minetest engine's limitations along with a very small Download it here: https://git.minetest.land/MineClone2/MineClone2/src/branch/mineclone5
playerbase on low spec computers, optimizations are hard to investigate.
## Completion status ## Completion status
This game is currently in **beta** stage. This game is currently in **beta** stage.

View File

@ -0,0 +1,55 @@
### `mcl_commands.register_command(name, def)`
#### Complex commands (WIP):
```
mcl_commands.register_command("test", {
type = "complex",
priv_level = 4,
func = function(context)
if self.match_type("int", {min=1, max=255} then
print(self.last_type)
end
end,
})
```
#### Basic commands:
* `context.type` is the context of execution: `player` or `commandblock`
* `context.commander` is the executor/commander of the command
* `context.pos` is the position where the command is executed
This param allow the use of position target selectors
```
mcl_commands.register_command("test", {
type = "basic",
priv_level = 4,
func = function(context)
if context.commander then
print(context.pos)
print("--------") --this "concept" param allow to run command correctly
--(with target selector and logging) from mods or command blocks
return true, S("Succesfull")
end
end,
})
```
### `mcl_commands.execute_command(name, params, context)`
#### As a player:
```
mcl_commands.execute_command("test", "foo bar true 1", {type="player", commander=player, pos=player:get_pos()})
```
#### As a command block:
```
mcl_commands.execute_command("test", "foo bar true 1", {type="commandblock", commander=commander, pos=node_pos})
```
### `mcl_commands.get_target_selector(target_selector)`
This function allow mods and commands to get the result of a given target selector in that form: `@e[gamemode=creative,limit=5]`
This function returns a code indicating the success and a table of ObjectRefs (can be empty)

View File

@ -0,0 +1,7 @@
mcl_commands.alias_command("?", "help", false)
mcl_commands.alias_command("pardon", "unban", false)
mcl_commands.rename_command("stop", "shutdown", false)
mcl_commands.alias_command("tell", "msg", false)
mcl_commands.alias_command("w", "msg", false)
mcl_commands.alias_command("tp", "teleport", false)
mcl_commands.rename_command("clear", "clearinv", false)

View File

@ -0,0 +1,201 @@
local S = minetest.get_translator(minetest.get_current_modname())
local C = minetest.colorize
--TODO: like mc error message
--TODO: complex command handling
--TODO: mc like help system
mcl_commands.types = {
bool = {
lengh = 1,
msg = S("Invalid boolean"),
func = function(word)
if word == "true" then
return true, true
elseif world == "false" then
return true, false
else
return false, nil
end
end,
},
int = {
lengh = 1,
msg = S("Invalid integer"),
func = function(int)
if tonumber(int) and tonumber(int) == math.round(int) then
return true, tonumber(int)
else
return false, nil
end
end,
},
float = {
lengh = 1,
msg = S("Invalid integer"),
func = function(float)
if tonumber(float) then
return true, tonumber(float)
else
return false, nil
end
end,
},
word = {
lengh = 1,
msg = S("Invalid word"),
func = function(word)
if word then
return true, word
else
return false, nil
end
end,
},
text = {},
pos = {
lengh = 3,
msg = S("Invalid position"),
func = function(x, y, z)
--FIXME
if true then
return true, nil
else
return false, nil
end
end,
},
target = {
lengh = 1,
msg = S("Invalid target selector"),
func = function(target)
--mcl_commands.get_target_selector(target_selector)
if minetest.player_exists(target) then
return true, target
else
return false, nil
end
end,
},
playername = {
lengh = 1,
msg = S("Invalid player name"),
func = function(name)
if minetest.player_exists(name) then
return true, name
else
return false, nil
end
end,
},
}
function mcl_commands.match_param(table, index, type, params)
local typedef = mcl_commands.types[type]
if typedef.lengh > 1 then
return
else
local params = {}
typedef.func()
end
end
mcl_commands.registered_commands = {}
function mcl_commands.register_complex_command()
end
--aims to avoid complexity for basic commands while keeping proper messages and privs management
function mcl_commands.register_basic_command(name, def)
local func
if def.params then
func = function(name, param)
local funcparams = {}
local i = 0
for str in string.gmatch(params, "([^ ]+)") do
i = i + 1
funcparams[i] = str
end
for _,type in pairs(def.params) do
mcl_commands.match_param(funcparams, index, type, params)
end
end
else
mcl_commands.registered_commands[name] = {type = "basic", description = def.desc, privs = def.privs}
func = function(name, param)
if param == "" then
local out, msg = def.func(name)
if out then
return true, C(mcl_colors.GRAY, msg) or C(mcl_colors.GRAY, S("succesful"))
else
return false, C(mcl_colors.RED, msg) or C(mcl_colors.RED, S("failed"))
end
else
return false, C(mcl_colors.RED, S("Invalid command usage"))
end
end
end
minetest.register_chatcommand(name, {
description = def.desc,
privs = def.privs,
func = func,
})
end
--[[
mcl_commands.register_basic_command("test", {
description = S("testing command"),
params = nil,
func = function(name)
end,
})
]]
mcl_commands.register_basic_command("testb", {
description = S("testing command"),
params = {
{type="bool"},
{type="int", params={min=1, max=10}}
},
func = function(name, bool, int)
return true, "test: "..int
end,
})
function mcl_commands.alias_command(alias, original_name, bypass_setting)
if minetest.settings:get_bool("mcl_builtin_commands_overide", true) or bypass_setting then
local def = minetest.registered_chatcommands[original_name]
minetest.register_chatcommand(alias, def)
minetest.log("action", string.format("[mcl_commands] Aliasing [%s] command to [%s]", original_name, alias))
else
minetest.log("action", string.format("[mcl_commands] Aliasing [%s] command to [%s] skipped according to setting", original_name, alias))
end
end
function mcl_commands.rename_command(new_name, original_name, bypass_setting)
if minetest.settings:get_bool("mcl_builtin_commands_overide", true) or bypass_setting then
local def = minetest.registered_chatcommands[original_name]
minetest.register_chatcommand(new_name, def)
minetest.unregister_chatcommand(original_name)
minetest.log("action", string.format("[mcl_commands] Renaming [%s] command to [%s]", original_name, new_name))
else
minetest.log("action", string.format("[mcl_commands] Renaming [%s] command to [%s] skipped according to setting", original_name, new_name))
end
end
--0: succesfull, table
--1: not connected player, nil
--2: invalid target selector, nil
function mcl_commands.get_target_selector(target_selector)
if minetest.player_exists(target_selector) then
local obj = minetest.get_player_by_name(target_selector)
if obj then
return 0, {obj}
else
return 1, nil
end
else
return 0, {}
end
end

View File

@ -0,0 +1,16 @@
local modpath = minetest.get_modpath(minetest.get_current_modname())
mcl_commands = {}
dofile(modpath.."/api.lua")
dofile(modpath.."/register/kill.lua")
dofile(modpath.."/register/setblock.lua")
dofile(modpath.."/register/seed.lua")
dofile(modpath.."/register/summon.lua")
dofile(modpath.."/register/say.lua")
dofile(modpath.."/register/list.lua")
dofile(modpath.."/register/sound.lua")
dofile(modpath.."/register/banlist.lua")
dofile(modpath.."/alias.lua")

View File

@ -0,0 +1,16 @@
local S = minetest.get_translator(minetest.get_current_modname())
--[[minetest.register_chatcommand("banlist", {
description = S("List bans"),
privs = minetest.registered_chatcommands["ban"].privs,
func = function(name)
return true, S("Ban list: @1", minetest.get_ban_list())
end,
})]]
mcl_commands.register_basic_command("banlist", {
description = S("List bans"),
func = function(name)
return true, S("Ban list: @1", minetest.get_ban_list())
end,
})

View File

@ -0,0 +1,52 @@
local S = minetest.get_translator(minetest.get_current_modname())
--[[
minetest.register_chatcommand("list", {
description = S("Show who is logged on"),
params = "",
privs = {},
func = function(name)
--local players = ""
--for _, player in ipairs(minetest.get_connected_players()) do
-- players = players..player:get_player_name().."\n"
--end
--minetest.chat_send_player(name, players)
local player_list = minetest.get_connected_players()
local header = S("There are @1/@2 players online:", #player_list, minetest.settings:get("max_users") or "unknown").."\n"
local players = {}
for _, player in ipairs(player_list) do
table.insert(players, player:get_player_name())
end
return true, header..table.concat(players, ", ")
end
})
]]
local max_users = minetest.settings:get("max_users") or "unknown" --TODO: check if the setting is dynamic in mc
local playersstring = ""
local function generate_player_list()
local player_list = minetest.get_connected_players()
local header = S("There are @1/@2 players online:", #player_list, max_users).."\n"
local players = {}
for _, player in ipairs(player_list) do
table.insert(players, player:get_player_name())
end
playersstring = header..table.concat(players, ", ")
end
minetest.register_on_joinplayer(function(player)
generate_player_list()
end)
minetest.register_on_leaveplayer(function(player)
generate_player_list()
end)
mcl_commands.register_basic_command("list", {
description = S("Show who is logged on"),
params = nil,
func = function(name)
return true, playersstring
end,
})

View File

@ -1,10 +1,12 @@
local S = minetest.get_translator(minetest.get_current_modname()) local S = minetest.get_translator(minetest.get_current_modname())
local seed = minetest.get_mapgen_setting("seed")
minetest.register_chatcommand("seed", { minetest.register_chatcommand("seed", {
description = S("Displays the world seed"), description = S("Displays the world seed"),
params = "", params = "",
privs = {}, privs = {},
func = function(name) func = function(name)
minetest.chat_send_player(name, "Seed: "..minetest.get_mapgen_setting("seed")) minetest.chat_send_player(name, "Seed: "..seed)
end end
}) })

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 B

View File

@ -0,0 +1,5 @@
local S = minetest.get_translator(minetest.get_current_modname())
minetest.register_privilege("maphack", {
description = S("Can place and use advanced blocks like mob spawners, command blocks and barriers."),
})

View File

@ -538,12 +538,3 @@ function mcl_util.get_object_name(object)
return luaentity.nametag and luaentity.nametag ~= "" and luaentity.nametag or luaentity.description or luaentity.name return luaentity.nametag and luaentity.nametag ~= "" and luaentity.nametag or luaentity.description or luaentity.name
end end
end end
function mcl_util.replace_mob(obj, mob)
local rot = obj:get_yaw()
local pos = obj:get_pos()
obj:remove()
obj = minetest.add_entity(pos, mob)
obj:set_yaw(rot)
return obj
end

View File

@ -38,32 +38,18 @@ function image:encode_header()
self.data = self.data self.data = self.data
.. string.char(0) -- image id .. string.char(0) -- image id
.. string.char(0) -- color map type .. 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_colormap_spec() -- color map specification
self:encode_image_spec() -- image specification self:encode_image_spec() -- image specification
end end
function image:encode_data() 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 _, row in ipairs(self.pixels) do
for _, pixel in ipairs(row) do for _, pixel in ipairs(row) do
current_pixel = string.char(pixel[3], pixel[2], pixel[1]) self.data = self.data
if current_pixel ~= previous_pixel or count == 128 then .. string.char(pixel[3], pixel[2], pixel[1])
packets[#packets +1] = rle_packet
count = 1
previous_pixel = current_pixel
else
count = count + 1
end
rle_packet = string.char(128 + count - 1) .. current_pixel
end end
end end
packets[#packets +1] = rle_packet
self.data = self.data .. table.concat(packets)
end end
function image:encode_footer() function image:encode_footer()

View File

@ -84,7 +84,7 @@ local function attach_object(self, obj)
end end
end, name) end, name)
obj:set_look_horizontal(yaw) obj:set_look_horizontal(yaw)
mcl_title.set(obj, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60}) mcl_tmp_message.message(obj, S("Sneak to dismount"))
else else
obj:get_luaentity()._old_visual_size = visual_size obj:get_luaentity()._old_visual_size = visual_size
end end

View File

@ -6,7 +6,6 @@ Boats are used to travel on the surface of water.=Les bateaux sont utilisés pou
Dark Oak Boat=Bateau en Chêne Noir Dark Oak Boat=Bateau en Chêne Noir
Jungle Boat=Bateau en Acajou Jungle Boat=Bateau en Acajou
Oak Boat=Bateau en Chêne Oak Boat=Bateau en Chêne
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=Faites un clic droit sur une source d'eau pour placer le bateau. Faites un clic droit sur le bateau pour y entrer. Utilisez [Gauche] et [Droite] pour diriger, [Avant] pour accélérer et [Arrière] pour ralentir ou reculer. Utilisez [Sneak] pour le quitter, frappez le bateau pour le faire tomber en tant qu'objet. Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Rightclick the boat again to leave it, punch the boat to make it drop as an item.=Faites un clic droit sur une source d'eau pour placer le bateau. Faites un clic droit sur le bateau pour y entrer. Utilisez [Gauche] et [Droite] pour diriger, [Avant] pour accélérer et [Arrière] pour ralentir ou reculer. Cliquez de nouveau avec le bouton droit sur le bateau pour le quitter, frappez le bateau pour le faire tomber en tant qu'objet.
Spruce Boat=Bateau en Sapin Spruce Boat=Bateau en Sapin
Water vehicle=Véhicule aquatique Water vehicle=Véhicule aquatique
Sneak to dismount=

View File

@ -1,7 +1,7 @@
name = mcl_boats name = mcl_boats
author = PilzAdam author = PilzAdam
description = Adds drivable boats. description = Adds drivable boats.
depends = mcl_player, flowlib, mcl_title depends = mcl_player, flowlib
optional_depends = mcl_core, doc_identifier optional_depends = mcl_core, doc_identifier

View File

@ -67,9 +67,14 @@ function mcl_burning.set_on_fire(obj, burn_time)
end end
if not storage.burn_time or burn_time >= storage.burn_time then if not storage.burn_time or burn_time >= storage.burn_time then
if obj:is_player() then if obj:is_player() and not storage.fire_hud_id then
mcl_burning.channels[obj]:send_all(tostring(mcl_burning.animation_frames)) storage.fire_hud_id = obj:hud_add({
mcl_burning.channels[obj]:send_all("start") hud_elem_type = "image",
position = {x = 0.5, y = 0.5},
scale = {x = -100, y = -100},
text = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. 1,
z_index = 1000,
})
end end
storage.burn_time = burn_time storage.burn_time = burn_time
storage.fire_damage_timer = 0 storage.fire_damage_timer = 0
@ -90,6 +95,7 @@ function mcl_burning.set_on_fire(obj, burn_time)
fire_entity:set_properties({visual_size = size}) fire_entity:set_properties({visual_size = size})
fire_entity:set_attach(obj, "", offset, {x = 0, y = 0, z = 0}) fire_entity:set_attach(obj, "", offset, {x = 0, y = 0, z = 0})
local fire_luaentity = fire_entity:get_luaentity() local fire_luaentity = fire_entity:get_luaentity()
fire_luaentity:update_frame(obj, storage)
for _, other in pairs(minetest.get_objects_inside_radius(fire_entity:get_pos(), 0)) do for _, other in pairs(minetest.get_objects_inside_radius(fire_entity:get_pos(), 0)) do
local other_luaentity = other:get_luaentity() local other_luaentity = other:get_luaentity()
@ -105,7 +111,9 @@ function mcl_burning.extinguish(obj)
if mcl_burning.is_burning(obj) then if mcl_burning.is_burning(obj) then
local storage = mcl_burning.get_storage(obj) local storage = mcl_burning.get_storage(obj)
if obj:is_player() then if obj:is_player() then
mcl_burning.channels[obj]:send_all("stop") if storage.fire_hud_id then
obj:hud_remove(storage.fire_hud_id)
end
mcl_burning.storage[obj] = {} mcl_burning.storage[obj] = {}
else else
storage.burn_time = nil storage.burn_time = nil

View File

@ -7,7 +7,6 @@ local get_item_group = minetest.get_item_group
mcl_burning = { mcl_burning = {
storage = {}, storage = {},
channels = {},
animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8 animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8
} }
@ -55,11 +54,12 @@ minetest.register_on_joinplayer(function(player)
end end
mcl_burning.storage[player] = storage mcl_burning.storage[player] = storage
mcl_burning.channels[player] = minetest.mod_channel_join("mcl_burning:" .. player:get_player_name())
end) end)
minetest.register_on_leaveplayer(function(player) minetest.register_on_leaveplayer(function(player)
player:get_meta():set_string("mcl_burning:data", minetest.serialize(mcl_burning.storage[player])) local storage = mcl_burning.storage[player]
storage.fire_hud_id = nil
player:get_meta():set_string("mcl_burning:data", minetest.serialize(storage))
mcl_burning.storage[player] = nil mcl_burning.storage[player] = nil
end) end)
@ -68,28 +68,27 @@ minetest.register_entity("mcl_burning:fire", {
initial_properties = { initial_properties = {
physical = false, physical = false,
collisionbox = {0, 0, 0, 0, 0, 0}, collisionbox = {0, 0, 0, 0, 0, 0},
visual = "upright_sprite", visual = "cube",
textures = {
name = "mcl_burning_entity_flame_animated.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 1.0,
},
},
spritediv = {x = 1, y = mcl_burning.animation_frames},
pointable = false, pointable = false,
glow = -1, glow = -1,
backface_culling = false, backface_culling = false,
}, },
animation_frame = 0, animation_frame = 0,
animation_timer = 0, animation_timer = 0,
on_activate = function(self) on_step = function(self, dtime)
self.object:set_sprite({x = 0, y = 0}, mcl_burning.animation_frames, 1.0 / mcl_burning.animation_frames) local parent, storage = self:sanity_check()
end,
on_step = function(self) if parent then
if not self:sanity_check() then self.animation_timer = self.animation_timer + dtime
if self.animation_timer >= 0.1 then
self.animation_timer = 0
self.animation_frame = self.animation_frame + 1
if self.animation_frame > mcl_burning.animation_frames - 1 then
self.animation_frame = 0
end
self:update_frame(parent, storage)
end
else
self.object:remove() self.object:remove()
end end
end, end,
@ -97,15 +96,23 @@ minetest.register_entity("mcl_burning:fire", {
local parent = self.object:get_attach() local parent = self.object:get_attach()
if not parent then if not parent then
return false return
end end
local storage = mcl_burning.get_storage(parent) local storage = mcl_burning.get_storage(parent)
if not storage or not storage.burn_time then if not storage or not storage.burn_time then
return false return
end end
return true return parent, storage
end,
update_frame = function(self, parent, storage)
local frame_overlay = "^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. self.animation_frame
local fire_texture = "mcl_burning_entity_flame_animated.png" .. frame_overlay
self.object:set_properties({textures = {"blank.png", "blank.png", fire_texture, fire_texture, fire_texture, fire_texture}})
if parent:is_player() then
parent:hud_change(storage.fire_hud_id, "text", "mcl_burning_hud_flame_animated.png" .. frame_overlay)
end
end, end,
}) })

View File

@ -290,10 +290,10 @@ function minetest.handle_node_drops(pos, drops, digger)
end end
end end
if digger and mcl_experience.throw_xp and not silk_touch_drop then if digger and mcl_experience.throw_experience and not silk_touch_drop then
local experience_amount = minetest.get_item_group(dug_node.name,"xp") local experience_amount = minetest.get_item_group(dug_node.name,"xp")
if experience_amount > 0 then if experience_amount > 0 then
mcl_experience.throw_xp(pos, experience_amount) mcl_experience.throw_experience(pos, experience_amount)
end end
end end
@ -480,7 +480,7 @@ minetest.register_entity(":__builtin:item", {
end, end,
get_staticdata = function(self) get_staticdata = function(self)
local data = minetest.serialize({ return minetest.serialize({
itemstring = self.itemstring, itemstring = self.itemstring,
always_collect = self.always_collect, always_collect = self.always_collect,
age = self.age, age = self.age,
@ -488,39 +488,6 @@ minetest.register_entity(":__builtin:item", {
_flowing = self._flowing, _flowing = self._flowing,
_removed = self._removed, _removed = self._removed,
}) })
-- sfan5 guessed that the biggest serializable item
-- entity would have a size of 65530 bytes. This has
-- been experimentally verified to be still too large.
--
-- anon5 has calculated that the biggest serializable
-- item entity has a size of exactly 65487 bytes:
--
-- 1. serializeString16 can handle max. 65535 bytes.
-- 2. The following engine metadata is always saved:
-- • 1 byte (version)
-- • 2 byte (length prefix)
-- • 14 byte “__builtin:item”
-- • 4 byte (length prefix)
-- • 2 byte (health)
-- • 3 × 4 byte = 12 byte (position)
-- • 4 byte (yaw)
-- • 1 byte (version 2)
-- • 2 × 4 byte = 8 byte (pitch and roll)
-- 3. This leaves 65487 bytes for the serialization.
if #data > 65487 then -- would crash the engine
local stack = ItemStack(self.itemstring)
stack:get_meta():from_table(nil)
self.itemstring = stack:to_string()
minetest.log(
"warning",
"Overlong item entity metadata removed: “" ..
self.itemstring ..
"” had serialized length of " ..
#data
)
return self:get_staticdata()
end
return data
end, end,
on_activate = function(self, staticdata, dtime_s) on_activate = function(self, staticdata, dtime_s)
@ -608,7 +575,7 @@ minetest.register_entity(":__builtin:item", {
return true return true
end, end,
on_step = function(self, dtime, moveresult) on_step = function(self, dtime)
if self._removed then if self._removed then
self.object:set_properties({ self.object:set_properties({
physical = false physical = false
@ -675,18 +642,6 @@ minetest.register_entity(":__builtin:item", {
end end
end end
-- Destroy item when it collides with a cactus
if moveresult and moveresult.collides then
for _, collision in pairs(moveresult.collisions) do
local pos = collision.node_pos
if collision.type == "node" and minetest.get_node(pos).name == "mcl_core:cactus" then
self._removed = true
self.object:remove()
return
end
end
end
-- Push item out when stuck inside solid opaque node -- Push item out when stuck inside solid opaque node
if def and def.walkable and def.groups and def.groups.opaque == 1 then if def and def.walkable and def.groups and def.groups.opaque == 1 then
local shootdir local shootdir

View File

@ -198,20 +198,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
else else
self._last_float_check = self._last_float_check + dtime self._last_float_check = self._last_float_check + dtime
end end
local pos, rou_pos, node
local pos, rou_pos, node = self.object:get_pos()
local r = 0.6
for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do
if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then
detach_driver(self)
for d = 1, #drop do
minetest.add_item(pos, drop[d])
end
self.object:remove()
return
end
end
-- Drop minecart if it isn't on a rail anymore -- Drop minecart if it isn't on a rail anymore
if self._last_float_check >= mcl_minecarts.check_float_time then if self._last_float_check >= mcl_minecarts.check_float_time then
pos = self.object:get_pos() pos = self.object:get_pos()
@ -659,7 +646,7 @@ register_minecart(
if player then if player then
mcl_player.player_set_animation(player, "sit" , 30) mcl_player.player_set_animation(player, "sit" , 30)
player:set_eye_offset({x=0, y=-5.5, z=0},{x=0, y=-4, z=0}) player:set_eye_offset({x=0, y=-5.5, z=0},{x=0, y=-4, z=0})
mcl_title.set(clicker, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60}) mcl_tmp_message.message(clicker, S("Sneak to dismount"))
end end
end, name) end, name)
end end

View File

@ -33,4 +33,3 @@ Activates minecarts when powered=Active les wagonnets lorsqu'il est alimenté
Emits redstone power when a minecart is detected=Émet de l'énergie redstone lorsqu'un wagonnet est détecté Emits redstone power when a minecart is detected=Émet de l'énergie redstone lorsqu'un wagonnet est détecté
Vehicle for fast travel on rails=Véhicule pour voyager rapidement sur rails Vehicle for fast travel on rails=Véhicule pour voyager rapidement sur rails
Can be ignited by tools or powered activator rail=Peut être allumé par des outils ou un rail d'activation motorisé Can be ignited by tools or powered activator rail=Peut être allumé par des outils ou un rail d'activation motorisé
Sneak to dismount=

View File

@ -1,6 +1,6 @@
name = mcl_minecarts name = mcl_minecarts
author = Krock author = Krock
description = Minecarts are vehicles to move players quickly on rails. description = Minecarts are vehicles to move players quickly on rails.
depends = mcl_title, mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons depends = mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons
optional_depends = doc_identifier, mcl_wip optional_depends = doc_identifier, mcl_wip

View File

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

View File

@ -103,7 +103,7 @@ mobs:register_mob("mobs_mc:enderdragon", {
mcl_portals.spawn_gateway_portal() mcl_portals.spawn_gateway_portal()
mcl_structures.call_struct(self._portal_pos, "end_exit_portal_open") mcl_structures.call_struct(self._portal_pos, "end_exit_portal_open")
if self._initial then 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}) minetest.set_node(vector.add(self._portal_pos, vector.new(3, 5, 3)), {name = mobs_mc.items.dragon_egg})
end end
end end

View File

@ -28,7 +28,6 @@ Pig=
Polar Bear= Polar Bear=
Rabbit= Rabbit=
Killer Bunny= Killer Bunny=
The Killer Bunny=
Sheep= Sheep=
Shulker= Shulker=
Silverfish= Silverfish=

View File

@ -233,4 +233,4 @@ mobs:spawn(spawn_grass)
mobs:register_egg("mobs_mc:rabbit", S("Rabbit"), "mobs_mc_spawn_icon_rabbit.png", 0) mobs:register_egg("mobs_mc:rabbit", S("Rabbit"), "mobs_mc_spawn_icon_rabbit.png", 0)
-- Note: This spawn egg does not exist in Minecraft -- Note: This spawn egg does not exist in Minecraft
mobs:register_egg("mobs_mc:killer_bunny", S("Killer Bunny"), "mobs_mc_spawn_icon_rabbit_caerbannog.png", 0) mobs:register_egg("mobs_mc:killer_bunny", S("Killer Bunny"), "mobs_mc_spawn_icon_rabbit.png^[colorize:#FF0000:192", 0) -- TODO: Update inventory image

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1,31 +0,0 @@
# lightning
Lightning mod for MineClone2 with the following API:
## lightning.register_on_strike(function(pos, pos2, objects))
Custom function called when a lightning strikes.
* `pos`: impact position
* `pos2`: rounded node position where fire is placed
* `objects`: table with ObjectRefs of all objects within a radius of 3.5 around pos2
## lightning.strike(pos)
Let a lightning strike.
* `pos`: optional, if not given a random pos will be chosen
* `returns`: bool - success if a strike happened
### Examples:
```
lightning.register_on_strike(function(pos, pos2, objects)
for _, obj in pairs(objects) do
obj:remove()
end
minetest.add_entity(pos, "mobs_mc:sheep")
end)
minetest.register_on_respawnplayer(function(player)
lightning.strike(player:get_pos())
end)
```

View File

@ -31,7 +31,6 @@ lightning = {
size = 100, size = 100,
-- disable this to stop lightning mod from striking -- disable this to stop lightning mod from striking
auto = true, auto = true,
on_strike_functions = {},
} }
local rng = PcgRandom(32321123312123) local rng = PcgRandom(32321123312123)
@ -55,18 +54,6 @@ end
minetest.register_globalstep(revertsky) minetest.register_globalstep(revertsky)
-- lightning strike API
-- See API.md
--[[
lightning.register_on_strike(function(pos, pos2, objects)
-- code
end)
]]
function lightning.register_on_strike(func)
table.insert(lightning.on_strike_functions, func)
end
-- select a random strike point, midpoint -- select a random strike point, midpoint
local function choose_pos(pos) local function choose_pos(pos)
if not pos then if not pos then
@ -107,6 +94,7 @@ local function choose_pos(pos)
return pos, pos2 return pos, pos2
end end
-- lightning strike API
-- * pos: optional, if not given a random pos will be chosen -- * pos: optional, if not given a random pos will be chosen
-- * returns: bool - success if a strike happened -- * returns: bool - success if a strike happened
function lightning.strike(pos) function lightning.strike(pos)
@ -120,28 +108,21 @@ function lightning.strike(pos)
if not pos then if not pos then
return false return false
end end
local objects = get_objects_inside_radius(pos2, 3.5)
if lightning.on_strike_functions then
for _, func in pairs(lightning.on_strike_functions) do
func(pos, pos2, objects)
end
end
end
lightning.register_on_strike(function(pos, pos2, objects)
local particle_pos = vector.offset(pos2, 0, (lightning.size / 2) + 0.5, 0)
local particle_size = lightning.size * 10
local time = 0.2
add_particlespawner({ add_particlespawner({
amount = 1, amount = 1,
time = time, time = 0.2,
-- make it hit the top of a block exactly with the bottom -- make it hit the top of a block exactly with the bottom
minpos = particle_pos, minpos = {x = pos2.x, y = pos2.y + (lightning.size / 2) + 1/2, z = pos2.z },
maxpos = particle_pos, maxpos = {x = pos2.x, y = pos2.y + (lightning.size / 2) + 1/2, z = pos2.z },
minexptime = time, minvel = {x = 0, y = 0, z = 0},
maxexptime = time, maxvel = {x = 0, y = 0, z = 0},
minsize = particle_size, minacc = {x = 0, y = 0, z = 0},
maxsize = particle_size, maxacc = {x = 0, y = 0, z = 0},
minexptime = 0.2,
maxexptime = 0.2,
minsize = lightning.size * 10,
maxsize = lightning.size * 10,
collisiondetection = true, collisiondetection = true,
vertical = true, vertical = true,
-- to make it appear hitting the node that will get set on fire, make sure -- to make it appear hitting the node that will get set on fire, make sure
@ -154,14 +135,18 @@ lightning.register_on_strike(function(pos, pos2, objects)
sound_play({ name = "lightning_thunder", gain = 10 }, { pos = pos, max_hear_distance = 500 }, true) sound_play({ name = "lightning_thunder", gain = 10 }, { pos = pos, max_hear_distance = 500 }, true)
-- damage nearby objects, transform mobs -- damage nearby objects, transform mobs
for _, obj in pairs(objects) do -- TODO: use an API insteed of hardcoding this behaviour
local objs = get_objects_inside_radius(pos2, 3.5)
for o=1, #objs do
local obj = objs[o]
local lua = obj:get_luaentity() local lua = obj:get_luaentity()
if lua and lua._on_strike then -- pig → zombie pigman (no damage)
lua._on_strike(lua, pos, pos2, objects)
end
-- remove this when mob API is done
if lua and lua.name == "mobs_mc:pig" then if lua and lua.name == "mobs_mc:pig" then
mcl_util.replace_mob(obj, "mobs_mc:pigman") local rot = obj:get_yaw()
obj:remove()
obj = add_entity(pos2, "mobs_mc:pigman")
obj:set_yaw(rot)
-- mooshroom: toggle color red/brown (no damage)
elseif lua and lua.name == "mobs_mc:mooshroom" then elseif lua and lua.name == "mobs_mc:mooshroom" then
if lua.base_texture[1] == "mobs_mc_mooshroom.png" then if lua.base_texture[1] == "mobs_mc_mooshroom.png" then
lua.base_texture = { "mobs_mc_mooshroom_brown.png", "mobs_mc_mushroom_brown.png" } lua.base_texture = { "mobs_mc_mooshroom_brown.png", "mobs_mc_mushroom_brown.png" }
@ -169,10 +154,23 @@ lightning.register_on_strike(function(pos, pos2, objects)
lua.base_texture = { "mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png" } lua.base_texture = { "mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png" }
end end
obj:set_properties({textures = lua.base_texture}) obj:set_properties({textures = lua.base_texture})
elseif lua and lua.name == "mobs_mc:villager" then -- villager → witch (no damage)
mcl_util.replace_mob(obj, "mobs_mc:witch") --elseif lua and lua.name == "mobs_mc:villager" then
-- Witches are incomplete, this code is unused
-- TODO: Enable this code when witches are working.
--[[
local rot = obj:get_yaw()
obj:remove()
obj = minetest.add_entity(pos2, "mobs_mc:witch")
obj:set_yaw(rot)
]]
-- charged creeper
elseif lua and lua.name == "mobs_mc:creeper" then elseif lua and lua.name == "mobs_mc:creeper" then
mcl_util.replace_mob(obj, "mobs_mc:creeper_charged") local rot = obj:get_yaw()
obj:remove()
obj = add_entity(pos2, "mobs_mc:creeper_charged")
obj:set_yaw(rot)
-- Other objects: Just damage
else else
mcl_util.deal_damage(obj, 5, {type = "lightning_bolt"}) mcl_util.deal_damage(obj, 5, {type = "lightning_bolt"})
end end
@ -225,7 +223,8 @@ lightning.register_on_strike(function(pos, pos2, objects)
end end
end end
end end
end)
end
-- if other mods disable auto lightning during initialization, don't trigger the first lightning. -- if other mods disable auto lightning during initialization, don't trigger the first lightning.
after(5, function(dtime) after(5, function(dtime)

View File

@ -2,7 +2,7 @@
Using it as fuel turns it into: @1.=L'utiliser comme combustible le transforme en : @1. Using it as fuel turns it into: @1.=L'utiliser comme combustible le transforme en : @1.
@1 seconds=@1 secondes @1 seconds=@1 secondes
# Item count times item name # Item count times item name
@1×@2=@1×@ %@1×@2=%@1×@
# Itemname (25%) # Itemname (25%)
@1 (@2%)=@1 (@2%) @1 (@2%)=@1 (@2%)
# Itemname (<0.5%) # Itemname (<0.5%)

View File

@ -1,24 +0,0 @@
# mcl_item_id
Show the item ID of an item in the description.
With this API, you can register a different name space than "mineclone" for your mod.
## mcl_item_id.set_mod_namespace(modname, namespace)
Set a name space for all items in a mod.
* param1: the modname
* param2: (optional) string of the desired name space, if nil, it is the name of the mod
## mcl_item_id.get_mod_namespace(modname)
Get the name space of a mod registered with mcl_item_id.set_mod_namespace(modname, namespace).
* param1: the modname
### Examples:
The name of the mod is "mod" which registered an item called "mod:itemname".
* mcl_item_id.set_mod_namespace("mod", "mymod") will show "mymod:itemname" in the description of "mod:itemname"
* mcl_item_id.set_mod_namespace(minetest.get_current_modname()) will show "mod:itemname" in the description of "mod:itemname"
* mcl_item_id.get_mod_namespace(minetest.get_current_modname()) will return "mod"
(If no namespace is set by a mod, mcl_item_id.get_mod_namespace(minetest.get_current_modname()) will return "mineclone")

View File

@ -1,62 +0,0 @@
mcl_item_id = {
mod_namespaces = {},
}
local game = "mineclone"
function mcl_item_id.set_mod_namespace(modname, namespace)
local namespace = namespace or modname
mcl_item_id.mod_namespaces[modname] = namespace
end
function mcl_item_id.get_mod_namespace(modname)
local namespace = mcl_item_id.mod_namespaces[modname]
if namespace then
return namespace
else
return game
end
end
local same_id = {
enchanting = { "table" },
experience = { "bottle" },
heads = { "skeleton", "zombie", "creeper", "wither_skeleton" },
mobitems = { "rabbit", "chicken" },
walls = {
"andesite", "brick", "cobble", "diorite", "endbricks",
"granite", "mossycobble", "netherbrick", "prismarine",
"rednetherbrick", "redsandstone", "sandstone",
"stonebrick", "stonebrickmossy",
},
wool = {
"black", "blue", "brown", "cyan", "green",
"grey", "light_blue", "lime", "magenta", "orange",
"pink", "purple", "red", "silver", "white", "yellow",
},
}
tt.register_snippet(function(itemstring)
local def = minetest.registered_items[itemstring]
local item_split = itemstring:find(":")
local id_string = itemstring:sub(item_split)
local id_modname = itemstring:sub(1, item_split - 1)
local new_id = game .. id_string
local mod_namespace = mcl_item_id.get_mod_namespace(id_modname)
for mod, ids in pairs(same_id) do
for _, id in pairs(ids) do
if itemstring == "mcl_" .. mod .. ":" .. id then
new_id = game .. ":" .. id .. "_" .. mod:gsub("s", "")
end
end
end
if mod_namespace ~= game then
new_id = mod_namespace .. id_string
end
if mod_namespace ~= id_modname then
minetest.register_alias_force(new_id, itemstring)
end
if minetest.settings:get_bool("mcl_item_id_debug", false) then
return new_id, "#555555"
end
end)

View File

@ -1,3 +0,0 @@
name = mcl_item_id
author = NO11
depends = tt

View File

@ -45,4 +45,3 @@ Mining durability: @1=Grabehaltbarkeit: @1
Block breaking strength: @1=Blockbruchstärke: @1 Block breaking strength: @1=Blockbruchstärke: @1
@1 uses=@1 Verwendungen @1 uses=@1 Verwendungen
Unlimited uses=Unbegrenzte Verwendungen Unlimited uses=Unbegrenzte Verwendungen
Durability: @1=Haltbarkeit: @1

View File

@ -45,4 +45,3 @@ Mining durability: @1=
Block breaking strength: @1= Block breaking strength: @1=
@1 uses= @1 uses=
Unlimited uses= Unlimited uses=
Durability: @1=

View File

@ -107,8 +107,3 @@ tt.register_snippet(function(itemstring)
end end
end) end)
tt.register_snippet(function(itemstring, _, itemstack)
if itemstring:sub(1, 23) == "mcl_fishing:fishing_rod" or itemstring:sub(1, 12) == "mcl_bows:bow" then
return S("Durability: @1", S("@1 uses", mcl_util.calculate_durability(itemstack or ItemStack(itemstring))))
end
end)

View File

@ -425,7 +425,6 @@ function hb.hide_hudbar(player, identifier)
local name = player:get_player_name() local name = player:get_player_name()
local hudtable = hb.get_hudtable(identifier) local hudtable = hb.get_hudtable(identifier)
if hudtable == nil then return false end if hudtable == nil then return false end
if hudtable.hudstate[name].hidden == true then return true end
if hb.settings.bar_type == "progress_bar" then if hb.settings.bar_type == "progress_bar" then
if hudtable.hudids[name].icon then if hudtable.hudids[name].icon then
player:hud_change(hudtable.hudids[name].icon, "scale", {x=0,y=0}) player:hud_change(hudtable.hudids[name].icon, "scale", {x=0,y=0})
@ -444,7 +443,6 @@ function hb.unhide_hudbar(player, identifier)
local name = player:get_player_name() local name = player:get_player_name()
local hudtable = hb.get_hudtable(identifier) local hudtable = hb.get_hudtable(identifier)
if hudtable == nil then return false end if hudtable == nil then return false end
if hudtable.hudstate[name].hidden == false then return true end
local value = hudtable.hudstate[name].value local value = hudtable.hudstate[name].value
local max = hudtable.hudstate[name].max local max = hudtable.hudstate[name].max
if hb.settings.bar_type == "progress_bar" then if hb.settings.bar_type == "progress_bar" then

View File

@ -3,8 +3,123 @@ local S = minetest.get_translator(modname)
mcl_credits = { mcl_credits = {
players = {}, 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) local function add_hud_element(def, huds, y)

View File

@ -7,7 +7,6 @@ Creator of MineClone2=Schöpfer von MineClone2
Developers=Entwickler Developers=Entwickler
Jump to speed up (additionally sprint)=Springen, um zu beschleunigen (zusätzlich sprinten) Jump to speed up (additionally sprint)=Springen, um zu beschleunigen (zusätzlich sprinten)
Maintainers=Betreuer Maintainers=Betreuer
MineClone5=MineClone5
Original Mod Authors=Original-Mod-Autoren Original Mod Authors=Original-Mod-Autoren
Sneak to skip=Schleichen zum Überspringen Sneak to skip=Schleichen zum Überspringen
Textures=Texturen Textures=Texturen

View File

@ -1,14 +0,0 @@
# textdomain: mcl_credits
3D Models=
A faithful Open Source clone of Minecraft=
Contributors=
Creator of MineClone=
Creator of MineClone2=
Developers=
Jump to speed up (additionally sprint)=
Maintainers=
MineClone5=
Original Mod Authors=
Sneak to skip=
Textures=
Translations=

View File

@ -1,14 +0,0 @@
# textdomain: mcl_credits
3D Models=Modèles 3D
A faithful Open Source clone of Minecraft=Un clone open source de Minecraft
Contributors=Contributeurs
Creator of MineClone=Créateur de MineClone
Creator of MineClone2=Créateur de MineClone2
Developers=Développeurs
Jump to speed up (additionally sprint)=Saut pour accélérer (peut être combiné avec sprint)
Maintainers=Mainteneurs
MineClone5=MineClone5
Original Mod Authors=Auteurs des mods originaux
Sneak to skip=Shift pour passer
Textures=Textures
Translations=Traductions

View File

@ -1,14 +0,0 @@
# textdomain: mcl_credits
3D Models=
A faithful Open Source clone of Minecraft=
Contributors=
Creator of MineClone=
Creator of MineClone2=
Developers=
Jump to speed up (additionally sprint)=
Maintainers=
MineClone5=
Original Mod Authors=
Sneak to skip=
Textures=
Translations=

View File

@ -1,14 +0,0 @@
# textdomain: mcl_credits
3D Models=
A faithful Open Source clone of Minecraft=
Contributors=
Creator of MineClone=
Creator of MineClone2=
Developers=
Jump to speed up (additionally sprint)=
Maintainers=
MineClone5=
Original Mod Authors=
Sneak to skip=
Textures=
Translations=

View File

@ -1,141 +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("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,195 +1,183 @@
mcl_experience = { local S = minetest.get_translator(minetest.get_current_modname())
on_add_xp = {},
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
dofile(modpath .. "/command.lua") while (xp > size_to_xp[i][1]) and (i < l) do
dofile(modpath .. "/orb.lua") i = i + 1
dofile(modpath .. "/bottle.lua") end
return ((i-1) / (l-1) * delta_size + size_min)/100
-- local storage
local hud_bars = {}
local hud_levels = {}
local caches = {}
-- helpers
local function 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
elseif xp > 352 then
a, b, c = 2.5, -40.5, 360 - xp
else
a, b, c = 1, 6, -xp
end end
D = b * b - 4 * a * c minetest.register_on_mods_loaded(function()
registered_nodes = minetest.registered_nodes
end)
if D == 0 then local function load_data(player)
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
return math.floor(math.pow(level, 2) + 6 * level)
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)
end
return 0
end
local function calculate_bounds(level)
return level_to_xp(level), level_to_xp(level + 1)
end
local function xp_to_bar(xp, level)
local xp_min, xp_max = calculate_bounds(level)
return (xp - xp_min) / (xp_max - xp_min)
end
local function bar_to_xp(bar, level)
local xp_min, xp_max = calculate_bounds(level)
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
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() 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
if old_level == cache.level then -- saves data to be utilized on next login
minetest.sound_play("mcl_experience", { local function save_data(player)
to_player = name, local name = player:get_player_name()
gain = 0.1, local temp_pool = pool[name]
pitch = math.random(75, 99) / 100, local meta = player:get_meta()
meta:set_int("xp", temp_pool.xp)
pool[name] = nil
end
local player_huds = {} -- the list of players hud lists (3d array)
hud_manager = {} -- hud manager class
-- 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
cache.last_time = current_time 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 else
minetest.sound_play("mcl_experience_level_up", {
to_player = name,
gain = 0.2,
})
cache.last_time = current_time + 0.2
end
end
end
function mcl_experience.throw_xp(pos, total_xp)
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))
if not obj then
return false return false
end end
obj:set_velocity(vector.new(
math.random(-2, 2) * math.random(),
math.random( 2, 5),
math.random(-2, 2) * math.random()
))
i = i + xp
j = j + 1
end
end end
-------------------
function mcl_experience.update(player) -- saves specific users data for when they relog
local xp = mcl_experience.get_xp(player) minetest.register_on_leaveplayer(function(player)
local cache = caches[player] save_data(player)
end)
cache.level = xp_to_level(xp) -- is used for shutdowns to save all data
local function save_all()
if not minetest.is_creative_enabled(player:get_player_name()) then for name,_ in pairs(pool) do
player:hud_change(hud_bars[player], "text", "mcl_experience_bar_background.png^[lowpart:" local player = minetest.get_player_by_name(name)
.. math.floor(math.floor(xp_to_bar(xp, cache.level) * 18) / 18 * 100) if player then
.. ":mcl_experience_bar.png^[transformR270" save_data(player)
)
if cache.level == 0 then
player:hud_change(hud_levels[player], "text", "")
else
player:hud_change(hud_levels[player], "text", tostring(cache.level))
end end
end end
end end
function mcl_experience.register_on_add_xp(func, priority) -- save all data to mod storage on shutdown
table.insert(mcl_experience.on_add_xp, {func = func, priority = priority or 0}) 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 end
-- callbacks 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) minetest.register_on_joinplayer(function(player)
caches[player] = {
last_time = get_time(),
}
if not minetest.is_creative_enabled(player:get_player_name()) then load_data(player)
hud_bars[player] = player:hud_add({
name = player:get_player_name()
temp_pool = pool[name]
hud_manager.add_hud(player,"experience_bar",
{
hud_elem_type = "image", 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}, position = {x=0.5, y=1},
offset = {x = (-9 * 28) - 3, y = -(48 + 24 + 16 - 5)}, offset = {x = (-9 * 28) - 3, y = -(48 + 24 + 16 - 5)},
scale = {x = 2.8, y = 3.0}, scale = {x = 2.8, y = 3.0},
@ -197,31 +185,457 @@ minetest.register_on_joinplayer(function(player)
z_index = 11, z_index = 11,
}) })
hud_levels[player] = player:hud_add({ hud_manager.add_hud(player,"xp_level",
hud_elem_type = "text", {
position = {x = 0.5, y = 1}, hud_elem_type = "text", position = {x=0.5, y=1},
name = "xp_level", text = tostring(temp_pool.level),
number = 0x80FF20, number = 0x80FF20,
offset = {x = 0, y = -(48 + 24 + 24)}, offset = {x = 0, y = -(48 + 24 + 24)},
z_index = 12, 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
elseif xp > 352 then
a, b, c = 2.5, -40.5, 360-xp
else
a, b, c = 1, 6, -xp
end
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)))
end
return 0
end end
mcl_experience.update(player) function mcl_experience.level_to_xp(level)
end) if (level >= 1 and level <= 16) then
return math.floor(math.pow(level, 2) + 6 * level)
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);
end
return 0
end
minetest.register_on_leaveplayer(function(player) function mcl_experience.xp_to_bar(xp, level)
hud_bars[player] = nil local level = level or mcl_experience.xp_to_level(xp)
hud_levels[player] = nil local xp_this_level = mcl_experience.level_to_xp(level)
caches[player] = nil local xp_next_level = mcl_experience.level_to_xp(level+1)
end) 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
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
function mcl_experience.add_experience(player, experience)
local name = player:get_player_name()
local temp_pool = pool[name]
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 = experience * multiplier
local new_wear = wear - repair
if new_wear < 0 then
experience = math.floor(-new_wear / multiplier + 0.5)
new_wear = 0
else
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
--reset player level
local name
local temp_pool
local xp_amount
minetest.register_on_dieplayer(function(player) minetest.register_on_dieplayer(function(player)
if not minetest.settings:get_bool("mcl_keepInventory", false) then if minetest.settings:get_bool("mcl_keepInventory", false) then
mcl_experience.throw_xp(player:get_pos(), mcl_experience.get_xp(player)) return
mcl_experience.set_xp(player, 0)
end 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) end)
minetest.register_on_mods_loaded(function() local collector, pos, pos2
table.sort(mcl_experience.on_add_xp, function(a, b) return a.priority < b.priority end) local direction, distance, player_velocity, goal
end) 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
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({
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
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,
})
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,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

@ -417,7 +417,8 @@ function mcl_inventory.set_creative_formspec(player, start_i, pagenum, inv_size,
return return
"style["..this_tab..";border=false;bgimg=;bgimg_pressed=]".. "style["..this_tab..";border=false;bgimg=;bgimg_pressed=]"..
"item_image_button[" .. boffset[this_tab] ..";1,1;"..tab_icon[this_tab]..";"..this_tab..";]".. "item_image_button[" .. boffset[this_tab] ..";1,1;"..tab_icon[this_tab]..";"..this_tab..";]"..
"image[" .. offset[this_tab] .. ";1.5,1.44;" .. bg_img .. "]" "image[" .. offset[this_tab] .. ";1.5,1.44;" .. bg_img .. "]" ..
"image[" .. boffset[this_tab] .. ";1,1;crafting_creative_marker.png]"
end end
local caption = "" local caption = ""
if name ~= "inv" and filtername[name] then if name ~= "inv" and filtername[name] then

View File

@ -1,50 +0,0 @@
# mcl_title
Allow mods to show messages in the hud of players.
## mcl_title.set(player, type, data)
Show a hud message of `type` to player `player` with `data` as params.
The element will stay for the per-player param `stay` or `data.stay` (in gametick which is 1/20 second).
Here is a usage exemple:
```lua
--show a title in the HUD with minecraft color "gold"
mcl_title.set(player, "title", {text="dummy text", color="gold"})
--show a subtitle in the HUD with hex color "#612D2D"
mcl_title.set(player, "subtitle", {text="dummy subtitle", color="#612D2D"})
--show an actionbar in the HUD (above the hotbar) with minecraft color "red"
mcl_title.set(player, "subtitle", {text="dummy actionbar", color="red"})
--show a title in the HUD with minecraft color "gold" staying for 3 seconds (override stay setting)
mcl_title.set(player, "title", {text="dummy text", color="gold", stay=60})
```
## mcl_title.remove(player, type)
Hide HUD element of type `type` for player `player`.
## mcl_title.clear(player)
Remove every title/subtitle/actionbar from a player.
Basicaly run `mcl_title.remove(player, type)` for every type.
## mcl_title.params_set(player, params)
Allow mods to set `stay` and upcomming `fadeIn`/`fadeOut` params.
```lua
mcl_title.params_set(player, {stay = 600}) --elements with no 'data.stay' field will stay during 30s (600/20)
```
## mcl_title.params_get(player)
Get `stay` and upcomming `fadeIn` and `fadeOut` params of a player as a table.
```lua
mcl_title.params_get(player)
```

View File

@ -1,236 +0,0 @@
--Based on:
--https://www.digminecraft.com/game_commands/title_command.php
--https://youtu.be/oVrtQRO2hpY
--TODO: use SSCSM to reduce lag and network trafic (just send modchannel messages)
--TODO: fadeIn and fadeOut animation (needs engine change: SSCSM or native support)
--TODO: allow obfuscating text (needs engine change: SSCSM or native support)
--TODO: allow colorizing and styling of part of the text (NEEDS ENGINE CHANGE!!!)
--TODO: exactly mc like layout
--Note that the table storing timeouts use playername as index insteed of player objects (faster)
--This is intended in order to speedup the process of removing HUD elements the the timeout is up
local huds_idx = {}
local hud_hide_timeouts = {}
hud_hide_timeouts.title = {}
hud_hide_timeouts.subtitle = {}
hud_hide_timeouts.actionbar = {}
huds_idx.title = {}
huds_idx.subtitle = {}
huds_idx.actionbar = {}
mcl_title = {}
mcl_title.defaults = {fadein = 10, stay = 70, fadeout = 20}
mcl_title.layout = {}
mcl_title.layout.title = {position = {x = 0.5, y = 0.5}, alignment = {x = 0, y = -1.3}, size = 7}
mcl_title.layout.subtitle = {position = {x = 0.5, y = 0.5}, alignment = {x = 0, y = 1.7}, size = 4}
mcl_title.layout.actionbar = {position = {x = 0.5, y = 1}, alignment = {x = 0, y = 0}, size = 1}
local get_color = mcl_util.get_color
--local string = string
local pairs = pairs
local function gametick_to_secondes(gametick)
if gametick then
return gametick / 20
else
return nil
end
end
--https://github.com/minetest/minetest/blob/b3b075ea02034306256b486dd45410aa765f035a/doc/lua_api.txt#L8477
--[[
local function style_to_bits(bold, italic)
if bold then
if italic then
return 3
else
return 1
end
else
if italic then
return 2
else
return 0
end
end
end
]]
--PARAMS SYSTEM
local player_params = {}
minetest.register_on_joinplayer(function(player)
--local playername = player:get_player_name()
player_params[player] = {
stay = mcl_title.defaults.stay,
--fadeIn = mcl_title.defaults.fadein,
--fadeOut = mcl_title.defaults.fadeout,
}
local _, hex_color = get_color("white")
huds_idx.title[player] = player:hud_add({
hud_elem_type = "text",
position = mcl_title.layout.title.position,
alignment = mcl_title.layout.title.alignment,
text = "",
--style = 0,
size = {x = mcl_title.layout.title.size},
number = hex_color,
z_index = 100,
})
huds_idx.subtitle[player] = player:hud_add({
hud_elem_type = "text",
position = mcl_title.layout.subtitle.position,
alignment = mcl_title.layout.subtitle.alignment,
text = "",
--style = 0,
size = {x = mcl_title.layout.subtitle.size},
number = hex_color,
z_index = 100,
})
huds_idx.actionbar[player] = player:hud_add({
hud_elem_type = "text",
position = mcl_title.layout.actionbar.position,
offset = {x = 0, y = -210},
alignment = mcl_title.layout.actionbar.alignment,
--style = 0,
text = "",
size = {x = mcl_title.layout.actionbar.size},
number = hex_color,
z_index = 100,
})
end)
minetest.register_on_leaveplayer(function(player)
local playername = player:get_player_name()
--remove player params from the list
player_params[player] = nil
--remove HUD idx from the list (HUD elements are removed by the engine)
huds_idx.title[player] = nil
huds_idx.subtitle[player] = nil
huds_idx.actionbar[player] = nil
--remove timers from list
hud_hide_timeouts.title[playername] = nil
hud_hide_timeouts.subtitle[playername] = nil
hud_hide_timeouts.actionbar[playername] = nil
end)
function mcl_title.params_set(player, data)
player_params[player] = {
stay = data.stay or mcl_title.defaults.stay,
--fadeIn = data.fadeIn or mcl_title.defaults.fadein,
--fadeOut = data.fadeOut or mcl_title.defaults.fadeout,
}
end
function mcl_title.params_get(player)
return player_params[player]
end
--API FUNCTIONS
function mcl_title.set(player, type, data)
if not data.color then
data.color = "white"
end
local _, hex_color = get_color(data.color)
if not hex_color then
return false
end
player:hud_change(huds_idx[type][player], "text", data.text)
player:hud_change(huds_idx[type][player], "number", hex_color)
--apply bold and italic
--player:hud_change(huds_idx[type][player], "style", style_to_bits(data.bold, data.italic))
hud_hide_timeouts[type][player:get_player_name()] = gametick_to_secondes(data.stay) or gametick_to_secondes(mcl_title.params_get(player).stay)
return true
end
function mcl_title.remove(player, type)
if player then
player:hud_change(huds_idx[type][player], "text", "")
--player:hud_change(huds_idx[type][player], "style", 0) --no styling
end
end
function mcl_title.clear(player)
mcl_title.remove(player, "title")
mcl_title.remove(player, "subtitle")
mcl_title.remove(player, "actionbar")
end
minetest.register_on_dieplayer(function(player)
mcl_title.clear(player)
end)
minetest.register_globalstep(function(dtime)
local new_timeouts = {
title = {},
subtitle = {},
actionbar = {},
}
for element, content in pairs(hud_hide_timeouts) do
for name, timeout in pairs(content) do
timeout = timeout - dtime
if timeout <= 0 then
local player = minetest.get_player_by_name(name)
mcl_title.remove(player, element)
else
new_timeouts[element][name] = timeout
end
end
end
hud_hide_timeouts = new_timeouts
end)
--DEBUG STUFF!!
--[[
minetest.register_chatcommand("title", {
func = function(name, param)
local player = minetest.get_player_by_name(name)
mcl_title.set(player, "title", {text=param, color="gold", bold=true, italic=true})
end,
})
minetest.register_chatcommand("subtitle", {
func = function(name, param)
local player = minetest.get_player_by_name(name)
mcl_title.set(player, "subtitle", {text=param, color="gold"})
end,
})
minetest.register_chatcommand("actionbar", {
func = function(name, param)
local player = minetest.get_player_by_name(name)
mcl_title.set(player, "actionbar", {text=param, color="gold"})
end,
})
minetest.register_chatcommand("timeout", {
func = function(name, param)
local player = minetest.get_player_by_name(name)
mcl_title.params_set(player, {stay = 600})
end,
})
minetest.register_chatcommand("all", {
func = function(name, param)
local player = minetest.get_player_by_name(name)
mcl_title.params_set(player, {stay = 600})
mcl_title.set(player, "title", {text=param, color="gold"})
mcl_title.set(player, "subtitle", {text=param, color="gold"})
mcl_title.set(player, "actionbar", {text=param, color="gold"})
end,
})
]]

View File

@ -1,4 +0,0 @@
name = mcl_title
description = Add an API to add in HUD title
depends = mcl_colors
author = AFCMS

View File

@ -0,0 +1,7 @@
# mcl_temp_message
Allow mods to show short messages in the hud of players.
## mcl_tmp_message.message(player, message)
Show above the hotbar a hud message <message> to player <player>.

View File

@ -0,0 +1,44 @@
mcl_tmp_message = {}
local huds = {}
local hud_hide_timeouts = {}
function mcl_tmp_message.message(player, message)
local name = player:get_player_name()
player:hud_change(huds[name], "text", message)
hud_hide_timeouts[name] = 3
end
minetest.register_on_joinplayer(function(player)
huds[player:get_player_name()] = player:hud_add({
hud_elem_type = "text",
position = {x=0.5, y=1},
offset = {x = 0, y = -210},
alignment = {x=0, y=0},
number = 0xFFFFFF ,
text = "",
z_index = 100,
})
end)
minetest.register_on_leaveplayer(function(player)
local name = player:get_player_name()
huds[name] = nil
hud_hide_timeouts[name] = nil
end)
minetest.register_globalstep(function(dtime)
local new_timeouts = {}
for name, timeout in pairs(hud_hide_timeouts) do
timeout = timeout - dtime
if timeout <= 0 then
local player = minetest.get_player_by_name(name)
if player then
player:hud_change(huds[name], "text", "")
end
else
new_timeouts[name] = timeout
end
end
hud_hide_timeouts = new_timeouts
end)

View File

@ -0,0 +1,3 @@
name = mcl_tmp_message
author = Fleckenstein
description = A simple API to show a temporary message to a player

View File

@ -168,56 +168,6 @@ local dispenserdef = {
end end
inv:set_stack("main", stack_id, stack) inv:set_stack("main", stack_id, stack)
-- Use shears on sheeps
elseif igroups.shears then
for _, obj in pairs(minetest.get_objects_inside_radius(droppos, 1)) do
local entity = obj:get_luaentity()
if entity and not entity.child and not entity.gotten then
local entname = entity.name
local pos = obj:get_pos()
local used, texture = false
if entname == "mobs_mc:sheep" then
minetest.add_item(pos, entity.drops[2].name .. " " .. math.random(1, 3))
if not entity.color then
entity.color = "unicolor_white"
end
entity.base_texture = { "blank.png", "mobs_mc_sheep.png" }
texture = entity.base_texture
entity.drops = {
{ name = mobs_mc.items.mutton_raw, chance = 1, min = 1, max = 2 },
}
used = true
elseif entname == "mobs_mc:snowman" then
texture = {
"mobs_mc_snowman.png",
"blank.png", "blank.png",
"blank.png", "blank.png",
"blank.png", "blank.png",
}
used = true
elseif entname == "mobs_mc:mooshroom" then
local droppos = vector.offset(pos, 0, 1.4, 0)
if entity.base_texture[1] == "mobs_mc_mooshroom_brown.png" then
minetest.add_item(droppos, mobs_mc.items.mushroom_brown .. " 5")
else
minetest.add_item(droppos, mobs_mc.items.mushroom_red .. " 5")
end
obj = mcl_util.replace_mob(obj, "mobs_mc:cow")
entity = obj:get_luaentity()
used = true
end
if used then
obj:set_properties({ textures = texture })
entity.gotten = true
minetest.sound_play("mcl_tools_shears_cut", { pos = pos }, true)
stack:add_wear(65535 / stackdef._mcl_diggroups.shearsy.uses)
inv:set_stack("main", stack_id, stack)
break
end
end
end
-- Spawn Egg -- Spawn Egg
elseif igroups.spawn_egg then elseif igroups.spawn_egg then
-- Spawn mob -- Spawn mob

View File

@ -53,15 +53,6 @@ local function get_consumed_materials(tool, material)
return materials_used return materials_used
end end
local function contains(table, value)
for _, i in pairs(table) do
if i == value then
return true
end
end
return false
end
-- Given 2 input stacks, tells you which is the tool and which is the material. -- Given 2 input stacks, tells you which is the tool and which is the material.
-- Returns ("tool", input1, input2) if input1 is tool and input2 is material. -- Returns ("tool", input1, input2) if input1 is tool and input2 is material.
-- Returns ("material", input2, input1) if input1 is material and input2 is tool. -- Returns ("material", input2, input1) if input1 is material and input2 is tool.
@ -69,15 +60,9 @@ end
local function distinguish_tool_and_material(input1, input2) local function distinguish_tool_and_material(input1, input2)
local def1 = input1:get_definition() local def1 = input1:get_definition()
local def2 = input2:get_definition() local def2 = input2:get_definition()
local r1 = def1._repair_material if def1.type == "tool" and def1._repair_material then
local r2 = def2._repair_material
if def1.type == "tool" and r1 and type(r1) == "table" and contains(r1, input2) then
return "tool", input1, input2 return "tool", input1, input2
elseif def2.type == "tool" and r2 and type(r2) == "table" and contains(r2, input1) then elseif def2.type == "tool" and def2._repair_material then
return "material", input2, input1
elseif def1.type == "tool" and r1 then
return "tool", input1, input2
elseif def2.type == "tool" and r2 then
return "material", input2, input1 return "material", input2, input1
else else
return nil return nil
@ -136,29 +121,12 @@ local function update_anvil_slots(meta)
local distinguished, tool, material = distinguish_tool_and_material(input1, input2) local distinguished, tool, material = distinguish_tool_and_material(input1, input2)
if distinguished then if distinguished then
local tooldef = tool:get_definition() local tooldef = tool:get_definition()
local repair = tooldef._repair_material
local has_correct_material = false local has_correct_material = false
local material_name = material:get_name() if string.sub(tooldef._repair_material, 1, 6) == "group:" then
if type(repair) == "string" then has_correct_material = minetest.get_item_group(material:get_name(), string.sub(tooldef._repair_material, 7)) ~= 0
if string.sub(repair, 1, 6) == "group:" then elseif material:get_name() == tooldef._repair_material then
has_correct_material = minetest.get_item_group(material_name, string.sub(repair, 7)) ~= 0
elseif material_name == repair then
has_correct_material = true has_correct_material = true
end end
else
if contains(repair, material_name) then
has_correct_material = true
else
for _, r in pairs(repair) do
if string.sub(r, 1, 6) == "group:" then
if minetest.get_item_group(material_name, string.sub(r, 7)) ~= 0 then
has_correct_material = true
end
end
end
end
end
if has_correct_material and tool:get_wear() > 0 then if has_correct_material and tool:get_wear() > 0 then
local materials_used = get_consumed_materials(tool, material) local materials_used = get_consumed_materials(tool, material)
local new_wear = calculate_repair(tool:get_wear(), MAX_WEAR, MATERIAL_TOOL_REPAIR_BOOST[materials_used]) local new_wear = calculate_repair(tool:get_wear(), MAX_WEAR, MATERIAL_TOOL_REPAIR_BOOST[materials_used])
@ -316,12 +284,6 @@ local function damage_anvil_by_falling(pos, distance)
end end
end end
local anvilbox = {
type = "fixed",
fixed = {
{ -8 / 16, -8 / 16, -6 / 16, 8 / 16, 8 / 16, 6 / 16 },
},
}
local anvildef = { local anvildef = {
groups = {pickaxey=1, falling_node=1, falling_node_damage=1, crush_after_fall=1, deco_block=1, anvil=1}, groups = {pickaxey=1, falling_node=1, falling_node_damage=1, crush_after_fall=1, deco_block=1, anvil=1},
tiles = {"mcl_anvils_anvil_top_damaged_0.png^[transformR90", "mcl_anvils_anvil_base.png", "mcl_anvils_anvil_side.png"}, tiles = {"mcl_anvils_anvil_top_damaged_0.png^[transformR90", "mcl_anvils_anvil_base.png", "mcl_anvils_anvil_side.png"},
@ -335,14 +297,11 @@ local anvildef = {
node_box = { node_box = {
type = "fixed", type = "fixed",
fixed = { fixed = {
{ -6 / 16, -8 / 16, -6 / 16, 6 / 16, -4 / 16, 6 / 16 }, {-8/16, 2/16, -5/16, 8/16, 8/16, 5/16}, -- top
{ -5 / 16, -4 / 16, -4 / 16, 5 / 16, -3 / 16, 4 / 16 }, {-5/16, -4/16, -2/16, 5/16, 5/16, 2/16}, -- middle
{ -4 / 16, -3 / 16, -2 / 16, 4 / 16, 2 / 16, 2 / 16 }, {-8/16, -8/16, -5/16, 8/16, -4/16, 5/16}, -- base
{ -8 / 16, 2 / 16, -5 / 16, 8 / 16, 8 / 16, 5 / 16 },
} }
}, },
selection_box = anvilbox,
collision_box = anvilbox,
sounds = mcl_sounds.node_sound_metal_defaults(), sounds = mcl_sounds.node_sound_metal_defaults(),
_mcl_blast_resistance = 1200, _mcl_blast_resistance = 1200,
_mcl_hardness = 5, _mcl_hardness = 5,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 220 B

View File

@ -1,5 +1,5 @@
name = mcl_armor name = mcl_armor
author = stu author = stu
description = Adds craftable armor that is visible to other players. description = Adds craftable armor that is visible to other players.
depends = mcl_core, mcl_player, mcl_enchanting, mcl_damage depends = mcl_core, mcl_player, mcl_enchanting
optional_depends = mcl_fire, ethereal, bakedclay optional_depends = mcl_fire, ethereal, bakedclay

Binary file not shown.

Before

Width:  |  Height:  |  Size: 409 B

After

Width:  |  Height:  |  Size: 167 B

View File

@ -119,7 +119,8 @@ local patterns = {
name = N("@1 Thing Charge"), name = N("@1 Thing Charge"),
type = "shapeless", type = "shapeless",
{ e, "mcl_core:apple_gold_enchanted", d }, -- TODO: Replace with enchanted golden apple
{ e, "mcl_core:apple_gold", d },
}, },
["rhombus"] = { ["rhombus"] = {
name = N("@1 Lozenge"), name = N("@1 Lozenge"),

View File

@ -12,7 +12,15 @@ Authors of media (textures)
BlockMen (CC BY-SA 3.0) BlockMen (CC BY-SA 3.0)
This mod adds a bed to Minetest which allows to skip the night. This mod adds a bed to Minetest which allows to skip the night.
To sleep, rightclick the bed. To sleep, rightclick the bed. If playing in singleplayer mode the night gets skipped
Another feature is a controlled respawning. If you have slept in bed your respawn point is set to the beds location and you will respawn there after immediately. If playing multiplayer you get shown how many other players are in bed too,
if all players are sleeping the night gets skipped. The night skip can be forced if more
than 50% of the players are lying in bed and use this option.
Another feature is a controlled respawning. If you have slept in bed (not just lying in
it) your respawn point is set to the beds location and you will respawn there after
death. death.
Use the mcl_playersSleepingPercentage setting to enable/disable night skipping or set a percentage of how many players need to sleep to skip the night. You can disable the respawn at beds by setting "enable_bed_respawn = false" in
minetest.conf.
You can disable the night skip feature by setting "enable_bed_night_skip = false" in
minetest.conf or by using the /set command in-game.

View File

@ -14,34 +14,39 @@ local worlds_mod = minetest.get_modpath("mcl_worlds")
local function get_look_yaw(pos) local function get_look_yaw(pos)
local n = minetest.get_node(pos) local n = minetest.get_node(pos)
local param = n.param2 if n.param2 == 1 then
if param == 1 then return math.pi / 2, n.param2
return math.pi / 2, param elseif n.param2 == 3 then
elseif param == 3 then return -math.pi / 2, n.param2
return -math.pi / 2, param elseif n.param2 == 0 then
elseif param == 0 then return math.pi, n.param2
return math.pi, param
else else
return 0, param return 0, n.param2
end end
end end
local function players_in_bed_setting()
return tonumber(minetest.settings:get("mcl_playersSleepingPercentage")) or 100
end
local function is_night_skip_enabled() local function is_night_skip_enabled()
return players_in_bed_setting() <= 100 local enable_night_skip = minetest.settings:get_bool("enable_bed_night_skip")
if enable_night_skip == nil then
enable_night_skip = true
end
return enable_night_skip
end end
local function check_in_beds(players) local function check_in_beds(players)
local in_bed = mcl_beds.player
if not players then if not players then
players = minetest.get_connected_players() players = minetest.get_connected_players()
end end
if player_in_bed <= 0 then
for n, player in pairs(players) do
local name = player:get_player_name()
if not in_bed[name] then
return false return false
end end
return players_in_bed_setting() <= (player_in_bed * 100) / #players end
return #players > 0
end end
-- These monsters do not prevent sleep -- These monsters do not prevent sleep
@ -193,7 +198,7 @@ end
local function update_formspecs(finished, ges) local function update_formspecs(finished, ges)
local ges = ges or #minetest.get_connected_players() local ges = ges or #minetest.get_connected_players()
local form_n = "size[12,5;true]" local form_n = "size[12,5;true]"
local all_in_bed = players_in_bed_setting() <= (player_in_bed * 100) / ges local all_in_bed = ges == player_in_bed
local night_skip = is_night_skip_enabled() local night_skip = is_night_skip_enabled()
local button_leave = "button_exit[4,3;4,0.75;leave;"..F(S("Leave bed")).."]" local button_leave = "button_exit[4,3;4,0.75;leave;"..F(S("Leave bed")).."]"
local button_abort = "button_exit[4,3;4,0.75;leave;"..F(S("Abort sleep")).."]" local button_abort = "button_exit[4,3;4,0.75;leave;"..F(S("Abort sleep")).."]"
@ -216,13 +221,7 @@ local function update_formspecs(finished, ges)
form_n = form_n .. bg_sleep form_n = form_n .. bg_sleep
form_n = form_n .. button_abort form_n = form_n .. button_abort
else else
local comment = "You will fall asleep when " text = text .. "\n" .. S("You will fall asleep when all players are in bed.")
if players_in_bed_setting() == 100 then
comment = S(comment .. "all players are in bed.")
else
comment = S(comment .. "@1% of all players are in bed.", players_in_bed_setting())
end
text = text .. "\n" .. comment
form_n = form_n .. bg_presleep form_n = form_n .. bg_presleep
form_n = form_n .. button_leave form_n = form_n .. button_leave
end end
@ -331,7 +330,7 @@ function mcl_beds.on_rightclick(pos, player, is_top)
message = select(2, lay_down(player, ppos, other)) message = select(2, lay_down(player, ppos, other))
end end
if message then if message then
mcl_title.set(player, "actionbar", {text=message, color="white", stay=60}) mcl_tmp_message.message(player, message)
end end
else else
lay_down(player, nil, nil, false) lay_down(player, nil, nil, false)
@ -350,6 +349,7 @@ function mcl_beds.on_rightclick(pos, player, is_top)
end end
end end
-- Callbacks -- Callbacks
minetest.register_on_joinplayer(function(player) minetest.register_on_joinplayer(function(player)
local meta = player:get_meta() local meta = player:get_meta()

View File

@ -37,6 +37,5 @@ Players in bed: @1/@2=Spieler im Bett: @1/@2
Note: Night skip is disabled.=Anmerkung: Überspringen der Nacht deaktiviert. Note: Night skip is disabled.=Anmerkung: Überspringen der Nacht deaktiviert.
You're sleeping.=Sie schlafen. You're sleeping.=Sie schlafen.
You will fall asleep when all players are in bed.=Sie werden einschlafen, wenn alle Spieler im Bett sind. You will fall asleep when all players are in bed.=Sie werden einschlafen, wenn alle Spieler im Bett sind.
You will fall asleep when @1% of all players are in bed.=Sie werden einschlafen, wenn @1% der Spieler im Bett sind.
You're in bed.=Sie sind im Bett. You're in bed.=Sie sind im Bett.
Allows you to sleep=Zum Einschafen Allows you to sleep=Zum Einschafen

View File

@ -37,6 +37,5 @@ Players in bed: @1/@2=
Note: Night skip is disabled.= Note: Night skip is disabled.=
You're sleeping.= You're sleeping.=
You will fall asleep when all players are in bed.= You will fall asleep when all players are in bed.=
You will fall asleep when @1% of all players are in bed.=
You're in bed.= You're in bed.=
Allows you to sleep= Allows you to sleep=

View File

@ -43,7 +43,7 @@ S("An arrow fired from a bow has a regular damage of 1-9. At full charge, there'
S("Arrows might get stuck on solid blocks and can be retrieved again. They are also capable of pushing wooden buttons."), S("Arrows might get stuck on solid blocks and can be retrieved again. They are also capable of pushing wooden buttons."),
_doc_items_usagehelp = S("To use arrows as ammunition for a bow, just put them anywhere in your inventory, they will be used up automatically. To use arrows as ammunition for a dispenser, place them in the dispenser's inventory. To retrieve an arrow that sticks in a block, simply walk close to it."), _doc_items_usagehelp = S("To use arrows as ammunition for a bow, just put them anywhere in your inventory, they will be used up automatically. To use arrows as ammunition for a dispenser, place them in the dispenser's inventory. To retrieve an arrow that sticks in a block, simply walk close to it."),
inventory_image = "mcl_bows_arrow_inv.png", inventory_image = "mcl_bows_arrow_inv.png",
groups = { ammo=1, ammo_bow=1, ammo_bow_regular=1, ammo_crossbow=1 }, groups = { ammo=1, ammo_bow=1, ammo_bow_regular=1 },
_on_dispense = function(itemstack, dispenserpos, droppos, dropnode, dropdir) _on_dispense = function(itemstack, dispenserpos, droppos, dropnode, dropdir)
-- Shoot arrow -- Shoot arrow
local shootpos = vector.add(dispenserpos, vector.multiply(dropdir, 0.51)) local shootpos = vector.add(dispenserpos, vector.multiply(dropdir, 0.51))
@ -324,10 +324,8 @@ function ARROW_ENTITY.on_step(self, dtime)
end end
if not obj:is_player() then if not obj:is_player() then
mcl_burning.extinguish(self.object) mcl_burning.extinguish(self.object)
if self._piercing == 0 then
self.object:remove() self.object:remove()
end end
end
return return
end end
end end

View File

@ -1,454 +0,0 @@
local S = minetest.get_translator(minetest.get_current_modname())
mcl_bows_s = {}
-- local arrows = {
-- ["mcl_bows:arrow"] = "mcl_bows:arrow_entity",
-- }
local GRAVITY = 9.81
local BOW_DURABILITY = 385
-- Charging time in microseconds
local _BOW_CHARGE_TIME_HALF = 350000 -- bow level 1
local _BOW_CHARGE_TIME_FULL = 900000 -- bow level 2 (full charge)
local BOW_CHARGE_TIME_HALF = 350000 -- bow level 1
local BOW_CHARGE_TIME_FULL = 900000 -- bow level 2 (full charge)
-- Factor to multiply with player speed while player uses bow
-- This emulates the sneak speed.
local PLAYER_USE_CROSSBOW_SPEED = tonumber(minetest.settings:get("movement_speed_crouch")) / tonumber(minetest.settings:get("movement_speed_walk"))
-- TODO: Use Minecraft speed (ca. 53 m/s)
-- Currently nerfed because at full speed the arrow would easily get out of the range of the loaded map.
local BOW_MAX_SPEED = 68
local function play_load_sound(id, pos)
minetest.sound_play("mcl_bows_crossbow_drawback_"..id, {pos=pos, max_hear_distance=12}, true)
end
--[[ Store the charging state of each player.
keys: player name
value:
nil = not charging or player not existing
number: currently charging, the number is the time from minetest.get_us_time
in which the charging has started
]]
local bow_load = {}
-- Another player table, this one stores the wield index of the bow being charged
local bow_index = {}
function mcl_bows_s.shoot_arrow_crossbow(arrow_item, pos, dir, yaw, shooter, power, damage, is_critical, crossbow_stack, collectable)
local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, arrow_item.."_entity")
if power == nil then
power = BOW_MAX_SPEED --19
end
if damage == nil then
damage = 3
end
local knockback
if crossbow_stack then
local enchantments = mcl_enchanting.get_enchantments(crossbow_stack)
if enchantments.piercing then
obj:get_luaentity()._piercing = 1 * enchantments.piercing
else
obj:get_luaentity()._piercing = 0
end
end
obj:set_velocity({x=dir.x*power, y=dir.y*power, z=dir.z*power})
obj:set_acceleration({x=0, y=-GRAVITY, z=0})
obj:set_yaw(yaw-math.pi/2)
local le = obj:get_luaentity()
le._shooter = shooter
le._source_object = shooter
le._damage = damage
le._is_critical = is_critical
le._startpos = pos
le._knockback = knockback
le._collectable = collectable
minetest.sound_play("mcl_bows_crossbow_shoot", {pos=pos, max_hear_distance=16}, true)
if shooter and shooter:is_player() then
if obj:get_luaentity().player == "" then
obj:get_luaentity().player = shooter
end
obj:get_luaentity().node = shooter:get_inventory():get_stack("main", 1):get_name()
end
return obj
end
local function get_arrow(player)
local inv = player:get_inventory()
local arrow_stack, arrow_stack_id
for i=1, inv:get_size("main") do
local it = inv:get_stack("main", i)
if not it:is_empty() and minetest.get_item_group(it:get_name(), "ammo_crossbow") ~= 0 then
arrow_stack = it
arrow_stack_id = i
break
end
end
return arrow_stack, arrow_stack_id
end
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")
if not arrow_itemstring then
return false
end
local playerpos = player:get_pos()
local dir = player:get_look_dir()
local yaw = player:get_look_horizontal()
if has_multishot_enchantment then
mcl_bows_s.shoot_arrow_crossbow(arrow_itemstring, {x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, {x=dir.x, y=dir.y, z=dir.z + .2}, yaw, player, power, damage, is_critical, player:get_wielded_item(), false)
mcl_bows_s.shoot_arrow_crossbow(arrow_itemstring, {x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, {x=dir.x, y=dir.y, z=dir.z - .2}, yaw, player, power, damage, is_critical, player:get_wielded_item(), false)
mcl_bows_s.shoot_arrow_crossbow(arrow_itemstring, {x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, dir, yaw, player, power, damage, is_critical, player:get_wielded_item(), true)
else
mcl_bows_s.shoot_arrow_crossbow(arrow_itemstring, {x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, dir, yaw, player, power, damage, is_critical, player:get_wielded_item(), true)
end
return true
end
-- Bow item, uncharged state
minetest.register_tool("mcl_bows:crossbow", {
description = S("Corssbow"),
_tt_help = S("Launches arrows"),
_doc_items_longdesc = S("Bows are ranged weapons to shoot arrows at your foes.").."\n"..
S("The speed and damage of the arrow increases the longer you charge. The regular damage of the arrow is between 1 and 9. At full charge, there's also a 20% of a critical hit, dealing 10 damage instead."),
_doc_items_usagehelp = S("To use the bow, you first need to have at least one arrow anywhere in your inventory (unless in Creative Mode). Hold down the right mouse button to charge, release to shoot."),
_doc_items_durability = BOW_DURABILITY,
inventory_image = "mcl_bows_crossbow.png",
wield_scale = mcl_vars.tool_wield_scale,
stack_max = 1,
range = 4,
-- Trick to disable digging as well
on_use = function() return end,
on_place = function(itemstack, player, pointed_thing)
if pointed_thing and pointed_thing.type == "node" then
-- Call on_rightclick if the pointed node defines it
local node = minetest.get_node(pointed_thing.under)
if player and not player:get_player_control().sneak then
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, player, itemstack) or itemstack
end
end
end
itemstack:get_meta():set_string("active", "true")
return itemstack
end,
on_secondary_use = function(itemstack)
itemstack:get_meta():set_string("active", "true")
return itemstack
end,
groups = {weapon=1,weapon_ranged=1,crossbow=1,enchantability=1},
_mcl_uses = 326,
})
minetest.register_tool("mcl_bows:crossbow_loaded", {
description = S("Corssbow"),
_tt_help = S("Launches arrows"),
_doc_items_longdesc = S("Corssbow are ranged weapons to shoot arrows at your foes.").."\n"..
S("The speed and damage of the arrow increases the longer you charge. The regular damage of the arrow is between 1 and 9. At full charge, there's also a 20% of a critical hit, dealing 10 damage instead."),
_doc_items_usagehelp = S("To use the corssbow, you first need to have at least one arrow anywhere in your inventory (unless in Creative Mode). Hold down the right mouse button to charge, release to load an arrow into the chamber, then to shoot press left mouse."),
_doc_items_durability = BOW_DURABILITY,
inventory_image = "mcl_bows_crossbow_3.png",
wield_scale = mcl_vars.tool_wield_scale,
stack_max = 1,
range = 4,
-- Trick to disable digging as well
on_use = function() return end,
on_place = function(itemstack, player, pointed_thing)
if pointed_thing and pointed_thing.type == "node" then
-- Call on_rightclick if the pointed node defines it
local node = minetest.get_node(pointed_thing.under)
if player and not player:get_player_control().sneak then
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, player, itemstack) or itemstack
end
end
end
itemstack:get_meta():set_string("active", "true")
return itemstack
end,
on_secondary_use = function(itemstack)
itemstack:get_meta():set_string("active", "true")
return itemstack
end,
groups = {weapon=1,weapon_ranged=1,crossbow=1,enchantability=1},
_mcl_uses = 326,
})
-- Iterates through player inventory and resets all the bows in "charging" state back to their original stage
local function reset_bows(player)
local inv = player:get_inventory()
local list = inv:get_list("main")
for place, stack in pairs(list) do
if stack:get_name() == "mcl_bows:crossbow" or stack:get_name() == "mcl_bows:crossbow_enchanted" then
stack:get_meta():set_string("active", "")
elseif stack:get_name()=="mcl_bows:crossbow_0" or stack:get_name()=="mcl_bows:crossbow_1" or stack:get_name()=="mcl_bows:crossbow_2" then
stack:set_name("mcl_bows:crossbow")
stack:get_meta():set_string("active", "")
list[place] = stack
elseif stack:get_name()=="mcl_bows:crossbow_0_enchanted" or stack:get_name()=="mcl_bows:crossbow_1_enchanted" or stack:get_name()=="mcl_bows:crossbow_2_enchanted" then
stack:set_name("mcl_bows:crossbow_enchanted")
stack:get_meta():set_string("active", "")
list[place] = stack
end
end
inv:set_list("main", list)
end
-- Resets the bow charging state and player speed. To be used when the player is no longer charging the bow
local function reset_bow_state(player, also_reset_bows)
bow_load[player:get_player_name()] = nil
bow_index[player:get_player_name()] = nil
if minetest.get_modpath("playerphysics") then
playerphysics.remove_physics_factor(player, "speed", "mcl_bows:use_crossbow")
end
if also_reset_bows then
reset_bows(player)
end
end
-- Bow in charging state
for level=0, 2 do
minetest.register_tool("mcl_bows:crossbow_"..level, {
description = S("Crossbow"),
_doc_items_create_entry = false,
inventory_image = "mcl_bows_crossbow_"..level..".png",
wield_scale = mcl_vars.tool_wield_scale,
stack_max = 1,
range = 0, -- Pointing range to 0 to prevent punching with bow :D
groups = {not_in_creative_inventory=1, not_in_craft_guide=1, bow=1, enchantability=1},
-- Trick to disable digging as well
on_use = function() return end,
on_drop = function(itemstack, dropper, pos)
reset_bow_state(dropper)
itemstack:get_meta():set_string("active", "")
if mcl_enchanting.is_enchanted(itemstack:get_name()) then
itemstack:set_name("mcl_bows:crossbow_enchanted")
else
itemstack:set_name("mcl_bows:crossbow")
end
minetest.item_drop(itemstack, dropper, pos)
itemstack:take_item()
return itemstack
end,
-- Prevent accidental interaction with itemframes and other nodes
on_place = function(itemstack)
return itemstack
end,
_mcl_uses = 385,
})
end
controls.register_on_release(function(player, key, time)
if key~="RMB" then return end
--local inv = minetest.get_inventory({type="player", name=player:get_player_name()})
local wielditem = player:get_wielded_item()
if wielditem:get_name()=="mcl_bows:crossbow_2" and get_arrow(player) or wielditem:get_name()=="mcl_bows:crossbow_2" and minetest.is_creative_enabled(player:get_player_name()) or wielditem:get_name()=="mcl_bows:crossbow_2_enchanted" and get_arrow(player) or wielditem:get_name()=="mcl_bows:crossbow_2_enchanted" and minetest.is_creative_enabled(player:get_player_name()) then
local arrow_stack, arrow_stack_id = get_arrow(player)
local arrow_itemstring
if minetest.is_creative_enabled(player:get_player_name()) then
if arrow_stack then
arrow_itemstring = arrow_stack:get_name()
else
arrow_itemstring = "mcl_bows:arrow"
end
else
arrow_itemstring = arrow_stack:get_name()
arrow_stack:take_item()
player:get_inventory():set_stack("main", arrow_stack_id, arrow_stack)
end
wielditem:get_meta():set_string("arrow", arrow_itemstring)
if wielditem:get_name()=="mcl_bows:crossbow_2" then
wielditem:set_name("mcl_bows:crossbow_loaded")
else
wielditem:set_name("mcl_bows:crossbow_loaded_enchanted")
end
player:set_wielded_item(wielditem)
minetest.sound_play("mcl_bows_crossbow_load", {pos=player:get_pos(), max_hear_distance=16}, true)
else
reset_bow_state(player, true)
end
end)
controls.register_on_press(function(player, key, time)
if key~="LMB" then return end
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
local p_load = bow_load[player:get_player_name()]
local charge
-- Type sanity check
if type(p_load) == "number" then
charge = minetest.get_us_time() - p_load
else
-- In case something goes wrong ...
-- Just assume minimum charge.
charge = 0
minetest.log("warning", "[mcl_bows] Player "..player:get_player_name().." fires arrow with non-numeric bow_load!")
end
charge = math.max(math.min(charge, BOW_CHARGE_TIME_FULL), 0)
local charge_ratio = charge / BOW_CHARGE_TIME_FULL
charge_ratio = math.max(math.min(charge_ratio, 1), 0)
-- Calculate damage and speed
-- Fully charged
local is_critical = false
speed = BOW_MAX_SPEED
local r = math.random(1,5)
if r == 1 then
-- 20% chance for critical hit
damage = 10
is_critical = true
else
damage = 9
end
local has_shot = player_shoot_arrow(wielditem, player, speed, damage, is_critical)
if enchanted then
wielditem:set_name("mcl_bows:crossbow_enchanted")
else
wielditem:set_name("mcl_bows:crossbow")
end
if has_shot and not minetest.is_creative_enabled(player:get_player_name()) then
local durability = BOW_DURABILITY
local unbreaking = mcl_enchanting.get_enchantment(wielditem, "unbreaking")
local multishot = mcl_enchanting.get_enchantment(wielditem, "multishot")
if unbreaking > 0 then
durability = durability * (unbreaking + 1)
end
if multishot then
durability = durability / 3
end
wielditem:add_wear(65535/durability)
end
player:set_wielded_item(wielditem)
reset_bow_state(player, true)
end
end)
controls.register_on_hold(function(player, key, time)
local name = player:get_player_name()
local creative = minetest.is_creative_enabled(name)
if key ~= "RMB" then
return
end
--local inv = minetest.get_inventory({type="player", name=name})
local wielditem = player:get_wielded_item()
local enchantments = mcl_enchanting.get_enchantments(wielditem)
if enchantments.quick_charge then
BOW_CHARGE_TIME_HALF = _BOW_CHARGE_TIME_HALF - (enchantments.quick_charge * 0.13 * 1000000 * .5)
BOW_CHARGE_TIME_FULL = _BOW_CHARGE_TIME_FULL - (enchantments.quick_charge * 0.13 * 1000000)
else
BOW_CHARGE_TIME_HALF = _BOW_CHARGE_TIME_HALF
BOW_CHARGE_TIME_FULL = _BOW_CHARGE_TIME_FULL
end
if bow_load[name] == nil and (wielditem:get_name()=="mcl_bows:crossbow" or wielditem:get_name()=="mcl_bows:crossbow_enchanted") and wielditem:get_meta():get("active") and (creative or get_arrow(player)) then
local enchanted = mcl_enchanting.is_enchanted(wielditem:get_name())
if enchanted then
wielditem:set_name("mcl_bows:crossbow_0_enchanted")
play_load_sound(0, player:get_pos())
else
wielditem:set_name("mcl_bows:crossbow_0")
play_load_sound(0, player:get_pos())
end
player:set_wielded_item(wielditem)
if minetest.get_modpath("playerphysics") then
-- Slow player down when using bow
playerphysics.add_physics_factor(player, "speed", "mcl_bows:use_crossbow", PLAYER_USE_CROSSBOW_SPEED)
end
bow_load[name] = minetest.get_us_time()
bow_index[name] = player:get_wield_index()
else
if player:get_wield_index() == bow_index[name] then
if type(bow_load[name]) == "number" then
if wielditem:get_name() == "mcl_bows:crossbow_0" and minetest.get_us_time() - bow_load[name] >= BOW_CHARGE_TIME_HALF then
wielditem:set_name("mcl_bows:crossbow_1")
play_load_sound(1, player:get_pos())
elseif wielditem:get_name() == "mcl_bows:crossbow_0_enchanted" and minetest.get_us_time() - bow_load[name] >= BOW_CHARGE_TIME_HALF then
wielditem:set_name("mcl_bows:crossbow_1_enchanted")
play_load_sound(1, player:get_pos())
elseif wielditem:get_name() == "mcl_bows:crossbow_1" and minetest.get_us_time() - bow_load[name] >= BOW_CHARGE_TIME_FULL then
wielditem:set_name("mcl_bows:crossbow_2")
play_load_sound(2, player:get_pos())
elseif wielditem:get_name() == "mcl_bows:crossbow_1_enchanted" and minetest.get_us_time() - bow_load[name] >= BOW_CHARGE_TIME_FULL then
wielditem:set_name("mcl_bows:crossbow_2_enchanted")
play_load_sound(2, player:get_pos())
end
else
if wielditem:get_name() == "mcl_bows:crossbow_0" or wielditem:get_name() == "mcl_bows:crossbow_1" or wielditem:get_name() == "mcl_bows:crossbow_2" then
wielditem:set_name("mcl_bows:crossbow")
play_load_sound(1, player:get_pos())
elseif wielditem:get_name() == "mcl_bows:crossbow_0_enchanted" or wielditem:get_name() == "mcl_bows:crossbow_1_enchanted" or wielditem:get_name() == "mcl_bows:crossbow_2_enchanted" then
wielditem:set_name("mcl_bows:crossbow_enchanted")
play_load_sound(1, player:get_pos())
end
end
player:set_wielded_item(wielditem)
else
reset_bow_state(player, true)
end
end
end)
minetest.register_globalstep(function(dtime)
for _, player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
local wielditem = player:get_wielded_item()
local wieldindex = player:get_wield_index()
--local controls = player:get_player_control()
if type(bow_load[name]) == "number" and ((wielditem:get_name()~="mcl_bows:crossbow_0" and wielditem:get_name()~="mcl_bows:crossbow_1" and wielditem:get_name()~="mcl_bows:crossbow_2" and wielditem:get_name()~="mcl_bows:crossbow_0_enchanted" and wielditem:get_name()~="mcl_bows:crossbow_1_enchanted" and wielditem:get_name()~="mcl_bows:crossbow_2_enchanted") or wieldindex ~= bow_index[name]) then
reset_bow_state(player, true)
end
end
end)
minetest.register_on_joinplayer(function(player)
reset_bows(player)
end)
minetest.register_on_leaveplayer(function(player)
reset_bow_state(player, true)
end)
if minetest.get_modpath("mcl_core") and minetest.get_modpath("mcl_mobitems") then
minetest.register_craft({
output = "mcl_bows:crossbow",
recipe = {
{"mcl_core:stick", "mcl_core:iron_ingot", "mcl_core:stick"},
{"mcl_mobitems:string", "mcl_bows:arrow", "mcl_mobitems:string"},
{"", "mcl_core:stick", ""},
}
})
end
minetest.register_craft({
type = "fuel",
recipe = "group:bow",
burntime = 15,
})
-- Add entry aliases for the Help
if minetest.get_modpath("doc") then
doc.add_entry_alias("tools", "mcl_bows:crossbow", "tools", "mcl_bows:crossbow_0")
doc.add_entry_alias("tools", "mcl_bows:crossbow", "tools", "mcl_bows:crossbow_1")
doc.add_entry_alias("tools", "mcl_bows:crossbow", "tools", "mcl_bows:crossbow_2")
end

View File

@ -1,11 +1,5 @@
--Bow
dofile(minetest.get_modpath("mcl_bows") .. "/arrow.lua") dofile(minetest.get_modpath("mcl_bows") .. "/arrow.lua")
dofile(minetest.get_modpath("mcl_bows") .. "/bow.lua") dofile(minetest.get_modpath("mcl_bows") .. "/bow.lua")
dofile(minetest.get_modpath("mcl_bows") .. "/rocket.lua")
--Crossbow
dofile(minetest.get_modpath("mcl_bows") .. "/crossbow.lua")
--Compatiblility with older MineClone worlds
minetest.register_alias("mcl_throwing:bow", "mcl_bows:bow") minetest.register_alias("mcl_throwing:bow", "mcl_bows:bow")
minetest.register_alias("mcl_throwing:arrow", "mcl_bows:arrow") minetest.register_alias("mcl_throwing:arrow", "mcl_bows:arrow")

Some files were not shown because too many files have changed in this diff Show More