forked from VoxeLibre/VoxeLibre
Compare commits
8 Commits
master
...
projectile
Author | SHA1 | Date |
---|---|---|
teknomunk | 1d4784e26a | |
teknomunk | 92e4185032 | |
teknomunk | 667748b9b4 | |
teknomunk | dca1475685 | |
teknomunk | e4a13f2f5a | |
teknomunk | c7c0189259 | |
teknomunk | d1af7d04da | |
teknomunk | f46b99772b |
|
@ -13,17 +13,16 @@ labels:
|
||||||
Thanks for taking the time to fill out this bug report!
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
|
||||||
Please follow our contributing guidelines first:
|
Please follow our contributing guidelines first:
|
||||||
https://git.minetest.land/VoxeLibre/VoxeLibre/src/branch/master/CONTRIBUTING.md#rules-about-both-bugs-and-feature-requests
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CONTRIBUTING.md#rules-about-both-bugs-and-feature-requests
|
||||||
|
|
||||||
By submitting this issue, you agree to follow our Code of Conduct:
|
By submitting this issue, you agree to follow our Code of Conduct:
|
||||||
https://git.minetest.land/VoxeLibre/VoxeLibre/src/branch/master/CODE_OF_CONDUCT.md
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CODE_OF_CONDUCT.md
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
What version of VoxeLibre are you using? We do not provide support for outdated versions of VoxeLibre.
|
What version of VoxeLibre are you using? We do not provide support for outdated versions of VoxeLibre.
|
||||||
"/ver" command will output the version you're running.
|
|
||||||
Current latest version is listed here, at the top:
|
Current latest version is listed here, at the top:
|
||||||
https://git.minetest.land/VoxeLibre/VoxeLibre/tags
|
https://git.minetest.land/MineClone2/MineClone2/tags
|
||||||
-->
|
-->
|
||||||
VoxeLibre version:
|
VoxeLibre version:
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
name: "Feature request"
|
name: "Feature request"
|
||||||
about: "File a feature request"
|
about: "File a feature request not in Minecraft"
|
||||||
labels:
|
labels:
|
||||||
|
|
||||||
- "non-Minecraft feature"
|
- "non-Minecraft feature"
|
||||||
|
@ -10,17 +10,17 @@ labels:
|
||||||
---
|
---
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Got a new feature request? Explain to us why we should consider your idea.
|
Got a new non-Minecraft feature request? Explain to us why we should consider your idea.
|
||||||
|
|
||||||
Please follow our contributing guidelines first:
|
Please follow our contributing guidelines first:
|
||||||
https://git.minetest.land/VoxeLibre/VoxeLibre/src/branch/master/CONTRIBUTING.md#rules-about-both-bugs-and-feature-requests
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CONTRIBUTING.md#rules-about-both-bugs-and-feature-requests
|
||||||
|
|
||||||
By submitting this issue, you agree to follow our Code of Conduct:
|
By submitting this issue, you agree to follow our Code of Conduct:
|
||||||
https://git.minetest.land/VoxeLibre/VoxeLibre/src/branch/master/CODE_OF_CONDUCT.md
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CODE_OF_CONDUCT.md
|
||||||
-->
|
-->
|
||||||
|
|
||||||
### Feature
|
### Feature
|
||||||
Tell us about your requested feature!
|
Tell us about your requested feature not in Minecraft!
|
||||||
|
|
||||||
### Why
|
### Why
|
||||||
Tell us why should we implement it!
|
Tell us why should we implement it!
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
name: "Missing Feature request"
|
||||||
|
about: "File a missing feature request in Minecraft but not in MineClone2"
|
||||||
|
labels:
|
||||||
|
|
||||||
|
- "missing feature"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Thanks for taking the time to fill out this missing feature request!
|
||||||
|
|
||||||
|
Please follow our contributing guidelines first:
|
||||||
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CONTRIBUTING.md#rules-about-both-bugs-and-feature-requests
|
||||||
|
|
||||||
|
By submitting this issue, you agree to follow our Code of Conduct:
|
||||||
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CODE_OF_CONDUCT.md
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Current feature in Minecraft
|
||||||
|
Tell us about the feature currently in Minecraft! What is it like on Minecraft?
|
||||||
|
|
||||||
|
### Current feature in VoxeLibre
|
||||||
|
Tell us about the feature currently in VoxeLibre! What is different?
|
|
@ -8,13 +8,13 @@ labels:
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Please follow our contributing guidelines first:
|
Please follow our contributing guidelines first:
|
||||||
https://git.minetest.land/VoxeLibre/VoxeLibre/src/branch/master/CONTRIBUTING.md#how-you-can-help-as-a-programmer
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CONTRIBUTING.md#how-you-can-help-as-a-programmer
|
||||||
|
|
||||||
By submitting this pull request, you agree to follow our Code of Conduct:
|
By submitting this pull request, you agree to follow our Code of Conduct:
|
||||||
https://git.minetest.land/VoxeLibre/VoxeLibre/src/branch/master/CODE_OF_CONDUCT.md
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CODE_OF_CONDUCT.md
|
||||||
-->
|
-->
|
||||||
|
|
||||||
Tell us about your pull request! Reference related issues, if necessary.
|
Tell us about your pull request! Reference related issues, if necessary
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
Tell us how to test your changes!
|
Tell us how to test your changes!
|
||||||
|
|
20
CREDITS.md
20
CREDITS.md
|
@ -3,7 +3,7 @@
|
||||||
## Creator of MineClone
|
## Creator of MineClone
|
||||||
* davedevils
|
* davedevils
|
||||||
|
|
||||||
## Creator of VoxeLibre
|
## Creator of MineClone2
|
||||||
* Wuzzy
|
* Wuzzy
|
||||||
|
|
||||||
## Maintainers
|
## Maintainers
|
||||||
|
@ -20,10 +20,10 @@
|
||||||
* epCode
|
* epCode
|
||||||
* chmodsayshello
|
* chmodsayshello
|
||||||
* MrRar
|
* MrRar
|
||||||
|
* FossFanatic
|
||||||
* SmokeyDope
|
* SmokeyDope
|
||||||
* Faerraven / Michieal
|
* Faerraven / Michieal
|
||||||
* rudzik8
|
* Codiac
|
||||||
* teknomunk
|
|
||||||
|
|
||||||
## Past Developers
|
## Past Developers
|
||||||
* jordan4ibanez
|
* jordan4ibanez
|
||||||
|
@ -34,11 +34,10 @@
|
||||||
* NO11
|
* NO11
|
||||||
* SumianVoice
|
* SumianVoice
|
||||||
* PrairieWind
|
* PrairieWind
|
||||||
* FossFanatic
|
|
||||||
* Codiac
|
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
* RandomLegoBrick
|
* RandomLegoBrick
|
||||||
|
* rudzik8
|
||||||
* Code-Sploit
|
* Code-Sploit
|
||||||
* aligator
|
* aligator
|
||||||
* Rootyjr
|
* Rootyjr
|
||||||
|
@ -130,17 +129,6 @@
|
||||||
* Bakawun
|
* Bakawun
|
||||||
* JoseDouglas26
|
* JoseDouglas26
|
||||||
* Zasco
|
* Zasco
|
||||||
* PrWalterB
|
|
||||||
* michaljmalinowski
|
|
||||||
* nixnoxus
|
|
||||||
* Potiron
|
|
||||||
* Tuxilio
|
|
||||||
* Impulse
|
|
||||||
* Doods
|
|
||||||
* SOS-Games
|
|
||||||
* Bram
|
|
||||||
* qoheniac
|
|
||||||
* WillConker
|
|
||||||
|
|
||||||
## Music
|
## Music
|
||||||
* Jordach for the jukebox music compilation from Big Freaking Dig
|
* Jordach for the jukebox music compilation from Big Freaking Dig
|
||||||
|
|
|
@ -64,12 +64,9 @@ Use the `/giveme` chat command to obtain them. See the in-game help for
|
||||||
an explanation.
|
an explanation.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
To run the game with the best performance and support, we recommend the latest
|
This game requires [Minetest](http://minetest.net) to run (version 5.4.1 or
|
||||||
stable version of [Minetest](http://minetest.net), be we always make an effort
|
later). So you need to install Minetest first. Only stable versions of Minetest
|
||||||
to support one version behind the latest stable version. In some cases, older
|
are officially supported.
|
||||||
versions might still be good enough but you would be missing out on important
|
|
||||||
Minetest features that enable important features for our game.
|
|
||||||
|
|
||||||
There is no support for running VoxeLibre in development versions of Minetest.
|
There is no support for running VoxeLibre in development versions of Minetest.
|
||||||
|
|
||||||
To install VoxeLibre (if you haven't already), move this directory into the
|
To install VoxeLibre (if you haven't already), move this directory into the
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# VoxeLibre
|
# VoxeLibre
|
||||||
Un jeu inspiré de Minecraft pour Minetest. Forké depuis Mineclone par davedevils.
|
Un jeu non-officiel similaire à Minecraft pour Minetest. Forké depuis Mineclone par davedevils. Développé par de nombreuses personnes. Pas développé ni supporté par Mojang AB.
|
||||||
Développé par de nombreuses personnes, voir CREDITS.md pour une liste complète.
|
|
||||||
|
|
||||||
### Gameplay
|
### Gameplay
|
||||||
Vous atterissez dans un monde fait entièrement de cubes et généré aléatoirement. Vous pouvez explorer le monde, miner et construire presque n'importe quel bloc pour créer de nouvelles structures. Vous pouvez choisir de jouer en "mode survie" dans lequel vous devez combattre des monstres et la faim et progresser lentement dans différents aspects du jeu, comme l'extraction de minerai, l'agriculture, la construction de machines et ainsi de suite. Ou alors vous pouvez jouer en "mode créatif" où vous pouvez construire à peu près n'importe quoi instantanément.
|
Vous atterissez dans un monde fait entièrement de cubes et généré aléatoirement. Vous pouvez explorer le monde, miner et construire presque n'importe quel bloc pour créer de nouvelles structures. Vous pouvez choisir de jouer en "mode survie" dans lequel vous devez combattre des monstres et la faim et progresser lentement dans différents aspects du jeu, comme l'extraction de minerai, l'agriculture, la construction de machines et ainsi de suite. Ou alors vous pouvez jouer en "mode créatif" où vous pouvez construire à peu près n'importe quoi instantanément.
|
||||||
|
@ -22,7 +21,7 @@ Vous atterissez dans un monde fait entièrement de cubes et généré aléatoire
|
||||||
### Commencer
|
### Commencer
|
||||||
* **Frappez un arbre** jusqu'à ce qu'il casse et donne du bois
|
* **Frappez un arbre** jusqu'à ce qu'il casse et donne du bois
|
||||||
* Placez le **bois dans la grille 2x2** (la "grille de fabrication" de votre menu d'inventaire) et fabriquez 4 planches de bois
|
* Placez le **bois dans la grille 2x2** (la "grille de fabrication" de votre menu d'inventaire) et fabriquez 4 planches de bois
|
||||||
* Placez les 4 planches de bois dans la grille 2x2 et **fabriquez un établi**
|
* Placer les 4 planches de bois dans la grille 2x2 et **fabriquez un établi**
|
||||||
* **Faites un clic droit sur l'établi** (icone livre) pour apprendre toutes les recettes possibles
|
* **Faites un clic droit sur l'établi** (icone livre) pour apprendre toutes les recettes possibles
|
||||||
* **Fabriquez une pioche de bois** pour miner la pierre
|
* **Fabriquez une pioche de bois** pour miner la pierre
|
||||||
* Différents outils minent différentes sortes de blocs. Essayez-les !
|
* Différents outils minent différentes sortes de blocs. Essayez-les !
|
||||||
|
@ -31,10 +30,10 @@ Vous atterissez dans un monde fait entièrement de cubes et généré aléatoire
|
||||||
### Agriculture
|
### Agriculture
|
||||||
* Trouvez des graines
|
* Trouvez des graines
|
||||||
* Fabriquez une houe
|
* Fabriquez une houe
|
||||||
* Faites un clic droit sur la terre ou un bloc similaire avec la houe pour créer des terres agricoles
|
* Faites un clic droit sur la terre ou des blocs similaires avec la houe pour créer des terres agricoles
|
||||||
* Placez des graines sur des terres agricoles et regardez les pousser
|
* Placer des graines sur des terres agricoles et regardez les pousser
|
||||||
* Récoltez les plantes une fois matûres
|
* Récoltez les plantes une fois matûres
|
||||||
* Les terres agricoles proches de l'eau deviennent humides et accélèrent la croissance
|
* Les terres agricoles proche de l'eau deviennent humides et accélèrent la croissance
|
||||||
|
|
||||||
### Four
|
### Four
|
||||||
* Fabriquez un four
|
* Fabriquez un four
|
||||||
|
@ -62,28 +61,26 @@ Pour installer VoxeLibre (si ce n'est pas déjà fait), déplacez ce dossier dan
|
||||||
## Liens utiles
|
## Liens utiles
|
||||||
Le dépôt de VoxeLibre est hébergé sur Mesehub. Pour contribuer ou signaler des problèmes, allez là-bas.
|
Le dépôt de VoxeLibre est hébergé sur Mesehub. Pour contribuer ou signaler des problèmes, allez là-bas.
|
||||||
|
|
||||||
* Mesehub : <https://git.minetest.land/VoxeLibre/VoxeLibre>
|
* Mesehub : <https://git.minetest.land/MineClone2/MineClone2>
|
||||||
* Discord : <https://discord.gg/xE4z8EEpDC>
|
* Discord : <https://discord.gg/xE4z8EEpDC>
|
||||||
* YouTube : <https://www.youtube.com/channel/UClI_YcsXMF3KNeJtoBfnk9A>
|
* 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/>
|
||||||
|
* Forums Minetest : <https://forum.minetest.net/viewtopic.php?f=50&t=16407>
|
||||||
* ContentDB : <https://content.minetest.net/packages/wuzzy/mineclone2/>
|
* ContentDB : <https://content.minetest.net/packages/wuzzy/mineclone2/>
|
||||||
* OpenCollective : <https://opencollective.com/mineclone2>
|
* OpenCollective : <https://opencollective.com/mineclone2>
|
||||||
* Mastodon : <https://fosstodon.org/@VoxeLibre>
|
|
||||||
* Lemmy : <https://lemm.ee/c/voxelibre>
|
|
||||||
* Espace Matrix : <https://app.element.io/#/room/#voxelibre:matrix.org>
|
|
||||||
* Forums Minetest : <https://forum.minetest.net/viewtopic.php?f=50&t=16407>
|
|
||||||
* Reddit : <https://www.reddit.com/r/VoxeLibre/>
|
|
||||||
* IRC (peu utilisé) : <https://web.libera.chat/#mineclone2>
|
|
||||||
|
|
||||||
## Objectif
|
## Objectif
|
||||||
* Créer un jeu stable, performant, moddable et libre inspiré de Minecraft en utilisant le moteur de jeu Minetest, utilisable à la fois en mode solo et multijoueur.
|
* Essentiellement, créer un clone de Minecraft stable, moddable, libre et gratuit basé sur le moteur de jeu Minetest avec des fonctionnalités abouties, utilisable à la fois en mode solo et multijoueur. Actuellement, beaucoup des fonctionnalités de **Minecraft Java Edition** sont déjà implémentées et leur amélioration est prioritaire sur les nouvelles demandes.
|
||||||
* Actuellement, un grand nombre de fonctionnalités sont déjà implémentées.
|
* Avec une priorité moindre, implémenter les fonctionnalités des versions **Minecraft + OptiFine** (OptiFine autant que supporté par le moteur Minetest). Cela signifie que les fonctionnalités présentes dans les versions listées sont priorisées.
|
||||||
L'amélioration des fonctionnalités existantes est toujours la bienvenue.
|
* Dans l'idéal, créer une expérience performante qui tourne bien sur des ordinateurs à basse performance. Malheureusement, en raison des mécanismes de Minecraft et des limitations du moteur Minetest ainsi que de la petite taille de la communauté de joueurs sur des ordinateurs à basses performances, les optimisations sont difficiles à explorer.
|
||||||
|
|
||||||
## Statut de complétion
|
## Statut de complétion
|
||||||
Ce jeu est actuellement au stade **beta**.
|
Ce jeu est actuellement au stade **beta**.
|
||||||
Il est jouable mais incomplet en fonctionnalités.
|
Il est jouable mais incomplet en fonctionnalités.
|
||||||
La rétro-compatibilité n'est pas entièrement garantie, mettre votre monde à jour peut causer de petits bugs.
|
La rétro-compatibilité n'est pas entièrement garantie, mettre votre monde à jour peut causer de petits bugs.
|
||||||
Si vous voulez utiliser la version de développement de VoxeLibre en production, la branche master est habituellement relativement stable.
|
Si vous voulez utiliser la version de développement de VoxeLibre en production, la branche master est habituellement relativement stable. Les branches de test fusionnent souvent des pull requests expérimentales et doivent être considérées comme moins stable.
|
||||||
|
|
||||||
Les principales fonctionnalités suivantes sont disponibles :
|
Les principales fonctionnalités suivantes sont disponibles :
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
This file is severely out of date. If you can help updating this translation, please reach out to us (contact in README.md - the English version).
|
# MineClone 2
|
||||||
|
|
||||||
# VoxeLibre
|
|
||||||
一個非官方的Minetest遊戲,遊玩方式和Minecraft類似。由davedevils從MineClone分拆。
|
一個非官方的Minetest遊戲,遊玩方式和Minecraft類似。由davedevils從MineClone分拆。
|
||||||
由許多人開發。並非由Mojang Studios開發。<!-- "Mojang AB"'s Name changed at 2020/05, main README should change too -->
|
由許多人開發。並非由Mojang Studios開發。<!-- "Mojang AB"'s Name changed at 2020/05, main README should change too -->
|
||||||
|
|
||||||
|
|
114
RELEASE.md
114
RELEASE.md
|
@ -28,16 +28,16 @@ git commit -m "Updated release credits and set version for v0.87"
|
||||||
git add releasenotes/0_87-the_prismatic_release.md
|
git add releasenotes/0_87-the_prismatic_release.md
|
||||||
git commit -m "Add release notes for v0.87"
|
git commit -m "Add release notes for v0.87"
|
||||||
```
|
```
|
||||||
7. **Tag and push to the tag:**
|
5. **Tag and push to the tag:**
|
||||||
```
|
```
|
||||||
git tag 0.87.0
|
git tag 0.87.0
|
||||||
git push origin 0.87.0
|
git push origin 0.87.0
|
||||||
```
|
```
|
||||||
8. Update version in game.conf to the next version with -SNAPSHOT suffix:
|
6. Update version in game.conf to the next version with -SNAPSHOT suffix:
|
||||||
```
|
```
|
||||||
git commit -m "Post-release set version 0.88.0-SNAPSHOT"
|
git commit -m "Post-release set version 0.87.0-SNAPSHOT"
|
||||||
```
|
```
|
||||||
9. Push the above to a new branch, and make the release PR. Merge to finalize release process.
|
7. Push the above to a new branch, and make the release PR. Merge to finalize release process.
|
||||||
|
|
||||||
### Release via ContentDB
|
### Release via ContentDB
|
||||||
|
|
||||||
|
@ -68,100 +68,46 @@ git commit -m "Post-release set version 0.88.0-SNAPSHOT"
|
||||||
|
|
||||||
## Hotfix Release
|
## Hotfix Release
|
||||||
|
|
||||||
### Before releasing
|
The below is not up-to-date. At the next hotfix the process should be finalized and updated.
|
||||||
|
|
||||||
First, determine if the current state of the master branch is fine for the Hotfix.
|
|
||||||
In general, Hotfixes shouldn't contain new features to minimize the risk of regressions.
|
|
||||||
|
|
||||||
* If it hasn't been long since the release, and the only PRs merged so far are bugfixes and/or documentation changes,
|
|
||||||
it is certainly fine to use it as a base for the release.
|
|
||||||
* If there are some features merged, but they are aimed at fixing/alleviating important issues with the last released version, it may still be fine.
|
|
||||||
* If there are some simple QoL features merged that are irrelevant to the last release, it may still be fine to use it as a base for the Hotfix.
|
|
||||||
* If there are major features or large overhauls merged, it *most probably* is **not** fine to use as a base for the Hotfix.
|
|
||||||
|
|
||||||
If you decided that the current state of the master branch can be used as the Hotfix version, make sure that all the PRs merged since the last release
|
|
||||||
are in the Hotfix milestone and you are working on a clean branch based on the master branch, up-to-date with the one on the repo.
|
|
||||||
In this case, **skip** the following section.
|
|
||||||
|
|
||||||
### Prepare release branch
|
### Prepare release branch
|
||||||
|
|
||||||
If you decided that the current state of the master branch shouldn't be used as the Hotfix version, you must prepare a release branch.
|
When hotfixing, you should never release new features. Any new code increases risk of new bugs which has additional testing/release concerns.
|
||||||
|
To mitigate this, you just release the last release, and the relevant bug fix. For this, we do the following:
|
||||||
|
|
||||||
|
* Create release branch from the last release tag, push it:
|
||||||
|
|
||||||
1. Create release branch from the last release tag, push it:
|
|
||||||
```
|
```
|
||||||
git checkout -b release/0.82.1 0.82.0
|
git checkout -b release/0.82.1 0.82.0
|
||||||
|
|
||||||
git push origin release/0.82.1
|
git push origin release/0.82.1
|
||||||
```
|
```
|
||||||
2. Cherry-pick the relevant commits from the master branch, or merge them from other (PR) branches.
|
|
||||||
3. Make sure your local copy of the branch contains all the relevant changes, **do not rebase**.
|
|
||||||
|
|
||||||
### Release process
|
#### Prepare feature branch and fix
|
||||||
|
|
||||||
1. Update CREDITS.md if it is needed
|
* Create feature branch from that release branch (can review it to check only fix is there, nothing else, and use to also merge into master separately)
|
||||||
2. Update version in game.conf
|
|
||||||
3. If you've changed CREDITS.md, run the script:
|
`git checkout -b hotfix_bug_1_branch`
|
||||||
```
|
|
||||||
lua tools/generate_ingame_credits.lua
|
* Fix crash/serious bug and commit
|
||||||
```
|
* Push branch and create pr to the release and also the master branch (Do not rebase, to reduce merge conflict risk. Do not delete after first merge or it needs to be repushed)
|
||||||
4. Make a commit for the above:
|
|
||||||
```
|
#### Update version and tag the release
|
||||||
git add game.conf
|
|
||||||
git commit -m "Set version for hotfix v0.87.1"
|
* After all fixes are in release branch, pull it locally (best to avoid a merge conflict as feature branch will need to be merged into master also, which already changed version):
|
||||||
```
|
|
||||||
or, if credits got updated:
|
* Update version in game.conf to hotfix version and commit it. Example: version=0.82.1
|
||||||
```
|
|
||||||
git add CREDITS.md
|
* Tag it, push tag and branch:
|
||||||
git add mods/HUD/mcl_credits/people.lua
|
|
||||||
git add game.conf
|
|
||||||
git commit -m "Updated release credits and set version for hotfix v0.87.1"
|
|
||||||
```
|
|
||||||
5. Add a section in the last releasnotes, like this:
|
|
||||||
```
|
|
||||||
## 0.87.1 hotfix
|
|
||||||
```
|
|
||||||
and describe the changes there
|
|
||||||
|
|
||||||
6. Make a commit for the releasenotes changes:
|
|
||||||
```
|
|
||||||
git add releasenotes/0_87-the_prismatic_release.md
|
|
||||||
git commit -m "Update release notes for hotfix v0.87.1"
|
|
||||||
```
|
|
||||||
7. **Tag and push to the tag:**
|
|
||||||
```
|
|
||||||
git tag 0.87.1
|
|
||||||
git push origin 0.87.1
|
|
||||||
```
|
|
||||||
8. If you are skipping some changes from the master branch (and thus are using a prepared master branch from the previous section),
|
|
||||||
push to the remote and skip the next two steps:
|
|
||||||
```
|
```
|
||||||
|
git tag 0.82.1
|
||||||
|
|
||||||
|
git push origin 0.82.1
|
||||||
|
|
||||||
git push origin release/0.82.1
|
git push origin release/0.82.1
|
||||||
```
|
```
|
||||||
9. If you're releasing master branch, update version in game.conf to the next version with -SNAPSHOT suffix:
|
|
||||||
```
|
|
||||||
git commit -m "Post-hotfix reset version 0.88.0-SNAPSHOT"
|
|
||||||
```
|
|
||||||
10. If you're releasing master branch, push the above to a new branch, and make the release PR. Merge to finalize release process.
|
|
||||||
|
|
||||||
### Release via ContentDB
|
Note: If you have to do more than 1 hotfix release, can do it on the same release branch.
|
||||||
|
|
||||||
1. Go to VoxeLibre page (https://content.minetest.net/packages/Wuzzy/mineclone2/)
|
|
||||||
2. Click [+Release] button
|
|
||||||
3. Enter the release tag number in the title and Git reference box. For example (without quotes): "0.87.1"
|
|
||||||
4. In the minimum minetest version, put the oldest supported version (as of 19/05/2024 it is 5.6), leave the Maximum minetest version blank
|
|
||||||
5. Click save. Hotfix is now live.
|
|
||||||
|
|
||||||
### After releasing
|
|
||||||
|
|
||||||
...inform people.
|
|
||||||
|
|
||||||
* Add a comment to the forum post with the release number and change log. Maintainer will update the main post with code link.
|
|
||||||
* Add a Discord announcement post and @everyone with link to the release issue and release notes, and describe briefly what the hotfix does.
|
|
||||||
* Add a Matrix announcement post and @room with content like above.
|
|
||||||
* Share the news on reddit + Lemmy. Good subs to share with:
|
|
||||||
* r/linux_gaming
|
|
||||||
* r/opensourcegames
|
|
||||||
* r/opensource
|
|
||||||
* r/freesoftware
|
|
||||||
* r/linuxmasterrace
|
|
||||||
* r/VoxeLibre
|
|
||||||
* r/MineClone2 (*for now*)
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
title = VoxeLibre
|
title = VoxeLibre
|
||||||
description = A survival sandbox game. Survive, gather, hunt, build, explore, and do much more.
|
description = A survival sandbox game. Survive, gather, hunt, build, explore, and do much more.
|
||||||
disallowed_mapgens = v6
|
disallowed_mapgens = v6
|
||||||
version=0.88.0-SNAPSHOT
|
version=0.87.0-SNAPSHOT
|
||||||
|
|
|
@ -94,16 +94,5 @@ function minetest.check_single_for_falling(pos)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if get_item_group(node.name, "supported_node_facedir") ~= 0 then
|
|
||||||
local dir = facedir_to_dir(node.param2)
|
|
||||||
if dir then
|
|
||||||
local def = registered_nodes[get_node(vector.add(pos, dir)).name]
|
|
||||||
if def and def.drawtype == "airlike" then
|
|
||||||
drop_attached_node(pos)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
mcl_util = {}
|
mcl_util = {}
|
||||||
|
|
||||||
dofile(minetest.get_modpath(minetest.get_current_modname()).."/roman_numerals.lua")
|
local modname = minetest.get_current_modname()
|
||||||
|
local modpath = minetest.get_modpath(modname)
|
||||||
|
dofile(modpath.."/roman_numerals.lua")
|
||||||
|
dofile(modpath.."/nodes.lua")
|
||||||
|
|
||||||
-- Updates all values in t using values from to*.
|
-- Updates all values in t using values from to*.
|
||||||
function table.update(t, ...)
|
function table.update(t, ...)
|
||||||
|
@ -112,24 +115,6 @@ function mcl_util.validate_vector (vect)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Minetest 5.3.0 or less can only measure the light level. This came in at 5.4
|
|
||||||
-- This function has been known to fail in multiple places so the error handling is added increase safety and improve
|
|
||||||
-- debugging. See:
|
|
||||||
-- https://git.minetest.land/VoxeLibre/VoxeLibre/issues/1392
|
|
||||||
function mcl_util.get_natural_light (pos, time)
|
|
||||||
local status, retVal = pcall(minetest.get_natural_light, pos, time)
|
|
||||||
if status then
|
|
||||||
return retVal
|
|
||||||
else
|
|
||||||
minetest.log("warning", "Failed to get natural light at pos: " .. dump(pos) .. ", time: " .. dump(time))
|
|
||||||
if (pos) then
|
|
||||||
local node = minetest.get_node(pos)
|
|
||||||
minetest.log("warning", "Node at pos: " .. dump(node.name))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_util.file_exists(name)
|
function mcl_util.file_exists(name)
|
||||||
if type(name) ~= "string" then return end
|
if type(name) ~= "string" then return end
|
||||||
local f = io.open(name)
|
local f = io.open(name)
|
||||||
|
@ -140,119 +125,6 @@ function mcl_util.file_exists(name)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Based on minetest.rotate_and_place
|
|
||||||
|
|
||||||
--[[
|
|
||||||
Attempt to predict the desired orientation of the pillar-like node
|
|
||||||
defined by `itemstack`, and place it accordingly in one of 3 possible
|
|
||||||
orientations (X, Y or Z).
|
|
||||||
|
|
||||||
Stacks are handled normally if the `infinitestacks`
|
|
||||||
field is false or omitted (else, the itemstack is not changed).
|
|
||||||
* `invert_wall`: if `true`, place wall-orientation on the ground and ground-
|
|
||||||
orientation on wall
|
|
||||||
|
|
||||||
This function is a simplified version of minetest.rotate_and_place.
|
|
||||||
The Minetest function is seen as inappropriate because this includes mirror
|
|
||||||
images of possible orientations, causing problems with pillar shadings.
|
|
||||||
]]
|
|
||||||
function mcl_util.rotate_axis_and_place(itemstack, placer, pointed_thing, infinitestacks, invert_wall)
|
|
||||||
local unode = minetest.get_node_or_nil(pointed_thing.under)
|
|
||||||
if not unode then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local undef = minetest.registered_nodes[unode.name]
|
|
||||||
if undef and undef.on_rightclick and not invert_wall then
|
|
||||||
undef.on_rightclick(pointed_thing.under, unode, placer,
|
|
||||||
itemstack, pointed_thing)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local fdir = minetest.dir_to_facedir(placer:get_look_dir())
|
|
||||||
local wield_name = itemstack:get_name()
|
|
||||||
|
|
||||||
local above = pointed_thing.above
|
|
||||||
local under = pointed_thing.under
|
|
||||||
local is_x = (above.x ~= under.x)
|
|
||||||
local is_y = (above.y ~= under.y)
|
|
||||||
local is_z = (above.z ~= under.z)
|
|
||||||
|
|
||||||
local anode = minetest.get_node_or_nil(above)
|
|
||||||
if not anode then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local pos = pointed_thing.above
|
|
||||||
local node = anode
|
|
||||||
|
|
||||||
if undef and undef.buildable_to then
|
|
||||||
pos = pointed_thing.under
|
|
||||||
node = unode
|
|
||||||
end
|
|
||||||
|
|
||||||
if minetest.is_protected(pos, placer:get_player_name()) then
|
|
||||||
minetest.record_protection_violation(pos, placer:get_player_name())
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local ndef = minetest.registered_nodes[node.name]
|
|
||||||
if not ndef or not ndef.buildable_to then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local p2
|
|
||||||
if is_y then
|
|
||||||
p2 = 0
|
|
||||||
elseif is_x then
|
|
||||||
p2 = 12
|
|
||||||
elseif is_z then
|
|
||||||
p2 = 6
|
|
||||||
end
|
|
||||||
minetest.set_node(pos, {name = wield_name, param2 = p2})
|
|
||||||
|
|
||||||
if not infinitestacks then
|
|
||||||
itemstack:take_item()
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Wrapper of above function for use as `on_place` callback (Recommended).
|
|
||||||
-- Similar to minetest.rotate_node.
|
|
||||||
function mcl_util.rotate_axis(itemstack, placer, pointed_thing)
|
|
||||||
mcl_util.rotate_axis_and_place(itemstack, placer, pointed_thing,
|
|
||||||
minetest.is_creative_enabled(placer:get_player_name()),
|
|
||||||
placer:get_player_control().sneak)
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Returns position of the neighbor of a double chest node
|
|
||||||
-- or nil if node is invalid.
|
|
||||||
-- This function assumes that the large chest is actually intact
|
|
||||||
-- * pos: Position of the node to investigate
|
|
||||||
-- * param2: param2 of that node
|
|
||||||
-- * side: Which "half" the investigated node is. "left" or "right"
|
|
||||||
function mcl_util.get_double_container_neighbor_pos(pos, param2, side)
|
|
||||||
if side == "right" then
|
|
||||||
if param2 == 0 then
|
|
||||||
return {x = pos.x - 1, y = pos.y, z = pos.z}
|
|
||||||
elseif param2 == 1 then
|
|
||||||
return {x = pos.x, y = pos.y, z = pos.z + 1}
|
|
||||||
elseif param2 == 2 then
|
|
||||||
return {x = pos.x + 1, y = pos.y, z = pos.z}
|
|
||||||
elseif param2 == 3 then
|
|
||||||
return {x = pos.x, y = pos.y, z = pos.z - 1}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if param2 == 0 then
|
|
||||||
return {x = pos.x + 1, y = pos.y, z = pos.z}
|
|
||||||
elseif param2 == 1 then
|
|
||||||
return {x = pos.x, y = pos.y, z = pos.z - 1}
|
|
||||||
elseif param2 == 2 then
|
|
||||||
return {x = pos.x - 1, y = pos.y, z = pos.z}
|
|
||||||
elseif param2 == 3 then
|
|
||||||
return {x = pos.x, y = pos.y, z = pos.z + 1}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Selects item stack to transfer from
|
--- Selects item stack to transfer from
|
||||||
---@param src_inventory InvRef Source innentory to pull from
|
---@param src_inventory InvRef Source innentory to pull from
|
||||||
---@param src_list string Name of source inventory list to pull from
|
---@param src_list string Name of source inventory list to pull from
|
||||||
|
@ -410,61 +282,6 @@ function mcl_util.is_fuel(item)
|
||||||
return minetest.get_craft_result({method = "fuel", width = 1, items = {item}}).time ~= 0
|
return minetest.get_craft_result({method = "fuel", width = 1, items = {item}}).time ~= 0
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns a on_place function for plants
|
|
||||||
-- * condition: function(pos, node, itemstack)
|
|
||||||
-- * A function which is called by the on_place function to check if the node can be placed
|
|
||||||
-- * Must return true, if placement is allowed, false otherwise.
|
|
||||||
-- * If it returns a string, placement is allowed, but will place this itemstring as a node instead
|
|
||||||
-- * pos, node: Position and node table of plant node
|
|
||||||
-- * itemstack: Itemstack to place
|
|
||||||
function mcl_util.generate_on_place_plant_function(condition)
|
|
||||||
return function(itemstack, placer, pointed_thing)
|
|
||||||
if pointed_thing.type ~= "node" then
|
|
||||||
-- no interaction possible with entities
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Call on_rightclick if the pointed node defines it
|
|
||||||
local node = minetest.get_node(pointed_thing.under)
|
|
||||||
if placer and not placer: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, placer, itemstack) or itemstack
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local place_pos
|
|
||||||
local def_under = minetest.registered_nodes[minetest.get_node(pointed_thing.under).name]
|
|
||||||
local def_above = minetest.registered_nodes[minetest.get_node(pointed_thing.above).name]
|
|
||||||
if not def_under or not def_above then
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
if def_under.buildable_to and def_under.name ~= itemstack:get_name() then
|
|
||||||
place_pos = pointed_thing.under
|
|
||||||
elseif def_above.buildable_to and def_above.name ~= itemstack:get_name() then
|
|
||||||
place_pos = pointed_thing.above
|
|
||||||
pointed_thing.under = pointed_thing.above
|
|
||||||
else
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check placement rules
|
|
||||||
local result, param2 = condition(place_pos, node, itemstack)
|
|
||||||
if result == true then
|
|
||||||
local idef = itemstack:get_definition()
|
|
||||||
local new_itemstack, success = minetest.item_place_node(itemstack, placer, pointed_thing, param2)
|
|
||||||
|
|
||||||
if success then
|
|
||||||
if idef.sounds and idef.sounds.place then
|
|
||||||
minetest.sound_play(idef.sounds.place, {pos = pointed_thing.above, gain = 1}, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
itemstack = new_itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- adjust the y level of an object to the center of its collisionbox
|
-- adjust the y level of an object to the center of its collisionbox
|
||||||
-- used to get the origin position of entity explosions
|
-- used to get the origin position of entity explosions
|
||||||
function mcl_util.get_object_center(obj)
|
function mcl_util.get_object_center(obj)
|
||||||
|
@ -736,243 +553,6 @@ function mcl_util.set_bone_position(obj, bone, pos, rot)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---Return a function to use in `on_place`.
|
|
||||||
---
|
|
||||||
---Allow to bypass the `buildable_to` node field in a `on_place` callback.
|
|
||||||
---
|
|
||||||
---You have to make sure that the nodes you return true for have `buildable_to = true`.
|
|
||||||
---@param func fun(node_name: string): boolean Return `true` if node must not replace the buildable_to node which have `node_name`
|
|
||||||
---@return fun(itemstack: ItemStack, placer: ObjectRef, pointed_thing: pointed_thing, param2: integer): ItemStack?
|
|
||||||
function mcl_util.bypass_buildable_to(func)
|
|
||||||
--------------------------
|
|
||||||
-- MINETEST CODE: UTILS --
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
local function copy_pointed_thing(pointed_thing)
|
|
||||||
return {
|
|
||||||
type = pointed_thing.type,
|
|
||||||
above = pointed_thing.above and vector.copy(pointed_thing.above),
|
|
||||||
under = pointed_thing.under and vector.copy(pointed_thing.under),
|
|
||||||
ref = pointed_thing.ref,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function user_name(user)
|
|
||||||
return user and user:get_player_name() or ""
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Returns a logging function. For empty names, does not log.
|
|
||||||
local function make_log(name)
|
|
||||||
return name ~= "" and minetest.log or function() end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function check_attached_node(p, n, group_rating)
|
|
||||||
local def = core.registered_nodes[n.name]
|
|
||||||
local d = vector.zero()
|
|
||||||
if group_rating == 3 then
|
|
||||||
-- always attach to floor
|
|
||||||
d.y = -1
|
|
||||||
elseif group_rating == 4 then
|
|
||||||
-- always attach to ceiling
|
|
||||||
d.y = 1
|
|
||||||
elseif group_rating == 2 then
|
|
||||||
-- attach to facedir or 4dir direction
|
|
||||||
if (def.paramtype2 == "facedir" or
|
|
||||||
def.paramtype2 == "colorfacedir") then
|
|
||||||
-- Attach to whatever facedir is "mounted to".
|
|
||||||
-- For facedir, this is where tile no. 5 point at.
|
|
||||||
|
|
||||||
-- The fallback vector here is in case 'facedir to dir' is nil due
|
|
||||||
-- to voxelmanip placing a wallmounted node without resetting a
|
|
||||||
-- pre-existing param2 value that is out-of-range for facedir.
|
|
||||||
-- The fallback vector corresponds to param2 = 0.
|
|
||||||
d = core.facedir_to_dir(n.param2) or vector.new(0, 0, 1)
|
|
||||||
elseif (def.paramtype2 == "4dir" or
|
|
||||||
def.paramtype2 == "color4dir") then
|
|
||||||
-- Similar to facedir handling
|
|
||||||
d = core.fourdir_to_dir(n.param2) or vector.new(0, 0, 1)
|
|
||||||
end
|
|
||||||
elseif def.paramtype2 == "wallmounted" or
|
|
||||||
def.paramtype2 == "colorwallmounted" then
|
|
||||||
-- Attach to whatever this node is "mounted to".
|
|
||||||
-- This where tile no. 2 points at.
|
|
||||||
|
|
||||||
-- The fallback vector here is used for the same reason as
|
|
||||||
-- for facedir nodes.
|
|
||||||
d = core.wallmounted_to_dir(n.param2) or vector.new(0, 1, 0)
|
|
||||||
else
|
|
||||||
d.y = -1
|
|
||||||
end
|
|
||||||
local p2 = vector.add(p, d)
|
|
||||||
local nn = core.get_node(p2).name
|
|
||||||
local def2 = core.registered_nodes[nn]
|
|
||||||
if def2 and not def2.walkable then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
return function(itemstack, placer, pointed_thing, param2)
|
|
||||||
-------------------
|
|
||||||
-- MINETEST CODE --
|
|
||||||
-------------------
|
|
||||||
local def = itemstack:get_definition()
|
|
||||||
if def.type ~= "node" or pointed_thing.type ~= "node" then
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
local under = pointed_thing.under
|
|
||||||
local oldnode_under = minetest.get_node_or_nil(under)
|
|
||||||
local above = pointed_thing.above
|
|
||||||
local oldnode_above = minetest.get_node_or_nil(above)
|
|
||||||
local playername = user_name(placer)
|
|
||||||
local log = make_log(playername)
|
|
||||||
|
|
||||||
if not oldnode_under or not oldnode_above then
|
|
||||||
log("info", playername .. " tried to place"
|
|
||||||
.. " node in unloaded position " .. minetest.pos_to_string(above))
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
local olddef_under = minetest.registered_nodes[oldnode_under.name]
|
|
||||||
olddef_under = olddef_under or minetest.nodedef_default
|
|
||||||
local olddef_above = minetest.registered_nodes[oldnode_above.name]
|
|
||||||
olddef_above = olddef_above or minetest.nodedef_default
|
|
||||||
|
|
||||||
if not olddef_above.buildable_to and not olddef_under.buildable_to then
|
|
||||||
log("info", playername .. " tried to place"
|
|
||||||
.. " node in invalid position " .. minetest.pos_to_string(above)
|
|
||||||
.. ", replacing " .. oldnode_above.name)
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
---------------------
|
|
||||||
-- CUSTOMIZED CODE --
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
-- Place above pointed node
|
|
||||||
local place_to = vector.copy(above)
|
|
||||||
|
|
||||||
-- If node under is buildable_to, check for callback result and place into it instead
|
|
||||||
if olddef_under.buildable_to and not func(oldnode_under.name) then
|
|
||||||
log("info", "node under is buildable to")
|
|
||||||
place_to = vector.copy(under)
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------
|
|
||||||
-- MINETEST CODE --
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
if minetest.is_protected(place_to, playername) then
|
|
||||||
log("action", playername
|
|
||||||
.. " tried to place " .. def.name
|
|
||||||
.. " at protected position "
|
|
||||||
.. minetest.pos_to_string(place_to))
|
|
||||||
minetest.record_protection_violation(place_to, playername)
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
local oldnode = minetest.get_node(place_to)
|
|
||||||
local newnode = {name = def.name, param1 = 0, param2 = param2 or 0}
|
|
||||||
|
|
||||||
-- Calculate direction for wall mounted stuff like torches and signs
|
|
||||||
if def.place_param2 ~= nil then
|
|
||||||
newnode.param2 = def.place_param2
|
|
||||||
elseif (def.paramtype2 == "wallmounted" or
|
|
||||||
def.paramtype2 == "colorwallmounted") and not param2 then
|
|
||||||
local dir = vector.subtract(under, above)
|
|
||||||
newnode.param2 = minetest.dir_to_wallmounted(dir)
|
|
||||||
-- Calculate the direction for furnaces and chests and stuff
|
|
||||||
elseif (def.paramtype2 == "facedir" or
|
|
||||||
def.paramtype2 == "colorfacedir" or
|
|
||||||
def.paramtype2 == "4dir" or
|
|
||||||
def.paramtype2 == "color4dir") and not param2 then
|
|
||||||
local placer_pos = placer and placer:get_pos()
|
|
||||||
if placer_pos then
|
|
||||||
local dir = vector.subtract(above, placer_pos)
|
|
||||||
newnode.param2 = minetest.dir_to_facedir(dir)
|
|
||||||
log("info", "facedir: " .. newnode.param2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local metatable = itemstack:get_meta():to_table().fields
|
|
||||||
|
|
||||||
-- Transfer color information
|
|
||||||
if metatable.palette_index and not def.place_param2 then
|
|
||||||
local color_divisor = nil
|
|
||||||
if def.paramtype2 == "color" then
|
|
||||||
color_divisor = 1
|
|
||||||
elseif def.paramtype2 == "colorwallmounted" then
|
|
||||||
color_divisor = 8
|
|
||||||
elseif def.paramtype2 == "colorfacedir" then
|
|
||||||
color_divisor = 32
|
|
||||||
elseif def.paramtype2 == "color4dir" then
|
|
||||||
color_divisor = 4
|
|
||||||
elseif def.paramtype2 == "colordegrotate" then
|
|
||||||
color_divisor = 32
|
|
||||||
end
|
|
||||||
if color_divisor then
|
|
||||||
local color = math.floor(metatable.palette_index / color_divisor)
|
|
||||||
local other = newnode.param2 % color_divisor
|
|
||||||
newnode.param2 = color * color_divisor + other
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if the node is attached and if it can be placed there
|
|
||||||
local an = minetest.get_item_group(def.name, "attached_node")
|
|
||||||
if an ~= 0 and
|
|
||||||
not check_attached_node(place_to, newnode, an) then
|
|
||||||
log("action", "attached node " .. def.name ..
|
|
||||||
" cannot be placed at " .. minetest.pos_to_string(place_to))
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
log("action", playername .. " places node "
|
|
||||||
.. def.name .. " at " .. minetest.pos_to_string(place_to))
|
|
||||||
|
|
||||||
-- Add node and update
|
|
||||||
minetest.add_node(place_to, newnode)
|
|
||||||
|
|
||||||
-- Play sound if it was done by a player
|
|
||||||
if playername ~= "" and def.sounds and def.sounds.place then
|
|
||||||
minetest.sound_play(def.sounds.place, {
|
|
||||||
pos = place_to,
|
|
||||||
exclude_player = playername,
|
|
||||||
}, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
local take_item = true
|
|
||||||
|
|
||||||
-- Run callback
|
|
||||||
if def.after_place_node then
|
|
||||||
-- Deepcopy place_to and pointed_thing because callback can modify it
|
|
||||||
local place_to_copy = vector.copy(place_to)
|
|
||||||
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
|
|
||||||
if def.after_place_node(place_to_copy, placer, itemstack,
|
|
||||||
pointed_thing_copy) then
|
|
||||||
take_item = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Run script hook
|
|
||||||
for _, callback in ipairs(minetest.registered_on_placenodes) do
|
|
||||||
-- Deepcopy pos, node and pointed_thing because callback can modify them
|
|
||||||
local place_to_copy = vector.copy(place_to)
|
|
||||||
local newnode_copy = {name = newnode.name, param1 = newnode.param1, param2 = newnode.param2}
|
|
||||||
local oldnode_copy = {name = oldnode.name, param1 = oldnode.param1, param2 = oldnode.param2}
|
|
||||||
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
|
|
||||||
if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack, pointed_thing_copy) then
|
|
||||||
take_item = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if take_item then
|
|
||||||
itemstack:take_item()
|
|
||||||
end
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[Check for a protection violation in a given area.
|
--[[Check for a protection violation in a given area.
|
||||||
--
|
--
|
||||||
-- Applies is_protected() to a 3D lattice of points in the defined volume. The points are spaced
|
-- Applies is_protected() to a 3D lattice of points in the defined volume. The points are spaced
|
||||||
|
@ -1018,33 +598,6 @@ function mcl_util.check_position_protection(position, player)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local palette_indexes = {grass_palette_index = 0, foliage_palette_index = 0, water_palette_index = 0}
|
|
||||||
function mcl_util.get_palette_indexes_from_pos(pos)
|
|
||||||
local biome_data = minetest.get_biome_data(pos)
|
|
||||||
local biome = biome_data.biome
|
|
||||||
local biome_name = minetest.get_biome_name(biome)
|
|
||||||
local reg_biome = minetest.registered_biomes[biome_name]
|
|
||||||
if reg_biome and reg_biome._mcl_grass_palette_index and reg_biome._mcl_foliage_palette_index and reg_biome._mcl_water_palette_index then
|
|
||||||
local gpi = reg_biome._mcl_grass_palette_index
|
|
||||||
local fpi = reg_biome._mcl_foliage_palette_index
|
|
||||||
local wpi = reg_biome._mcl_water_palette_index
|
|
||||||
local palette_indexes = {grass_palette_index = gpi, foliage_palette_index = fpi, water_palette_index = wpi}
|
|
||||||
return palette_indexes
|
|
||||||
else
|
|
||||||
return palette_indexes
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_util.get_colorwallmounted_rotation(pos)
|
|
||||||
local colorwallmounted_node = minetest.get_node(pos)
|
|
||||||
for i = 0, 32, 1 do
|
|
||||||
local colorwallmounted_rotation = colorwallmounted_node.param2 - (i * 8)
|
|
||||||
if colorwallmounted_rotation < 6 then
|
|
||||||
return colorwallmounted_rotation
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---Move items from one inventory list to another, drop items that do not fit in provided pos and direction.
|
---Move items from one inventory list to another, drop items that do not fit in provided pos and direction.
|
||||||
---@param src_inv mt.InvRef
|
---@param src_inv mt.InvRef
|
||||||
---@param src_listname string
|
---@param src_listname string
|
||||||
|
|
|
@ -0,0 +1,462 @@
|
||||||
|
-- Functions related to nodes and node definitions
|
||||||
|
|
||||||
|
-- Minetest 5.3.0 or less can only measure the light level. This came in at 5.4
|
||||||
|
-- This function has been known to fail in multiple places so the error handling is added increase safety and improve
|
||||||
|
-- debugging. See:
|
||||||
|
-- https://git.minetest.land/VoxeLibre/VoxeLibre/issues/1392
|
||||||
|
function mcl_util.get_natural_light (pos, time)
|
||||||
|
local status, retVal = pcall(minetest.get_natural_light, pos, time)
|
||||||
|
if status then
|
||||||
|
return retVal
|
||||||
|
else
|
||||||
|
minetest.log("warning", "Failed to get natural light at pos: " .. dump(pos) .. ", time: " .. dump(time))
|
||||||
|
if (pos) then
|
||||||
|
local node = minetest.get_node(pos)
|
||||||
|
minetest.log("warning", "Node at pos: " .. dump(node.name))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Based on minetest.rotate_and_place
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Attempt to predict the desired orientation of the pillar-like node
|
||||||
|
defined by `itemstack`, and place it accordingly in one of 3 possible
|
||||||
|
orientations (X, Y or Z).
|
||||||
|
|
||||||
|
Stacks are handled normally if the `infinitestacks`
|
||||||
|
field is false or omitted (else, the itemstack is not changed).
|
||||||
|
* `invert_wall`: if `true`, place wall-orientation on the ground and ground-
|
||||||
|
orientation on wall
|
||||||
|
|
||||||
|
This function is a simplified version of minetest.rotate_and_place.
|
||||||
|
The Minetest function is seen as inappropriate because this includes mirror
|
||||||
|
images of possible orientations, causing problems with pillar shadings.
|
||||||
|
]]
|
||||||
|
function mcl_util.rotate_axis_and_place(itemstack, placer, pointed_thing, infinitestacks, invert_wall)
|
||||||
|
local unode = minetest.get_node_or_nil(pointed_thing.under)
|
||||||
|
if not unode then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local undef = minetest.registered_nodes[unode.name]
|
||||||
|
if undef and undef.on_rightclick and not invert_wall then
|
||||||
|
undef.on_rightclick(pointed_thing.under, unode, placer,
|
||||||
|
itemstack, pointed_thing)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local fdir = minetest.dir_to_facedir(placer:get_look_dir())
|
||||||
|
local wield_name = itemstack:get_name()
|
||||||
|
|
||||||
|
local above = pointed_thing.above
|
||||||
|
local under = pointed_thing.under
|
||||||
|
local is_x = (above.x ~= under.x)
|
||||||
|
local is_y = (above.y ~= under.y)
|
||||||
|
local is_z = (above.z ~= under.z)
|
||||||
|
|
||||||
|
local anode = minetest.get_node_or_nil(above)
|
||||||
|
if not anode then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local pos = pointed_thing.above
|
||||||
|
local node = anode
|
||||||
|
|
||||||
|
if undef and undef.buildable_to then
|
||||||
|
pos = pointed_thing.under
|
||||||
|
node = unode
|
||||||
|
end
|
||||||
|
|
||||||
|
if minetest.is_protected(pos, placer:get_player_name()) then
|
||||||
|
minetest.record_protection_violation(pos, placer:get_player_name())
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local ndef = minetest.registered_nodes[node.name]
|
||||||
|
if not ndef or not ndef.buildable_to then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local p2
|
||||||
|
if is_y then
|
||||||
|
p2 = 0
|
||||||
|
elseif is_x then
|
||||||
|
p2 = 12
|
||||||
|
elseif is_z then
|
||||||
|
p2 = 6
|
||||||
|
end
|
||||||
|
minetest.set_node(pos, {name = wield_name, param2 = p2})
|
||||||
|
|
||||||
|
if not infinitestacks then
|
||||||
|
itemstack:take_item()
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Wrapper of above function for use as `on_place` callback (Recommended).
|
||||||
|
-- Similar to minetest.rotate_node.
|
||||||
|
function mcl_util.rotate_axis(itemstack, placer, pointed_thing)
|
||||||
|
mcl_util.rotate_axis_and_place(itemstack, placer, pointed_thing,
|
||||||
|
minetest.is_creative_enabled(placer:get_player_name()),
|
||||||
|
placer:get_player_control().sneak)
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns position of the neighbor of a double chest node
|
||||||
|
-- or nil if node is invalid.
|
||||||
|
-- This function assumes that the large chest is actually intact
|
||||||
|
-- * pos: Position of the node to investigate
|
||||||
|
-- * param2: param2 of that node
|
||||||
|
-- * side: Which "half" the investigated node is. "left" or "right"
|
||||||
|
function mcl_util.get_double_container_neighbor_pos(pos, param2, side)
|
||||||
|
if side == "right" then
|
||||||
|
if param2 == 0 then
|
||||||
|
return {x = pos.x - 1, y = pos.y, z = pos.z}
|
||||||
|
elseif param2 == 1 then
|
||||||
|
return {x = pos.x, y = pos.y, z = pos.z + 1}
|
||||||
|
elseif param2 == 2 then
|
||||||
|
return {x = pos.x + 1, y = pos.y, z = pos.z}
|
||||||
|
elseif param2 == 3 then
|
||||||
|
return {x = pos.x, y = pos.y, z = pos.z - 1}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if param2 == 0 then
|
||||||
|
return {x = pos.x + 1, y = pos.y, z = pos.z}
|
||||||
|
elseif param2 == 1 then
|
||||||
|
return {x = pos.x, y = pos.y, z = pos.z - 1}
|
||||||
|
elseif param2 == 2 then
|
||||||
|
return {x = pos.x - 1, y = pos.y, z = pos.z}
|
||||||
|
elseif param2 == 3 then
|
||||||
|
return {x = pos.x, y = pos.y, z = pos.z + 1}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns a on_place function for plants
|
||||||
|
-- * condition: function(pos, node, itemstack)
|
||||||
|
-- * A function which is called by the on_place function to check if the node can be placed
|
||||||
|
-- * Must return true, if placement is allowed, false otherwise.
|
||||||
|
-- * If it returns a string, placement is allowed, but will place this itemstring as a node instead
|
||||||
|
-- * pos, node: Position and node table of plant node
|
||||||
|
-- * itemstack: Itemstack to place
|
||||||
|
function mcl_util.generate_on_place_plant_function(condition)
|
||||||
|
return function(itemstack, placer, pointed_thing)
|
||||||
|
if pointed_thing.type ~= "node" then
|
||||||
|
-- no interaction possible with entities
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Call on_rightclick if the pointed node defines it
|
||||||
|
local node = minetest.get_node(pointed_thing.under)
|
||||||
|
if placer and not placer: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, placer, itemstack) or itemstack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local place_pos
|
||||||
|
local def_under = minetest.registered_nodes[minetest.get_node(pointed_thing.under).name]
|
||||||
|
local def_above = minetest.registered_nodes[minetest.get_node(pointed_thing.above).name]
|
||||||
|
if not def_under or not def_above then
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
if def_under.buildable_to and def_under.name ~= itemstack:get_name() then
|
||||||
|
place_pos = pointed_thing.under
|
||||||
|
elseif def_above.buildable_to and def_above.name ~= itemstack:get_name() then
|
||||||
|
place_pos = pointed_thing.above
|
||||||
|
pointed_thing.under = pointed_thing.above
|
||||||
|
else
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check placement rules
|
||||||
|
local result, param2 = condition(place_pos, node, itemstack)
|
||||||
|
if result == true then
|
||||||
|
local idef = itemstack:get_definition()
|
||||||
|
local new_itemstack, success = minetest.item_place_node(itemstack, placer, pointed_thing, param2)
|
||||||
|
|
||||||
|
if success then
|
||||||
|
if idef.sounds and idef.sounds.place then
|
||||||
|
minetest.sound_play(idef.sounds.place, {pos = pointed_thing.above, gain = 1}, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
itemstack = new_itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---Return a function to use in `on_place`.
|
||||||
|
---
|
||||||
|
---Allow to bypass the `buildable_to` node field in a `on_place` callback.
|
||||||
|
---
|
||||||
|
---You have to make sure that the nodes you return true for have `buildable_to = true`.
|
||||||
|
---@param func fun(node_name: string): boolean Return `true` if node must not replace the buildable_to node which have `node_name`
|
||||||
|
---@return fun(itemstack: ItemStack, placer: ObjectRef, pointed_thing: pointed_thing, param2: integer): ItemStack?
|
||||||
|
function mcl_util.bypass_buildable_to(func)
|
||||||
|
--------------------------
|
||||||
|
-- MINETEST CODE: UTILS --
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
local function copy_pointed_thing(pointed_thing)
|
||||||
|
return {
|
||||||
|
type = pointed_thing.type,
|
||||||
|
above = pointed_thing.above and vector.copy(pointed_thing.above),
|
||||||
|
under = pointed_thing.under and vector.copy(pointed_thing.under),
|
||||||
|
ref = pointed_thing.ref,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function user_name(user)
|
||||||
|
return user and user:get_player_name() or ""
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns a logging function. For empty names, does not log.
|
||||||
|
local function make_log(name)
|
||||||
|
return name ~= "" and minetest.log or function() end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function check_attached_node(p, n, group_rating)
|
||||||
|
local def = core.registered_nodes[n.name]
|
||||||
|
local d = vector.zero()
|
||||||
|
if group_rating == 3 then
|
||||||
|
-- always attach to floor
|
||||||
|
d.y = -1
|
||||||
|
elseif group_rating == 4 then
|
||||||
|
-- always attach to ceiling
|
||||||
|
d.y = 1
|
||||||
|
elseif group_rating == 2 then
|
||||||
|
-- attach to facedir or 4dir direction
|
||||||
|
if (def.paramtype2 == "facedir" or
|
||||||
|
def.paramtype2 == "colorfacedir") then
|
||||||
|
-- Attach to whatever facedir is "mounted to".
|
||||||
|
-- For facedir, this is where tile no. 5 point at.
|
||||||
|
|
||||||
|
-- The fallback vector here is in case 'facedir to dir' is nil due
|
||||||
|
-- to voxelmanip placing a wallmounted node without resetting a
|
||||||
|
-- pre-existing param2 value that is out-of-range for facedir.
|
||||||
|
-- The fallback vector corresponds to param2 = 0.
|
||||||
|
d = core.facedir_to_dir(n.param2) or vector.new(0, 0, 1)
|
||||||
|
elseif (def.paramtype2 == "4dir" or
|
||||||
|
def.paramtype2 == "color4dir") then
|
||||||
|
-- Similar to facedir handling
|
||||||
|
d = core.fourdir_to_dir(n.param2) or vector.new(0, 0, 1)
|
||||||
|
end
|
||||||
|
elseif def.paramtype2 == "wallmounted" or
|
||||||
|
def.paramtype2 == "colorwallmounted" then
|
||||||
|
-- Attach to whatever this node is "mounted to".
|
||||||
|
-- This where tile no. 2 points at.
|
||||||
|
|
||||||
|
-- The fallback vector here is used for the same reason as
|
||||||
|
-- for facedir nodes.
|
||||||
|
d = core.wallmounted_to_dir(n.param2) or vector.new(0, 1, 0)
|
||||||
|
else
|
||||||
|
d.y = -1
|
||||||
|
end
|
||||||
|
local p2 = vector.add(p, d)
|
||||||
|
local nn = core.get_node(p2).name
|
||||||
|
local def2 = core.registered_nodes[nn]
|
||||||
|
if def2 and not def2.walkable then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return function(itemstack, placer, pointed_thing, param2)
|
||||||
|
-------------------
|
||||||
|
-- MINETEST CODE --
|
||||||
|
-------------------
|
||||||
|
local def = itemstack:get_definition()
|
||||||
|
if def.type ~= "node" or pointed_thing.type ~= "node" then
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
local under = pointed_thing.under
|
||||||
|
local oldnode_under = minetest.get_node_or_nil(under)
|
||||||
|
local above = pointed_thing.above
|
||||||
|
local oldnode_above = minetest.get_node_or_nil(above)
|
||||||
|
local playername = user_name(placer)
|
||||||
|
local log = make_log(playername)
|
||||||
|
|
||||||
|
if not oldnode_under or not oldnode_above then
|
||||||
|
log("info", playername .. " tried to place"
|
||||||
|
.. " node in unloaded position " .. minetest.pos_to_string(above))
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
local olddef_under = minetest.registered_nodes[oldnode_under.name]
|
||||||
|
olddef_under = olddef_under or minetest.nodedef_default
|
||||||
|
local olddef_above = minetest.registered_nodes[oldnode_above.name]
|
||||||
|
olddef_above = olddef_above or minetest.nodedef_default
|
||||||
|
|
||||||
|
if not olddef_above.buildable_to and not olddef_under.buildable_to then
|
||||||
|
log("info", playername .. " tried to place"
|
||||||
|
.. " node in invalid position " .. minetest.pos_to_string(above)
|
||||||
|
.. ", replacing " .. oldnode_above.name)
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
-- CUSTOMIZED CODE --
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
-- Place above pointed node
|
||||||
|
local place_to = vector.copy(above)
|
||||||
|
|
||||||
|
-- If node under is buildable_to, check for callback result and place into it instead
|
||||||
|
if olddef_under.buildable_to and not func(oldnode_under.name) then
|
||||||
|
log("info", "node under is buildable to")
|
||||||
|
place_to = vector.copy(under)
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
-- MINETEST CODE --
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
if minetest.is_protected(place_to, playername) then
|
||||||
|
log("action", playername
|
||||||
|
.. " tried to place " .. def.name
|
||||||
|
.. " at protected position "
|
||||||
|
.. minetest.pos_to_string(place_to))
|
||||||
|
minetest.record_protection_violation(place_to, playername)
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
local oldnode = minetest.get_node(place_to)
|
||||||
|
local newnode = {name = def.name, param1 = 0, param2 = param2 or 0}
|
||||||
|
|
||||||
|
-- Calculate direction for wall mounted stuff like torches and signs
|
||||||
|
if def.place_param2 ~= nil then
|
||||||
|
newnode.param2 = def.place_param2
|
||||||
|
elseif (def.paramtype2 == "wallmounted" or
|
||||||
|
def.paramtype2 == "colorwallmounted") and not param2 then
|
||||||
|
local dir = vector.subtract(under, above)
|
||||||
|
newnode.param2 = minetest.dir_to_wallmounted(dir)
|
||||||
|
-- Calculate the direction for furnaces and chests and stuff
|
||||||
|
elseif (def.paramtype2 == "facedir" or
|
||||||
|
def.paramtype2 == "colorfacedir" or
|
||||||
|
def.paramtype2 == "4dir" or
|
||||||
|
def.paramtype2 == "color4dir") and not param2 then
|
||||||
|
local placer_pos = placer and placer:get_pos()
|
||||||
|
if placer_pos then
|
||||||
|
local dir = vector.subtract(above, placer_pos)
|
||||||
|
newnode.param2 = minetest.dir_to_facedir(dir)
|
||||||
|
log("info", "facedir: " .. newnode.param2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local metatable = itemstack:get_meta():to_table().fields
|
||||||
|
|
||||||
|
-- Transfer color information
|
||||||
|
if metatable.palette_index and not def.place_param2 then
|
||||||
|
local color_divisor = nil
|
||||||
|
if def.paramtype2 == "color" then
|
||||||
|
color_divisor = 1
|
||||||
|
elseif def.paramtype2 == "colorwallmounted" then
|
||||||
|
color_divisor = 8
|
||||||
|
elseif def.paramtype2 == "colorfacedir" then
|
||||||
|
color_divisor = 32
|
||||||
|
elseif def.paramtype2 == "color4dir" then
|
||||||
|
color_divisor = 4
|
||||||
|
elseif def.paramtype2 == "colordegrotate" then
|
||||||
|
color_divisor = 32
|
||||||
|
end
|
||||||
|
if color_divisor then
|
||||||
|
local color = math.floor(metatable.palette_index / color_divisor)
|
||||||
|
local other = newnode.param2 % color_divisor
|
||||||
|
newnode.param2 = color * color_divisor + other
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if the node is attached and if it can be placed there
|
||||||
|
local an = minetest.get_item_group(def.name, "attached_node")
|
||||||
|
if an ~= 0 and
|
||||||
|
not check_attached_node(place_to, newnode, an) then
|
||||||
|
log("action", "attached node " .. def.name ..
|
||||||
|
" cannot be placed at " .. minetest.pos_to_string(place_to))
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
log("action", playername .. " places node "
|
||||||
|
.. def.name .. " at " .. minetest.pos_to_string(place_to))
|
||||||
|
|
||||||
|
-- Add node and update
|
||||||
|
minetest.add_node(place_to, newnode)
|
||||||
|
|
||||||
|
-- Play sound if it was done by a player
|
||||||
|
if playername ~= "" and def.sounds and def.sounds.place then
|
||||||
|
minetest.sound_play(def.sounds.place, {
|
||||||
|
pos = place_to,
|
||||||
|
exclude_player = playername,
|
||||||
|
}, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
local take_item = true
|
||||||
|
|
||||||
|
-- Run callback
|
||||||
|
if def.after_place_node then
|
||||||
|
-- Deepcopy place_to and pointed_thing because callback can modify it
|
||||||
|
local place_to_copy = vector.copy(place_to)
|
||||||
|
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
|
||||||
|
if def.after_place_node(place_to_copy, placer, itemstack,
|
||||||
|
pointed_thing_copy) then
|
||||||
|
take_item = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Run script hook
|
||||||
|
for _, callback in ipairs(minetest.registered_on_placenodes) do
|
||||||
|
-- Deepcopy pos, node and pointed_thing because callback can modify them
|
||||||
|
local place_to_copy = vector.copy(place_to)
|
||||||
|
local newnode_copy = {name = newnode.name, param1 = newnode.param1, param2 = newnode.param2}
|
||||||
|
local oldnode_copy = {name = oldnode.name, param1 = oldnode.param1, param2 = oldnode.param2}
|
||||||
|
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
|
||||||
|
if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack, pointed_thing_copy) then
|
||||||
|
take_item = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if take_item then
|
||||||
|
itemstack:take_item()
|
||||||
|
end
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local palette_indexes = {grass_palette_index = 0, foliage_palette_index = 0, water_palette_index = 0}
|
||||||
|
function mcl_util.get_palette_indexes_from_pos(pos)
|
||||||
|
local biome_data = minetest.get_biome_data(pos)
|
||||||
|
local biome = biome_data.biome
|
||||||
|
local biome_name = minetest.get_biome_name(biome)
|
||||||
|
local reg_biome = minetest.registered_biomes[biome_name]
|
||||||
|
if reg_biome and reg_biome._mcl_grass_palette_index and reg_biome._mcl_foliage_palette_index and reg_biome._mcl_water_palette_index then
|
||||||
|
local gpi = reg_biome._mcl_grass_palette_index
|
||||||
|
local fpi = reg_biome._mcl_foliage_palette_index
|
||||||
|
local wpi = reg_biome._mcl_water_palette_index
|
||||||
|
local palette_indexes = {grass_palette_index = gpi, foliage_palette_index = fpi, water_palette_index = wpi}
|
||||||
|
return palette_indexes
|
||||||
|
else
|
||||||
|
return palette_indexes
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_util.get_colorwallmounted_rotation(pos)
|
||||||
|
local colorwallmounted_node = minetest.get_node(pos)
|
||||||
|
for i = 0, 32, 1 do
|
||||||
|
local colorwallmounted_rotation = colorwallmounted_node.param2 - (i * 8)
|
||||||
|
if colorwallmounted_rotation < 6 then
|
||||||
|
return colorwallmounted_rotation
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_util.match_node_to_filter(node_name, filters)
|
||||||
|
for i = 1,#filters do
|
||||||
|
local filter = filters[i]
|
||||||
|
if node_name == filter then return true end
|
||||||
|
|
||||||
|
if string.sub(filter,1,6) == "group:" and minetest.get_item_group(node_name, string.sub(filter,7)) ~= 0 then return true end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
|
@ -150,11 +150,6 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
||||||
local tmp = minetest.deserialize(staticdata)
|
local tmp = minetest.deserialize(staticdata)
|
||||||
|
|
||||||
if tmp then
|
if tmp then
|
||||||
-- Patch incorrectly converted mobs
|
|
||||||
if tmp.base_mesh ~= minetest.registered_entities[self.name].mesh then
|
|
||||||
mcl_mobs.strip_staticdata(tmp)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _,stat in pairs(tmp) do
|
for _,stat in pairs(tmp) do
|
||||||
self[_] = stat
|
self[_] = stat
|
||||||
end
|
end
|
||||||
|
|
|
@ -671,13 +671,6 @@ mob will spawn e.g.
|
||||||
mobs_animal:sheep_chance 11000
|
mobs_animal:sheep_chance 11000
|
||||||
mobs_monster:sand_monster_chance 100
|
mobs_monster:sand_monster_chance 100
|
||||||
|
|
||||||
Registering Mob Conversion
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Sometimes you need to completely replace one mob with a different version. To do this, use:
|
|
||||||
|
|
||||||
mcl_mobs.register_conversion(old_name, new_name)
|
|
||||||
|
|
||||||
|
|
||||||
Rideable Horse Example Mob
|
Rideable Horse Example Mob
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
|
@ -802,37 +802,34 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- alert others to the attack
|
-- alert others to the attack
|
||||||
local alert_pos = hitter:get_pos()
|
local objs = minetest.get_objects_inside_radius(hitter:get_pos(), self.view_range)
|
||||||
if alert_pos then
|
local obj = nil
|
||||||
local objs = minetest.get_objects_inside_radius(alert_pos, self.view_range)
|
|
||||||
local obj = nil
|
|
||||||
|
|
||||||
for n = 1, #objs do
|
for n = 1, #objs do
|
||||||
|
|
||||||
obj = objs[n]:get_luaentity()
|
obj = objs[n]:get_luaentity()
|
||||||
|
|
||||||
if obj then
|
if obj then
|
||||||
-- only alert members of same mob or friends
|
-- only alert members of same mob or friends
|
||||||
if obj.group_attack
|
if obj.group_attack
|
||||||
and obj.state ~= "attack"
|
and obj.state ~= "attack"
|
||||||
and obj.owner ~= name then
|
and obj.owner ~= name then
|
||||||
if obj.name == self.name then
|
if obj.name == self.name then
|
||||||
obj:do_attack(hitter)
|
obj:do_attack(hitter)
|
||||||
elseif type(obj.group_attack) == "table" then
|
elseif type(obj.group_attack) == "table" then
|
||||||
for i=1, #obj.group_attack do
|
for i=1, #obj.group_attack do
|
||||||
if obj.group_attack[i] == self.name then
|
if obj.group_attack[i] == self.name then
|
||||||
obj._aggro = true
|
obj._aggro = true
|
||||||
obj:do_attack(hitter)
|
obj:do_attack(hitter)
|
||||||
break
|
break
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- have owned mobs attack player threat
|
-- have owned mobs attack player threat
|
||||||
if obj.owner == name and obj.owner_loyal then
|
if obj.owner == name and obj.owner_loyal then
|
||||||
obj:do_attack(self.object)
|
obj:do_attack(self.object)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -343,37 +343,6 @@ function mcl_mobs.register_mob(name, def)
|
||||||
end -- END mcl_mobs.register_mob function
|
end -- END mcl_mobs.register_mob function
|
||||||
|
|
||||||
|
|
||||||
local STRIP_FIELDS = { "mesh", "base_size", "textures", "base_mesh", "base_texture" }
|
|
||||||
function mcl_mobs.strip_staticdata(unpacked_staticdata)
|
|
||||||
-- Strip select fields from the staticdata to prevent conversion issues
|
|
||||||
for i = 1,#STRIP_FIELDS do
|
|
||||||
unpacked_staticdata[STRIP_FIELDS[i]] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
function mcl_mobs.register_conversion(old_name, new_name)
|
|
||||||
minetest.register_entity(old_name, {
|
|
||||||
on_activate = function(self, staticdata, dtime)
|
|
||||||
local unpacked_staticdata = minetest.deserialize(staticdata)
|
|
||||||
mcl_mobs.strip_staticdata(unpacked_staticdata)
|
|
||||||
staticdata = minetest.serialize(unpacked_staticdata)
|
|
||||||
|
|
||||||
local old_object = self.object
|
|
||||||
if not old_object then return end
|
|
||||||
|
|
||||||
local pos = old_object:get_pos()
|
|
||||||
if not pos then return end
|
|
||||||
old_object:remove()
|
|
||||||
|
|
||||||
local new_object = minetest.add_entity(pos, new_name, staticdata)
|
|
||||||
if not new_object then return end
|
|
||||||
|
|
||||||
local hook = (new_object:get_luaentity() or {})._on_after_convert
|
|
||||||
if hook then hook(new_object) end
|
|
||||||
end,
|
|
||||||
_convert_to = new_name,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.get_arrow_damage_func(damage, typ)
|
function mcl_mobs.get_arrow_damage_func(damage, typ)
|
||||||
local typ = mcl_damage.types[typ] and typ or "arrow"
|
local typ = mcl_damage.types[typ] and typ or "arrow"
|
||||||
return function(projectile, object)
|
return function(projectile, object)
|
||||||
|
@ -592,12 +561,7 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg
|
||||||
--minetest.log("min light: " .. mob_light_lvl[1])
|
--minetest.log("min light: " .. mob_light_lvl[1])
|
||||||
--minetest.log("max light: " .. mob_light_lvl[2])
|
--minetest.log("max light: " .. mob_light_lvl[2])
|
||||||
|
|
||||||
-- Handle egg conversion
|
mcl_mobspawners.setup_spawner(pointed_thing.under, itemstack:get_name(), mob_light_lvl[1], mob_light_lvl[2])
|
||||||
local mob_name = itemstack:get_name()
|
|
||||||
local convert_to = (minetest.registered_entities[mob_name] or {})._convert_to
|
|
||||||
if convert_to then mob_name = convert_to end
|
|
||||||
|
|
||||||
mcl_mobspawners.setup_spawner(pointed_thing.under, mob_name, mob_light_lvl[1], mob_light_lvl[2])
|
|
||||||
if not minetest.is_creative_enabled(name) then
|
if not minetest.is_creative_enabled(name) then
|
||||||
itemstack:take_item()
|
itemstack:take_item()
|
||||||
end
|
end
|
||||||
|
|
|
@ -633,7 +633,7 @@ local function get_next_mob_spawn_pos(pos)
|
||||||
-- those further away from the player.
|
-- those further away from the player.
|
||||||
local fx = (math_random(1,10000)-1) / 10000
|
local fx = (math_random(1,10000)-1) / 10000
|
||||||
local x = inverse_pwl(fx, SPAWN_DISTANCE_CDF_PWL)
|
local x = inverse_pwl(fx, SPAWN_DISTANCE_CDF_PWL)
|
||||||
local distance = x * (MOB_SPAWN_ZONE_OUTER - MOB_SPAWN_ZONE_INNER) + MOB_SPAWN_ZONE_INNER
|
distance = x * (MOB_SPAWN_ZONE_OUTER - MOB_SPAWN_ZONE_INNER) + MOB_SPAWN_ZONE_INNER
|
||||||
--print("Using spawn distance of "..tostring(distance).." fx="..tostring(fx)..",x="..tostring(x))
|
--print("Using spawn distance of "..tostring(distance).." fx="..tostring(fx)..",x="..tostring(x))
|
||||||
|
|
||||||
-- TODO Floor xoff and zoff and add 0.5 so it tries to spawn in the middle of the square. Less failed attempts.
|
-- TODO Floor xoff and zoff and add 0.5 so it tries to spawn in the middle of the square. Less failed attempts.
|
||||||
|
|
|
@ -491,17 +491,20 @@ mcl_mobs.register_mob("mobs_mc:rover", {
|
||||||
view_range = 64,
|
view_range = 64,
|
||||||
fear_height = 4,
|
fear_height = 4,
|
||||||
attack_type = "dogfight",
|
attack_type = "dogfight",
|
||||||
_on_after_convert = function(obj)
|
})
|
||||||
|
|
||||||
|
-- compat
|
||||||
|
minetest.register_entity("mobs_mc:enderman", {
|
||||||
|
on_activate = function(self, staticdata, dtime)
|
||||||
|
local obj = minetest.add_entity(self.object:get_pos(), "mobs_mc:rover", staticdata)
|
||||||
obj:set_properties({
|
obj:set_properties({
|
||||||
mesh = "vl_rover.b3d",
|
mesh = "vl_rover.b3d",
|
||||||
textures = { "vl_mobs_rover.png^vl_mobs_rover_face.png" },
|
textures = { "vl_mobs_rover.png^vl_mobs_rover_face.png" },
|
||||||
visual_size = {x=10, y=10},
|
visual_size = {x=10, y=10},
|
||||||
})
|
})
|
||||||
end
|
self.object:remove()
|
||||||
}) -- END mcl_mobs.register_mob("mobs_mc:rover", {
|
end,
|
||||||
|
})
|
||||||
-- compat
|
|
||||||
mcl_mobs.register_conversion("mobs_mc:enderman", "mobs_mc:rover")
|
|
||||||
|
|
||||||
-- End spawn
|
-- End spawn
|
||||||
mcl_mobs:spawn_specific(
|
mcl_mobs:spawn_specific(
|
||||||
|
|
|
@ -32,11 +32,10 @@ local function get_texture(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not texture or texture == "" then
|
if not texture then
|
||||||
texture = "vl_stalker_default.png"
|
texture = "vl_stalker_default.png"
|
||||||
end
|
end
|
||||||
texture = texture:gsub("([\\^:\\[])","\\%1") -- escape texture modifiers
|
texture = "([combine:16x24:0,0=" .. texture .. ":0,16=" .. texture .. texture_suff
|
||||||
texture = "([combine:16x24:0,0=(" .. texture .. "):0,16=(" .. texture ..")".. texture_suff
|
|
||||||
if self.attack then
|
if self.attack then
|
||||||
texture = texture .. ")^vl_mobs_stalker_overlay_angry.png"
|
texture = texture .. ")^vl_mobs_stalker_overlay_angry.png"
|
||||||
else
|
else
|
||||||
|
@ -132,11 +131,7 @@ mcl_mobs.register_mob("mobs_mc:stalker", {
|
||||||
self:boom(mcl_util.get_object_center(self.object), self.explosion_strength)
|
self:boom(mcl_util.get_object_center(self.object), self.explosion_strength)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local new_texture = get_texture(self)
|
self.object:set_properties({textures={get_texture(self)}})
|
||||||
if self._stalker_texture ~= new_texture then
|
|
||||||
self.object:set_properties({textures={new_texture, "mobs_mc_empty.png"}})
|
|
||||||
self._stalker_texture = new_texture
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
on_die = function(self, pos, cmi_cause)
|
on_die = function(self, pos, cmi_cause)
|
||||||
-- Drop a random music disc when killed by skeleton or stray
|
-- Drop a random music disc when killed by skeleton or stray
|
||||||
|
@ -180,18 +175,7 @@ mcl_mobs.register_mob("mobs_mc:stalker", {
|
||||||
floats = 1,
|
floats = 1,
|
||||||
fear_height = 4,
|
fear_height = 4,
|
||||||
view_range = 16,
|
view_range = 16,
|
||||||
|
})
|
||||||
_on_after_convert = function(obj)
|
|
||||||
obj:set_properties({
|
|
||||||
visual_size = {x=2, y=2},
|
|
||||||
mesh = "vl_stalker.b3d",
|
|
||||||
textures = {
|
|
||||||
{get_texture({}),
|
|
||||||
"mobs_mc_empty.png"},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
}) -- END mcl_mobs.register_mob("mobs_mc:stalker", {
|
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:stalker_overloaded", {
|
mcl_mobs.register_mob("mobs_mc:stalker_overloaded", {
|
||||||
description = S("Overloaded Stalker"),
|
description = S("Overloaded Stalker"),
|
||||||
|
@ -320,8 +304,26 @@ mcl_mobs.register_mob("mobs_mc:stalker_overloaded", {
|
||||||
--Having trouble when fire is placed with lightning
|
--Having trouble when fire is placed with lightning
|
||||||
fire_resistant = true,
|
fire_resistant = true,
|
||||||
glow = 3,
|
glow = 3,
|
||||||
|
})
|
||||||
|
|
||||||
_on_after_convert = function(obj)
|
-- compat
|
||||||
|
minetest.register_entity("mobs_mc:creeper", {
|
||||||
|
on_activate = function(self, staticdata, dtime)
|
||||||
|
local obj = minetest.add_entity(self.object:get_pos(), "mobs_mc:stalker", staticdata)
|
||||||
|
obj:set_properties({
|
||||||
|
visual_size = {x=2, y=2},
|
||||||
|
mesh = "vl_stalker.b3d",
|
||||||
|
textures = {
|
||||||
|
{get_texture({}),
|
||||||
|
"mobs_mc_empty.png"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
self.object:remove()
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
minetest.register_entity("mobs_mc:creeper_charged", {
|
||||||
|
on_activate = function(self, staticdata, dtime)
|
||||||
|
local obj = minetest.add_entity(self.object:get_pos(), "mobs_mc:stalker_overloaded", staticdata)
|
||||||
obj:set_properties({
|
obj:set_properties({
|
||||||
visual_size = {x=2, y=2},
|
visual_size = {x=2, y=2},
|
||||||
mesh = "vl_stalker.b3d",
|
mesh = "vl_stalker.b3d",
|
||||||
|
@ -330,12 +332,9 @@ mcl_mobs.register_mob("mobs_mc:stalker_overloaded", {
|
||||||
AURA},
|
AURA},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
self.object:remove()
|
||||||
end,
|
end,
|
||||||
}) -- END mcl_mobs.register_mob("mobs_mc:stalker_overloaded", {
|
})
|
||||||
|
|
||||||
-- compat
|
|
||||||
mcl_mobs.register_conversion("mobs_mc:creeper", "mobs_mc:stalker")
|
|
||||||
mcl_mobs.register_conversion("mobs_mc:creeper_charged", "mobs_mc:stalker_overloaded")
|
|
||||||
|
|
||||||
mcl_mobs:spawn_specific(
|
mcl_mobs:spawn_specific(
|
||||||
"mobs_mc:stalker",
|
"mobs_mc:stalker",
|
||||||
|
|
|
@ -105,6 +105,8 @@ function mcl_raids.promote_to_raidcaptain(c) -- object
|
||||||
if cmi_cause and cmi_cause.type == "punch" and cmi_cause.puncher:is_player() then
|
if cmi_cause and cmi_cause.type == "punch" and cmi_cause.puncher:is_player() then
|
||||||
awards.unlock(cmi_cause.puncher:get_player_name(), "mcl:voluntary_exile")
|
awards.unlock(cmi_cause.puncher:get_player_name(), "mcl:voluntary_exile")
|
||||||
local lv = mcl_potions.get_effect_level(cmi_cause.puncher, "bad_omen")
|
local lv = mcl_potions.get_effect_level(cmi_cause.puncher, "bad_omen")
|
||||||
|
if not lv then lv = 0
|
||||||
|
else lv = lv.factor end
|
||||||
lv = math.max(5,lv + 1)
|
lv = math.max(5,lv + 1)
|
||||||
mcl_potions.give_effect_by_level("bad_omen", cmi_cause.puncher, lv, 6000)
|
mcl_potions.give_effect_by_level("bad_omen", cmi_cause.puncher, lv, 6000)
|
||||||
end
|
end
|
||||||
|
@ -309,7 +311,7 @@ mcl_events.register_event("raid",{
|
||||||
self.health_max = 1
|
self.health_max = 1
|
||||||
self.health = 0
|
self.health = 0
|
||||||
local lv = mcl_potions.get_effect_level(minetest.get_player_by_name(self.player), "bad_omen")
|
local lv = mcl_potions.get_effect_level(minetest.get_player_by_name(self.player), "bad_omen")
|
||||||
if lv > 1 then self.max_stage = 6 end
|
if lv and lv.factor and lv.factor > 1 then self.max_stage = 6 end
|
||||||
end,
|
end,
|
||||||
cond_progress = function(self)
|
cond_progress = function(self)
|
||||||
if not is_player_near(self) then return false end
|
if not is_player_near(self) then return false end
|
||||||
|
|
|
@ -678,7 +678,6 @@ local function make_formspec(name)
|
||||||
image_button[2.4,0.12;0.8,0.8;craftguide_search_icon.png;search;]
|
image_button[2.4,0.12;0.8,0.8;craftguide_search_icon.png;search;]
|
||||||
image_button[3.05,0.12;0.8,0.8;craftguide_clear_icon.png;clear;]
|
image_button[3.05,0.12;0.8,0.8;craftguide_clear_icon.png;clear;]
|
||||||
field_close_on_enter[filter;false]
|
field_close_on_enter[filter;false]
|
||||||
field_enter_after_edit[filter;true]
|
|
||||||
]]
|
]]
|
||||||
|
|
||||||
fs[#fs + 1] = fmt([[ tooltip[search;%s]
|
fs[#fs + 1] = fmt([[ tooltip[search;%s]
|
||||||
|
|
|
@ -126,9 +126,6 @@ S("• I: Show/hide inventory menu").."\n\n"..
|
||||||
S("Inventory interaction:").."\n"..
|
S("Inventory interaction:").."\n"..
|
||||||
S("See the entry “Basics > Inventory”.").."\n\n"..
|
S("See the entry “Basics > Inventory”.").."\n\n"..
|
||||||
|
|
||||||
S("Hunger/Eating:").."\n"..
|
|
||||||
S("• While holding food, hold the right mouse button (PC) or double-tap and hold the second tap (Android) to eat").."\n\n"..
|
|
||||||
|
|
||||||
S("Camera:").."\n"..
|
S("Camera:").."\n"..
|
||||||
S("• Z: Zoom").."\n"..
|
S("• Z: Zoom").."\n"..
|
||||||
S("• F7: Toggle camera mode").."\n\n"..
|
S("• F7: Toggle camera mode").."\n\n"..
|
||||||
|
|
|
@ -78,8 +78,6 @@ World interaction:=
|
||||||
• I: Show/hide inventory menu=
|
• I: Show/hide inventory menu=
|
||||||
Inventory interaction:=
|
Inventory interaction:=
|
||||||
See the entry “Basics > Inventory”.=
|
See the entry “Basics > Inventory”.=
|
||||||
Hunger/Eating:=
|
|
||||||
• While holding food, hold the right mouse button (PC) or double-tap and hold the second tap (Android) to eat=
|
|
||||||
Camera:=
|
Camera:=
|
||||||
• Z: Zoom=
|
• Z: Zoom=
|
||||||
• F7: Toggle camera mode=
|
• F7: Toggle camera mode=
|
||||||
|
|
|
@ -22,10 +22,10 @@ return {
|
||||||
"epCode",
|
"epCode",
|
||||||
"chmodsayshello",
|
"chmodsayshello",
|
||||||
"MrRar",
|
"MrRar",
|
||||||
|
"FossFanatic ",
|
||||||
"SmokeyDope",
|
"SmokeyDope",
|
||||||
"Faerraven / Michieal",
|
"Faerraven / Michieal",
|
||||||
"rudzik8",
|
"Codiac",
|
||||||
"teknomunk",
|
|
||||||
}},
|
}},
|
||||||
{S("Past Developers"), 0xF84355, {
|
{S("Past Developers"), 0xF84355, {
|
||||||
"jordan4ibanez",
|
"jordan4ibanez",
|
||||||
|
@ -36,11 +36,10 @@ return {
|
||||||
"NO11",
|
"NO11",
|
||||||
"SumianVoice",
|
"SumianVoice",
|
||||||
"PrairieWind",
|
"PrairieWind",
|
||||||
"FossFanatic",
|
|
||||||
"Codiac",
|
|
||||||
}},
|
}},
|
||||||
{S("Contributors"), 0x52FF00, {
|
{S("Contributors"), 0x52FF00, {
|
||||||
"RandomLegoBrick",
|
"RandomLegoBrick",
|
||||||
|
"rudzik8",
|
||||||
"Code-Sploit",
|
"Code-Sploit",
|
||||||
"aligator",
|
"aligator",
|
||||||
"Rootyjr",
|
"Rootyjr",
|
||||||
|
@ -132,17 +131,6 @@ return {
|
||||||
"Bakawun",
|
"Bakawun",
|
||||||
"JoseDouglas26",
|
"JoseDouglas26",
|
||||||
"Zasco",
|
"Zasco",
|
||||||
"PrWalterB",
|
|
||||||
"michaljmalinowski",
|
|
||||||
"nixnoxus",
|
|
||||||
"Potiron",
|
|
||||||
"Tuxilio",
|
|
||||||
"Impulse",
|
|
||||||
"Doods",
|
|
||||||
"SOS-Games",
|
|
||||||
"Bram",
|
|
||||||
"qoheniac",
|
|
||||||
"WillConker",
|
|
||||||
}},
|
}},
|
||||||
{S("Music"), 0xA60014, {
|
{S("Music"), 0xA60014, {
|
||||||
"Jordach for the jukebox music compilation from Big Freaking Dig",
|
"Jordach for the jukebox music compilation from Big Freaking Dig",
|
||||||
|
|
|
@ -106,6 +106,11 @@ minetest.register_on_mods_loaded(function()
|
||||||
end
|
end
|
||||||
if def.groups.brewitem then
|
if def.groups.brewitem then
|
||||||
local str = name
|
local str = name
|
||||||
|
if def.groups._mcl_potion == 1 then
|
||||||
|
local stack = ItemStack(name)
|
||||||
|
tt.reload_itemstack_description(stack)
|
||||||
|
str = stack:to_string()
|
||||||
|
end
|
||||||
table.insert(inventory_lists["brew"], str)
|
table.insert(inventory_lists["brew"], str)
|
||||||
nonmisc = true
|
nonmisc = true
|
||||||
end
|
end
|
||||||
|
@ -123,12 +128,14 @@ minetest.register_on_mods_loaded(function()
|
||||||
local stack = ItemStack(name)
|
local stack = ItemStack(name)
|
||||||
local potency = def._default_potent_level - 1
|
local potency = def._default_potent_level - 1
|
||||||
stack:get_meta():set_int("mcl_potions:potion_potent", potency)
|
stack:get_meta():set_int("mcl_potions:potion_potent", potency)
|
||||||
|
tt.reload_itemstack_description(stack)
|
||||||
table.insert(inventory_lists["brew"], stack:to_string())
|
table.insert(inventory_lists["brew"], stack:to_string())
|
||||||
end
|
end
|
||||||
if def.has_plus then
|
if def.has_plus then
|
||||||
local stack = ItemStack(name)
|
local stack = ItemStack(name)
|
||||||
local extend = def._default_extend_level
|
local extend = def._default_extend_level
|
||||||
stack:get_meta():set_int("mcl_potions:potion_plus", extend)
|
stack:get_meta():set_int("mcl_potions:potion_plus", extend)
|
||||||
|
tt.reload_itemstack_description(stack)
|
||||||
table.insert(inventory_lists["brew"], stack:to_string())
|
table.insert(inventory_lists["brew"], stack:to_string())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -137,17 +144,6 @@ minetest.register_on_mods_loaded(function()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Itemstack descriptions need to be reloaded separately, because tt invalidates minetest.registered_items iterators, somehow
|
|
||||||
-- (and pairs() uses said iterators internally)
|
|
||||||
-- TODO investigate the iterator invalidation, where does it happen?
|
|
||||||
for name, list in pairs(inventory_lists) do
|
|
||||||
for i=1, #list do
|
|
||||||
local stack = ItemStack(list[i])
|
|
||||||
tt.reload_itemstack_description(stack)
|
|
||||||
list[i] = stack:to_string()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for ench, def in pairs(mcl_enchanting.enchantments) do
|
for ench, def in pairs(mcl_enchanting.enchantments) do
|
||||||
local str = "mcl_enchanting:book_enchanted " .. ench .. " " .. def.max_level
|
local str = "mcl_enchanting:book_enchanted " .. ench .. " " .. def.max_level
|
||||||
if def.inv_tool_tab then
|
if def.inv_tool_tab then
|
||||||
|
|
|
@ -28,7 +28,6 @@ local function get_anvil_formspec(set_name)
|
||||||
|
|
||||||
"field[4.125,0.75;7.25,1;name;;" .. F(set_name) .. "]",
|
"field[4.125,0.75;7.25,1;name;;" .. F(set_name) .. "]",
|
||||||
"field_close_on_enter[name;false]",
|
"field_close_on_enter[name;false]",
|
||||||
"field_enter_after_edit[name;true]",
|
|
||||||
"set_focus[name;true]",
|
"set_focus[name;true]",
|
||||||
|
|
||||||
mcl_formspec.get_itemslot_bg_v4(1.625, 2.6, 1, 1),
|
mcl_formspec.get_itemslot_bg_v4(1.625, 2.6, 1, 1),
|
||||||
|
|
|
@ -84,9 +84,6 @@ local pallete_order = {
|
||||||
pane_magenta_flat = 16,
|
pane_magenta_flat = 16,
|
||||||
pane_magenta = 16
|
pane_magenta = 16
|
||||||
}
|
}
|
||||||
local EFFECT_CONVERSIONS = {
|
|
||||||
strenght = "strength"
|
|
||||||
}
|
|
||||||
|
|
||||||
local function get_beacon_beam(glass_nodename)
|
local function get_beacon_beam(glass_nodename)
|
||||||
if glass_nodename == "air" then return 0 end
|
if glass_nodename == "air" then return 0 end
|
||||||
|
@ -215,30 +212,24 @@ local function effect_player(effect,pos,power_level, effect_level,player)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function apply_effects_to_all_players(pos)
|
local function apply_effects_to_all_players(pos)
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
local effect_string = meta:get_string("effect")
|
local effect_string = meta:get_string("effect")
|
||||||
local effect_level = meta:get_int("effect_level")
|
local effect_level = meta:get_int("effect_level")
|
||||||
|
|
||||||
local power_level = beacon_blockcheck(pos)
|
local power_level = beacon_blockcheck(pos)
|
||||||
|
|
||||||
local new_effect_string = EFFECT_CONVERSIONS[effect_string]
|
if effect_level == 2 and power_level < 4 then --no need to run loops when beacon is in an invalid setup :P
|
||||||
if new_effect_string then
|
|
||||||
effect_string = new_effect_string
|
|
||||||
meta:set_string("effect", effect_string)
|
|
||||||
end
|
|
||||||
|
|
||||||
if effect_string == "" or ( effect_level == 2 and power_level < 4 ) then --no need to run loops when beacon is in an invalid setup :P
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local beacon_distance = (power_level + 1) * 10
|
local beacon_distance = (power_level + 1) * 10
|
||||||
|
|
||||||
for _, player in pairs(minetest.get_connected_players()) do
|
for _, player in pairs(minetest.get_connected_players()) do
|
||||||
if vector.distance(pos, player:get_pos()) <= beacon_distance then
|
if vector.distance(pos, player:get_pos()) <= beacon_distance then
|
||||||
if not clear_obstructed_beam(pos) then
|
if not clear_obstructed_beam(pos) then
|
||||||
effect_player(effect_string, pos, power_level, effect_level, player)
|
effect_player(effect_string, pos, power_level, effect_level, player)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -253,7 +244,8 @@ minetest.register_node("mcl_beacons:beacon", {
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
local inv = meta:get_inventory()
|
local inv = meta:get_inventory()
|
||||||
inv:set_size("input", 1)
|
inv:set_size("input", 1)
|
||||||
meta:set_string("formspec", formspec_string)
|
local form = formspec_string
|
||||||
|
meta:set_string("formspec", form)
|
||||||
end,
|
end,
|
||||||
on_destruct = function(pos)
|
on_destruct = function(pos)
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
|
@ -265,7 +257,7 @@ minetest.register_node("mcl_beacons:beacon", {
|
||||||
remove_beacon_beam(pos)
|
remove_beacon_beam(pos)
|
||||||
end,
|
end,
|
||||||
on_receive_fields = function(pos, formname, fields, sender)
|
on_receive_fields = function(pos, formname, fields, sender)
|
||||||
if fields.swiftness or fields.regeneration or fields.leaping or fields.strength
|
if fields.swiftness or fields.regeneration or fields.leaping or fields.strenght
|
||||||
or fields.haste or fields.resistance or fields.absorption or fields.slow_falling then
|
or fields.haste or fields.resistance or fields.absorption or fields.slow_falling then
|
||||||
local sender_name = sender:get_player_name()
|
local sender_name = sender:get_player_name()
|
||||||
local power_level = beacon_blockcheck(pos)
|
local power_level = beacon_blockcheck(pos)
|
||||||
|
@ -329,7 +321,7 @@ minetest.register_node("mcl_beacons:beacon", {
|
||||||
end
|
end
|
||||||
minetest.get_meta(pos):set_string("effect","resistance")
|
minetest.get_meta(pos):set_string("effect","resistance")
|
||||||
successful = true
|
successful = true
|
||||||
elseif fields.strength and power_level >= 3 then
|
elseif fields.strenght and power_level >= 3 then
|
||||||
if power_level == 4 then
|
if power_level == 4 then
|
||||||
minetest.get_meta(pos):set_int("effect_level",2)
|
minetest.get_meta(pos):set_int("effect_level",2)
|
||||||
else
|
else
|
||||||
|
@ -371,6 +363,7 @@ minetest.register_node("mcl_beacons:beacon", {
|
||||||
node = minetest.get_node({x=pos.x,y=y,z=pos.z})
|
node = minetest.get_node({x=pos.x,y=y,z=pos.z})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
if minetest.get_item_group(node.name, "glass") ~= 0 or minetest.get_item_group(node.name,"material_glass") ~= 0 then
|
if minetest.get_item_group(node.name, "glass") ~= 0 or minetest.get_item_group(node.name,"material_glass") ~= 0 then
|
||||||
beam_palette_index = get_beacon_beam(node.name)
|
beam_palette_index = get_beacon_beam(node.name)
|
||||||
end
|
end
|
||||||
|
@ -433,15 +426,6 @@ minetest.register_abm{
|
||||||
apply_effects_to_all_players(pos)
|
apply_effects_to_all_players(pos)
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
minetest.register_lbm({
|
|
||||||
label = "Update beacon formspecs (0.87.1)",
|
|
||||||
name = "mcl_beacons:update_beacon_formspecs_0_87_1",
|
|
||||||
nodenames = { "mcl_beacons:beacon" },
|
|
||||||
action = function(pos)
|
|
||||||
local meta = minetest.get_meta(pos)
|
|
||||||
meta:set_string("formspec", formspec_string)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = "mcl_beacons:beacon",
|
output = "mcl_beacons:beacon",
|
||||||
|
|
|
@ -225,9 +225,11 @@ local function update_formspecs(finished, players)
|
||||||
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")).."]"
|
||||||
local bg_presleep = "bgcolor[#00000080;true]"
|
local bg_presleep = "bgcolor[#00000080;true]"
|
||||||
local bg_sleep = "bgcolor[#000000FF;true]"
|
local bg_sleep = "bgcolor[#000000FF;true]"
|
||||||
local chatbox = "field[0.3,4.5;10,1;chatmessage;"..F(S("Chat:"))..";]"
|
local chatbox = "field[0.2,4.5;9,1;chatmessage;"..F(S("Chat:"))..";]"
|
||||||
local chatsubmit = "button[10,3.73;2,2;chatsubmit;"..F(S("Send")).."]"
|
local chatsubmit = "button[9.2,3.75;1,2;chatsubmit;"..F(S("send!")).."]"
|
||||||
local defaultmessagebutton = "button[10.98,2.93;1,2;defaultmessage;zzZ]"
|
local defaultmessagebutton = "button[10.2,3.75;1,2;defaultmessage;zzZzzZ]"
|
||||||
|
|
||||||
|
form_n = form_n .. chatbox .. chatsubmit --because these should be in the formspec in ANY case, they might as well be added here already
|
||||||
|
|
||||||
if finished then
|
if finished then
|
||||||
for name,_ in pairs(mcl_beds.player) do
|
for name,_ in pairs(mcl_beds.player) do
|
||||||
|
@ -235,7 +237,6 @@ local function update_formspecs(finished, players)
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
elseif not is_sp then
|
elseif not is_sp then
|
||||||
form_n = form_n .. chatbox .. chatsubmit --because these should be in the formspec in ANY case, they might as well be added here already
|
|
||||||
local text = S("Players in bed: @1/@2", player_in_bed, ges)
|
local text = S("Players in bed: @1/@2", player_in_bed, ges)
|
||||||
if not night_skip then
|
if not night_skip then
|
||||||
text = text .. "\n" .. S("Note: Night skip is disabled.")
|
text = text .. "\n" .. S("Note: Night skip is disabled.")
|
||||||
|
|
|
@ -196,7 +196,7 @@ end
|
||||||
mcl_stairs.register_stair_and_slab("blackstone", "mcl_blackstone:blackstone",
|
mcl_stairs.register_stair_and_slab("blackstone", "mcl_blackstone:blackstone",
|
||||||
{cracky=3, pickaxey=1, material_stone=1},
|
{cracky=3, pickaxey=1, material_stone=1},
|
||||||
{"mcl_blackstone_top.png", "mcl_blackstone_top.png", "mcl_blackstone_side.png"},
|
{"mcl_blackstone_top.png", "mcl_blackstone_top.png", "mcl_blackstone_side.png"},
|
||||||
S("Blackstone Stair"),
|
S("Blackstone Stairs"),
|
||||||
S("Blackstone Slab"),
|
S("Blackstone Slab"),
|
||||||
mcl_sounds.node_sound_stone_defaults(), 6, 2,
|
mcl_sounds.node_sound_stone_defaults(), 6, 2,
|
||||||
S("Double Blackstone Slab"), nil)
|
S("Double Blackstone Slab"), nil)
|
||||||
|
@ -204,7 +204,7 @@ mcl_stairs.register_stair_and_slab("blackstone", "mcl_blackstone:blackstone",
|
||||||
mcl_stairs.register_stair_and_slab("blackstone_polished", "mcl_blackstone:blackstone_polished",
|
mcl_stairs.register_stair_and_slab("blackstone_polished", "mcl_blackstone:blackstone_polished",
|
||||||
{cracky=3, pickaxey=1, material_stone=1},
|
{cracky=3, pickaxey=1, material_stone=1},
|
||||||
{"mcl_blackstone_polished.png"},
|
{"mcl_blackstone_polished.png"},
|
||||||
S("Polished Blackstone Stair"),
|
S("Polished Blackstone Stairs"),
|
||||||
S("Polished Blackstone Slab"),
|
S("Polished Blackstone Slab"),
|
||||||
mcl_sounds.node_sound_stone_defaults(), 6, 2,
|
mcl_sounds.node_sound_stone_defaults(), 6, 2,
|
||||||
S("Double Polished Blackstone Slab"), nil)
|
S("Double Polished Blackstone Slab"), nil)
|
||||||
|
@ -212,7 +212,7 @@ mcl_stairs.register_stair_and_slab("blackstone_polished", "mcl_blackstone:blacks
|
||||||
mcl_stairs.register_stair_and_slab("blackstone_chiseled_polished", "mcl_blackstone:blackstone_chiseled_polished",
|
mcl_stairs.register_stair_and_slab("blackstone_chiseled_polished", "mcl_blackstone:blackstone_chiseled_polished",
|
||||||
{cracky=3, pickaxey=1, material_stone=1},
|
{cracky=3, pickaxey=1, material_stone=1},
|
||||||
{"mcl_blackstone_chiseled_polished.png"},
|
{"mcl_blackstone_chiseled_polished.png"},
|
||||||
S("Chiseled Polished Blackstone Stair"),
|
S("Chiseled Polished Blackstone Stairs"),
|
||||||
S("Chiseled Polished Blackstone Slab"),
|
S("Chiseled Polished Blackstone Slab"),
|
||||||
mcl_sounds.node_sound_stone_defaults(), 6, 2,
|
mcl_sounds.node_sound_stone_defaults(), 6, 2,
|
||||||
S("Double Chiseled Polished Blackstone Slab"), nil)
|
S("Double Chiseled Polished Blackstone Slab"), nil)
|
||||||
|
@ -220,10 +220,10 @@ mcl_stairs.register_stair_and_slab("blackstone_chiseled_polished", "mcl_blacksto
|
||||||
mcl_stairs.register_stair_and_slab("blackstone_brick_polished", "mcl_blackstone:blackstone_brick_polished",
|
mcl_stairs.register_stair_and_slab("blackstone_brick_polished", "mcl_blackstone:blackstone_brick_polished",
|
||||||
{cracky=3, pickaxey=1, material_stone=1},
|
{cracky=3, pickaxey=1, material_stone=1},
|
||||||
{"mcl_blackstone_polished_bricks.png"},
|
{"mcl_blackstone_polished_bricks.png"},
|
||||||
S("Polished Blackstone Brick Stair"),
|
S("Polished Blackstone Brick Stair Stairs"),
|
||||||
S("Polished Blackstone Brick Slab"),
|
S("Polished Blackstone Brick Stair Slab"),
|
||||||
mcl_sounds.node_sound_stone_defaults(), 6, 2,
|
mcl_sounds.node_sound_stone_defaults(), 6, 2,
|
||||||
S("Double Polished Blackstone Brick Slab"), nil)
|
S("Double Polished Blackstone Brick Stair Slab"), nil)
|
||||||
|
|
||||||
--Wall
|
--Wall
|
||||||
mcl_walls.register_wall(
|
mcl_walls.register_wall(
|
||||||
|
|
|
@ -9,10 +9,10 @@ Blackstone Slab=Schwarzstein Stufe
|
||||||
Polished Blackstone Slab=Polierte Schwarzstein Stufe
|
Polished Blackstone Slab=Polierte Schwarzstein Stufe
|
||||||
Chiseled Polished Blackstone Slab=Gemeißelte Polierte Schwarzstein Stufe
|
Chiseled Polished Blackstone Slab=Gemeißelte Polierte Schwarzstein Stufe
|
||||||
Polished Blackstone Brick Slab=Polierte Schwarzsteinziegel Stufe
|
Polished Blackstone Brick Slab=Polierte Schwarzsteinziegel Stufe
|
||||||
Blackstone Stair=Schwarzstein Treppe
|
Blackstone Stairs=Schwarzstein Treppe
|
||||||
Polished Blackstone Stair=Polierte Schwarzstein Treppe
|
Polished Blackstone Stairs=Polierte Schwarzstein Treppe
|
||||||
Chiseled Polished Blackstone Stair=Gemeißelte Polierte Schwarzstein Treppe
|
Chiseled Polished Blackstone Stairs=Gemeißelte Polierte Schwarzstein Treppe
|
||||||
Polished Blackstone Brick Stair=Polierte Schwarzsteinziegel Treppe
|
Polished Blackstone Brick Stairs=Polierte Schwarzsteinziegel Treppe
|
||||||
Quartz Bricks=Quartz Ziegel
|
Quartz Bricks=Quartz Ziegel
|
||||||
Soul Torch=Seelenfakel
|
Soul Torch=Seelenfakel
|
||||||
Soul Lantern=Seelenlaterne
|
Soul Lantern=Seelenlaterne
|
||||||
|
|
|
@ -56,7 +56,65 @@ S("Arrows might get stuck on solid blocks and can be retrieved again. They are a
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
local ARROW_ENTITY={
|
-- Destroy arrow entity self at pos and drops it as an item
|
||||||
|
local function spawn_item(self, pos)
|
||||||
|
if not minetest.is_creative_enabled("") then
|
||||||
|
local item = minetest.add_item(pos, "mcl_bows:arrow")
|
||||||
|
item:set_velocity(vector.new(0, 0, 0))
|
||||||
|
item:set_yaw(self.object:get_yaw())
|
||||||
|
end
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function stuck_arrow_on_step(self, dtime)
|
||||||
|
self._stucktimer = self._stucktimer + dtime
|
||||||
|
self._stuckrechecktimer = self._stuckrechecktimer + dtime
|
||||||
|
if self._stucktimer > ARROW_TIMEOUT then
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
|
||||||
|
-- Drop arrow as item when it is no longer stuck
|
||||||
|
-- FIXME: Arrows are a bit slow to react and continue to float in mid air for a few seconds.
|
||||||
|
if self._stuckrechecktimer > STUCK_RECHECK_TIME then
|
||||||
|
local stuckin_def
|
||||||
|
if self._stuckin then
|
||||||
|
stuckin_def = minetest.registered_nodes[minetest.get_node(self._stuckin).name]
|
||||||
|
end
|
||||||
|
-- TODO: In MC, arrow just falls down without turning into an item
|
||||||
|
if stuckin_def and stuckin_def.walkable == false then
|
||||||
|
spawn_item(self, pos)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self._stuckrechecktimer = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pickup arrow if player is nearby (not in Creative Mode)
|
||||||
|
local objects = minetest.get_objects_inside_radius(pos, 1)
|
||||||
|
for _,obj in ipairs(objects) do
|
||||||
|
if obj:is_player() then
|
||||||
|
if self._collectable and not minetest.is_creative_enabled(obj:get_player_name()) then
|
||||||
|
if obj:get_inventory():room_for_item("main", "mcl_bows:arrow") then
|
||||||
|
obj:get_inventory():add_item("main", "mcl_bows:arrow")
|
||||||
|
minetest.sound_play("item_drop_pickup", {
|
||||||
|
pos = pos,
|
||||||
|
max_hear_distance = 16,
|
||||||
|
gain = 1.0,
|
||||||
|
}, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
vl_projectile.register("mcl_bows:arrow_entity", {
|
||||||
physical = true,
|
physical = true,
|
||||||
pointable = false,
|
pointable = false,
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
|
@ -67,7 +125,10 @@ local ARROW_ENTITY={
|
||||||
collide_with_objects = false,
|
collide_with_objects = false,
|
||||||
_fire_damage_resistant = true,
|
_fire_damage_resistant = true,
|
||||||
|
|
||||||
_lastpos={},
|
_save_fields = {
|
||||||
|
"last_pos", "startpos", "damage", "is_critical", "stuck", "stuckin", "stuckin_player",
|
||||||
|
},
|
||||||
|
|
||||||
_startpos=nil,
|
_startpos=nil,
|
||||||
_damage=1, -- Damage on impact
|
_damage=1, -- Damage on impact
|
||||||
_is_critical=false, -- Whether this arrow would deal critical damage
|
_is_critical=false, -- Whether this arrow would deal critical damage
|
||||||
|
@ -81,282 +142,41 @@ local ARROW_ENTITY={
|
||||||
_blocked = false,
|
_blocked = false,
|
||||||
_viscosity=0, -- Viscosity of node the arrow is currently in
|
_viscosity=0, -- Viscosity of node the arrow is currently in
|
||||||
_deflection_cooloff=0, -- Cooloff timer after an arrow deflection, to prevent many deflections in quick succession
|
_deflection_cooloff=0, -- Cooloff timer after an arrow deflection, to prevent many deflections in quick succession
|
||||||
}
|
|
||||||
|
|
||||||
-- Destroy arrow entity self at pos and drops it as an item
|
_vl_projectile = {
|
||||||
local function spawn_item(self, pos)
|
survive_collision = true,
|
||||||
if not minetest.is_creative_enabled("") then
|
sticks_in_players = true,
|
||||||
local item = minetest.add_item(pos, "mcl_bows:arrow")
|
damage_groups = function(self)
|
||||||
item:set_velocity(vector.new(0, 0, 0))
|
return { fleshy = self._damage }
|
||||||
item:set_yaw(self.object:get_yaw())
|
end,
|
||||||
end
|
behaviors = {
|
||||||
mcl_burning.extinguish(self.object)
|
vl_projectile.collides_with_solids,
|
||||||
self.object:remove()
|
vl_projectile.raycast_collides_with_entities,
|
||||||
end
|
},
|
||||||
|
allow_punching = function(self, entity_def, projectile_def, entity)
|
||||||
|
local lua = entity:get_luaentity()
|
||||||
|
if lua and lua.name == "mobs_mc:rover" then return false end
|
||||||
|
|
||||||
local function damage_particles(pos, is_critical)
|
return true
|
||||||
if is_critical then
|
end,
|
||||||
minetest.add_particlespawner({
|
sounds = {
|
||||||
amount = 15,
|
on_entity_collision = function(self, _, _, obj)
|
||||||
time = 0.1,
|
if obj:is_player() then
|
||||||
minpos = vector.offset(pos, -0.5, -0.5, -0.5),
|
return {{name="mcl_bows_hit_player", gain=0.1}, {to_player=self._shooter:get_player_name()}, true}
|
||||||
maxpos = vector.offset(pos, 0.5, 0.5, 0.5),
|
|
||||||
minvel = vector.new(-0.1, -0.1, -0.1),
|
|
||||||
maxvel = vector.new(0.1, 0.1, 0.1),
|
|
||||||
minexptime = 1,
|
|
||||||
maxexptime = 2,
|
|
||||||
minsize = 1.5,
|
|
||||||
maxsize = 1.5,
|
|
||||||
collisiondetection = false,
|
|
||||||
vertical = false,
|
|
||||||
texture = "mcl_particles_crit.png^[colorize:#bc7a57:127",
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function ARROW_ENTITY.on_step(self, dtime)
|
|
||||||
mcl_burning.tick(self.object, dtime, self)
|
|
||||||
-- mcl_burning.tick may remove object immediately
|
|
||||||
if not self.object:get_pos() then return end
|
|
||||||
|
|
||||||
self._time_in_air = self._time_in_air + .001
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local dpos = vector.round(vector.new(pos)) -- digital pos
|
|
||||||
local node = minetest.get_node(dpos)
|
|
||||||
|
|
||||||
if self._stuck then
|
|
||||||
self._stucktimer = self._stucktimer + dtime
|
|
||||||
self._stuckrechecktimer = self._stuckrechecktimer + dtime
|
|
||||||
if self._stucktimer > ARROW_TIMEOUT then
|
|
||||||
mcl_burning.extinguish(self.object)
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
-- Drop arrow as item when it is no longer stuck
|
|
||||||
-- FIXME: Arrows are a bit slow to react and continue to float in mid air for a few seconds.
|
|
||||||
if self._stuckrechecktimer > STUCK_RECHECK_TIME then
|
|
||||||
local stuckin_def
|
|
||||||
if self._stuckin then
|
|
||||||
stuckin_def = minetest.registered_nodes[minetest.get_node(self._stuckin).name]
|
|
||||||
end
|
|
||||||
-- TODO: In MC, arrow just falls down without turning into an item
|
|
||||||
if stuckin_def and stuckin_def.walkable == false then
|
|
||||||
spawn_item(self, pos)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
self._stuckrechecktimer = 0
|
|
||||||
end
|
|
||||||
-- Pickup arrow if player is nearby (not in Creative Mode)
|
|
||||||
local objects = minetest.get_objects_inside_radius(pos, 1)
|
|
||||||
for _,obj in ipairs(objects) do
|
|
||||||
if obj:is_player() then
|
|
||||||
if self._collectable and not minetest.is_creative_enabled(obj:get_player_name()) then
|
|
||||||
if obj:get_inventory():room_for_item("main", "mcl_bows:arrow") then
|
|
||||||
obj:get_inventory():add_item("main", "mcl_bows:arrow")
|
|
||||||
minetest.sound_play("item_drop_pickup", {
|
|
||||||
pos = pos,
|
|
||||||
max_hear_distance = 16,
|
|
||||||
gain = 1.0,
|
|
||||||
}, true)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
mcl_burning.extinguish(self.object)
|
|
||||||
self.object:remove()
|
return {{name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true}
|
||||||
return
|
|
||||||
end
|
end
|
||||||
end
|
},
|
||||||
|
on_collide_with_solid = function(self, pos, node, node_def)
|
||||||
|
local def = node_def
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
|
local dpos = vector.round(vector.new(pos)) -- digital pos
|
||||||
|
|
||||||
-- Check for object "collision". Done every tick (hopefully this is not too stressing)
|
|
||||||
else
|
|
||||||
|
|
||||||
if self._damage >= 9 and self._in_player == false then
|
|
||||||
minetest.add_particlespawner({
|
|
||||||
amount = 20,
|
|
||||||
time = .2,
|
|
||||||
minpos = vector.new(0,0,0),
|
|
||||||
maxpos = vector.new(0,0,0),
|
|
||||||
minvel = vector.new(-0.1,-0.1,-0.1),
|
|
||||||
maxvel = vector.new(0.1,0.1,0.1),
|
|
||||||
minexptime = 0.5,
|
|
||||||
maxexptime = 0.5,
|
|
||||||
minsize = 2,
|
|
||||||
maxsize = 2,
|
|
||||||
attached = self.object,
|
|
||||||
collisiondetection = false,
|
|
||||||
vertical = false,
|
|
||||||
texture = "mobs_mc_arrow_particle.png",
|
|
||||||
glow = 1,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local closest_object
|
|
||||||
local closest_distance
|
|
||||||
|
|
||||||
if self._deflection_cooloff > 0 then
|
|
||||||
self._deflection_cooloff = self._deflection_cooloff - dtime
|
|
||||||
end
|
|
||||||
|
|
||||||
local arrow_dir = self.object:get_velocity()
|
|
||||||
--create a raycast from the arrow based on the velocity of the arrow to deal with lag
|
|
||||||
local raycast = minetest.raycast(pos, vector.add(pos, vector.multiply(arrow_dir, 0.1)), true, false)
|
|
||||||
for hitpoint in raycast do
|
|
||||||
if hitpoint.type == "object" then
|
|
||||||
-- find the closest object that is in the way of the arrow
|
|
||||||
local ok = false
|
|
||||||
if hitpoint.ref:is_player() and enable_pvp then
|
|
||||||
ok = true
|
|
||||||
elseif not hitpoint.ref:is_player() and hitpoint.ref:get_luaentity() then
|
|
||||||
if (hitpoint.ref:get_luaentity().is_mob or hitpoint.ref:get_luaentity()._hittable_by_projectile) then
|
|
||||||
ok = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if ok then
|
|
||||||
local dist = vector.distance(hitpoint.ref:get_pos(), pos)
|
|
||||||
if not closest_object or not closest_distance then
|
|
||||||
closest_object = hitpoint.ref
|
|
||||||
closest_distance = dist
|
|
||||||
elseif dist < closest_distance then
|
|
||||||
closest_object = hitpoint.ref
|
|
||||||
closest_distance = dist
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if closest_object then
|
|
||||||
local obj = closest_object
|
|
||||||
local is_player = obj:is_player()
|
|
||||||
local lua = obj:get_luaentity()
|
|
||||||
if obj == self._shooter and self._time_in_air > 1.02 or obj ~= self._shooter and (is_player or (lua and (lua.is_mob or lua._hittable_by_projectile))) then
|
|
||||||
if obj:get_hp() > 0 then
|
|
||||||
-- Check if there is no solid node between arrow and object
|
|
||||||
local ray = minetest.raycast(self.object:get_pos(), obj:get_pos(), true)
|
|
||||||
for pointed_thing in ray do
|
|
||||||
if pointed_thing.type == "object" and pointed_thing.ref == closest_object then
|
|
||||||
-- Target reached! We can proceed now.
|
|
||||||
break
|
|
||||||
elseif pointed_thing.type == "node" then
|
|
||||||
local nn = minetest.get_node(minetest.get_pointed_thing_position(pointed_thing)).name
|
|
||||||
local def = minetest.registered_nodes[nn]
|
|
||||||
if (not def) or def.walkable then
|
|
||||||
-- There's a node in the way. Delete arrow without damage
|
|
||||||
mcl_burning.extinguish(self.object)
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Punch target object but avoid hurting enderman.
|
|
||||||
if not lua or lua.name ~= "mobs_mc:rover" then
|
|
||||||
if not self._in_player then
|
|
||||||
damage_particles(vector.add(pos, vector.multiply(self.object:get_velocity(), 0.1)), self._is_critical)
|
|
||||||
end
|
|
||||||
if mcl_burning.is_burning(self.object) then
|
|
||||||
mcl_burning.set_on_fire(obj, 5)
|
|
||||||
end
|
|
||||||
if not self._in_player and not self._blocked then
|
|
||||||
obj:punch(self.object, 1.0, {
|
|
||||||
full_punch_interval=1.0,
|
|
||||||
damage_groups={fleshy=self._damage},
|
|
||||||
}, self.object:get_velocity())
|
|
||||||
if obj:is_player() then
|
|
||||||
if not mcl_shields.is_blocking(obj) then
|
|
||||||
local placement
|
|
||||||
self._placement = math.random(1, 2)
|
|
||||||
if self._placement == 1 then
|
|
||||||
placement = "front"
|
|
||||||
else
|
|
||||||
placement = "back"
|
|
||||||
end
|
|
||||||
self._in_player = true
|
|
||||||
if self._placement == 2 then
|
|
||||||
self._rotation_station = 90
|
|
||||||
else
|
|
||||||
self._rotation_station = -90
|
|
||||||
end
|
|
||||||
self._y_position = random_arrow_positions("y", placement)
|
|
||||||
self._x_position = random_arrow_positions("x", placement)
|
|
||||||
if self._y_position > 6 and self._x_position < 2 and self._x_position > -2 then
|
|
||||||
self._attach_parent = "Head"
|
|
||||||
self._y_position = self._y_position - 6
|
|
||||||
elseif self._x_position > 2 then
|
|
||||||
self._attach_parent = "Arm_Right"
|
|
||||||
self._y_position = self._y_position - 3
|
|
||||||
self._x_position = self._x_position - 2
|
|
||||||
elseif self._x_position < -2 then
|
|
||||||
self._attach_parent = "Arm_Left"
|
|
||||||
self._y_position = self._y_position - 3
|
|
||||||
self._x_position = self._x_position + 2
|
|
||||||
else
|
|
||||||
self._attach_parent = "Body"
|
|
||||||
end
|
|
||||||
self._z_rotation = math.random(-30, 30)
|
|
||||||
self._y_rotation = math.random( -30, 30)
|
|
||||||
self.object:set_attach(
|
|
||||||
obj, self._attach_parent,
|
|
||||||
vector.new(self._x_position, self._y_position, random_arrow_positions("z", placement)),
|
|
||||||
vector.new(0, self._rotation_station + self._y_rotation, self._z_rotation)
|
|
||||||
)
|
|
||||||
else
|
|
||||||
self._blocked = true
|
|
||||||
self.object:set_velocity(vector.multiply(self.object:get_velocity(), -0.25))
|
|
||||||
end
|
|
||||||
minetest.after(150, function()
|
|
||||||
self.object:remove()
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
self.object:remove()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
if is_player then
|
|
||||||
if self._shooter and self._shooter:is_player() and not self._in_player and not self._blocked then
|
|
||||||
-- “Ding” sound for hitting another player
|
|
||||||
minetest.sound_play({name="mcl_bows_hit_player", gain=0.1}, {to_player=self._shooter:get_player_name()}, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if lua then
|
|
||||||
local entity_name = lua.name
|
|
||||||
-- Achievement for hitting skeleton, wither skeleton or stray (TODO) with an arrow at least 50 meters away
|
|
||||||
-- NOTE: Range has been reduced because mobs unload much earlier than that ... >_>
|
|
||||||
-- TODO: This achievement should be given for the kill, not just a hit
|
|
||||||
if self._shooter and self._shooter:is_player() and vector.distance(pos, self._startpos) >= 20 then
|
|
||||||
if mod_awards and (entity_name == "mobs_mc:skeleton" or entity_name == "mobs_mc:stray" or entity_name == "mobs_mc:witherskeleton") then
|
|
||||||
awards.unlock(self._shooter:get_player_name(), "mcl:snipeSkeleton")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not self._in_player and not self._blocked then
|
|
||||||
minetest.sound_play({name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not obj:is_player() then
|
|
||||||
mcl_burning.extinguish(self.object)
|
|
||||||
if self._piercing == 0 then
|
|
||||||
self.object:remove()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check for node collision
|
|
||||||
if self._lastpos.x~=nil and not self._stuck then
|
|
||||||
local def = minetest.registered_nodes[node.name]
|
|
||||||
local vel = self.object:get_velocity()
|
|
||||||
-- Arrow has stopped in one axis, so it probably hit something.
|
|
||||||
-- This detection is a bit clunky, but sadly, MT does not offer a direct collision detection for us. :-(
|
|
||||||
if (math.abs(vel.x) < 0.0001) or (math.abs(vel.z) < 0.0001) or (math.abs(vel.y) < 0.00001) then
|
|
||||||
-- Check for the node to which the arrow is pointing
|
-- Check for the node to which the arrow is pointing
|
||||||
local dir
|
local dir
|
||||||
if math.abs(vel.y) < 0.00001 then
|
if math.abs(vel.y) < 0.00001 then
|
||||||
if self._lastpos.y < pos.y then
|
if self._last_pos.y < pos.y then
|
||||||
dir = vector.new(0, 1, 0)
|
dir = vector.new(0, 1, 0)
|
||||||
else
|
else
|
||||||
dir = vector.new(0, -1, 0)
|
dir = vector.new(0, -1, 0)
|
||||||
|
@ -380,50 +200,147 @@ function ARROW_ENTITY.on_step(self, dtime)
|
||||||
-- Reset deflection cooloff timer to prevent many deflections happening in quick succession
|
-- Reset deflection cooloff timer to prevent many deflections happening in quick succession
|
||||||
self._deflection_cooloff = 1.0
|
self._deflection_cooloff = 1.0
|
||||||
end
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Node was walkable, make arrow stuck
|
||||||
|
self._stuck = true
|
||||||
|
self._stucktimer = 0
|
||||||
|
self._stuckrechecktimer = 0
|
||||||
|
|
||||||
|
self.object:set_velocity(vector.new(0, 0, 0))
|
||||||
|
self.object:set_acceleration(vector.new(0, 0, 0))
|
||||||
|
|
||||||
|
minetest.sound_play({name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true)
|
||||||
|
|
||||||
|
if mcl_burning.is_burning(self.object) and snode.name == "mcl_tnt:tnt" then
|
||||||
|
tnt.ignite(self._stuckin)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Ignite Campfires
|
||||||
|
if mod_campfire and mcl_burning.is_burning(self.object) and minetest.get_item_group(snode.name, "campfire") ~= 0 then
|
||||||
|
mcl_campfires.light_campfire(self._stuckin)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Activate target
|
||||||
|
if mod_target and snode.name == "mcl_target:target_off" then
|
||||||
|
mcl_target.hit(self._stuckin, 1) --10 redstone ticks
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Push the button! Push, push, push the button!
|
||||||
|
if mod_button and minetest.get_item_group(node.name, "button") > 0 and minetest.get_item_group(node.name, "button_push_by_arrow") == 1 then
|
||||||
|
local bdir = minetest.wallmounted_to_dir(node.param2)
|
||||||
|
-- Check the button orientation
|
||||||
|
if vector.equals(vector.add(dpos, bdir), self._stuckin) then
|
||||||
|
mesecon.push_button(dpos, node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
on_collide_with_entity = function(self, pos, obj)
|
||||||
|
local is_player = obj:is_player()
|
||||||
|
local lua = obj:get_luaentity()
|
||||||
|
|
||||||
|
-- Make sure collision is valid
|
||||||
|
if obj == self._shooter then
|
||||||
|
if self._time_in_air < 1.02 then return end
|
||||||
else
|
else
|
||||||
|
if not (is_player or (lua and (lua.is_mob or lua._hittable_by_projectile))) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Node was walkable, make arrow stuck
|
if obj:get_hp() > 0 then
|
||||||
self._stuck = true
|
-- Check if there is no solid node between arrow and object
|
||||||
self._stucktimer = 0
|
-- TODO: remove. this code should never occur if vl_projectile is working correctly
|
||||||
self._stuckrechecktimer = 0
|
local ray = minetest.raycast(self.object:get_pos(), obj:get_pos(), true)
|
||||||
|
for pointed_thing in ray do
|
||||||
self.object:set_velocity(vector.new(0, 0, 0))
|
if pointed_thing.type == "object" and pointed_thing.ref == obj then
|
||||||
self.object:set_acceleration(vector.new(0, 0, 0))
|
-- Target reached! We can proceed now.
|
||||||
|
break
|
||||||
minetest.sound_play({name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true)
|
elseif pointed_thing.type == "node" then
|
||||||
|
local nn = minetest.get_node(minetest.get_pointed_thing_position(pointed_thing)).name
|
||||||
if mcl_burning.is_burning(self.object) and snode.name == "mcl_tnt:tnt" then
|
local def = minetest.registered_nodes[nn]
|
||||||
tnt.ignite(self._stuckin)
|
if (not def) or def.walkable then
|
||||||
|
-- There's a node in the way. Delete arrow without damage
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Ignite Campfires
|
if lua then
|
||||||
if mod_campfire and mcl_burning.is_burning(self.object) and minetest.get_item_group(snode.name, "campfire") ~= 0 then
|
local entity_name = lua.name
|
||||||
mcl_campfires.light_campfire(self._stuckin)
|
-- Achievement for hitting skeleton, wither skeleton or stray (TODO) with an arrow at least 50 meters away
|
||||||
end
|
-- NOTE: Range has been reduced because mobs unload much earlier than that ... >_>
|
||||||
|
-- TODO: This achievement should be given for the kill, not just a hit
|
||||||
-- Activate target
|
if self._shooter and self._shooter:is_player() and vector.distance(pos, self._startpos) >= 20 then
|
||||||
if mod_target and snode.name == "mcl_target:target_off" then
|
if mod_awards and (entity_name == "mobs_mc:skeleton" or entity_name == "mobs_mc:stray" or entity_name == "mobs_mc:witherskeleton") then
|
||||||
mcl_target.hit(self._stuckin, 1) --10 redstone ticks
|
awards.unlock(self._shooter:get_player_name(), "mcl:snipeSkeleton")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Push the button! Push, push, push the button!
|
|
||||||
if mod_button and minetest.get_item_group(node.name, "button") > 0 and minetest.get_item_group(node.name, "button_push_by_arrow") == 1 then
|
|
||||||
local bdir = minetest.wallmounted_to_dir(node.param2)
|
|
||||||
-- Check the button orientation
|
|
||||||
if vector.equals(vector.add(dpos, bdir), self._stuckin) then
|
|
||||||
mesecon.push_button(dpos, node)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif (def and def.liquidtype ~= "none") then
|
|
||||||
-- Slow down arrow in liquids
|
if not obj:is_player() then
|
||||||
local v = def.liquid_viscosity
|
mcl_burning.extinguish(self.object)
|
||||||
if not v then
|
if self._piercing == 0 then
|
||||||
v = 0
|
self.object:remove()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
--local old_v = self._viscosity
|
end
|
||||||
|
},
|
||||||
|
on_step = function(self, dtime)
|
||||||
|
mcl_burning.tick(self.object, dtime, self)
|
||||||
|
|
||||||
|
-- mcl_burning.tick may remove object immediately
|
||||||
|
if not self.object:get_pos() then return end
|
||||||
|
|
||||||
|
self._time_in_air = self._time_in_air + dtime
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
--local dpos = vector.round(vector.new(pos)) -- digital pos
|
||||||
|
--local node = minetest.get_node(dpos)
|
||||||
|
|
||||||
|
if self._stuck then
|
||||||
|
return stuck_arrow_on_step(self, dtime)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add tracer
|
||||||
|
if self._damage >= 9 and self._in_player == false then
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 20,
|
||||||
|
time = .2,
|
||||||
|
minpos = vector.new(0,0,0),
|
||||||
|
maxpos = vector.new(0,0,0),
|
||||||
|
minvel = vector.new(-0.1,-0.1,-0.1),
|
||||||
|
maxvel = vector.new(0.1,0.1,0.1),
|
||||||
|
minexptime = 0.5,
|
||||||
|
maxexptime = 0.5,
|
||||||
|
minsize = 2,
|
||||||
|
maxsize = 2,
|
||||||
|
attached = self.object,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mobs_mc_arrow_particle.png",
|
||||||
|
glow = 1,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
if self._deflection_cooloff > 0 then
|
||||||
|
self._deflection_cooloff = self._deflection_cooloff - dtime
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: change to use vl_physics
|
||||||
|
-- TODO: move to vl_projectile
|
||||||
|
local def = minetest.registered_nodes[minetest.get_node(pos).name]
|
||||||
|
if def and def.liquidtype ~= "none" then
|
||||||
|
-- Slow down arrow in liquids
|
||||||
|
local v = def.liquid_viscosity or 0
|
||||||
self._viscosity = v
|
self._viscosity = v
|
||||||
|
|
||||||
local vpenalty = math.max(0.1, 0.98 - 0.1 * v)
|
local vpenalty = math.max(0.1, 0.98 - 0.1 * v)
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
if math.abs(vel.x) > 0.001 then
|
if math.abs(vel.x) > 0.001 then
|
||||||
vel.x = vel.x * vpenalty
|
vel.x = vel.x * vpenalty
|
||||||
end
|
end
|
||||||
|
@ -432,79 +349,70 @@ function ARROW_ENTITY.on_step(self, dtime)
|
||||||
end
|
end
|
||||||
self.object:set_velocity(vel)
|
self.object:set_velocity(vel)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
-- Update yaw
|
-- Process as projectile
|
||||||
if not self._stuck then
|
vl_projectile.update_projectile(self, dtime)
|
||||||
|
|
||||||
|
-- Update yaw
|
||||||
local vel = self.object:get_velocity()
|
local vel = self.object:get_velocity()
|
||||||
local yaw = minetest.dir_to_yaw(vel)+YAW_OFFSET
|
if vel and not self._stuck then
|
||||||
local pitch = dir_to_pitch(vel)
|
local yaw = minetest.dir_to_yaw(vel)+YAW_OFFSET
|
||||||
self.object:set_rotation({ x = 0, y = yaw, z = pitch })
|
local pitch = dir_to_pitch(vel)
|
||||||
end
|
self.object:set_rotation({ x = 0, y = yaw, z = pitch })
|
||||||
|
|
||||||
-- Update internal variable
|
|
||||||
self._lastpos = pos
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Force recheck of stuck arrows when punched.
|
|
||||||
-- Otherwise, punching has no effect.
|
|
||||||
function ARROW_ENTITY.on_punch(self)
|
|
||||||
if self._stuck then
|
|
||||||
self._stuckrechecktimer = STUCK_RECHECK_TIME
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function ARROW_ENTITY.get_staticdata(self)
|
|
||||||
local out = {
|
|
||||||
lastpos = self._lastpos,
|
|
||||||
startpos = self._startpos,
|
|
||||||
damage = self._damage,
|
|
||||||
is_critical = self._is_critical,
|
|
||||||
stuck = self._stuck,
|
|
||||||
stuckin = self._stuckin,
|
|
||||||
stuckin_player = self._in_player,
|
|
||||||
}
|
|
||||||
if self._stuck then
|
|
||||||
-- If _stucktimer is missing for some reason, assume the maximum
|
|
||||||
if not self._stucktimer then
|
|
||||||
self._stucktimer = ARROW_TIMEOUT
|
|
||||||
end
|
end
|
||||||
out.stuckstarttime = minetest.get_gametime() - self._stucktimer
|
end,
|
||||||
end
|
|
||||||
if self._shooter and self._shooter:is_player() then
|
|
||||||
out.shootername = self._shooter:get_player_name()
|
|
||||||
end
|
|
||||||
return minetest.serialize(out)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ARROW_ENTITY.on_activate(self, staticdata, dtime_s)
|
-- Force recheck of stuck arrows when punched.
|
||||||
self._time_in_air = 1.0
|
-- Otherwise, punching has no effect.
|
||||||
local data = minetest.deserialize(staticdata)
|
on_punch = function(self)
|
||||||
if data then
|
if self._stuck then
|
||||||
self._stuck = data.stuck
|
|
||||||
if data.stuck then
|
|
||||||
if data.stuckstarttime then
|
|
||||||
-- First, check if the stuck arrow is aleady past its life timer.
|
|
||||||
-- If yes, delete it.
|
|
||||||
self._stucktimer = minetest.get_gametime() - data.stuckstarttime
|
|
||||||
if self._stucktimer > ARROW_TIMEOUT then
|
|
||||||
mcl_burning.extinguish(self.object)
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Perform a stuck recheck on the next step.
|
|
||||||
self._stuckrechecktimer = STUCK_RECHECK_TIME
|
self._stuckrechecktimer = STUCK_RECHECK_TIME
|
||||||
|
end
|
||||||
self._stuckin = data.stuckin
|
end,
|
||||||
|
get_staticdata = function(self)
|
||||||
|
local out = {}
|
||||||
|
local save_fields = self._save_fields
|
||||||
|
for i = 1,#save_fields do
|
||||||
|
local field = save_fields[i]
|
||||||
|
out[field] = self["_"..field]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get the remaining arrow state
|
if self._stuck then
|
||||||
self._lastpos = data.lastpos
|
-- If _stucktimer is missing for some reason, assume the maximum
|
||||||
self._startpos = data.startpos
|
if not self._stucktimer then
|
||||||
self._damage = data.damage
|
self._stucktimer = ARROW_TIMEOUT
|
||||||
self._is_critical = data.is_critical
|
end
|
||||||
|
out.stuckstarttime = minetest.get_gametime() - self._stucktimer
|
||||||
|
end
|
||||||
|
|
||||||
|
if self._shooter and self._shooter:is_player() then
|
||||||
|
out.shootername = self._shooter:get_player_name()
|
||||||
|
end
|
||||||
|
return minetest.serialize(out)
|
||||||
|
end,
|
||||||
|
on_activate = function(self, staticdata, dtime_s)
|
||||||
|
self.object:set_armor_groups({ immortal = 1 })
|
||||||
|
|
||||||
|
self._time_in_air = 1.0
|
||||||
|
local data = minetest.deserialize(staticdata)
|
||||||
|
if not data then return end
|
||||||
|
|
||||||
|
-- Restore arrow state
|
||||||
|
local save_fields = self._save_fields
|
||||||
|
for i = 1,#save_fields do
|
||||||
|
local field = save_fields[i]
|
||||||
|
self["_"..field] = data[field]
|
||||||
|
end
|
||||||
|
|
||||||
|
if data.stuckstarttime then
|
||||||
|
-- First, check if the stuck arrow is aleady past its life timer.
|
||||||
|
-- If yes, delete it.
|
||||||
|
self._stucktimer = minetest.get_gametime() - data.stuckstarttime
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Perform a stuck recheck on the next step.
|
||||||
|
self._stuckrechecktimer = STUCK_RECHECK_TIME
|
||||||
|
|
||||||
if data.shootername then
|
if data.shootername then
|
||||||
local shooter = minetest.get_player_by_name(data.shootername)
|
local shooter = minetest.get_player_by_name(data.shootername)
|
||||||
if shooter and shooter:is_player() then
|
if shooter and shooter:is_player() then
|
||||||
|
@ -515,9 +423,8 @@ function ARROW_ENTITY.on_activate(self, staticdata, dtime_s)
|
||||||
if data.stuckin_player then
|
if data.stuckin_player then
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
end
|
end
|
||||||
end
|
end,
|
||||||
self.object:set_armor_groups({ immortal = 1 })
|
})
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_on_respawnplayer(function(player)
|
minetest.register_on_respawnplayer(function(player)
|
||||||
for _, obj in pairs(player:get_children()) do
|
for _, obj in pairs(player:get_children()) do
|
||||||
|
@ -528,8 +435,6 @@ minetest.register_on_respawnplayer(function(player)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
minetest.register_entity("mcl_bows:arrow_entity", ARROW_ENTITY)
|
|
||||||
|
|
||||||
if minetest.get_modpath("mcl_core") and minetest.get_modpath("mcl_mobitems") then
|
if minetest.get_modpath("mcl_core") and minetest.get_modpath("mcl_mobitems") then
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = "mcl_bows:arrow 4",
|
output = "mcl_bows:arrow 4",
|
||||||
|
@ -544,3 +449,4 @@ end
|
||||||
if minetest.get_modpath("doc_identifier") then
|
if minetest.get_modpath("doc_identifier") then
|
||||||
doc.sub.identifier.register_object("mcl_bows:arrow_entity", "craftitems", "mcl_bows:arrow")
|
doc.sub.identifier.register_object("mcl_bows:arrow_entity", "craftitems", "mcl_bows:arrow")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,7 @@ function mcl_bows.shoot_arrow(arrow_item, pos, dir, yaw, shooter, power, damage,
|
||||||
le._startpos = pos
|
le._startpos = pos
|
||||||
le._knockback = knockback
|
le._knockback = knockback
|
||||||
le._collectable = collectable
|
le._collectable = collectable
|
||||||
|
le._arrow_item = arrow_item
|
||||||
minetest.sound_play("mcl_bows_bow_shoot", {pos=pos, max_hear_distance=16}, true)
|
minetest.sound_play("mcl_bows_bow_shoot", {pos=pos, max_hear_distance=16}, true)
|
||||||
if shooter and shooter:is_player() then
|
if shooter and shooter:is_player() then
|
||||||
if obj:get_luaentity().player == "" then
|
if obj:get_luaentity().player == "" then
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name = mcl_bows
|
name = mcl_bows
|
||||||
author = Arcelmi
|
author = Arcelmi
|
||||||
description = This mod adds bows and arrows for MineClone 2.
|
description = This mod adds bows and arrows for MineClone 2.
|
||||||
depends = controls, mcl_particles, mcl_enchanting, mcl_init, mcl_util, mcl_shields, mcl_fovapi, mcl_luck
|
depends = controls, mcl_particles, mcl_enchanting, mcl_init, mcl_util, mcl_shields, mcl_fovapi, mcl_luck, vl_projectile
|
||||||
optional_depends = awards, mcl_achievements, mcl_core, mcl_mobitems, playerphysics, doc, doc_identifier, mesecons_button
|
optional_depends = awards, mcl_achievements, mcl_core, mcl_mobitems, playerphysics, doc, doc_identifier, mesecons_button
|
||||||
|
|
||||||
|
|
|
@ -340,7 +340,6 @@ local function on_put(pos, listname, index, stack, player)
|
||||||
local inv = meta:get_inventory()
|
local inv = meta:get_inventory()
|
||||||
local str = ""
|
local str = ""
|
||||||
local stack
|
local stack
|
||||||
local oldparam2 = minetest.get_node(pos).param2
|
|
||||||
for i=1, inv:get_size("stand") do
|
for i=1, inv:get_size("stand") do
|
||||||
stack = inv:get_stack("stand", i)
|
stack = inv:get_stack("stand", i)
|
||||||
if not stack:is_empty() then
|
if not stack:is_empty() then
|
||||||
|
@ -348,7 +347,7 @@ local function on_put(pos, listname, index, stack, player)
|
||||||
else str = str.."0"
|
else str = str.."0"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
minetest.swap_node(pos, {name = "mcl_brewing:stand_"..str, param2 = oldparam2})
|
minetest.swap_node(pos, {name = "mcl_brewing:stand_"..str})
|
||||||
minetest.get_node_timer(pos):start(1.0)
|
minetest.get_node_timer(pos):start(1.0)
|
||||||
--some code here to enforce only potions getting placed on stands
|
--some code here to enforce only potions getting placed on stands
|
||||||
end
|
end
|
||||||
|
|
|
@ -205,8 +205,7 @@ walkover.register_global(function(pos, _, player)
|
||||||
if frost_walker <= 0 then
|
if frost_walker <= 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- 1011 = sqrt(4096000)/2; 4096000 is the max number of nodes for find_nodes_in_area_under_air
|
local radius = frost_walker + 2
|
||||||
local radius = math.min(frost_walker + 2, 1011)
|
|
||||||
local minp = {x = pos.x - radius, y = pos.y, z = pos.z - radius}
|
local minp = {x = pos.x - radius, y = pos.y, z = pos.z - radius}
|
||||||
local maxp = {x = pos.x + radius, y = pos.y, z = pos.z + radius}
|
local maxp = {x = pos.x + radius, y = pos.y, z = pos.z + radius}
|
||||||
local positions = minetest.find_nodes_in_area_under_air(minp, maxp, "mcl_core:water_source")
|
local positions = minetest.find_nodes_in_area_under_air(minp, maxp, "mcl_core:water_source")
|
||||||
|
|
|
@ -22,7 +22,6 @@ minetest.register_entity("mcl_end:ender_eye", {
|
||||||
self._phase = 0
|
self._phase = 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not self._luck then self._luck = 0 end
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_step = function(self, dtime)
|
on_step = function(self, dtime)
|
||||||
|
|
|
@ -53,7 +53,7 @@ for i=0, 3 do
|
||||||
end
|
end
|
||||||
if 3 ~= i and mcl_dye and
|
if 3 ~= i and mcl_dye and
|
||||||
clicker:get_wielded_item():get_name() == "mcl_bone_meal:bone_meal" then
|
clicker:get_wielded_item():get_name() == "mcl_bone_meal:bone_meal" then
|
||||||
mcl_dye.apply_bone_meal({under=pos, above=vector.offset(pos,0,1,0)},clicker)
|
mcl_dye.apply_bone_meal({under=pos},clicker)
|
||||||
if not minetest.is_creative_enabled(pn) then
|
if not minetest.is_creative_enabled(pn) then
|
||||||
itemstack:take_item()
|
itemstack:take_item()
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,19 +27,18 @@ minetest.register_craftitem("mcl_fire:fire_charge", {
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Ignite/light fire
|
-- Ignite/light fire
|
||||||
local used = nil
|
|
||||||
local node = get_node(pointed_thing.under)
|
local node = get_node(pointed_thing.under)
|
||||||
if pointed_thing.type == "node" then
|
if pointed_thing.type == "node" then
|
||||||
local nodedef = minetest.registered_nodes[node.name]
|
local nodedef = minetest.registered_nodes[node.name]
|
||||||
if nodedef and nodedef._on_ignite then
|
if nodedef and nodedef._on_ignite then
|
||||||
local overwrite = nodedef._on_ignite(user, pointed_thing)
|
local overwrite = nodedef._on_ignite(user, pointed_thing)
|
||||||
if not overwrite then
|
if not overwrite then
|
||||||
used = mcl_fire.set_fire(pointed_thing, user, false)
|
mcl_fire.set_fire(pointed_thing, user, false)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
used = mcl_fire.set_fire(pointed_thing, user, false)
|
mcl_fire.set_fire(pointed_thing, user, false)
|
||||||
end
|
end
|
||||||
if not minetest.is_creative_enabled(user:get_player_name()) and used then
|
if not minetest.is_creative_enabled(user:get_player_name()) then
|
||||||
itemstack:take_item()
|
itemstack:take_item()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,22 +31,23 @@ minetest.register_tool("mcl_fire:flint_and_steel", {
|
||||||
{pos = pointed_thing.above, gain = 0.5, max_hear_distance = 8},
|
{pos = pointed_thing.above, gain = 0.5, max_hear_distance = 8},
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
local used = nil
|
local used = false
|
||||||
if pointed_thing.type == "node" then
|
if pointed_thing.type == "node" then
|
||||||
local nodedef = minetest.registered_nodes[get_node(pointed_thing.under).name]
|
local nodedef = minetest.registered_nodes[get_node(pointed_thing.under).name]
|
||||||
if nodedef and nodedef._on_ignite then
|
if nodedef and nodedef._on_ignite then
|
||||||
local overwrite = nodedef._on_ignite(user, pointed_thing)
|
local overwrite = nodedef._on_ignite(user, pointed_thing)
|
||||||
if not overwrite then
|
if not overwrite then
|
||||||
used = mcl_fire.set_fire(pointed_thing, user, false)
|
mcl_fire.set_fire(pointed_thing, user, false)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
used = mcl_fire.set_fire(pointed_thing, user, false)
|
mcl_fire.set_fire(pointed_thing, user, false)
|
||||||
end
|
end
|
||||||
|
used = true
|
||||||
end
|
end
|
||||||
if itemstack:get_count() == 0 and idef.sound and idef.sound.breaks then
|
if itemstack:get_count() == 0 and idef.sound and idef.sound.breaks then
|
||||||
minetest.sound_play(idef.sound.breaks, {pos=user:get_pos(), gain=0.5}, true)
|
minetest.sound_play(idef.sound.breaks, {pos=user:get_pos(), gain=0.5}, true)
|
||||||
end
|
end
|
||||||
if (not minetest.is_creative_enabled(user:get_player_name())) and used then
|
if (not minetest.is_creative_enabled(user:get_player_name())) and used == true then
|
||||||
itemstack:add_wear(65535/65) -- 65 uses
|
itemstack:add_wear(65535/65) -- 65 uses
|
||||||
end
|
end
|
||||||
return itemstack
|
return itemstack
|
||||||
|
|
|
@ -469,7 +469,7 @@ function mcl_fire.set_fire(pointed_thing, player, allow_on_fire)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
return add_node(pointed_thing.above, {name="mcl_fire:fire"})
|
add_node(pointed_thing.above, {name="mcl_fire:fire"})
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_lbm({
|
minetest.register_lbm({
|
||||||
|
|
|
@ -654,7 +654,7 @@ end
|
||||||
minetest.register_alias("mcl_hoppers:hopper_item", "mcl_hoppers:hopper")
|
minetest.register_alias("mcl_hoppers:hopper_item", "mcl_hoppers:hopper")
|
||||||
|
|
||||||
minetest.register_lbm({
|
minetest.register_lbm({
|
||||||
label = "Update hopper formspecs (0.60.0)",
|
label = "Update hopper formspecs (0.60.0",
|
||||||
name = "mcl_hoppers:update_formspec_0_60_0",
|
name = "mcl_hoppers:update_formspec_0_60_0",
|
||||||
nodenames = {"group:hopper"},
|
nodenames = {"group:hopper"},
|
||||||
run_at_every_load = false,
|
run_at_every_load = false,
|
||||||
|
|
|
@ -638,7 +638,7 @@ function mcl_itemframes.create_base_definitions()
|
||||||
paramtype = "light",
|
paramtype = "light",
|
||||||
paramtype2 = "facedir",
|
paramtype2 = "facedir",
|
||||||
sunlight_propagates = true,
|
sunlight_propagates = true,
|
||||||
groups = { dig_immediate = 3, deco_block = 1, dig_by_piston = 1, container = 1, supported_node_facedir = 1 },
|
groups = { dig_immediate = 3, deco_block = 1, dig_by_piston = 1, container = 1, attached_node_facedir = 1 },
|
||||||
sounds = mcl_sounds.node_sound_defaults(),
|
sounds = mcl_sounds.node_sound_defaults(),
|
||||||
node_placement_prediction = "",
|
node_placement_prediction = "",
|
||||||
|
|
||||||
|
|
|
@ -266,7 +266,7 @@ minetest.register_node("mcl_mangrove:river_water_logged_roots",rwlroots)
|
||||||
|
|
||||||
minetest.register_node("mcl_mangrove:mangrove_mud_roots", {
|
minetest.register_node("mcl_mangrove:mangrove_mud_roots", {
|
||||||
description = S("Muddy Mangrove Roots"),
|
description = S("Muddy Mangrove Roots"),
|
||||||
_tt_help = S("Crafted with Mud and Mangrove roots"),
|
_tt_help = S("crafted with Mud and Mangrove roots"),
|
||||||
_doc_items_longdesc = S("Muddy Mangrove Roots is a block from mangrove swamp.It drowns player a bit inside it."),
|
_doc_items_longdesc = S("Muddy Mangrove Roots is a block from mangrove swamp.It drowns player a bit inside it."),
|
||||||
tiles = {
|
tiles = {
|
||||||
"mcl_mud.png^mcl_mangrove_roots_top.png",
|
"mcl_mud.png^mcl_mangrove_roots_top.png",
|
||||||
|
|
|
@ -21,7 +21,7 @@ water logged mangrove roots=racines de palétuvier immergées
|
||||||
Mangrove roots, despite being a full block, can be waterlogged and do not flow water out=Les racines de palétuvier sont un bloc plein mais qui peut être immergé et ne remplace pas l'eau.
|
Mangrove roots, despite being a full block, can be waterlogged and do not flow water out=Les racines de palétuvier sont un bloc plein mais qui peut être immergé et ne remplace pas l'eau.
|
||||||
These cannot be crafted yet only occure when get in contact of water.=Elles ne peuvent être fabriquées mais se forment au contact de l'eau.
|
These cannot be crafted yet only occure when get in contact of water.=Elles ne peuvent être fabriquées mais se forment au contact de l'eau.
|
||||||
Muddy Mangrove Roots=Racines de palétuvier boueuses
|
Muddy Mangrove Roots=Racines de palétuvier boueuses
|
||||||
Crafted with Mud and Mangrove roots=Fabriqué avec de la boue et des racines de palétuvier
|
crafted with Mud and Mangrove roots=fabriqué avec de la boue et des racines de palétuvier
|
||||||
Muddy Mangrove Roots is a block from mangrove swamp.It drowns player a bit inside it.=Les racines de palétuvier boueuses sont un bloc du marécage de la mangrove. Il noie un joueur à l'intérieur.
|
Muddy Mangrove Roots is a block from mangrove swamp.It drowns player a bit inside it.=Les racines de palétuvier boueuses sont un bloc du marécage de la mangrove. Il noie un joueur à l'intérieur.
|
||||||
Mangrove Door=Porte en palétuvier
|
Mangrove Door=Porte en palétuvier
|
||||||
Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal.=Les portes en bois sont des barrières hautes à 2 blocs qui peuvent être ouvertes ou fermées à la main et par un signal redstone.
|
Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal.=Les portes en bois sont des barrières hautes à 2 blocs qui peuvent être ouvertes ou fermées à la main et par un signal redstone.
|
||||||
|
|
|
@ -21,7 +21,7 @@ water logged mangrove roots=水没したマングローブの根
|
||||||
Mangrove roots, despite being a full block, can be waterlogged and do not flow water out=マングローブの根は、フルブロックであるにもかかわらず水没することがあり、水が流出しない
|
Mangrove roots, despite being a full block, can be waterlogged and do not flow water out=マングローブの根は、フルブロックであるにもかかわらず水没することがあり、水が流出しない
|
||||||
These cannot be crafted yet only occure when get in contact of water.=これはクラフトできないものの、水と接触したときだけ発生します。
|
These cannot be crafted yet only occure when get in contact of water.=これはクラフトできないものの、水と接触したときだけ発生します。
|
||||||
Muddy Mangrove Roots=泥に塗れたマングローブの根
|
Muddy Mangrove Roots=泥に塗れたマングローブの根
|
||||||
Crafted with Mud and Mangrove roots=泥とマングローブの根で作られたもの
|
crafted with Mud and Mangrove roots=泥とマングローブの根で作られたもの
|
||||||
Muddy Mangrove Roots is a block from mangrove swamp.It drowns player a bit inside it.=泥に塗れたマングローブの根は、マングローブの沼地から1ブロックの場所にあります。
|
Muddy Mangrove Roots is a block from mangrove swamp.It drowns player a bit inside it.=泥に塗れたマングローブの根は、マングローブの沼地から1ブロックの場所にあります。
|
||||||
Mangrove Door=マングローブのドア
|
Mangrove Door=マングローブのドア
|
||||||
Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal.=木製のドアは、高さ2ブロックの障壁で、手やレッドストーンの信号で開閉できます。
|
Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal.=木製のドアは、高さ2ブロックの障壁で、手やレッドストーンの信号で開閉できます。
|
||||||
|
|
|
@ -21,7 +21,7 @@ water logged mangrove roots=Raízes de Mangue Alagadas
|
||||||
Mangrove roots, despite being a full block, can be waterlogged and do not flow water out=Raízes de mangue, mesmo sendo um bloco inteiro, podem ser alagadas e não escorre água delas.
|
Mangrove roots, despite being a full block, can be waterlogged and do not flow water out=Raízes de mangue, mesmo sendo um bloco inteiro, podem ser alagadas e não escorre água delas.
|
||||||
These cannot be crafted yet only occure when get in contact of water.=Essas não podem ser fabricadas ainda ocorrendo apenas quando tem contato com a água.
|
These cannot be crafted yet only occure when get in contact of water.=Essas não podem ser fabricadas ainda ocorrendo apenas quando tem contato com a água.
|
||||||
Muddy Mangrove Roots=Raízes Barrentas de Mangue
|
Muddy Mangrove Roots=Raízes Barrentas de Mangue
|
||||||
Crafted with Mud and Mangrove roots=Fabricadas com barro e raízes de mangue
|
crafted with Mud and Mangrove roots=Fabricadas com barro e raízes de mangue
|
||||||
Muddy Mangrove Roots is a block from mangrove swamp.It drowns player a bit inside it.=Raízes barrentas de mangue é um bloco dos pântanos de mangue. Afunda o jogador um pouco para dentro de si.
|
Muddy Mangrove Roots is a block from mangrove swamp.It drowns player a bit inside it.=Raízes barrentas de mangue é um bloco dos pântanos de mangue. Afunda o jogador um pouco para dentro de si.
|
||||||
Mangrove Door=Porta de Mangue
|
Mangrove Door=Porta de Mangue
|
||||||
Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal.=Portas de madeira são barreiras de 2 blocos de altura as quais podem ser abertas ou fechadas pela mão e por um sinal de redstone.
|
Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal.=Portas de madeira são barreiras de 2 blocos de altura as quais podem ser abertas ou fechadas pela mão e por um sinal de redstone.
|
||||||
|
|
|
@ -21,7 +21,7 @@ water logged mangrove roots=Затопленные мангровые корни
|
||||||
Mangrove roots, despite being a full block, can be waterlogged and do not flow water out=Мангровые корни, не смотря на то что это полный блок, может быть затоплен и не выпускать воду наружу.
|
Mangrove roots, despite being a full block, can be waterlogged and do not flow water out=Мангровые корни, не смотря на то что это полный блок, может быть затоплен и не выпускать воду наружу.
|
||||||
These cannot be crafted yet only occure when get in contact of water.=Нельзя скрафтить, появляется только при контакте с водой.
|
These cannot be crafted yet only occure when get in contact of water.=Нельзя скрафтить, появляется только при контакте с водой.
|
||||||
Muddy Mangrove Roots=Грязные мангровые корни
|
Muddy Mangrove Roots=Грязные мангровые корни
|
||||||
Crafted with Mud and Mangrove roots=Крафтится с помощью грязи и мангровых корней
|
crafted with Mud and Mangrove roots=Крафтится с помощью грязи и мангровых корней
|
||||||
Muddy Mangrove Roots is a block from mangrove swamp.It drowns player a bit inside it.=Грязные мангровые корни это блок из мангровых болот. Игрок немного погружается внутрь них.
|
Muddy Mangrove Roots is a block from mangrove swamp.It drowns player a bit inside it.=Грязные мангровые корни это блок из мангровых болот. Игрок немного погружается внутрь них.
|
||||||
Mangrove Door=Мангровая дверь
|
Mangrove Door=Мангровая дверь
|
||||||
Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal.=Деревянные двери это преграды высотой в 2 блока, которые можно открывать и закрывать вручную и по сигналу редстоуна.
|
Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal.=Деревянные двери это преграды высотой в 2 блока, которые можно открывать и закрывать вручную и по сигналу редстоуна.
|
||||||
|
|
|
@ -21,7 +21,7 @@ water logged mangrove roots=
|
||||||
Mangrove roots, despite being a full block, can be waterlogged and do not flow water out=
|
Mangrove roots, despite being a full block, can be waterlogged and do not flow water out=
|
||||||
These cannot be crafted yet only occure when get in contact of water.=
|
These cannot be crafted yet only occure when get in contact of water.=
|
||||||
Muddy Mangrove Roots=
|
Muddy Mangrove Roots=
|
||||||
Crafted with Mud and Mangrove roots=
|
crafted with Mud and Mangrove roots=
|
||||||
Muddy Mangrove Roots is a block from mangrove swamp.It drowns player a bit inside it.=
|
Muddy Mangrove Roots is a block from mangrove swamp.It drowns player a bit inside it.=
|
||||||
Mangrove Door=
|
Mangrove Door=
|
||||||
Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal.=
|
Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal.=
|
||||||
|
|
|
@ -63,8 +63,8 @@ local function set_doll_properties(doll, mob)
|
||||||
xs = doll_size_overrides[mob].x
|
xs = doll_size_overrides[mob].x
|
||||||
ys = doll_size_overrides[mob].y
|
ys = doll_size_overrides[mob].y
|
||||||
else
|
else
|
||||||
xs = (mobinfo.visual_size.x or 0) * 0.33333
|
xs = mobinfo.visual_size.x * 0.33333
|
||||||
ys = (mobinfo.visual_size.y or 0) * 0.33333
|
ys = mobinfo.visual_size.y * 0.33333
|
||||||
end
|
end
|
||||||
local prop = {
|
local prop = {
|
||||||
mesh = mobinfo.mesh,
|
mesh = mobinfo.mesh,
|
||||||
|
@ -83,13 +83,6 @@ local function respawn_doll(pos)
|
||||||
local mob = meta:get_string("Mob")
|
local mob = meta:get_string("Mob")
|
||||||
local doll
|
local doll
|
||||||
if mob and mob ~= "" then
|
if mob and mob ~= "" then
|
||||||
-- Handle conversion of mob spawners
|
|
||||||
local convert_to = (minetest.registered_entities[mob] or {})._convert_to
|
|
||||||
if convert_to then
|
|
||||||
mob = convert_to
|
|
||||||
meta:set_string("Mob", mob)
|
|
||||||
end
|
|
||||||
|
|
||||||
doll = find_doll(pos)
|
doll = find_doll(pos)
|
||||||
if not doll then
|
if not doll then
|
||||||
doll = spawn_doll(pos)
|
doll = spawn_doll(pos)
|
||||||
|
@ -135,6 +128,7 @@ function mcl_mobspawners.setup_spawner(pos, Mob, MinLight, MaxLight, MaxMobsInAr
|
||||||
end
|
end
|
||||||
set_doll_properties(doll, Mob)
|
set_doll_properties(doll, Mob)
|
||||||
|
|
||||||
|
|
||||||
-- Start spawning very soon
|
-- Start spawning very soon
|
||||||
local t = minetest.get_node_timer(pos)
|
local t = minetest.get_node_timer(pos)
|
||||||
t:start(2)
|
t:start(2)
|
||||||
|
@ -171,6 +165,7 @@ local function spawn_mobs(pos, elapsed)
|
||||||
local count = 0
|
local count = 0
|
||||||
local ent
|
local ent
|
||||||
|
|
||||||
|
|
||||||
local timer = minetest.get_node_timer(pos)
|
local timer = minetest.get_node_timer(pos)
|
||||||
|
|
||||||
-- spawn mob if player detected and in range
|
-- spawn mob if player detected and in range
|
||||||
|
@ -363,15 +358,11 @@ doll_def.on_activate = function(self, staticdata, dtime_s)
|
||||||
if mob == "" or mob == nil then
|
if mob == "" or mob == nil then
|
||||||
mob = default_mob
|
mob = default_mob
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Handle conversion of mob spawners
|
|
||||||
local convert_to = (minetest.registered_entities[mob] or {})._convert_to
|
|
||||||
if convert_to then mob = convert_to end
|
|
||||||
|
|
||||||
set_doll_properties(self.object, mob)
|
set_doll_properties(self.object, mob)
|
||||||
self.object:set_velocity({x=0, y=0, z=0})
|
self.object:set_velocity({x=0, y=0, z=0})
|
||||||
self.object:set_acceleration({x=0, y=0, z=0})
|
self.object:set_acceleration({x=0, y=0, z=0})
|
||||||
self.object:set_armor_groups({immortal=1})
|
self.object:set_armor_groups({immortal=1})
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
doll_def.on_step = function(self, dtime)
|
doll_def.on_step = function(self, dtime)
|
||||||
|
@ -399,11 +390,3 @@ minetest.register_lbm({
|
||||||
respawn_doll(pos)
|
respawn_doll(pos)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_on_mods_loaded(function()
|
|
||||||
for name,mobinfo in pairs(minetest.registered_entities) do
|
|
||||||
if ( mobinfo.is_mob or name:find("mobs_mc") ) and not ( mobinfo.visual_size or mobinfo._convert_to ) then
|
|
||||||
minetest.log("warning", "Definition for "..tostring(name).." is missing field 'visual_size', mob spawners will not work properly")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
|
@ -66,33 +66,6 @@ This section describes parts of the API related to defining and managing effects
|
||||||
|
|
||||||
`mcl_potions.get_effect(object, effect_name)` - returns a table containing values of the effect of the ID `effect_name` on the `object` if the object has the named effect, `false` otherwise.
|
`mcl_potions.get_effect(object, effect_name)` - returns a table containing values of the effect of the ID `effect_name` on the `object` if the object has the named effect, `false` otherwise.
|
||||||
|
|
||||||
* table returned by the above function is like this:
|
|
||||||
```lua
|
|
||||||
effect = {
|
|
||||||
dur = float -- duration of the effect in seconds, may be infinite
|
|
||||||
timer = float -- how much of the duration (in seconds) has already elapsed
|
|
||||||
no_particles = bool -- if this is true, no particles signifying this effect will appear
|
|
||||||
|
|
||||||
-- player-only fields
|
|
||||||
hud_index = int -- position in the HUD used by this effect (icon, level, timer) - probably meaningless outside mcl_potions
|
|
||||||
|
|
||||||
-- optional fields
|
|
||||||
factor = float -- power of the effect if the effect uses factor; this may mean different things depending on the effect
|
|
||||||
step = float -- how often (in seconds) the on_step() function of the effect is executed, if it exists
|
|
||||||
hit_timer = float -- how much of the step (in seconds) has already elapsed
|
|
||||||
|
|
||||||
-- effect-specific fields
|
|
||||||
-- effects in mcl_potions have their own fields here, for now external effects can't add any here
|
|
||||||
blocked = bool -- used by conduit power
|
|
||||||
high = bool -- used by nausea
|
|
||||||
vignette = int -- handle to the HUD vignette of the effect, used by effects that use one
|
|
||||||
absorb = float -- "HP" of the absorption effect
|
|
||||||
waypoints = table -- used by glowing, indexed by player ObjectRef, contains HUD handles for the glowing waypoints
|
|
||||||
flash = float -- used by darkness, denotes vision range modifier
|
|
||||||
flashdir = bool -- used by darkness, denotes whether vision range is increasing (or decreasing)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
`mcl_potions.get_effect_level(object, effect_name)` – returns the level of the effect of the ID `effect_name` on the `object`. If the effect has no levels, returns `1`. If the object doesn't have the effect, returns `0`. If the effect is not registered, returns `nil`.
|
`mcl_potions.get_effect_level(object, effect_name)` – returns the level of the effect of the ID `effect_name` on the `object`. If the effect has no levels, returns `1`. If the object doesn't have the effect, returns `0`. If the effect is not registered, returns `nil`.
|
||||||
|
|
||||||
|
@ -147,7 +120,6 @@ This section describes parts of the API related to defining and managing effects
|
||||||
|
|
||||||
#### Internally registered effects
|
#### Internally registered effects
|
||||||
You can't register effects going by these names, because they are already used:
|
You can't register effects going by these names, because they are already used:
|
||||||
|
|
||||||
* `invisibility`
|
* `invisibility`
|
||||||
* `poison`
|
* `poison`
|
||||||
* `regeneration`
|
* `regeneration`
|
||||||
|
|
|
@ -372,9 +372,7 @@ mcl_potions.register_effect({
|
||||||
playerphysics.add_physics_factor(object, "gravity", "mcl_potions:slow_falling", 0.5)
|
playerphysics.add_physics_factor(object, "gravity", "mcl_potions:slow_falling", 0.5)
|
||||||
end,
|
end,
|
||||||
on_step = function(dtime, object, factor, duration)
|
on_step = function(dtime, object, factor, duration)
|
||||||
local vel = object:get_velocity()
|
local vel = object:get_velocity().y
|
||||||
if not vel then return end
|
|
||||||
vel = vel.y
|
|
||||||
if vel < -3 then object:add_velocity(vector.new(0,-3-vel,0)) end
|
if vel < -3 then object:add_velocity(vector.new(0,-3-vel,0)) end
|
||||||
end,
|
end,
|
||||||
on_end = function(object)
|
on_end = function(object)
|
||||||
|
@ -432,9 +430,7 @@ mcl_potions.register_effect({
|
||||||
return S("moves body upwards at @1 nodes/s", factor)
|
return S("moves body upwards at @1 nodes/s", factor)
|
||||||
end,
|
end,
|
||||||
on_step = function(dtime, object, factor, duration)
|
on_step = function(dtime, object, factor, duration)
|
||||||
local vel = object:get_velocity()
|
local vel = object:get_velocity().y
|
||||||
if not vel then return end
|
|
||||||
vel = vel.y
|
|
||||||
if vel<factor then object:add_velocity(vector.new(0,factor,0)) end
|
if vel<factor then object:add_velocity(vector.new(0,factor,0)) end
|
||||||
end,
|
end,
|
||||||
particle_color = "#420E7E",
|
particle_color = "#420E7E",
|
||||||
|
@ -456,16 +452,11 @@ mcl_potions.register_effect({
|
||||||
object:get_meta():set_int("night_vision", 1)
|
object:get_meta():set_int("night_vision", 1)
|
||||||
mcl_weather.skycolor.update_sky_color({object})
|
mcl_weather.skycolor.update_sky_color({object})
|
||||||
end,
|
end,
|
||||||
on_load = function(object, factor)
|
|
||||||
object:get_meta():set_int("night_vision", 1)
|
|
||||||
mcl_weather.skycolor.update_sky_color({object})
|
|
||||||
end,
|
|
||||||
on_step = function(dtime, object, factor, duration)
|
on_step = function(dtime, object, factor, duration)
|
||||||
mcl_weather.skycolor.update_sky_color({object})
|
mcl_weather.skycolor.update_sky_color({object})
|
||||||
end,
|
end,
|
||||||
on_end = function(object)
|
on_end = function(object)
|
||||||
local meta = object:get_meta()
|
local meta = object:get_meta()
|
||||||
if not meta then return end
|
|
||||||
meta:set_int("night_vision", 0)
|
meta:set_int("night_vision", 0)
|
||||||
mcl_weather.skycolor.update_sky_color({object})
|
mcl_weather.skycolor.update_sky_color({object})
|
||||||
end,
|
end,
|
||||||
|
@ -508,9 +499,7 @@ mcl_potions.register_effect({
|
||||||
mcl_weather.skycolor.update_sky_color({object})
|
mcl_weather.skycolor.update_sky_color({object})
|
||||||
end,
|
end,
|
||||||
on_end = function(object)
|
on_end = function(object)
|
||||||
local meta = object:get_meta()
|
object:get_meta():set_int("darkness", 0)
|
||||||
if not meta then return end
|
|
||||||
meta:set_int("darkness", 0)
|
|
||||||
mcl_weather.skycolor.update_sky_color({object})
|
mcl_weather.skycolor.update_sky_color({object})
|
||||||
object:set_sky({fog = {
|
object:set_sky({fog = {
|
||||||
fog_distance = -1,
|
fog_distance = -1,
|
||||||
|
@ -1095,7 +1084,6 @@ function mcl_potions.update_haste_and_fatigue(player)
|
||||||
if f_fac ~= 1 then meta:set_float("mcl_potions:fatigue", 1 - f_fac)
|
if f_fac ~= 1 then meta:set_float("mcl_potions:fatigue", 1 - f_fac)
|
||||||
else meta:set_string("mcl_potions:fatigue", "") end
|
else meta:set_string("mcl_potions:fatigue", "") end
|
||||||
meta:set_tool_capabilities()
|
meta:set_tool_capabilities()
|
||||||
meta:set_string("groupcaps_hash","")
|
|
||||||
mcl_enchanting.update_groupcaps(item)
|
mcl_enchanting.update_groupcaps(item)
|
||||||
if h_fac == 0 and f_fac == 1 then
|
if h_fac == 0 and f_fac == 1 then
|
||||||
player:set_wielded_item(item)
|
player:set_wielded_item(item)
|
||||||
|
@ -1356,7 +1344,7 @@ minetest.register_globalstep(function(dtime)
|
||||||
potions_set_hud(object)
|
potions_set_hud(object)
|
||||||
else
|
else
|
||||||
local ent = object:get_luaentity()
|
local ent = object:get_luaentity()
|
||||||
if ent and ent._mcl_potions then
|
if ent then
|
||||||
ent._mcl_potions["_EF_"..name] = nil
|
ent._mcl_potions["_EF_"..name] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1371,7 +1359,7 @@ minetest.register_globalstep(function(dtime)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local ent = object:get_luaentity()
|
local ent = object:get_luaentity()
|
||||||
if ent and ent._mcl_potions then
|
if ent then
|
||||||
ent._mcl_potions["_EF_"..name] = EF[name][object]
|
ent._mcl_potions["_EF_"..name] = EF[name][object]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1404,7 +1392,6 @@ function mcl_potions._reset_haste_fatigue_item_meta(player)
|
||||||
meta:set_string("mcl_potions:haste", "")
|
meta:set_string("mcl_potions:haste", "")
|
||||||
meta:set_string("mcl_potions:fatigue", "")
|
meta:set_string("mcl_potions:fatigue", "")
|
||||||
meta:set_tool_capabilities()
|
meta:set_tool_capabilities()
|
||||||
meta:set_string("groupcaps_hash","")
|
|
||||||
mcl_enchanting.update_groupcaps(item)
|
mcl_enchanting.update_groupcaps(item)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1529,11 +1516,6 @@ function mcl_potions._load_player_effects(player)
|
||||||
local loaded = minetest.deserialize(meta:get_string("mcl_potions:_EF_"..name))
|
local loaded = minetest.deserialize(meta:get_string("mcl_potions:_EF_"..name))
|
||||||
if loaded then
|
if loaded then
|
||||||
EF[name][player] = loaded
|
EF[name][player] = loaded
|
||||||
end
|
|
||||||
if EF[name][player] then -- this is needed because of legacy effects loaded separately
|
|
||||||
if effect.uses_factor and type(EF[name][player].factor) ~= "number" then
|
|
||||||
EF[name][player].factor = effect.level_to_factor(1)
|
|
||||||
end
|
|
||||||
if effect.on_load then
|
if effect.on_load then
|
||||||
effect.on_load(player, EF[name][player].factor)
|
effect.on_load(player, EF[name][player].factor)
|
||||||
end
|
end
|
||||||
|
@ -1551,9 +1533,6 @@ function mcl_potions._load_entity_effects(entity)
|
||||||
local loaded = entity._mcl_potions["_EF_"..name]
|
local loaded = entity._mcl_potions["_EF_"..name]
|
||||||
if loaded then
|
if loaded then
|
||||||
EF[name][object] = loaded
|
EF[name][object] = loaded
|
||||||
if effect.uses_factor and not loaded.factor then
|
|
||||||
EF[name][object].factor = effect.level_to_factor(1)
|
|
||||||
end
|
|
||||||
if effect.on_load then
|
if effect.on_load then
|
||||||
effect.on_load(object, EF[name][object].factor)
|
effect.on_load(object, EF[name][object].factor)
|
||||||
end
|
end
|
||||||
|
@ -1792,14 +1771,9 @@ end
|
||||||
local function target_valid(object, name)
|
local function target_valid(object, name)
|
||||||
if not object or object:get_hp() <= 0 then return false end
|
if not object or object:get_hp() <= 0 then return false end
|
||||||
|
|
||||||
-- Don't apply effects to anything other than players and entities that have mcl_potions support
|
|
||||||
-- but are not bosses
|
|
||||||
local entity = object:get_luaentity()
|
local entity = object:get_luaentity()
|
||||||
if not object:is_player() and (not entity or entity.is_boss or not entity._mcl_potions) then
|
if entity and entity.is_boss then return false end
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check resistances
|
|
||||||
for i=1, #registered_res_predicates do
|
for i=1, #registered_res_predicates do
|
||||||
if registered_res_predicates[i](object, name) then return false end
|
if registered_res_predicates[i](object, name) then return false end
|
||||||
end
|
end
|
||||||
|
@ -1851,10 +1825,6 @@ end
|
||||||
|
|
||||||
function mcl_potions.give_effect_by_level(name, object, level, duration, no_particles)
|
function mcl_potions.give_effect_by_level(name, object, level, duration, no_particles)
|
||||||
if level == 0 then return false end
|
if level == 0 then return false end
|
||||||
if not registered_effects[name] then
|
|
||||||
minetest.log("warning", "[mcl_potions] Trying to give unknown effect "..tostring(name))
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if not registered_effects[name].uses_factor then
|
if not registered_effects[name].uses_factor then
|
||||||
return mcl_potions.give_effect(name, object, 0, duration, no_particles)
|
return mcl_potions.give_effect(name, object, 0, duration, no_particles)
|
||||||
end
|
end
|
||||||
|
|
|
@ -173,8 +173,8 @@ function mcl_potions.register_lingering(name, descr, color, def)
|
||||||
obj:set_velocity({x=dropdir.x*velocity,y=dropdir.y*velocity,z=dropdir.z*velocity})
|
obj:set_velocity({x=dropdir.x*velocity,y=dropdir.y*velocity,z=dropdir.z*velocity})
|
||||||
obj:set_acceleration({x=dropdir.x*-3, y=-9.8, z=dropdir.z*-3})
|
obj:set_acceleration({x=dropdir.x*-3, y=-9.8, z=dropdir.z*-3})
|
||||||
local ent = obj:get_luaentity()
|
local ent = obj:get_luaentity()
|
||||||
ent._potency = stack:get_meta():get_int("mcl_potions:potion_potent")
|
ent._potency = item:get_meta():get_int("mcl_potions:potion_potent")
|
||||||
ent._plus = stack:get_meta():get_int("mcl_potions:potion_plus")
|
ent._plus = item:get_meta():get_int("mcl_potions:potion_plus")
|
||||||
ent._effect_list = def._effect_list
|
ent._effect_list = def._effect_list
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,283 +1,27 @@
|
||||||
# textdomain: mcl_potions
|
# textdomain: mcl_potions
|
||||||
Invisibility=Invisibilité
|
<effect> <duration> [<factor>]=<effet> <durée> [<facteur>]
|
||||||
body is invisible=le corps est invisible
|
|
||||||
|
|
||||||
Poison=Poison
|
|
||||||
-1 HP / @1 s=-1 PV / @1 s
|
|
||||||
|
|
||||||
Regeneration=Régénération
|
|
||||||
+1 HP / @1 s=+1 PV / @1 s
|
|
||||||
|
|
||||||
Strength=Force
|
|
||||||
+@1% melee damage=+@1% dégâts de mêlée
|
|
||||||
|
|
||||||
Weakness=Faiblesse
|
|
||||||
-@1% melee damage=-@1% dégâts de mêlée
|
|
||||||
|
|
||||||
Water Breathing=Respiration aquatique
|
|
||||||
limitless breathing under water=respiration illimitée sous l'eau
|
|
||||||
|
|
||||||
Dolphin's Grace=Grâce du dauphin
|
|
||||||
swimming gracefully=nage gracieuse
|
|
||||||
|
|
||||||
Leaping=Saut
|
|
||||||
+@1% jumping power=+@1% puissance de saut
|
|
||||||
-@1% jumping power=-@1% puissance de saut
|
|
||||||
|
|
||||||
Slow Falling=Chute lente
|
|
||||||
decreases gravity effects=diminue les effets de la gravité
|
|
||||||
|
|
||||||
Swiftness=Rapidité
|
|
||||||
+@1% running speed=+@1% vitesse de course
|
|
||||||
|
|
||||||
Slowness=Lenteur
|
|
||||||
-@1% running speed=-@1% vitesse de course
|
|
||||||
|
|
||||||
Levitation=Lévitation
|
|
||||||
moves body upwards at @1 nodes/s=déplace le corps vers le haut à @1 nœuds/s
|
|
||||||
|
|
||||||
Night Vision=Vision nocturne
|
|
||||||
improved vision during the night=améliore la vision durant la nuit
|
|
||||||
|
|
||||||
Darkness=Obscurité
|
|
||||||
surrounded by darkness=entouré d'obscurité
|
|
||||||
not seeing anything beyond @1 nodes=ne vois rien au-delà de @1 nœuds
|
|
||||||
|
|
||||||
Glowing=Surbrillance
|
|
||||||
more visible at all times=plus visible en permanence
|
|
||||||
|
|
||||||
Health Boost=Bonus de santé
|
|
||||||
HP increased by @1=PV augmentés de @1
|
|
||||||
|
|
||||||
Absorption=Absorption
|
|
||||||
absorbs up to @1 incoming damage=absorbe jusqu'à @1 dégâts reçus
|
|
||||||
|
|
||||||
Fire Resistance=Résistance au feu
|
|
||||||
resistance to fire damage=resistance aux dégâts du feu
|
|
||||||
|
|
||||||
Resistance=Résistance
|
|
||||||
resist @1% of incoming damage=résiste à @1% des dégâts reçus
|
|
||||||
|
|
||||||
Luck=Chance
|
|
||||||
|
|
||||||
Bad Luck=Malchance
|
|
||||||
|
|
||||||
Bad Omen=Mauvais présage
|
|
||||||
danger is imminent=un danger est imminent
|
|
||||||
|
|
||||||
Hero of the Village=Héros du village
|
|
||||||
|
|
||||||
Withering=Dépérissement
|
|
||||||
-1 HP / @1 s, can kill=-1 PV / @1 s, peut tuer
|
|
||||||
|
|
||||||
Frost=Gel
|
|
||||||
-1 HP / 1 s, can kill, -@1% running speed=-1 PV / @1 s, peut tuer, -@1% vitesse de course
|
|
||||||
|
|
||||||
Blindness=Cécité
|
|
||||||
impaired sight=déficience visuelle
|
|
||||||
|
|
||||||
Nausea=Nausée
|
|
||||||
not feeling very well...=ne se sent pas très bien
|
|
||||||
frequency: @1 / 1 s=fréquence : @1 / 1 s
|
|
||||||
|
|
||||||
Food Poisoning=Intoxication alimentaire
|
|
||||||
exhausts by @1 per second=s'épuise de @1 par seconde
|
|
||||||
|
|
||||||
Saturation=Saturation
|
|
||||||
saturates by @1 per second=sature de @1 par seconde
|
|
||||||
|
|
||||||
Haste=Célérité
|
|
||||||
+@1% mining and attack speed=+@1% de vitesse d'attaque et de minage
|
|
||||||
|
|
||||||
Fatigue=Fatigue
|
|
||||||
-@1% mining and attack speed=-@1% de vitesse d'attaque et de minage
|
|
||||||
|
|
||||||
Conduit Power=Force de conduit
|
|
||||||
+@1% mining and attack speed in water=+@1% de vitesse d'attaque et de minage sous l'eau
|
|
||||||
|
|
||||||
|
|
||||||
<effect>|heal|list|clear|remove <duration|heal-amount|effect>|INF [<level>] [<factor>] [NOPART]=<effet>|heal|list|clear|remove <durée|quantité-soin|effet>|INF [<niveau>] [<facteur>] [NOPART]
|
|
||||||
|
|
||||||
Add a status effect to yourself. Arguments: <effect>: name of status effect. Passing "list" as effect name lists available effects. Passing "heal" as effect name heals (or harms) by amount designed by the next parameter. Passing "clear" as effect name removes all effects. Passing "remove" as effect name removes the effect named by the next parameter. <duration>: duration in seconds. Passing "INF" as duration makes the effect infinite. (<heal-amount>: amount of healing when the effect is "heal", passing a negative value subtracts health. <effect>: name of a status effect to be removed when using "remove" as the previous parameter.) <level>: effect power determinant, bigger level results in more powerful effect for effects that depend on the level (no changes for other effects), defaults to 1, pass F to use low-level factor instead. <factor>: effect strength modifier, can mean different things depending on the effect, no changes for effects that do not depend on level/factor. NOPART at the end means no particles will be shown for this effect.=Ajoute un effet de statut à vous-même. Arguments : <effet> : nom de l'effet de statut. Utiliser "list" comme nom d'effet liste les effets disponibles. Utiliser "heal" comme nom d'effet soigne (ou blesse) d'un nombre défini par le paramètre suivant. Utiliser "clear" comme nom d'effet enleve tous les effets. Utiliser "remove" comme nom d'effet enlève l'effet nommé dans le paramètre suivant. <durée> : durée en secondes. Utiliser "INF" comme durée rends l'effet infini. (<quantité-soin> : quantité de soin lorsque l'effet est "heal", utiliser une valeur négative enlève de la santé. <effet> : nom de l'effet de statut à enlever lors de l'utilisation de "remove" comme paramètre précédent.) <niveau> : détermine la puissance de l'effet, un niveau plus élevé se traduit par un effet plus puissant pour les effets qui dépendent du niveau (aucun changement pour les autres effets), défaut à 1, utiliser F pour utiliser un facteur de bas niveau à la place. <facteur> : effet modificateur de force, peut avoir différentes significations en fonction de l'effet, pas de changement pour les effets qui ne dépendent pas du niveau/facteur. NOPART à la fin signifie qu'aucune particule ne sera affichée pour cet effet.
|
|
||||||
|
|
||||||
Missing effect parameter!=Paramètre d'effet manquant !
|
|
||||||
Missing or invalid heal amount parameter!=Paramètre de quantité de soin manquant ou invalide !
|
|
||||||
Player @1 healed by @2 HP.=Joueur @1 soigné de @2 PV.
|
|
||||||
Player @1 harmed by @2 HP.=Joueur @1 blessé de @2 PV.
|
|
||||||
Effects cleared for player @1=Effets effacés pour le joueur @1
|
|
||||||
Removed effect @1 from player @2=Effet @1 enlevé pour le joueur @2
|
|
||||||
@1 is not an available status effect.=@1 n'est pas un effet de statut disponible.
|
|
||||||
Missing or invalid duration parameter!=Paramètre de durée manquant ou invalide !
|
|
||||||
Invalid level parameter!=Paramètre de niveau invalide !
|
|
||||||
Missing or invalid factor parameter when level is F!=Paramètre de facteur manquant ou invalide quand le niveau est F!
|
|
||||||
|
|
||||||
@1 effect given to player @2 for @3 seconds with factor of @4.=Effet @1 donné au joueur @2 pour @3 seconds avec un facteur de @4.
|
|
||||||
@1 effect given to player @2 for @3 seconds.=Effet @1 donné au joueur @2 pour @3 seconds.
|
|
||||||
Giving effect @1 to player @2 failed.=L'attribution de l'effet @1 au joueur @2 a échoué.
|
|
||||||
@1 effect on level @2 given to player @3 for @4 seconds.=Effet @1 de niveau @2 donné au joueur @3 pendant @4 secondes.
|
|
||||||
|
|
||||||
|
|
||||||
A throwable potion that will shatter on impact, where it gives all nearby players and mobs a status effect or a set of status effects.=Une potion jetable qui se brisera à l'impact, où elle donne à tous les joueurs et créatures proches un effet de statut ou un ensemble d'effets de statut.
|
|
||||||
Use the “Punch” key to throw it.=Utilisez la touche "Frapper" pour la lancer.
|
|
||||||
|
|
||||||
|
|
||||||
A throwable potion that will shatter on impact, where it creates a magic cloud that lingers around for a while. Any player or mob inside the cloud will receive the potion's effect or set of effects, possibly repeatedly.=Une potion jetable qui se brisera à l'impact, où elle crée un nuage magique qui persiste pendant un moment. Tout joueur ou mob à l'intérieur du nuage recevra l'effet de la potion ou un ensemble d'effets, peut-être à plusieurs reprises.
|
|
||||||
|
|
||||||
|
|
||||||
This particular arrow is tipped and will give an effect when it hits a player or mob.=Cette flèche particulière est enchantée et donnera un effet lorsqu'elle touche un joueur ou un mob.
|
|
||||||
|
|
||||||
|
|
||||||
Use the “Place” key to drink it.=Utilisez la touche "Utiliser" pour la boire.
|
|
||||||
Drinking a potion gives you a particular effect or set of effects.=Boire une potion vous donne un effet particulier ou un ensemble d'effets.
|
|
||||||
|
|
||||||
@1 Potion @2=@1 potion @2
|
|
||||||
@1 Potion=Potion @1
|
|
||||||
Potion @1=Potion @1
|
|
||||||
Strange Potion=Potion étrange
|
|
||||||
|
|
||||||
Splash @1=@1 jetable
|
|
||||||
Lingering @1=@1 persistante
|
|
||||||
@1 Arrow @2=@1 flèche @2
|
|
||||||
@1 Arrow=Flèche @1
|
|
||||||
Arrow @1=Flèche @1
|
|
||||||
Strange Tipped Arrow=Flèche à pointe étrange
|
|
||||||
|
|
||||||
Mighty=puissante
|
|
||||||
of Trolling=de trollage
|
|
||||||
|
|
||||||
Dragon's Breath=Souffle du dragon
|
|
||||||
This item is used in brewing and can be combined with splash potions to create lingering potions.=Cet objet est utilisé dans le brassage et peut être combiné avec des potions jetables pour créer des potions persistantes.
|
|
||||||
|
|
||||||
Awkward=étrange
|
|
||||||
No effect=Aucun effet
|
|
||||||
Has an awkward taste and is used for brewing potions.=A un goût étrange et est utilisée pour préparer des potions.
|
|
||||||
|
|
||||||
Mundane=banale
|
|
||||||
Has a terrible taste and is not really useful for brewing potions.=A un goût terrible et n'est pas vraiment utile pour préparer des potions.
|
|
||||||
|
|
||||||
Thick=épaisse
|
|
||||||
Has a bitter taste and may be useful for brewing potions.=A un goût amer et peut être utile pour préparer des potions.
|
|
||||||
|
|
||||||
of Healing=de guérison
|
|
||||||
+@1 HP=+@1 PV
|
|
||||||
Instantly heals.=Guérit instantanément
|
|
||||||
|
|
||||||
of Harming=de dégâts
|
|
||||||
-@1 HP=-@1 PV
|
|
||||||
Instantly deals damage.=Inflige des dégâts instantanément.
|
|
||||||
|
|
||||||
of Night Vision=de vision nocturne
|
|
||||||
Increases the perceived brightness of light under a dark sky.=Augmente la luminosité de la lumière perçue sous un ciel sombre.
|
|
||||||
|
|
||||||
of Swiftness=de rapidité
|
|
||||||
Increases walking speed.=Augmente la vitesse de marche.
|
|
||||||
|
|
||||||
of Slowness=de lenteur
|
|
||||||
Decreases walking speed.=Diminue la vitesse de marche.
|
|
||||||
|
|
||||||
of Leaping=de saut
|
|
||||||
Increases jump strength.=Augmente la force de saut.
|
|
||||||
|
|
||||||
of Withering=de dépérissement
|
|
||||||
Applies the withering effect which deals damage at a regular interval and can kill.=Applique l'effet de dépérissement qui inflige des dégâts à intervalles réguliers et peut tuer.
|
|
||||||
|
|
||||||
of Poison=de poison
|
|
||||||
Applies the poison effect which deals damage at a regular interval.=Applique l'effet de poison qui inflige des dégâts à intervalles réguliers.
|
|
||||||
|
|
||||||
of Regeneration=de régénération
|
|
||||||
Regenerates health over time.=Régénère la santé au fil du temps.
|
|
||||||
|
|
||||||
of Invisibility=d'invisibilité
|
|
||||||
Grants invisibility.=Confère l'invisibilité.
|
|
||||||
|
|
||||||
of Water Breathing=de respiration aquatique
|
|
||||||
Grants limitless breath underwater.=Confère une respiration illimitée sous l'eau.
|
|
||||||
|
|
||||||
of Fire Resistance=de résistance au feu
|
|
||||||
Grants immunity to damage from heat sources like fire.=Confère une immunité aux dégâts causés par des sources de chaleur comme le feu.
|
|
||||||
|
|
||||||
of Strength=de force
|
|
||||||
Increases attack power.=Augmente la puissance d'attaque.
|
|
||||||
|
|
||||||
of Weakness=de faiblesse
|
|
||||||
Decreases attack power.=Diminue la puissance d'attaque.
|
|
||||||
|
|
||||||
of Slow Falling=de chute lente
|
|
||||||
Instead of falling, you descend gracefully.=Au lieu de tomber, vous descendez avec grâce.
|
|
||||||
|
|
||||||
of Levitation=de lévitation
|
|
||||||
Floats body slowly upwards.=Le corps flotte lentement vers le haut.
|
|
||||||
|
|
||||||
of Darkness=d'obscurité
|
|
||||||
Surrounds with darkness.=Entoure d'obscurité.
|
|
||||||
|
|
||||||
of Glowing=de surbrillance
|
|
||||||
Highlights for others to see.=Mise en valeur pour que les autres voient.
|
|
||||||
|
|
||||||
of Health Boost=de bonus de santé
|
|
||||||
Increases health.=Augmente la santé.
|
|
||||||
|
|
||||||
of Absorption=d'absorption
|
|
||||||
Absorbs some incoming damage.=Absorbe les dégâts reçus.
|
|
||||||
|
|
||||||
of Resistance=de résistance
|
|
||||||
Decreases damage taken.=Diminue les dégâts subis.
|
|
||||||
|
|
||||||
of Stone Cloak=de manteau de pierre
|
|
||||||
Decreases damage taken at the cost of speed.=Diminue les dégâts subis au détriment de la vitesse.
|
|
||||||
|
|
||||||
of Luck=de chance
|
|
||||||
Increases luck.=Augmente la chance.
|
|
||||||
|
|
||||||
of Bad Luck=de malchance
|
|
||||||
Decreases luck.=Diminue la chance.
|
|
||||||
|
|
||||||
of Frost=de gel
|
|
||||||
Freezes...=Gèle...
|
|
||||||
|
|
||||||
of Blindness=de cécité
|
|
||||||
Impairs sight.=Altére la vue.
|
|
||||||
|
|
||||||
of Nausea=de nausée
|
|
||||||
Disintegrates senses.=Désintègre les sens.
|
|
||||||
|
|
||||||
of Food Poisoning=d'intoxication alimentaire
|
|
||||||
Moves bowels too fast.=Déplace les intestins trop rapidement.
|
|
||||||
|
|
||||||
of Saturation=de saturation
|
|
||||||
Satisfies hunger.=Satisfait la faim.
|
|
||||||
|
|
||||||
of Haste=de célérité
|
|
||||||
Increases digging and attack speed.=Augmente la vitesse de creusage et d'attaque.
|
|
||||||
|
|
||||||
of Fatigue=de fatigue
|
|
||||||
Decreases digging and attack speed.=Diminue la vitesse de creusage et d'attaque.
|
|
||||||
|
|
||||||
Ominous=funeste
|
|
||||||
Attracts danger.=Attire le danger.
|
|
||||||
|
|
||||||
Unknown Potion=Potion inconnue
|
|
||||||
Right-click to identify=Clic droit pour identifier
|
|
||||||
Unknown Tipped Arrow=Flèche à pointe inconnue
|
|
||||||
|
|
||||||
|
Add a status effect to yourself. Arguments: <effect>: name of status effect, e.g. poison. <duration>: duration in seconds. <factor>: effect strength multiplier (1 @= 100%)=Ajoutez-vous un effet de statut. Arguments: <effet>: nom de l'effet de statut, par ex. poison. <duration>: durée en secondes. <facteur>: multiplicateur de force d'effet (1 @= 100%)
|
||||||
|
|
||||||
|
Missing effect parameter!=Paramètre d'effet manquant!
|
||||||
|
Missing or invalid duration parameter!=Paramètre durée manquant ou invalide!
|
||||||
|
Invalid factor parameter!=Paramètre facteur invalide!
|
||||||
|
@1 is not an available status effect.=@1 n'est pas un effet disponible.
|
||||||
Fermented Spider Eye=Oeil d'araignée fermenté
|
Fermented Spider Eye=Oeil d'araignée fermenté
|
||||||
Try different combinations to create potions.=Essayez différentes combinaisons pour créer des potions.
|
|
||||||
|
|
||||||
Glass Bottle=Bouteille en verre
|
Glass Bottle=Bouteille en verre
|
||||||
Liquid container=Récipient de liquide
|
Liquid container=Récipient de liquide
|
||||||
|
|
||||||
A glass bottle is used as a container for liquids and can be used to collect water directly.=Une bouteille en verre est utilisée comme récipient pour les liquides et peut être utilisée pour recueillir l'eau directement.
|
A glass bottle is used as a container for liquids and can be used to collect water directly.=Une bouteille en verre est utilisée comme récipient pour les liquides et peut être utilisée pour collecter l'eau directement.
|
||||||
|
|
||||||
To collect water, use it on a cauldron with water (which removes a level of water) or any water source (which removes no water).=Pour récolter l'eau, utilisez la sur un chaudron avec de l'eau (qui enlève un niveau d'eau) ou toute source d'eau (qui n'enlève pas d'eau).
|
To collect water, use it on a cauldron with water (which removes a level of water) or any water source (which removes no water).=Pour collecter l'eau, poser la sur un chaudron avec de l'eau (qui enlève un niveau d'eau) ou toute source d'eau (qui n'enlève pas d'eau).
|
||||||
|
|
||||||
Water Bottle=Bouteille d'eau
|
Water Bottle=Bouteille d'eau
|
||||||
Water bottles can be used to fill cauldrons. Drinking water has no effect.=Les bouteilles d'eau peuvent être utilisées pour remplir les chaudrons. Boire l'eau n'a aucun effet.
|
Water bottles can be used to fill cauldrons. Drinking water has no effect.=Les bouteilles d'eau peuvent être utilisées pour remplir les chaudrons. L'eau potable n'a aucun effet.
|
||||||
|
|
||||||
Use the “Place” key to drink. Place this item on a cauldron to pour the water into the cauldron.=Utilisez la touche "Utiliser" pour boire. Placez cet article sur un chaudron pour verser l'eau dans le chaudron.
|
Use the “Place” key to drink. Place this item on a cauldron to pour the water into the cauldron.=Utilisez la touche "Utiliser" pour boire. Placez cet article sur un chaudron pour verser l'eau dans le chaudron.
|
||||||
|
|
||||||
River Water Bottle=Bouteille d'eau de rivière
|
River Water Bottle=Bouteille d'eau de rivière
|
||||||
River water bottles can be used to fill cauldrons. Drinking it has no effect.=Les bouteilles d'eau de rivière peuvent être utilisées pour remplir les chaudrons. La boire n'a aucun effet.
|
River water bottles can be used to fill cauldrons. Drinking it has no effect.=Les bouteilles d'eau de rivière peuvent être utilisées pour remplir les chaudrons. Le boire n'a aucun effet.
|
||||||
|
|
||||||
Use the “Place” key to drink. Place this item on a cauldron to pour the river water into the cauldron.=Utilisez la touche "Utiliser" pour boire. Placez cet objet sur un chaudron pour verser l'eau de la rivière dans le chaudron.
|
Use the “Place” key to drink. Place this item on a cauldron to pour the river water into the cauldron.=Utilisez la touche "Utiliser" pour boire. Placez cet objet sur un chaudron pour verser l'eau de la rivière dans le chaudron.
|
||||||
|
|
||||||
|
@ -293,3 +37,79 @@ A throwable water bottle that will shatter on impact, where it creates a cloud o
|
||||||
Glistering Melon=Melon étincelant
|
Glistering Melon=Melon étincelant
|
||||||
|
|
||||||
This shiny melon is full of tiny gold nuggets and would be nice in an item frame. It isn't edible and not useful for anything else.=Ce melon brillant est plein de minuscules pépites d'or et serait bien dans un cadre d'objet. Il n'est pas comestible et n'est utile à rien d'autre.
|
This shiny melon is full of tiny gold nuggets and would be nice in an item frame. It isn't edible and not useful for anything else.=Ce melon brillant est plein de minuscules pépites d'or et serait bien dans un cadre d'objet. Il n'est pas comestible et n'est utile à rien d'autre.
|
||||||
|
|
||||||
|
A throwable potion that will shatter on impact, where it creates a magic cloud that lingers around for a while. Any player or mob inside the cloud will receive the potion's effect, possibly repeatedly.=Une potion jetable qui se brisera à l'impact, où elle crée un nuage magique qui persiste pendant un moment. Tout joueur ou mob à l'intérieur du nuage recevra l'effet de la potion, peut-être à plusieurs reprises.
|
||||||
|
|
||||||
|
Use the “Punch” key to throw it.=Utilisez la touche "Frapper" pour le lancer.
|
||||||
|
Use the “Place” key to drink it.=Utilisez la touche "Utiliser" pour le boire.
|
||||||
|
Drinking a potion gives you a particular effect.=Boire une potion vous donne un effet particulier.
|
||||||
|
1 HP/@1s | @2=1 PV/@1s | @2
|
||||||
|
@1 HP=@1 PV
|
||||||
|
@1 Potion=Potion @1
|
||||||
|
Splash @1 Potion=Potion @1 jetable
|
||||||
|
Lingering @1 Potion=Potion @1 persistante
|
||||||
|
Arrow of @1=Flêche de @1
|
||||||
|
II= II
|
||||||
|
IV= IV
|
||||||
|
@1 Potion@2=@1 Potion@2
|
||||||
|
Splash @1@2 Potion=Potion @1@2 jetable
|
||||||
|
Lingering @1@2 Potion=Potion @1@2 persistante
|
||||||
|
Arrow of @1@2=Flêche de @1@2
|
||||||
|
@1 + Potion=@1 + Potion
|
||||||
|
Splash @1 + Potion=Potion @1 + jetable
|
||||||
|
Lingering @1 + Potion=Potion @1 + persistante
|
||||||
|
Arrow of @1 +=Flêche de @1 +
|
||||||
|
Awkward Potion=Potion étrange
|
||||||
|
Awkward Splash Potion=Potion étrange jetable
|
||||||
|
Awkward Lingering Potion=Potion étrange persistante
|
||||||
|
Has an awkward taste and is used for brewing potions.=A un goût étrange et est utilisé pour préparer des potions.
|
||||||
|
Mundane Potion=Potion banale
|
||||||
|
Mundane Splash Potion=Potion banale jetable
|
||||||
|
Mundane Lingering Potion=Potion banale persistante
|
||||||
|
Has a terrible taste and is not useful for brewing potions.=A un goût terrible et n'est pas utile pour préparer des potions.
|
||||||
|
Thick Potion=Potion épaisse
|
||||||
|
Thick Splash Potion=Potion épaisse jetable
|
||||||
|
Thick Lingering Potion=Potion épaisse persistante
|
||||||
|
Has a bitter taste and is not useful for brewing potions.=A un goût amer et n'est pas utile pour préparer des potions.
|
||||||
|
Dragon's Breath=Souffle du dragon
|
||||||
|
|
||||||
|
This item is used in brewing and can be combined with splash potions to create lingering potions.=Cet objet est utilisé dans le brassage et peut être combiné avec des potions d'éclaboussures pour créer des potions persistantes.
|
||||||
|
|
||||||
|
Healing=Guérison
|
||||||
|
+4 HP=+4 PV
|
||||||
|
+8 HP=+8 PV
|
||||||
|
Instantly heals.=Guérit instantanément.
|
||||||
|
Harming=Dégâts
|
||||||
|
-6 HP=-6 PV
|
||||||
|
-12 HP=-12 PV
|
||||||
|
Instantly deals damage.=Donne des dégâts instantanément.
|
||||||
|
Night Vision=Vision Nocturne
|
||||||
|
Increases the perceived brightness of light under a dark sky.=Augmente la luminosité perçue de la lumière sous un ciel sombre.
|
||||||
|
Swiftness=Rapidité
|
||||||
|
Increases walking speed.=Augmente la vitesse de marche.
|
||||||
|
Slowness=Lenteur
|
||||||
|
Decreases walking speed.=Diminue la vitesse de marche.
|
||||||
|
Leaping=Saut
|
||||||
|
Increases jump strength.=Augmente la force de saut.
|
||||||
|
Poison=Poison
|
||||||
|
Applies the poison effect which deals damage at a regular interval.=Applique l'effet de poison qui inflige des dégâts à intervalle régulier.
|
||||||
|
Regeneration=Régénération
|
||||||
|
Regenerates health over time.=Régénère la santé au fil du temps.
|
||||||
|
Invisibility=Invisibilité
|
||||||
|
Grants invisibility.=Accorde l'invisibilité.
|
||||||
|
Water Breathing=Respiration aquatique
|
||||||
|
Grants limitless breath underwater.=Donne une respiration illimitée sous l'eau.
|
||||||
|
Fire Resistance=Résistance au feu
|
||||||
|
Grants immunity to damage from heat sources like fire.=Confère une immunité aux dégâts causés par des sources de chaleur comme le feu.
|
||||||
|
Weakness=Faiblesse
|
||||||
|
Weakness +=Faiblesse +
|
||||||
|
Strength=Force
|
||||||
|
Strength II=Force II
|
||||||
|
Strength +=Force +
|
||||||
|
Try different combinations to create potions.=Essayez différentes combinaisons pour créer des potions.
|
||||||
|
No effect=Aucun effet
|
||||||
|
|
||||||
|
A throwable potion that will shatter on impact, where it gives all nearby players and mobs a status effect.=Une potion jetable qui se brisera à l'impact, où elle donne à tous les joueurs et créatures proches un effet de statut.
|
||||||
|
|
||||||
|
This particular arrow is tipped and will give an effect when it hits a player or mob.=Cette flèche particulière est enchantée et donnera un effet lorsqu'elle touche un joueur ou un mob.
|
||||||
|
|
||||||
|
|
|
@ -841,22 +841,20 @@ local function replace_legacy_potion(itemstack)
|
||||||
end
|
end
|
||||||
local compat = "mcl_potions:compat_potion"
|
local compat = "mcl_potions:compat_potion"
|
||||||
local compat_arrow = "mcl_potions:compat_arrow"
|
local compat_arrow = "mcl_potions:compat_arrow"
|
||||||
local compat_def = {
|
minetest.register_craftitem(compat, {
|
||||||
description = S("Unknown Potion") .. "\n" .. minetest.colorize("#ff0", S("Right-click to identify")),
|
description = S("Unknown Potion"),
|
||||||
|
_tt_help = S("Right-click to identify"),
|
||||||
image = "mcl_potions_potion_overlay.png^[colorize:#00F:127^mcl_potions_potion_bottle.png^vl_unknown.png",
|
image = "mcl_potions_potion_overlay.png^[colorize:#00F:127^mcl_potions_potion_bottle.png^vl_unknown.png",
|
||||||
groups = {not_in_creative_inventory = 1},
|
|
||||||
on_secondary_use = replace_legacy_potion,
|
on_secondary_use = replace_legacy_potion,
|
||||||
on_place = replace_legacy_potion,
|
on_place = replace_legacy_potion,
|
||||||
}
|
})
|
||||||
local compat_arrow_def = {
|
minetest.register_craftitem(compat_arrow, {
|
||||||
description = S("Unknown Tipped Arrow") .. "\n" .. minetest.colorize("#ff0", S("Right-click to identify")),
|
description = S("Unknown Tipped Arrow"),
|
||||||
|
_tt_help = S("Right-click to identify"),
|
||||||
image = "mcl_bows_arrow_inv.png^(mcl_potions_arrow_inv.png^[colorize:#FFF:100)^vl_unknown.png",
|
image = "mcl_bows_arrow_inv.png^(mcl_potions_arrow_inv.png^[colorize:#FFF:100)^vl_unknown.png",
|
||||||
groups = {not_in_creative_inventory = 1},
|
|
||||||
on_secondary_use = replace_legacy_potion,
|
on_secondary_use = replace_legacy_potion,
|
||||||
on_place = replace_legacy_potion,
|
on_place = replace_legacy_potion,
|
||||||
}
|
})
|
||||||
minetest.register_craftitem(compat, compat_def)
|
|
||||||
minetest.register_craftitem(compat_arrow, compat_arrow_def)
|
|
||||||
|
|
||||||
local old_potions_plus = {
|
local old_potions_plus = {
|
||||||
"fire_resistance", "water_breathing", "invisibility", "regeneration", "poison",
|
"fire_resistance", "water_breathing", "invisibility", "regeneration", "poison",
|
||||||
|
@ -868,14 +866,14 @@ local old_potions_2 = {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, name in pairs(old_potions_2) do
|
for _, name in pairs(old_potions_2) do
|
||||||
minetest.register_craftitem("mcl_potions:" .. name .. "_2", compat_def)
|
minetest.register_alias("mcl_potions:" .. name .. "_2", compat)
|
||||||
minetest.register_craftitem("mcl_potions:" .. name .. "_2_splash", compat_def)
|
minetest.register_alias("mcl_potions:" .. name .. "_2_splash", compat)
|
||||||
minetest.register_craftitem("mcl_potions:" .. name .. "_2_lingering", compat_def)
|
minetest.register_alias("mcl_potions:" .. name .. "_2_lingering", compat)
|
||||||
minetest.register_craftitem("mcl_potions:" .. name .. "_2_arrow", compat_arrow_def)
|
minetest.register_alias("mcl_potions:" .. name .. "_2_arrow", compat_arrow)
|
||||||
end
|
end
|
||||||
for _, name in pairs(old_potions_plus) do
|
for _, name in pairs(old_potions_plus) do
|
||||||
minetest.register_craftitem("mcl_potions:" .. name .. "_plus", compat_def)
|
minetest.register_alias("mcl_potions:" .. name .. "_plus", compat)
|
||||||
minetest.register_craftitem("mcl_potions:" .. name .. "_plus_splash", compat_def)
|
minetest.register_alias("mcl_potions:" .. name .. "_plus_splash", compat)
|
||||||
minetest.register_craftitem("mcl_potions:" .. name .. "_plus_lingering", compat_def)
|
minetest.register_alias("mcl_potions:" .. name .. "_plus_lingering", compat)
|
||||||
minetest.register_craftitem("mcl_potions:" .. name .. "_plus_arrow", compat_arrow_def)
|
minetest.register_alias("mcl_potions:" .. name .. "_plus_arrow", compat_arrow)
|
||||||
end
|
end
|
||||||
|
|
|
@ -65,8 +65,8 @@ function mcl_potions.register_splash(name, descr, color, def)
|
||||||
obj:set_velocity({x=dropdir.x*velocity,y=dropdir.y*velocity,z=dropdir.z*velocity})
|
obj:set_velocity({x=dropdir.x*velocity,y=dropdir.y*velocity,z=dropdir.z*velocity})
|
||||||
obj:set_acceleration({x=dropdir.x*-3, y=-9.8, z=dropdir.z*-3})
|
obj:set_acceleration({x=dropdir.x*-3, y=-9.8, z=dropdir.z*-3})
|
||||||
local ent = obj:get_luaentity()
|
local ent = obj:get_luaentity()
|
||||||
ent._potency = stack:get_meta():get_int("mcl_potions:potion_potent")
|
ent._potency = item:get_meta():get_int("mcl_potions:potion_potent")
|
||||||
ent._plus = stack:get_meta():get_int("mcl_potions:potion_plus")
|
ent._plus = item:get_meta():get_int("mcl_potions:potion_plus")
|
||||||
ent._effect_list = def._effect_list
|
ent._effect_list = def._effect_list
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
|
@ -77,7 +77,7 @@ mcl_stairs.register_slab("granite", "mcl_core:granite",
|
||||||
mcl_stairs.register_stair("diorite", "mcl_core:diorite",
|
mcl_stairs.register_stair("diorite", "mcl_core:diorite",
|
||||||
{pickaxey=1, material_stone=1},
|
{pickaxey=1, material_stone=1},
|
||||||
{"mcl_core_diorite.png"},
|
{"mcl_core_diorite.png"},
|
||||||
S("Diorite Stairs"),
|
S("Granite Stairs"),
|
||||||
mcl_sounds.node_sound_stone_defaults(), 0.8, 0.8)
|
mcl_sounds.node_sound_stone_defaults(), 0.8, 0.8)
|
||||||
mcl_stairs.register_slab("diorite", "mcl_core:diorite",
|
mcl_stairs.register_slab("diorite", "mcl_core:diorite",
|
||||||
{pickaxey=1, material_stone=1},
|
{pickaxey=1, material_stone=1},
|
||||||
|
|
|
@ -66,15 +66,13 @@ function mcl_stonecutter.register_recipe(input, output, count)
|
||||||
local fallthrough = mcl_stonecutter.registered_recipes[output]
|
local fallthrough = mcl_stonecutter.registered_recipes[output]
|
||||||
if fallthrough then
|
if fallthrough then
|
||||||
for o, c in pairs(fallthrough) do
|
for o, c in pairs(fallthrough) do
|
||||||
if not mcl_stonecutter.registered_recipes[input][o] then
|
mcl_stonecutter.register_recipe(input, o, c * count)
|
||||||
mcl_stonecutter.register_recipe(input, o, c * count)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for i, recipes in pairs(mcl_stonecutter.registered_recipes) do
|
for i, recipes in pairs(mcl_stonecutter.registered_recipes) do
|
||||||
for name, c in pairs(recipes) do
|
for name, c in pairs(recipes) do
|
||||||
if name == input and not mcl_stonecutter.registered_recipes[i][output] then
|
if name == input then
|
||||||
mcl_stonecutter.register_recipe(i, output, c * count)
|
mcl_stonecutter.register_recipe(i, output, c * count)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
local modname = minetest.get_current_modname()
|
||||||
|
local modpath = minetest.get_modpath(modname)
|
||||||
|
local S = minetest.get_translator(modname)
|
||||||
|
|
||||||
|
local mod_target = minetest.get_modpath("mcl_target")
|
||||||
|
local how_to_throw = S("Use the punch key to throw.")
|
||||||
|
|
||||||
|
-- Egg
|
||||||
|
minetest.register_craftitem("mcl_throwing:egg", {
|
||||||
|
description = S("Egg"),
|
||||||
|
_tt_help = S("Throwable").."\n"..S("Chance to hatch chicks when broken"),
|
||||||
|
_doc_items_longdesc = S("Eggs can be thrown or launched from a dispenser and breaks on impact. There is a small chance that 1 or even 4 chicks will pop out of the egg."),
|
||||||
|
_doc_items_usagehelp = how_to_throw,
|
||||||
|
inventory_image = "mcl_throwing_egg.png",
|
||||||
|
stack_max = 16,
|
||||||
|
on_use = mcl_throwing.get_player_throw_function("mcl_throwing:egg_entity"),
|
||||||
|
_on_dispense = mcl_throwing.dispense_function,
|
||||||
|
groups = { craftitem = 1 },
|
||||||
|
})
|
||||||
|
mcl_throwing.register_throwable_object("mcl_throwing:egg", "mcl_throwing:egg_entity", 22)
|
||||||
|
|
||||||
|
minetest.register_entity("mcl_throwing:egg_entity",{
|
||||||
|
physical = false,
|
||||||
|
timer=0,
|
||||||
|
textures = {"mcl_throwing_egg.png"},
|
||||||
|
visual_size = {x=0.45, y=0.45},
|
||||||
|
collisionbox = {0,0,0,0,0,0},
|
||||||
|
pointable = false,
|
||||||
|
|
||||||
|
get_staticdata = mcl_throwing.get_staticdata,
|
||||||
|
on_activate = mcl_throwing.on_activate,
|
||||||
|
|
||||||
|
on_step = vl_projectile.update_projectile,
|
||||||
|
_lastpos={},
|
||||||
|
_thrower = nil,
|
||||||
|
_vl_projectile = {
|
||||||
|
behaviors = {
|
||||||
|
vl_projectile.collides_with_solids,
|
||||||
|
},
|
||||||
|
on_collide_with_solid = function(self, pos, node)
|
||||||
|
if mod_target and node.name == "mcl_target:target_off" then
|
||||||
|
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 1/8 chance to spawn a chick
|
||||||
|
-- FIXME: Chicks have a quite good chance to spawn in walls
|
||||||
|
if math.random(1,8) ~= 1 then return end
|
||||||
|
|
||||||
|
mcl_mobs.spawn_child(self._lastpos, "mobs_mc:chicken")
|
||||||
|
|
||||||
|
-- BONUS ROUND: 1/32 chance to spawn 3 additional chicks
|
||||||
|
if math.random(1,32) ~= 1 then return end
|
||||||
|
|
||||||
|
local offsets = {
|
||||||
|
{ x=0.7, y=0, z=0 },
|
||||||
|
{ x=-0.7, y=0, z=-0.7 },
|
||||||
|
{ x=-0.7, y=0, z=0.7 },
|
||||||
|
}
|
||||||
|
for o=1, 3 do
|
||||||
|
local pos = vector.add(self._lastpos, offsets[o])
|
||||||
|
mcl_mobs.spawn_child(pos, "mobs_mc:chicken")
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
sounds = {
|
||||||
|
on_collision = {"mcl_throwing_egg_impact", {max_hear_distance=10, gain=0.5}, true}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
local modname = minetest.get_current_modname()
|
||||||
|
local modpath = minetest.get_modpath(modname)
|
||||||
|
local S = minetest.get_translator(modname)
|
||||||
|
|
||||||
|
local math = math
|
||||||
|
local vector = vector
|
||||||
|
|
||||||
|
local mod_target = minetest.get_modpath("mcl_target")
|
||||||
|
local how_to_throw = S("Use the punch key to throw.")
|
||||||
|
|
||||||
|
-- Ender Pearl
|
||||||
|
minetest.register_craftitem("mcl_throwing:ender_pearl", {
|
||||||
|
description = S("Ender Pearl"),
|
||||||
|
_tt_help = S("Throwable").."\n"..minetest.colorize(mcl_colors.YELLOW, S("Teleports you on impact for cost of 5 HP")),
|
||||||
|
_doc_items_longdesc = S("An ender pearl is an item which can be used for teleportation at the cost of health. It can be thrown and teleport the thrower to its impact location when it hits a solid block or a plant. Each teleportation hurts the user by 5 hit points."),
|
||||||
|
_doc_items_usagehelp = how_to_throw,
|
||||||
|
wield_image = "mcl_throwing_ender_pearl.png",
|
||||||
|
inventory_image = "mcl_throwing_ender_pearl.png",
|
||||||
|
stack_max = 16,
|
||||||
|
on_use = mcl_throwing.get_player_throw_function("mcl_throwing:ender_pearl_entity"),
|
||||||
|
groups = { transport = 1 },
|
||||||
|
})
|
||||||
|
mcl_throwing.register_throwable_object("mcl_throwing:ender_pearl", "mcl_throwing:ender_pearl_entity", 22)
|
||||||
|
|
||||||
|
-- Ender pearl entity
|
||||||
|
minetest.register_entity("mcl_throwing:ender_pearl_entity",{
|
||||||
|
physical = false,
|
||||||
|
timer=0,
|
||||||
|
textures = {"mcl_throwing_ender_pearl.png"},
|
||||||
|
visual_size = {x=0.9, y=0.9},
|
||||||
|
collisionbox = {0,0,0,0,0,0},
|
||||||
|
pointable = false,
|
||||||
|
|
||||||
|
get_staticdata = mcl_throwing.get_staticdata,
|
||||||
|
on_activate = mcl_throwing.on_activate,
|
||||||
|
|
||||||
|
on_step = vl_projectile.update_projectile,
|
||||||
|
_lastpos={},
|
||||||
|
_thrower = nil, -- Player ObjectRef of the player who threw the ender pearl
|
||||||
|
_vl_projectile = {
|
||||||
|
behaviors = {
|
||||||
|
vl_projectile.collides_with_solids,
|
||||||
|
},
|
||||||
|
collides_with = {
|
||||||
|
"mcl_core:vine", "mcl_core:deadbush",
|
||||||
|
"group:flower", "group:sapling",
|
||||||
|
"group:plant", "group:mushroom",
|
||||||
|
},
|
||||||
|
on_collide_with_solid = function(self, pos, node)
|
||||||
|
if mod_target and node.name == "mcl_target:target_off" then
|
||||||
|
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
|
||||||
|
end
|
||||||
|
|
||||||
|
if node.name == "ignore" then
|
||||||
|
-- FIXME: This also means the player loses an ender pearl for throwing into unloaded areas
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Make sure we have a reference to the player
|
||||||
|
local player = self._thrower and minetest.get_player_by_name(self._thrower)
|
||||||
|
if not player then return end
|
||||||
|
|
||||||
|
-- Teleport and hurt player
|
||||||
|
|
||||||
|
-- First determine good teleport position
|
||||||
|
local dir = {x=0, y=0, z=0}
|
||||||
|
|
||||||
|
local v = self.object:get_velocity()
|
||||||
|
if node_def and node_def.walkable then
|
||||||
|
local vc = table.copy(v) -- vector for calculating
|
||||||
|
-- Node is walkable, we have to find a place somewhere outside of that node
|
||||||
|
vc = vector.normalize(vc)
|
||||||
|
|
||||||
|
-- Zero-out the two axes with a lower absolute value than
|
||||||
|
-- the axis with the strongest force
|
||||||
|
local lv, ld
|
||||||
|
lv, ld = math.abs(vc.y), "y"
|
||||||
|
if math.abs(vc.x) > lv then
|
||||||
|
lv, ld = math.abs(vc.x), "x"
|
||||||
|
end
|
||||||
|
if math.abs(vc.z) > lv then
|
||||||
|
ld = "z" --math.abs(vc.z)
|
||||||
|
end
|
||||||
|
if ld ~= "x" then vc.x = 0 end
|
||||||
|
if ld ~= "y" then vc.y = 0 end
|
||||||
|
if ld ~= "z" then vc.z = 0 end
|
||||||
|
|
||||||
|
-- Final tweaks to the teleporting pos, based on direction
|
||||||
|
-- Impact from the side
|
||||||
|
dir.x = vc.x * -1
|
||||||
|
dir.z = vc.z * -1
|
||||||
|
|
||||||
|
-- Special case: top or bottom of node
|
||||||
|
if vc.y > 0 then
|
||||||
|
-- We need more space when impact is from below
|
||||||
|
dir.y = -2.3
|
||||||
|
elseif vc.y < 0 then
|
||||||
|
-- Standing on top
|
||||||
|
dir.y = 0.5
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- If node was not walkable, no modification to pos is made.
|
||||||
|
|
||||||
|
-- Final teleportation position
|
||||||
|
local telepos = vector.add(pos, dir)
|
||||||
|
local telenode = minetest.get_node(telepos)
|
||||||
|
|
||||||
|
--[[ It may be possible that telepos is walkable due to the algorithm.
|
||||||
|
Especially when the ender pearl is faster horizontally than vertical.
|
||||||
|
This applies final fixing, just to be sure we're not in a walkable node ]]
|
||||||
|
if not minetest.registered_nodes[telenode.name] or minetest.registered_nodes[telenode.name].walkable then
|
||||||
|
if v.y < 0 then
|
||||||
|
telepos.y = telepos.y + 0.5
|
||||||
|
else
|
||||||
|
telepos.y = telepos.y - 2.3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local oldpos = player:get_pos()
|
||||||
|
-- Teleport and hurt player
|
||||||
|
player:set_pos(telepos)
|
||||||
|
player:set_hp(player:get_hp() - 5, { type = "fall", from = "mod" })
|
||||||
|
|
||||||
|
-- 5% chance to spawn endermite at the player's origin
|
||||||
|
local r = math.random(1,20)
|
||||||
|
if r == 1 then
|
||||||
|
minetest.add_entity(oldpos, "mobs_mc:endermite")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
},
|
||||||
|
})
|
|
@ -17,21 +17,18 @@ function mcl_throwing.register_throwable_object(name, entity, velocity)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_throwing.throw(throw_item, pos, dir, velocity, thrower)
|
function mcl_throwing.throw(throw_item, pos, dir, velocity, thrower)
|
||||||
if velocity == nil then
|
velocity = velocity or velocities[throw_item] or 22
|
||||||
velocity = velocities[throw_item]
|
|
||||||
end
|
|
||||||
if velocity == nil then
|
|
||||||
velocity = 22
|
|
||||||
end
|
|
||||||
minetest.sound_play("mcl_throwing_throw", {pos=pos, gain=0.4, max_hear_distance=16}, true)
|
minetest.sound_play("mcl_throwing_throw", {pos=pos, gain=0.4, max_hear_distance=16}, true)
|
||||||
|
|
||||||
local itemstring = ItemStack(throw_item):get_name()
|
local itemstring = ItemStack(throw_item):get_name()
|
||||||
local obj = minetest.add_entity(pos, entity_mapping[itemstring])
|
local obj = vl_projectile.create(entity_mapping[itemstring], {
|
||||||
obj:set_velocity({x=dir.x*velocity, y=dir.y*velocity, z=dir.z*velocity})
|
pos = pos,
|
||||||
obj:set_acceleration({x=dir.x*-3, y=-GRAVITY, z=dir.z*-3})
|
owner = thrower,
|
||||||
if thrower then
|
dir = dir,
|
||||||
obj:get_luaentity()._thrower = thrower
|
velocity = velocity,
|
||||||
end
|
drag = 3,
|
||||||
|
})
|
||||||
|
obj:get_luaentity()._thrower = thrower
|
||||||
return obj
|
return obj
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -71,6 +68,7 @@ end
|
||||||
|
|
||||||
function mcl_throwing.on_activate(self, staticdata, dtime_s)
|
function mcl_throwing.on_activate(self, staticdata, dtime_s)
|
||||||
local data = minetest.deserialize(staticdata)
|
local data = minetest.deserialize(staticdata)
|
||||||
|
self._staticdata = data
|
||||||
if data then
|
if data then
|
||||||
self._lastpos = data._lastpos
|
self._lastpos = data._lastpos
|
||||||
self._thrower = data._thrower
|
self._thrower = data._thrower
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
name = mcl_throwing
|
name = mcl_throwing
|
||||||
depends = mcl_colors
|
depends = mcl_colors, vl_projectile
|
||||||
optional_depends = mcl_core, mcl_mobitems, doc, mcl_target
|
optional_depends = mcl_core, mcl_mobitems, doc, mcl_target
|
||||||
|
|
|
@ -1,330 +1,7 @@
|
||||||
local S = minetest.get_translator(minetest.get_current_modname())
|
local modname = minetest.get_current_modname()
|
||||||
|
local modpath = minetest.get_modpath(modname)
|
||||||
|
|
||||||
local math = math
|
dofile(modpath.."/snowball.lua")
|
||||||
local vector = vector
|
dofile(modpath.."/egg.lua")
|
||||||
|
dofile(modpath.."/ender_pearl.lua")
|
||||||
|
|
||||||
local mod_target = minetest.get_modpath("mcl_target")
|
|
||||||
|
|
||||||
-- The snowball entity
|
|
||||||
local snowball_ENTITY={
|
|
||||||
physical = false,
|
|
||||||
timer=0,
|
|
||||||
textures = {"mcl_throwing_snowball.png"},
|
|
||||||
visual_size = {x=0.5, y=0.5},
|
|
||||||
collisionbox = {0,0,0,0,0,0},
|
|
||||||
pointable = false,
|
|
||||||
|
|
||||||
get_staticdata = mcl_throwing.get_staticdata,
|
|
||||||
on_activate = mcl_throwing.on_activate,
|
|
||||||
_thrower = nil,
|
|
||||||
|
|
||||||
_lastpos={},
|
|
||||||
}
|
|
||||||
|
|
||||||
local egg_ENTITY={
|
|
||||||
physical = false,
|
|
||||||
timer=0,
|
|
||||||
textures = {"mcl_throwing_egg.png"},
|
|
||||||
visual_size = {x=0.45, y=0.45},
|
|
||||||
collisionbox = {0,0,0,0,0,0},
|
|
||||||
pointable = false,
|
|
||||||
|
|
||||||
get_staticdata = mcl_throwing.get_staticdata,
|
|
||||||
on_activate = mcl_throwing.on_activate,
|
|
||||||
_thrower = nil,
|
|
||||||
|
|
||||||
_lastpos={},
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Ender pearl entity
|
|
||||||
local pearl_ENTITY={
|
|
||||||
physical = false,
|
|
||||||
timer=0,
|
|
||||||
textures = {"mcl_throwing_ender_pearl.png"},
|
|
||||||
visual_size = {x=0.9, y=0.9},
|
|
||||||
collisionbox = {0,0,0,0,0,0},
|
|
||||||
pointable = false,
|
|
||||||
|
|
||||||
get_staticdata = mcl_throwing.get_staticdata,
|
|
||||||
on_activate = mcl_throwing.on_activate,
|
|
||||||
|
|
||||||
_lastpos={},
|
|
||||||
_thrower = nil, -- Player ObjectRef of the player who threw the ender pearl
|
|
||||||
}
|
|
||||||
|
|
||||||
local function check_object_hit(self, pos, dmg)
|
|
||||||
for _,object in pairs(minetest.get_objects_inside_radius(pos, 1.5)) do
|
|
||||||
|
|
||||||
local entity = object:get_luaentity()
|
|
||||||
|
|
||||||
if entity
|
|
||||||
and entity.name ~= self.object:get_luaentity().name then
|
|
||||||
|
|
||||||
if object:is_player() and self._thrower ~= object:get_player_name() then
|
|
||||||
self.object:remove()
|
|
||||||
return true
|
|
||||||
elseif (entity.is_mob == true or entity._hittable_by_projectile) and (self._thrower ~= object) then
|
|
||||||
object:punch(self.object, 1.0, {
|
|
||||||
full_punch_interval = 1.0,
|
|
||||||
damage_groups = dmg,
|
|
||||||
}, nil)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function snowball_particles(pos, vel)
|
|
||||||
local vel = vector.normalize(vector.multiply(vel, -1))
|
|
||||||
minetest.add_particlespawner({
|
|
||||||
amount = 20,
|
|
||||||
time = 0.001,
|
|
||||||
minpos = pos,
|
|
||||||
maxpos = pos,
|
|
||||||
minvel = vector.add({x=-2, y=3, z=-2}, vel),
|
|
||||||
maxvel = vector.add({x=2, y=5, z=2}, vel),
|
|
||||||
minacc = {x=0, y=-9.81, z=0},
|
|
||||||
maxacc = {x=0, y=-9.81, z=0},
|
|
||||||
minexptime = 1,
|
|
||||||
maxexptime = 3,
|
|
||||||
minsize = 0.7,
|
|
||||||
maxsize = 0.7,
|
|
||||||
collisiondetection = true,
|
|
||||||
collision_removal = true,
|
|
||||||
object_collision = false,
|
|
||||||
texture = "weather_pack_snow_snowflake"..math.random(1,2)..".png",
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Snowball on_step()--> called when snowball is moving.
|
|
||||||
local function snowball_on_step(self, dtime)
|
|
||||||
self.timer = self.timer + dtime
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local vel = self.object:get_velocity()
|
|
||||||
local node = minetest.get_node(pos)
|
|
||||||
local def = minetest.registered_nodes[node.name]
|
|
||||||
|
|
||||||
-- Destroy when hitting a solid node
|
|
||||||
if self._lastpos.x~=nil then
|
|
||||||
if (def and def.walkable) or not def then
|
|
||||||
minetest.sound_play("mcl_throwing_snowball_impact_hard", { pos = pos, max_hear_distance=16, gain=0.7 }, true)
|
|
||||||
snowball_particles(self._lastpos, vel)
|
|
||||||
self.object:remove()
|
|
||||||
if mod_target and node.name == "mcl_target:target_off" then
|
|
||||||
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if check_object_hit(self, pos, {snowball_vulnerable = 3}) then
|
|
||||||
minetest.sound_play("mcl_throwing_snowball_impact_soft", { pos = pos, max_hear_distance=16, gain=0.7 }, true)
|
|
||||||
snowball_particles(pos, vel)
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
self._lastpos={x=pos.x, y=pos.y, z=pos.z} -- Set _lastpos-->Node will be added at last pos outside the node
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Movement function of egg
|
|
||||||
local function egg_on_step(self, dtime)
|
|
||||||
self.timer = self.timer + dtime
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local node = minetest.get_node(pos)
|
|
||||||
local def = minetest.registered_nodes[node.name]
|
|
||||||
|
|
||||||
-- Destroy when hitting a solid node with chance to spawn chicks
|
|
||||||
if self._lastpos.x then
|
|
||||||
if (def and def.walkable) or not def then
|
|
||||||
-- 1/8 chance to spawn a chick
|
|
||||||
-- FIXME: Chicks have a quite good chance to spawn in walls
|
|
||||||
local r = math.random(1,8)
|
|
||||||
|
|
||||||
if r == 1 then
|
|
||||||
mcl_mobs.spawn_child(self._lastpos, "mobs_mc:chicken")
|
|
||||||
|
|
||||||
-- BONUS ROUND: 1/32 chance to spawn 3 additional chicks
|
|
||||||
local r = math.random(1,32)
|
|
||||||
if r == 1 then
|
|
||||||
local offsets = {
|
|
||||||
{ x=0.7, y=0, z=0 },
|
|
||||||
{ x=-0.7, y=0, z=-0.7 },
|
|
||||||
{ x=-0.7, y=0, z=0.7 },
|
|
||||||
}
|
|
||||||
for o=1, 3 do
|
|
||||||
local pos = vector.add(self._lastpos, offsets[o])
|
|
||||||
mcl_mobs.spawn_child(pos, "mobs_mc:chicken")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
minetest.sound_play("mcl_throwing_egg_impact", { pos = self.object:get_pos(), max_hear_distance=10, gain=0.5 }, true)
|
|
||||||
self.object:remove()
|
|
||||||
if mod_target and node.name == "mcl_target:target_off" then
|
|
||||||
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Destroy when hitting a mob or player (no chick spawning)
|
|
||||||
if check_object_hit(self, pos, 0) then
|
|
||||||
minetest.sound_play("mcl_throwing_egg_impact", { pos = self.object:get_pos(), max_hear_distance=10, gain=0.5 }, true)
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
self._lastpos={x=pos.x, y=pos.y, z=pos.z} -- Set lastpos-->Node will be added at last pos outside the node
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Movement function of ender pearl
|
|
||||||
local function pearl_on_step(self, dtime)
|
|
||||||
self.timer = self.timer + dtime
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
pos.y = math.floor(pos.y)
|
|
||||||
local node = minetest.get_node(pos)
|
|
||||||
local nn = node.name
|
|
||||||
local def = minetest.registered_nodes[node.name]
|
|
||||||
|
|
||||||
-- Destroy when hitting a solid node
|
|
||||||
if self._lastpos.x~=nil then
|
|
||||||
local walkable = (def and def.walkable)
|
|
||||||
|
|
||||||
-- No teleport for hitting ignore for now. Otherwise the player could get stuck.
|
|
||||||
-- FIXME: This also means the player loses an ender pearl for throwing into unloaded areas
|
|
||||||
if node.name == "ignore" then
|
|
||||||
self.object:remove()
|
|
||||||
-- Activate when hitting a solid node or a plant
|
|
||||||
elseif walkable or nn == "mcl_core:vine" or nn == "mcl_core:deadbush" or minetest.get_item_group(nn, "flower") ~= 0 or minetest.get_item_group(nn, "sapling") ~= 0 or minetest.get_item_group(nn, "plant") ~= 0 or minetest.get_item_group(nn, "mushroom") ~= 0 or not def then
|
|
||||||
local player = self._thrower and minetest.get_player_by_name(self._thrower)
|
|
||||||
if player then
|
|
||||||
-- Teleport and hurt player
|
|
||||||
|
|
||||||
-- First determine good teleport position
|
|
||||||
local dir = {x=0, y=0, z=0}
|
|
||||||
|
|
||||||
local v = self.object:get_velocity()
|
|
||||||
if walkable then
|
|
||||||
local vc = table.copy(v) -- vector for calculating
|
|
||||||
-- Node is walkable, we have to find a place somewhere outside of that node
|
|
||||||
vc = vector.normalize(vc)
|
|
||||||
|
|
||||||
-- Zero-out the two axes with a lower absolute value than
|
|
||||||
-- the axis with the strongest force
|
|
||||||
local lv, ld
|
|
||||||
lv, ld = math.abs(vc.y), "y"
|
|
||||||
if math.abs(vc.x) > lv then
|
|
||||||
lv, ld = math.abs(vc.x), "x"
|
|
||||||
end
|
|
||||||
if math.abs(vc.z) > lv then
|
|
||||||
ld = "z" --math.abs(vc.z)
|
|
||||||
end
|
|
||||||
if ld ~= "x" then vc.x = 0 end
|
|
||||||
if ld ~= "y" then vc.y = 0 end
|
|
||||||
if ld ~= "z" then vc.z = 0 end
|
|
||||||
|
|
||||||
-- Final tweaks to the teleporting pos, based on direction
|
|
||||||
-- Impact from the side
|
|
||||||
dir.x = vc.x * -1
|
|
||||||
dir.z = vc.z * -1
|
|
||||||
|
|
||||||
-- Special case: top or bottom of node
|
|
||||||
if vc.y > 0 then
|
|
||||||
-- We need more space when impact is from below
|
|
||||||
dir.y = -2.3
|
|
||||||
elseif vc.y < 0 then
|
|
||||||
-- Standing on top
|
|
||||||
dir.y = 0.5
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- If node was not walkable, no modification to pos is made.
|
|
||||||
|
|
||||||
-- Final teleportation position
|
|
||||||
local telepos = vector.add(pos, dir)
|
|
||||||
local telenode = minetest.get_node(telepos)
|
|
||||||
|
|
||||||
--[[ It may be possible that telepos is walkable due to the algorithm.
|
|
||||||
Especially when the ender pearl is faster horizontally than vertical.
|
|
||||||
This applies final fixing, just to be sure we're not in a walkable node ]]
|
|
||||||
if not minetest.registered_nodes[telenode.name] or minetest.registered_nodes[telenode.name].walkable then
|
|
||||||
if v.y < 0 then
|
|
||||||
telepos.y = telepos.y + 0.5
|
|
||||||
else
|
|
||||||
telepos.y = telepos.y - 2.3
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local oldpos = player:get_pos()
|
|
||||||
-- Teleport and hurt player
|
|
||||||
player:set_pos(telepos)
|
|
||||||
player:set_hp(player:get_hp() - 5, { type = "fall", from = "mod" })
|
|
||||||
|
|
||||||
-- 5% chance to spawn endermite at the player's origin
|
|
||||||
local r = math.random(1,20)
|
|
||||||
if r == 1 then
|
|
||||||
minetest.add_entity(oldpos, "mobs_mc:endermite")
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
self.object:remove()
|
|
||||||
if mod_target and node.name == "mcl_target:target_off" then
|
|
||||||
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self._lastpos={x=pos.x, y=pos.y, z=pos.z} -- Set lastpos-->Node will be added at last pos outside the node
|
|
||||||
end
|
|
||||||
|
|
||||||
snowball_ENTITY.on_step = snowball_on_step
|
|
||||||
egg_ENTITY.on_step = egg_on_step
|
|
||||||
pearl_ENTITY.on_step = pearl_on_step
|
|
||||||
|
|
||||||
minetest.register_entity("mcl_throwing:snowball_entity", snowball_ENTITY)
|
|
||||||
minetest.register_entity("mcl_throwing:egg_entity", egg_ENTITY)
|
|
||||||
minetest.register_entity("mcl_throwing:ender_pearl_entity", pearl_ENTITY)
|
|
||||||
|
|
||||||
|
|
||||||
local how_to_throw = S("Use the punch key to throw.")
|
|
||||||
|
|
||||||
-- Snowball
|
|
||||||
minetest.register_craftitem("mcl_throwing:snowball", {
|
|
||||||
description = S("Snowball"),
|
|
||||||
_tt_help = S("Throwable"),
|
|
||||||
_doc_items_longdesc = S("Snowballs can be thrown or launched from a dispenser for fun. Hitting something with a snowball does nothing."),
|
|
||||||
_doc_items_usagehelp = how_to_throw,
|
|
||||||
inventory_image = "mcl_throwing_snowball.png",
|
|
||||||
stack_max = 64,
|
|
||||||
groups = { weapon_ranged = 1 },
|
|
||||||
on_use = mcl_throwing.get_player_throw_function("mcl_throwing:snowball_entity"),
|
|
||||||
_on_dispense = mcl_throwing.dispense_function,
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Egg
|
|
||||||
minetest.register_craftitem("mcl_throwing:egg", {
|
|
||||||
description = S("Egg"),
|
|
||||||
_tt_help = S("Throwable").."\n"..S("Chance to hatch chicks when broken"),
|
|
||||||
_doc_items_longdesc = S("Eggs can be thrown or launched from a dispenser and breaks on impact. There is a small chance that 1 or even 4 chicks will pop out of the egg."),
|
|
||||||
_doc_items_usagehelp = how_to_throw,
|
|
||||||
inventory_image = "mcl_throwing_egg.png",
|
|
||||||
stack_max = 64,
|
|
||||||
on_use = mcl_throwing.get_player_throw_function("mcl_throwing:egg_entity"),
|
|
||||||
_on_dispense = mcl_throwing.dispense_function,
|
|
||||||
groups = { craftitem = 1 },
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Ender Pearl
|
|
||||||
minetest.register_craftitem("mcl_throwing:ender_pearl", {
|
|
||||||
description = S("Ender Pearl"),
|
|
||||||
_tt_help = S("Throwable").."\n"..minetest.colorize(mcl_colors.YELLOW, S("Teleports you on impact for cost of 5 HP")),
|
|
||||||
_doc_items_longdesc = S("An ender pearl is an item which can be used for teleportation at the cost of health. It can be thrown and teleport the thrower to its impact location when it hits a solid block or a plant. Each teleportation hurts the user by 5 hit points."),
|
|
||||||
_doc_items_usagehelp = how_to_throw,
|
|
||||||
wield_image = "mcl_throwing_ender_pearl.png",
|
|
||||||
inventory_image = "mcl_throwing_ender_pearl.png",
|
|
||||||
stack_max = 16,
|
|
||||||
on_use = mcl_throwing.get_player_throw_function("mcl_throwing:ender_pearl_entity"),
|
|
||||||
groups = { transport = 1 },
|
|
||||||
})
|
|
||||||
|
|
||||||
mcl_throwing.register_throwable_object("mcl_throwing:snowball", "mcl_throwing:snowball_entity", 22)
|
|
||||||
mcl_throwing.register_throwable_object("mcl_throwing:egg", "mcl_throwing:egg_entity", 22)
|
|
||||||
mcl_throwing.register_throwable_object("mcl_throwing:ender_pearl", "mcl_throwing:ender_pearl_entity", 22)
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
local modname = minetest.get_current_modname()
|
||||||
|
local modpath = minetest.get_modpath(modname)
|
||||||
|
local S = minetest.get_translator(modname)
|
||||||
|
|
||||||
|
local how_to_throw = S("Use the punch key to throw.")
|
||||||
|
|
||||||
|
-- Snowball
|
||||||
|
minetest.register_craftitem("mcl_throwing:snowball", {
|
||||||
|
description = S("Snowball"),
|
||||||
|
_tt_help = S("Throwable"),
|
||||||
|
_doc_items_longdesc = S("Snowballs can be thrown or launched from a dispenser for fun. Hitting something with a snowball does nothing."),
|
||||||
|
_doc_items_usagehelp = how_to_throw,
|
||||||
|
inventory_image = "mcl_throwing_snowball.png",
|
||||||
|
stack_max = 16,
|
||||||
|
groups = { weapon_ranged = 1 },
|
||||||
|
on_use = mcl_throwing.get_player_throw_function("mcl_throwing:snowball_entity"),
|
||||||
|
_on_dispense = mcl_throwing.dispense_function,
|
||||||
|
})
|
||||||
|
mcl_throwing.register_throwable_object("mcl_throwing:snowball", "mcl_throwing:snowball_entity", 22)
|
||||||
|
|
||||||
|
-- The snowball entity
|
||||||
|
local function snowball_particles(pos, vel)
|
||||||
|
local vel = vector.normalize(vector.multiply(vel, -1))
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 20,
|
||||||
|
time = 0.001,
|
||||||
|
minpos = pos,
|
||||||
|
maxpos = pos,
|
||||||
|
minvel = vector.add({x=-2, y=3, z=-2}, vel),
|
||||||
|
maxvel = vector.add({x=2, y=5, z=2}, vel),
|
||||||
|
minacc = {x=0, y=-9.81, z=0},
|
||||||
|
maxacc = {x=0, y=-9.81, z=0},
|
||||||
|
minexptime = 1,
|
||||||
|
maxexptime = 3,
|
||||||
|
minsize = 0.7,
|
||||||
|
maxsize = 0.7,
|
||||||
|
collisiondetection = true,
|
||||||
|
collision_removal = true,
|
||||||
|
object_collision = false,
|
||||||
|
texture = "weather_pack_snow_snowflake"..math.random(1,2)..".png",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
vl_projectile.register("mcl_throwing:snowball_entity", {
|
||||||
|
physical = false,
|
||||||
|
timer=0,
|
||||||
|
textures = {"mcl_throwing_snowball.png"},
|
||||||
|
visual_size = {x=0.5, y=0.5},
|
||||||
|
collisionbox = {0,0,0,0,0,0},
|
||||||
|
pointable = false,
|
||||||
|
|
||||||
|
get_staticdata = mcl_throwing.get_staticdata,
|
||||||
|
on_activate = mcl_throwing.on_activate,
|
||||||
|
_vl_projectile = {
|
||||||
|
behaviors = {
|
||||||
|
vl_projectile.collides_with_solids,
|
||||||
|
vl_projectile.collides_with_entities,
|
||||||
|
},
|
||||||
|
on_collide_with_solid = function(self, pos, node)
|
||||||
|
if mod_target and node.name == "mcl_target:target_off" then
|
||||||
|
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
|
||||||
|
end
|
||||||
|
|
||||||
|
snowball_particles(self._last_pos or pos, self.object:get_velocity())
|
||||||
|
end,
|
||||||
|
on_collide_with_entity = function(self, pos, entity)
|
||||||
|
snowball_particles(self._last_pos or pos, self.object:get_velocity())
|
||||||
|
end,
|
||||||
|
sounds = {
|
||||||
|
on_solid_collision = {"mcl_throwing_snowball_impact_hard", { max_hear_distance=16, gain=0.7 }, true},
|
||||||
|
on_entity_collision = {"mcl_throwing_snowball_impact_soft", { max_hear_distance=16, gain=0.7 }, true}
|
||||||
|
},
|
||||||
|
damage_groups = { snowball_vulnerable = 3 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
|
@ -43,7 +43,7 @@ function vl_hollow_logs.register_hollow_log(defs)
|
||||||
local groups = {axey = 1, building_block = 1, handy = 1, hollow_log = 1}
|
local groups = {axey = 1, building_block = 1, handy = 1, hollow_log = 1}
|
||||||
|
|
||||||
if not defs[5] then
|
if not defs[5] then
|
||||||
table.update(groups, {fire_encouragement = 5, fire_flammability = 5, flammable = 2, hollow_log_burnable = 1})
|
groups = table.insert(groups, {fire_encouragement = 5, fire_flammability = 5, flammable = 2, hollow_log_burnable = 1})
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_node(modname .. ":"..name.."_hollow", {
|
minetest.register_node(modname .. ":"..name.."_hollow", {
|
||||||
|
|
|
@ -0,0 +1,341 @@
|
||||||
|
local mod = {}
|
||||||
|
vl_projectile = mod
|
||||||
|
|
||||||
|
local GRAVITY = tonumber(minetest.settings:get("movement_gravity"))
|
||||||
|
local enable_pvp = minetest.settings:get_bool("enable_pvp")
|
||||||
|
|
||||||
|
function mod.update_projectile(self, dtime)
|
||||||
|
local entity_name = self.name
|
||||||
|
local entity_def = minetest.registered_entities[entity_name] or {}
|
||||||
|
local entity_vl_projectile = entity_def._vl_projectile or {}
|
||||||
|
|
||||||
|
-- Update entity timer
|
||||||
|
self.timer = (self.timer or 0) + dtime
|
||||||
|
|
||||||
|
-- Run behaviors
|
||||||
|
local behaviors = entity_vl_projectile.behaviors or {}
|
||||||
|
for i=1,#behaviors do
|
||||||
|
local behavior = behaviors[i]
|
||||||
|
if behavior(self, dtime, entity_def, entity_vl_projectile) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function no_op()
|
||||||
|
end
|
||||||
|
local function damage_particles(pos, is_critical)
|
||||||
|
if is_critical then
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 15,
|
||||||
|
time = 0.1,
|
||||||
|
minpos = vector.offset(pos, -0.5, -0.5, -0.5),
|
||||||
|
maxpos = vector.offset(pos, 0.5, 0.5, 0.5),
|
||||||
|
minvel = vector.new(-0.1, -0.1, -0.1),
|
||||||
|
maxvel = vector.new(0.1, 0.1, 0.1),
|
||||||
|
minexptime = 1,
|
||||||
|
maxexptime = 2,
|
||||||
|
minsize = 1.5,
|
||||||
|
maxsize = 1.5,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_particles_crit.png^[colorize:#bc7a57:127",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function random_hit_positions(positions, placement)
|
||||||
|
if positions == "x" then
|
||||||
|
return math.random(-4, 4)
|
||||||
|
elseif positions == "y" then
|
||||||
|
return math.random(0, 10)
|
||||||
|
end
|
||||||
|
if placement == "front" and positions == "z" then
|
||||||
|
return 3
|
||||||
|
elseif placement == "back" and positions == "z" then
|
||||||
|
return -3
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
local function check_hitpoint(hitpoint)
|
||||||
|
if hitpoint.type ~= "object" then return false end
|
||||||
|
|
||||||
|
-- find the closest object that is in the way of the arrow
|
||||||
|
if hitpoint.ref:is_player() and enable_pvp then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if not hitpoint.ref:is_player() and hitpoint.ref:get_luaentity() then
|
||||||
|
if (hitpoint.ref:get_luaentity().is_mob or hitpoint.ref:get_luaentity()._hittable_by_projectile) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local function handle_player_sticking(self, entity_def, projectile_def, entity)
|
||||||
|
if self._in_player or self._blocked then return end
|
||||||
|
if not projectile_def.sticks_in_players then return end
|
||||||
|
|
||||||
|
minetest.after(150, function()
|
||||||
|
self.object:remove()
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Handle blocking projectiles
|
||||||
|
if mcl_shields.is_blocking(obj) then
|
||||||
|
self._blocked = true
|
||||||
|
self.object:set_velocity(vector.multiply(self.object:get_velocity(), -0.25))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle when the projectile hits the player
|
||||||
|
local placement
|
||||||
|
self._placement = math.random(1, 2)
|
||||||
|
if self._placement == 1 then
|
||||||
|
placement = "front"
|
||||||
|
else
|
||||||
|
placement = "back"
|
||||||
|
end
|
||||||
|
self._in_player = true
|
||||||
|
if self._placement == 2 then
|
||||||
|
self._rotation_station = 90
|
||||||
|
else
|
||||||
|
self._rotation_station = -90
|
||||||
|
end
|
||||||
|
self._y_position = random_arrow_positions("y", placement)
|
||||||
|
self._x_position = random_arrow_positions("x", placement)
|
||||||
|
if self._y_position > 6 and self._x_position < 2 and self._x_position > -2 then
|
||||||
|
self._attach_parent = "Head"
|
||||||
|
self._y_position = self._y_position - 6
|
||||||
|
elseif self._x_position > 2 then
|
||||||
|
self._attach_parent = "Arm_Right"
|
||||||
|
self._y_position = self._y_position - 3
|
||||||
|
self._x_position = self._x_position - 2
|
||||||
|
elseif self._x_position < -2 then
|
||||||
|
self._attach_parent = "Arm_Left"
|
||||||
|
self._y_position = self._y_position - 3
|
||||||
|
self._x_position = self._x_position + 2
|
||||||
|
else
|
||||||
|
self._attach_parent = "Body"
|
||||||
|
end
|
||||||
|
self._z_rotation = math.random(-30, 30)
|
||||||
|
self._y_rotation = math.random( -30, 30)
|
||||||
|
self.object:set_attach(
|
||||||
|
obj, self._attach_parent,
|
||||||
|
vector.new(self._x_position, self._y_position, random_arrow_positions("z", placement)),
|
||||||
|
vector.new(0, self._rotation_station + self._y_rotation, self._z_rotation)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mod.collides_with_solids(self, dtime, entity_def, projectile_def)
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
|
||||||
|
-- Don't try to do anything on first update
|
||||||
|
if not self._last_pos then
|
||||||
|
self._last_pos = pos
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if the object can collide with this node
|
||||||
|
local node = minetest.get_node(pos)
|
||||||
|
local node_def = minetest.registered_nodes[node.name]
|
||||||
|
local collides_with = projectile_def.collides_with
|
||||||
|
|
||||||
|
if entity_def.physical then
|
||||||
|
-- Projectile has stopped in one axis, so it probably hit something.
|
||||||
|
-- This detection is a bit clunky, but sadly, MT does not offer a direct collision detection for us. :-(
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
|
if not( (math.abs(vel.x) < 0.0001) or (math.abs(vel.z) < 0.0001) or (math.abs(vel.y) < 0.00001) ) then
|
||||||
|
self._last_pos = pos
|
||||||
|
return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if node_def and not node_def.walkable and (not collides_with or not mcl_util.match_node_to_filter(node.name, collides_with)) then
|
||||||
|
self._last_pos = pos
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Call entity collied hook
|
||||||
|
local hook = projectile_def.on_collide_with_solid or no_op
|
||||||
|
hook(self, pos, node, node_def)
|
||||||
|
|
||||||
|
-- Call node collided hook
|
||||||
|
local hook = (node_def._vl_projectile or {}).on_collide or no_op
|
||||||
|
hook(self, pos, node, node_def)
|
||||||
|
|
||||||
|
-- Play sounds
|
||||||
|
local sounds = projectile_def.sounds or {}
|
||||||
|
local sound = sounds.on_solid_collision or sounds.on_collision
|
||||||
|
if sound then
|
||||||
|
local arg2 = table.copy(sound[2])
|
||||||
|
arg2.pos = pos
|
||||||
|
minetest.sound_play(sound[1], arg2, sound[3])
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Normally objects should be removed on collision with solids
|
||||||
|
if not projectile_def.survive_collision then
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Done with behaviors
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handle_entity_collision(self, entity_def, projectile_def, entity)
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
local dir = vector.normalize(self.object:get_velocity())
|
||||||
|
local self_vl_projectile = self._vl_projectile
|
||||||
|
|
||||||
|
-- Allow punching
|
||||||
|
local allow_punching = projectile_def.allow_punching or true
|
||||||
|
if type(allow_punching) == "function" then
|
||||||
|
allow_punching = allow_punching(self, entity_def, projectile_def, entity)
|
||||||
|
end
|
||||||
|
print("allow_punching="..tostring(allow_punching))
|
||||||
|
|
||||||
|
if allow_punching then
|
||||||
|
-- Get damage
|
||||||
|
local dmg = projectile_def.damage_groups or 0
|
||||||
|
if type(dmg) == "function" then
|
||||||
|
dmg = dmg(self, entity_def, projectile_def, entity)
|
||||||
|
end
|
||||||
|
|
||||||
|
local entity_lua = entity:get_luaentity()
|
||||||
|
|
||||||
|
-- Apply damage
|
||||||
|
-- Note: Damage blocking for shields is handled in mcl_shields with an mcl_damage modifier
|
||||||
|
local do_damage = false
|
||||||
|
if entity:is_player() and projectile_def.hits_players and self_vl_projectile.owner ~= hit:get_player_name() then
|
||||||
|
do_damage = true
|
||||||
|
|
||||||
|
handle_player_sticking(self, entity_def, projectile_def, entity)
|
||||||
|
elseif entity_lua and (entity_lua.is_mob == true or entity_lua._hittable_by_projectile) and (self_vl_projectile.owner ~= entity) then
|
||||||
|
do_damage = true
|
||||||
|
entity:punch(self.object, 1.0, projectile_def.tool or { full_punch_interval = 1.0, damage_groups = dmg }, dir )
|
||||||
|
end
|
||||||
|
|
||||||
|
if do_damage then
|
||||||
|
entity:punch(self.object, 1.0, projectile_def.tool or { full_punch_interval = 1.0, damage_groups = dmg }, dir )
|
||||||
|
|
||||||
|
-- Indicate damage
|
||||||
|
damage_particles(vector.add(pos, vector.multiply(self.object:get_velocity(), 0.1)), self._is_critical)
|
||||||
|
|
||||||
|
-- Light things on fire
|
||||||
|
if mcl_burning.is_burning(self.object) then
|
||||||
|
mcl_burning.set_on_fire(obj, 5)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Call entity collision hook
|
||||||
|
(projectile_def.on_collide_with_entity or no_op)(self, pos, entity)
|
||||||
|
|
||||||
|
-- Call reverse entity collision hook
|
||||||
|
local other_entity_def = minetest.registered_entities[entity.name] or {}
|
||||||
|
local other_entity_vl_projectile = other_entity_def._vl_projectile or {}
|
||||||
|
local hook = (other_entity_vl_projectile or {}).on_collide or no_op
|
||||||
|
hook(entity, self)
|
||||||
|
|
||||||
|
-- Play sounds
|
||||||
|
local sounds = (projectile_def.sounds or {})
|
||||||
|
local sound = sounds.on_entity_collide or sounds.on_collision
|
||||||
|
if type(sound) == "function" then sound = sound(self, entity_def, projectile_def, entity)
|
||||||
|
if sound then
|
||||||
|
local arg2 = table.copy(sound[2])
|
||||||
|
arg2.pos = pos
|
||||||
|
minetest.sound_play(sound[1], arg2, sound[3])
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Normally objects should be removed on collision with entities
|
||||||
|
if not projectile_def.survive_collision then
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function mod.collides_with_entities(self, dtime, entity_def, projectile_def)
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
|
||||||
|
local hit = nil
|
||||||
|
local owner = self._vl_projectile.owner
|
||||||
|
|
||||||
|
local objects = minetest.get_objects_inside_radius(pos, 1.5)
|
||||||
|
for i = 1,#objects do
|
||||||
|
local object = objects[i]
|
||||||
|
local entity = object:get_luaentity()
|
||||||
|
|
||||||
|
if entity and entity.name ~= self.object:get_luaentity().name then
|
||||||
|
if object:is_player() and owner ~= object:get_player_name() then
|
||||||
|
return handle_entity_collision(self, entity_def, projectile_def, object)
|
||||||
|
elseif (entity.is_mob == true or entity._hittable_by_projectile) and (owner ~= object) then
|
||||||
|
return handle_entity_collision(self, entity_def, projectile_def, object)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mod.raycast_collides_with_entities(self, dtime, entity_def, projectile_def)
|
||||||
|
local closest_object
|
||||||
|
local closest_distance
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
local arrow_dir = self.object:get_velocity()
|
||||||
|
|
||||||
|
--create a raycast from the arrow based on the velocity of the arrow to deal with lag
|
||||||
|
local raycast = minetest.raycast(pos, vector.add(pos, vector.multiply(arrow_dir, 0.1)), true, false)
|
||||||
|
for hitpoint in raycast do
|
||||||
|
if check_hitpoint(hitpoint) then
|
||||||
|
local hitpoint_ref = hitpoint.ref
|
||||||
|
local dist = vector.distance(hitpoint_ref:get_pos(), pos)
|
||||||
|
if not closest_distance or dist < closest_distance then
|
||||||
|
closest_object = hitpoint_ref
|
||||||
|
closest_distance = dist
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
if closest_object then
|
||||||
|
return handle_entity_collision(self, entity_def, projectile_def, closest_object)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mod.create(entity_id, options)
|
||||||
|
local obj = minetest.add_entity(options.pos, entity_id, options.staticdata)
|
||||||
|
|
||||||
|
-- Set initial velocoty and acceleration
|
||||||
|
obj:set_velocity(vector.multiply(options.dir or vector.zero(), options.velocity or 0))
|
||||||
|
obj:set_acceleration(vector.add(
|
||||||
|
vector.multiply(options.dir or vector.zero(), -math.abs(options.drag)),
|
||||||
|
vector.new(0,-GRAVITY,0)
|
||||||
|
))
|
||||||
|
|
||||||
|
-- Update projectile parameters
|
||||||
|
local luaentity = obj:get_luaentity()
|
||||||
|
luaentity._vl_projectile = {
|
||||||
|
owner = options.owner,
|
||||||
|
extra = options.extra,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Make the update function easy to get to
|
||||||
|
luaentity.update_projectile = mod.update_projectile
|
||||||
|
|
||||||
|
-- And provide the caller with the created object
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
function mod.register(name, def)
|
||||||
|
assert(def._vl_projectile)
|
||||||
|
|
||||||
|
if not def.on_step then
|
||||||
|
def.on_step = mod.update_projectile
|
||||||
|
end
|
||||||
|
|
||||||
|
def._thrower = nil
|
||||||
|
def._shooter = nil
|
||||||
|
def._last_pos = nil
|
||||||
|
|
||||||
|
minetest.register_entity(name, def)
|
||||||
|
end
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
name = vl_projectile
|
||||||
|
depends = mcl_util
|
|
@ -1,199 +0,0 @@
|
||||||
## 0.87 – The Prismatic release
|
|
||||||
|
|
||||||
### Contributors
|
|
||||||
#### New Developers
|
|
||||||
* rudzik8
|
|
||||||
* teknomunk
|
|
||||||
|
|
||||||
#### New Contributors
|
|
||||||
* PrWalterB
|
|
||||||
* michaljmalinowski
|
|
||||||
* nixnoxus
|
|
||||||
* Potiron
|
|
||||||
* Tuxilio
|
|
||||||
* Impulse
|
|
||||||
* Doods
|
|
||||||
* SOS-Games
|
|
||||||
* Bram
|
|
||||||
* qoheniac
|
|
||||||
* WillConker
|
|
||||||
|
|
||||||
### Game rename
|
|
||||||
Based on months of collecting suggestions, analysis and vetting of possible names, community voting and discussion between developers, the rename of the game has reached its conclusion! The project has been renamed to **VoxeLibre**.
|
|
||||||
|
|
||||||
Along with this, a documentation update has been conducted by Herowl, teknomunk and rudzik8. Make sure to check out the updated Contributing Guidelines!
|
|
||||||
|
|
||||||
### Potions and Effects redo
|
|
||||||
After more than half a year of work, it is finally here! The whole system has been rewritten from the ground up by Herowl. New effects, potions and brewing recipes have been added. Potion tooltips have been reworked. In the HUD you can now see more information about what effects you have at the moment, including level and duration. Beacon has also received more effects and is now quite functional.
|
|
||||||
|
|
||||||
A few new items to be used as brewing ingredients have been added. For now you can obtain them from fishing and trading with villagers. Those items will be made obtainable from other sources and will get more uses, hopefully in the next release. The functionality of some effects is not complete and they are also not obtainable yet (hero of the village and conduit power).
|
|
||||||
|
|
||||||
Some of the old potions and tipped arrows don't work with the new API and have to be converted. To avoid constant rechecking of all inventories, they have been bound to placeholder definitions. What this means is that if you notice weird potions or arrows marked with question marks, you will have to right-click them to run the conversion. A small price to pay for less lag, right?
|
|
||||||
|
|
||||||
Improved support of mobs by the effects and potions, including effects being properly saved on mobs. Despite that, some effects still don't work with mobs, because the mobs' code doesn't support them properly:
|
|
||||||
|
|
||||||
* the following effects don't work with mobs at all: water breathing, dolphin's grace, leaping, swiftness, slowness, slow falling, night vision, darkness, frost, health boost, absorption, fire resistance, resistance, luck, bad luck, blindness, nausea, food poisoning, saturation, haste, fatigue, conduit power
|
|
||||||
* the following effects should work with mobs: invisibility, regeneration, poison, withering, strength, weakness, levitation, glowing
|
|
||||||
* the following effects have no effect on mobs (but can be applied with the API): bad omen, hero of the village
|
|
||||||
|
|
||||||
While not everything is available in game, a great API (documented in the module) has been exposed for modders, allowing adding new effects and potions. Potions can now have all sorts of custom effects and multiple effects at once. Effects and potions can now have indefinitely many levels and potentially infinite duration, available with the `/effect` command and the modding API. Effects can still (and even more so than before!) be fine-tuned with factors and abnormal levels, which can sometimes give unexpected results, but it's all left up to modders.
|
|
||||||
|
|
||||||
### Nether Portals rewrite
|
|
||||||
Another large rework! Thanks to emptyshore, portals to (and from) the Nether now work better than ever, connecting properly to each other. They shouldn't cause unwelcome surprises either, stranding you where you never expected to go, and shouldn't teleport you up into the skies.
|
|
||||||
|
|
||||||
### Mob spawning system
|
|
||||||
An update by Bakawun improved the mob spawning, optimizing it and making the randomness work better, as well as properly taking into account set spawn chances. This update also changed the mob spawn chances and ratios.
|
|
||||||
|
|
||||||
Another improvement to the system was made by teknomunk, who wrote a new system for spawn position calculation. This enables overhead spawning, among other things allowing for some mob farms to work.
|
|
||||||
|
|
||||||
Also, light and height checking of Slimes has been fixed by Codiac, so they should no longer spawn in large numbers in inappropriate places.
|
|
||||||
|
|
||||||
### Mob improvements
|
|
||||||
Not only did mob spawning get improved, but mobs themselves did too.
|
|
||||||
|
|
||||||
Rover is a new mob, replacing the enderman. Along with this rework by Herowl and teknomunk, node picking code was refactored and generalized, paving the way for more mobs visibly holding actual items in the future.
|
|
||||||
|
|
||||||
Stalker is another new mob, replacing the creeper. This rework completed by Herowl contains a new camouflage mechanic and otherwise a new look at a well-known concept.
|
|
||||||
|
|
||||||
Ghast received a great update by Bakawun and Herowl. Its hitbox should now work properly, and deflecting the fireballs should work properly again too. Also the achievement for killing a Ghast with a ghast fireball should now be granted properly.
|
|
||||||
|
|
||||||
Sounds for Hoglin/Zoglin, Piglin and skeletons have been updated by Bakawun and should now be used properly.
|
|
||||||
|
|
||||||
Strider received a few fixes by nixnoxus. Breeding, attracting and riding should now work.
|
|
||||||
|
|
||||||
### Eating animation
|
|
||||||
Eating is no longer instant in survival mode, but delayed instead with the new system designed by Eliy21.
|
|
||||||
|
|
||||||
To signify it properly, Herowl added an animation visible in the first-person mode.
|
|
||||||
|
|
||||||
### New blocks
|
|
||||||
* Colored End Rod variants by Herowl.
|
|
||||||
* Colored Redstone Lamps by Herowl.
|
|
||||||
* Glazed Terracotta Pillars by Potiron.
|
|
||||||
* Compressed Cobblestone by SmokeyDope.
|
|
||||||
* Clovers and Four-leaf Clovers by Herowl.
|
|
||||||
* Hollow logs by JoseDouglas26 and Herowl
|
|
||||||
|
|
||||||
### Capes
|
|
||||||
Thanks to the changes by chmodsayshello and rudzik8, you can now pick a cape in the character skin customization UI. Thanks for the "Minetest" cape texture to QwertyDragon.
|
|
||||||
|
|
||||||
### Colored leather armor
|
|
||||||
Leather armor can now be colored (and washed) thanks to AFCMS and Herowl. Aside of the crafting recipes, this added a command and a modding API for this.
|
|
||||||
|
|
||||||
### Cherry blossom particles
|
|
||||||
The particles of the cherry blossom, which fall from the cherry leaves, have been vastly improved by Wbjitscool and Herowl. Plant some cherry trees and behold the new animation and the wind direction changing 3 times a game day.
|
|
||||||
|
|
||||||
### Signs text editing
|
|
||||||
Now you can edit the text on signs by right-clicking ("place") on a sign placed in the world, all thanks to Araca.
|
|
||||||
|
|
||||||
### Tool durability tooltips
|
|
||||||
Yet another feature from Araca, tooltips for tools (and weapons) will now display how much durability they have remaining. Now you have more precise info than just the wearbar!
|
|
||||||
|
|
||||||
### Creative inventory fixes
|
|
||||||
Items can't be moved around in the creative inventory, tabs there have proper tooltips and searching works on Android with Minetest 5.8+ – all thanks to rudzik8.
|
|
||||||
|
|
||||||
### Help UI – Mobs section
|
|
||||||
A "Mobs" section added to the Help UI by SOS-Games. Translations may be missing.
|
|
||||||
|
|
||||||
### Texture pack converter
|
|
||||||
One of our tools, the Python script allowing conversion of Minecraft resource packs (texture-wise) to the Minetest format to work with our game, has received a great update by Impulse and Doods. It should now work with packs from newer versions, but keep in mind that not everything can be automated and the packs may still require some manual fixes (and additions, if you want to cover all of our own features that have no equivalents in Minecraft).
|
|
||||||
|
|
||||||
### New Translation
|
|
||||||
* Occitan by PrWalterB
|
|
||||||
|
|
||||||
### Translation updates
|
|
||||||
* Spanish by megustanlosfrijoles
|
|
||||||
* French by syl
|
|
||||||
* Polish by Herowl
|
|
||||||
* German by Tuxilio, Herowl and qoheniac
|
|
||||||
* Syntax fixes in translation files by megustanlosfrijoles
|
|
||||||
|
|
||||||
### Other changes
|
|
||||||
* Melon and pumpkin generation – by michaljmalinowski
|
|
||||||
* Golden rails accelerate carts properly – by nixnoxus
|
|
||||||
* Elytra Animation works again – by MrRar
|
|
||||||
* Mobs aggro disabled when damage is disabled – by emptyshore
|
|
||||||
* Fortune enchantment on hoes works – by JoseDouglas26
|
|
||||||
* Typo in pumpkin.lua fixed – by SmokeyDope
|
|
||||||
* Node rotation at placement improvements – by JoseDouglas26
|
|
||||||
* Nylium reverting to netherrack – by JoseDouglas26
|
|
||||||
* Hunger debug setting exposed properly – by SmokeyDope
|
|
||||||
* Nether vine placement fixes – by SmokeyDope
|
|
||||||
* Survival inventory tabs API fixes – by Impulse
|
|
||||||
* Cactus damaging mobs – by Eliy21
|
|
||||||
* Sweet berry bush slowdown decreased – by Eliy21
|
|
||||||
* Fixed scaffolding placement replacing other blocks without a trace – by JoseDouglas26
|
|
||||||
* Nodes fireproofing and missing plank recipes added – by Doods
|
|
||||||
* End Rods now use a proper mesh model – by Herowl
|
|
||||||
* Piglin bartering improvements – by nixnoxus
|
|
||||||
* Hopper item movement improved – by teknomunk
|
|
||||||
* Partial item stack pickup – by teknomunk
|
|
||||||
* Bone meal node protection check – by CyberMango
|
|
||||||
* Undeclared variable usage fixed – by nixnoxus
|
|
||||||
* Biome check when spawning override (API, Skyblock support) – by AncientMariner
|
|
||||||
* Reimported tga_encoder as subtree (this allows support of some mods) – by Herowl
|
|
||||||
* Bed placement and destruction fixes – by teknomunk
|
|
||||||
* Item tooltip shouldn't be modified needlessly (this fixes some bugs causing items to not stack properly) – by Herowl
|
|
||||||
* Beds now properly ignore players in other dimensions – by nixnoxus
|
|
||||||
* Stray pixels in leather cap texture removed – by SmokeyDope
|
|
||||||
* Allow lecterns to be placed on sides of blocks – by JoseDouglas26
|
|
||||||
* Fix warnings – by JoseDouglas26
|
|
||||||
* Experience from trading – by nixnoxus
|
|
||||||
* Boats easier to destroy with punching – by Eliy21
|
|
||||||
* Horse and Donkey animation fix – by Bakawun
|
|
||||||
* Villagers won't eat shulker boxes (independent of their food content) anymore – by teknomunk
|
|
||||||
* Beds now properly ignore players in the wrong dimensions when counting – by nixnoxus
|
|
||||||
* Shears now wear properly when harvesting comb from a beehive – by teknomunk
|
|
||||||
* Improved compatibility with mapgen mods – by Bram
|
|
||||||
* Item frame attachment fixed – by rudzik8
|
|
||||||
* Stray pixels in sweet berry textures removed – by rudzik8
|
|
||||||
* Warning related to milk bucket fixed – by teknomunk
|
|
||||||
* Seed is now logged when entering a world – by Nicu
|
|
||||||
* Startup warnings from mcl_stonecutter fixed – by Herowl
|
|
||||||
* Sleeping GUI improved – by Nicu
|
|
||||||
* Description capitalization fix – by syl
|
|
||||||
|
|
||||||
### Special thanks
|
|
||||||
* To emptyshore, for the in-depth research and testing of the Mob Spawning System rework, as well as his aforementioned Nether Portals system rework.
|
|
||||||
|
|
||||||
### Crash fixes
|
|
||||||
* Damage animation related crash – by Herowl
|
|
||||||
* Shields-related crash – by Impulse
|
|
||||||
* Elytra-related crash – by Herowl
|
|
||||||
* Damage animation and player invulnerability related crash – by Eliy21
|
|
||||||
* Rocket explosion related crash – by Herowl
|
|
||||||
* New game load crash – by AncientMariner
|
|
||||||
* XP orbs related crash – by teknomunk
|
|
||||||
* Ghast fireball related crash – by Araca
|
|
||||||
* Crash related to server restart while a player is dead – by teknomunk
|
|
||||||
* Crashes related to the new effects API – by teknomunk and Herowl
|
|
||||||
|
|
||||||
## 0.87.1 hotfix
|
|
||||||
* Fixed crash when shooting potions from a dispenser – by teknomunk
|
|
||||||
* Fixed crash related to custom mobspawners – by teknomunk
|
|
||||||
* Fixed beacon crash – by teknomunk
|
|
||||||
* Fixed eye of ender crash – by Herowl
|
|
||||||
* Fixed Stalker texture generation – by teknomunk
|
|
||||||
* Correctly refresh enchanted tool capabilities – by teknomunk
|
|
||||||
* Fixed creative inventory misbehaving – by Herowl
|
|
||||||
* Fixed variable definition in mob spawning code – by teknomunk
|
|
||||||
* Updated documentation – by Herowl and teknomunk
|
|
||||||
* Increased stack size for snowballs and eggs – by JoseDouglas26
|
|
||||||
|
|
||||||
## 0.87.2 hotfix
|
|
||||||
* Zombie texture improvements – by SmokeyDope
|
|
||||||
* Wrong name of diorite stairs fixed – by qoheniac
|
|
||||||
* Fixed flint and steel wearing down when not placing fire – by JoseDouglas26 and WillConker
|
|
||||||
* Fixed brewing stands' rotation – by JoseDouglas26 and WillConker
|
|
||||||
* Fixed beacon formspec – by teknomunk
|
|
||||||
* Made all hollow logs breakable properly – by teknomunk
|
|
||||||
* Instructions on how to eat added to the help menu – by teknomunk
|
|
||||||
* Potion conversion fixed – by Herowl
|
|
||||||
* Fixed some node names – by seventeenthShulker
|
|
||||||
* Fixed anvil and craftguide formspecs on mobile – by Herowl
|
|
||||||
* Fixed effect loading – by Herowl
|
|
||||||
* Fixed crash while fighting wither – by teknomunk
|
|
||||||
* Fixed crash when bonemealing sweet berry bushes – by teknomunk
|
|
||||||
* Fixed some mob conversion crashes – by teknomunk
|
|
||||||
* Fixed crash related to the frost walker enchantment – by WillConker
|
|
||||||
* Fixed some mob-related crashes – by Herowl
|
|
Binary file not shown.
Before Width: | Height: | Size: 412 B After Width: | Height: | Size: 317 B |
Binary file not shown.
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.1 KiB |
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
local colors = {
|
local colors = {
|
||||||
["Creator of MineClone"] = "0x0A9400",
|
["Creator of MineClone"] = "0x0A9400",
|
||||||
["Creator of VoxeLibre"] = "0xFBF837",
|
["Creator of MineClone2"] = "0xFBF837",
|
||||||
["Maintainers"] = "0xFF51D5",
|
["Maintainers"] = "0xFF51D5",
|
||||||
["Developers"] = "0xF84355",
|
["Developers"] = "0xF84355",
|
||||||
["Past Developers"] = "0xF84355",
|
["Past Developers"] = "0xF84355",
|
||||||
|
|
Loading…
Reference in New Issue