Compare commits

..

8 Commits

71 changed files with 1725 additions and 2117 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -802,9 +802,7 @@ 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 objs = minetest.get_objects_inside_radius(alert_pos, self.view_range)
local obj = nil local obj = nil
for n = 1, #objs do for n = 1, #objs do
@ -836,7 +834,6 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
end end
end end
end end
end
function mob_class:check_aggro(dtime) function mob_class:check_aggro(dtime)
if not self._aggro or not self.attack then return end if not self._aggro or not self.attack then return end

View File

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

View File

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

View File

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

View File

@ -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",

View File

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

View File

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

View File

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

View File

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

View File

@ -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",

View File

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

View File

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

View File

@ -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
@ -221,13 +218,7 @@ local function apply_effects_to_all_players(pos)
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
@ -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",

View File

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

View File

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

View File

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

View File

@ -56,33 +56,6 @@ S("Arrows might get stuck on solid blocks and can be retrieved again. They are a
end, end,
}) })
local ARROW_ENTITY={
physical = true,
pointable = false,
visual = "mesh",
mesh = "mcl_bows_arrow.obj",
visual_size = {x=-1, y=1},
textures = {"mcl_bows_arrow.png"},
collisionbox = {-0.19, -0.125, -0.19, 0.19, 0.125, 0.19},
collide_with_objects = false,
_fire_damage_resistant = true,
_lastpos={},
_startpos=nil,
_damage=1, -- Damage on impact
_is_critical=false, -- Whether this arrow would deal critical damage
_stuck=false, -- Whether arrow is stuck
_stucktimer=nil,-- Amount of time (in seconds) the arrow has been stuck so far
_stuckrechecktimer=nil,-- An additional timer for periodically re-checking the stuck status of an arrow
_stuckin=nil, --Position of node in which arow is stuck.
_shooter=nil, -- ObjectRef of player or mob who shot it
_is_arrow = true,
_in_player = false,
_blocked = false,
_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
}
-- Destroy arrow entity self at pos and drops it as an item -- Destroy arrow entity self at pos and drops it as an item
local function spawn_item(self, pos) local function spawn_item(self, pos)
if not minetest.is_creative_enabled("") then if not minetest.is_creative_enabled("") then
@ -94,38 +67,7 @@ local function spawn_item(self, pos)
self.object:remove() self.object:remove()
end end
local function damage_particles(pos, is_critical) local function stuck_arrow_on_step(self, dtime)
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
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._stucktimer = self._stucktimer + dtime
self._stuckrechecktimer = self._stuckrechecktimer + dtime self._stuckrechecktimer = self._stuckrechecktimer + dtime
if self._stucktimer > ARROW_TIMEOUT then if self._stucktimer > ARROW_TIMEOUT then
@ -133,6 +75,9 @@ function ARROW_ENTITY.on_step(self, dtime)
self.object:remove() self.object:remove()
return return
end end
local pos = self.object:get_pos()
-- Drop arrow as item when it is no longer stuck -- 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. -- 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 if self._stuckrechecktimer > STUCK_RECHECK_TIME then
@ -147,6 +92,7 @@ function ARROW_ENTITY.on_step(self, dtime)
end end
self._stuckrechecktimer = 0 self._stuckrechecktimer = 0
end end
-- Pickup arrow if player is nearby (not in Creative Mode) -- Pickup arrow if player is nearby (not in Creative Mode)
local objects = minetest.get_objects_inside_radius(pos, 1) local objects = minetest.get_objects_inside_radius(pos, 1)
for _,obj in ipairs(objects) do for _,obj in ipairs(objects) do
@ -166,197 +112,71 @@ function ARROW_ENTITY.on_step(self, dtime)
return return
end end
end end
-- 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 end
local closest_object vl_projectile.register("mcl_bows:arrow_entity", {
local closest_distance physical = true,
pointable = false,
visual = "mesh",
mesh = "mcl_bows_arrow.obj",
visual_size = {x=-1, y=1},
textures = {"mcl_bows_arrow.png"},
collisionbox = {-0.19, -0.125, -0.19, 0.19, 0.125, 0.19},
collide_with_objects = false,
_fire_damage_resistant = true,
if self._deflection_cooloff > 0 then _save_fields = {
self._deflection_cooloff = self._deflection_cooloff - dtime "last_pos", "startpos", "damage", "is_critical", "stuck", "stuckin", "stuckin_player",
end },
local arrow_dir = self.object:get_velocity() _startpos=nil,
--create a raycast from the arrow based on the velocity of the arrow to deal with lag _damage=1, -- Damage on impact
local raycast = minetest.raycast(pos, vector.add(pos, vector.multiply(arrow_dir, 0.1)), true, false) _is_critical=false, -- Whether this arrow would deal critical damage
for hitpoint in raycast do _stuck=false, -- Whether arrow is stuck
if hitpoint.type == "object" then _stucktimer=nil,-- Amount of time (in seconds) the arrow has been stuck so far
-- find the closest object that is in the way of the arrow _stuckrechecktimer=nil,-- An additional timer for periodically re-checking the stuck status of an arrow
local ok = false _stuckin=nil, --Position of node in which arow is stuck.
if hitpoint.ref:is_player() and enable_pvp then _shooter=nil, -- ObjectRef of player or mob who shot it
ok = true _is_arrow = true,
elseif not hitpoint.ref:is_player() and hitpoint.ref:get_luaentity() then _in_player = false,
if (hitpoint.ref:get_luaentity().is_mob or hitpoint.ref:get_luaentity()._hittable_by_projectile) then _blocked = false,
ok = true _viscosity=0, -- Viscosity of node the arrow is currently in
end _deflection_cooloff=0, -- Cooloff timer after an arrow deflection, to prevent many deflections in quick succession
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 _vl_projectile = {
local obj = closest_object survive_collision = true,
local is_player = obj:is_player() sticks_in_players = true,
local lua = obj:get_luaentity() damage_groups = function(self)
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 return { fleshy = self._damage }
if obj:get_hp() > 0 then end,
-- Check if there is no solid node between arrow and object behaviors = {
local ray = minetest.raycast(self.object:get_pos(), obj:get_pos(), true) vl_projectile.collides_with_solids,
for pointed_thing in ray do vl_projectile.raycast_collides_with_entities,
if pointed_thing.type == "object" and pointed_thing.ref == closest_object then },
-- Target reached! We can proceed now. allow_punching = function(self, entity_def, projectile_def, entity)
break local lua = entity:get_luaentity()
elseif pointed_thing.type == "node" then if lua and lua.name == "mobs_mc:rover" then return false end
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. return true
if not lua or lua.name ~= "mobs_mc:rover" then end,
if not self._in_player then sounds = {
damage_particles(vector.add(pos, vector.multiply(self.object:get_velocity(), 0.1)), self._is_critical) on_entity_collision = function(self, _, _, obj)
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 obj:is_player() then
if not mcl_shields.is_blocking(obj) then return {{name="mcl_bows_hit_player", gain=0.1}, {to_player=self._shooter:get_player_name()}, true}
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 end
return {{name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true}
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
end },
on_collide_with_solid = function(self, pos, node, node_def)
if lua then local def = node_def
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() local vel = self.object:get_velocity()
-- Arrow has stopped in one axis, so it probably hit something. local dpos = vector.round(vector.new(pos)) -- digital pos
-- 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,7 +200,8 @@ 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
else return
end
-- Node was walkable, make arrow stuck -- Node was walkable, make arrow stuck
self._stuck = true self._stuck = true
@ -414,16 +235,112 @@ function ARROW_ENTITY.on_step(self, dtime)
mesecon.push_button(dpos, node) mesecon.push_button(dpos, node)
end end
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
if not (is_player or (lua and (lua.is_mob or lua._hittable_by_projectile))) then
return
end end
elseif (def and def.liquidtype ~= "none") then end
if obj:get_hp() > 0 then
-- Check if there is no solid node between arrow and object
-- TODO: remove. this code should never occur if vl_projectile is working correctly
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 == obj 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
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
end
if not obj:is_player() then
mcl_burning.extinguish(self.object)
if self._piercing == 0 then
self.object:remove()
end
end
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 -- Slow down arrow in liquids
local v = def.liquid_viscosity local v = def.liquid_viscosity or 0
if not v then
v = 0
end
--local old_v = self._viscosity
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,38 +349,34 @@ function ARROW_ENTITY.on_step(self, dtime)
end end
self.object:set_velocity(vel) self.object:set_velocity(vel)
end end
end
-- Process as projectile
vl_projectile.update_projectile(self, dtime)
-- Update yaw -- Update yaw
if not self._stuck then
local vel = self.object:get_velocity() local vel = self.object:get_velocity()
if vel and not self._stuck then
local yaw = minetest.dir_to_yaw(vel)+YAW_OFFSET local yaw = minetest.dir_to_yaw(vel)+YAW_OFFSET
local pitch = dir_to_pitch(vel) local pitch = dir_to_pitch(vel)
self.object:set_rotation({ x = 0, y = yaw, z = pitch }) self.object:set_rotation({ x = 0, y = yaw, z = pitch })
end end
end,
-- Update internal variable
self._lastpos = pos
end
-- Force recheck of stuck arrows when punched. -- Force recheck of stuck arrows when punched.
-- Otherwise, punching has no effect. -- Otherwise, punching has no effect.
function ARROW_ENTITY.on_punch(self) on_punch = function(self)
if self._stuck then if self._stuck then
self._stuckrechecktimer = STUCK_RECHECK_TIME self._stuckrechecktimer = STUCK_RECHECK_TIME
end end
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
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 self._stuck then
-- If _stucktimer is missing for some reason, assume the maximum -- If _stucktimer is missing for some reason, assume the maximum
if not self._stucktimer then if not self._stucktimer then
@ -471,40 +384,35 @@ function ARROW_ENTITY.get_staticdata(self)
end end
out.stuckstarttime = minetest.get_gametime() - self._stucktimer out.stuckstarttime = minetest.get_gametime() - self._stucktimer
end end
if self._shooter and self._shooter:is_player() then if self._shooter and self._shooter:is_player() then
out.shootername = self._shooter:get_player_name() out.shootername = self._shooter:get_player_name()
end end
return minetest.serialize(out) return minetest.serialize(out)
end end,
on_activate = function(self, staticdata, dtime_s)
self.object:set_armor_groups({ immortal = 1 })
function ARROW_ENTITY.on_activate(self, staticdata, dtime_s)
self._time_in_air = 1.0 self._time_in_air = 1.0
local data = minetest.deserialize(staticdata) local data = minetest.deserialize(staticdata)
if data then if not data then return end
self._stuck = data.stuck
if data.stuck then -- 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 if data.stuckstarttime then
-- First, check if the stuck arrow is aleady past its life timer. -- First, check if the stuck arrow is aleady past its life timer.
-- If yes, delete it. -- If yes, delete it.
self._stucktimer = minetest.get_gametime() - data.stuckstarttime self._stucktimer = minetest.get_gametime() - data.stuckstarttime
if self._stucktimer > ARROW_TIMEOUT then
mcl_burning.extinguish(self.object)
self.object:remove()
return
end
end end
-- Perform a stuck recheck on the next step. -- Perform a stuck recheck on the next step.
self._stuckrechecktimer = STUCK_RECHECK_TIME self._stuckrechecktimer = STUCK_RECHECK_TIME
self._stuckin = data.stuckin
end
-- Get the remaining arrow state
self._lastpos = data.lastpos
self._startpos = data.startpos
self._damage = data.damage
self._is_critical = data.is_critical
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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({

View File

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

View File

@ -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 = "",

View File

@ -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",

View File

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

View File

@ -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ブロックの障壁で、手やレッドストーンの信号で開閉できます。

View File

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

View File

@ -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 блока, которые можно открывать и закрывать вручную и по сигналу редстоуна.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 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%)
-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 effect parameter!=Paramètre d'effet manquant!
Missing or invalid heal amount parameter!=Paramètre de quantité de soin manquant ou invalide ! Missing or invalid duration parameter!=Paramètre durée manquant ou invalide!
Player @1 healed by @2 HP.=Joueur @1 soigné de @2 PV. Invalid factor parameter!=Paramètre facteur invalide!
Player @1 harmed by @2 HP.=Joueur @1 blessé de @2 PV. @1 is not an available status effect.=@1 n'est pas un effet disponible.
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
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.

View File

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

View File

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

View File

@ -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},

View File

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

View File

@ -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}
},
},
})

View File

@ -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
},
})

View File

@ -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,
velocity = velocity,
drag = 3,
})
obj:get_luaentity()._thrower = thrower obj:get_luaentity()._thrower = thrower
end
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

View File

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

View File

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

View File

@ -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 },
},
})

View File

@ -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", {

View File

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

View File

@ -0,0 +1,2 @@
name = vl_projectile
depends = mcl_util

View File

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

View File

@ -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",