Compare commits
25 Commits
master
...
new_mapgen
Author | SHA1 | Date |
---|---|---|
|
888251e3ec | |
|
b91d8875f3 | |
|
8eb0edaf51 | |
|
4bd06723b4 | |
|
b565de7122 | |
|
74257f5ff2 | |
|
ca37c60511 | |
|
9dadbb2bb4 | |
|
18c815fbf1 | |
|
cc064636a9 | |
|
398f51b1da | |
|
9fe692fb6f | |
|
605bb8c619 | |
|
53cecf7b41 | |
|
792c740a4d | |
|
34ab356193 | |
|
03371421d8 | |
|
54ea8ba6b1 | |
|
229f821758 | |
|
bb008d6341 | |
|
682d2b3229 | |
|
f592437e8a | |
|
a639630b32 | |
|
679a1db436 | |
|
b3e8f24876 |
2
API.md
|
@ -42,7 +42,7 @@ A lot of things are possible by using one of the APIs in the mods. Note that not
|
|||
* Buckets: `ITEMS/mcl_buckets`
|
||||
* Dispenser support: `ITEMS/REDSTONE/mcl_dispensers`
|
||||
|
||||
### Mobs
|
||||
## Mobs
|
||||
* Mobs: `ENTITIES/mcl_mobs`
|
||||
|
||||
MineClone 2 uses its own mobs framework, called “Mobs Redo: MineClone 2 Edition” or “MRM” for short.
|
||||
|
|
123
CONTRIBUTING.md
|
@ -2,7 +2,7 @@
|
|||
So you want to contribute to MineClone2?
|
||||
Wow, thank you! :-)
|
||||
|
||||
MineClone2 is maintained by Nicu and Cora. If you have any
|
||||
MineClone2 is maintained by Nicu and Fleckenstein. If you have any
|
||||
problems or questions, contact us (See Links section below).
|
||||
|
||||
You can help with MineClone2's development in many different ways,
|
||||
|
@ -11,9 +11,18 @@ whether you're a programmer or not.
|
|||
## MineClone2's development target is to...
|
||||
- Crucially, create a stable, moddable, free/libre clone of Minecraft
|
||||
based on the Minetest engine with polished features, usable in both
|
||||
singleplayer and multiplayer. Currently, a lot of Minecraft features
|
||||
are already implemented.
|
||||
Polishing existing features is always welcome.
|
||||
singleplayer and multiplayer. Currently, most of **Minecraft Java
|
||||
Edition 1.12.2** features are already implemented and polishing existing
|
||||
features are prioritized over new feature requests.
|
||||
- With lessened priority yet strictly, implement features targetting
|
||||
**Minecraft version 1.17 + OptiFine** (OptiFine only as far as supported
|
||||
by the Minetest Engine). This means features in parity with the listed
|
||||
Minecraft experiences are prioritized over those that don't fulfill this
|
||||
scope.
|
||||
- Optionally, create a performant experience that will run relatively
|
||||
well on really low spec computers. Unfortunately, due to Minecraft's
|
||||
mechanisms and Minetest engine's limitations along with a very small
|
||||
playerbase on low spec computers, optimizations are hard to investigate.
|
||||
|
||||
## Links
|
||||
* [Mesehub](https://git.minetest.land/MineClone2/MineClone2)
|
||||
|
@ -36,10 +45,8 @@ referenced frequently because of its usefulness. As such, it is valuable
|
|||
in learning how git works and its terminology. It can also help you
|
||||
keeping your game updated, and easily test pull requests.
|
||||
|
||||
Look at our wiki for some concrete guides:
|
||||
https://git.minetest.land/MineClone2/MineClone2/wiki/
|
||||
|
||||
## How you can help as a non-programmer
|
||||
|
||||
As someone who does not know how to write programs in Lua or does not
|
||||
know how to use the Minetest API, you can still help us out a lot. For
|
||||
example, by opening an issue in the
|
||||
|
@ -51,10 +58,12 @@ you can report a bug or request a feature.
|
|||
discussion.
|
||||
* Choose a descriptive title (e.g. not just "crash", "bug" or "question"
|
||||
).
|
||||
* Please write in plain, understandable English. It will be easier to
|
||||
communicate.
|
||||
* Please start the issue title with a capital letter.
|
||||
* Always check the currently opened issues before creating a new one.
|
||||
Try not to report bugs that have already been reported or request features
|
||||
that already have been requested. This can often be ambiguous though.
|
||||
If in doubt open an issue!
|
||||
Don't report bugs that have already been reported or request features
|
||||
that already have been requested.
|
||||
* If you know about Minetest's inner workings, please think about
|
||||
whether the bug / the feature that you are reporting / requesting is
|
||||
actually an issue with Minetest itself, and if it is, head to the
|
||||
|
@ -64,9 +73,6 @@ instead.
|
|||
an issue, feel free to ask on the Discord / Matrix server or the IRC
|
||||
channel.
|
||||
|
||||
The link to the mesehub registration page is: https://git.minetest.land/user/sign_up
|
||||
(It appears to sometimes get lost on the page itsself)
|
||||
|
||||
### Reporting bugs
|
||||
* A bug is an unintended behavior or, in the worst case, a crash.
|
||||
However, it is not a bug if you believe something is missing in the
|
||||
|
@ -105,28 +111,23 @@ would report issues will pull requests similar to when you were
|
|||
reporting bugs that are the mainline (See Reporting bugs section). You
|
||||
can find currently open pull requests here:
|
||||
<https://git.minetest.land/MineClone2/MineClone2/pulls>. Note that pull
|
||||
requests that start with a `WIP:` are not done yet and therefore could
|
||||
still undergo substantial change. Testing these is still helpful however
|
||||
because that is the reason developers put them up as WIP so other people
|
||||
can have a look at the PR.
|
||||
requests that start with a `WIP:` are not done yet, and therefore might
|
||||
not work, so it's not very useful to try them out yet.
|
||||
|
||||
### Contributing assets
|
||||
Due to license problems, MineClone2 cannot use Minecraft's assets,
|
||||
therefore we are always looking for asset contributions.
|
||||
|
||||
To contribute assets, it can be useful to learn git basics and read
|
||||
the section for Programmers of this document, however this is not required.
|
||||
It's also a good idea to join the Discord server
|
||||
Due to license problems, MineClone2 unfortunately cannot use
|
||||
Minecraft's assets, therefore we are always looking for asset
|
||||
contributions. To contribute assets, it can be useful to learn git
|
||||
basics and read the section for Programmers of this document, however
|
||||
this is not required. It's also a good idea to join the Discord server
|
||||
(or alternatively IRC or Matrix).
|
||||
|
||||
#### Textures
|
||||
For textures we use the Pixel Perfection texture pack. For older Minecraft
|
||||
features that is mostly enough but a lot of the newer textures in it are
|
||||
copies or slight modifications of the original MC textures so great caution
|
||||
needs to be taken when using any textures coming from Minecraft texture
|
||||
packs.
|
||||
If you want to make such contributions, join our Discord server. Demands
|
||||
for textures will be communicated there.
|
||||
For textures we use the Pixel Perfection texture pack. This is mostly
|
||||
enough; however in some cases - e.g. for newer Minecraft features, it's
|
||||
useful to have texture artists around. If you want to make such
|
||||
contributions, join our Discord server. Demands for textures will be
|
||||
communicated there.
|
||||
|
||||
#### Sounds
|
||||
MineClone2 currently does not have a consistent way to handle sounds.
|
||||
|
@ -250,25 +251,16 @@ of the results)
|
|||
* [Official Minecraft Wiki](https://minecraft.fandom.com/wiki/Minecraft_Wiki)
|
||||
(Include a link to the specific page you used)
|
||||
|
||||
### Guidelines
|
||||
### Stick to our guidelines
|
||||
|
||||
#### Git Guidelines
|
||||
* Pushing to master is disabled - don't even try it.
|
||||
* Every change is tracked as a PR.
|
||||
* All but the tiniest changes require at least one approval from a Developer
|
||||
* To update branches we use rebase not merge (so we don't end up with
|
||||
excessive git bureaucracy commits in master)
|
||||
* We use merge to add the commits from a PR/branch to master
|
||||
* Submodules should only be used if a) upstream is highly reliable and
|
||||
b) it is 100% certain that no mcl2 specific changes to the code will be
|
||||
needed (this has never been the case before, hence mcl2 is submodule free so far)
|
||||
* Commit messages should be descriptive and never contain mcl2 specific
|
||||
issueids - there are other projects who might use commits from mcl2 and
|
||||
it will confuse their issue trackers.
|
||||
* Try to group your submissions best as you can:
|
||||
* Try to keep your PRs small: In some cases things reasonably be can't
|
||||
split up but in general multiple small PRs are better than a big one.
|
||||
* Similarly multiple small commits are better than a giant one. (use git commit -p)
|
||||
* We use merge rather than rebase or squash merge
|
||||
* We don't use git submodules.
|
||||
* Your commit names should be relatively descriptive, e.g. when saying
|
||||
"Fix #issueid", the commit message should also contain the title of the
|
||||
issue.
|
||||
* Try to keep your commits as atomic as possible (advise, but completely
|
||||
optional)
|
||||
|
||||
#### Code Guidelines
|
||||
* Each mod must provide `mod.conf`.
|
||||
|
@ -351,23 +343,36 @@ Active and trusted contributors are often granted write access to the
|
|||
MineClone2 repository.
|
||||
|
||||
#### Developer responsibilities
|
||||
- If you have developer privileges you can just open a new branch in the
|
||||
mcl2 repository (which is preferred). From that you create a pull request.
|
||||
This way other people can review your changes and make sure they work
|
||||
before they get merged.
|
||||
- If you do not (yet) have developer privs you do your work on a branch
|
||||
on your private repository e.g. using the "fork" function on mesehub.
|
||||
- Any developer is welcome to review, test and merge PRs. A PR needs
|
||||
at least one approval (by someone else than the author) but the maintainers
|
||||
are usually relatively quick to react to new submissions.
|
||||
- You should not push things directly to
|
||||
MineClone2 master - rather, do your work on a branch on your private
|
||||
repository, then create a pull request. This way other people can review
|
||||
your changes and make sure they work before they get merged.
|
||||
- Merge PRs only when they have recieved the necessary feedback and have
|
||||
been tested by at least two different people (including the author of
|
||||
the pull request), to avoid crashes or the introduction of new bugs.
|
||||
- You may also be assigned to issues or pull
|
||||
requests as a developer. In this case it is your responsibility to fix
|
||||
the issue / review and merge the pull request when it is ready. You can
|
||||
also unassign yourself from the issue / PR if you have no time or don't
|
||||
want to take care of it for some other reason. After all, everyone is a
|
||||
volunteer and we can't expect you to do work that you are not interested
|
||||
in. **The important thing is that you make sure to inform us if you
|
||||
won't take care of something that has been assigned to you.**
|
||||
- Please assign yourself to something that you want to work on to avoid
|
||||
duplicate work.
|
||||
- As a developer, it should be easy to reach you about your work. You
|
||||
should be in at least one of the public MineClone2 discussion rooms -
|
||||
preferrably Discord, but if you really don't like Discord, Matrix
|
||||
or IRC are fine too.
|
||||
|
||||
### Maintainer status
|
||||
Maintainers carry the main responsibility for the project.
|
||||
|
||||
#### Maintainer responsibilities
|
||||
- Making sure issues are addressed and pull requests are reviewed and
|
||||
merged.
|
||||
merged, by assigning either themselves or Developers to issues / PRs
|
||||
- Making releases
|
||||
- Making sure guidelines are kept
|
||||
- Making project decisions based on community feedback
|
||||
- Granting/revoking developer access
|
||||
- Enforcing the code of conduct (See CODE_OF_CONDUCT.md)
|
||||
|
@ -375,8 +380,8 @@ merged.
|
|||
- Resolving conflicts and problems within the community
|
||||
|
||||
#### Current maintainers
|
||||
* Cora - responsible for gameplay review, publishing releases,
|
||||
technical guidelines
|
||||
* Fleckenstein - responsible for gameplay review, publishing releases,
|
||||
technical guidelines and issue/PR delegation
|
||||
* Nicu - responsible for community related issues
|
||||
|
||||
#### Release process
|
||||
|
|
27
CREDITS.md
|
@ -27,12 +27,6 @@
|
|||
* Code-Sploit
|
||||
* NO11
|
||||
* kabou
|
||||
* rudzik8
|
||||
* chmodsayshello
|
||||
* PrairieWind
|
||||
* RandomLegoBrick
|
||||
* SumianVoice
|
||||
* MrRar
|
||||
|
||||
## Contributors
|
||||
* Laurent Rocher
|
||||
|
@ -77,14 +71,6 @@
|
|||
* Sven792
|
||||
* aldum
|
||||
* Dieter44
|
||||
* Pepebotella
|
||||
* MrRar
|
||||
* Lazerbeak12345
|
||||
* mrminer
|
||||
* Thunder1035
|
||||
* opfromthestart
|
||||
* snowyu
|
||||
* FaceDeer
|
||||
|
||||
## MineClone5
|
||||
* kay27
|
||||
|
@ -92,12 +78,10 @@
|
|||
* epCode
|
||||
* NO11
|
||||
* j45
|
||||
* chmodsayshello
|
||||
* 3raven
|
||||
* PrairieWind
|
||||
* PrarieWind
|
||||
* Gustavo1
|
||||
* CableGuy67
|
||||
* MrRar
|
||||
|
||||
## Mineclonia
|
||||
* erlehmann
|
||||
|
@ -135,7 +119,6 @@
|
|||
* 4Evergreen4
|
||||
* jordan4ibanez
|
||||
* paramat
|
||||
* cora
|
||||
|
||||
## 3D Models
|
||||
* 22i
|
||||
|
@ -151,9 +134,6 @@
|
|||
* yutyo
|
||||
* NO11
|
||||
* kay27
|
||||
* MysticTempest
|
||||
* RandomLegoBrick
|
||||
* cora
|
||||
|
||||
## Translations
|
||||
* Wuzzy
|
||||
|
@ -163,10 +143,6 @@
|
|||
* pitchum
|
||||
* todoporlalibertad
|
||||
* Marcin Serwin
|
||||
* Pepebotella
|
||||
* Emojigit
|
||||
* snowyu
|
||||
* 3raven
|
||||
|
||||
## Funders
|
||||
* 40W
|
||||
|
@ -174,6 +150,5 @@
|
|||
## Special thanks
|
||||
* celeron55 for creating Minetest
|
||||
* Jordach for the jukebox music compilation from Big Freaking Dig
|
||||
* wsor for working tirelessly in the shadows for the good of all of us, particularly helping with solving contentDB and copyright issues.
|
||||
* The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game
|
||||
* Notch and Jeb for being the major forces behind Minecraft
|
||||
|
|
|
@ -41,7 +41,6 @@ Please read <http://minecraft.gamepedia.com/Breaking> to learn how digging times
|
|||
* `flammable=-1` Does not get destroyed by fire
|
||||
* `fire_encouragement`: How quickly this block catches fire
|
||||
* `fire_flammability`: How fast the block will burn away
|
||||
* `path_creation_possible=1`: Node can be turned into grass path by using a shovel on it
|
||||
* `spreading_dirt_type=1`: A dirt-type block with a cover (e.g. grass) which may spread to neighbor dirt blocks
|
||||
* `dirtifies_below_solid=1`: This node turns into dirt immediately when a solid or dirtifier node is placed on top
|
||||
* `dirtifier=1`: This node turns nodes the above group into dirt when placed above
|
||||
|
@ -57,7 +56,6 @@ Please read <http://minecraft.gamepedia.com/Breaking> to learn how digging times
|
|||
* `no_eat_delay=1`: Only for foodstuffs. When eating this, all eating delays are ignored.
|
||||
* `can_eat_when_full=1`: Only for foodstuffs. This item can be eaten when the user has a full hunger bar
|
||||
* `attached_node_facedir=1`: Like `attached_node`, but for facedir nodes
|
||||
* `supported_node=1`: Like `attached_node`, but can be placed on any nodes that do not have the `drawtype="airlike"` attribute.
|
||||
* `cauldron`: Cauldron. 1: Empty. 2-4: Water height
|
||||
* `anvil`: Anvil. 1: No damage. 2-3: Higher damage levels
|
||||
* `no_rename=1`: Item cannot be renamed by anvil
|
||||
|
@ -73,7 +71,6 @@ Please read <http://minecraft.gamepedia.com/Breaking> to learn how digging times
|
|||
* `coral_block=X`: Coral block (1 = alive, 2 = dead)
|
||||
* `coral_species=X`: Specifies the species of a coral; equal X means equal species
|
||||
* `set_on_fire=X`: Sets any (not fire-resistant) mob or player on fire for X seconds when touching
|
||||
* `compostability=X`: Item can be used on a composter block; X (1-100) is the % chance of adding a level of compost
|
||||
|
||||
#### Footnotes
|
||||
|
||||
|
@ -102,8 +99,6 @@ Please read <http://minecraft.gamepedia.com/Breaking> to learn how digging times
|
|||
* `water_bucket=1`: Bucket containing a liquid of group “water”
|
||||
* `enchantability=X`: How good the enchantments are the item gets (1 equals book)
|
||||
* `enchanted=1`: The item is already enchanted, meaning that it can't be enchanted using an enchanting table
|
||||
* `cobble=1`: Cobblestone of any kind
|
||||
* `soul_block`: Fire burning on these blocks turns to soul fire, can be used to craft soul torch
|
||||
|
||||
### Material groups
|
||||
|
||||
|
@ -205,9 +200,6 @@ These groups are used mostly for informational purposes
|
|||
* `building_block=1`: Block is a building block
|
||||
* `deco_block=1`: Block is a decorational block
|
||||
|
||||
* `blast_furnace_smeltable=1` : Item or node is smeltable by a blast furnace
|
||||
* `smoker_cookable=1` : Food is cookable by a smoker.
|
||||
|
||||
|
||||
## Fake item groups
|
||||
These groups put similar items together which should all be treated by the gameplay or the GUI as a single item.
|
||||
|
|
20
README.md
|
@ -2,7 +2,7 @@
|
|||
An unofficial Minecraft-like game for Minetest. Forked from MineClone by davedevils.
|
||||
Developed by many people. Not developed or endorsed by Mojang AB.
|
||||
|
||||
Version: 0.80 (in development)
|
||||
Version: 0.72.0
|
||||
|
||||
### Gameplay
|
||||
You start in a randomly-generated world made entirely of cubes. You can explore
|
||||
|
@ -66,7 +66,7 @@ Use the `/giveme` chat command to obtain them. See the in-game help for
|
|||
an explanation.
|
||||
|
||||
## Installation
|
||||
This game requires [Minetest](http://minetest.net) to run (version 5.4.1 or
|
||||
This game requires [Minetest](http://minetest.net) to run (version 5.3.0 or
|
||||
later). So you need to install Minetest first. Only stable versions of Minetest
|
||||
are officially supported.
|
||||
There is no support for running MineClone2 in development versions of Minetest.
|
||||
|
@ -91,11 +91,11 @@ The MineClone2 repository is hosted at Mesehub. To contribute or report issues,
|
|||
## Target
|
||||
- Crucially, create a stable, moddable, free/libre clone of Minecraft
|
||||
based on the Minetest engine with polished features, usable in both
|
||||
singleplayer and multiplayer. Currently, a lot of **Minecraft Java
|
||||
Edition** features are already implemented and polishing existing
|
||||
singleplayer and multiplayer. Currently, most of **Minecraft Java
|
||||
Edition 1.12.2** features are already implemented and polishing existing
|
||||
features are prioritized over new feature requests.
|
||||
- With lessened priority yet strictly, implement features targetting
|
||||
**Current Minecraft versions + OptiFine** (OptiFine only as far as supported
|
||||
**Minecraft version 1.17 + OptiFine** (OptiFine only as far as supported
|
||||
by the Minetest Engine). This means features in parity with the listed
|
||||
Minecraft experiences are prioritized over those that don't fulfill this
|
||||
scope.
|
||||
|
@ -108,7 +108,8 @@ playerbase on low spec computers, optimizations are hard to investigate.
|
|||
This game is currently in **beta** stage.
|
||||
It is playable, but not yet feature-complete.
|
||||
Backwards-compability is not entirely guaranteed, updating your world might cause small bugs.
|
||||
If you want to use the development version of MineClone2 in production, the master branch is usually relatively stable. The testing branch often features some experimental PRs and should be considered less stable.
|
||||
If you want to use the git version of MineClone2 in production, consider using the production branch.
|
||||
It is updated weekly and contains relatively stable code for servers.
|
||||
|
||||
The following main features are available:
|
||||
|
||||
|
@ -123,7 +124,7 @@ The following main features are available:
|
|||
* Most blocks in the overworld
|
||||
* Water and lava
|
||||
* Weather
|
||||
* 28 biomes + 5 Nether Biomes
|
||||
* 28 biomes
|
||||
* The Nether, a fiery underworld in another dimension
|
||||
* Redstone circuits (partially)
|
||||
* Minecarts (partial)
|
||||
|
@ -161,7 +162,7 @@ The following features are incomplete:
|
|||
* Special minecarts
|
||||
* A couple of non-trivial blocks and items
|
||||
|
||||
Bonus features (not found in Minecraft):
|
||||
Bonus features (not found in Minecraft 1.12):
|
||||
|
||||
* Built-in crafting guide which shows you crafting and smelting recipes
|
||||
* In-game help system containing extensive help about gameplay basics, blocks, items and more
|
||||
|
@ -174,9 +175,6 @@ Bonus features (not found in Minecraft):
|
|||
* Nether Brick Fence Gate
|
||||
* Red Nether Brick Fence
|
||||
* Red Nether Brick Fence Gate
|
||||
* Structure replacements - these small variants of Minecraft structures serve as replacements until we can get large structures working:
|
||||
* Woodland Cabin (Mansions)
|
||||
* Nether Outpost (Fortress)
|
||||
|
||||
Technical differences from Minecraft:
|
||||
|
||||
|
|
|
@ -1,170 +0,0 @@
|
|||
# MineClone2
|
||||
Un jeu non-officiel similaire à Minecraft pour Minetest. Forké depuis Mineclone par davedevils. Développé par de nombreuses personnes. Ni développé ou supporté par Mojang AB.
|
||||
|
||||
Version: 0.79 (en dévelopment)
|
||||
|
||||
### 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.
|
||||
|
||||
### Résumé du Gameplay
|
||||
|
||||
* Jeu de type bac-à-sable, sans objetifs
|
||||
* Survie : combattre des monstres hostiles et la faim
|
||||
* Creuser pour du minerai et d'autres trésors
|
||||
* Magie : gagner de l'expérience et enchanter les outils
|
||||
* Utiliser les blocs ramassés pour construire de magnifiques bâtiments, votre imagination est la limite
|
||||
* Ramasser des fleurs (et d'autres sources de teinture) et colorez votre monde
|
||||
* Trouvez des graines et commencez à cultiver
|
||||
* Trouvez ou fabriquez des centaines d'objets
|
||||
* Construisez un réseau ferroviaire complexe et amusez vous avec les wagonnets
|
||||
* En mode créatif vous pouvez construire presque n'importe quoi gratuitement et sans limite
|
||||
|
||||
## Comment jouer (démarrer rapidement)
|
||||
### Commencer
|
||||
* **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
|
||||
* Placer les 4 planches de bois dans la grille 2x2 et **fabriquez une table d'artisanat**
|
||||
* **Cliquez droit la table d'artisanat** (icone livre) pour apprendre toutes les recettes possibles
|
||||
* **Fabriquez une pioche de bois** pour miner la pierre
|
||||
* Différents outils minent différentes sortes de blocs. Essayez les !
|
||||
* Continuez à jouer comme vous voulez. Amusez vous !
|
||||
|
||||
### Agriculture
|
||||
* Trouvez des graines
|
||||
* Fabriquez une houe
|
||||
* Cliquez droit la terre ou des blocs similaires avec la houe pour créer des terres agricoles
|
||||
* Placer des graines sur des terres agricoles et regardez les pousser
|
||||
* Récoltez les plantes une fois matûres
|
||||
* Les terres agricoles proche de l'eau deviennent humides et accélèrent la croissance
|
||||
|
||||
### Four
|
||||
* Fabriquer un Four
|
||||
* Le four permet d'obtenir plus d'objets
|
||||
* L'emplacement du haut doit contienir un objet fondable (par ex : minerai de fer)
|
||||
* L'emplacement du bas doit contienir un objet combustible (par ex : charbon)
|
||||
* Voir le guide d'artisanat pour en apprendre plus sur les objets fondables et combustibles
|
||||
|
||||
### Aide supplémentaire
|
||||
Plus d'aide à propos du jeu, des blocs, objets et plus encore peuvent être trouvés dans le jeu. Vous pouvez accéder à l'aide depuis le menu inventaire.
|
||||
|
||||
### Objets spéciaux
|
||||
Les objets suivants sont intéressants pour le mode Créatif et pour les constructeurs de cartes d'aventure. Ils ne peuvent être obtenus dans le jeu ou dans l'inventaire créatif.
|
||||
|
||||
* Barrière : `mcl_core:barrier`
|
||||
|
||||
Utilisez la commande de chat `/giveme` pour les obtenir. Voir l'aide interne au jeu pour une explication.
|
||||
|
||||
## Installation
|
||||
Ce jeu nécessite [Minetest](http://minetest.net) pour fonctionner (version 5.4.1 ou plus). Vous devez donc installer Minetest d'abord. Seules les versions stables de Minetest sont officielement supportées.
|
||||
Il n'y a pas de support de MineClone2 dans les versions développement de Minetest.
|
||||
|
||||
Pour installer MineClone2 (si ce n'est pas déjà fait), déplacez ce dossier dans le dossier “games” de Minetest. Consultez l'aide de Minetest pour en apprendre plus.
|
||||
|
||||
## Liens utiles
|
||||
Le dépôt de MineClone2 est hébergé sur Mesehub. Pour contribuer ou rapporter des problèmes, aller là-bas.
|
||||
|
||||
* Mesehub: <https://git.minetest.land/MineClone2/MineClone2>
|
||||
* Discord: <https://discord.gg/xE4z8EEpDC>
|
||||
* YouTube <https://www.youtube.com/channel/UClI_YcsXMF3KNeJtoBfnk9A>
|
||||
* IRC: <https://web.libera.chat/#mineclone2>
|
||||
* Matrix: <https://app.element.io/#/room/#mc2:matrix.org>
|
||||
* Reddit: <https://www.reddit.com/r/MineClone2/>
|
||||
* Minetest forums: <https://forum.minetest.net/viewtopic.php?f=50&t=16407>
|
||||
* ContentDB: <https://content.minetest.net/packages/wuzzy/mineclone2/>
|
||||
* OpenCollective: <https://opencollective.com/mineclone2>
|
||||
|
||||
## Objectif
|
||||
* Créer un clone stable, moddable, libre et gratuit basé sur le moteur de jeu Minetest avec des fonctionalités abouties, utilisable à la fois en mode solo et multijoueur. Actuellement, beaucoup des fonctionalités de **Minecraft Java Edition** sont déjà implémentées et leur amélioration est prioritaire sur les nouvelles demandes.
|
||||
* Avec une priorité moindre, implémenter les fonctionalités des versions **Minecraft + OptiFine** (OtiFine autant que supporté par le moteur Minetest). Cela signifie que les fonctionalités présentes dans les versions listées sont priorisées.
|
||||
* 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 à basse performances, les optimisations sont difficiles à explorer.
|
||||
|
||||
## Statut de complétion
|
||||
Ce jeu est actuellement au stade **beta**.
|
||||
Il est jouable mais incomplet en fonctionalités.
|
||||
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 MineClone2 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 fonctionalités suivantes sont disponibles :
|
||||
|
||||
* Outils, armes
|
||||
* Armure
|
||||
* Système de fabrication : grille 2x2, table d'artisanat (grille 3x3), four, incluant un guide de fabrication
|
||||
* Coffres, grands coffres, coffre ender, boite de shulker
|
||||
* Fours, entonnoirs
|
||||
* Faim
|
||||
* La plupart des monstres et animaux
|
||||
* Tout les minerais de Minecraft
|
||||
* La plupart des blocs de l'overworld
|
||||
* Eau et lave
|
||||
* Météo
|
||||
* 28 biomes + 5 biomes du nether
|
||||
* Le Nether, monde souterrain brûlant dans une autre dimension
|
||||
* Circuits Redstone (partiel)
|
||||
* Effets de Statut (partiel)
|
||||
* Expérience
|
||||
* Enchantement
|
||||
* Brassage, potions, flèches trempées (partiel)
|
||||
* Bâteaux
|
||||
* Feu
|
||||
* Blocs de construction : escaliers, dalles, portes, trappes, barrière, portillon, muret
|
||||
* Horloge
|
||||
* Boussole
|
||||
* Eponge
|
||||
* Bloc de slime
|
||||
* Petites plantes et pousses
|
||||
* Teintures
|
||||
* Bannières
|
||||
* Blocs de décoration : verre, verre teinté, vitres, barres de fer, terre cuites (et couleurs), têtes et plus
|
||||
* Cadres d'objets
|
||||
* Juke-boxes
|
||||
* Livres pour écrire
|
||||
* Commandes
|
||||
* Villages
|
||||
* L'End
|
||||
* et plus !
|
||||
|
||||
Les fonctionalités suivantes sont incomplètes :
|
||||
|
||||
* certains monstres et animaux
|
||||
* certains composants de Redstone
|
||||
* Wagonnets spéciaux
|
||||
* quelques blocs et objets non-triviaux
|
||||
|
||||
Fonctionalités bonus (absentes de Minecraft) :
|
||||
|
||||
* Guide d'artisanat intégré au jeu qui montre les recettes d'artisanat et de cuisson
|
||||
* Système d'aide intégré au jeu contenant des informations à propos des techniques de base, blocs, objets et plus
|
||||
* Recettes d'artisanat temporaires. Elles existent uniquement pour rendre des objets accessibles qui ne le seraient pas autrement sauf en mode créatif. Elles seront retirées au cours de l'avancement du développement et de l'ajout de nouvelles fonctionalités.
|
||||
* Pousses dans les coffres en mapgen v6
|
||||
* Entièrement moddable (grâce la puissante API lua de Minetest)
|
||||
* Nouveaux blocs et objets :
|
||||
* Outil de recherche, montre l'aide de ce qu'il touche
|
||||
* Plus de dalles et d'escaliers
|
||||
* Portillon en briques du Nether
|
||||
* Barrière en briques du Nether rouges
|
||||
* Portillon en briques du Nether rouges
|
||||
* Structures de remplacement - ces petites variantes de structures de Minecraft servent de remplacement en attendant qu'on arrive à en faire fonctionner de plus grandes :
|
||||
* Cabine dans les bois (Manoir des bois)
|
||||
* Avant-poste du Nether (Forteresse)
|
||||
|
||||
Différences techniques avec Minecraft :
|
||||
* Limite en hauteur de 31000 blocs (bien plus grand que Minecraft)
|
||||
* Taille horizontale du monde 62000×62000 blocs (bien plus petit que Minecraft mais toujours très grand)
|
||||
* Toujours assez incomplet et buggé
|
||||
* Des blocs, objets, ennemis et fonctionalités manquent
|
||||
* Quelques objets ont des noms légèrement différents pour être plus faciles à distinguer
|
||||
* Des musiques différentes pour le juke-boxe
|
||||
* Des textures différentes (Pixel Perfection)
|
||||
* Des sons différents (sources diverses)
|
||||
* Un moteur de jeu différent (Minetest)
|
||||
* Des bonus cachés différents
|
||||
...et enfin MineClone2 est un logiciel libre !
|
||||
|
||||
## Autres fichiers readme
|
||||
|
||||
* `LICENSE.txt`: Le texte de la license GPLv3
|
||||
* `CONTRIBUTING.md`: Information pour ceux qui veulent contribuer
|
||||
* `API.md`: Pour les modders Minetest qui veulent modder ce jeu
|
||||
* `LEGAL.md`: Information légale
|
||||
* `CREDITS.md`: Liste des contributeurs
|
|
@ -1,2 +1,2 @@
|
|||
title = MineClone 2
|
||||
name = MineClone 2
|
||||
description = A survival sandbox game. Survive, gather, hunt, build, explore, and do much more.
|
||||
|
|
|
@ -1,93 +1,29 @@
|
|||
-- Overrides the builtin minetest.check_single_for_falling.
|
||||
-- We need to do this in order to handle nodes in mineclone specific groups
|
||||
-- "supported_node" and "attached_node_facedir".
|
||||
--
|
||||
-- Nodes in group "supported_node" can be placed on any node that does not
|
||||
-- have the "airlike" drawtype. Carpets are an example of this type.
|
||||
|
||||
local vector = vector
|
||||
|
||||
local facedir_to_dir = minetest.facedir_to_dir
|
||||
local get_item_group = minetest.get_item_group
|
||||
local remove_node = minetest.remove_node
|
||||
local get_node = minetest.get_node
|
||||
local get_meta = minetest.get_meta
|
||||
local registered_nodes = minetest.registered_nodes
|
||||
local get_node_drops = minetest.get_node_drops
|
||||
local add_item = minetest.add_item
|
||||
|
||||
-- drop_attached_node(p)
|
||||
--
|
||||
-- This function is copied verbatim from minetest/builtin/game/falling.lua
|
||||
-- We need this to do the exact same dropping node handling in our override
|
||||
-- minetest.check_single_for_falling() function as in the builtin function.
|
||||
--
|
||||
local function drop_attached_node(p)
|
||||
local n = get_node(p)
|
||||
local drops = get_node_drops(n, "")
|
||||
local def = registered_nodes[n.name]
|
||||
if def and def.preserve_metadata then
|
||||
local oldmeta = get_meta(p):to_table().fields
|
||||
-- Copy pos and node because the callback can modify them.
|
||||
local pos_copy = vector.new(p)
|
||||
local node_copy = {name=n.name, param1=n.param1, param2=n.param2}
|
||||
local drop_stacks = {}
|
||||
for k, v in pairs(drops) do
|
||||
drop_stacks[k] = ItemStack(v)
|
||||
end
|
||||
drops = drop_stacks
|
||||
def.preserve_metadata(pos_copy, node_copy, oldmeta, drops)
|
||||
end
|
||||
if def and def.sounds and def.sounds.fall then
|
||||
core.sound_play(def.sounds.fall, {pos = p}, true)
|
||||
end
|
||||
remove_node(p)
|
||||
for _, item in pairs(drops) do
|
||||
local pos = {
|
||||
x = p.x + math.random()/2 - 0.25,
|
||||
y = p.y + math.random()/2 - 0.25,
|
||||
z = p.z + math.random()/2 - 0.25,
|
||||
}
|
||||
add_item(pos, item)
|
||||
end
|
||||
end
|
||||
|
||||
-- minetest.check_single_for_falling(pos)
|
||||
--
|
||||
-- * causes an unsupported `group:falling_node` node to fall and causes an
|
||||
-- unattached `group:attached_node` or `group:attached_node_facedir` node
|
||||
-- or unsupported `group:supported_node` node to drop.
|
||||
-- * does not spread these updates to neighbours.
|
||||
--
|
||||
-- Returns true if the node at <pos> has spawned a falling node or has been
|
||||
-- dropped as item(s).
|
||||
--
|
||||
local original_function = minetest.check_single_for_falling
|
||||
|
||||
function minetest.check_single_for_falling(pos)
|
||||
if original_function(pos) then
|
||||
return true
|
||||
end
|
||||
|
||||
local node = get_node(pos)
|
||||
local ret_o = original_function(pos)
|
||||
local ret = false
|
||||
local node = minetest.get_node(pos)
|
||||
if get_item_group(node.name, "attached_node_facedir") ~= 0 then
|
||||
local dir = facedir_to_dir(node.param2)
|
||||
if dir then
|
||||
if get_item_group(get_node(vector.add(pos, dir)).name, "solid") == 0 then
|
||||
drop_attached_node(pos)
|
||||
return true
|
||||
remove_node(pos)
|
||||
local drops = minetest.get_node_drops(node.name, "")
|
||||
for dr=1, #drops do
|
||||
minetest.add_item(pos, drops[dr])
|
||||
end
|
||||
ret = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if get_item_group(node.name, "supported_node") ~= 0 then
|
||||
local def = registered_nodes[get_node(vector.offset(pos, 0, -1, 0)).name]
|
||||
if def and def.drawtype == "airlike" then
|
||||
drop_attached_node(pos)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
return ret_o or ret
|
||||
end
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ mcl_damage = {
|
|||
cactus = {},
|
||||
fall = {bypasses_armor = true},
|
||||
fly_into_wall = {bypasses_armor = true}, -- unused
|
||||
out_of_world = {bypasses_armor = true, bypasses_magic = true, bypasses_invulnerability = true, bypasses_totem = true},
|
||||
out_of_world = {bypasses_armor = true, bypasses_magic = true, bypasses_invulnerability = true},
|
||||
generic = {bypasses_armor = true},
|
||||
magic = {is_magic = true, bypasses_armor = true},
|
||||
dragon_breath = {is_magic = true, bypasses_armor = true}, -- this is only used for dragon fireball; dragon fireball does not actually deal impact damage tho, so this is unreachable
|
||||
|
@ -33,8 +33,6 @@ mcl_damage = {
|
|||
}
|
||||
}
|
||||
|
||||
local damage_enabled = minetest.settings:get_bool("enabled_damage",true)
|
||||
|
||||
function mcl_damage.register_modifier(func, priority)
|
||||
table.insert(mcl_damage.modifiers, {func = func, priority = priority or 0})
|
||||
end
|
||||
|
@ -80,7 +78,7 @@ function mcl_damage.from_punch(mcl_reason, object)
|
|||
mcl_reason.type = "arrow"
|
||||
elseif luaentity._is_fireball then
|
||||
mcl_reason.type = "fireball"
|
||||
elseif luaentity.is_mob then
|
||||
elseif luaentity._cmi_is_mob then
|
||||
mcl_reason.type = "mob"
|
||||
end
|
||||
mcl_reason.source = mcl_reason.source or luaentity._source_object
|
||||
|
@ -141,7 +139,6 @@ function mcl_damage.register_type(name, def)
|
|||
end
|
||||
|
||||
minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
||||
if not damage_enabled then return 0 end
|
||||
if hp_change < 0 then
|
||||
if player:get_hp() <= 0 then
|
||||
return 0
|
||||
|
@ -152,7 +149,6 @@ minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
|||
end, true)
|
||||
|
||||
minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
||||
if not damage_enabled then return 0 end
|
||||
if player:get_hp() > 0 then
|
||||
mt_reason.approved = true
|
||||
if hp_change < 0 then
|
||||
|
@ -165,9 +161,9 @@ minetest.register_on_dieplayer(function(player, mt_reason)
|
|||
if mt_reason.approved then
|
||||
mcl_damage.run_death_callbacks(player, mcl_damage.from_mt(mt_reason))
|
||||
end
|
||||
minetest.log("action","Player "..player:get_player_name().." died at "..minetest.pos_to_string(vector.round(player:get_pos())))
|
||||
end)
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
table.sort(mcl_damage.modifiers, function(a, b) return a.priority < b.priority end)
|
||||
end)
|
||||
|
||||
|
|
|
@ -337,6 +337,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
|||
if not obj:is_player() then
|
||||
return
|
||||
end
|
||||
|
||||
mcl_util.deal_damage(obj, damage, {type = "explosion", direct = direct, source = source})
|
||||
|
||||
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
||||
|
|
|
@ -24,241 +24,9 @@ mcl_vars.inventory_header = ""
|
|||
-- Tool wield size
|
||||
mcl_vars.tool_wield_scale = { x = 1.8, y = 1.8, z = 1 }
|
||||
|
||||
-- Mapgen variables
|
||||
local mg_name = minetest.get_mapgen_setting("mg_name")
|
||||
local minecraft_height_limit = 256
|
||||
local superflat = mg_name == "flat" and minetest.get_mapgen_setting("mcl_superflat_classic") == "true"
|
||||
local singlenode = mg_name == "singlenode"
|
||||
|
||||
-- Calculate mapgen_edge_min/mapgen_edge_max
|
||||
mcl_vars.chunksize = math.max(1, tonumber(minetest.get_mapgen_setting("chunksize")) or 5)
|
||||
mcl_vars.MAP_BLOCKSIZE = math.max(1, minetest.MAP_BLOCKSIZE or 16)
|
||||
mcl_vars.mapgen_limit = math.max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000)
|
||||
mcl_vars.MAX_MAP_GENERATION_LIMIT = math.max(1, minetest.MAX_MAP_GENERATION_LIMIT or 31000)
|
||||
local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2)
|
||||
mcl_vars.central_chunk_offset_in_nodes = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
|
||||
mcl_vars.chunk_size_in_nodes = mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE
|
||||
local central_chunk_min_pos = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
|
||||
local central_chunk_max_pos = central_chunk_min_pos + mcl_vars.chunk_size_in_nodes - 1
|
||||
local ccfmin = central_chunk_min_pos - mcl_vars.MAP_BLOCKSIZE -- Fullminp/fullmaxp of central chunk, in nodes
|
||||
local ccfmax = central_chunk_max_pos + mcl_vars.MAP_BLOCKSIZE
|
||||
local mapgen_limit_b = math.floor(math.min(mcl_vars.mapgen_limit, mcl_vars.MAX_MAP_GENERATION_LIMIT) / mcl_vars.MAP_BLOCKSIZE)
|
||||
local mapgen_limit_min = -mapgen_limit_b * mcl_vars.MAP_BLOCKSIZE
|
||||
local mapgen_limit_max = (mapgen_limit_b + 1) * mcl_vars.MAP_BLOCKSIZE - 1
|
||||
local numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / mcl_vars.chunk_size_in_nodes), 0) -- Number of complete chunks from central chunk
|
||||
local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / mcl_vars.chunk_size_in_nodes), 0) -- fullminp/fullmaxp to effective mapgen limits.
|
||||
mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * mcl_vars.chunk_size_in_nodes
|
||||
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * mcl_vars.chunk_size_in_nodes
|
||||
|
||||
local function coordinate_to_block(x)
|
||||
return math.floor(x / mcl_vars.MAP_BLOCKSIZE)
|
||||
end
|
||||
|
||||
local function coordinate_to_chunk(x)
|
||||
return math.floor((coordinate_to_block(x) - central_chunk_offset) / mcl_vars.chunksize)
|
||||
end
|
||||
|
||||
function mcl_vars.pos_to_block(pos)
|
||||
return {
|
||||
x = coordinate_to_block(pos.x),
|
||||
y = coordinate_to_block(pos.y),
|
||||
z = coordinate_to_block(pos.z)
|
||||
}
|
||||
end
|
||||
|
||||
function mcl_vars.pos_to_chunk(pos)
|
||||
return {
|
||||
x = coordinate_to_chunk(pos.x),
|
||||
y = coordinate_to_chunk(pos.y),
|
||||
z = coordinate_to_chunk(pos.z)
|
||||
}
|
||||
end
|
||||
|
||||
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / mcl_vars.chunk_size_in_nodes)
|
||||
local k_positive_z = k_positive * 2
|
||||
local k_positive_y = k_positive_z * k_positive_z
|
||||
|
||||
function mcl_vars.get_chunk_number(pos) -- unsigned int
|
||||
local c = mcl_vars.pos_to_chunk(pos)
|
||||
return
|
||||
(c.y + k_positive) * k_positive_y +
|
||||
(c.z + k_positive) * k_positive_z +
|
||||
c.x + k_positive
|
||||
end
|
||||
|
||||
if not superflat and not singlenode then
|
||||
-- Normal mode
|
||||
--[[ Realm stacking (h is for height)
|
||||
- Overworld (h>=256)
|
||||
- Void (h>=1000)
|
||||
- Realm Barrier (h=11), to allow escaping the End
|
||||
- End (h>=256)
|
||||
- Void (h>=1000)
|
||||
- Nether (h=128)
|
||||
- Void (h>=1000)
|
||||
]]
|
||||
|
||||
-- Overworld
|
||||
mcl_vars.mg_overworld_min = -62
|
||||
mcl_vars.mg_overworld_max_official = mcl_vars.mg_overworld_min + minecraft_height_limit
|
||||
mcl_vars.mg_bedrock_overworld_min = mcl_vars.mg_overworld_min
|
||||
mcl_vars.mg_bedrock_overworld_max = mcl_vars.mg_bedrock_overworld_min + 4
|
||||
mcl_vars.mg_lava_overworld_max = mcl_vars.mg_overworld_min + 10
|
||||
mcl_vars.mg_lava = true
|
||||
mcl_vars.mg_bedrock_is_rough = true
|
||||
|
||||
elseif singlenode then
|
||||
mcl_vars.mg_overworld_min = -66
|
||||
mcl_vars.mg_overworld_max_official = mcl_vars.mg_overworld_min + minecraft_height_limit
|
||||
mcl_vars.mg_bedrock_overworld_min = mcl_vars.mg_overworld_min
|
||||
mcl_vars.mg_bedrock_overworld_max = mcl_vars.mg_bedrock_overworld_min
|
||||
mcl_vars.mg_lava = false
|
||||
mcl_vars.mg_lava_overworld_max = mcl_vars.mg_overworld_min
|
||||
mcl_vars.mg_bedrock_is_rough = false
|
||||
else
|
||||
-- Classic superflat
|
||||
local ground = minetest.get_mapgen_setting("mgflat_ground_level")
|
||||
ground = tonumber(ground)
|
||||
if not ground then
|
||||
ground = 8
|
||||
end
|
||||
mcl_vars.mg_overworld_min = ground - 3
|
||||
mcl_vars.mg_overworld_max_official = mcl_vars.mg_overworld_min + minecraft_height_limit
|
||||
mcl_vars.mg_bedrock_overworld_min = mcl_vars.mg_overworld_min
|
||||
mcl_vars.mg_bedrock_overworld_max = mcl_vars.mg_bedrock_overworld_min
|
||||
mcl_vars.mg_lava = false
|
||||
mcl_vars.mg_lava_overworld_max = mcl_vars.mg_overworld_min
|
||||
mcl_vars.mg_bedrock_is_rough = false
|
||||
end
|
||||
|
||||
mcl_vars.mg_overworld_max = mcl_vars.mapgen_edge_max
|
||||
|
||||
-- The Nether (around Y = -29000)
|
||||
mcl_vars.mg_nether_min = -29067 -- Carefully chosen to be at a mapchunk border
|
||||
mcl_vars.mg_nether_max = mcl_vars.mg_nether_min + 128
|
||||
mcl_vars.mg_bedrock_nether_bottom_min = mcl_vars.mg_nether_min
|
||||
mcl_vars.mg_bedrock_nether_top_max = mcl_vars.mg_nether_max
|
||||
if not superflat then
|
||||
mcl_vars.mg_bedrock_nether_bottom_max = mcl_vars.mg_bedrock_nether_bottom_min + 4
|
||||
mcl_vars.mg_bedrock_nether_top_min = mcl_vars.mg_bedrock_nether_top_max - 4
|
||||
mcl_vars.mg_lava_nether_max = mcl_vars.mg_nether_min + 31
|
||||
else
|
||||
-- Thin bedrock in classic superflat mapgen
|
||||
mcl_vars.mg_bedrock_nether_bottom_max = mcl_vars.mg_bedrock_nether_bottom_min
|
||||
mcl_vars.mg_bedrock_nether_top_min = mcl_vars.mg_bedrock_nether_top_max
|
||||
mcl_vars.mg_lava_nether_max = mcl_vars.mg_nether_min + 2
|
||||
end
|
||||
if mg_name == "flat" then
|
||||
if superflat then
|
||||
mcl_vars.mg_flat_nether_floor = mcl_vars.mg_bedrock_nether_bottom_max + 4
|
||||
mcl_vars.mg_flat_nether_ceiling = mcl_vars.mg_bedrock_nether_bottom_max + 52
|
||||
else
|
||||
mcl_vars.mg_flat_nether_floor = mcl_vars.mg_lava_nether_max + 4
|
||||
mcl_vars.mg_flat_nether_ceiling = mcl_vars.mg_lava_nether_max + 52
|
||||
end
|
||||
end
|
||||
|
||||
-- The End (surface at ca. Y = -27000)
|
||||
mcl_vars.mg_end_min = -27073 -- Carefully chosen to be at a mapchunk border
|
||||
mcl_vars.mg_end_max_official = mcl_vars.mg_end_min + minecraft_height_limit
|
||||
mcl_vars.mg_end_max = mcl_vars.mg_overworld_min - 2000
|
||||
mcl_vars.mg_end_platform_pos = { x = 100, y = mcl_vars.mg_end_min + 64, z = 0 }
|
||||
mcl_vars.mg_end_exit_portal_pos = vector.new(0, mcl_vars.mg_end_min + 71, 0)
|
||||
|
||||
-- Realm barrier used to safely separate the End from the void below the Overworld
|
||||
mcl_vars.mg_realm_barrier_overworld_end_max = mcl_vars.mg_end_max
|
||||
mcl_vars.mg_realm_barrier_overworld_end_min = mcl_vars.mg_end_max - 11
|
||||
|
||||
-- Use MineClone 2-style dungeons
|
||||
mcl_vars.mg_dungeons = true
|
||||
|
||||
-- Set default stack sizes
|
||||
minetest.nodedef_default.stack_max = 64
|
||||
minetest.craftitemdef_default.stack_max = 64
|
||||
|
||||
-- Set random seed for all other mods (Remember to make sure no other mod calls this function)
|
||||
math.randomseed(os.time())
|
||||
|
||||
local chunks = {} -- intervals of chunks generated
|
||||
function mcl_vars.add_chunk(pos)
|
||||
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
|
||||
local prev
|
||||
for i, d in pairs(chunks) do
|
||||
if n <= d[2] then -- we've found it
|
||||
if (n == d[2]) or (n >= d[1]) then return end -- already here
|
||||
if n == d[1]-1 then -- right before:
|
||||
if prev and (prev[2] == n-1) then
|
||||
prev[2] = d[2]
|
||||
table.remove(chunks, i)
|
||||
return
|
||||
end
|
||||
d[1] = n
|
||||
return
|
||||
end
|
||||
if prev and (prev[2] == n-1) then --join to previous
|
||||
prev[2] = n
|
||||
return
|
||||
end
|
||||
table.insert(chunks, i, {n, n}) -- insert new interval before i
|
||||
return
|
||||
end
|
||||
prev = d
|
||||
end
|
||||
chunks[#chunks+1] = {n, n}
|
||||
end
|
||||
function mcl_vars.is_generated(pos)
|
||||
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
|
||||
for i, d in pairs(chunks) do
|
||||
if n <= d[2] then
|
||||
return (n >= d[1])
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- "Trivial" (actually NOT) function to just read the node and some stuff to not just return "ignore", like mt 5.4 does.
|
||||
-- p: Position, if it's wrong, {name="error"} node will return.
|
||||
-- force: optional (default: false) - Do the maximum to still read the node within us_timeout.
|
||||
-- us_timeout: optional (default: 244 = 0.000244 s = 1/80/80/80), set it at least to 3000000 to let mapgen to finish its job.
|
||||
--
|
||||
-- returns node definition, eg. {name="air"}. Unfortunately still can return {name="ignore"}.
|
||||
function mcl_vars.get_node(p, force, us_timeout)
|
||||
-- check initial circumstances
|
||||
if not p or not p.x or not p.y or not p.z then return {name="error"} end
|
||||
|
||||
-- try common way
|
||||
local node = minetest.get_node(p)
|
||||
if node.name ~= "ignore" then
|
||||
return node
|
||||
end
|
||||
|
||||
-- copy table to get sure it won't changed by other threads
|
||||
local pos = {x=p.x,y=p.y,z=p.z}
|
||||
|
||||
-- try LVM
|
||||
minetest.get_voxel_manip():read_from_map(pos, pos)
|
||||
node = minetest.get_node(pos)
|
||||
if node.name ~= "ignore" or not force then
|
||||
return node
|
||||
end
|
||||
|
||||
-- all ways failed - need to emerge (or forceload if generated)
|
||||
local us_timeout = us_timeout or 244
|
||||
if mcl_vars.is_generated(pos) then
|
||||
minetest.chat_send_all("IMPOSSIBLE! Please report this to MCL2 issue tracker!")
|
||||
minetest.forceload_block(pos)
|
||||
else
|
||||
minetest.emerge_area(pos, pos)
|
||||
end
|
||||
|
||||
local t = minetest.get_us_time()
|
||||
|
||||
node = minetest.get_node(pos)
|
||||
|
||||
while (not node or node.name == "ignore") and (minetest.get_us_time() - t < us_timeout) do
|
||||
node = minetest.get_node(pos)
|
||||
end
|
||||
|
||||
return node
|
||||
-- it still can return "ignore", LOL, even if force = true, but only after time out
|
||||
end
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
# mcl_mapgen
|
||||
------------
|
||||
Helps to avoid problems caused by 'chunk-in-shell' feature of mapgen.cpp.
|
||||
|
||||
It also queues your generators to run them in proper order:
|
||||
|
||||
### mcl_mapgen.register_on_generated(lvm_callback_function, order_number)
|
||||
-------------------------------------------------------------------------
|
||||
Replacement of engine API function `minetest.register_on_generated(function(vm_context))`
|
||||
|
||||
It is still unsafe. Cavegen part can and will overwrite outer 1-block layer of the chunk which is expected to be generated.
|
||||
|
||||
Nodes marked as `is_ground_content` could be overwritten. Air and water are usually 'ground content' too.
|
||||
For Minetest 5.4 it doesn't recommended to place blocks within lvm callback function.
|
||||
|
||||
See https://git.minetest.land/MineClone2/MineClone2/issues/1395
|
||||
|
||||
* `lvm_callback_function`: chunk callback LVM function definition:
|
||||
* `function(vm_context)`:
|
||||
* `vm_context` will pass into next lvm callback function from the queue!
|
||||
* `vm_context`: a table which already contains some LVM data as the fields, and some of them can be added in your lvm callback function:
|
||||
* `vm`: curent voxel manipulator object itself;
|
||||
* `chunkseed`: seed of this mapchunk;
|
||||
* `minp` & `maxp`: minimum and maximum chunk position;
|
||||
* `emin` & `emax`: minimum and maximum chunk position WITH SHELL AROUND IT;
|
||||
* `area`: voxel area, can be helpful to access data;
|
||||
* `data`: LVM buffer data array, data loads into it before the callbacks;
|
||||
* `write`: set it to true in your lvm callback functionm, if you changed `data` and want to write it;
|
||||
* `param2_data`: LVM buffer data array of `param2`, *NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS* - you load it yourself:
|
||||
* `vm_context.param2_data = vm_context.param2_data or vm_context.vm:get_param2_data(vm_context.lvm_param2_buffer)`
|
||||
* `write_param2`: set it to true in your lvm callback function, if you used `param2_data` and want to write it;
|
||||
* `light`: LVM buffer data array of light, *NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS* - you load it yourself:
|
||||
* `vm_context.light = vm_context.light or vm_context.vm.get_light_data(vm_context.lvm_light_buffer)`
|
||||
* `write_light`: set it to true in your lvm callback function, if you used `light` and want to write it;
|
||||
* `lvm_param2_buffer`: static `param2` buffer pointer, used to load `param2_data` array;
|
||||
* `shadow`: set it to false to disable shadow propagation;
|
||||
* `heightmap`: mapgen object contanting y coordinates of ground level,
|
||||
* *NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS* - load it yourself:
|
||||
* `vm_context.heightmap = vm_context.heightmap or minetest.get_mapgen_object('heightmap')`
|
||||
* `biomemap`: mapgen object contanting biome IDs of nodes,
|
||||
* *NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS* - load it yourself:
|
||||
* `vm_context.biomemap = vm_context.biomemap or minetest.get_mapgen_object('biomemap')`
|
||||
* `heatmap`: mapgen object contanting temperature values of nodes,
|
||||
* *NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS* - load it yourself:
|
||||
* `vm_context.heatmap = vm_context.heatmap or minetest.get_mapgen_object('heatmap')`
|
||||
* `humiditymap`: mapgen object contanting humidity values of nodes,
|
||||
* *NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS* - load it yourself:
|
||||
* `vm_context.humiditymap = vm_context.humiditymap or minetest.get_mapgen_object('humiditymap')`
|
||||
* `gennotify`: mapgen object contanting mapping table of structures, see Minetest Lua API for explanation,
|
||||
* *NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS* - load it yourself:
|
||||
* `vm_context.gennotify = vm_context.gennotify or minetest.get_mapgen_object('gennotify')`
|
||||
* `order_number` (optional): the less, the earlier,
|
||||
* e.g. `mcl_mapgen.order.BUILDINGS` or `mcl_mapgen.order.LARGE_BUILDINGS`
|
||||
|
||||
### mcl_mapgen.register_mapgen_block_lvm(lvm_callback_function, order_number)
|
||||
-----------------------------------------------------------------------------
|
||||
Registers lvm callback function to be called when current block (usually 16x16x16 nodes) generation is REALLY 100% finished.
|
||||
|
||||
`vm_context` passes into lvm callback function.
|
||||
* `lvm_callback_function`: the block callback LVM function definition - same as for chunks - see definition example above;
|
||||
* `order_number` (optional): the less, the earlier,
|
||||
* e.g. `mcl_mapgen.order.BUILDINGS` or `mcl_mapgen.order.LARGE_BUILDINGS`
|
||||
|
||||
### mcl_mapgen.register_mapgen_block(node_callback_function, order_number)
|
||||
--------------------------------------------------------------------------
|
||||
Registers node_callback function to be called when current block (usually 16x16x16 nodes) generation is REALLY 100% finished.
|
||||
* `node_callback_function`: node callback function definition:
|
||||
* `function(minp, maxp, seed)`:
|
||||
* `minp` & `maxp`: minimum and maximum block position;
|
||||
* `seed`: seed of this mapblock;
|
||||
* `order_number` (optional): the less, the earlier,
|
||||
* e.g. `mcl_mapgen.order.BUILDINGS` or `mcl_mapgen.order.LARGE_BUILDINGS`
|
||||
|
||||
### mcl_mapgen.register_mapgen(callback_function, order_number)
|
||||
---------------------------------------------------------------
|
||||
Registers callback function to be called when current chunk generation is REALLY 100% finished.
|
||||
|
||||
For LVM it's the most frustrating function from this mod.
|
||||
|
||||
It can't provide you access to mapgen objects. They are probably gone long ago.
|
||||
|
||||
Don't use it for accessing mapgen objects please.
|
||||
|
||||
To use VM you have to run `vm_context.vm = mcl_mapgen.get_voxel_manip(vm_context.emin, vm_context.emax)`.
|
||||
* `callback_function`: callback function definition:
|
||||
* `function(minp, maxp, seed, vm_context)`:
|
||||
* `minp` & `maxp`: minimum and maximum block position;
|
||||
* `seed`: seed of this mapblock;
|
||||
* `vm_context`: a table - see description above.
|
||||
* `order_number` (optional): the less, the earlier.
|
||||
|
||||
### mcl_mapgen.register_mapgen_lvm(lvm_callback_function, order_number)
|
||||
-----------------------------------------------------------------------
|
||||
Registers lvm callback function to be called when current chunk generation is REALLY 100% finished.
|
||||
|
||||
It's the most frustrating function from this mod. It can't provide you access to mapgen objects. They are probably gone long ago.
|
||||
|
||||
Don't use it for accessing mapgen objects please.
|
||||
|
||||
`vm_context` passes into lvm callback function.
|
||||
* `lvm_callback_function`: the block callback LVM function definition - same as above;
|
||||
* `order_number` (optional): the less, the earlier.
|
||||
|
||||
### mcl_mapgen.get_far_node(pos)
|
||||
--------------------------------
|
||||
Returns node if it is generated, otherwise returns `{name = "ignore"}`.
|
||||
|
||||
### mcl_mapgen.clamp_to_chunk(x, size)
|
||||
--------------------------------------
|
||||
Returns new `x`, slighty tuned to make structure of size `size` be within single chunk side of 80 nodes.
|
||||
|
||||
### function mcl_mapgen.get_chunk_beginning(x)
|
||||
----------------------------------------------
|
||||
Returns chunk beginning of `x`. It is the same as `minp.axis` for per-chunk callbacks, but we don't always have `minp`.
|
||||
|
||||
## Constants:
|
||||
* `mcl_mapgen.EDGE_MIN`, `mcl_mapgen.EDGE_MAX` - world edges, min & max.
|
||||
* `mcl_mapgen.seed`, `mcl_mapgen.name` - mapgen seed & name.
|
||||
* `mcl_mapgen.v6`, `mcl_mapgen.superflat`, `mcl_mapgen.singlenode` - is mapgen v6, superflat, singlenode.
|
||||
* `mcl_mapgen.normal` is mapgen normal (not superflat or singlenode).
|
|
@ -0,0 +1,574 @@
|
|||
mcl_mapgen = {}
|
||||
|
||||
local order = { -- mcl_mapgen.order...
|
||||
DEFAULT = 5000,
|
||||
CHORUS = 100000,
|
||||
BUILDINGS = 200000,
|
||||
VILLAGES = 900000,
|
||||
DUNGEONS = 950000,
|
||||
STRONGHOLDS = 999999,
|
||||
OCEAN_MONUMENT = 1000000,
|
||||
LARGE_BUILDINGS = 2000000,
|
||||
}
|
||||
|
||||
-- Begin of Compatibility stuff
|
||||
|
||||
local function unsigned(v)
|
||||
if v < 0 then
|
||||
v = 0x100000000 - (math.abs(v) % 0x100000000)
|
||||
end
|
||||
return v % 0x100000000
|
||||
end
|
||||
|
||||
if bit == nil then
|
||||
bit = {}
|
||||
function bit.bxor(a, b)
|
||||
local a = unsigned(a)
|
||||
local b = unsigned(b)
|
||||
local c = 0
|
||||
for n = 31, 0, -1 do
|
||||
local mask = math.floor(2^n)
|
||||
if (a >= mask) ~= (b >= mask) then
|
||||
c = c + mask
|
||||
end
|
||||
a = a % mask
|
||||
b = b % mask
|
||||
end
|
||||
return c
|
||||
end
|
||||
end
|
||||
|
||||
if vector.metatable == nil then
|
||||
function math.round(x)
|
||||
if x >= 0 then
|
||||
return math.floor(x + 0.5)
|
||||
end
|
||||
return math.ceil(x - 0.5)
|
||||
end
|
||||
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/vector.lua")
|
||||
end
|
||||
|
||||
-- End of compatibility stuff
|
||||
|
||||
local math_floor = math.floor
|
||||
local math_max = math.max
|
||||
local minetest_get_node = minetest.get_node
|
||||
local minetest_get_voxel_manip = minetest.get_voxel_manip
|
||||
local minetest_log = minetest.log
|
||||
local minetest_pos_to_string = minetest.pos_to_string
|
||||
|
||||
-- Calculate mapgen_edge_min/mapgen_edge_max
|
||||
mcl_mapgen.CS = math_max(1, tonumber(minetest.get_mapgen_setting("chunksize")) or 5)
|
||||
mcl_mapgen.BS = math_max(1, core.MAP_BLOCKSIZE or 16)
|
||||
mcl_mapgen.LIMIT = math_max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000)
|
||||
mcl_mapgen.MAX_LIMIT = 31000 -- MAX_MAP_GENERATION_LIMIT, https://github.com/minetest/minetest/issues/10428
|
||||
mcl_mapgen.OFFSET = - math_floor(mcl_mapgen.CS / 2)
|
||||
mcl_mapgen.OFFSET_NODES = mcl_mapgen.OFFSET * mcl_mapgen.BS
|
||||
mcl_mapgen.CS_NODES = mcl_mapgen.CS * mcl_mapgen.BS
|
||||
mcl_mapgen.LAST_BLOCK = mcl_mapgen.CS - 1
|
||||
mcl_mapgen.LAST_NODE_IN_BLOCK = mcl_mapgen.BS - 1
|
||||
mcl_mapgen.LAST_NODE_IN_CHUNK = mcl_mapgen.CS_NODES - 1
|
||||
mcl_mapgen.HALF_CS_NODES = math_floor(mcl_mapgen.CS_NODES / 2)
|
||||
mcl_mapgen.HALF_BS = math_floor(mcl_mapgen.BS / 2)
|
||||
mcl_mapgen.CS_3D = mcl_mapgen.CS^3
|
||||
mcl_mapgen.CHUNK_WITH_SHELL = mcl_mapgen.CS + 2
|
||||
mcl_mapgen.CHUNK_WITH_SHELL_3D = mcl_mapgen.CHUNK_WITH_SHELL^3
|
||||
|
||||
local central_chunk_min_pos = mcl_mapgen.OFFSET * mcl_mapgen.BS
|
||||
local central_chunk_max_pos = central_chunk_min_pos + mcl_mapgen.CS_NODES - 1
|
||||
|
||||
local ccfmin = central_chunk_min_pos - mcl_mapgen.BS -- Fullminp/fullmaxp of central chunk, in nodes
|
||||
local ccfmax = central_chunk_max_pos + mcl_mapgen.BS
|
||||
|
||||
local mapgen_limit_b = math_floor(math.min(mcl_mapgen.LIMIT, mcl_mapgen.MAX_LIMIT) / mcl_mapgen.BS)
|
||||
local mapgen_limit_min = - mapgen_limit_b * mcl_mapgen.BS
|
||||
local mapgen_limit_max = (mapgen_limit_b + 1) * mcl_mapgen.BS - 1
|
||||
|
||||
local numcmin = math_max(math_floor((ccfmin - mapgen_limit_min) / mcl_mapgen.CS_NODES), 0) -- Number of complete chunks from central chunk
|
||||
local numcmax = math_max(math_floor((mapgen_limit_max - ccfmax) / mcl_mapgen.CS_NODES), 0) -- fullminp/fullmaxp to effective mapgen limits.
|
||||
|
||||
mcl_mapgen.EDGE_MIN = central_chunk_min_pos - numcmin * mcl_mapgen.CS_NODES
|
||||
mcl_mapgen.EDGE_MAX = central_chunk_max_pos + numcmax * mcl_mapgen.CS_NODES
|
||||
|
||||
minetest_log("action", "[mcl_mapgen] World edges: mcl_mapgen.EDGE_MIN = " .. tostring(mcl_mapgen.EDGE_MIN) .. ", mcl_mapgen.EDGE_MAX = " .. tostring(mcl_mapgen.EDGE_MAX))
|
||||
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- Mapgen variables
|
||||
local overworld, end_, nether = {}, {}, {}
|
||||
local seed = minetest.get_mapgen_setting("seed")
|
||||
mcl_mapgen.seed = seed
|
||||
mcl_mapgen.name = minetest.get_mapgen_setting("mg_name")
|
||||
mcl_mapgen.v6 = mcl_mapgen.name == "v6"
|
||||
mcl_mapgen.flat = mcl_mapgen.name == "flat"
|
||||
mcl_mapgen.superflat = mcl_mapgen.flat and minetest.get_mapgen_setting("mcl_superflat_classic") == "true"
|
||||
mcl_mapgen.singlenode = mcl_mapgen.name == "singlenode"
|
||||
mcl_mapgen.normal = not mcl_mapgen.superflat and not mcl_mapgen.singlenode
|
||||
local flat, superflat, singlenode, normal = mcl_mapgen.flat, mcl_mapgen.superflat, mcl_mapgen.singlenode, mcl_mapgen.normal
|
||||
|
||||
minetest_log("action", "[mcl_mapgen] Mapgen mode: " .. (normal and "normal" or (superflat and "superflat" or (flat and "flat" or "singlenode"))))
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- Generator queues
|
||||
local queue_unsafe_engine = {}
|
||||
local queue_chunks_nodes = {}
|
||||
local queue_chunks_lvm = {}
|
||||
local queue_blocks_nodes = {}
|
||||
local queue_blocks_lvm = {}
|
||||
|
||||
-- Requirements. 0 means 'none', greater than 0 means 'required'
|
||||
local block = 0
|
||||
local queue_blocks_lvm_counter = 0
|
||||
local lvm_chunk = 0
|
||||
local param2 = 0
|
||||
local nodes_block = 0
|
||||
local nodes_chunk = 0
|
||||
local safe_functions = 0
|
||||
|
||||
local BS, CS = mcl_mapgen.BS, mcl_mapgen.CS -- Mapblock size (in nodes), Mapchunk size (in blocks)
|
||||
local offset = mcl_mapgen.OFFSET -- Central mapchunk offset (in blocks)
|
||||
local CS_NODES = mcl_mapgen.CS_NODES
|
||||
local LAST_BLOCK = mcl_mapgen.LAST_BLOCK
|
||||
local LAST_NODE_IN_BLOCK = mcl_mapgen.LAST_NODE_IN_BLOCK
|
||||
local LAST_NODE_IN_CHUNK = mcl_mapgen.LAST_NODE_IN_CHUNK
|
||||
local HALF_CS_NODES = mcl_mapgen.HALF_CS_NODES
|
||||
local CS_3D = mcl_mapgen.CS_3D
|
||||
local CHUNK_WITH_SHELL = mcl_mapgen.CHUNK_WITH_SHELL
|
||||
local CHUNK_WITH_SHELL_3D = mcl_mapgen.CHUNK_WITH_SHELL_3D
|
||||
|
||||
local DEFAULT_ORDER = order.DEFAULT
|
||||
|
||||
function mcl_mapgen.register_on_generated(callback_function, order)
|
||||
queue_unsafe_engine[#queue_unsafe_engine+1] = {i = order or DEFAULT_ORDER, f = callback_function}
|
||||
table.sort(queue_unsafe_engine, function(a, b) return (a.i <= b.i) end)
|
||||
end
|
||||
function mcl_mapgen.register_mapgen(callback_function, order)
|
||||
nodes_chunk = nodes_chunk + 1
|
||||
safe_functions = safe_functions + 1
|
||||
queue_chunks_nodes[nodes_chunk] = {i = order or DEFAULT_ORDER, f = callback_function}
|
||||
table.sort(queue_chunks_nodes, function(a, b) return (a.i <= b.i) end)
|
||||
end
|
||||
function mcl_mapgen.register_mapgen_lvm(callback_function, order)
|
||||
lvm_chunk = lvm_chunk + 1
|
||||
safe_functions = safe_functions + 1
|
||||
queue_chunks_lvm[lvm_chunk] = {i = order or DEFAULT_ORDER, f = callback_function}
|
||||
table.sort(queue_chunks_lvm, function(a, b) return (a.i <= b.i) end)
|
||||
end
|
||||
function mcl_mapgen.register_mapgen_block(callback_function, order)
|
||||
block = block + 1
|
||||
nodes_block = nodes_block + 1
|
||||
safe_functions = safe_functions + 1
|
||||
queue_blocks_nodes[nodes_block] = {i = order or DEFAULT_ORDER, f = callback_function}
|
||||
table.sort(queue_blocks_nodes, function(a, b) return (a.i <= b.i) end)
|
||||
end
|
||||
function mcl_mapgen.register_mapgen_block_lvm(callback_function, order)
|
||||
block = block + 1
|
||||
queue_blocks_lvm_counter = queue_blocks_lvm_counter + 1
|
||||
safe_functions = safe_functions + 1
|
||||
queue_blocks_lvm[queue_blocks_lvm_counter] = {order = order or DEFAULT_ORDER, callback_function = callback_function}
|
||||
table.sort(queue_blocks_lvm, function(a, b) return (a.order <= b.order) end)
|
||||
end
|
||||
|
||||
local vm_context -- here will be many references and flags, like: param2, light_data, heightmap, biomemap, heatmap, humiditymap, gennotify, write_lvm, write_param2, shadow
|
||||
local data, param2_data, light, area
|
||||
local lvm_buffer, lvm_param2_buffer, lvm_light_buffer = {}, {}, {} -- Static buffer pointers
|
||||
|
||||
local all_blocks_in_chunk = {}
|
||||
for x = -1, LAST_BLOCK+1 do
|
||||
for y = -1, LAST_BLOCK+1 do
|
||||
for z = -1, LAST_BLOCK+1 do
|
||||
all_blocks_in_chunk[CHUNK_WITH_SHELL * (CHUNK_WITH_SHELL * y + z) + x] = vector.new(x, y, z)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local chunk_scan_range = {
|
||||
[-CS_NODES] = {-1 , -1 },
|
||||
[ 0 ] = {-1 , LAST_BLOCK+1},
|
||||
[ CS_NODES] = {LAST_BLOCK+1, LAST_BLOCK+1},
|
||||
}
|
||||
|
||||
local function is_chunk_finished(minp)
|
||||
local center = vector.add(minp, HALF_CS_NODES)
|
||||
for check_x = center.x - CS_NODES, center.x + CS_NODES, CS_NODES do
|
||||
for check_y = center.y - CS_NODES, center.y + CS_NODES, CS_NODES do
|
||||
for check_z = center.z - CS_NODES, center.z + CS_NODES, CS_NODES do
|
||||
local pos = vector.new(check_x, check_y, check_z)
|
||||
if pos ~= center then
|
||||
minetest_get_voxel_manip():read_from_map(pos, pos)
|
||||
local node = minetest_get_node(pos)
|
||||
if node.name == "ignore" then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function uint32_t(v)
|
||||
if v >= 0 then
|
||||
return v % 0x100000000
|
||||
end
|
||||
return 0x100000000 - (math.abs(v) % 0x100000000)
|
||||
end
|
||||
|
||||
local function get_block_seed(pos, current_seed)
|
||||
local current_seed = current_seed or uint32_t(tonumber(seed))
|
||||
return uint32_t(uint32_t(23 * pos.x) + uint32_t(42123 * pos.y) + uint32_t(38134234 * pos.z) + current_seed)
|
||||
end
|
||||
|
||||
local function get_block_seed2(pos, current_seed)
|
||||
local current_seed = current_seed or uint32_t(tonumber(seed))
|
||||
local n = uint32_t(uint32_t(1619 * pos.x) + uint32_t(31337 * pos.y) + uint32_t(52591 * pos.z) + uint32_t(1013 * current_seed))
|
||||
n = bit.bxor(bit.rshift(n, 13), n)
|
||||
local seed = uint32_t((n * uint32_t(n * n * 60493 + 19990303) + 1376312589))
|
||||
return seed
|
||||
end
|
||||
|
||||
local function get_block_seed3(pos, current_seed)
|
||||
local current_seed = uint32_t(current_seed or uint32_t(tonumber(seed)))
|
||||
local x = uint32_t((pos.x + 32768) * 13)
|
||||
local y = uint32_t((pos.y + 32767) * 13873)
|
||||
local z = uint32_t((pos.z + 76705) * 115249)
|
||||
local seed = uint32_t(bit.bxor(current_seed, x, y, z))
|
||||
return seed
|
||||
end
|
||||
|
||||
minetest.register_on_generated(function(minp, maxp, chunkseed)
|
||||
local minp, maxp, chunkseed = minp, maxp, chunkseed
|
||||
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
|
||||
data = vm:get_data(lvm_buffer)
|
||||
area = VoxelArea:new({MinEdge=emin, MaxEdge=emax})
|
||||
vm_context = {
|
||||
data = data,
|
||||
param2_data = param2_data,
|
||||
light = light,
|
||||
area = area,
|
||||
lvm_buffer = lvm_buffer,
|
||||
lvm_param2_buffer = lvm_param2_buffer,
|
||||
lvm_light_buffer = lvm_light_buffer,
|
||||
vm = vm,
|
||||
emin = emin,
|
||||
emax = emax,
|
||||
minp = minp,
|
||||
maxp = maxp,
|
||||
chunkseed = chunkseed,
|
||||
}
|
||||
|
||||
local current_blocks = {}
|
||||
local current_chunks = {}
|
||||
if safe_functions > 0 then
|
||||
local ready_blocks = table.copy(all_blocks_in_chunk)
|
||||
local p0 = vector.new(minp)
|
||||
local center = vector.add(p0, HALF_CS_NODES)
|
||||
for x = -CS_NODES, CS_NODES, CS_NODES do
|
||||
for y = -CS_NODES, CS_NODES, CS_NODES do
|
||||
for z = -CS_NODES, CS_NODES, CS_NODES do
|
||||
if x ~= 0 or y ~= 0 or z ~= 0 then
|
||||
local offset = vector.new(x, y, z)
|
||||
local pos = center + offset
|
||||
minetest_get_voxel_manip():read_from_map(pos, pos)
|
||||
local node = minetest_get_node(pos)
|
||||
local is_generated = node.name ~= "ignore"
|
||||
if is_generated then
|
||||
local adjacent_chunk_pos = p0 + offset
|
||||
if is_chunk_finished(adjacent_chunk_pos) then
|
||||
current_chunks[#current_chunks + 1] = adjacent_chunk_pos
|
||||
end
|
||||
else
|
||||
local scan_range_x = chunk_scan_range[x]
|
||||
for cut_x = scan_range_x[1], scan_range_x[2] do
|
||||
local scan_range_y = chunk_scan_range[y]
|
||||
for cut_y = scan_range_y[1], scan_range_y[2] do
|
||||
local scan_range_z = chunk_scan_range[z]
|
||||
for cut_z = scan_range_z[1], scan_range_z[2] do
|
||||
ready_blocks[CHUNK_WITH_SHELL * (CHUNK_WITH_SHELL * cut_y + cut_z) + cut_x] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local number_of_blocks = 0
|
||||
for k, offset in pairs(ready_blocks) do
|
||||
if queue_blocks_lvm_counter > 0 or nodes_block > 0 then
|
||||
local block_minp = p0 + vector.multiply(offset, BS)
|
||||
local block_maxp = vector.add(block_minp, LAST_NODE_IN_BLOCK)
|
||||
local blockseed = get_block_seed3(block_minp)
|
||||
vm_context.minp, vm_context.maxp, vm_context.blockseed = block_minp, block_maxp, blockseed
|
||||
-- --
|
||||
-- mcl_mapgen.register_mapgen_block_lvm(function(vm_context), order_number) --
|
||||
-- --
|
||||
for _, v in pairs(queue_blocks_lvm) do
|
||||
v.callback_function(vm_context)
|
||||
end
|
||||
if nodes_block > 0 then
|
||||
current_blocks[#current_blocks + 1] = { minp = block_minp, maxp = block_maxp, blockseed = blockseed }
|
||||
end
|
||||
end
|
||||
number_of_blocks = number_of_blocks + 1
|
||||
end
|
||||
if number_of_blocks == CHUNK_WITH_SHELL_3D then
|
||||
current_chunks[#current_chunks + 1] = p0
|
||||
end
|
||||
end
|
||||
|
||||
if #queue_unsafe_engine > 0 then
|
||||
vm_context.minp, vm_context.maxp = minp, maxp
|
||||
-- * U N S A F E --
|
||||
-- mcl_mapgen.register_on_generated(function(vm_context), order_number) --
|
||||
-- * U N S A F E --
|
||||
for _, v in pairs(queue_unsafe_engine) do
|
||||
v.f(vm_context)
|
||||
end
|
||||
if vm_context.write then
|
||||
vm:set_data(data)
|
||||
end
|
||||
if vm_context.write_param2 then
|
||||
vm:set_param2_data(vm_context.param2_data)
|
||||
end
|
||||
if vm_context.write_light then
|
||||
vm:set_light_data(light)
|
||||
end
|
||||
if vm_context.write or vm_context.write_param2 or vm_context.write_light then
|
||||
vm:calc_lighting(minp, maxp, (vm_context.shadow ~= nil) or true)
|
||||
vm:write_to_map()
|
||||
vm:update_liquids()
|
||||
elseif vm_context.calc_lighting then
|
||||
vm:calc_lighting(minp, maxp, (vm_context.shadow ~= nil) or true)
|
||||
end
|
||||
end
|
||||
|
||||
for i, chunk_minp in pairs(current_chunks) do
|
||||
local chunk_maxp = vector.add(chunk_minp, LAST_NODE_IN_CHUNK)
|
||||
local current_chunk_seed = get_block_seed3(vector.subtract(chunk_minp, BS))
|
||||
area = VoxelArea:new({MinEdge=minp, MaxEdge=maxp})
|
||||
vm_context = {
|
||||
data = data,
|
||||
param2_data = param2_data,
|
||||
light = light,
|
||||
area = area,
|
||||
lvm_buffer = lvm_buffer,
|
||||
lvm_param2_buffer = lvm_param2_buffer,
|
||||
lvm_light_buffer = lvm_light_buffer,
|
||||
emin = chunk_minp,
|
||||
emax = chunk_maxp,
|
||||
minp = chunk_minp,
|
||||
maxp = chunk_maxp,
|
||||
chunkseed = current_chunk_seed,
|
||||
}
|
||||
-- --
|
||||
-- mcl_mapgen.register_mapgen_lvm(function(vm_context), order_number) --
|
||||
-- --
|
||||
for _, v in pairs(queue_chunks_lvm) do
|
||||
v.f(vm_context)
|
||||
end
|
||||
-- --
|
||||
-- mcl_mapgen.register_mapgen(function(minp, maxp, chunkseed, vm_context), order_number) --
|
||||
-- --
|
||||
for _, v in pairs(queue_chunks_nodes) do
|
||||
v.f(chunk_minp, chunk_maxp, current_chunk_seed, vm_context)
|
||||
end
|
||||
if vm_context.write or vm_context.write_param2 or vm_context.write_light then
|
||||
if vm_context.write then
|
||||
vm:set_data(data)
|
||||
end
|
||||
if vm_context.write_param2 then
|
||||
vm:set_param2_data(param2_data)
|
||||
end
|
||||
if vm_context.write_light then
|
||||
vm:set_light_data(light)
|
||||
end
|
||||
-- caused error from torches (?)
|
||||
-- vm:calc_lighting(minp, maxp, vm_context.shadow or true)
|
||||
vm:write_to_map()
|
||||
vm:update_liquids()
|
||||
elseif vm_context.calc_lighting then
|
||||
vm:calc_lighting(minp, maxp, (vm_context.shadow ~= nil) or true)
|
||||
end
|
||||
end
|
||||
|
||||
for _, b in pairs(current_blocks) do
|
||||
-- --
|
||||
-- mcl_mapgen.register_mapgen_block(function(minp, maxp, blockseed), order_number) --
|
||||
-- --
|
||||
for _, v in pairs(queue_blocks_nodes) do
|
||||
v.f(b.minp, b.maxp, b.blockseed)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_generated = mcl_mapgen.register_chunk_generator
|
||||
|
||||
function mcl_mapgen.get_far_node(p)
|
||||
local p = p
|
||||
local node = minetest_get_node(p)
|
||||
if node.name ~= "ignore" then return node end
|
||||
minetest_get_voxel_manip():read_from_map(p, p)
|
||||
return minetest_get_node(p)
|
||||
end
|
||||
|
||||
local function coordinate_to_block(x)
|
||||
return math_floor(x / BS)
|
||||
end
|
||||
|
||||
local function coordinate_to_chunk(x)
|
||||
return math_floor((coordinate_to_block(x) - offset) / CS)
|
||||
end
|
||||
|
||||
function mcl_mapgen.pos_to_block(pos)
|
||||
return {
|
||||
x = coordinate_to_block(pos.x),
|
||||
y = coordinate_to_block(pos.y),
|
||||
z = coordinate_to_block(pos.z)
|
||||
}
|
||||
end
|
||||
|
||||
function mcl_mapgen.pos_to_chunk(pos)
|
||||
return {
|
||||
x = coordinate_to_chunk(pos.x),
|
||||
y = coordinate_to_chunk(pos.y),
|
||||
z = coordinate_to_chunk(pos.z)
|
||||
}
|
||||
end
|
||||
|
||||
local k_positive = math.ceil(mcl_mapgen.MAX_LIMIT / mcl_mapgen.CS_NODES)
|
||||
local k_positive_z = k_positive * 2
|
||||
local k_positive_y = k_positive_z * k_positive_z
|
||||
|
||||
function mcl_mapgen.get_chunk_number(pos) -- unsigned int
|
||||
local c = mcl_mapgen.pos_to_chunk(pos)
|
||||
return
|
||||
(c.y + k_positive) * k_positive_y +
|
||||
(c.z + k_positive) * k_positive_z +
|
||||
c.x + k_positive
|
||||
end
|
||||
|
||||
mcl_mapgen.minecraft_height_limit = 256
|
||||
|
||||
mcl_mapgen.bedrock_is_rough = normal
|
||||
|
||||
-- Overworld
|
||||
overworld.min = -62
|
||||
if superflat then
|
||||
mcl_mapgen.ground = tonumber(minetest.get_mapgen_setting("mgflat_ground_level")) or 8
|
||||
overworld.min = mcl_mapgen.ground - 3
|
||||
end
|
||||
-- if singlenode then mcl_mapgen.overworld.min = -66 end -- DONT KNOW WHY
|
||||
overworld.max = mcl_mapgen.EDGE_MAX
|
||||
|
||||
overworld.bedrock_min = overworld.min
|
||||
overworld.bedrock_max = overworld.bedrock_min + (mcl_mapgen.bedrock_is_rough and 4 or 0)
|
||||
|
||||
mcl_mapgen.lava = normal
|
||||
overworld.lava_max = overworld.min + (normal and 10 or 0)
|
||||
|
||||
|
||||
-- The Nether (around Y = -29000)
|
||||
nether.min = -29067 -- Carefully chosen to be at a mapchunk border
|
||||
nether.max = nether.min + 128
|
||||
nether.bedrock_bottom_min = nether.min
|
||||
nether.bedrock_top_max = nether.max
|
||||
if not superflat then
|
||||
nether.bedrock_bottom_max = nether.bedrock_bottom_min + 4
|
||||
nether.bedrock_top_min = nether.bedrock_top_max - 4
|
||||
nether.lava_max = nether.min + 31
|
||||
else
|
||||
-- Thin bedrock in classic superflat mapgen
|
||||
nether.bedrock_bottom_max = nether.bedrock_bottom_min
|
||||
nether.bedrock_top_min = nether.bedrock_top_max
|
||||
nether.lava_max = nether.min + 2
|
||||
end
|
||||
if superflat then
|
||||
nether.flat_floor = nether.bedrock_bottom_max + 4
|
||||
nether.flat_ceiling = nether.bedrock_bottom_max + 52
|
||||
elseif flat then
|
||||
nether.flat_floor = nether.lava_max + 4
|
||||
nether.flat_ceiling = nether.lava_max + 52
|
||||
end
|
||||
|
||||
-- The End (surface at ca. Y = -27000)
|
||||
end_.min = -27073 -- Carefully chosen to be at a mapchunk border
|
||||
end_.max = overworld.min - 2000
|
||||
end_.platform_pos = { x = 100, y = end_.min + 74, z = 0 }
|
||||
|
||||
-- Realm barrier used to safely separate the End from the void below the Overworld
|
||||
mcl_mapgen.realm_barrier_overworld_end_max = end_.max
|
||||
mcl_mapgen.realm_barrier_overworld_end_min = end_.max - 11
|
||||
|
||||
-- Use MineClone 2-style dungeons for normal mapgen
|
||||
mcl_mapgen.dungeons = normal
|
||||
|
||||
mcl_mapgen.overworld = overworld
|
||||
mcl_mapgen.end_ = end_
|
||||
mcl_mapgen["end"] = mcl_mapgen.end_
|
||||
mcl_mapgen.nether = nether
|
||||
|
||||
mcl_mapgen.order = order
|
||||
|
||||
function mcl_mapgen.get_voxel_manip(vm_context)
|
||||
if vm_context.vm then
|
||||
return vm
|
||||
end
|
||||
vm_context.vm = minetest.get_voxel_manip(vm_context.emin, vm_context.emax)
|
||||
vm_context.emin, vm_context.emax = vm_context.vm:read_from_map(vm_context.emin, vm_context.emax)
|
||||
vm_context.area = VoxelArea:new({MinEdge=vm_context.emin, MaxEdge=vm_context.emax})
|
||||
return vm_context.vm
|
||||
end
|
||||
|
||||
function mcl_mapgen.clamp_to_chunk(x, size)
|
||||
if not size then
|
||||
minetest.log("warning", "[mcl_mapgen] Couldn't clamp " .. tostring(x) .. " - missing size")
|
||||
return x
|
||||
end
|
||||
if size > CS_NODES then
|
||||
minetest.log("warning", "[mcl_mapgen] Couldn't clamp " .. tostring(x) .. " - given size " .. tostring(size) .. " greater than chunk size " .. tostring(mcl_mapgen.CS_NODES))
|
||||
return x
|
||||
end
|
||||
local offset_in_chunk = (x + central_chunk_min_pos) % CS_NODES
|
||||
local x2_in_chunk = offset_in_chunk + size
|
||||
if x2_in_chunk <= CS_NODES then
|
||||
return x
|
||||
end
|
||||
local overflow = x2_in_chunk - CS_NODES
|
||||
if overflow > size / 2 then
|
||||
local next_x = x + (size - overflow)
|
||||
if next_x < mcl_mapgen.EDGE_MAX then
|
||||
return next_x
|
||||
end
|
||||
end
|
||||
return x - overflow
|
||||
end
|
||||
|
||||
function mcl_mapgen.get_chunk_beginning(x)
|
||||
if tonumber(x) then
|
||||
return x - ((x + central_chunk_min_pos) % CS_NODES)
|
||||
end
|
||||
if x.x then
|
||||
return {
|
||||
x = mcl_mapgen.get_chunk_beginning(x.x),
|
||||
y = mcl_mapgen.get_chunk_beginning(x.y),
|
||||
z = mcl_mapgen.get_chunk_beginning(x.z)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_mapgen.get_chunk_ending(x)
|
||||
if tonumber(x) then
|
||||
return mcl_mapgen.get_chunk_beginning(x) + LAST_NODE_IN_CHUNK
|
||||
end
|
||||
if x.x then
|
||||
return {
|
||||
x = mcl_mapgen.get_chunk_beginning(x.x) + LAST_NODE_IN_CHUNK,
|
||||
y = mcl_mapgen.get_chunk_beginning(x.y) + LAST_NODE_IN_CHUNK,
|
||||
z = mcl_mapgen.get_chunk_beginning(x.z) + LAST_NODE_IN_CHUNK
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
mcl_mapgen.get_block_seed = get_block_seed
|
||||
mcl_mapgen.get_block_seed2 = get_block_seed2
|
||||
mcl_mapgen.get_block_seed3 = get_block_seed3
|
|
@ -0,0 +1,4 @@
|
|||
name = mcl_mapgen
|
||||
author = kay27
|
||||
description = MineClone 2/5 MapGen Basic Stuff
|
||||
depends = mcl_init
|
|
@ -0,0 +1,362 @@
|
|||
--[[
|
||||
Vector helpers
|
||||
Note: The vector.*-functions must be able to accept old vectors that had no metatables
|
||||
]]
|
||||
|
||||
-- localize functions
|
||||
local setmetatable = setmetatable
|
||||
|
||||
vector = {}
|
||||
|
||||
local metatable = {}
|
||||
vector.metatable = metatable
|
||||
|
||||
local xyz = {"x", "y", "z"}
|
||||
|
||||
-- only called when rawget(v, key) returns nil
|
||||
function metatable.__index(v, key)
|
||||
return rawget(v, xyz[key]) or vector[key]
|
||||
end
|
||||
|
||||
-- only called when rawget(v, key) returns nil
|
||||
function metatable.__newindex(v, key, value)
|
||||
rawset(v, xyz[key] or key, value)
|
||||
end
|
||||
|
||||
-- constructors
|
||||
|
||||
local function fast_new(x, y, z)
|
||||
return setmetatable({x = x, y = y, z = z}, metatable)
|
||||
end
|
||||
|
||||
function vector.new(a, b, c)
|
||||
if a and b and c then
|
||||
return fast_new(a, b, c)
|
||||
end
|
||||
|
||||
-- deprecated, use vector.copy and vector.zero directly
|
||||
if type(a) == "table" then
|
||||
return vector.copy(a)
|
||||
else
|
||||
assert(not a, "Invalid arguments for vector.new()")
|
||||
return vector.zero()
|
||||
end
|
||||
end
|
||||
|
||||
function vector.zero()
|
||||
return fast_new(0, 0, 0)
|
||||
end
|
||||
|
||||
function vector.copy(v)
|
||||
assert(v.x and v.y and v.z, "Invalid vector passed to vector.copy()")
|
||||
return fast_new(v.x, v.y, v.z)
|
||||
end
|
||||
|
||||
function vector.from_string(s, init)
|
||||
local x, y, z, np = string.match(s, "^%s*%(%s*([^%s,]+)%s*[,%s]%s*([^%s,]+)%s*[,%s]" ..
|
||||
"%s*([^%s,]+)%s*[,%s]?%s*%)()", init)
|
||||
x = tonumber(x)
|
||||
y = tonumber(y)
|
||||
z = tonumber(z)
|
||||
if not (x and y and z) then
|
||||
return nil
|
||||
end
|
||||
return fast_new(x, y, z), np
|
||||
end
|
||||
|
||||
function vector.to_string(v)
|
||||
return string.format("(%g, %g, %g)", v.x, v.y, v.z)
|
||||
end
|
||||
metatable.__tostring = vector.to_string
|
||||
|
||||
function vector.equals(a, b)
|
||||
return a.x == b.x and
|
||||
a.y == b.y and
|
||||
a.z == b.z
|
||||
end
|
||||
metatable.__eq = vector.equals
|
||||
|
||||
-- unary operations
|
||||
|
||||
function vector.length(v)
|
||||
return math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
|
||||
end
|
||||
-- Note: we can not use __len because it is already used for primitive table length
|
||||
|
||||
function vector.normalize(v)
|
||||
local len = vector.length(v)
|
||||
if len == 0 then
|
||||
return fast_new(0, 0, 0)
|
||||
else
|
||||
return vector.divide(v, len)
|
||||
end
|
||||
end
|
||||
|
||||
function vector.floor(v)
|
||||
return vector.apply(v, math.floor)
|
||||
end
|
||||
|
||||
function vector.round(v)
|
||||
return fast_new(
|
||||
math.round(v.x),
|
||||
math.round(v.y),
|
||||
math.round(v.z)
|
||||
)
|
||||
end
|
||||
|
||||
function vector.apply(v, func)
|
||||
return fast_new(
|
||||
func(v.x),
|
||||
func(v.y),
|
||||
func(v.z)
|
||||
)
|
||||
end
|
||||
|
||||
function vector.distance(a, b)
|
||||
local x = a.x - b.x
|
||||
local y = a.y - b.y
|
||||
local z = a.z - b.z
|
||||
return math.sqrt(x * x + y * y + z * z)
|
||||
end
|
||||
|
||||
function vector.direction(pos1, pos2)
|
||||
return vector.subtract(pos2, pos1):normalize()
|
||||
end
|
||||
|
||||
function vector.angle(a, b)
|
||||
local dotp = vector.dot(a, b)
|
||||
local cp = vector.cross(a, b)
|
||||
local crossplen = vector.length(cp)
|
||||
return math.atan2(crossplen, dotp)
|
||||
end
|
||||
|
||||
function vector.dot(a, b)
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z
|
||||
end
|
||||
|
||||
function vector.cross(a, b)
|
||||
return fast_new(
|
||||
a.y * b.z - a.z * b.y,
|
||||
a.z * b.x - a.x * b.z,
|
||||
a.x * b.y - a.y * b.x
|
||||
)
|
||||
end
|
||||
|
||||
function metatable.__unm(v)
|
||||
return fast_new(-v.x, -v.y, -v.z)
|
||||
end
|
||||
|
||||
-- add, sub, mul, div operations
|
||||
|
||||
function vector.add(a, b)
|
||||
if type(b) == "table" then
|
||||
return fast_new(
|
||||
a.x + b.x,
|
||||
a.y + b.y,
|
||||
a.z + b.z
|
||||
)
|
||||
else
|
||||
return fast_new(
|
||||
a.x + b,
|
||||
a.y + b,
|
||||
a.z + b
|
||||
)
|
||||
end
|
||||
end
|
||||
function metatable.__add(a, b)
|
||||
return fast_new(
|
||||
a.x + b.x,
|
||||
a.y + b.y,
|
||||
a.z + b.z
|
||||
)
|
||||
end
|
||||
|
||||
function vector.subtract(a, b)
|
||||
if type(b) == "table" then
|
||||
return fast_new(
|
||||
a.x - b.x,
|
||||
a.y - b.y,
|
||||
a.z - b.z
|
||||
)
|
||||
else
|
||||
return fast_new(
|
||||
a.x - b,
|
||||
a.y - b,
|
||||
a.z - b
|
||||
)
|
||||
end
|
||||
end
|
||||
function metatable.__sub(a, b)
|
||||
return fast_new(
|
||||
a.x - b.x,
|
||||
a.y - b.y,
|
||||
a.z - b.z
|
||||
)
|
||||
end
|
||||
|
||||
function vector.multiply(a, b)
|
||||
if type(b) == "table" then
|
||||
return fast_new(
|
||||
a.x * b.x,
|
||||
a.y * b.y,
|
||||
a.z * b.z
|
||||
)
|
||||
else
|
||||
return fast_new(
|
||||
a.x * b,
|
||||
a.y * b,
|
||||
a.z * b
|
||||
)
|
||||
end
|
||||
end
|
||||
function metatable.__mul(a, b)
|
||||
if type(a) == "table" then
|
||||
return fast_new(
|
||||
a.x * b,
|
||||
a.y * b,
|
||||
a.z * b
|
||||
)
|
||||
else
|
||||
return fast_new(
|
||||
a * b.x,
|
||||
a * b.y,
|
||||
a * b.z
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function vector.divide(a, b)
|
||||
if type(b) == "table" then
|
||||
return fast_new(
|
||||
a.x / b.x,
|
||||
a.y / b.y,
|
||||
a.z / b.z
|
||||
)
|
||||
else
|
||||
return fast_new(
|
||||
a.x / b,
|
||||
a.y / b,
|
||||
a.z / b
|
||||
)
|
||||
end
|
||||
end
|
||||
function metatable.__div(a, b)
|
||||
-- scalar/vector makes no sense
|
||||
return fast_new(
|
||||
a.x / b,
|
||||
a.y / b,
|
||||
a.z / b
|
||||
)
|
||||
end
|
||||
|
||||
-- misc stuff
|
||||
|
||||
function vector.offset(v, x, y, z)
|
||||
return fast_new(
|
||||
v.x + x,
|
||||
v.y + y,
|
||||
v.z + z
|
||||
)
|
||||
end
|
||||
|
||||
function vector.sort(a, b)
|
||||
return fast_new(math.min(a.x, b.x), math.min(a.y, b.y), math.min(a.z, b.z)),
|
||||
fast_new(math.max(a.x, b.x), math.max(a.y, b.y), math.max(a.z, b.z))
|
||||
end
|
||||
|
||||
function vector.check(v)
|
||||
return getmetatable(v) == metatable
|
||||
end
|
||||
|
||||
local function sin(x)
|
||||
if x % math.pi == 0 then
|
||||
return 0
|
||||
else
|
||||
return math.sin(x)
|
||||
end
|
||||
end
|
||||
|
||||
local function cos(x)
|
||||
if x % math.pi == math.pi / 2 then
|
||||
return 0
|
||||
else
|
||||
return math.cos(x)
|
||||
end
|
||||
end
|
||||
|
||||
function vector.rotate_around_axis(v, axis, angle)
|
||||
local cosangle = cos(angle)
|
||||
local sinangle = sin(angle)
|
||||
axis = vector.normalize(axis)
|
||||
-- https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula
|
||||
local dot_axis = vector.multiply(axis, vector.dot(axis, v))
|
||||
local cross = vector.cross(v, axis)
|
||||
return vector.new(
|
||||
cross.x * sinangle + (v.x - dot_axis.x) * cosangle + dot_axis.x,
|
||||
cross.y * sinangle + (v.y - dot_axis.y) * cosangle + dot_axis.y,
|
||||
cross.z * sinangle + (v.z - dot_axis.z) * cosangle + dot_axis.z
|
||||
)
|
||||
end
|
||||
|
||||
function vector.rotate(v, rot)
|
||||
local sinpitch = sin(-rot.x)
|
||||
local sinyaw = sin(-rot.y)
|
||||
local sinroll = sin(-rot.z)
|
||||
local cospitch = cos(rot.x)
|
||||
local cosyaw = cos(rot.y)
|
||||
local cosroll = math.cos(rot.z)
|
||||
-- Rotation matrix that applies yaw, pitch and roll
|
||||
local matrix = {
|
||||
{
|
||||
sinyaw * sinpitch * sinroll + cosyaw * cosroll,
|
||||
sinyaw * sinpitch * cosroll - cosyaw * sinroll,
|
||||
sinyaw * cospitch,
|
||||
},
|
||||
{
|
||||
cospitch * sinroll,
|
||||
cospitch * cosroll,
|
||||
-sinpitch,
|
||||
},
|
||||
{
|
||||
cosyaw * sinpitch * sinroll - sinyaw * cosroll,
|
||||
cosyaw * sinpitch * cosroll + sinyaw * sinroll,
|
||||
cosyaw * cospitch,
|
||||
},
|
||||
}
|
||||
-- Compute matrix multiplication: `matrix` * `v`
|
||||
return vector.new(
|
||||
matrix[1][1] * v.x + matrix[1][2] * v.y + matrix[1][3] * v.z,
|
||||
matrix[2][1] * v.x + matrix[2][2] * v.y + matrix[2][3] * v.z,
|
||||
matrix[3][1] * v.x + matrix[3][2] * v.y + matrix[3][3] * v.z
|
||||
)
|
||||
end
|
||||
|
||||
function vector.dir_to_rotation(forward, up)
|
||||
forward = vector.normalize(forward)
|
||||
local rot = vector.new(math.asin(forward.y), -math.atan2(forward.x, forward.z), 0)
|
||||
if not up then
|
||||
return rot
|
||||
end
|
||||
assert(vector.dot(forward, up) < 0.000001,
|
||||
"Invalid vectors passed to vector.dir_to_rotation().")
|
||||
up = vector.normalize(up)
|
||||
-- Calculate vector pointing up with roll = 0, just based on forward vector.
|
||||
local forwup = vector.rotate(vector.new(0, 1, 0), rot)
|
||||
-- 'forwup' and 'up' are now in a plane with 'forward' as normal.
|
||||
-- The angle between them is the absolute of the roll value we're looking for.
|
||||
rot.z = vector.angle(forwup, up)
|
||||
|
||||
-- Since vector.angle never returns a negative value or a value greater
|
||||
-- than math.pi, rot.z has to be inverted sometimes.
|
||||
-- To determine wether this is the case, we rotate the up vector back around
|
||||
-- the forward vector and check if it worked out.
|
||||
local back = vector.rotate_around_axis(up, forward, -rot.z)
|
||||
|
||||
-- We don't use vector.equals for this because of floating point imprecision.
|
||||
if (back.x - forwup.x) * (back.x - forwup.x) +
|
||||
(back.y - forwup.y) * (back.y - forwup.y) +
|
||||
(back.z - forwup.z) * (back.z - forwup.z) > 0.0000001 then
|
||||
rot.z = -rot.z
|
||||
end
|
||||
return rot
|
||||
end
|
Before Width: | Height: | Size: 144 B |
|
@ -0,0 +1,107 @@
|
|||
# mcl_time v2.2
|
||||
## by kay27 for MineClone 5
|
||||
---------------------------
|
||||
This mod counts time when all players sleep or some area is inactive.
|
||||
|
||||
It depends very much on `time_speed` configuration variable, which could be changed 'on the fly' by a chat command:
|
||||
* `/set time_speed 72`
|
||||
|
||||
If `time_speed` set to 0, this mod logs warnings and returns zeroes.
|
||||
|
||||
### mcl_time.get_seconds_irl()
|
||||
------------------------------
|
||||
Returns: Integer value of realtime (not in-game) seconds since world creation.
|
||||
|
||||
Usually this value grow smoothly. But when you skip the night being in the bed, or leave some area for some time, you may experience value jumps. That's basically the idea of this mod.
|
||||
|
||||
### mcl_time.get_number_of_times(last_time, interval, chance)
|
||||
-------------------------------------------------------------
|
||||
Returns the number of how many times something would probably happen if the area was active and we didn't skip the nights.
|
||||
|
||||
Arguments:
|
||||
* `last_time` - you pass last known for you value of `seconds_irl`
|
||||
* `interval` and `chance` - interval and chance like from ABM setup
|
||||
|
||||
Returns:
|
||||
* Integer number of how many times something would probably happen if the area was active all the time and we didn't skip the nights.
|
||||
* Integer value of in-real-life (not in-game) seconds since world creation.
|
||||
|
||||
### mcl_time.touch(pos)
|
||||
-----------------------
|
||||
This function 'toches' node at position `pos` by writing `_t` meta variable of `seconds_irl`.
|
||||
|
||||
### mcl_time.get_number_of_times_at_pos(pos, interval, chance)
|
||||
--------------------------------------------------------------
|
||||
Returns the number of how many times something would probably happen for node at pos `pos` if the area was active and we didn't skip the nights.
|
||||
It reads and updates meta variable `_t` from position `pos` and uses it as previous `seconds_irl`, so we don't need to remember it.
|
||||
|
||||
Argunments:
|
||||
* `pos` - node position
|
||||
* `interval` and `chance` - interval and chance like from ABM setup
|
||||
|
||||
Returns:
|
||||
* Integer number of how many times something would happen to the node at position `pos` if the area was active all the time and we didn't skip the nights.
|
||||
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `0`.
|
||||
|
||||
### mcl_time.get_number_of_times_at_pos_or_1(pos, interval, chance)
|
||||
-------------------------------------------------------------------
|
||||
Returns the number of how many times something would probably happen for node at pos `pos` if the area was active and we didn't skip the nights.
|
||||
It reads and updates meta variable `_t` from position `pos` and uses it as previous `seconds_irl`, so we don't need to remember it.
|
||||
|
||||
Argunments:
|
||||
* `pos` - node position
|
||||
* `interval` and `chance` - interval and chance like from ABM setup
|
||||
|
||||
Returns:
|
||||
* Integer number of how many times something would happen to the node at position `pos` if the area was active all the time and we didn't skip the nights.
|
||||
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `1`.
|
||||
|
||||
### mcl_time.get_number_of_times_at_pos_or_nil(pos, interval, chance)
|
||||
---------------------------------------------------------------------
|
||||
Returns the number of how many times something would probably happen for node at pos `pos` if the area was active and we didn't skip the nights.
|
||||
It reads and updates meta variable `_t` from position `pos` and uses it as previous `seconds_irl`, so we don't need to remember it.
|
||||
|
||||
Argunments:
|
||||
* `pos` - node position
|
||||
* `interval` and `chance` - interval and chance like from ABM setup
|
||||
|
||||
Returns:
|
||||
* Integer number of how many times something would happen to the node at position `pos` if the area was active all the time and we didn't skip the nights.
|
||||
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `nil`.
|
||||
|
||||
### mcl_time.get_irl_seconds_passed_at_pos(pos)
|
||||
-----------------------------------------------
|
||||
Returns the number of how many in-real-life seconds would be passed for the node at position `pos`, if the area was active all the time and we didn't skip the nights.
|
||||
It uses node meta variable `_t` to calculate this value.
|
||||
|
||||
Argunments:
|
||||
* `pos` - node position
|
||||
|
||||
Returns:
|
||||
* Integer number of how many in-real-life seconds would be passed for the node at position `pos, if the area was active all the time and we didn't skip the nights.
|
||||
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `0`.
|
||||
|
||||
### mcl_time.get_irl_seconds_passed_at_pos_or_1(pos)
|
||||
----------------------------------------------------
|
||||
Returns the number of how many in-real-life seconds would be passed for the node at position `pos`, if the area was active all the time and we didn't skip the nights.
|
||||
It uses node meta variable `_t` to calculate this value.
|
||||
|
||||
Argunments:
|
||||
* `pos` - node position
|
||||
|
||||
Returns:
|
||||
* Integer number of how many in-real-life seconds would be passed for the node at position `pos, if the area was active all the time and we didn't skip the nights.
|
||||
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `1`.
|
||||
|
||||
### mcl_time.get_irl_seconds_passed_at_pos_or_nil(pos)
|
||||
----------------------------------------------------
|
||||
Returns the number of how many in-real-life seconds would be passed for the node at position `pos`, if the area was active all the time and we didn't skip the nights.
|
||||
It uses node meta variable `_t` to calculate this value.
|
||||
|
||||
Argunments:
|
||||
* `pos` - node position
|
||||
|
||||
Returns:
|
||||
* Integer number of how many in-real-life seconds would be passed for the node at position `pos, if the area was active all the time and we didn't skip the nights.
|
||||
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `nil`.
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
mcl_time = {}
|
||||
|
||||
local time_update_interval = 2
|
||||
local retry_on_fail_interval = 500
|
||||
local default_time_speed = 72
|
||||
local save_to_storage_interval = 600
|
||||
local meta_name = "_t"
|
||||
|
||||
local current_time_update_interval = time_update_interval
|
||||
|
||||
local storage = minetest.get_mod_storage()
|
||||
local seconds_irl_public = tonumber(storage:get_string("seconds_irl")) or -2
|
||||
local last_save_seconds_irl = seconds_irl_public
|
||||
local next_save_seconds_irl = last_save_seconds_irl + save_to_storage_interval
|
||||
|
||||
local previous_seconds_irl = -2
|
||||
local time_speed_is_ok = true
|
||||
|
||||
local function get_seconds_irl()
|
||||
local time_speed = tonumber(minetest.settings:get("time_speed") or default_time_speed)
|
||||
if time_speed < 1 then
|
||||
if time_speed_is_ok then
|
||||
minetest.log("warning", "[mcl_time] time_speed < 1 - please increase to make mcl_time api work (default: " .. default_time_speed .. ")")
|
||||
time_speed_is_ok = false
|
||||
end
|
||||
return 0
|
||||
else
|
||||
if not time_speed_is_ok then
|
||||
minetest.log("warning", "[mcl_time] time_speed is now " .. time_speed)
|
||||
time_speed_is_ok = true
|
||||
end
|
||||
end
|
||||
local irl_multiplier = 86400 / time_speed
|
||||
local day_count = minetest.get_day_count()
|
||||
local timeofday = minetest.get_timeofday()
|
||||
local seconds_irl
|
||||
if not day_count or not timeofday then
|
||||
seconds_irl = seconds_irl_public
|
||||
else
|
||||
local days_ig = 0.0 + day_count + timeofday
|
||||
seconds_irl = days_ig * irl_multiplier
|
||||
end
|
||||
|
||||
if previous_seconds_irl == seconds_irl then
|
||||
current_time_update_interval = math.min(current_time_update_interval * 2, retry_on_fail_interval)
|
||||
minetest.log("warning", "[mcl_time] Time doesn't change! seconds_irl=" .. tostring(seconds_irl)
|
||||
.. ", day_count = " .. tostring(day_count) .. ", timeofday=" .. tostring(timeofday)
|
||||
.. " - increasing update interval to " .. tostring(current_time_update_interval))
|
||||
else
|
||||
previous_seconds_irl = seconds_irl
|
||||
if current_time_update_interval ~= time_update_interval then
|
||||
current_time_update_interval = time_update_interval
|
||||
minetest.log("action", "[mcl_time] Time is changing again: seconds_irl=" .. tostring(seconds_irl)
|
||||
.. ", day_count = " .. tostring(day_count) .. ", timeofday=" .. tostring(timeofday)
|
||||
.. ", update_interval=" .. tostring(current_time_update_interval))
|
||||
end
|
||||
end
|
||||
|
||||
if last_save_seconds_irl >= next_save_seconds_irl then
|
||||
storage:set_string("seconds_irl", tostring(seconds_irl))
|
||||
next_save_seconds_irl = seconds_irl + save_to_storage_interval
|
||||
end
|
||||
|
||||
return math.floor(seconds_irl)
|
||||
end
|
||||
|
||||
seconds_irl_public = get_seconds_irl()
|
||||
|
||||
function mcl_time.get_seconds_irl()
|
||||
return seconds_irl_public
|
||||
end
|
||||
|
||||
local function time_runner()
|
||||
seconds_irl_public = get_seconds_irl()
|
||||
minetest.after(current_time_update_interval, time_runner)
|
||||
end
|
||||
|
||||
function mcl_time.get_number_of_times(last_time, interval, chance)
|
||||
if not last_time then return 0, seconds_irl_publicend end
|
||||
if seconds_irl_public < 2 then return 0, seconds_irl_public end
|
||||
if not interval then return 0, seconds_irl_public end
|
||||
if not chance then return 0, seconds_irl_public end
|
||||
if interval < 1 then return 0, seconds_irl_public end
|
||||
if chance < 1 then return 0, seconds_irl_public end
|
||||
local number_of_intervals = (seconds_irl_public - last_time) / interval
|
||||
if number_of_intervals < 1 then return 0, seconds_irl_public end
|
||||
local average_chance = (1 + chance) / 2
|
||||
local number_of_times = math.floor(number_of_intervals / average_chance)
|
||||
return number_of_times, seconds_irl_public
|
||||
end
|
||||
|
||||
local get_number_of_times = mcl_time.get_number_of_times
|
||||
|
||||
function mcl_time.touch(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_int(meta_name, seconds_irl_public)
|
||||
end
|
||||
|
||||
function mcl_time.get_number_of_times_at_pos(pos, interval, chance)
|
||||
if not pos then return 0 end
|
||||
if not time_speed_is_ok then return 0 end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local last_time = meta:get_int(meta_name)
|
||||
meta:set_int(meta_name, seconds_irl_public)
|
||||
local number_of_times = (last_time <= 0) and 0 or get_number_of_times(last_time, interval, chance)
|
||||
return number_of_times
|
||||
end
|
||||
|
||||
local get_number_of_times_at_pos = mcl_time.get_number_of_times_at_pos
|
||||
|
||||
function mcl_time.get_number_of_times_at_pos_or_1(pos, interval, chance)
|
||||
return math.max(get_number_of_times_at_pos(pos, interval, chance), 1)
|
||||
end
|
||||
|
||||
function mcl_time.get_number_of_times_at_pos_or_nil(pos, interval, chance)
|
||||
local number_of_times_at_pos = get_number_of_times_at_pos(pos, interval, chance)
|
||||
if number_of_times_at_pos > 0 then
|
||||
return number_of_times_at_pos
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_time.get_irl_seconds_passed_at_pos(pos)
|
||||
if not pos then return 0 end
|
||||
if not time_speed_is_ok then return 0 end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local last_time = meta:get_int(meta_name)
|
||||
meta:set_int(meta_name, seconds_irl_public)
|
||||
local irl_seconds_passed = (last_time <= 0) and 0 or (seconds_irl_public - last_time)
|
||||
return irl_seconds_passed
|
||||
end
|
||||
|
||||
function mcl_time.get_irl_seconds_passed_at_pos_or_1(pos)
|
||||
if not pos then return 1 end
|
||||
if not time_speed_is_ok then return 1 end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local last_time = meta:get_int(meta_name)
|
||||
meta:set_int(meta_name, seconds_irl_public)
|
||||
local irl_seconds_passed = (last_time <= 0) and 1 or (seconds_irl_public - last_time)
|
||||
return irl_seconds_passed
|
||||
end
|
||||
|
||||
function mcl_time.get_irl_seconds_passed_at_pos_or_nil(pos)
|
||||
if not pos then return end
|
||||
if not time_speed_is_ok then return end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local last_time = meta:get_int(meta_name)
|
||||
meta:set_int(meta_name, seconds_irl_public)
|
||||
if last_time <= 0 then return end
|
||||
local delta_time = seconds_irl_public - last_time
|
||||
if delta_time <= 0 then return end
|
||||
meta:set_int(meta_name, seconds_irl_public)
|
||||
return delta_time
|
||||
end
|
||||
|
||||
time_runner()
|
||||
local day_count = minetest.get_day_count()
|
||||
local timeofday = minetest.get_timeofday()
|
||||
minetest.log("action", "[mcl_time] time runner started, current in-real-life seconds: " .. seconds_irl_public
|
||||
.. ", time_speed: " .. tostring(minetest.settings:get("time_speed"))
|
||||
.. ", day_count: " .. tostring(day_count)
|
||||
.. ", timeofday: " .. tostring(timeofday)
|
||||
.. ", update_interval=" .. tostring(current_time_update_interval)
|
||||
)
|
|
@ -0,0 +1,3 @@
|
|||
name = mcl_time
|
||||
author = kay27
|
||||
description = This mod counts time when all players sleep or some area is inactive
|
|
@ -524,7 +524,7 @@ function mcl_util.deal_damage(target, damage, mcl_reason)
|
|||
if luaentity.deal_damage then
|
||||
luaentity:deal_damage(damage, mcl_reason or {type = "generic"})
|
||||
return
|
||||
elseif luaentity.is_mob then
|
||||
elseif luaentity._cmi_is_mob then
|
||||
-- local puncher = mcl_reason and mcl_reason.direct or target
|
||||
-- target:punch(puncher, 1.0, {full_punch_interval = 1.0, damage_groups = {fleshy = damage}}, vector.direction(puncher:get_pos(), target:get_pos()), damage)
|
||||
if luaentity.health > 0 then
|
||||
|
@ -544,7 +544,7 @@ end
|
|||
function mcl_util.get_hp(obj)
|
||||
local luaentity = obj:get_luaentity()
|
||||
|
||||
if luaentity and luaentity.is_mob then
|
||||
if luaentity and luaentity._cmi_is_mob then
|
||||
return luaentity.health
|
||||
else
|
||||
return obj:get_hp()
|
||||
|
@ -610,95 +610,3 @@ function mcl_util.get_pointed_thing(player, liquid)
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- This following part is 2 wrapper functions + helpers for
|
||||
-- object:set_bones
|
||||
-- and player:set_properties preventing them from being resent on
|
||||
-- every globalstep when they have not changed.
|
||||
|
||||
local function roundN(n, d)
|
||||
if type(n) ~= "number" then return n end
|
||||
local m = 10^d
|
||||
return math.floor(n * m + 0.5) / m
|
||||
end
|
||||
|
||||
local function close_enough(a,b)
|
||||
local rt=true
|
||||
if type(a) == "table" and type(b) == "table" then
|
||||
for k,v in pairs(a) do
|
||||
if roundN(v,2) ~= roundN(b[k],2) then
|
||||
rt=false
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
rt = roundN(a,2) == roundN(b,2)
|
||||
end
|
||||
return rt
|
||||
end
|
||||
|
||||
local function props_changed(props,oldprops)
|
||||
local changed=false
|
||||
local p={}
|
||||
for k,v in pairs(props) do
|
||||
if not close_enough(v,oldprops[k]) then
|
||||
p[k]=v
|
||||
changed=true
|
||||
end
|
||||
end
|
||||
return changed,p
|
||||
end
|
||||
|
||||
--tests for roundN
|
||||
local test_round1=15
|
||||
local test_round2=15.00199999999
|
||||
local test_round3=15.00111111
|
||||
local test_round4=15.00999999
|
||||
|
||||
assert(roundN(test_round1,2)==roundN(test_round1,2))
|
||||
assert(roundN(test_round1,2)==roundN(test_round2,2))
|
||||
assert(roundN(test_round1,2)==roundN(test_round3,2))
|
||||
assert(roundN(test_round1,2)~=roundN(test_round4,2))
|
||||
|
||||
-- tests for close_enough
|
||||
local test_cb = {-0.35,0,-0.35,0.35,0.8,0.35} --collisionboxes
|
||||
local test_cb_close = {-0.351213,0,-0.35,0.35,0.8,0.351212}
|
||||
local test_cb_diff = {-0.35,0,-1.35,0.35,0.8,0.35}
|
||||
|
||||
local test_eh = 1.65 --eye height
|
||||
local test_eh_close = 1.65123123
|
||||
local test_eh_diff = 1.35
|
||||
|
||||
local test_nt = { r = 225, b = 225, a = 225, g = 225 } --nametag
|
||||
local test_nt_diff = { r = 225, b = 225, a = 0, g = 225 }
|
||||
|
||||
assert(close_enough(test_cb,test_cb_close))
|
||||
assert(not close_enough(test_cb,test_cb_diff))
|
||||
assert(close_enough(test_eh,test_eh_close))
|
||||
assert(not close_enough(test_eh,test_eh_diff))
|
||||
assert(not close_enough(test_nt,test_nt_diff)) --no floats involved here
|
||||
|
||||
--tests for properties_changed
|
||||
local test_properties_set1={collisionbox = {-0.35,0,-0.35,0.35,0.8,0.35}, eye_height = 0.65, nametag_color = { r = 225, b = 225, a = 225, g = 225 }}
|
||||
local test_properties_set2={collisionbox = {-0.35,0,-0.35,0.35,0.8,0.35}, eye_height = 1.35, nametag_color = { r = 225, b = 225, a = 225, g = 225 }}
|
||||
|
||||
local test_p1,_=props_changed(test_properties_set1,test_properties_set1)
|
||||
local test_p2,_=props_changed(test_properties_set1,test_properties_set2)
|
||||
|
||||
assert(not test_p1)
|
||||
assert(test_p2)
|
||||
|
||||
function mcl_util.set_properties(obj,props)
|
||||
local changed,p=props_changed(props,obj:get_properties())
|
||||
if changed then
|
||||
obj:set_properties(p)
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_util.set_bone_position(obj,b,p,r) --bone,position,rotation
|
||||
local oldp,oldr=obj:get_bone_position(b)
|
||||
if vector.equals(vector.round(oldp),vector.round(p)) and vector.equals(vector.round(oldr),vector.round(r)) then
|
||||
return
|
||||
end
|
||||
obj:set_bone_position(b,p,r)
|
||||
end
|
||||
|
|
|
@ -5,25 +5,25 @@ local get_connected_players = minetest.get_connected_players
|
|||
-- For a given position, returns a 2-tuple:
|
||||
-- 1st return value: true if pos is in void
|
||||
-- 2nd return value: true if it is in the deadly part of the void
|
||||
local min1, min2, min3 = mcl_mapgen.overworld.min, mcl_mapgen.end_.min, mcl_mapgen.nether.min
|
||||
local max1, max2, max3 = mcl_mapgen.overworld.max, mcl_mapgen.end_.max, mcl_mapgen.nether.max+128
|
||||
function mcl_worlds.is_in_void(pos)
|
||||
local void =
|
||||
not ((pos.y < mcl_vars.mg_overworld_max and pos.y > mcl_vars.mg_overworld_min) or
|
||||
(pos.y < mcl_vars.mg_nether_max+128 and pos.y > mcl_vars.mg_nether_min) or
|
||||
(pos.y < mcl_vars.mg_end_max and pos.y > mcl_vars.mg_end_min))
|
||||
local y = pos.y
|
||||
local void = not ((y < max1 and y > min1) or (y < max2 and y > min2) or (y < max3 and y > min3))
|
||||
|
||||
local void_deadly = false
|
||||
local deadly_tolerance = 64 -- the player must be this many nodes “deep” into the void to be damaged
|
||||
if void then
|
||||
-- Overworld → Void → End → Void → Nether → Void
|
||||
if pos.y < mcl_vars.mg_overworld_min and pos.y > mcl_vars.mg_end_max then
|
||||
void_deadly = pos.y < mcl_vars.mg_overworld_min - deadly_tolerance
|
||||
elseif pos.y < mcl_vars.mg_end_min and pos.y > mcl_vars.mg_nether_max+128 then
|
||||
if y < min1 and y > max2 then
|
||||
void_deadly = y < min1 - deadly_tolerance
|
||||
elseif y < min2 and y > max3 then
|
||||
-- The void between End and Nether. Like usual, but here, the void
|
||||
-- *above* the Nether also has a small tolerance area, so player
|
||||
-- can fly above the Nether without getting hurt instantly.
|
||||
void_deadly = (pos.y < mcl_vars.mg_end_min - deadly_tolerance) and (pos.y > mcl_vars.mg_nether_max+128 + deadly_tolerance)
|
||||
elseif pos.y < mcl_vars.mg_nether_min then
|
||||
void_deadly = pos.y < mcl_vars.mg_nether_min - deadly_tolerance
|
||||
void_deadly = (y < min2 - deadly_tolerance) and (y > max3 + deadly_tolerance)
|
||||
elseif y < min3 then
|
||||
void_deadly = y < min3 - deadly_tolerance
|
||||
end
|
||||
end
|
||||
return void, void_deadly
|
||||
|
@ -35,12 +35,12 @@ end
|
|||
-- If the Y coordinate is not located in any dimension, it will return:
|
||||
-- nil, "void"
|
||||
function mcl_worlds.y_to_layer(y)
|
||||
if y >= mcl_vars.mg_overworld_min then
|
||||
return y - mcl_vars.mg_overworld_min, "overworld"
|
||||
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max+128 then
|
||||
return y - mcl_vars.mg_nether_min, "nether"
|
||||
elseif y >= mcl_vars.mg_end_min and y <= mcl_vars.mg_end_max then
|
||||
return y - mcl_vars.mg_end_min, "end"
|
||||
if y >= min1 then
|
||||
return y - min1, "overworld"
|
||||
elseif y >= min3 and y <= max3 then
|
||||
return y - min3, "nether"
|
||||
elseif y >= min2 and y <= max2 then
|
||||
return y - min2, "end"
|
||||
else
|
||||
return nil, "void"
|
||||
end
|
||||
|
@ -61,25 +61,25 @@ local pos_to_dimension = mcl_worlds.pos_to_dimension
|
|||
-- MineClone 2.
|
||||
-- mc_dimension is one of "overworld", "nether", "end" (default: "overworld").
|
||||
function mcl_worlds.layer_to_y(layer, mc_dimension)
|
||||
if mc_dimension == "overworld" or mc_dimension == nil then
|
||||
return layer + mcl_vars.mg_overworld_min
|
||||
if not mc_dimension or mc_dimension == "overworld" then
|
||||
return layer + min1
|
||||
elseif mc_dimension == "nether" then
|
||||
return layer + mcl_vars.mg_nether_min
|
||||
return layer + min3
|
||||
elseif mc_dimension == "end" then
|
||||
return layer + mcl_vars.mg_end_min
|
||||
return layer + min2
|
||||
end
|
||||
end
|
||||
|
||||
-- Takes a position and returns true if this position can have weather
|
||||
function mcl_worlds.has_weather(pos)
|
||||
-- Weather in the Overworld and the high part of the void below
|
||||
return pos.y <= mcl_vars.mg_overworld_max and pos.y >= mcl_vars.mg_overworld_min - 64
|
||||
return pos.y <= max1 and pos.y >= min1 - 64
|
||||
end
|
||||
|
||||
-- Takes a position and returns true if this position can have Nether dust
|
||||
function mcl_worlds.has_dust(pos)
|
||||
-- Weather in the Overworld and the high part of the void below
|
||||
return pos.y <= mcl_vars.mg_nether_max + 138 and pos.y >= mcl_vars.mg_nether_min - 10
|
||||
return pos.y <= max3 + 138 and pos.y >= min3 - 10
|
||||
end
|
||||
|
||||
-- Takes a position (pos) and returns true if compasses are working here
|
||||
|
@ -89,7 +89,7 @@ function mcl_worlds.compass_works(pos)
|
|||
if dim == "nether" or dim == "end" then
|
||||
return false
|
||||
elseif dim == "void" then
|
||||
return pos.y <= mcl_vars.mg_overworld_max and pos.y >= mcl_vars.mg_overworld_min - 64
|
||||
return pos.y <= max1 and pos.y >= min1 - 64
|
||||
else
|
||||
return true
|
||||
end
|
||||
|
@ -152,23 +152,3 @@ minetest.register_globalstep(function(dtime)
|
|||
dimtimer = 0
|
||||
end
|
||||
end)
|
||||
|
||||
function mcl_worlds.get_cloud_parameters()
|
||||
if minetest.get_mapgen_setting("mg_name") == "valleys" then
|
||||
return {
|
||||
height = 384, --valleys has a much higher average elevation thus often "normal" landscape ends up in the clouds
|
||||
speed = {x=-2, z=0},
|
||||
thickness=5,
|
||||
color="#FFF0FEF",
|
||||
ambient = "#201060",
|
||||
}
|
||||
else
|
||||
-- MC-style clouds: Layer 127, thickness 4, fly to the “West”
|
||||
return {
|
||||
height = mcl_worlds.layer_to_y(127),
|
||||
speed = {x=-2, z=0},
|
||||
thickness = 4,
|
||||
color = "#FFF0FEF",
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
name = mcl_worlds
|
||||
author = Wuzzy
|
||||
description = Utility functions for worlds and the “dimensions”.
|
||||
depends = mcl_init
|
||||
|
||||
depends = mcl_mapgen
|
||||
|
|
|
@ -28,10 +28,11 @@ end)
|
|||
local timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
timer = timer + dtime
|
||||
if timer >= 0.6 then
|
||||
if timer >= 0.3 then
|
||||
for _, player in pairs(get_connected_players()) do
|
||||
local ppos = player:get_pos()
|
||||
local npos = vector.add(ppos, vector.new(0, -0.1, 0))
|
||||
ppos.y = ceil(ppos.y)
|
||||
local npos = vector.add(ppos, vector.new(0, -1, 0))
|
||||
if npos then
|
||||
local node = get_node(npos)
|
||||
if node then
|
||||
|
|
|
@ -13,21 +13,11 @@ local function is_group(pos, group)
|
|||
end
|
||||
|
||||
local is_water = flowlib.is_water
|
||||
local function is_river_water(p)
|
||||
local n = minetest.get_node(p).name
|
||||
if n == "mclx_core:river_water_source" or n == "mclx_core:river_water_flowing" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function is_ice(pos)
|
||||
return is_group(pos, "ice")
|
||||
end
|
||||
|
||||
local function is_fire(pos)
|
||||
return is_group(pos, "set_on_fire")
|
||||
end
|
||||
|
||||
local function get_sign(i)
|
||||
if i == 0 then
|
||||
return 0
|
||||
|
@ -120,11 +110,9 @@ end
|
|||
|
||||
local boat = {
|
||||
physical = true,
|
||||
pointable = true,
|
||||
-- Warning: Do not change the position of the collisionbox top surface,
|
||||
-- lowering it causes the boat to fall through the world if underwater
|
||||
collisionbox = {-0.5, -0.35, -0.5, 0.5, 0.3, 0.5},
|
||||
selectionbox = {-0.7, -0.35, -0.7, 0.7, 0.3, 0.7},
|
||||
visual = "mesh",
|
||||
mesh = "mcl_boats_boat.b3d",
|
||||
textures = {"mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png"},
|
||||
|
@ -206,8 +194,6 @@ end
|
|||
|
||||
function boat.on_step(self, dtime, moveresult)
|
||||
mcl_burning.tick(self.object, dtime, self)
|
||||
-- mcl_burning.tick may remove object immediately
|
||||
if not self.object:get_pos() then return end
|
||||
|
||||
self._v = get_v(self.object:get_velocity()) * get_sign(self._v)
|
||||
local v_factor = 1
|
||||
|
@ -216,21 +202,16 @@ function boat.on_step(self, dtime, moveresult)
|
|||
local on_water = true
|
||||
local on_ice = false
|
||||
local in_water = is_water({x=p.x, y=p.y-boat_y_offset+1, z=p.z})
|
||||
local in_river_water = is_river_water({x=p.x, y=p.y-boat_y_offset+1, z=p.z})
|
||||
local waterp = {x=p.x, y=p.y-boat_y_offset - 0.1, z=p.z}
|
||||
if not is_water(waterp) then
|
||||
on_water = false
|
||||
if not in_water and is_ice(waterp) then
|
||||
on_ice = true
|
||||
elseif is_fire({x=p.x, y=p.y-boat_y_offset, z=p.z}) then
|
||||
boat.on_death(self, nil)
|
||||
self.object:remove()
|
||||
return
|
||||
else
|
||||
v_slowdown = 0.04
|
||||
v_factor = 0.5
|
||||
end
|
||||
elseif in_water and not in_river_water then
|
||||
elseif in_water then
|
||||
on_water = false
|
||||
in_water = true
|
||||
v_factor = 0.75
|
||||
|
@ -333,7 +314,7 @@ function boat.on_step(self, dtime, moveresult)
|
|||
|
||||
for _, obj in pairs(minetest.get_objects_inside_radius(self.object:get_pos(), 1.3)) do
|
||||
local entity = obj:get_luaentity()
|
||||
if entity and entity.is_mob then
|
||||
if entity and entity._cmi_is_mob then
|
||||
attach_object(self, obj)
|
||||
break
|
||||
end
|
||||
|
@ -362,18 +343,7 @@ function boat.on_step(self, dtime, moveresult)
|
|||
else
|
||||
p.y = p.y + 1
|
||||
local is_obsidian_boat = self.object:get_luaentity()._itemstring == "mcl_boats:boat_obsidian"
|
||||
if is_river_water(p) then
|
||||
local y = self.object:get_velocity().y
|
||||
if y >= 5 then
|
||||
y = 5
|
||||
elseif y < 0 then
|
||||
new_acce = {x = 0, y = 10, z = 0}
|
||||
else
|
||||
new_acce = {x = 0, y = 2, z = 0}
|
||||
end
|
||||
new_velo = get_velocity(self._v, self.object:get_yaw(), y)
|
||||
self.object:set_pos(self.object:get_pos())
|
||||
elseif is_water(p) and not is_river_water(p) or is_obsidian_boat then
|
||||
if is_water(p) or is_obsidian_boat then
|
||||
-- Inside water: Slowly sink
|
||||
local y = self.object:get_velocity().y
|
||||
y = y - 0.01
|
||||
|
@ -413,21 +383,13 @@ end
|
|||
-- Register one entity for all boat types
|
||||
minetest.register_entity("mcl_boats:boat", boat)
|
||||
|
||||
local cboat = table.copy(boat)
|
||||
cboat.mesh = "mcl_boats_boat_with_chest.b3d"
|
||||
cboat.textures = {"mcl_boats_texture_oak_chest_boat.png", "mcl_boats_texture_oak_chest_boat.png", "mcl_boats_texture_oak_chest_boat.png", "mcl_boats_texture_oak_chest_boat.png", "mcl_boats_texture_oak_chest_boat.png"}
|
||||
cboat._itemstring = "mcl_boats:chest_boat"
|
||||
|
||||
minetest.register_entity("mcl_boats:chest_boat", cboat)
|
||||
mcl_entity_invs.register_inv("mcl_boats:chest_boat","Boat",27)
|
||||
|
||||
local boat_ids = { "boat", "boat_spruce", "boat_birch", "boat_jungle", "boat_acacia", "boat_dark_oak", "boat_obsidian", "boat_mangrove", "chest_boat", "chest_boat_spruce", "chest_boat_birch", "chest_boat_jungle", "chest_boat_acacia", "chest_boat_dark_oak", "chest_boat_mangrove" }
|
||||
local names = { S("Oak Boat"), S("Spruce Boat"), S("Birch Boat"), S("Jungle Boat"), S("Acacia Boat"), S("Dark Oak Boat"), S("Obsidian Boat"), S("Mangrove Boat"), S("Oak Chest Boat"), S("Spruce Chest Boat"), S("Birch Chest Boat"), S("Jungle Chest Boat"), S("Acacia Chest Boat"), S("Dark Oak Chest Boat"), S("Mangrove Chest Boat") }
|
||||
local boat_ids = { "boat", "boat_spruce", "boat_birch", "boat_jungle", "boat_acacia", "boat_dark_oak", "boat_obsidian" }
|
||||
local names = { S("Oak Boat"), S("Spruce Boat"), S("Birch Boat"), S("Jungle Boat"), S("Acacia Boat"), S("Dark Oak Boat"), S("Obsidian Boat") }
|
||||
local craftstuffs = {}
|
||||
if minetest.get_modpath("mcl_core") then
|
||||
craftstuffs = { "mcl_core:wood", "mcl_core:sprucewood", "mcl_core:birchwood", "mcl_core:junglewood", "mcl_core:acaciawood", "mcl_core:darkwood", "mcl_core:obsidian", "mcl_mangrove:mangrove_wood" }
|
||||
craftstuffs = { "mcl_core:wood", "mcl_core:sprucewood", "mcl_core:birchwood", "mcl_core:junglewood", "mcl_core:acaciawood", "mcl_core:darkwood", "mcl_core:obsidian" }
|
||||
end
|
||||
local images = { "oak", "spruce", "birch", "jungle", "acacia", "dark_oak", "obsidian", "mangrove", "oak_chest", "spruce_chest", "birch_chest", "jungle_chest", "acacia_chest", "dark_oak_chest", "mangrove_chest" }
|
||||
local images = { "oak", "spruce", "birch", "jungle", "acacia", "dark_oak", "obsidian" }
|
||||
|
||||
for b=1, #boat_ids do
|
||||
local itemstring = "mcl_boats:"..boat_ids[b]
|
||||
|
@ -477,11 +439,7 @@ for b=1, #boat_ids do
|
|||
else
|
||||
pos = vector.add(pos, vector.multiply(dir, boat_y_offset_ground))
|
||||
end
|
||||
local boat_ent = "mcl_boats:boat"
|
||||
if itemstring:find("chest") then
|
||||
boat_ent = "mcl_boats:chest_boat"
|
||||
end
|
||||
local boat = minetest.add_entity(pos, boat_ent)
|
||||
local boat = minetest.add_entity(pos, "mcl_boats:boat")
|
||||
local texture = "mcl_boats_texture_"..images[b].."_boat.png"
|
||||
boat:get_luaentity()._itemstring = itemstring
|
||||
boat:set_properties({textures = { texture, texture, texture, texture, texture }})
|
||||
|
@ -504,14 +462,6 @@ for b=1, #boat_ids do
|
|||
})
|
||||
|
||||
local c = craftstuffs[b]
|
||||
if not itemstring:find("chest") then
|
||||
minetest.register_craft({
|
||||
output = itemstring:gsub(":boat",":chest_boat"),
|
||||
recipe = {
|
||||
{"mcl_chests:chest"},
|
||||
{itemstring},
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = itemstring,
|
||||
recipe = {
|
||||
|
@ -519,7 +469,6 @@ for b=1, #boat_ids do
|
|||
{c, c, c},
|
||||
},
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
|
|
|
@ -9,5 +9,4 @@ Oak Boat=Bateau en Chêne
|
|||
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=Faites un clic droit sur une source d'eau pour placer le bateau. Faites un clic droit sur le bateau pour y entrer. Utilisez [Gauche] et [Droite] pour diriger, [Avant] pour accélérer et [Arrière] pour ralentir ou reculer. Utilisez [Sneak] pour le quitter, frappez le bateau pour le faire tomber en tant qu'objet.
|
||||
Spruce Boat=Bateau en Sapin
|
||||
Water vehicle=Véhicule aquatique
|
||||
Sneak to dismount=Se baisser pour descendre
|
||||
Obsidian Boat=Bateau en Obsidienne
|
||||
Sneak to dismount=
|
|
@ -10,4 +10,3 @@ Rightclick on a water source to place the boat. Rightclick the boat to enter it.
|
|||
Spruce Boat=
|
||||
Water vehicle=
|
||||
Sneak to dismount=
|
||||
Obsidian Boat=
|
|
@ -1,5 +1,7 @@
|
|||
name = mcl_boats
|
||||
author = PilzAdam
|
||||
description = Adds drivable boats.
|
||||
depends = mcl_player, flowlib, mcl_title, mcl_entity_invs
|
||||
depends = mcl_player, flowlib, mcl_title
|
||||
optional_depends = mcl_core, doc_identifier
|
||||
|
||||
|
||||
|
|
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
|
@ -1,5 +1,3 @@
|
|||
local enable_damage = minetest.settings:get_bool("enable_damage")
|
||||
|
||||
function mcl_burning.get_storage(obj)
|
||||
return obj:is_player() and mcl_burning.storage[obj] or obj:get_luaentity()
|
||||
end
|
||||
|
@ -79,7 +77,7 @@ end
|
|||
-- The effective burn duration is modified by obj's armor protection.
|
||||
-- If obj was already burning, its burn duration is updated if the current
|
||||
-- duration is less than burn_time.
|
||||
-- If obj is dead, fireproof or enable_damage is disabled, this function does nothing.
|
||||
-- If obj is dead, fireproof or a creative player, this function does nothing.
|
||||
--
|
||||
function mcl_burning.set_on_fire(obj, burn_time)
|
||||
if obj:get_hp() < 0 then
|
||||
|
@ -91,9 +89,8 @@ function mcl_burning.set_on_fire(obj, burn_time)
|
|||
return
|
||||
end
|
||||
|
||||
if obj:is_player() and not enable_damage then
|
||||
if obj:is_player() and minetest.is_creative_enabled(obj:get_player_name()) then
|
||||
burn_time = 0
|
||||
return
|
||||
else
|
||||
local max_fire_prot_lvl = 0
|
||||
local inv = mcl_util.get_inventory(obj)
|
||||
|
@ -134,10 +131,20 @@ function mcl_burning.set_on_fire(obj, burn_time)
|
|||
if obj:is_player() then
|
||||
mcl_burning.update_hud(obj)
|
||||
end
|
||||
|
||||
-- FIXME: does this code make sense? It removes attached fire luaentities from
|
||||
-- another object that happen to be at the same position.
|
||||
local fire_luaentity = fire_entity:get_luaentity()
|
||||
for _, other in pairs(minetest.get_objects_inside_radius(fire_entity:get_pos(), 0)) do
|
||||
local other_luaentity = other:get_luaentity()
|
||||
if other_luaentity and other_luaentity.name == "mcl_burning:fire" and other_luaentity ~= fire_luaentity then
|
||||
other:remove()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_burning.extinguish(obj)
|
||||
if not obj:get_pos() then return end
|
||||
if mcl_burning.is_burning(obj) then
|
||||
local storage = mcl_burning.get_storage(obj)
|
||||
if obj:is_player() then
|
||||
|
|
|
@ -57,7 +57,7 @@ minetest.register_on_joinplayer(function(player)
|
|||
local storage = {}
|
||||
local burn_data = player:get_meta():get_string("mcl_burning:data")
|
||||
if burn_data ~= "" then
|
||||
storage = minetest.deserialize(burn_data) or storage
|
||||
storage = minetest.deserialize(burn_data)
|
||||
end
|
||||
mcl_burning.storage[player] = storage
|
||||
if storage.burn_time and storage.burn_time > 0 then
|
||||
|
@ -67,13 +67,6 @@ end)
|
|||
|
||||
local function on_leaveplayer(player)
|
||||
local storage = mcl_burning.storage[player]
|
||||
if not storage then
|
||||
-- For some unexplained reasons, mcl_burning.storage can be `nil` here.
|
||||
-- Logging this exception to assist in finding the cause of this.
|
||||
minetest.log("warning", "on_leaveplayer: missing mcl_burning.storage "
|
||||
.. "for player " .. player:get_player_name())
|
||||
storage = {}
|
||||
end
|
||||
storage.fire_hud_id = nil
|
||||
player:get_meta():set_string("mcl_burning:data", minetest.serialize(storage))
|
||||
mcl_burning.storage[player] = nil
|
||||
|
@ -105,7 +98,8 @@ minetest.register_entity("mcl_burning:fire", {
|
|||
glow = -1,
|
||||
backface_culling = false,
|
||||
},
|
||||
_mcl_animation_timer = 0,
|
||||
animation_frame = 0,
|
||||
animation_timer = 0,
|
||||
on_activate = function(self)
|
||||
self.object:set_sprite({x = 0, y = 0}, animation_frames, 1.0 / animation_frames)
|
||||
end,
|
||||
|
@ -121,9 +115,9 @@ minetest.register_entity("mcl_burning:fire", {
|
|||
return
|
||||
end
|
||||
if parent:is_player() then
|
||||
self._mcl_animation_timer = self._mcl_animation_timer + dtime
|
||||
if self._mcl_animation_timer >= 0.1 then
|
||||
self._mcl_animation_timer = 0
|
||||
self.animation_timer = self.animation_timer + dtime
|
||||
if self.animation_timer >= 0.1 then
|
||||
self.animation_timer = 0
|
||||
mcl_burning.update_hud(parent)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
name = mcl_burning
|
||||
description = Burning Objects for MineClone2
|
||||
author = Fleckenstein
|
||||
depends = mcl_weather
|
|
@ -1,55 +1,66 @@
|
|||
-- Dripping Water Mod
|
||||
-- by kddekadenz
|
||||
-- License of code, textures & sounds: CC0
|
||||
|
||||
local math = math
|
||||
local function make_drop(pos,liquid,sound,interval)
|
||||
local pt = {
|
||||
velocity = vector.new(0,0,0),
|
||||
collision_removal = false,
|
||||
}
|
||||
local t = math.random() + math.random(1, interval)
|
||||
minetest.after(t,function()
|
||||
local x, z = math.random(-45, 45) / 100, math.random(-45, 45) / 100
|
||||
pt.pos = vector.offset(pos,x,-0.52,z)
|
||||
pt.acceleration = vector.new(0,0,0)
|
||||
pt.collisiondetection = false
|
||||
pt.expirationtime = t
|
||||
|
||||
pt.texture="[combine:2x2:" .. -math.random(1, 16) .. "," .. -math.random(1, 16) .. "=default_" .. liquid .. "_source_animated.png"
|
||||
minetest.add_particle(pt)
|
||||
minetest.after(t,function()
|
||||
pt.acceleration = vector.new(0,-5,0)
|
||||
pt.collisiondetection = true
|
||||
pt.expirationtime = math.random() + math.random(1, interval/2)
|
||||
minetest.add_particle(pt)
|
||||
minetest.sound_play({name = "drippingwater_" .. sound .. "drip"}, {pos = pos, gain = 0.5, max_hear_distance = 8}, true)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
-- License of code, textures & sounds: CC0
|
||||
|
||||
local function register_drop(liquid, glow, sound, nodes, interval, chance)
|
||||
local function register_drop(liquid, glow, sound, nodes)
|
||||
minetest.register_entity("mcl_dripping:drop_" .. liquid, {
|
||||
hp_max = 1,
|
||||
physical = true,
|
||||
collide_with_objects = false,
|
||||
collisionbox = {-0.01, 0.01, -0.01, 0.01, 0.01, 0.01},
|
||||
glow = glow,
|
||||
pointable = false,
|
||||
visual = "sprite",
|
||||
visual_size = {x = 0.1, y = 0.1},
|
||||
textures = {""},
|
||||
spritediv = {x = 1, y = 1},
|
||||
initial_sprite_basepos = {x = 0, y = 0},
|
||||
static_save = false,
|
||||
_dropped = false,
|
||||
on_activate = function(self)
|
||||
self.object:set_properties({
|
||||
textures = {"[combine:2x2:" .. -math.random(1, 16) .. "," .. -math.random(1, 16) .. "=default_" .. liquid .. "_source_animated.png"}
|
||||
})
|
||||
end,
|
||||
on_step = function(self, dtime)
|
||||
local k = math.random(1, 222)
|
||||
local ownpos = self.object:get_pos()
|
||||
if k == 1 then
|
||||
self.object:set_acceleration(vector.new(0, -5, 0))
|
||||
end
|
||||
if minetest.get_node(vector.offset(ownpos, 0, 0.5, 0)).name == "air" then
|
||||
self.object:set_acceleration(vector.new(0, -5, 0))
|
||||
end
|
||||
if minetest.get_node(vector.offset(ownpos, 0, -0.1, 0)).name ~= "air" then
|
||||
local ent = self.object:get_luaentity()
|
||||
if not ent._dropped then
|
||||
ent._dropped = true
|
||||
minetest.sound_play({name = "drippingwater_" .. sound .. "drip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
|
||||
end
|
||||
if k < 3 then
|
||||
self.object:remove()
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
minetest.register_abm({
|
||||
label = "Create drops",
|
||||
nodenames = nodes,
|
||||
neighbors = {"group:" .. liquid},
|
||||
interval = interval,
|
||||
chance = chance,
|
||||
interval = 2,
|
||||
chance = 22,
|
||||
action = function(pos)
|
||||
local r = math.ceil(interval / 20)
|
||||
local nn=minetest.find_nodes_in_area(vector.offset(pos,-r,0,-r),vector.offset(pos,r,0,r),nodes)
|
||||
--start a bunch of particle cycles to be able to get away
|
||||
--with longer abm cycles
|
||||
table.shuffle(nn)
|
||||
for i=1,math.random(#nn) do
|
||||
if minetest.get_item_group(minetest.get_node(vector.offset(nn[i], 0, 1, 0)).name, liquid) ~= 0
|
||||
and minetest.get_node(vector.offset(nn[i], 0, -1, 0)).name == "air" then
|
||||
make_drop(nn[i],liquid,sound,interval)
|
||||
end
|
||||
if minetest.get_item_group(minetest.get_node(vector.offset(pos, 0, 1, 0)).name, liquid) ~= 0
|
||||
and minetest.get_node(vector.offset(pos, 0, -1, 0)).name == "air" then
|
||||
local x, z = math.random(-45, 45) / 100, math.random(-45, 45) / 100
|
||||
minetest.add_entity(vector.offset(pos, x, -0.520, z), "mcl_dripping:drop_" .. liquid)
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
register_drop("water", 1, "", {"group:opaque", "group:leaves"},60,10)
|
||||
register_drop("lava", math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3), "lava", {"group:opaque"},60,10)
|
||||
register_drop("water", 1, "", {"group:opaque", "group:leaves"})
|
||||
register_drop("lava", math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3), "lava", {"group:opaque"})
|
|
@ -1,13 +0,0 @@
|
|||
mcl_entity_invs
|
||||
===============
|
||||
|
||||
Inventories for your entities. It's simple. Depend on mcl_entity_invs and register your entity like so:
|
||||
|
||||
* mcl_entity_invs.register_inv("entity:name","Title shown in formspec",inventory_size,disable_on_righclick)
|
||||
*If disable_on_righclick is true other mods can handle when to show the inventory themselves
|
||||
* The inventory size can be set dynamically by initializing it with an explicit nil
|
||||
|
||||
* mcl_entity_invs.show_inv_form(entity,clicker,[formspec text])
|
||||
* formspec_text is an additional text that is put after the title
|
||||
|
||||
It works by setting up a detached inventory per entity which is accessed by an id/hash generated from the entities position at creation, the progressed gametime at creation and a random salt.
|
|
@ -1,170 +0,0 @@
|
|||
mcl_entity_invs = {}
|
||||
|
||||
local open_invs = {}
|
||||
|
||||
local function check_distance(inv,player,count)
|
||||
for _,o in pairs(minetest.get_objects_inside_radius(player:get_pos(),5)) do
|
||||
local l = o:get_luaentity()
|
||||
if l and l._inv_id and inv:get_location().name == l._inv_id then return count end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local inv_callbacks = {
|
||||
allow_take = function(inv, listname, index, stack, player)
|
||||
return check_distance(inv,player,stack:get_count())
|
||||
end,
|
||||
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||||
return check_distance(inv,player,count)
|
||||
end,
|
||||
allow_put = function(inv, listname, index, stack, player)
|
||||
return check_distance(inv,player,stack:get_count())
|
||||
end,
|
||||
}
|
||||
|
||||
local function load_inv(ent,size)
|
||||
if not ent._inv_id then return end
|
||||
local inv = minetest.get_inventory({type="detached", name=ent._inv_id})
|
||||
if not inv then
|
||||
inv = minetest.create_detached_inventory(ent._inv_id, inv_callbacks)
|
||||
inv:set_size("main", size)
|
||||
if ent._items then
|
||||
inv:set_list("main",ent._items)
|
||||
end
|
||||
end
|
||||
return inv
|
||||
end
|
||||
|
||||
local function save_inv(ent)
|
||||
if ent._inv then
|
||||
ent._items = {}
|
||||
for i,it in ipairs(ent._inv:get_list("main")) do
|
||||
ent._items[i] = it:to_string()
|
||||
end
|
||||
minetest.remove_detached_inventory(ent._inv_id)
|
||||
ent._inv = nil
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_entity_invs.show_inv_form(ent,player,text)
|
||||
if not ent._inv_id then return end
|
||||
if not open_invs[ent] then
|
||||
open_invs[ent] = 0
|
||||
end
|
||||
text = text or ""
|
||||
ent._inv = load_inv(ent,ent._inv_size)
|
||||
open_invs[ent] = open_invs[ent] + 1
|
||||
local playername = player:get_player_name()
|
||||
local rows = 3
|
||||
local cols = (math.ceil(ent._inv_size/rows))
|
||||
local spacing = (9 - cols) / 2
|
||||
local formspec = "size[9,8.75]"
|
||||
.. "label[0,0;" .. minetest.formspec_escape(
|
||||
minetest.colorize("#313131", ent._inv_title .. " ".. text)) .. "]"
|
||||
.. "list[detached:"..ent._inv_id..";main;"..spacing..",0.5;"..cols..","..rows..";]"
|
||||
.. mcl_formspec.get_itemslot_bg(spacing,0.5,cols,rows)
|
||||
.. "label[0,4.0;" .. minetest.formspec_escape(
|
||||
minetest.colorize("#313131", "Inventory")) .. "]"
|
||||
.. "list[current_player;main;0,4.5;9,3;9]"
|
||||
.. mcl_formspec.get_itemslot_bg(0,4.5,9,3)
|
||||
.. "list[current_player;main;0,7.74;9,1;]"
|
||||
.. mcl_formspec.get_itemslot_bg(0,7.74,9,1)
|
||||
.. "listring[detached:"..ent._inv_id..";main]"
|
||||
.. "listring[current_player;main]"
|
||||
minetest.show_formspec(playername,ent._inv_id,formspec)
|
||||
end
|
||||
|
||||
local function drop_inv(ent)
|
||||
if not ent._items then return end
|
||||
local pos = ent.object:get_pos()
|
||||
for i,it in pairs(ent._items) do
|
||||
local p = vector.add(pos,vector.new(math.random() - 0.5, math.random()-0.5, math.random()-0.5))
|
||||
minetest.add_item(p,it)
|
||||
end
|
||||
ent._items = nil
|
||||
end
|
||||
|
||||
local function on_remove(self,killer,oldf)
|
||||
save_inv(self)
|
||||
drop_inv(self)
|
||||
if oldf then return oldf(self,killer) end
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
for k,v in pairs(open_invs) do
|
||||
if formname == k._inv_id then
|
||||
open_invs[k] = open_invs[k] - 1
|
||||
if open_invs[k] < 1 then
|
||||
save_inv(k)
|
||||
open_invs[k] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function mcl_entity_invs.register_inv(entity_name,show_name,size,no_on_righclick)
|
||||
assert(minetest.registered_entities[entity_name],"mcl_entity_invs.register_inv called with invalid entity: "..tostring(entity_name))
|
||||
minetest.registered_entities[entity_name]._inv_size = size
|
||||
minetest.registered_entities[entity_name]._inv_title = show_name
|
||||
|
||||
local old_oa = minetest.registered_entities[entity_name].on_activate
|
||||
minetest.registered_entities[entity_name].on_activate = function(self,staticdata,dtime_s)
|
||||
local r
|
||||
if old_oa then r=old_oa(self,staticdata,dtime_s) end
|
||||
local d = minetest.deserialize(staticdata)
|
||||
if type(d) == "table" and d._inv_id then
|
||||
self._inv_id = d._inv_id
|
||||
self._items = d._items
|
||||
self._inv_size = d._inv_size
|
||||
else
|
||||
self._inv_id="entity_inv_"..minetest.sha1(minetest.get_gametime()..minetest.pos_to_string(self.object:get_pos())..tostring(math.random()))
|
||||
--gametime and position for collision safety and math.random salt to protect against position brute-force
|
||||
end
|
||||
return r
|
||||
end
|
||||
if not no_on_righclick then
|
||||
local old_rc = minetest.registered_entities[entity_name].on_rightclick
|
||||
minetest.registered_entities[entity_name].on_rightclick = function(self,clicker)
|
||||
mcl_entity_invs.show_inv_form(self,clicker,show_name)
|
||||
if old_rc then return old_rc(self,clicker) end
|
||||
end
|
||||
end
|
||||
|
||||
local old_gsd = minetest.registered_entities[entity_name].get_staticdata
|
||||
minetest.registered_entities[entity_name].get_staticdata = function(self)
|
||||
local old_sd = old_gsd(self)
|
||||
local d = minetest.deserialize(old_sd)
|
||||
assert(type(d) == "table","mcl_entity_invs currently only works with entities that return a (serialized) table in get_staticdata. "..tostring(self.name).." returned: "..tostring(old_sd))
|
||||
d._inv_id = self._inv_id
|
||||
d._inv_size = self._inv_size
|
||||
d._items = {}
|
||||
if self._items then
|
||||
for i,it in ipairs(self._items) do
|
||||
d._items[i] = it
|
||||
end
|
||||
end
|
||||
return minetest.serialize(d)
|
||||
end
|
||||
|
||||
local old_ode = minetest.registered_entities[entity_name].on_deactivate
|
||||
minetest.registered_entities[entity_name].on_deactivate = function(self,removal)
|
||||
save_inv(self)
|
||||
if removal then
|
||||
on_remove(self)
|
||||
end
|
||||
if old_ode then return old_ode(self,removal) end
|
||||
end
|
||||
|
||||
local old_od = minetest.registered_entities[entity_name].on_death
|
||||
minetest.registered_entities[entity_name].on_death = function(self,killer)
|
||||
if not self.is_mob then
|
||||
on_remove(self,killer,old_od)
|
||||
end
|
||||
end
|
||||
local old_odi = minetest.registered_entities[entity_name].on_die
|
||||
minetest.registered_entities[entity_name].on_die = function(self,killer)
|
||||
if self.is_mob then
|
||||
on_remove(self,killer,old_od)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,3 +0,0 @@
|
|||
name = mcl_entity_invs
|
||||
author = cora
|
||||
depends = mcl_formspec
|
|
@ -59,10 +59,6 @@ mcl_item_entity.register_pickup_achievement("tree", "mcl:mineWood")
|
|||
mcl_item_entity.register_pickup_achievement("mcl_mobitems:blaze_rod", "mcl:blazeRod")
|
||||
mcl_item_entity.register_pickup_achievement("mcl_mobitems:leather", "mcl:killCow")
|
||||
mcl_item_entity.register_pickup_achievement("mcl_core:diamond", "mcl:diamonds")
|
||||
mcl_item_entity.register_pickup_achievement("mcl_core:crying_obsidian", "mcl:whosCuttingOnions")
|
||||
mcl_item_entity.register_pickup_achievement("mcl_nether:ancient_debris", "mcl:hiddenInTheDepths")
|
||||
mcl_item_entity.register_pickup_achievement("mcl_end:dragon_egg", "mcl:PickUpDragonEgg")
|
||||
mcl_item_entity.register_pickup_achievement("mcl_armor:elytra", "mcl:skysTheLimit")
|
||||
|
||||
local function check_pickup_achievements(object, player)
|
||||
if has_awards then
|
||||
|
@ -82,6 +78,7 @@ local function enable_physics(object, luaentity, ignore_check)
|
|||
object:set_properties({
|
||||
physical = true
|
||||
})
|
||||
object:set_velocity({x=0,y=0,z=0})
|
||||
object:set_acceleration({x=0,y=-get_gravity(),z=0})
|
||||
end
|
||||
end
|
||||
|
@ -320,14 +317,18 @@ function minetest.handle_node_drops(pos, drops, digger)
|
|||
-- Spawn item and apply random speed
|
||||
local obj = minetest.add_item(dpos, drop_item)
|
||||
if obj then
|
||||
-- set the velocity multiplier to the stored amount or if the game dug this node, apply a bigger velocity
|
||||
local v = 1
|
||||
if digger and digger:is_player() then
|
||||
obj:get_luaentity().random_velocity = 1
|
||||
else
|
||||
obj:get_luaentity().random_velocity = 1.6
|
||||
local x = math.random(1, 5)
|
||||
if math.random(1,2) == 1 then
|
||||
x = -x
|
||||
end
|
||||
local z = math.random(1, 5)
|
||||
if math.random(1,2) == 1 then
|
||||
z = -z
|
||||
end
|
||||
obj:set_velocity({x=1/x, y=obj:get_velocity().y, z=1/z})
|
||||
|
||||
obj:get_luaentity().age = item_drop_settings.dug_buffer
|
||||
|
||||
obj:get_luaentity()._insta_collect = false
|
||||
end
|
||||
end
|
||||
|
@ -406,33 +407,9 @@ minetest.register_entity(":__builtin:item", {
|
|||
-- Number of seconds this item entity has existed so far
|
||||
age = 0,
|
||||
|
||||
-- Multiplier for initial random velocity when the item is spawned
|
||||
random_velocity = 1,
|
||||
|
||||
-- How old it has become in the collection animation
|
||||
collection_age = 0,
|
||||
|
||||
-- Function to apply a random velocity
|
||||
apply_random_vel = function(self, speed)
|
||||
if not self or not self.object or not self.object:get_luaentity() then
|
||||
return
|
||||
end
|
||||
-- if you passed a value then use that for the velocity multiplier
|
||||
if speed ~= nil then self.random_velocity = speed end
|
||||
|
||||
local vel = self.object:get_velocity()
|
||||
if vel and vel.x == 0 and vel.z == 0 and self.random_velocity > 0 then
|
||||
local v = self.random_velocity
|
||||
local x = math.random(5, 10) / 10 * v
|
||||
if math.random(0,10) < 5 then x = -x end
|
||||
local z = math.random(5, 10) / 10 * v
|
||||
if math.random(0,10) < 5 then z = -z end
|
||||
local y = math.random(2,4)
|
||||
self.object:set_velocity({x=x, y=y, z=z})
|
||||
end
|
||||
self.random_velocity = 0
|
||||
end,
|
||||
|
||||
set_item = function(self, itemstring)
|
||||
self.itemstring = itemstring
|
||||
if self.itemstring == "" then
|
||||
|
@ -441,11 +418,7 @@ minetest.register_entity(":__builtin:item", {
|
|||
end
|
||||
local stack = ItemStack(itemstring)
|
||||
if minetest.get_item_group(stack:get_name(), "compass") > 0 then
|
||||
if string.find(stack:get_name(), "_lodestone") then
|
||||
stack:set_name("mcl_compass:18_lodestone")
|
||||
else
|
||||
stack:set_name("mcl_compass:18")
|
||||
end
|
||||
stack:set_name("mcl_compass:16")
|
||||
itemstring = stack:to_string()
|
||||
self.itemstring = itemstring
|
||||
end
|
||||
|
@ -485,9 +458,27 @@ minetest.register_entity(":__builtin:item", {
|
|||
glow = glow,
|
||||
}
|
||||
self.object:set_properties(prop)
|
||||
if item_drop_settings.random_item_velocity == true and self.age < 1 then
|
||||
minetest.after(0, self.apply_random_vel, self)
|
||||
if item_drop_settings.random_item_velocity == true then
|
||||
minetest.after(0, function(self)
|
||||
if not self or not self.object or not self.object:get_luaentity() then
|
||||
return
|
||||
end
|
||||
local vel = self.object:get_velocity()
|
||||
if vel and vel.x == 0 and vel.z == 0 then
|
||||
local x = math.random(1, 5)
|
||||
if math.random(1,2) == 1 then
|
||||
x = -x
|
||||
end
|
||||
local z = math.random(1, 5)
|
||||
if math.random(1,2) == 1 then
|
||||
z = -z
|
||||
end
|
||||
local y = math.random(2,4)
|
||||
self.object:set_velocity({x=1/x, y=y, z=1/z})
|
||||
end
|
||||
end, self)
|
||||
end
|
||||
|
||||
end,
|
||||
|
||||
get_staticdata = function(self)
|
||||
|
@ -541,9 +532,9 @@ minetest.register_entity(":__builtin:item", {
|
|||
self.itemstring = data.itemstring
|
||||
self.always_collect = data.always_collect
|
||||
if data.age then
|
||||
self.age = data.age
|
||||
self.age = data.age + dtime_s
|
||||
else
|
||||
self.age = self.age
|
||||
self.age = dtime_s
|
||||
end
|
||||
--remember collection data
|
||||
-- If true, can collect item without delay
|
||||
|
@ -576,7 +567,7 @@ minetest.register_entity(":__builtin:item", {
|
|||
self._forcetimer = 0
|
||||
|
||||
self.object:set_armor_groups({immortal = 1})
|
||||
-- self.object:set_velocity({x = 0, y = 2, z = 0})
|
||||
self.object:set_velocity({x = 0, y = 2, z = 0})
|
||||
self.object:set_acceleration({x = 0, y = -get_gravity(), z = 0})
|
||||
self:set_item(self.itemstring)
|
||||
end,
|
||||
|
@ -606,13 +597,12 @@ minetest.register_entity(":__builtin:item", {
|
|||
end
|
||||
-- Merge the remote stack into this one
|
||||
|
||||
-- local pos = object:get_pos()
|
||||
-- pos.y = pos.y + ((total_count - count) / max_count) * 0.15
|
||||
-- self.object:move_to(pos)
|
||||
local pos = object:get_pos()
|
||||
pos.y = pos.y + ((total_count - count) / max_count) * 0.15
|
||||
self.object:move_to(pos)
|
||||
|
||||
self.age = 0 -- Handle as new entity
|
||||
own_stack:set_count(total_count)
|
||||
self.random_velocity = 0
|
||||
self:set_item(own_stack:to_string())
|
||||
|
||||
entity._removed = true
|
||||
|
@ -651,53 +641,33 @@ minetest.register_entity(":__builtin:item", {
|
|||
local node = minetest.get_node_or_nil(p)
|
||||
local in_unloaded = (node == nil)
|
||||
|
||||
if in_unloaded then
|
||||
-- Don't infinetly fall into unloaded map
|
||||
disable_physics(self.object, self)
|
||||
return
|
||||
end
|
||||
|
||||
if self.is_clock then
|
||||
self.object:set_properties({
|
||||
textures = {"mcl_clock:clock_" .. (mcl_worlds.clock_works(p) and mcl_clock.old_time or mcl_clock.random_frame)}
|
||||
})
|
||||
end
|
||||
|
||||
local nn = node.name
|
||||
local is_in_water = (minetest.get_item_group(nn, "liquid") ~= 0)
|
||||
local nn_above = minetest.get_node({x=p.x, y=p.y+0.1, z=p.z}).name
|
||||
-- make sure it's more or less stationary and is at water level
|
||||
local sleep_threshold = 0.3
|
||||
local is_floating = false
|
||||
local is_stationary = math.abs(self.object:get_velocity().x) < sleep_threshold
|
||||
and math.abs(self.object:get_velocity().y) < sleep_threshold
|
||||
and math.abs(self.object:get_velocity().z) < sleep_threshold
|
||||
if is_in_water and is_stationary then
|
||||
is_floating = (is_in_water
|
||||
and (minetest.get_item_group(nn_above, "liquid") == 0))
|
||||
end
|
||||
|
||||
if is_floating and self.physical_state == true then
|
||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||
disable_physics(self.object, self)
|
||||
end
|
||||
-- If no collector was found for a long enough time, declare the magnet as disabled
|
||||
if self._magnet_active and (self._collector_timer == nil or (self._collector_timer > item_drop_settings.magnet_time)) then
|
||||
self._magnet_active = false
|
||||
enable_physics(self.object, self)
|
||||
return
|
||||
end
|
||||
if in_unloaded then
|
||||
-- Don't infinetly fall into unloaded map
|
||||
disable_physics(self.object, self)
|
||||
return
|
||||
end
|
||||
|
||||
-- Destroy item in lava, fire or special nodes
|
||||
|
||||
local nn = node.name
|
||||
local def = minetest.registered_nodes[nn]
|
||||
local lg = minetest.get_item_group(nn, "lava")
|
||||
local fg = minetest.get_item_group(nn, "fire")
|
||||
local dg = minetest.get_item_group(nn, "destroys_items")
|
||||
if (def and (lg ~= 0 or fg ~= 0 or dg == 1)) then
|
||||
--Wait 2 seconds to allow mob drops to be cooked, & picked up instead of instantly destroyed.
|
||||
if self.age > 2 and minetest.get_item_group(self.itemstring, "fire_immune") == 0 then
|
||||
if self.age > 2 then
|
||||
if dg ~= 2 then
|
||||
minetest.sound_play("builtin_item_lava", {pos = self.object:get_pos(), gain = 0.5})
|
||||
end
|
||||
|
@ -720,7 +690,7 @@ minetest.register_entity(":__builtin:item", {
|
|||
end
|
||||
|
||||
-- Push item out when stuck inside solid opaque node
|
||||
if not is_in_water and def and def.walkable and def.groups and def.groups.opaque == 1 then
|
||||
if def and def.walkable and def.groups and def.groups.opaque == 1 then
|
||||
local shootdir
|
||||
local cx = (p.x % 1) - 0.5
|
||||
local cz = (p.z % 1) - 0.5
|
||||
|
@ -761,8 +731,8 @@ minetest.register_entity(":__builtin:item", {
|
|||
local newv = vector.multiply(shootdir, 3)
|
||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||
self.object:set_velocity(newv)
|
||||
disable_physics(self.object, self, false, false)
|
||||
|
||||
disable_physics(self.object, self, false, false)
|
||||
|
||||
if shootdir.y == 0 then
|
||||
self._force = newv
|
||||
|
@ -800,9 +770,8 @@ minetest.register_entity(":__builtin:item", {
|
|||
return
|
||||
end
|
||||
|
||||
-- Move item around on flowing liquids; add 'source' check to allow items to continue flowing a bit in the source block of flowing water.
|
||||
if def and not is_floating and (def.liquidtype == "flowing" or def.liquidtype == "source") then
|
||||
self._flowing = true
|
||||
-- Move item around on flowing liquids
|
||||
if def and def.liquidtype == "flowing" then
|
||||
|
||||
--[[ Get flowing direction (function call from flowlib), if there's a liquid.
|
||||
NOTE: According to Qwertymine, flowlib.quickflow is only reliable for liquids with a flowing distance of 7.
|
||||
|
@ -811,11 +780,11 @@ minetest.register_entity(":__builtin:item", {
|
|||
-- Just to make sure we don't manipulate the speed for no reason
|
||||
if vec.x ~= 0 or vec.y ~= 0 or vec.z ~= 0 then
|
||||
-- Minecraft Wiki: Flowing speed is "about 1.39 meters per second"
|
||||
local f = 1.2
|
||||
local f = 1.39
|
||||
-- Set new item moving speed into the direciton of the liquid
|
||||
local newv = vector.multiply(vec, f)
|
||||
-- Swap to acceleration instead of a static speed to better mimic MC mechanics.
|
||||
self.object:set_acceleration({x = newv.x, y = -0.22, z = newv.z})
|
||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||
self.object:set_velocity({x = newv.x, y = -0.22, z = newv.z})
|
||||
|
||||
self.physical_state = true
|
||||
self._flowing = true
|
||||
|
@ -824,29 +793,7 @@ minetest.register_entity(":__builtin:item", {
|
|||
})
|
||||
return
|
||||
end
|
||||
if is_in_water and def.liquidtype == "source" then
|
||||
local cur_vec = self.object:get_velocity()
|
||||
-- apply some acceleration in the opposite direction so it doesn't slide forever
|
||||
local vec = {
|
||||
x = 0 -cur_vec.x*0.9,
|
||||
y = 3 -cur_vec.y*0.9,
|
||||
z = 0 -cur_vec.z*0.9}
|
||||
self.object:set_acceleration(vec)
|
||||
-- slow down the item in water
|
||||
local vel = self.object:get_velocity()
|
||||
if vel.y < 0 then
|
||||
vel.y = vel.y * 0.9
|
||||
end
|
||||
self.object:set_velocity(vel)
|
||||
if self.physical_state ~= false or self._flowing ~= true then
|
||||
self.physical_state = true
|
||||
self._flowing = true
|
||||
self.object:set_properties({
|
||||
physical = true
|
||||
})
|
||||
end
|
||||
end
|
||||
elseif self._flowing == true and not is_in_water and not is_floating then
|
||||
elseif self._flowing == true then
|
||||
-- Disable flowing physics if not on/in flowing liquid
|
||||
self._flowing = false
|
||||
enable_physics(self.object, self, true)
|
||||
|
@ -855,13 +802,10 @@ minetest.register_entity(":__builtin:item", {
|
|||
|
||||
-- If node is not registered or node is walkably solid and resting on nodebox
|
||||
local nn = minetest.get_node({x=p.x, y=p.y-0.5, z=p.z}).name
|
||||
local def = minetest.registered_nodes[nn]
|
||||
local v = self.object:get_velocity()
|
||||
local is_on_floor = def and (def.walkable
|
||||
and not def.groups.slippery and v.y == 0)
|
||||
|
||||
if not minetest.registered_nodes[nn]
|
||||
or is_floating or is_on_floor then
|
||||
if not minetest.registered_nodes[nn] or minetest.registered_nodes[nn].walkable and v.y == 0 then
|
||||
if self.physical_state then
|
||||
local own_stack = ItemStack(self.object:get_luaentity().itemstring)
|
||||
-- Merge with close entities of the same item
|
||||
for _, object in pairs(minetest.get_objects_inside_radius(p, 0.8)) do
|
||||
|
@ -872,17 +816,14 @@ minetest.register_entity(":__builtin:item", {
|
|||
return
|
||||
end
|
||||
end
|
||||
-- don't disable if underwater
|
||||
if not is_in_water then
|
||||
end
|
||||
disable_physics(self.object, self)
|
||||
end
|
||||
end
|
||||
else
|
||||
if self._magnet_active == false and not is_floating then
|
||||
if self._magnet_active == false then
|
||||
enable_physics(self.object, self)
|
||||
end
|
||||
end
|
||||
|
||||
end,
|
||||
|
||||
-- Note: on_punch intentionally left out. The player should *not* be able to collect items by punching
|
||||
|
|
|
@ -677,8 +677,7 @@ register_minecart(
|
|||
{ "mcl_chests_normal.png", "mcl_minecarts_minecart.png" },
|
||||
"mcl_minecarts_minecart_chest.png",
|
||||
{"mcl_minecarts:minecart", "mcl_chests:chest"},
|
||||
nil, nil, true)
|
||||
mcl_entity_invs.register_inv("mcl_minecarts:chest_minecart","Minecart",27)
|
||||
nil, nil, false)
|
||||
|
||||
-- Minecart with Furnace
|
||||
register_minecart(
|
||||
|
@ -731,7 +730,7 @@ register_minecart(
|
|||
"mcl_minecarts_minecart.png",
|
||||
}})
|
||||
end
|
||||
end, nil, true
|
||||
end, nil, false
|
||||
)
|
||||
|
||||
-- Minecart with Command Block
|
||||
|
@ -831,7 +830,8 @@ minetest.register_craft({
|
|||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
-- TODO: Re-enable crafting of special minecarts when they have been implemented
|
||||
--[[minetest.register_craft({
|
||||
output = "mcl_minecarts:furnace_minecart",
|
||||
recipe = {
|
||||
{"mcl_furnaces:furnace"},
|
||||
|
@ -839,16 +839,13 @@ minetest.register_craft({
|
|||
},
|
||||
})
|
||||
|
||||
-- TODO: Re-enable crafting of special minecarts when they have been implemented
|
||||
|
||||
--[[minetest.register_craft({
|
||||
minetest.register_craft({
|
||||
output = "mcl_minecarts:hopper_minecart",
|
||||
recipe = {
|
||||
{"mcl_hoppers:hopper"},
|
||||
{"mcl_minecarts:minecart"},
|
||||
},
|
||||
})
|
||||
--]]
|
||||
|
||||
minetest.register_craft({
|
||||
output = "mcl_minecarts:chest_minecart",
|
||||
|
@ -856,7 +853,7 @@ minetest.register_craft({
|
|||
{"mcl_chests:chest"},
|
||||
{"mcl_minecarts:minecart"},
|
||||
},
|
||||
})
|
||||
})]]
|
||||
|
||||
|
||||
if has_mcl_wip then
|
||||
|
|
|
@ -33,4 +33,4 @@ Activates minecarts when powered=Active les wagonnets lorsqu'il est alimenté
|
|||
Emits redstone power when a minecart is detected=Émet de l'énergie redstone lorsqu'un wagonnet est détecté
|
||||
Vehicle for fast travel on rails=Véhicule pour voyager rapidement sur rails
|
||||
Can be ignited by tools or powered activator rail=Peut être allumé par des outils ou un rail d'activation motorisé
|
||||
Sneak to dismount=Se baisser pour descendre
|
||||
Sneak to dismount=
|
|
@ -1,5 +1,6 @@
|
|||
name = mcl_minecarts
|
||||
author = Krock
|
||||
description = Minecarts are vehicles to move players quickly on rails.
|
||||
depends = mcl_title, mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons, mcl_entity_invs
|
||||
depends = mcl_title, mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons
|
||||
optional_depends = doc_identifier, mcl_wip
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ local function register_rail(itemstring, tiles, def_extras, creative)
|
|||
stack_max = 64,
|
||||
groups = groups,
|
||||
sounds = mcl_sounds.node_sound_metal_defaults(),
|
||||
_mcl_blast_resistance = 0.7,
|
||||
_mcl_blast_resistance = 3.5,
|
||||
_mcl_hardness = 0.7,
|
||||
after_destruct = function(pos)
|
||||
-- Scan for minecarts in this pos and force them to execute their "floating" check.
|
||||
|
|
|
@ -44,7 +44,7 @@ functions needed for the mob to work properly which contains the following:
|
|||
'passive' when true allows animals to defend themselves when hit,
|
||||
otherwise they amble onwards.
|
||||
'walk_velocity' is the speed that your mob can walk around.
|
||||
'run_velocity'is the speed your mob can run with, usually when attacking.
|
||||
'run_velocity' is the speed your mob can run with, usually when attacking.
|
||||
'walk_chance' has a 0-100 chance value your mob will walk from standing,
|
||||
set to 0 for jumping mobs only.
|
||||
'jump' when true allows your mob to jump updwards.
|
||||
|
@ -66,13 +66,13 @@ functions needed for the mob to work properly which contains the following:
|
|||
walking, 0 to turn off height fear.
|
||||
'fall_speed' has the maximum speed the mob can fall at, default is -10.
|
||||
'fall_damage' when true causes falling to inflict damage.
|
||||
'water_damage'holds the damage per second infliced to mobs when standing in
|
||||
'water_damage' holds the damage per second infliced to mobs when standing in
|
||||
water (default: 0).
|
||||
'lava_damage' holds the damage per second inflicted to mobs when standing
|
||||
in lava (default: 8).
|
||||
'fire_damage' holds the damage per second inflicted to mobs when standing
|
||||
in fire (default: 1).
|
||||
'light_damage'holds the damage per second inflicted to mobs when it's too
|
||||
'light_damage' holds the damage per second inflicted to mobs when it's too
|
||||
bright (above 13 light).
|
||||
'suffocation' when true causes mobs to suffocate inside solid blocks (2 damage per second).
|
||||
'floats' when set to 1 mob will float in water, 0 has them sink.
|
||||
|
@ -119,7 +119,7 @@ functions needed for the mob to work properly which contains the following:
|
|||
attacking.
|
||||
'dogshoot_switch' allows switching between attack types by using timers
|
||||
(1 for shoot, 2 for dogfight)
|
||||
'dogshoot_count_max'contains how many seconds before switching from
|
||||
'dogshoot_count_max' contains how many seconds before switching from
|
||||
dogfight to shoot.
|
||||
'dogshoot_count2_max' contains how many seconds before switching from shoot
|
||||
to dogfight.
|
||||
|
@ -136,7 +136,7 @@ functions needed for the mob to work properly which contains the following:
|
|||
in minetest.conf is not false).
|
||||
'immune_to' is a table that holds specific damage when being hit by
|
||||
certain items e.g.
|
||||
{"default:sword_wood",0} -- causes no damage.
|
||||
{"default:sword_wood", 0} -- causes no damage.
|
||||
{"default:gold_lump", -10} -- heals by 10 health points.
|
||||
{"default:coal_block", 20} -- 20 damage when hit on head with coal blocks.
|
||||
|
||||
|
@ -194,10 +194,9 @@ functions needed for the mob to work properly which contains the following:
|
|||
'punch2' animations.
|
||||
|
||||
'animation' holds a table containing animation names and settings for use with mesh models:
|
||||
{
|
||||
'stand_start'start frame for when mob stands still.
|
||||
'stand_start' start frame for when mob stands still.
|
||||
'stand_end' end frame of stand animation.
|
||||
'stand_speed'speed of animation in frames per second.
|
||||
'stand_speed' speed of animation in frames per second.
|
||||
'walk_start' when mob is walking around.
|
||||
'walk_end'
|
||||
'walk_speed'
|
||||
|
@ -207,20 +206,19 @@ functions needed for the mob to work properly which contains the following:
|
|||
'fly_start' when a mob is flying.
|
||||
'fly_end'
|
||||
'fly_speed'
|
||||
'punch_start'when a mob melee attacks.
|
||||
'punch_start' when a mob melee attacks.
|
||||
'punch_end'
|
||||
'punch_speed'
|
||||
'punch2_start' alternative melee attack animation.
|
||||
'punch2_end'
|
||||
'punch2_speed'
|
||||
'shoot_start'shooting animation.
|
||||
'shoot_start' shooting animation.
|
||||
'shoot_end'
|
||||
'shoot_speed'
|
||||
'die_start' death animation
|
||||
'die_end'
|
||||
'die_speed'
|
||||
'die_loop' when set to false stops the animation looping.
|
||||
}
|
||||
|
||||
Using '_loop = false' setting will stop any of the above animations from
|
||||
looping.
|
||||
|
@ -239,7 +237,7 @@ functions needed for the mob to work properly which contains the following:
|
|||
'rain_damage' damage per second if mob is standing in rain (default: 0)
|
||||
'sunlight_damage' holds the damage per second inflicted to mobs when they
|
||||
are in direct sunlight
|
||||
'spawn_small_alternative' name of a smaller mob to use as replacement if
|
||||
'spawn_small_alternative': name of a smaller mob to use as replacement if
|
||||
spawning fails due to space requirements
|
||||
'glow' same as in entity definition
|
||||
'child' if true, spawn mob as child
|
||||
|
@ -255,13 +253,6 @@ functions needed for the mob to work properly which contains the following:
|
|||
'fire_resistant' If true, the mob can't burn
|
||||
'fire_damage_resistant' If true the mob will not take damage when burning
|
||||
'ignited_by_sunlight' If true the mod will burn at daytime. (Takes sunlight_damage per second)
|
||||
'nofollow' Do not follow players when they wield the "follow" item. For mobs (like villagers)
|
||||
that are bred in a different way.
|
||||
'pick_up' table of itemstrings the mob will pick up (e.g. for breeding)
|
||||
'on_pick_up' function that will be called on item pickup - return true to not pickup the item
|
||||
'custom_visual_size' will not reset visual_size from the base class on reload
|
||||
|
||||
mobs:gopath(self,target,callback_arrived) pathfind a way to target and run callback on arrival
|
||||
|
||||
|
||||
|
||||
|
@ -306,7 +297,7 @@ enhance mob functionality and have them do many interesting things:
|
|||
'on_die' a function that is called when the mob is killed; the
|
||||
parameters are (self, pos). Return true to skip the builtin
|
||||
death animation and death effects
|
||||
'on_rightclick'its same as in minetest.register_entity()
|
||||
'on_rightclick' its same as in minetest.register_entity()
|
||||
'on_blast' is called when an explosion happens near mob when using TNT
|
||||
functions, parameters are (object, damage) and returns
|
||||
(do_damage, do_knockback, drops)
|
||||
|
@ -317,13 +308,13 @@ enhance mob functionality and have them do many interesting things:
|
|||
'on_breed' called when two similar mobs breed, paramaters are
|
||||
(parent1, parent2) objects, return false to stop child from
|
||||
being resized and owner/tamed flags and child textures being
|
||||
applied.Function itself must spawn new child mob.
|
||||
applied. Function itself must spawn new child mob.
|
||||
'on_grown' is called when a child mob has grown up, only paramater is
|
||||
(self).
|
||||
'do_punch' called when mob is punched with paramaters (self, hitter,
|
||||
time_from_last_punch, tool_capabilities, direction), return
|
||||
false to stop punch damage and knockback from taking place.
|
||||
'custom_attack'when set this function is called instead of the normal mob
|
||||
'custom_attack' when set this function is called instead of the normal mob
|
||||
melee attack, parameters are (self, to_attack).
|
||||
'on_die' a function that is called when mob is killed (self, pos)
|
||||
'do_custom' a custom function that is called every tick while mob is
|
||||
|
@ -344,9 +335,9 @@ for each mob.
|
|||
damage at all (cannot exceed self.breath_max). Breath
|
||||
decreases by 1 each second while in a node with drowning
|
||||
damage and increases by 1 each second otherwise.
|
||||
'self.texture_list'contains list of all mob textures
|
||||
'self.texture_list' contains list of all mob textures
|
||||
'self.child_texture' contains mob child texture when growing up
|
||||
'self.base_texture'contains current skin texture which was randomly
|
||||
'self.base_texture' contains current skin texture which was randomly
|
||||
selected from textures list
|
||||
'self.gotten' this is used to track whether some special item has been
|
||||
gotten from the mob, for example, wool from sheep.
|
||||
|
@ -415,7 +406,7 @@ command which uses above names to make settings clearer:
|
|||
|
||||
|
||||
For each mob that spawns with this function is a field in mobs.spawning_mobs.
|
||||
It tells if the mob should spawn or not.Default is true.So other mods can
|
||||
It tells if the mob should spawn or not. Default is true. So other mods can
|
||||
only use the API of this mod by disabling the spawning of the default mobs in
|
||||
this mod.
|
||||
|
||||
|
@ -423,7 +414,7 @@ this mod.
|
|||
mobs:spawn_abm_check(pos, node, name)
|
||||
|
||||
This global function can be changed to contain additional checks for mobs to
|
||||
spawn e.g. mobs that spawn only in specific areas and the like.By returning
|
||||
spawn e.g. mobs that spawn only in specific areas and the like. By returning
|
||||
true the mob will not spawn.
|
||||
|
||||
'pos' holds the position of the spawning mob
|
||||
|
@ -434,7 +425,7 @@ true the mob will not spawn.
|
|||
MineClone 2 extensions
|
||||
----------------------
|
||||
|
||||
mcl_mobs:spawn_child(pos, mob_type)
|
||||
mobs:spawn_child(pos, mob_type)
|
||||
|
||||
This function spawns a mob as a child. The parameter mob_type is the
|
||||
entitystring of the new mob.
|
||||
|
@ -444,7 +435,6 @@ mobs:death_effect(pos, collisionbox)
|
|||
|
||||
Create death particles at pos with the given collisionbox.
|
||||
|
||||
mcl_mobs.spawn(pos,name/entity name)
|
||||
|
||||
Making Arrows
|
||||
-------------
|
||||
|
@ -456,7 +446,7 @@ This function registers a arrow for mobs with the attack type shoot.
|
|||
'name' is the name of the arrow
|
||||
'definition' is a table with the following values:
|
||||
'visual' same is in minetest.register_entity()
|
||||
'visual_size'same is in minetest.register_entity()
|
||||
'visual_size' same is in minetest.register_entity()
|
||||
'textures' same is in minetest.register_entity()
|
||||
'velocity' the velocity of the arrow
|
||||
'drop' if set to true any arrows hitting a node will drop as item
|
||||
|
@ -486,13 +476,13 @@ This function registers a arrow for mobs with the attack type shoot.
|
|||
Spawn Eggs
|
||||
----------
|
||||
|
||||
mobs:register_egg(name, description, background_color, overlay_color, addegg, no_creative)
|
||||
mobs:register_egg(name, description, background, addegg, no_creative)
|
||||
|
||||
This function registers a spawn egg which can be used by admin to properly spawn in a mob.
|
||||
|
||||
'name' this is the name of your new mob to spawn e.g. "mob:sheep"
|
||||
'description' the name of the new egg you are creating e.g. "Spawn Sheep"
|
||||
'background_color' and 'overlay_color' define the colors for the texture displayed for the egg in inventory
|
||||
'background' the texture displayed for the egg in inventory
|
||||
'addegg' would you like an egg image in front of your texture (1 = yes,
|
||||
0 = no)
|
||||
'no_creative' when set to true this stops spawn egg appearing in creative
|
||||
|
@ -508,7 +498,7 @@ mobs:boom(self, pos, radius)
|
|||
'radius' radius of explosion (typically set to 3)
|
||||
|
||||
This function generates an explosion which removes nodes in a specific radius
|
||||
and damages any entity caught inside the blast radius.Protection will limit
|
||||
and damages any entity caught inside the blast radius. Protection will limit
|
||||
node destruction but not entity damage.
|
||||
|
||||
|
||||
|
@ -552,7 +542,7 @@ mobs:protect(self, clicker)
|
|||
|
||||
This function can be used to right-click any tamed mob with mobs:protector item,
|
||||
this will protect the mob from harm inside of a protected area from other
|
||||
players.Will return true when mob right-clicked with mobs:protector item.
|
||||
players. Will return true when mob right-clicked with mobs:protector item.
|
||||
|
||||
'self' mob information
|
||||
'clicker' player information
|
||||
|
@ -588,7 +578,7 @@ This function allows an attached player to move the mob around and animate it at
|
|||
same time.
|
||||
|
||||
'self' mob information
|
||||
'move_animation'string containing movement animation e.g. "walk"
|
||||
'move_animation' string containing movement animation e.g. "walk"
|
||||
'stand_animation' string containing standing animation e.g. "stand"
|
||||
'can_fly' if true then jump and sneak controls will allow mob to fly
|
||||
up and down
|
||||
|
@ -606,7 +596,7 @@ controls.
|
|||
'can_shoot' true if mob can fire arrow (sneak and left mouse button
|
||||
fires)
|
||||
'arrow_entity' name of arrow entity used for firing
|
||||
'move_animation'string containing name of pre-defined animation e.g. "walk"
|
||||
'move_animation' string containing name of pre-defined animation e.g. "walk"
|
||||
or "fly" etc.
|
||||
'stand_animation' string containing name of pre-defined animation e.g.
|
||||
"stand" or "blink" etc.
|
||||
|
@ -633,7 +623,7 @@ Certain variables need to be set before using the above functions:
|
|||
'self.accel' acceleration speed
|
||||
'self.terrain_type' integer containing terrain mob can walk on
|
||||
(1 = water, 2 or 3 = land)
|
||||
'self.driver_attach_at'position offset for attaching player to mob
|
||||
'self.driver_attach_at' position offset for attaching player to mob
|
||||
'self.driver_eye_offset' position offset for attached player view
|
||||
'self.driver_scale' sets driver scale for mobs larger than {x=1, y=1}
|
||||
|
||||
|
@ -652,7 +642,7 @@ External Settings for "minetest.conf"
|
|||
multiplied by this number), defaults to 1.0.
|
||||
'mob_spawn_chance' multiplies chance of all mobs spawning and can be set
|
||||
to 0.5 to have mobs spawn more or 2.0 to spawn less.
|
||||
e.g.1 in 7000 * 0.5 = 1 in 3500 so better odds of
|
||||
e.g. 1 in 7000 * 0.5 = 1 in 3500 so better odds of
|
||||
spawning.
|
||||
'mobs_spawn' if false then mobs no longer spawn without spawner or
|
||||
spawn egg.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name = mcl_mobs
|
||||
author = PilzAdam
|
||||
description = Adds a mob API for mods to add animals or monsters, etc.
|
||||
depends = mcl_particles
|
||||
depends = mcl_mapgen, mcl_particles
|
||||
optional_depends = mcl_weather, mcl_explosions, mcl_hunger, mcl_worlds, invisibility, lucky_block, cmi, doc_identifier, mcl_armor, mcl_portals, mcl_experience
|
||||
|
|
|
@ -12,7 +12,7 @@ local crash_threshold = 6.5 -- ignored if enable_crash=false
|
|||
|
||||
local node_ok = function(pos, fallback)
|
||||
|
||||
fallback = fallback or mcl_mobs.fallback_node
|
||||
fallback = fallback or mobs.fallback_node
|
||||
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
|
||||
|
@ -119,7 +119,7 @@ end)
|
|||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function mcl_mobs.attach(entity, player)
|
||||
function mobs.attach(entity, player)
|
||||
|
||||
local attach_at, eye_offset
|
||||
|
||||
|
@ -162,7 +162,7 @@ function mcl_mobs.attach(entity, player)
|
|||
end
|
||||
|
||||
|
||||
function mcl_mobs.detach(player, offset)
|
||||
function mobs.detach(player, offset)
|
||||
|
||||
force_detach(player)
|
||||
|
||||
|
@ -185,7 +185,7 @@ function mcl_mobs.detach(player, offset)
|
|||
end
|
||||
|
||||
|
||||
function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||
function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||
|
||||
local rot_view = 0
|
||||
|
||||
|
@ -261,7 +261,7 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
|||
if entity.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
|
||||
|
||||
if stand_anim then
|
||||
mcl_mobs:set_animation(entity, stand_anim)
|
||||
mobs:set_animation(entity, stand_anim)
|
||||
end
|
||||
|
||||
return
|
||||
|
@ -269,7 +269,7 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
|||
|
||||
-- set moving animation
|
||||
if moving_anim then
|
||||
mcl_mobs:set_animation(entity, moving_anim)
|
||||
mobs:set_animation(entity, moving_anim)
|
||||
end
|
||||
|
||||
-- Stop!
|
||||
|
@ -388,7 +388,7 @@ end
|
|||
|
||||
-- directional flying routine by D00Med (edited by TenPlus1)
|
||||
|
||||
function mcl_mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
|
||||
function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
|
||||
|
||||
local ctrl = entity.driver:get_player_control()
|
||||
local velo = entity.object:get_velocity()
|
||||
|
@ -440,9 +440,9 @@ function mcl_mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_an
|
|||
-- change animation if stopped
|
||||
if velo.x == 0 and velo.y == 0 and velo.z == 0 then
|
||||
|
||||
mcl_mobs:set_animation(entity, stand_anim)
|
||||
mobs:set_animation(entity, stand_anim)
|
||||
else
|
||||
-- moving animation
|
||||
mcl_mobs:set_animation(entity, moving_anim)
|
||||
mobs:set_animation(entity, moving_anim)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
local dim = {"x", "z"}
|
||||
|
||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||
|
||||
local function load_schem(filename)
|
||||
local file = io.open(modpath .. "/schems/" .. filename, "r")
|
||||
local data = minetest.deserialize(file:read())
|
||||
file:close()
|
||||
return data
|
||||
end
|
||||
|
||||
local wither_spawn_schems = {}
|
||||
|
||||
for _, d in pairs(dim) do
|
||||
wither_spawn_schems[d] = load_schem("wither_spawn_" .. d .. ".we")
|
||||
end
|
||||
|
||||
local function check_schem(pos, schem)
|
||||
for _, n in pairs(schem) do
|
||||
if minetest.get_node(vector.add(pos, n)).name ~= n.name then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function remove_schem(pos, schem)
|
||||
for _, n in pairs(schem) do
|
||||
minetest.remove_node(vector.add(pos, n))
|
||||
end
|
||||
end
|
||||
|
||||
local function wither_spawn(pos)
|
||||
for _, d in pairs(dim) do
|
||||
for i = 0, 2 do
|
||||
local p = vector.add(pos, {x = 0, y = -2, z = 0, [d] = -i})
|
||||
local schem = wither_spawn_schems[d]
|
||||
if check_schem(p, schem) then
|
||||
remove_schem(p, schem)
|
||||
minetest.add_entity(vector.add(p, {x = 0, y = 1, z = 0, [d] = 1}), "mobs_mc:wither")
|
||||
local objects = minetest.get_objects_inside_radius(pos, 20)
|
||||
for _, players in ipairs(objects) do
|
||||
if players:is_player() then
|
||||
awards.unlock(players:get_player_name(), "mcl:witheringHeights")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local wither_head = minetest.registered_nodes["mcl_heads:wither_skeleton"]
|
||||
local old_on_place = wither_head.on_place
|
||||
function wither_head.on_place(itemstack, placer, pointed)
|
||||
local n = minetest.get_node(vector.offset(pointed.above,0,-1,0))
|
||||
if n and n.name == "mcl_nether:soul_sand" then
|
||||
minetest.after(0, wither_spawn, pointed.above)
|
||||
end
|
||||
return old_on_place(itemstack, placer, pointed)
|
||||
end
|
|
@ -1,4 +0,0 @@
|
|||
name = mcl_wither_spawning
|
||||
description = Wither Spawning for MineClone2
|
||||
author = Fleckenstein
|
||||
depends = mobs_mc, mcl_heads
|
|
@ -1 +0,0 @@
|
|||
return {{["y"] = 1, ["x"] = 0, ["name"] = "mcl_nether:soul_sand", ["z"] = 0}, {["y"] = 2, ["x"] = 0, ["name"] = "mcl_heads:wither_skeleton", ["z"] = 0, ["param2"] = 2, ["param1"] = 15}, {["y"] = 0, ["x"] = 1, ["name"] = "mcl_nether:soul_sand", ["z"] = 0}, {["y"] = 1, ["x"] = 1, ["name"] = "mcl_nether:soul_sand", ["z"] = 0}, {["y"] = 2, ["x"] = 1, ["name"] = "mcl_heads:wither_skeleton", ["z"] = 0, ["param2"] = 2, ["param1"] = 15}, {["y"] = 1, ["x"] = 2, ["name"] = "mcl_nether:soul_sand", ["z"] = 0}, {["y"] = 2, ["x"] = 2, ["name"] = "mcl_heads:wither_skeleton", ["z"] = 0, ["param2"] = 2, ["param1"] = 15}}
|
|
@ -1 +0,0 @@
|
|||
return {{["y"] = 0, ["x"] = 0, ["name"] = "mcl_nether:soul_sand", ["z"] = 1}, {["y"] = 1, ["x"] = 0, ["name"] = "mcl_nether:soul_sand", ["z"] = 0}, {["y"] = 1, ["x"] = 0, ["name"] = "mcl_nether:soul_sand", ["z"] = 1}, {["y"] = 1, ["x"] = 0, ["name"] = "mcl_nether:soul_sand", ["z"] = 2}, {["y"] = 2, ["x"] = 0, ["name"] = "mcl_heads:wither_skeleton", ["z"] = 0, ["param2"] = 1, ["param1"] = 15}, {["y"] = 2, ["x"] = 0, ["name"] = "mcl_heads:wither_skeleton", ["z"] = 1, ["param2"] = 1, ["param1"] = 15}, {["y"] = 2, ["x"] = 0, ["name"] = "mcl_heads:wither_skeleton", ["z"] = 2, ["param2"] = 1, ["param1"] = 15}}
|
|
@ -0,0 +1,333 @@
|
|||
--[[ This table contains the concrete itemstrings to be used by this mod.
|
||||
All mobs in this mod must use variables in this table, instead
|
||||
of hardcoding the itemstring.
|
||||
This way, external mods are enabled to replace the itemstrings to provide
|
||||
their own items and game integration is made much simpler.
|
||||
|
||||
An item IDs is supposed to be overwritten by adding
|
||||
mobs_mc.override.items["example:item"] in a game mod
|
||||
with name "mobs_mc_gameconfig". ]]
|
||||
|
||||
|
||||
-- Standard items
|
||||
|
||||
-- If true, mobs_mc adds the monster egg nodes (needs default mod).
|
||||
-- Set to false in your gameconfig mod if you create your own monster egg nodes.
|
||||
mobs_mc.create_monster_egg_nodes = true
|
||||
|
||||
mobs_mc.items = {}
|
||||
|
||||
mobs_mc.items = {
|
||||
-- Items defined in mobs_mc
|
||||
blaze_rod = "mobs_mc:blaze_rod",
|
||||
blaze_powder = "mobs_mc:blaze_powder",
|
||||
chicken_raw = "mobs_mc:chicken_raw",
|
||||
chicken_cooked = "mobs_mc:chicken_cooked",
|
||||
feather = "mobs_mc:feather",
|
||||
beef_raw = "mobs_mc:beef_raw",
|
||||
beef_cooked = "mobs_mc:beef_cooked",
|
||||
bowl = "mobs_mc:bowl",
|
||||
mushroom_stew = "mobs_mc:mushroom_stew",
|
||||
milk = "mobs_mc:milk_bucket",
|
||||
dragon_egg = "mobs_mc:dragon_egg",
|
||||
egg = "mobs_mc:egg",
|
||||
ender_eye = "mobs_mc:ender_eye",
|
||||
ghast_tear = "mobs_mc:ghast_tear",
|
||||
saddle = "mobs:saddle",
|
||||
iron_horse_armor = "mobs_mc:iron_horse_armor",
|
||||
gold_horse_armor = "mobs_mc:gold_horse_armor",
|
||||
diamond_horse_armor = "mobs_mc:diamond_horse_armor",
|
||||
porkchop_raw = "mobs_mc:porkchop_raw",
|
||||
porkchop_cooked = "mobs_mc:porkchop_cooked",
|
||||
carrot_on_a_stick = "mobs_mc:carrot_on_a_stick",
|
||||
rabbit_raw = "mobs_mc:rabbit_raw",
|
||||
rabbit_cooked = "mobs_mc:rabbit_cooked",
|
||||
rabbit_hide = "mobs_mc:rabbit_hide",
|
||||
mutton_raw = "mobs_mc:mutton_raw",
|
||||
mutton_cooked = "mobs_mc:mutton_cooked",
|
||||
shulker_shell = "mobs_mc:shulker_shell",
|
||||
magma_cream = "mobs_mc:magma_cream",
|
||||
spider_eye = "mobs_mc:spider_eye",
|
||||
snowball = "mobs_mc:snowball",
|
||||
totem = "mobs_mc:totem",
|
||||
rotten_flesh = "mobs_mc:rotten_flesh",
|
||||
nether_star = "mobs_mc:nether_star",
|
||||
bone = "mobs_mc:bone",
|
||||
slimeball = "mobs_mc:slimeball",
|
||||
arrow = "mobs_mc:arrow",
|
||||
bow = "mobs_mc:bow_wood",
|
||||
head_creeper = "mobs_mc:head_creeper",
|
||||
head_zombie = "mobs_mc:head_zombie",
|
||||
head_skeleton = "mobs_mc:head_skeleton",
|
||||
head_wither_skeleton = "mobs_mc:head_wither_skeleton",
|
||||
|
||||
-- External items
|
||||
-- Mobs Redo
|
||||
leather = "mobs:leather",
|
||||
shears = "mobs:shears",
|
||||
|
||||
-- Minetest Game
|
||||
top_snow = "default:snow",
|
||||
snow_block = "default:snowblock",
|
||||
mushroom_red = "flowers:mushroom_red",
|
||||
bucket = "bucket:bucket_empty",
|
||||
grass_block = "default:dirt_with_grass",
|
||||
string = "farming:string",
|
||||
stick = "default:stick",
|
||||
flint = "default:flint",
|
||||
iron_ingot = "default:steel_ingot",
|
||||
iron_block = "default:steelblock",
|
||||
fire = "fire:basic_flame",
|
||||
gunpowder = "tnt:gunpowder",
|
||||
flint_and_steel = "fire:flint_and_steel",
|
||||
water_source = "default:water_source",
|
||||
river_water_source = "default:river_water_source",
|
||||
black_dye = "dye:black",
|
||||
poppy = "flowers:rose",
|
||||
dandelion = "flowers:dandelion_yellow",
|
||||
coal = "default:coal_lump",
|
||||
emerald = "default:diamond",
|
||||
iron_axe = "default:axe_steel",
|
||||
gold_sword = "default:sword_mese",
|
||||
gold_ingot = "default:gold_ingot",
|
||||
gold_nugget = "default:gold_lump",
|
||||
glowstone_dust = "default:mese_crystal_fragment",
|
||||
redstone = "default:mese_crystal_fragment",
|
||||
glass_bottle = "vessels:glass_bottle",
|
||||
sugar = "default:papyrus",
|
||||
wheat = "farming:wheat",
|
||||
hay_bale = "farming:straw",
|
||||
prismarine_shard = "default:mese_crystal_fragment",
|
||||
prismarine_crystals = "default:mese_crystal",
|
||||
apple = "default:apple",
|
||||
golden_apple = "default:apple",
|
||||
rabbit_foot = "mobs_mc:rabbit_foot",
|
||||
|
||||
-- Boss items
|
||||
wet_sponge = "default:gold_block", -- only dropped by elder guardian; there is no equivalent block in Minetest Game
|
||||
|
||||
-- Other
|
||||
nether_brick_block = "nether:brick",
|
||||
mycelium = "ethereal:mushroom_dirt",
|
||||
carrot = "farming:carrot",
|
||||
potato = "farming:potato",
|
||||
golden_carrot = "farming:carrot_gold",
|
||||
fishing_rod = "fishing:pole_wood",
|
||||
fish_raw = "fishing:fish_raw",
|
||||
salmon_raw = "fishing:carp_raw",
|
||||
clownfish_raw = "fishing:clownfish_raw",
|
||||
pufferfish_raw = "fishing:pike_raw",
|
||||
|
||||
cookie = "farming:cookie",
|
||||
|
||||
|
||||
-- TODO: Add actual ender pearl
|
||||
ender_pearl = "farorb:farorb",
|
||||
|
||||
nether_portal = "nether:portal",
|
||||
netherrack = "nether:rack",
|
||||
nether_brick_block = "nether:brick",
|
||||
|
||||
-- Wool (Minecraft color scheme)
|
||||
wool_white = "wool:white",
|
||||
wool_light_grey = "wool:grey",
|
||||
wool_grey = "wool:dark_grey",
|
||||
wool_blue = "wool:blue",
|
||||
wool_lime = "wool:green",
|
||||
wool_green = "wool:dark_green",
|
||||
wool_purple = "wool:violet",
|
||||
wool_pink = "wool:pink",
|
||||
wool_yellow = "wool:yellow",
|
||||
wool_orange = "wool:orange",
|
||||
wool_brown = "wool:brown",
|
||||
wool_red = "wool:red",
|
||||
wool_cyan = "wool:cyan",
|
||||
wool_magenta = "wool:magenta",
|
||||
wool_black = "wool:black",
|
||||
-- Light blue intentionally missing
|
||||
|
||||
-- Special items
|
||||
music_discs = {}, -- No music discs by default; used by creeper. Override this if your game has music discs.
|
||||
}
|
||||
|
||||
-- Tables for attracting, feeding and breeding mobs
|
||||
mobs_mc.follow = {
|
||||
sheep = { mobs_mc.items.wheat },
|
||||
cow = { mobs_mc.items.wheat },
|
||||
chicken = { "farming:seed_wheat", "farming:seed_cotton" }, -- seeds in general
|
||||
parrot = { "farming:seed_wheat", "farming:seed_cotton" }, -- seeds in general
|
||||
horse = { mobs_mc.items.apple, mobs_mc.items.sugar, mobs_mc.items.wheat, mobs_mc.items.hay_bale, mobs_mc.items.golden_apple, mobs_mc.items.golden_carrot },
|
||||
llama = { mobs_mc.items.wheat, mobs_mc.items.hay_bale, },
|
||||
pig = { mobs_mc.items.potato, mobs_mc.items.carrot, mobs_mc.items.carrot_on_a_stick,
|
||||
mobs_mc.items.apple, -- Minetest Game extra
|
||||
},
|
||||
rabbit = { mobs_mc.items.dandelion, mobs_mc.items.carrot, mobs_mc.items.golden_carrot, "farming_plus:carrot_item", },
|
||||
ocelot = { mobs_mc.items.fish_raw, mobs_mc.items.salmon_raw, mobs_mc.items.clownfish_raw, mobs_mc.items.pufferfish_raw,
|
||||
mobs_mc.items.chicken_raw, -- Minetest Game extra
|
||||
},
|
||||
wolf = { mobs_mc.items.bone },
|
||||
dog = { mobs_mc.items.rabbit_raw, mobs_mc.items.rabbit_cooked, mobs_mc.items.mutton_raw, mobs_mc.items.mutton_cooked, mobs_mc.items.beef_raw, mobs_mc.items.beef_cooked, mobs_mc.items.chicken_raw, mobs_mc.items.chicken_cooked, mobs_mc.items.rotten_flesh,
|
||||
-- Mobs Redo items
|
||||
"mobs:meat", "mobs:meat_raw" },
|
||||
}
|
||||
|
||||
-- Contents for replace_what
|
||||
mobs_mc.replace = {
|
||||
-- Rabbits reduce carrot growth stage by 1
|
||||
rabbit = {
|
||||
-- Farming Redo carrots
|
||||
{"farming:carrot_8", "farming:carrot_7", 0},
|
||||
{"farming:carrot_7", "farming:carrot_6", 0},
|
||||
{"farming:carrot_6", "farming:carrot_5", 0},
|
||||
{"farming:carrot_5", "farming:carrot_4", 0},
|
||||
{"farming:carrot_4", "farming:carrot_3", 0},
|
||||
{"farming:carrot_3", "farming:carrot_2", 0},
|
||||
{"farming:carrot_2", "farming:carrot_1", 0},
|
||||
{"farming:carrot_1", "air", 0},
|
||||
-- Farming Plus carrots
|
||||
{"farming_plus:carrot", "farming_plus:carrot_7", 0},
|
||||
{"farming_plus:carrot_6", "farming_plus:carrot_5", 0},
|
||||
{"farming_plus:carrot_5", "farming_plus:carrot_4", 0},
|
||||
{"farming_plus:carrot_4", "farming_plus:carrot_3", 0},
|
||||
{"farming_plus:carrot_3", "farming_plus:carrot_2", 0},
|
||||
{"farming_plus:carrot_2", "farming_plus:carrot_1", 0},
|
||||
{"farming_plus:carrot_1", "air", 0},
|
||||
},
|
||||
-- Sheep eat grass
|
||||
sheep = {
|
||||
-- Grass Block
|
||||
{ "default:dirt_with_grass", "default:dirt", -1 },
|
||||
-- “Tall Grass”
|
||||
{ "default:grass_5", "air", 0 },
|
||||
{ "default:grass_4", "air", 0 },
|
||||
{ "default:grass_3", "air", 0 },
|
||||
{ "default:grass_2", "air", 0 },
|
||||
{ "default:grass_1", "air", 0 },
|
||||
},
|
||||
-- Silverfish populate stone, etc. with monster eggs
|
||||
silverfish = {
|
||||
{"default:stone", "mobs_mc:monster_egg_stone", -1},
|
||||
{"default:cobble", "mobs_mc:monster_egg_cobble", -1},
|
||||
{"default:mossycobble", "mobs_mc:monster_egg_mossycobble", -1},
|
||||
{"default:stonebrick", "mobs_mc:monster_egg_stonebrick", -1},
|
||||
{"default:stone_block", "mobs_mc:monster_egg_stone_block", -1},
|
||||
},
|
||||
}
|
||||
|
||||
-- List of nodes which endermen can take
|
||||
mobs_mc.enderman_takable = {
|
||||
-- Generic handling, useful for entensions
|
||||
"group:enderman_takable",
|
||||
|
||||
-- Generic nodes
|
||||
"group:sand",
|
||||
"group:flower",
|
||||
|
||||
-- Minetest Game
|
||||
"default:dirt",
|
||||
"default:dirt_with_grass",
|
||||
"default:dirt_with_dry_grass",
|
||||
"default:dirt_with_snow",
|
||||
"default:dirt_with_rainforest_litter",
|
||||
"default:dirt_with_grass_footsteps",
|
||||
-- FIXME: For some reason, Minetest has a Lua error when an enderman tries to place a Minetest Game cactus.
|
||||
-- Maybe this is because default:cactus has rotate_and_place?
|
||||
-- "default:cactus", -- TODO: Re-enable cactus when it works again
|
||||
"default:gravel",
|
||||
"default:clay",
|
||||
"flowers:mushroom_red",
|
||||
"flowers:mushroom_brown",
|
||||
"tnt:tnt",
|
||||
|
||||
-- Nether mod
|
||||
"nether:rack",
|
||||
}
|
||||
|
||||
--[[ Table of nodes to replace when an enderman takes it.
|
||||
If the enderman takes an indexed node, it the enderman will get the item in the value.
|
||||
Table indexes: Original node, taken by enderman.
|
||||
Table values: The item which the enderman *actually* gets
|
||||
Example:
|
||||
mobs_mc.enderman_node_replace = {
|
||||
["default:dirt_with_dry_grass"] = "default_dirt_with_grass",
|
||||
}
|
||||
-- This means, if the enderman takes a dirt with dry grass, he will get a dirt with grass
|
||||
-- on his hand instead.
|
||||
]]
|
||||
mobs_mc.enderman_replace_on_take = {} -- no replacements by default
|
||||
|
||||
-- A table which can be used to override block textures of blocks carried by endermen.
|
||||
-- Only works for cube-shaped nodes and nodeboxes.
|
||||
-- Key: itemstrings of the blocks to replace
|
||||
-- Value: A table with the texture overrides (6 textures)
|
||||
mobs_mc.enderman_block_texture_overrides = {
|
||||
}
|
||||
|
||||
-- List of nodes on which mobs can spawn
|
||||
mobs_mc.spawn = {
|
||||
solid = { "group:cracky", "group:crumbly", "group:shovely", "group:pickaxey" }, -- spawn on "solid" nodes (this is mostly just guessing)
|
||||
|
||||
grassland = { mobs_mc.items.grass_block, "ethereal:prairie_dirt" },
|
||||
savanna = { "default:dirt_with_dry_grass" },
|
||||
grassland_savanna = { mobs_mc.items.grass_block, "default:dirt_with_dry_grass" },
|
||||
desert = { "default:desert_sand", "group:sand" },
|
||||
jungle = { "default:dirt_with_rainforest_litter", "default:jungleleaves", "default:junglewood", "mcl_core:jungleleaves", "mcl_core:junglewood" },
|
||||
snow = { "default:snow", "default:snowblock", "default:dirt_with_snow" },
|
||||
end_city = { "default:sandstonebrick", "mcl_end:purpur_block", "mcl_end:end_stone" },
|
||||
wolf = { mobs_mc.items.grass_block, "default:dirt_with_rainforest_litter", "default:dirt", "default:dirt_with_snow", "default:snow", "default:snowblock" },
|
||||
village = { "mg_villages:road" },
|
||||
|
||||
-- These probably don't need overrides
|
||||
mushroom_island = { mobs_mc.items.mycelium, "mcl_core:mycelium" },
|
||||
nether_fortress = { mobs_mc.items.nether_brick_block, "mcl_nether:nether_brick", },
|
||||
nether = { mobs_mc.items.netherrack, "mcl_nether:netherrack", },
|
||||
nether_portal = { mobs_mc.items.nether_portal, "mcl_portals:portal" },
|
||||
water = { mobs_mc.items.water_source, "mcl_core:water_source", "default:water_source" },
|
||||
}
|
||||
|
||||
-- This table contains important spawn height references for the mob spawn height.
|
||||
-- Please base your mob spawn height on these numbers to keep things clean.
|
||||
mobs_mc.spawn_height = {
|
||||
water = tonumber(minetest.settings:get("water_level")) or 0, -- Water level in the Overworld
|
||||
|
||||
-- Overworld boundaries (inclusive) --I adjusted this to be more reasonable
|
||||
overworld_min = -64,-- -2999,
|
||||
overworld_max = 31000,
|
||||
|
||||
-- Nether boundaries (inclusive)
|
||||
nether_min = -29067,-- -3369,
|
||||
nether_max = -28939,-- -3000,
|
||||
|
||||
-- End boundaries (inclusive)
|
||||
end_min = -6200,
|
||||
end_max = -6000,
|
||||
}
|
||||
|
||||
mobs_mc.misc = {
|
||||
shears_wear = 276, -- Wear to add per shears usage (238 uses)
|
||||
totem_fail_nodes = {} -- List of nodes in which the totem of undying fails
|
||||
}
|
||||
|
||||
-- Item name overrides from mobs_mc_gameconfig (if present)
|
||||
if minetest.get_modpath("mobs_mc_gameconfig") and mobs_mc.override then
|
||||
local tables = {"items", "follow", "replace", "spawn", "spawn_height", "misc"}
|
||||
for t=1, #tables do
|
||||
local tbl = tables[t]
|
||||
if mobs_mc.override[tbl] then
|
||||
for k, v in pairs(mobs_mc.override[tbl]) do
|
||||
mobs_mc[tbl][k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if mobs_mc.override.enderman_takable then
|
||||
mobs_mc.enderman_takable = mobs_mc.override.enderman_takable
|
||||
end
|
||||
if mobs_mc.override.enderman_replace_on_take then
|
||||
mobs_mc.enderman_replace_on_take = mobs_mc.override.enderman_replace_on_take
|
||||
end
|
||||
if mobs_mc.enderman_block_texture_overrides then
|
||||
mobs_mc.enderman_block_texture_overrides = mobs_mc.override.enderman_block_texture_overrides
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,587 @@
|
|||
--MCmobs v0.5
|
||||
--maikerumine
|
||||
--made for MC like Survival game
|
||||
--License for code WTFPL and otherwise stated in readmes
|
||||
|
||||
--THIS IS THE MASTER ITEM LIST TO USE WITH DEFAULT
|
||||
|
||||
-- NOTE: Most strings intentionally not marked for translation, other mods already have these items.
|
||||
-- TODO: Remove this file eventually, most items are already outsourced in other mods.
|
||||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
local c = mobs_mc.is_item_variable_overridden
|
||||
|
||||
-- Blaze
|
||||
if c("blaze_rod") then
|
||||
minetest.register_craftitem("mobs_mc:blaze_rod", {
|
||||
description = "Blaze Rod",
|
||||
_doc_items_longdesc = "This is a crafting component dropped from dead blazes.",
|
||||
wield_image = "mcl_mobitems_blaze_rod.png",
|
||||
inventory_image = "mcl_mobitems_blaze_rod.png",
|
||||
})
|
||||
|
||||
-- Make blaze rod furnace-burnable. 1.5 times the burn time of a coal lump
|
||||
local coalcraft, burntime
|
||||
if minetest.get_modpath("default") then
|
||||
coalcraft = minetest.get_craft_result({method="fuel", width=1, items={"default:coal_lump"}})
|
||||
end
|
||||
if coalcraft then
|
||||
burntime = math.floor(coalcraft.time * 1.5)
|
||||
end
|
||||
if burntime == nil or burntime == 0 then
|
||||
burntime = 60
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
burntime = burntime,
|
||||
recipe = "mobs_mc:blaze_rod",
|
||||
})
|
||||
end
|
||||
|
||||
if c("blaze_powder") then
|
||||
minetest.register_craftitem("mobs_mc:blaze_powder", {
|
||||
description = "Blaze Powder",
|
||||
_doc_items_longdesc = "This item is mainly used for brewing potions and crafting.",
|
||||
wield_image = "mcl_mobitems_blaze_powder.png",
|
||||
inventory_image = "mcl_mobitems_blaze_powder.png",
|
||||
})
|
||||
end
|
||||
|
||||
if c("blaze_rod") and c("blaze_powder") then
|
||||
minetest.register_craft({
|
||||
output = "mobs_mc:blaze_powder 2",
|
||||
recipe = {{ "mobs_mc:blaze_rod" }},
|
||||
})
|
||||
end
|
||||
|
||||
-- Chicken
|
||||
if c("chicken_raw") then
|
||||
minetest.register_craftitem("mobs_mc:chicken_raw", {
|
||||
description = "Raw Chicken",
|
||||
_doc_items_longdesc = "Raw chicken is a food item and can be eaten safely. Cooking it will increase its nutritional value.",
|
||||
inventory_image = "mcl_mobitems_chicken_raw.png",
|
||||
groups = { food = 2, eatable = 2 },
|
||||
on_use = minetest.item_eat(2),
|
||||
})
|
||||
end
|
||||
|
||||
if c("chicken_cooked") then
|
||||
minetest.register_craftitem("mobs_mc:chicken_cooked", {
|
||||
description = "Cooked Chicken",
|
||||
_doc_items_longdesc = "A cooked chicken is a healthy food item which can be eaten.",
|
||||
inventory_image = "mcl_mobitems_chicken_cooked.png",
|
||||
groups = { food = 2, eatable = 6 },
|
||||
on_use = minetest.item_eat(6),
|
||||
})
|
||||
end
|
||||
|
||||
if c("chicken_raw") and c("chicken_cooked") then
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
output = "mobs_mc:chicken_cooked",
|
||||
recipe = "mobs_mc:chicken_raw",
|
||||
cooktime = 5,
|
||||
})
|
||||
end
|
||||
|
||||
if c("feather") then
|
||||
minetest.register_craftitem("mobs_mc:feather", {
|
||||
description = "Feather",
|
||||
_doc_items_longdesc = "Feathers are used in crafting and are dropped from chickens.",
|
||||
inventory_image = "mcl_mobitems_feather.png",
|
||||
})
|
||||
end
|
||||
|
||||
-- Cow and mooshroom
|
||||
if c("beef_raw") then
|
||||
minetest.register_craftitem("mobs_mc:beef_raw", {
|
||||
description = "Raw Beef",
|
||||
_doc_items_longdesc = "Raw beef is the flesh from cows and can be eaten safely. Cooking it will greatly increase its nutritional value.",
|
||||
inventory_image = "mcl_mobitems_beef_raw.png",
|
||||
groups = { food = 2, eatable = 3 },
|
||||
on_use = minetest.item_eat(3),
|
||||
})
|
||||
end
|
||||
|
||||
if c("beef_cooked") then
|
||||
minetest.register_craftitem("mobs_mc:beef_cooked", {
|
||||
description = "Steak",
|
||||
_doc_items_longdesc = "Steak is cooked beef from cows and can be eaten.",
|
||||
inventory_image = "mcl_mobitems_beef_cooked.png",
|
||||
groups = { food = 2, eatable = 8 },
|
||||
on_use = minetest.item_eat(8),
|
||||
})
|
||||
end
|
||||
|
||||
if c("beef_raw") and c("beef_cooked") then
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
output = "mobs_mc:beef_cooked",
|
||||
recipe = "mobs_mc:beef_raw",
|
||||
cooktime = 5,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
if c("milk") then
|
||||
-- milk
|
||||
minetest.register_craftitem("mobs_mc:milk_bucket", {
|
||||
description = "Milk",
|
||||
_doc_items_longdesc = "Milk is a food item obtained by using a bucket on a cow.",
|
||||
inventory_image = "mobs_bucket_milk.png",
|
||||
groups = { food = 3, eatable = 1 },
|
||||
on_use = minetest.item_eat(1, "bucket:bucket_empty"),
|
||||
stack_max = 1,
|
||||
})
|
||||
end
|
||||
|
||||
if c("bowl") then
|
||||
minetest.register_craftitem("mobs_mc:bowl", {
|
||||
description = "Bowl",
|
||||
_doc_items_longdesc = "Bowls are mainly used to hold tasty soups.",
|
||||
inventory_image = "mcl_core_bowl.png",
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "mobs_mc:bowl",
|
||||
recipe = {
|
||||
{ "group:wood", "", "group:wood" },
|
||||
{ "", "group:wood", "", },
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "mobs_mc:bowl",
|
||||
burntime = 5,
|
||||
})
|
||||
end
|
||||
|
||||
if c("mushroom_stew") then
|
||||
minetest.register_craftitem("mobs_mc:mushroom_stew", {
|
||||
description = "Mushroom Stew",
|
||||
_doc_items_longdesc = "Mushroom stew is a healthy soup.",
|
||||
inventory_image = "farming_mushroom_stew.png",
|
||||
groups = { food = 3, eatable = 6 },
|
||||
on_use = minetest.item_eat(6, "mobs_mc:bowl"),
|
||||
stack_max = 1,
|
||||
})
|
||||
end
|
||||
|
||||
-- Ender dragon
|
||||
if c("dragon_egg") then
|
||||
|
||||
local dragon_egg_sounds
|
||||
if minetest.get_modpath("default") then
|
||||
dragon_egg_sounds = default.node_sound_stone_defaults()
|
||||
end
|
||||
|
||||
--ender dragon
|
||||
minetest.register_node("mobs_mc:dragon_egg", {
|
||||
description = "Dragon Egg",
|
||||
tiles = {
|
||||
"mcl_end_dragon_egg.png",
|
||||
"mcl_end_dragon_egg.png",
|
||||
"mcl_end_dragon_egg.png",
|
||||
"mcl_end_dragon_egg.png",
|
||||
"mcl_end_dragon_egg.png",
|
||||
"mcl_end_dragon_egg.png",
|
||||
},
|
||||
drawtype = "nodebox",
|
||||
is_ground_content = false,
|
||||
paramtype = "light",
|
||||
light_source = 1,
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.375, -0.5, -0.375, 0.375, -0.4375, 0.375},
|
||||
{-0.5, -0.4375, -0.5, 0.5, -0.1875, 0.5},
|
||||
{-0.4375, -0.1875, -0.4375, 0.4375, 0, 0.4375},
|
||||
{-0.375, 0, -0.375, 0.375, 0.125, 0.375},
|
||||
{-0.3125, 0.125, -0.3125, 0.3125, 0.25, 0.3125},
|
||||
{-0.25, 0.25, -0.25, 0.25, 0.3125, 0.25},
|
||||
{-0.1875, 0.3125, -0.1875, 0.1875, 0.375, 0.1875},
|
||||
{-0.125, 0.375, -0.125, 0.125, 0.4375, 0.125},
|
||||
{-0.0625, 0.4375, -0.0625, 0.0625, 0.5, 0.0625},
|
||||
}
|
||||
},
|
||||
selection_box = {
|
||||
type = "regular",
|
||||
},
|
||||
groups = {snappy = 1, falling_node = 1, deco_block = 1, not_in_creative_inventory = 1, dig_by_piston = 1 },
|
||||
sounds = dragon_egg_sounds,
|
||||
-- TODO: Make dragon egg teleport on punching
|
||||
})
|
||||
end
|
||||
|
||||
local longdesc_craftitem
|
||||
if minetest.get_modpath("doc_items") then
|
||||
longdesc_craftitem = doc.sub.items.temp.craftitem
|
||||
end
|
||||
|
||||
-- Enderman
|
||||
if c("ender_eye") then
|
||||
minetest.register_craftitem("mobs_mc:ender_eye", {
|
||||
description = "Eye of Ender",
|
||||
_doc_items_longdesc = longdesc_craftitem,
|
||||
inventory_image = "mcl_end_ender_eye.png",
|
||||
groups = { craftitem = 1 },
|
||||
})
|
||||
end
|
||||
|
||||
if c("ender_eye") and c("blaze_powder") and c("blaze_rod") then
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = 'mobs_mc:ender_eye',
|
||||
recipe = { 'mobs_mc:blaze_powder', 'mobs_mc:blaze_rod'},
|
||||
})
|
||||
end
|
||||
|
||||
-- Ghast
|
||||
if c("ghast_tear") then
|
||||
minetest.register_craftitem("mobs_mc:ghast_tear", {
|
||||
description = "Ghast Tear",
|
||||
_doc_items_longdesc = "A ghast tear is an item used in potion brewing. It is dropped from dead ghasts.",
|
||||
wield_image = "mcl_mobitems_ghast_tear.png",
|
||||
inventory_image = "mcl_mobitems_ghast_tear.png",
|
||||
groups = { brewitem = 1 },
|
||||
})
|
||||
end
|
||||
|
||||
-- Saddle
|
||||
if c("saddle") then
|
||||
-- Overwrite the saddle from Mobs Redo
|
||||
minetest.register_craftitem(":mobs:saddle", {
|
||||
description = "Saddle",
|
||||
_doc_items_longdesc = "Saddles can be put on horses, donkeys, mules and pigs in order to mount them.",
|
||||
_doc_items_usagehelp = "Rightclick an animal while holding a saddle to put on the saddle. You can now mount the animal by rightclicking it again.",
|
||||
inventory_image = "mcl_mobitems_saddle.png",
|
||||
stack_max = 1,
|
||||
})
|
||||
end
|
||||
|
||||
-- Horse Armor
|
||||
local horse_armor_use = S("Place it on a horse to put on the horse armor. Donkeys and mules can't wear horse armor.")
|
||||
-- TODO: Balance the horse armor strength, compare with MC armor strength
|
||||
if c("iron_horse_armor") then
|
||||
minetest.register_craftitem("mobs_mc:iron_horse_armor", {
|
||||
description = S("Iron Horse Armor"),
|
||||
_doc_items_longdesc = S("Iron horse armor can be worn by horses to increase their protection from harm a bit."),
|
||||
_doc_items_usagehelp = horse_armor_use,
|
||||
inventory_image = "mobs_mc_iron_horse_armor.png",
|
||||
_horse_overlay_image = "mobs_mc_horse_armor_iron.png",
|
||||
sounds = {
|
||||
_mcl_armor_equip = "mcl_armor_equip_iron",
|
||||
},
|
||||
stack_max = 1,
|
||||
groups = { horse_armor = 85 },
|
||||
})
|
||||
end
|
||||
if c("gold_horse_armor") then
|
||||
minetest.register_craftitem("mobs_mc:gold_horse_armor", {
|
||||
description = S("Golden Horse Armor"),
|
||||
_doc_items_longdesc = S("Golden horse armor can be worn by horses to increase their protection from harm."),
|
||||
_doc_items_usagehelp = horse_armor_use,
|
||||
inventory_image = "mobs_mc_gold_horse_armor.png",
|
||||
_horse_overlay_image = "mobs_mc_horse_armor_gold.png",
|
||||
sounds = {
|
||||
_mcl_armor_equip = "mcl_armor_equip_iron",
|
||||
},
|
||||
stack_max = 1,
|
||||
groups = { horse_armor = 60 },
|
||||
})
|
||||
end
|
||||
if c("diamond_horse_armor") then
|
||||
minetest.register_craftitem("mobs_mc:diamond_horse_armor", {
|
||||
description = S("Diamond Horse Armor"),
|
||||
_doc_items_longdesc = S("Diamond horse armor can be worn by horses to greatly increase their protection from harm."),
|
||||
_doc_items_usagehelp = horse_armor_use,
|
||||
inventory_image = "mobs_mc_diamond_horse_armor.png",
|
||||
_horse_overlay_image = "mobs_mc_horse_armor_diamond.png",
|
||||
sounds = {
|
||||
_mcl_armor_equip = "mcl_armor_equip_diamond",
|
||||
},
|
||||
stack_max = 1,
|
||||
groups = { horse_armor = 45 },
|
||||
})
|
||||
end
|
||||
|
||||
-- Pig
|
||||
if c("porkchop_raw") then
|
||||
minetest.register_craftitem("mobs_mc:porkchop_raw", {
|
||||
description = "Raw Porkchop",
|
||||
_doc_items_longdesc = "A raw porkchop is the flesh from a pig and can be eaten safely. Cooking it will greatly increase its nutritional value.",
|
||||
inventory_image = "mcl_mobitems_porkchop_raw.png",
|
||||
groups = { food = 2, eatable = 3 },
|
||||
on_use = minetest.item_eat(3),
|
||||
})
|
||||
end
|
||||
|
||||
if c("porkchop_cooked") then
|
||||
minetest.register_craftitem("mobs_mc:porkchop_cooked", {
|
||||
description = "Cooked Porkchop",
|
||||
_doc_items_longdesc = "Cooked porkchop is the cooked flesh of a pig and is used as food.",
|
||||
inventory_image = "mcl_mobitems_porkchop_cooked.png",
|
||||
groups = { food = 2, eatable = 8 },
|
||||
on_use = minetest.item_eat(8),
|
||||
})
|
||||
end
|
||||
|
||||
if c("porkchop_raw") and c("porkchop_cooked") then
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
output = "mobs_mc:porkchop_cooked",
|
||||
recipe = "mobs_mc:porkchop_raw",
|
||||
cooktime = 5,
|
||||
})
|
||||
end
|
||||
|
||||
if c("carrot_on_a_stick") then
|
||||
minetest.register_tool("mobs_mc:carrot_on_a_stick", {
|
||||
description = "Carrot on a Stick",
|
||||
_doc_items_longdesc = "A carrot on a stick can be used on saddled pigs to ride them. Pigs will also follow anyone who holds a carrot on a stick near them.",
|
||||
_doc_items_usagehelp = "Rightclick a saddled pig with the carrot on a stick to mount it. You can now ride it like a horse.",
|
||||
wield_image = "mcl_mobitems_carrot_on_a_stick.png",
|
||||
inventory_image = "mcl_mobitems_carrot_on_a_stick.png",
|
||||
sounds = { breaks = "default_tool_breaks" },
|
||||
})
|
||||
end
|
||||
|
||||
-- Poor-man's recipes for carrot on a stick
|
||||
if c("carrot_on_a_stick") and c("stick") and c("string") and minetest.get_modpath("farming") then
|
||||
minetest.register_craft({
|
||||
output = "mobs_mc:carrot_on_a_stick",
|
||||
recipe = {
|
||||
{"", "", "farming:string" },
|
||||
{"", "group:stick", "farming:string" },
|
||||
{"group:stick", "", "farming:bread" },
|
||||
}
|
||||
})
|
||||
|
||||
-- FIXME: Identify correct farming mod (check if it includes the carrot item)
|
||||
minetest.register_craft({
|
||||
output = "mobs_mc:carrot_on_a_stick",
|
||||
recipe = {
|
||||
{"", "", "farming:string" },
|
||||
{"", "group:stick", "farming:string" },
|
||||
{"group:stick", "", "farming:carrot" },
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
if c("carrot_on_a_stick") and c("stick") and c("string") and minetest.get_modpath("fishing") and minetest.get_modpath("farming") then
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "mobs_mc:carrot_on_a_stick",
|
||||
recipe = {"fishing:pole_wood", "farming:carrot"},
|
||||
})
|
||||
end
|
||||
|
||||
-- Rabbit
|
||||
if c("rabbit_raw") then
|
||||
minetest.register_craftitem("mobs_mc:rabbit_raw", {
|
||||
description = "Raw Rabbit",
|
||||
_doc_items_longdesc = "Raw rabbit is a food item from a dead rabbit. It can be eaten safely. Cooking it will increase its nutritional value.",
|
||||
inventory_image = "mcl_mobitems_rabbit_raw.png",
|
||||
groups = { food = 2, eatable = 3 },
|
||||
on_use = minetest.item_eat(3),
|
||||
})
|
||||
end
|
||||
|
||||
if c("rabbit_cooked") then
|
||||
minetest.register_craftitem("mobs_mc:rabbit_cooked", {
|
||||
description = "Cooked Rabbit",
|
||||
_doc_items_longdesc = "This is a food item which can be eaten.",
|
||||
inventory_image = "mcl_mobitems_rabbit_cooked.png",
|
||||
groups = { food = 2, eatable = 5 },
|
||||
on_use = minetest.item_eat(5),
|
||||
})
|
||||
end
|
||||
|
||||
if c("rabbit_raw") and c("rabbit_cooked") then
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
output = "mobs_mc:rabbit_cooked",
|
||||
recipe = "mobs_mc:rabbit_raw",
|
||||
cooktime = 5,
|
||||
})
|
||||
end
|
||||
|
||||
if c("rabbit_hide") then
|
||||
minetest.register_craftitem("mobs_mc:rabbit_hide", {
|
||||
description = "Rabbit Hide",
|
||||
_doc_items_longdesc = "Rabbit hide is used to create leather.",
|
||||
inventory_image = "mcl_mobitems_rabbit_hide.png"
|
||||
})
|
||||
end
|
||||
|
||||
if c("leather") and c("rabbit_hide") then
|
||||
minetest.register_craft({
|
||||
output = "mobs:leather",
|
||||
recipe = {
|
||||
{ "mobs_mc:rabbit_hide", "mobs_mc:rabbit_hide" },
|
||||
{ "mobs_mc:rabbit_hide", "mobs_mc:rabbit_hide" },
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
if c("rabbit_foot") then
|
||||
minetest.register_craftitem("mobs_mc:rabbit_foot", {
|
||||
description = "Rabbit's Foot",
|
||||
_doc_items_longdesc = "This item is used in brewing.",
|
||||
inventory_image = "mcl_mobitems_rabbit_foot.png"
|
||||
})
|
||||
end
|
||||
|
||||
-- Sheep
|
||||
if c("mutton_raw") then
|
||||
minetest.register_craftitem("mobs_mc:mutton_raw", {
|
||||
description = "Raw Mutton",
|
||||
_doc_items_longdesc = "Raw mutton is the flesh from a sheep and can be eaten safely. Cooking it will greatly increase its nutritional value.",
|
||||
inventory_image = "mcl_mobitems_mutton_raw.png",
|
||||
groups = { food = 2, eatable = 4 },
|
||||
on_use = minetest.item_eat(4),
|
||||
})
|
||||
end
|
||||
|
||||
if c("mutton_cooked") then
|
||||
minetest.register_craftitem("mobs_mc:mutton_cooked", {
|
||||
description = "Cooked Mutton",
|
||||
_doc_items_longdesc = "Cooked mutton is the cooked flesh from a sheep and is used as food.",
|
||||
inventory_image = "mcl_mobitems_mutton_cooked.png",
|
||||
groups = { food = 2, eatable = 8 },
|
||||
on_use = minetest.item_eat(8),
|
||||
})
|
||||
end
|
||||
|
||||
if c("mutton_raw") and c("mutton_cooked") then
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
output = "mobs_mc:mutton_cooked",
|
||||
recipe = "mobs_mc:mutton_raw",
|
||||
cooktime = 5,
|
||||
})
|
||||
end
|
||||
|
||||
-- Shulker
|
||||
if c("shulker_shell") then
|
||||
minetest.register_craftitem("mobs_mc:shulker_shell", {
|
||||
description = "Shulker Shell",
|
||||
_doc_items_longdesc = "Shulker shells are used in crafting. They are dropped from dead shulkers.",
|
||||
inventory_image = "mcl_mobitems_shulker_shell.png",
|
||||
groups = { craftitem = 1 },
|
||||
})
|
||||
end
|
||||
|
||||
-- Magma cube
|
||||
if c("magma_cream") then
|
||||
minetest.register_craftitem("mobs_mc:magma_cream", {
|
||||
description = "Magma Cream",
|
||||
_doc_items_longdesc = "Magma cream is a crafting component.",
|
||||
wield_image = "mcl_mobitems_magma_cream.png",
|
||||
inventory_image = "mcl_mobitems_magma_cream.png",
|
||||
groups = { brewitem = 1 },
|
||||
})
|
||||
end
|
||||
|
||||
-- Slime
|
||||
if c("slimeball") then
|
||||
minetest.register_craftitem("mobs_mc:slimeball", {
|
||||
description = "Slimeball",
|
||||
_doc_items_longdesc = "Slimeballs are used in crafting. They are dropped from slimes.",
|
||||
inventory_image = "mcl_mobitems_slimeball.png"
|
||||
})
|
||||
if minetest.get_modpath("mesecons_materials") then
|
||||
minetest.register_craft({
|
||||
output = "mesecons_materials:glue",
|
||||
recipe = {{ "mobs_mc:slimeball" }},
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- Spider
|
||||
if c("spider_eye") then
|
||||
minetest.register_craftitem("mobs_mc:spider_eye", {
|
||||
description = "Spider Eye",
|
||||
_doc_items_longdesc = "Spider eyes are used mainly in crafting and brewing. Spider eyes can be eaten, but they poison you and reduce your health by 2 hit points.",
|
||||
inventory_image = "mcl_mobitems_spider_eye.png",
|
||||
wield_image = "mcl_mobitems_spider_eye.png",
|
||||
-- Simplified poisonous food
|
||||
groups = { food = 2, eatable = -2 },
|
||||
on_use = minetest.item_eat(-2),
|
||||
})
|
||||
end
|
||||
|
||||
-- Evoker
|
||||
if c("totem") then
|
||||
-- Totem of Undying
|
||||
minetest.register_craftitem("mobs_mc:totem", {
|
||||
description = S("Totem of Undying"),
|
||||
_tt_help = minetest.colorize(mcl_colors.GREEN, S("Protects you from death while wielding it")),
|
||||
_doc_items_longdesc = S("A totem of undying is a rare artifact which may safe you from certain death."),
|
||||
_doc_items_usagehelp = S("The totem only works while you hold it in your hand. If you receive fatal damage, you are saved from death and you get a second chance with 1 HP. The totem is destroyed in the process, however."),
|
||||
inventory_image = "mcl_totems_totem.png",
|
||||
wield_image = "mcl_totems_totem.png",
|
||||
stack_max = 1,
|
||||
groups = {combat_item=1},
|
||||
})
|
||||
end
|
||||
|
||||
-- Rotten flesh
|
||||
if c("rotten_flesh") then
|
||||
minetest.register_craftitem("mobs_mc:rotten_flesh", {
|
||||
description = "Rotten Flesh",
|
||||
_doc_items_longdesc = "Yuck! This piece of flesh clearly has seen better days. Eating it will only poison you and reduces your health by 4 hit points. But tamed wolves can eat it just fine.",
|
||||
inventory_image = "mcl_mobitems_rotten_flesh.png",
|
||||
-- Simplified poisonous food
|
||||
groups = { food = 2, eatable = -4 },
|
||||
on_use = minetest.item_eat(-4),
|
||||
})
|
||||
end
|
||||
|
||||
-- Misc.
|
||||
if c("nether_star") then
|
||||
minetest.register_craftitem("mobs_mc:nether_star", {
|
||||
description = "Nether Star",
|
||||
_doc_items_longdesc = "A nether star is a crafting component. It is dropped from the Wither.",
|
||||
inventory_image = "mcl_mobitems_nether_star.png"
|
||||
})
|
||||
end
|
||||
|
||||
if c("snowball") and minetest.get_modpath("default") then
|
||||
minetest.register_craft({
|
||||
output = "mobs_mc:snowball 2",
|
||||
recipe = {
|
||||
{"default:snow"},
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = "default:snow 2",
|
||||
recipe = {
|
||||
{"mobs_mc:snowball", "mobs_mc:snowball"},
|
||||
{"mobs_mc:snowball", "mobs_mc:snowball"},
|
||||
},
|
||||
})
|
||||
-- Change the appearance of default snow to avoid confusion with snowball
|
||||
minetest.override_item("default:snow", {
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
})
|
||||
end
|
||||
|
||||
if c("bone") then
|
||||
minetest.register_craftitem("mobs_mc:bone", {
|
||||
description = "Bone",
|
||||
_doc_items_longdesc = "Bones can be used to tame wolves so they will protect you. They are also useful as a crafting ingredient.",
|
||||
_doc_items_usagehelp = "Hold the bone in your hand near wolves to attract them. Rightclick the wolf to give it a bone and tame it.",
|
||||
inventory_image = "mcl_mobitems_bone.png"
|
||||
})
|
||||
if minetest.get_modpath("bones") then
|
||||
minetest.register_craft({
|
||||
output = "mobs_mc:bone 3",
|
||||
recipe = {{ "bones:bones" }},
|
||||
})
|
||||
end
|
||||
end
|
|
@ -0,0 +1,402 @@
|
|||
--MCmobs v0.5
|
||||
--maikerumine
|
||||
--made for MC like Survival game
|
||||
--License for code WTFPL and otherwise stated in readmes
|
||||
|
||||
-- NOTE: Strings intentionally not marked for translation, other mods already have these items.
|
||||
-- TODO: Remove this file eventually, all items here are already outsourced in other mods.
|
||||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
--maikerumines throwing code
|
||||
--arrow (weapon)
|
||||
|
||||
local c = mobs_mc.is_item_variable_overridden
|
||||
|
||||
minetest.register_node("mobs_mc:arrow_box", {
|
||||
drawtype = "nodebox",
|
||||
is_ground_content = false,
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
-- Shaft
|
||||
{-6.5/17, -1.5/17, -1.5/17, -4.5/17, 1.5/17, 1.5/17},
|
||||
{-4.5/17, -0.5/17, -0.5/17, 5.5/17, 0.5/17, 0.5/17},
|
||||
{5.5/17, -1.5/17, -1.5/17, 6.5/17, 1.5/17, 1.5/17},
|
||||
-- Tip
|
||||
{-4.5/17, 2.5/17, 2.5/17, -3.5/17, -2.5/17, -2.5/17},
|
||||
{-8.5/17, 0.5/17, 0.5/17, -6.5/17, -0.5/17, -0.5/17},
|
||||
-- Fletching
|
||||
{6.5/17, 1.5/17, 1.5/17, 7.5/17, 2.5/17, 2.5/17},
|
||||
{7.5/17, -2.5/17, 2.5/17, 6.5/17, -1.5/17, 1.5/17},
|
||||
{7.5/17, 2.5/17, -2.5/17, 6.5/17, 1.5/17, -1.5/17},
|
||||
{6.5/17, -1.5/17, -1.5/17, 7.5/17, -2.5/17, -2.5/17},
|
||||
|
||||
{7.5/17, 2.5/17, 2.5/17, 8.5/17, 3.5/17, 3.5/17},
|
||||
{8.5/17, -3.5/17, 3.5/17, 7.5/17, -2.5/17, 2.5/17},
|
||||
{8.5/17, 3.5/17, -3.5/17, 7.5/17, 2.5/17, -2.5/17},
|
||||
{7.5/17, -2.5/17, -2.5/17, 8.5/17, -3.5/17, -3.5/17},
|
||||
}
|
||||
},
|
||||
tiles = {"mcl_bows_arrow.png^[transformFX", "mcl_bows_arrow.png^[transformFX", "mcl_bows_arrow_back.png", "mcl_bows_arrow_front.png", "mcl_bows_arrow.png", "mcl_bows_arrow.png^[transformFX"},
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false,
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
sunlight_propagates = true,
|
||||
groups = {not_in_creative_inventory=1, dig_immediate=3},
|
||||
node_placement_prediction = "",
|
||||
on_construct = function(pos)
|
||||
minetest.log("error", "[mobs_mc] Trying to construct mobs_mc:arrow_box at "..minetest.pos_to_string(pos))
|
||||
minetest.remove_node(pos)
|
||||
end,
|
||||
drop = "",
|
||||
})
|
||||
|
||||
local THROWING_ARROW_ENTITY={
|
||||
physical = false,
|
||||
timer=0,
|
||||
visual = "wielditem",
|
||||
visual_size = {x=0.1, y=0.1},
|
||||
textures = {"mobs_mc:arrow_box"},
|
||||
velocity = 10,
|
||||
lastpos={},
|
||||
collisionbox = {0,0,0,0,0,0},
|
||||
}
|
||||
|
||||
--ARROW CODE
|
||||
THROWING_ARROW_ENTITY.on_step = function(self, dtime)
|
||||
self.timer=self.timer+dtime
|
||||
local pos = self.object:get_pos()
|
||||
local node = minetest.get_node(pos)
|
||||
|
||||
minetest.add_particle({
|
||||
pos = pos,
|
||||
velocity = {x=0, y=0, z=0},
|
||||
acceleration = {x=0, y=0, z=0},
|
||||
expirationtime = .3,
|
||||
size = 1,
|
||||
collisiondetection = false,
|
||||
vertical = false,
|
||||
texture = "mobs_mc_arrow_particle.png",
|
||||
})
|
||||
|
||||
if self.timer>0.2 then
|
||||
local objs = minetest.get_objects_inside_radius({x=pos.x,y=pos.y,z=pos.z}, 1.5)
|
||||
for k, obj in pairs(objs) do
|
||||
if obj:get_luaentity() ~= nil then
|
||||
if obj:get_luaentity().name ~= "mobs_mc:arrow_entity" and obj:get_luaentity().name ~= "__builtin:item" then
|
||||
local damage = 3
|
||||
minetest.sound_play("damage", {pos = pos}, true)
|
||||
obj:punch(self.object, 1.0, {
|
||||
full_punch_interval=1.0,
|
||||
damage_groups={fleshy=damage},
|
||||
}, nil)
|
||||
self.object:remove()
|
||||
end
|
||||
else
|
||||
local damage = 3
|
||||
minetest.sound_play("damage", {pos = pos}, true)
|
||||
obj:punch(self.object, 1.0, {
|
||||
full_punch_interval=1.0,
|
||||
damage_groups={fleshy=damage},
|
||||
}, nil)
|
||||
self.object:remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.lastpos.x~=nil then
|
||||
if node.name ~= "air" then
|
||||
minetest.sound_play("bowhit1", {pos = pos}, true)
|
||||
minetest.add_item(self.lastpos, 'mobs_mc:arrow')
|
||||
self.object:remove()
|
||||
end
|
||||
end
|
||||
self.lastpos={x=pos.x, y=pos.y, z=pos.z}
|
||||
end
|
||||
|
||||
minetest.register_entity("mobs_mc:arrow_entity", THROWING_ARROW_ENTITY)
|
||||
|
||||
local arrows = {
|
||||
{"mobs_mc:arrow", "mobs_mc:arrow_entity" },
|
||||
}
|
||||
|
||||
local throwing_shoot_arrow = function(itemstack, player)
|
||||
for _,arrow in pairs(arrows) do
|
||||
if player:get_inventory():get_stack("main", player:get_wield_index()+1):get_name() == arrow[1] then
|
||||
if not minetest.is_creative_enabled(player:get_player_name()) then
|
||||
player:get_inventory():remove_item("main", arrow[1])
|
||||
end
|
||||
local playerpos = player:get_pos()
|
||||
local obj = minetest.add_entity({x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, arrow[2]) --mc
|
||||
local dir = player:get_look_dir()
|
||||
obj:set_velocity({x=dir.x*22, y=dir.y*22, z=dir.z*22})
|
||||
obj:set_acceleration({x=dir.x*-3, y=-10, z=dir.z*-3})
|
||||
obj:set_yaw(player:get_look_yaw()+math.pi)
|
||||
minetest.sound_play("throwing_sound", {pos=playerpos}, true)
|
||||
if obj:get_luaentity().player == "" then
|
||||
obj:get_luaentity().player = player
|
||||
end
|
||||
obj:get_luaentity().node = player:get_inventory():get_stack("main", 1):get_name()
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
if c("arrow") then
|
||||
minetest.register_craftitem("mobs_mc:arrow", {
|
||||
description = "Arrow",
|
||||
_doc_items_longdesc = "Arrows are ammunition for bows.",
|
||||
_doc_items_usagehelp = "To use arrows as ammunition for a bow, put them in the inventory slot following the bow. Slots are counted left to right, top to bottom.",
|
||||
inventory_image = "mcl_bows_arrow_inv.png",
|
||||
})
|
||||
end
|
||||
|
||||
if c("arrow") and c("flint") and c("feather") and c("stick") then
|
||||
minetest.register_craft({
|
||||
output = 'mobs_mc:arrow 4',
|
||||
recipe = {
|
||||
{mobs_mc.items.flint},
|
||||
{mobs_mc.items.stick},
|
||||
{mobs_mc.items.feather},
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
if c("bow") then
|
||||
minetest.register_tool("mobs_mc:bow_wood", {
|
||||
description = "Bow",
|
||||
_doc_items_longdesc = "Bows are ranged weapons to shoot arrows at your foes.",
|
||||
_doc_items_usagehelp = "To use the bow, you first need to have at least one arrow in slot following the bow. Leftclick to shoot. Each hit deals 3 damage.",
|
||||
inventory_image = "mcl_bows_bow.png",
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if throwing_shoot_arrow(itemstack, user, pointed_thing) then
|
||||
if not minetest.is_creative_enabled(user:get_player_name()) then
|
||||
itemstack:add_wear(65535/50)
|
||||
end
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'mobs_mc:bow_wood',
|
||||
recipe = {
|
||||
{mobs_mc.items.string, mobs_mc.items.stick, ''},
|
||||
{mobs_mc.items.string, '', mobs_mc.items.stick},
|
||||
{mobs_mc.items.string, mobs_mc.items.stick, ''},
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
local how_to_throw = "Hold it in your and and leftclick to throw."
|
||||
|
||||
-- egg throwing item
|
||||
-- egg entity
|
||||
if c("egg") then
|
||||
local egg_GRAVITY = 9
|
||||
local egg_VELOCITY = 19
|
||||
|
||||
mobs:register_arrow("mobs_mc:egg_entity", {
|
||||
visual = "sprite",
|
||||
visual_size = {x=.5, y=.5},
|
||||
textures = {"mobs_chicken_egg.png"},
|
||||
velocity = egg_VELOCITY,
|
||||
|
||||
hit_player = function(self, player)
|
||||
player:punch(minetest.get_player_by_name(self.playername) or self.object, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {},
|
||||
}, nil)
|
||||
end,
|
||||
|
||||
hit_mob = function(self, mob)
|
||||
mob:punch(minetest.get_player_by_name(self.playername) or self.object, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {},
|
||||
}, nil)
|
||||
end,
|
||||
|
||||
hit_node = function(self, pos, node)
|
||||
|
||||
if math.random(1, 10) > 1 then
|
||||
return
|
||||
end
|
||||
|
||||
pos.y = pos.y + 1
|
||||
|
||||
local nod = minetest.get_node_or_nil(pos)
|
||||
|
||||
if not nod
|
||||
or not minetest.registered_nodes[nod.name]
|
||||
or minetest.registered_nodes[nod.name].walkable == true then
|
||||
return
|
||||
end
|
||||
|
||||
local mob = minetest.add_entity(pos, "mobs_mc:chicken")
|
||||
local ent2 = mob:get_luaentity()
|
||||
|
||||
mob:set_properties({
|
||||
visual_size = {
|
||||
x = ent2.base_size.x / 2,
|
||||
y = ent2.base_size.y / 2
|
||||
},
|
||||
collisionbox = {
|
||||
ent2.base_colbox[1] / 2,
|
||||
ent2.base_colbox[2] / 2,
|
||||
ent2.base_colbox[3] / 2,
|
||||
ent2.base_colbox[4] / 2,
|
||||
ent2.base_colbox[5] / 2,
|
||||
ent2.base_colbox[6] / 2
|
||||
},
|
||||
})
|
||||
|
||||
ent2.child = true
|
||||
ent2.tamed = true
|
||||
ent2.owner = self.playername
|
||||
end
|
||||
})
|
||||
|
||||
-- shoot egg
|
||||
local mobs_shoot_egg = function (item, player, pointed_thing)
|
||||
|
||||
local playerpos = player:get_pos()
|
||||
|
||||
minetest.sound_play("default_place_node_hard", {
|
||||
pos = playerpos,
|
||||
gain = 1.0,
|
||||
max_hear_distance = 5,
|
||||
}, true)
|
||||
|
||||
local obj = minetest.add_entity({
|
||||
x = playerpos.x,
|
||||
y = playerpos.y +1.5,
|
||||
z = playerpos.z
|
||||
}, "mobs_mc:egg_entity")
|
||||
|
||||
local ent = obj:get_luaentity()
|
||||
local dir = player:get_look_dir()
|
||||
|
||||
ent.velocity = egg_VELOCITY -- needed for api internal timing
|
||||
ent.switch = 1 -- needed so that egg doesn't despawn straight away
|
||||
|
||||
obj:set_velocity({
|
||||
x = dir.x * egg_VELOCITY,
|
||||
y = dir.y * egg_VELOCITY,
|
||||
z = dir.z * egg_VELOCITY
|
||||
})
|
||||
|
||||
obj:set_acceleration({
|
||||
x = dir.x * -3,
|
||||
y = -egg_GRAVITY,
|
||||
z = dir.z * -3
|
||||
})
|
||||
|
||||
-- pass player name to egg for chick ownership
|
||||
local ent2 = obj:get_luaentity()
|
||||
ent2.playername = player:get_player_name()
|
||||
|
||||
if not minetest.is_creative_enabled(player:get_player_name()) then
|
||||
item:take_item()
|
||||
end
|
||||
|
||||
return item
|
||||
end
|
||||
|
||||
minetest.register_craftitem("mobs_mc:egg", {
|
||||
description = "Egg",
|
||||
_doc_items_longdesc = "Eggs can be thrown and break on impact. There is a small chance that 1 or even 4 chicks will pop out",
|
||||
_doc_items_usagehelp = how_to_throw,
|
||||
inventory_image = "mobs_chicken_egg.png",
|
||||
on_use = mobs_shoot_egg,
|
||||
})
|
||||
end
|
||||
|
||||
-- Snowball
|
||||
|
||||
local snowball_GRAVITY = 9
|
||||
local snowball_VELOCITY = 19
|
||||
|
||||
mobs:register_arrow("mobs_mc:snowball_entity", {
|
||||
visual = "sprite",
|
||||
visual_size = {x=.5, y=.5},
|
||||
textures = {"mcl_throwing_snowball.png"},
|
||||
velocity = snowball_VELOCITY,
|
||||
|
||||
hit_player = function(self, player)
|
||||
-- FIXME: No knockback
|
||||
player:punch(self.object, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {},
|
||||
}, nil)
|
||||
end,
|
||||
|
||||
hit_mob = function(self, mob)
|
||||
-- Hurt blazes, but not damage to anything else
|
||||
local dmg = {}
|
||||
if mob:get_luaentity().name == "mobs_mc:blaze" then
|
||||
dmg = {fleshy = 3}
|
||||
end
|
||||
-- FIXME: No knockback
|
||||
mob:punch(self.object, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = dmg,
|
||||
}, nil)
|
||||
end,
|
||||
|
||||
})
|
||||
|
||||
if c("snowball") then
|
||||
-- shoot snowball
|
||||
local mobs_shoot_snowball = function (item, player, pointed_thing)
|
||||
|
||||
local playerpos = player:get_pos()
|
||||
|
||||
local obj = minetest.add_entity({
|
||||
x = playerpos.x,
|
||||
y = playerpos.y +1.5,
|
||||
z = playerpos.z
|
||||
}, "mobs_mc:snowball_entity")
|
||||
|
||||
local ent = obj:get_luaentity()
|
||||
local dir = player:get_look_dir()
|
||||
|
||||
ent.velocity = snowball_VELOCITY -- needed for api internal timing
|
||||
ent.switch = 1 -- needed so that egg doesn't despawn straight away
|
||||
|
||||
obj:set_velocity({
|
||||
x = dir.x * snowball_VELOCITY,
|
||||
y = dir.y * snowball_VELOCITY,
|
||||
z = dir.z * snowball_VELOCITY
|
||||
})
|
||||
|
||||
obj:set_acceleration({
|
||||
x = dir.x * -3,
|
||||
y = -snowball_GRAVITY,
|
||||
z = dir.z * -3
|
||||
})
|
||||
|
||||
-- pass player name to egg for chick ownership
|
||||
local ent2 = obj:get_luaentity()
|
||||
ent2.playername = player:get_player_name()
|
||||
|
||||
if not minetest.is_creative_enabled(player:get_player_name()) then
|
||||
item:take_item()
|
||||
end
|
||||
|
||||
return item
|
||||
end
|
||||
|
||||
|
||||
-- Snowball
|
||||
minetest.register_craftitem("mobs_mc:snowball", {
|
||||
description = "Snowball",
|
||||
_doc_items_longdesc = "Snowballs can be thrown at your enemies. A snowball deals 3 damage to blazes, but is harmless to anything else.",
|
||||
_doc_items_usagehelp = how_to_throw,
|
||||
inventory_image = "mcl_throwing_snowball.png",
|
||||
on_use = mobs_shoot_snowball,
|
||||
})
|
||||
end
|
||||
|
||||
--end maikerumine code
|
|
@ -0,0 +1,65 @@
|
|||
local pr = PseudoRandom(os.time()*5)
|
||||
|
||||
local offsets = {}
|
||||
for x=-2, 2 do
|
||||
for z=-2, 2 do
|
||||
table.insert(offsets, {x=x, y=0, z=z})
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Periodically check and teleport mob to owner if not sitting (order ~= "sit") and
|
||||
the owner is too far away. To be used with do_custom. Note: Optimized for mobs smaller than 1×1×1.
|
||||
Larger mobs might have space problems after teleportation.
|
||||
|
||||
* dist: Minimum required distance from owner to teleport. Default: 12
|
||||
* teleport_check_interval: Optional. Interval in seconds to check the mob teleportation. Default: 4 ]]
|
||||
mobs_mc.make_owner_teleport_function = function(dist, teleport_check_interval)
|
||||
return function(self, dtime)
|
||||
-- No teleportation if no owner or if sitting
|
||||
if not self.owner or self.order == "sit" then
|
||||
return
|
||||
end
|
||||
if not teleport_check_interval then
|
||||
teleport_check_interval = 4
|
||||
end
|
||||
if not dist then
|
||||
dist = 12
|
||||
end
|
||||
if self._teleport_timer == nil then
|
||||
self._teleport_timer = teleport_check_interval
|
||||
return
|
||||
end
|
||||
self._teleport_timer = self._teleport_timer - dtime
|
||||
if self._teleport_timer <= 0 then
|
||||
self._teleport_timer = teleport_check_interval
|
||||
local mob_pos = self.object:get_pos()
|
||||
local owner = minetest.get_player_by_name(self.owner)
|
||||
if not owner then
|
||||
-- No owner found, no teleportation
|
||||
return
|
||||
end
|
||||
local owner_pos = owner:get_pos()
|
||||
local dist_from_owner = vector.distance(owner_pos, mob_pos)
|
||||
if dist_from_owner > dist then
|
||||
-- Check for nodes below air in a 5×1×5 area around the owner position
|
||||
local check_offsets = table.copy(offsets)
|
||||
-- Attempt to place mob near player. Must be placed on walkable node below a non-walkable one. Place inside that air node.
|
||||
while #check_offsets > 0 do
|
||||
local r = pr:next(1, #check_offsets)
|
||||
local telepos = vector.add(owner_pos, check_offsets[r])
|
||||
local telepos_below = {x=telepos.x, y=telepos.y-1, z=telepos.z}
|
||||
table.remove(check_offsets, r)
|
||||
-- Long story short, spawn on a platform
|
||||
local trynode = minetest.registered_nodes[minetest.get_node(telepos).name]
|
||||
local trybelownode = minetest.registered_nodes[minetest.get_node(telepos_below).name]
|
||||
if trynode and not trynode.walkable and
|
||||
trybelownode and trybelownode.walkable then
|
||||
-- Correct position found! Let's teleport.
|
||||
self.object:set_pos(telepos)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,62 @@
|
|||
--MC Heads for minetest
|
||||
--maikerumine
|
||||
|
||||
-- NOTE: Strings intentionally not marked for translation, other mods already have these items.
|
||||
-- TODO: Remove this file eventually, all items here are already outsourced in other mods.
|
||||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
-- Heads system
|
||||
|
||||
local sounds
|
||||
if minetest.get_modpath("default") then
|
||||
sounds = default.node_sound_defaults({
|
||||
footstep = {name="default_hard_footstep", gain=0.3}
|
||||
})
|
||||
end
|
||||
|
||||
local function addhead(mobname, desc, longdesc)
|
||||
if not mobs_mc.is_item_variable_overridden("head_"..mobname) then
|
||||
return
|
||||
end
|
||||
minetest.register_node("mobs_mc:head_"..mobname, {
|
||||
description = desc,
|
||||
_doc_items_longdesc = longdesc,
|
||||
drawtype = "nodebox",
|
||||
is_ground_content = false,
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{ -0.25, -0.5, -0.25, 0.25, 0.0, 0.25, },
|
||||
},
|
||||
},
|
||||
groups = { oddly_breakable_by_hand=3, head=1, },
|
||||
-- The head textures are based off the textures of an actual mob.
|
||||
-- FIXME: This code assumes 16×16 textures for the mob textures!
|
||||
tiles = {
|
||||
-- Note: bottom texture is overlaid over top texture to get rid of possible transparency.
|
||||
-- This is required for skeleton skull and wither skeleton skull.
|
||||
"[combine:16x16:-4,4=mobs_mc_"..mobname..".png", -- top
|
||||
"([combine:16x16:-4,4=mobs_mc_"..mobname..".png)^([combine:16x16:-12,4=mobs_mc_"..mobname..".png)", -- bottom
|
||||
"[combine:16x16:-12,0=mobs_mc_"..mobname..".png", -- left
|
||||
"[combine:16x16:4,0=mobs_mc_"..mobname..".png", -- right
|
||||
"[combine:16x16:-20,0=mobs_mc_"..mobname..".png", -- back
|
||||
"[combine:16x16:-4,0=mobs_mc_"..mobname..".png", -- front
|
||||
},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
sunlight_propagates = true,
|
||||
walkable = true,
|
||||
sounds = sounds,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = { -0.25, -0.5, -0.25, 0.25, 0.0, 0.25, },
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
-- Add heads
|
||||
addhead("zombie", "Zombie Head", "A zombie head is a small decorative block which resembles the head of a zombie.")
|
||||
addhead("creeper", "Creeper Head", "A creeper head is a small decorative block which resembles the head of a creeper.")
|
||||
addhead("skeleton", "Skeleton Skull", "A skeleton skull is a small decorative block which resembles the skull of a skeleton.")
|
||||
addhead("wither_skeleton", "Wither Skeleton Skull", "A wither skeleton skull is a small decorative block which resembles the skull of a wither skeleton.")
|
|
@ -0,0 +1,20 @@
|
|||
local function is_forbidden_node(pos, node)
|
||||
node = node or minetest.get_node(pos)
|
||||
return minetest.get_item_group(node.name, "stair") > 0 or minetest.get_item_group(node.name, "slab") > 0 or minetest.get_item_group(node.name, "carpet") > 0
|
||||
end
|
||||
|
||||
function mobs:spawn_abm_check(pos, node, name)
|
||||
-- Don't spawn monsters on mycelium
|
||||
if (node.name == "mcl_core:mycelium" or node.name == "mcl_core:mycelium_snow") and minetest.registered_entities[name].type == "monster" then
|
||||
return true
|
||||
--Don't Spawn mobs on stairs, slabs, or carpets
|
||||
elseif is_forbidden_node(pos, node) or is_forbidden_node(vector.add(pos, vector.new(0, 1, 0))) then
|
||||
return true
|
||||
-- Spawn on opaque or liquid nodes
|
||||
elseif minetest.get_item_group(node.name, "opaque") ~= 0 or minetest.registered_nodes[node.name].liquidtype ~= "none" or node.name == "mcl_core:grass_path" then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Reject everything else
|
||||
return true
|
||||
end
|
|
@ -75,7 +75,6 @@ Origin of those models:
|
|||
* `mobs_mc_mushroom_brown.png` (CC0)
|
||||
|
||||
* “Spawn egg” textures (`mobs_mc_spawn_icon_*`) by 22i
|
||||
* Llama decor (carpet) textures (`mobs_mc_llama_decor_*`) by erlehmann and rudzik8
|
||||
* Any other texture not mentioned here are licensed under the MIT License
|
||||
|
||||
## Sounds
|
||||
|
|
|
@ -17,6 +17,11 @@ This mod adds mobs which closely resemble the mobs from the game Minecraft, vers
|
|||
* Code: GNU General Public License, version 3 (see `LICENSE`)
|
||||
* Media: MIT, CC0, CC BY 3.0 CC BY-SA 4.0, LGPLv2.1, GPLv3. See `LICENSE_media.md` for details
|
||||
|
||||
## Useful information for developers
|
||||
|
||||
### Game integration
|
||||
Want to include this mod in your game? Read `gameconfig.md`.
|
||||
|
||||
### Links
|
||||
|
||||
* [`mobs_mc`](https://github.com/maikerumine/mobs_mc)
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
--###################
|
||||
--################### AGENT - seemingly unused
|
||||
--###################
|
||||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
mobs:register_mob("mobs_mc:agent", {
|
||||
type = "npc",
|
||||
spawn_class = "passive",
|
||||
passive = true,
|
||||
hp_min = 20,
|
||||
hp_max = 20,
|
||||
armor = 100,
|
||||
collisionbox = {-0.35, -0.01, -0.35, 0.35, 1, 0.35},
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_agent.b3d",
|
||||
textures = {
|
||||
{"mobs_mc_agent.png"},
|
||||
},
|
||||
-- TODO: sounds
|
||||
visual_size = {x=3, y=3},
|
||||
walk_chance = 0,
|
||||
walk_velocity = 0.6,
|
||||
run_velocity = 2,
|
||||
jump = true,
|
||||
animation = {
|
||||
stand_speed = 25,
|
||||
walk_speed = 25,
|
||||
run_speed = 50,
|
||||
stand_start = 20,
|
||||
stand_end = 60,
|
||||
walk_start = 0,
|
||||
walk_end = 20,
|
||||
run_start = 0,
|
||||
run_end = 20,
|
||||
},
|
||||
})
|
||||
|
||||
mobs:register_egg("mobs_mc:agent", S("Agent"), "mobs_mc_spawn_icon_agent.png", 0)
|
|
@ -2,12 +2,11 @@
|
|||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:bat", {
|
||||
mobs:register_mob("mobs_mc:bat", {
|
||||
description = S("Bat"),
|
||||
type = "animal",
|
||||
spawn_class = "ambient",
|
||||
can_despawn = true,
|
||||
spawn_in_group = 8,
|
||||
passive = true,
|
||||
hp_min = 6,
|
||||
hp_max = 6,
|
||||
|
@ -66,7 +65,7 @@ else
|
|||
end
|
||||
|
||||
-- Spawn on solid blocks at or below Sea level and the selected light level
|
||||
mcl_mobs:spawn_specific(
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:bat",
|
||||
"overworld",
|
||||
"ground",
|
||||
|
@ -139,9 +138,9 @@ maxlight,
|
|||
20,
|
||||
5000,
|
||||
2,
|
||||
mcl_vars.mg_overworld_min,
|
||||
mobs_mc.water_level-1)
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.water-1)
|
||||
|
||||
|
||||
-- spawn eggs
|
||||
mcl_mobs:register_egg("mobs_mc:bat", S("Bat"), "#4c3e30", "#0f0f0f", 0)
|
||||
mobs:register_egg("mobs_mc:bat", S("Bat"), "mobs_mc_spawn_icon_bat.png", 0)
|
||||
|
|
|
@ -12,12 +12,10 @@ local mod_target = minetest.get_modpath("mcl_target")
|
|||
--###################
|
||||
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:blaze", {
|
||||
mobs:register_mob("mobs_mc:blaze", {
|
||||
description = S("Blaze"),
|
||||
type = "monster",
|
||||
spawn_class = "hostile",
|
||||
spawn_in_group_min = 2,
|
||||
spawn_in_group = 3,
|
||||
hp_min = 20,
|
||||
hp_max = 20,
|
||||
xp_min = 10,
|
||||
|
@ -26,12 +24,6 @@ mcl_mobs:register_mob("mobs_mc:blaze", {
|
|||
rotate = -180,
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_blaze.b3d",
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 4,
|
||||
head_eye_height = 3.5,
|
||||
curiosity = 10,
|
||||
head_yaw_offset = 180,
|
||||
head_pitch_multiplier=-1,
|
||||
textures = {
|
||||
{"mobs_mc_blaze.png"},
|
||||
},
|
||||
|
@ -49,7 +41,7 @@ mcl_mobs:register_mob("mobs_mc:blaze", {
|
|||
reach = 2,
|
||||
pathfinding = 1,
|
||||
drops = {
|
||||
{name = "mcl_mobitems:blaze_rod",
|
||||
{name = mobs_mc.items.blaze_rod,
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 1,
|
||||
|
@ -139,7 +131,7 @@ mcl_mobs:register_mob("mobs_mc:blaze", {
|
|||
end,
|
||||
})
|
||||
|
||||
mcl_mobs:spawn_specific(
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:blaze",
|
||||
"nether",
|
||||
"ground",
|
||||
|
@ -149,11 +141,11 @@ minetest.LIGHT_MAX+1,
|
|||
30,
|
||||
5000,
|
||||
3,
|
||||
mcl_vars.mg_nether_min,
|
||||
mcl_vars.mg_nether_max)
|
||||
mobs_mc.spawn_height.nether_min,
|
||||
mobs_mc.spawn_height.nether_max)
|
||||
|
||||
-- Blaze fireball
|
||||
mcl_mobs:register_arrow("mobs_mc:blaze_fireball", {
|
||||
mobs:register_arrow("mobs_mc:blaze_fireball", {
|
||||
visual = "sprite",
|
||||
visual_size = {x = 0.3, y = 0.3},
|
||||
textures = {"mcl_fire_fire_charge.png"},
|
||||
|
@ -189,7 +181,7 @@ mcl_mobs:register_arrow("mobs_mc:blaze_fireball", {
|
|||
-- Node hit, make fire
|
||||
hit_node = function(self, pos, node)
|
||||
if node == "air" then
|
||||
minetest.set_node(pos, {name = "mcl_fire:fire"})
|
||||
minetest.set_node(pos, {name = mobs_mc.items.fire})
|
||||
else
|
||||
if self._shot_from_dispenser and mod_target and node == "mcl_target:target_off" then
|
||||
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
|
||||
|
@ -201,11 +193,11 @@ mcl_mobs:register_arrow("mobs_mc:blaze_fireball", {
|
|||
-- Set fire if node is air, or a replacable flammable node (e.g. a plant)
|
||||
if crashnode.name == "air" or
|
||||
(cndef and cndef.buildable_to and minetest.get_item_group(crashnode.name, "flammable") >= 1) then
|
||||
minetest.set_node(crashpos, {name = "mcl_fire:fire"})
|
||||
minetest.set_node(crashpos, {name = mobs_mc.items.fire})
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
-- spawn eggs
|
||||
mcl_mobs:register_egg("mobs_mc:blaze", S("Blaze"), "#f6b201", "#fff87e", 0)
|
||||
mobs:register_egg("mobs_mc:blaze", S("Blaze"), "mobs_mc_spawn_icon_blaze.png", 0)
|
||||
|
|
|
@ -8,7 +8,7 @@ local S = minetest.get_translator("mobs_mc")
|
|||
|
||||
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:chicken", {
|
||||
mobs:register_mob("mobs_mc:chicken", {
|
||||
description = S("Chicken"),
|
||||
type = "animal",
|
||||
spawn_class = "passive",
|
||||
|
@ -20,28 +20,22 @@ mcl_mobs:register_mob("mobs_mc:chicken", {
|
|||
collisionbox = {-0.2, -0.01, -0.2, 0.2, 0.69, 0.2},
|
||||
runaway = true,
|
||||
floats = 1,
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 4,
|
||||
head_eye_height = 1.5,
|
||||
horrizonatal_head_height = -.3,
|
||||
curiosity = 10,
|
||||
head_yaw="z",
|
||||
visual_size = {x=1,y=1},
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_chicken.b3d",
|
||||
textures = {
|
||||
{"mobs_mc_chicken.png"},
|
||||
},
|
||||
visual_size = {x=2.2, y=2.2},
|
||||
|
||||
makes_footstep_sound = true,
|
||||
walk_velocity = 1,
|
||||
drops = {
|
||||
{name = "mcl_mobitems:chicken",
|
||||
{name = mobs_mc.items.chicken_raw,
|
||||
chance = 1,
|
||||
min = 1,
|
||||
max = 1,
|
||||
looting = "common",},
|
||||
{name = "mcl_mobitems:feather",
|
||||
{name = mobs_mc.items.feather,
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,
|
||||
|
@ -64,28 +58,20 @@ mcl_mobs:register_mob("mobs_mc:chicken", {
|
|||
distance = 16,
|
||||
},
|
||||
animation = {
|
||||
stand_speed = 25, walk_speed = 25, run_speed = 50,
|
||||
stand_start = 0, stand_end = 0,
|
||||
walk_start = 0, walk_end = 20, walk_speed = 25,
|
||||
run_start = 0, run_end = 20, run_speed = 50,
|
||||
},
|
||||
child_animations = {
|
||||
stand_start = 31, stand_end = 31,
|
||||
walk_start = 31, walk_end = 51, walk_speed = 37,
|
||||
run_start = 31, run_end = 51, run_speed = 75,
|
||||
},
|
||||
follow = {
|
||||
"mcl_farming:wheat_seeds",
|
||||
"mcl_farming:melon_seeds",
|
||||
"mcl_farming:pumpkin_seeds",
|
||||
"mcl_farming:beetroot_seeds",
|
||||
walk_start = 0, walk_end = 40,
|
||||
run_start = 0, run_end = 40,
|
||||
},
|
||||
|
||||
follow = mobs_mc.follow.chicken,
|
||||
view_range = 16,
|
||||
fear_height = 4,
|
||||
|
||||
on_rightclick = function(self, clicker)
|
||||
if mcl_mobs:feed_tame(self, clicker, 1, true, false) then return end
|
||||
if mcl_mobs:protect(self, clicker) then return end
|
||||
if mcl_mobs:capture_mob(self, clicker, 0, 60, 5, false, nil) then return end
|
||||
if mobs:feed_tame(self, clicker, 1, true, true) then return end
|
||||
if mobs:protect(self, clicker) then return end
|
||||
if mobs:capture_mob(self, clicker, 0, 60, 5, false, nil) then return end
|
||||
end,
|
||||
|
||||
do_custom = function(self, dtime)
|
||||
|
@ -103,7 +89,7 @@ mcl_mobs:register_mob("mobs_mc:chicken", {
|
|||
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
minetest.add_item(pos, "mcl_throwing:egg")
|
||||
minetest.add_item(pos, mobs_mc.items.egg)
|
||||
|
||||
minetest.sound_play("mobs_mc_chicken_lay_egg", {
|
||||
pos = pos,
|
||||
|
@ -115,52 +101,34 @@ mcl_mobs:register_mob("mobs_mc:chicken", {
|
|||
})
|
||||
|
||||
--spawn
|
||||
mcl_mobs:spawn_specific(
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:chicken",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"flat",
|
||||
"IcePlainsSpikes",
|
||||
"ColdTaiga",
|
||||
"ColdTaiga_beach",
|
||||
"ColdTaiga_beach_water",
|
||||
"MegaTaiga",
|
||||
"MegaSpruceTaiga",
|
||||
"ExtremeHills",
|
||||
"ExtremeHills_beach",
|
||||
"ExtremeHillsM",
|
||||
"ExtremeHills+",
|
||||
"Plains",
|
||||
"Plains_beach",
|
||||
"SunflowerPlains",
|
||||
"Taiga",
|
||||
"Taiga_beach",
|
||||
"Forest",
|
||||
"Forest_beach",
|
||||
"FlowerForest",
|
||||
"FlowerForest_beach",
|
||||
"BirchForest",
|
||||
"BirchForestM",
|
||||
"RoofedForest",
|
||||
"Savanna",
|
||||
"Savanna_beach",
|
||||
"SavannaM",
|
||||
"Jungle",
|
||||
"Jungle_shore",
|
||||
"JungleM",
|
||||
"JungleM_shore",
|
||||
"JungleEdge",
|
||||
"JungleEdgeM",
|
||||
"Swampland",
|
||||
"Swampland_shore"
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"ColdTaiga",
|
||||
"SunflowerPlains",
|
||||
"RoofedForest",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"ExtremeHillsM",
|
||||
"BirchForestM",
|
||||
},
|
||||
9,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30, 17000,
|
||||
3,
|
||||
mobs_mc.water_level,
|
||||
mcl_vars.mg_overworld_max)
|
||||
mobs_mc.spawn_height.water,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn eggs
|
||||
mcl_mobs:register_egg("mobs_mc:chicken", S("Chicken"), "#a1a1a1", "#ff0000", 0)
|
||||
mobs:register_egg("mobs_mc:chicken", S("Chicken"), "mobs_mc_spawn_icon_chicken.png", 0)
|
||||
|
|
|
@ -1,274 +0,0 @@
|
|||
--MCmobs v0.4
|
||||
--maikerumine
|
||||
--made for MC like Survival game
|
||||
--License for code WTFPL and otherwise stated in readmes
|
||||
|
||||
local pi = math.pi
|
||||
local atann = math.atan
|
||||
local atan = function(x)
|
||||
if not x or x ~= x then
|
||||
return 0
|
||||
else
|
||||
return atann(x)
|
||||
end
|
||||
end
|
||||
|
||||
local dir_to_pitch = function(dir)
|
||||
local dir2 = vector.normalize(dir)
|
||||
local xz = math.abs(dir.x) + math.abs(dir.z)
|
||||
return -math.atan2(-dir.y, xz)
|
||||
end
|
||||
|
||||
local function degrees(rad)
|
||||
return rad * 180.0 / math.pi
|
||||
end
|
||||
|
||||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
--###################
|
||||
--################### cod
|
||||
--###################
|
||||
|
||||
local cod = {
|
||||
type = "animal",
|
||||
spawn_class = "water",
|
||||
can_despawn = true,
|
||||
passive = true,
|
||||
hp_min = 3,
|
||||
hp_max = 3,
|
||||
xp_min = 1,
|
||||
xp_max = 3,
|
||||
armor = 100,
|
||||
rotate = 180,
|
||||
spawn_in_group_min = 3,
|
||||
spawn_in_group = 8,
|
||||
tilt_swim = true,
|
||||
collisionbox = {-0.3, 0.0, -0.3, 0.3, 0.79, 0.3},
|
||||
visual = "mesh",
|
||||
mesh = "extra_mobs_cod.b3d",
|
||||
textures = {
|
||||
{"extra_mobs_cod.png"}
|
||||
},
|
||||
sounds = {
|
||||
},
|
||||
animation = {
|
||||
stand_start = 1,
|
||||
stand_end = 20,
|
||||
walk_start = 1,
|
||||
walk_end = 20,
|
||||
run_start = 1,
|
||||
run_end = 20,
|
||||
},
|
||||
drops = {
|
||||
{name = "mcl_fishing:fish_raw",
|
||||
chance = 1,
|
||||
min = 1,
|
||||
max = 1,},
|
||||
{name = "mcl_dye:white",
|
||||
chance = 20,
|
||||
min = 1,
|
||||
max = 1,},
|
||||
},
|
||||
visual_size = {x=3, y=3},
|
||||
makes_footstep_sound = false,
|
||||
fly = true,
|
||||
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
|
||||
breathes_in_water = true,
|
||||
jump = false,
|
||||
view_range = 16,
|
||||
runaway = true,
|
||||
fear_height = 4,
|
||||
do_custom = function(self)
|
||||
--[[ this is supposed to make them jump out the water but doesn't appear to work very well
|
||||
self.object:set_bone_position("body", vector.new(0,1,0), vector.new(degrees(dir_to_pitch(self.object:get_velocity())) * -1 + 90,0,0))
|
||||
if minetest.get_item_group(self.standing_in, "water") ~= 0 then
|
||||
if self.object:get_velocity().y < 5 then
|
||||
self.object:add_velocity({ x = 0 , y = math.random(-.007, .007), z = 0 })
|
||||
end
|
||||
end
|
||||
--]]
|
||||
for _,object in pairs(minetest.get_objects_inside_radius(self.object:get_pos(), 10)) do
|
||||
local lp = object:get_pos()
|
||||
local s = self.object:get_pos()
|
||||
local vec = {
|
||||
x = lp.x - s.x,
|
||||
y = lp.y - s.y,
|
||||
z = lp.z - s.z
|
||||
}
|
||||
if object and not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "mobs_mc:cod" then
|
||||
self.state = "runaway"
|
||||
self.object:set_rotation({x=0,y=(atan(vec.z / vec.x) + 3 * pi / 2) - self.rotate,z=0})
|
||||
end
|
||||
end
|
||||
end,
|
||||
on_rightclick = function(self, clicker)
|
||||
if clicker:get_wielded_item():get_name() == "mcl_buckets:bucket_water" then
|
||||
self.object:remove()
|
||||
clicker:set_wielded_item("mcl_buckets:bucket_cod")
|
||||
awards.unlock(clicker:get_player_name(), "mcl:tacticalFishing")
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:cod", cod)
|
||||
|
||||
|
||||
--spawning TODO: in schools
|
||||
|
||||
local water = 0
|
||||
|
||||
mcl_mobs:spawn_specific(
|
||||
"mobs_mc:cod",
|
||||
"overworld",
|
||||
"water",
|
||||
{
|
||||
"Mesa",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"Jungle",
|
||||
"Savanna",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
"RoofedForest",
|
||||
"ExtremeHills+_snowtop",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"JungleEdgeM",
|
||||
"ExtremeHillsM",
|
||||
"JungleM",
|
||||
"BirchForestM",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
"FlowerForest_beach",
|
||||
"Forest_beach",
|
||||
"StoneBeach",
|
||||
"ColdTaiga_beach_water",
|
||||
"Taiga_beach",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
"MesaPlateauFM_sandlevel",
|
||||
"MesaPlateauF_sandlevel",
|
||||
"MesaBryce_sandlevel",
|
||||
"Mesa_sandlevel",
|
||||
"RoofedForest_ocean",
|
||||
"JungleEdgeM_ocean",
|
||||
"BirchForestM_ocean",
|
||||
"BirchForest_ocean",
|
||||
"IcePlains_deep_ocean",
|
||||
"Jungle_deep_ocean",
|
||||
"Savanna_ocean",
|
||||
"MesaPlateauF_ocean",
|
||||
"ExtremeHillsM_deep_ocean",
|
||||
"Savanna_deep_ocean",
|
||||
"SunflowerPlains_ocean",
|
||||
"Swampland_deep_ocean",
|
||||
"Swampland_ocean",
|
||||
"MegaSpruceTaiga_deep_ocean",
|
||||
"ExtremeHillsM_ocean",
|
||||
"JungleEdgeM_deep_ocean",
|
||||
"SunflowerPlains_deep_ocean",
|
||||
"BirchForest_deep_ocean",
|
||||
"IcePlainsSpikes_ocean",
|
||||
"Mesa_ocean",
|
||||
"StoneBeach_ocean",
|
||||
"Plains_deep_ocean",
|
||||
"JungleEdge_deep_ocean",
|
||||
"SavannaM_deep_ocean",
|
||||
"Desert_deep_ocean",
|
||||
"Mesa_deep_ocean",
|
||||
"ColdTaiga_deep_ocean",
|
||||
"Plains_ocean",
|
||||
"MesaPlateauFM_ocean",
|
||||
"Forest_deep_ocean",
|
||||
"JungleM_deep_ocean",
|
||||
"FlowerForest_deep_ocean",
|
||||
"MushroomIsland_ocean",
|
||||
"MegaTaiga_ocean",
|
||||
"StoneBeach_deep_ocean",
|
||||
"IcePlainsSpikes_deep_ocean",
|
||||
"ColdTaiga_ocean",
|
||||
"SavannaM_ocean",
|
||||
"MesaPlateauF_deep_ocean",
|
||||
"MesaBryce_deep_ocean",
|
||||
"ExtremeHills+_deep_ocean",
|
||||
"ExtremeHills_ocean",
|
||||
"MushroomIsland_deep_ocean",
|
||||
"Forest_ocean",
|
||||
"MegaTaiga_deep_ocean",
|
||||
"JungleEdge_ocean",
|
||||
"MesaBryce_ocean",
|
||||
"MegaSpruceTaiga_ocean",
|
||||
"ExtremeHills+_ocean",
|
||||
"Jungle_ocean",
|
||||
"RoofedForest_deep_ocean",
|
||||
"IcePlains_ocean",
|
||||
"FlowerForest_ocean",
|
||||
"ExtremeHills_deep_ocean",
|
||||
"MesaPlateauFM_deep_ocean",
|
||||
"Desert_ocean",
|
||||
"Taiga_ocean",
|
||||
"BirchForestM_deep_ocean",
|
||||
"Taiga_deep_ocean",
|
||||
"JungleM_ocean",
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"StoneBeach_underground",
|
||||
"MesaBryce_underground",
|
||||
"Mesa_underground",
|
||||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
"ExtremeHills_underground",
|
||||
"MegaSpruceTaiga_underground",
|
||||
"BirchForestM_underground",
|
||||
"SavannaM_underground",
|
||||
"MesaPlateauFM_underground",
|
||||
"Desert_underground",
|
||||
"Savanna_underground",
|
||||
"Forest_underground",
|
||||
"SunflowerPlains_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
4000,
|
||||
3,
|
||||
water-16,
|
||||
water+1)
|
||||
|
||||
--spawn egg
|
||||
mcl_mobs:register_egg("mobs_mc:cod", S("Cod"), "#c1a76a", "#e5c48b", 0)
|
|
@ -17,21 +17,16 @@ local cow_def = {
|
|||
"mobs_mc_cow.png",
|
||||
"blank.png",
|
||||
}, },
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 10,
|
||||
head_eye_height = 1.1,
|
||||
horrizonatal_head_height=-1.8,
|
||||
curiosity = 2,
|
||||
head_yaw="z",
|
||||
visual_size = {x=2.8, y=2.8},
|
||||
makes_footstep_sound = true,
|
||||
walk_velocity = 1,
|
||||
drops = {
|
||||
{name = "mcl_mobitems:beef",
|
||||
{name = mobs_mc.items.beef_raw,
|
||||
chance = 1,
|
||||
min = 1,
|
||||
max = 3,
|
||||
looting = "common",},
|
||||
{name = "mcl_mobitems:leather",
|
||||
{name = mobs_mc.items.leather,
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,
|
||||
|
@ -46,70 +41,67 @@ local cow_def = {
|
|||
distance = 16,
|
||||
},
|
||||
animation = {
|
||||
stand_start = 0, stand_end = 0,
|
||||
walk_start = 0, walk_end = 40, walk_speed = 30,
|
||||
run_start = 0, run_end = 40, run_speed = 40,
|
||||
},
|
||||
child_animations = {
|
||||
stand_start = 41, stand_end = 41,
|
||||
walk_start = 41, walk_end = 81, walk_speed = 45,
|
||||
run_start = 41, run_end = 81, run_speed = 60,
|
||||
stand_speed = 25, walk_speed = 40,
|
||||
run_speed = 60, stand_start = 0,
|
||||
stand_end = 0, walk_start = 0,
|
||||
walk_end = 40, run_start = 0,
|
||||
run_end = 40,
|
||||
},
|
||||
follow = mobs_mc.follow.cow,
|
||||
on_rightclick = function(self, clicker)
|
||||
if mcl_mobs:feed_tame(self, clicker, 1, true, false) then return end
|
||||
if mcl_mobs:protect(self, clicker) then return end
|
||||
if mobs:feed_tame(self, clicker, 1, true, true) then return end
|
||||
if mobs:protect(self, clicker) then return end
|
||||
|
||||
if self.child then
|
||||
return
|
||||
end
|
||||
|
||||
local item = clicker:get_wielded_item()
|
||||
if item:get_name() == "mcl_buckets:bucket_empty" and clicker:get_inventory() then
|
||||
if item:get_name() == mobs_mc.items.bucket and clicker:get_inventory() then
|
||||
local inv = clicker:get_inventory()
|
||||
inv:remove_item("main", "mcl_buckets:bucket_empty")
|
||||
inv:remove_item("main", mobs_mc.items.bucket)
|
||||
minetest.sound_play("mobs_mc_cow_milk", {pos=self.object:get_pos(), gain=0.6})
|
||||
-- if room add bucket of milk to inventory, otherwise drop as item
|
||||
if inv:room_for_item("main", {name = "mcl_mobitems:milk_bucket"}) then
|
||||
clicker:get_inventory():add_item("main", "mcl_mobitems:milk_bucket")
|
||||
if inv:room_for_item("main", {name=mobs_mc.items.milk}) then
|
||||
clicker:get_inventory():add_item("main", mobs_mc.items.milk)
|
||||
else
|
||||
local pos = self.object:get_pos()
|
||||
pos.y = pos.y + 0.5
|
||||
minetest.add_item(pos, {name = "mcl_mobitems:milk_bucket"})
|
||||
minetest.add_item(pos, {name = mobs_mc.items.milk})
|
||||
end
|
||||
return
|
||||
end
|
||||
mcl_mobs:capture_mob(self, clicker, 0, 5, 60, false, nil)
|
||||
mobs:capture_mob(self, clicker, 0, 5, 60, false, nil)
|
||||
end,
|
||||
follow = "mcl_farming:wheat_item",
|
||||
follow = mobs_mc.items.wheat,
|
||||
view_range = 10,
|
||||
fear_height = 4,
|
||||
}
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:cow", cow_def)
|
||||
mobs:register_mob("mobs_mc:cow", cow_def)
|
||||
|
||||
-- Mooshroom
|
||||
local mooshroom_def = table.copy(cow_def)
|
||||
mooshroom_def.description = S("Mooshroom")
|
||||
mooshroom_def.spawn_in_group_min = 4
|
||||
mooshroom_def.spawn_in_group = 8
|
||||
mooshroom_def.mesh = "mobs_mc_cow.b3d"
|
||||
mooshroom_def.textures = { {"mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png"}, {"mobs_mc_mooshroom_brown.png", "mobs_mc_mushroom_brown.png" } }
|
||||
mooshroom_def.on_rightclick = function(self, clicker)
|
||||
if mcl_mobs:feed_tame(self, clicker, 1, true, false) then return end
|
||||
if mcl_mobs:protect(self, clicker) then return end
|
||||
if mobs:feed_tame(self, clicker, 1, true, true) then return end
|
||||
if mobs:protect(self, clicker) then return end
|
||||
|
||||
if self.child then
|
||||
return
|
||||
end
|
||||
local item = clicker:get_wielded_item()
|
||||
-- Use shears to get mushrooms and turn mooshroom into cow
|
||||
if item:get_name() == "mcl_tools:shears" then
|
||||
if item:get_name() == mobs_mc.items.shears then
|
||||
local pos = self.object:get_pos()
|
||||
minetest.sound_play("mcl_tools_shears_cut", {pos = pos}, true)
|
||||
|
||||
if self.base_texture[1] == "mobs_mc_mooshroom_brown.png" then
|
||||
minetest.add_item({x=pos.x, y=pos.y+1.4, z=pos.z}, "mcl_mushrooms:mushroom_brown 5")
|
||||
minetest.add_item({x=pos.x, y=pos.y+1.4, z=pos.z}, mobs_mc.items.mushroom_brown .. " 5")
|
||||
else
|
||||
minetest.add_item({x=pos.x, y=pos.y+1.4, z=pos.z}, "mcl_mushrooms:mushroom_red 5")
|
||||
minetest.add_item({x=pos.x, y=pos.y+1.4, z=pos.z}, mobs_mc.items.mushroom_red .. " 5")
|
||||
end
|
||||
|
||||
local oldyaw = self.object:get_yaw()
|
||||
|
@ -118,90 +110,75 @@ mooshroom_def.on_rightclick = function(self, clicker)
|
|||
cow:set_yaw(oldyaw)
|
||||
|
||||
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||
item:add_wear(mobs_mc.shears_wear)
|
||||
item:add_wear(mobs_mc.misc.shears_wear)
|
||||
clicker:get_inventory():set_stack("main", clicker:get_wield_index(), item)
|
||||
end
|
||||
-- Use bucket to milk
|
||||
elseif item:get_name() == "mcl_buckets:bucket_empty" and clicker:get_inventory() then
|
||||
elseif item:get_name() == mobs_mc.items.bucket and clicker:get_inventory() then
|
||||
local inv = clicker:get_inventory()
|
||||
inv:remove_item("main", "mcl_buckets:bucket_empty")
|
||||
inv:remove_item("main", mobs_mc.items.bucket)
|
||||
minetest.sound_play("mobs_mc_cow_milk", {pos=self.object:get_pos(), gain=0.6})
|
||||
-- If room, add milk to inventory, otherwise drop as item
|
||||
if inv:room_for_item("main", {name="mcl_mobitems:milk_bucket"}) then
|
||||
clicker:get_inventory():add_item("main", "mcl_mobitems:milk_bucket")
|
||||
if inv:room_for_item("main", {name=mobs_mc.items.milk}) then
|
||||
clicker:get_inventory():add_item("main", mobs_mc.items.milk)
|
||||
else
|
||||
local pos = self.object:get_pos()
|
||||
pos.y = pos.y + 0.5
|
||||
minetest.add_item(pos, {name = "mcl_mobitems:milk_bucket"})
|
||||
minetest.add_item(pos, {name = mobs_mc.items.milk})
|
||||
end
|
||||
-- Use bowl to get mushroom stew
|
||||
elseif item:get_name() == "mcl_core:bowl" and clicker:get_inventory() then
|
||||
elseif item:get_name() == mobs_mc.items.bowl and clicker:get_inventory() then
|
||||
local inv = clicker:get_inventory()
|
||||
inv:remove_item("main", "mcl_core:bowl")
|
||||
inv:remove_item("main", mobs_mc.items.bowl)
|
||||
minetest.sound_play("mobs_mc_cow_mushroom_stew", {pos=self.object:get_pos(), gain=0.6})
|
||||
-- If room, add mushroom stew to inventory, otherwise drop as item
|
||||
if inv:room_for_item("main", {name="mcl_mushrooms:mushroom_stew"}) then
|
||||
clicker:get_inventory():add_item("main", "mcl_mushrooms:mushroom_stew")
|
||||
if inv:room_for_item("main", {name=mobs_mc.items.mushroom_stew}) then
|
||||
clicker:get_inventory():add_item("main", mobs_mc.items.mushroom_stew)
|
||||
else
|
||||
local pos = self.object:get_pos()
|
||||
pos.y = pos.y + 0.5
|
||||
minetest.add_item(pos, {name = "mcl_mushrooms:mushroom_stew"})
|
||||
minetest.add_item(pos, {name = mobs_mc.items.mushroom_stew})
|
||||
end
|
||||
end
|
||||
mcl_mobs:capture_mob(self, clicker, 0, 5, 60, false, nil)
|
||||
mobs:capture_mob(self, clicker, 0, 5, 60, false, nil)
|
||||
end
|
||||
mcl_mobs:register_mob("mobs_mc:mooshroom", mooshroom_def)
|
||||
mobs:register_mob("mobs_mc:mooshroom", mooshroom_def)
|
||||
|
||||
|
||||
-- Spawning
|
||||
mcl_mobs:spawn_specific(
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:cow",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"flat",
|
||||
"MegaTaiga",
|
||||
"MegaSpruceTaiga",
|
||||
"ExtremeHills",
|
||||
"ExtremeHills_beach",
|
||||
"ExtremeHillsM",
|
||||
"ExtremeHills+",
|
||||
"StoneBeach",
|
||||
"Plains",
|
||||
"Plains_beach",
|
||||
"SunflowerPlains",
|
||||
"Taiga",
|
||||
"Taiga_beach",
|
||||
"Forest",
|
||||
"Forest_beach",
|
||||
"FlowerForest",
|
||||
"FlowerForest_beach",
|
||||
"BirchForest",
|
||||
"BirchForestM",
|
||||
"RoofedForest",
|
||||
"Savanna",
|
||||
"Savanna_beach",
|
||||
"SavannaM",
|
||||
"Jungle",
|
||||
"Jungle_shore",
|
||||
"JungleM",
|
||||
"JungleM_shore",
|
||||
"JungleEdge",
|
||||
"JungleEdgeM",
|
||||
"Swampland",
|
||||
"Swampland_shore"
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"ColdTaiga",
|
||||
"SunflowerPlains",
|
||||
"RoofedForest",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"ExtremeHillsM",
|
||||
"BirchForestM",
|
||||
},
|
||||
9,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
17000,
|
||||
10,
|
||||
mobs_mc.water_level,
|
||||
mcl_vars.mg_overworld_max)
|
||||
mobs_mc.spawn_height.water,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
|
||||
|
||||
mcl_mobs:spawn_specific(
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:mooshroom",
|
||||
"overworld",
|
||||
"ground",
|
||||
|
@ -214,9 +191,9 @@ minetest.LIGHT_MAX+1,
|
|||
30,
|
||||
17000,
|
||||
5,
|
||||
mcl_vars.mg_overworld_min,
|
||||
mcl_vars.mg_overworld_max)
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn egg
|
||||
mcl_mobs:register_egg("mobs_mc:cow", S("Cow"), "#443626", "#a1a1a1", 0)
|
||||
mcl_mobs:register_egg("mobs_mc:mooshroom", S("Mooshroom"), "#a00f10", "#b7b7b7", 0)
|
||||
mobs:register_egg("mobs_mc:cow", S("Cow"), "mobs_mc_spawn_icon_cow.png", 0)
|
||||
mobs:register_egg("mobs_mc:mooshroom", S("Mooshroom"), "mobs_mc_spawn_icon_mooshroom.png", 0)
|
||||
|
|
|
@ -9,10 +9,9 @@ local S = minetest.get_translator("mobs_mc")
|
|||
|
||||
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:creeper", {
|
||||
mobs:register_mob("mobs_mc:creeper", {
|
||||
type = "monster",
|
||||
spawn_class = "hostile",
|
||||
spawn_in_group = 1,
|
||||
hp_min = 20,
|
||||
hp_max = 20,
|
||||
xp_min = 5,
|
||||
|
@ -21,9 +20,6 @@ mcl_mobs:register_mob("mobs_mc:creeper", {
|
|||
pathfinding = 1,
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_creeper.b3d",
|
||||
head_swivel = "Head_Control",
|
||||
bone_eye_height = 2.35,
|
||||
curiosity = 2,
|
||||
textures = {
|
||||
{"mobs_mc_creeper.png",
|
||||
"mobs_mc_empty.png"},
|
||||
|
@ -62,7 +58,7 @@ mcl_mobs:register_mob("mobs_mc:creeper", {
|
|||
return
|
||||
end
|
||||
local item = clicker:get_wielded_item()
|
||||
if item:get_name() == "mcl_fire:flint_and_steel" then
|
||||
if item:get_name() == mobs_mc.items.flint_and_steel then
|
||||
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||
-- Wear tool
|
||||
local wdef = item:get_definition()
|
||||
|
@ -81,7 +77,7 @@ mcl_mobs:register_mob("mobs_mc:creeper", {
|
|||
if self._forced_explosion_countdown_timer ~= nil then
|
||||
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
|
||||
if self._forced_explosion_countdown_timer <= 0 then
|
||||
mcl_mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
|
||||
mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
@ -92,14 +88,14 @@ mcl_mobs:register_mob("mobs_mc:creeper", {
|
|||
if luaentity and luaentity.name:find("arrow") then
|
||||
local shooter_luaentity = luaentity._shooter and luaentity._shooter:get_luaentity()
|
||||
if shooter_luaentity and (shooter_luaentity.name == "mobs_mc:skeleton" or shooter_luaentity.name == "mobs_mc:stray") then
|
||||
minetest.add_item({x=pos.x, y=pos.y+1, z=pos.z}, "mcl_jukebox:record_" .. math.random(9))
|
||||
minetest.add_item({x=pos.x, y=pos.y+1, z=pos.z}, mobs_mc.items.music_discs[math.random(1, #mobs_mc.items.music_discs)])
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
maxdrops = 2,
|
||||
drops = {
|
||||
{name = "mcl_mobitems:gunpowder",
|
||||
{name = mobs_mc.items.gunpowder,
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,
|
||||
|
@ -107,7 +103,7 @@ mcl_mobs:register_mob("mobs_mc:creeper", {
|
|||
|
||||
-- Head
|
||||
-- TODO: Only drop if killed by charged creeper
|
||||
{name = "mcl_heads:creeper",
|
||||
{name = mobs_mc.items.head_creeper,
|
||||
chance = 200, -- 0.5%
|
||||
min = 1,
|
||||
max = 1,},
|
||||
|
@ -133,7 +129,7 @@ mcl_mobs:register_mob("mobs_mc:creeper", {
|
|||
view_range = 16,
|
||||
})
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:creeper_charged", {
|
||||
mobs:register_mob("mobs_mc:creeper_charged", {
|
||||
description = S("Creeper"),
|
||||
type = "monster",
|
||||
spawn_class = "hostile",
|
||||
|
@ -184,7 +180,7 @@ mcl_mobs:register_mob("mobs_mc:creeper_charged", {
|
|||
return
|
||||
end
|
||||
local item = clicker:get_wielded_item()
|
||||
if item:get_name() == "mcl_fire:flint_and_steel" then
|
||||
if item:get_name() == mobs_mc.items.flint_and_steel then
|
||||
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||
-- Wear tool
|
||||
local wdef = item:get_definition()
|
||||
|
@ -203,7 +199,7 @@ mcl_mobs:register_mob("mobs_mc:creeper_charged", {
|
|||
if self._forced_explosion_countdown_timer ~= nil then
|
||||
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
|
||||
if self._forced_explosion_countdown_timer <= 0 then
|
||||
mcl_mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
|
||||
mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
@ -214,14 +210,14 @@ mcl_mobs:register_mob("mobs_mc:creeper_charged", {
|
|||
if luaentity and luaentity.name:find("arrow") then
|
||||
local shooter_luaentity = luaentity._shooter and luaentity._shooter:get_luaentity()
|
||||
if shooter_luaentity and (shooter_luaentity.name == "mobs_mc:skeleton" or shooter_luaentity.name == "mobs_mc:stray") then
|
||||
minetest.add_item({x=pos.x, y=pos.y+1, z=pos.z}, "mcl_jukebox:record_" .. math.random(9))
|
||||
minetest.add_item({x=pos.x, y=pos.y+1, z=pos.z}, mobs_mc.items.music_discs[math.random(1, #mobs_mc.items.music_discs)])
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
maxdrops = 2,
|
||||
drops = {
|
||||
{name = "mcl_mobitems:gunpowder",
|
||||
{name = mobs_mc.items.gunpowder,
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,
|
||||
|
@ -229,7 +225,7 @@ mcl_mobs:register_mob("mobs_mc:creeper_charged", {
|
|||
|
||||
-- Head
|
||||
-- TODO: Only drop if killed by charged creeper
|
||||
{name = "mcl_heads:creeper",
|
||||
{name = mobs_mc.items.head_creeper,
|
||||
chance = 200, -- 0.5%
|
||||
min = 1,
|
||||
max = 1,},
|
||||
|
@ -258,7 +254,7 @@ mcl_mobs:register_mob("mobs_mc:creeper_charged", {
|
|||
glow = 3,
|
||||
})
|
||||
|
||||
mcl_mobs:spawn_specific(
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:creeper",
|
||||
"overworld",
|
||||
"ground",
|
||||
|
@ -278,6 +274,7 @@ mcl_mobs:spawn_specific(
|
|||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
|
@ -304,6 +301,7 @@ mcl_mobs:spawn_specific(
|
|||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
"MesaPlateauFM_sandlevel",
|
||||
|
@ -342,6 +340,7 @@ mcl_mobs:spawn_specific(
|
|||
"Forest_deep_ocean",
|
||||
"JungleM_deep_ocean",
|
||||
"FlowerForest_deep_ocean",
|
||||
"MushroomIsland_ocean",
|
||||
"MegaTaiga_ocean",
|
||||
"StoneBeach_deep_ocean",
|
||||
"IcePlainsSpikes_deep_ocean",
|
||||
|
@ -351,6 +350,7 @@ mcl_mobs:spawn_specific(
|
|||
"MesaBryce_deep_ocean",
|
||||
"ExtremeHills+_deep_ocean",
|
||||
"ExtremeHills_ocean",
|
||||
"MushroomIsland_deep_ocean",
|
||||
"Forest_ocean",
|
||||
"MegaTaiga_deep_ocean",
|
||||
"JungleEdge_ocean",
|
||||
|
@ -376,6 +376,7 @@ mcl_mobs:spawn_specific(
|
|||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
|
@ -403,8 +404,8 @@ mcl_mobs:spawn_specific(
|
|||
20,
|
||||
16500,
|
||||
2,
|
||||
mcl_vars.mg_overworld_min,
|
||||
mcl_vars.mg_overworld_max)
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn eggs
|
||||
mcl_mobs:register_egg("mobs_mc:creeper", S("Creeper"), "#0da70a", "#000000", 0)
|
||||
mobs:register_egg("mobs_mc:creeper", S("Creeper"), "mobs_mc_spawn_icon_creeper.png", 0)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
mcl_mobs
|
|
@ -1,253 +0,0 @@
|
|||
--MCmobs v0.4
|
||||
--maikerumine
|
||||
--made for MC like Survival game
|
||||
--License for code WTFPL and otherwise stated in readmes
|
||||
|
||||
local pi = math.pi
|
||||
local atann = math.atan
|
||||
local atan = function(x)
|
||||
if not x or x ~= x then
|
||||
return 0
|
||||
else
|
||||
return atann(x)
|
||||
end
|
||||
end
|
||||
|
||||
local dir_to_pitch = function(dir)
|
||||
local dir2 = vector.normalize(dir)
|
||||
local xz = math.abs(dir.x) + math.abs(dir.z)
|
||||
return -math.atan2(-dir.y, xz)
|
||||
end
|
||||
|
||||
local function degrees(rad)
|
||||
return rad * 180.0 / math.pi
|
||||
end
|
||||
|
||||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
--###################
|
||||
--################### dolphin
|
||||
--###################
|
||||
|
||||
local dolphin = {
|
||||
type = "animal",
|
||||
spawn_class = "water",
|
||||
can_despawn = true,
|
||||
passive = true,
|
||||
hp_min = 10,
|
||||
hp_max = 10,
|
||||
xp_min = 1,
|
||||
xp_max = 3,
|
||||
armor = 100,
|
||||
walk_chance = 100,
|
||||
breath_max = 120,
|
||||
rotate = 180,
|
||||
spawn_in_group_min = 3,
|
||||
spawn_in_group = 5,
|
||||
tilt_swim = true,
|
||||
collisionbox = {-0.3, 0.0, -0.3, 0.3, 0.79, 0.3},
|
||||
visual = "mesh",
|
||||
mesh = "extra_mobs_dolphin.b3d",
|
||||
textures = {
|
||||
{"extra_mobs_dolphin.png"}
|
||||
},
|
||||
sounds = {
|
||||
},
|
||||
animation = {
|
||||
stand_start = 20,
|
||||
stand_end = 20,
|
||||
walk_start = 0,
|
||||
walk_end = 15,
|
||||
run_start = 30,
|
||||
run_end = 45,
|
||||
},
|
||||
drops = {
|
||||
{name = "mcl_fishing:fish_raw",
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 1,},
|
||||
},
|
||||
visual_size = {x=3, y=3},
|
||||
makes_footstep_sound = false,
|
||||
fly = true,
|
||||
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
|
||||
breathes_in_water = true,
|
||||
jump = false,
|
||||
view_range = 16,
|
||||
fear_height = 4,
|
||||
walk_velocity = 3,
|
||||
run_velocity = 6,
|
||||
reach = 2,
|
||||
damage = 2.5,
|
||||
attack_type = "dogfight",
|
||||
do_custom = function(self,dtime)
|
||||
--[[ this is supposed to make them jump out the water but doesn't appear to work very well
|
||||
self.object:set_bone_position("body", vector.new(0,1,0), vector.new(degrees(dir_to_pitch(self.object:get_velocity())) * -1 + 90,0,0))
|
||||
if minetest.get_item_group(self.standing_in, "water") ~= 0 then
|
||||
if self.object:get_velocity().y < 5 then
|
||||
self.object:add_velocity({ x = 0 , y = math.random(-.007, .007), z = 0 })
|
||||
end
|
||||
end
|
||||
--]]
|
||||
end,
|
||||
}
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:dolphin", dolphin)
|
||||
|
||||
|
||||
--spawning TO DO: in schools
|
||||
local water = 0
|
||||
mcl_mobs:spawn_specific(
|
||||
"mobs_mc:dolphin",
|
||||
"overworld",
|
||||
"water",
|
||||
{
|
||||
"Mesa",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"Jungle",
|
||||
"Savanna",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
"RoofedForest",
|
||||
"ExtremeHills+_snowtop",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"JungleEdgeM",
|
||||
"ExtremeHillsM",
|
||||
"JungleM",
|
||||
"BirchForestM",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
"FlowerForest_beach",
|
||||
"Forest_beach",
|
||||
"StoneBeach",
|
||||
"Taiga_beach",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
"MesaPlateauFM_sandlevel",
|
||||
"MesaPlateauF_sandlevel",
|
||||
"MesaBryce_sandlevel",
|
||||
"Mesa_sandlevel",
|
||||
"RoofedForest_ocean",
|
||||
"JungleEdgeM_ocean",
|
||||
"BirchForestM_ocean",
|
||||
"BirchForest_ocean",
|
||||
"IcePlains_deep_ocean",
|
||||
"Jungle_deep_ocean",
|
||||
"Savanna_ocean",
|
||||
"MesaPlateauF_ocean",
|
||||
"ExtremeHillsM_deep_ocean",
|
||||
"Savanna_deep_ocean",
|
||||
"SunflowerPlains_ocean",
|
||||
"Swampland_deep_ocean",
|
||||
"Swampland_ocean",
|
||||
"MegaSpruceTaiga_deep_ocean",
|
||||
"ExtremeHillsM_ocean",
|
||||
"JungleEdgeM_deep_ocean",
|
||||
"SunflowerPlains_deep_ocean",
|
||||
"BirchForest_deep_ocean",
|
||||
"IcePlainsSpikes_ocean",
|
||||
"Mesa_ocean",
|
||||
"StoneBeach_ocean",
|
||||
"Plains_deep_ocean",
|
||||
"JungleEdge_deep_ocean",
|
||||
"SavannaM_deep_ocean",
|
||||
"Desert_deep_ocean",
|
||||
"Mesa_deep_ocean",
|
||||
"ColdTaiga_deep_ocean",
|
||||
"Plains_ocean",
|
||||
"MesaPlateauFM_ocean",
|
||||
"Forest_deep_ocean",
|
||||
"JungleM_deep_ocean",
|
||||
"FlowerForest_deep_ocean",
|
||||
"MushroomIsland_ocean",
|
||||
"MegaTaiga_ocean",
|
||||
"StoneBeach_deep_ocean",
|
||||
"IcePlainsSpikes_deep_ocean",
|
||||
"ColdTaiga_ocean",
|
||||
"SavannaM_ocean",
|
||||
"MesaPlateauF_deep_ocean",
|
||||
"MesaBryce_deep_ocean",
|
||||
"ExtremeHills+_deep_ocean",
|
||||
"ExtremeHills_ocean",
|
||||
"MushroomIsland_deep_ocean",
|
||||
"Forest_ocean",
|
||||
"MegaTaiga_deep_ocean",
|
||||
"JungleEdge_ocean",
|
||||
"MesaBryce_ocean",
|
||||
"MegaSpruceTaiga_ocean",
|
||||
"ExtremeHills+_ocean",
|
||||
"Jungle_ocean",
|
||||
"RoofedForest_deep_ocean",
|
||||
"IcePlains_ocean",
|
||||
"FlowerForest_ocean",
|
||||
"ExtremeHills_deep_ocean",
|
||||
"MesaPlateauFM_deep_ocean",
|
||||
"Desert_ocean",
|
||||
"Taiga_ocean",
|
||||
"BirchForestM_deep_ocean",
|
||||
"Taiga_deep_ocean",
|
||||
"JungleM_ocean",
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"StoneBeach_underground",
|
||||
"MesaBryce_underground",
|
||||
"Mesa_underground",
|
||||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
"ExtremeHills_underground",
|
||||
"MegaSpruceTaiga_underground",
|
||||
"BirchForestM_underground",
|
||||
"SavannaM_underground",
|
||||
"MesaPlateauFM_underground",
|
||||
"Desert_underground",
|
||||
"Savanna_underground",
|
||||
"Forest_underground",
|
||||
"SunflowerPlains_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
4000,
|
||||
3,
|
||||
water-16,
|
||||
water+1)
|
||||
|
||||
--spawn egg
|
||||
mcl_mobs:register_egg("mobs_mc:dolphin", S("Dolphin"), "#223b4d", "#f9f9f9", 0)
|
|
@ -4,51 +4,7 @@
|
|||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
local BEAM_CHECK_FREQUENCY = 2
|
||||
local POS_CHECK_FREQUENCY = 15
|
||||
local HEAL_AMMOUNT = 37
|
||||
|
||||
local function heal(self)
|
||||
local o = self.object
|
||||
self.health = math.min(self.hp_max,self.health + HEAL_AMMOUNT)
|
||||
end
|
||||
local function check_beam(self)
|
||||
for _, obj in ipairs(minetest.get_objects_inside_radius(self.object:get_pos(), 80)) do
|
||||
local luaentity = obj:get_luaentity()
|
||||
if luaentity and luaentity.name == "mcl_end:crystal" then
|
||||
if luaentity.beam then
|
||||
if luaentity.beam == self.beam then
|
||||
heal(self)
|
||||
break
|
||||
end
|
||||
else
|
||||
if self.beam then
|
||||
self.beam:remove()
|
||||
end
|
||||
minetest.add_entity(self.object:get_pos(), "mcl_end:crystal_beam"):get_luaentity():init(self.object, obj)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function check_pos(self)
|
||||
if self._portal_pos then
|
||||
-- migrate old format
|
||||
if type(self._portal_pos) == "string" then
|
||||
self._portal_pos = minetest.string_to_pos(self._portal_pos)
|
||||
end
|
||||
local portal_center = vector.add(self._portal_pos, vector.new(0, 11, 0))
|
||||
local pos = self.object:get_pos()
|
||||
if vector.distance(pos, portal_center) > 50 then
|
||||
self.object:set_pos(self._last_good_pos or portal_center)
|
||||
else
|
||||
self._last_good_pos = pos
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:enderdragon", {
|
||||
mobs:register_mob("mobs_mc:enderdragon", {
|
||||
description = S("Ender Dragon"),
|
||||
type = "monster",
|
||||
spawn_class = "hostile",
|
||||
|
@ -67,7 +23,7 @@ mcl_mobs:register_mob("mobs_mc:enderdragon", {
|
|||
{"mobs_mc_dragon.png"},
|
||||
},
|
||||
visual_size = {x=3, y=3},
|
||||
view_range = 64,
|
||||
view_range = 35,
|
||||
walk_velocity = 6,
|
||||
run_velocity = 6,
|
||||
can_despawn = false,
|
||||
|
@ -79,7 +35,6 @@ mcl_mobs:register_mob("mobs_mc:enderdragon", {
|
|||
},
|
||||
physical = true,
|
||||
damage = 10,
|
||||
knock_back = false,
|
||||
jump = true,
|
||||
jump_height = 14,
|
||||
fly = true,
|
||||
|
@ -105,33 +60,45 @@ mcl_mobs:register_mob("mobs_mc:enderdragon", {
|
|||
run_start = 0, run_end = 20,
|
||||
},
|
||||
ignores_nametag = true,
|
||||
do_custom = function(self,dtime)
|
||||
do_custom = function(self)
|
||||
mcl_bossbars.update_boss(self.object, "Ender Dragon", "light_purple")
|
||||
if self._pos_timer == nil or self._pos_timer > POS_CHECK_FREQUENCY then
|
||||
self._pos_timer = 0
|
||||
check_pos(self)
|
||||
for _, obj in ipairs(minetest.get_objects_inside_radius(self.object:get_pos(), 80)) do
|
||||
local luaentity = obj:get_luaentity()
|
||||
if luaentity and luaentity.name == "mcl_end:crystal" then
|
||||
if luaentity.beam then
|
||||
if luaentity.beam == self.beam then
|
||||
break
|
||||
end
|
||||
else
|
||||
if self.beam then
|
||||
self.beam:remove()
|
||||
end
|
||||
minetest.add_entity(self.object:get_pos(), "mcl_end:crystal_beam"):get_luaentity():init(self.object, obj)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if self._portal_pos then
|
||||
-- migrate old format
|
||||
if type(self._portal_pos) == "string" then
|
||||
self._portal_pos = minetest.string_to_pos(self._portal_pos)
|
||||
end
|
||||
local portal_center = vector.add(self._portal_pos, vector.new(3, 11, 3))
|
||||
local pos = self.object:get_pos()
|
||||
if vector.distance(pos, portal_center) > 50 then
|
||||
self.object:set_pos(self._last_good_pos or portal_center)
|
||||
else
|
||||
self._last_good_pos = pos
|
||||
end
|
||||
if self._beam_timer == nil or self._beam_timer > BEAM_CHECK_FREQUENCY then
|
||||
self._beam_timer = 0
|
||||
check_beam(self)
|
||||
end
|
||||
self._beam_timer = self._beam_timer + dtime
|
||||
self._pos_timer = self._pos_timer + dtime
|
||||
end,
|
||||
on_die = function(self, pos, cmi_cause)
|
||||
on_die = function(self, pos)
|
||||
if self._portal_pos then
|
||||
mcl_portals.spawn_gateway_portal()
|
||||
mcl_structures.place_structure(self._portal_pos,mcl_structures.registered_structures["end_exit_portal_open"],PseudoRandom(minetest.get_mapgen_setting("seed")),-1)
|
||||
mcl_structures.call_struct(self._portal_pos, "end_exit_portal_open")
|
||||
if self._initial then
|
||||
mcl_experience.throw_xp(pos, 11500) -- 500 + 11500 = 12000
|
||||
minetest.set_node(vector.add(self._portal_pos, vector.new(0, 5, 0)), {name = "mcl_end:dragon_egg"})
|
||||
end
|
||||
end
|
||||
|
||||
-- Free The End Advancement
|
||||
for _,players in pairs(minetest.get_objects_inside_radius(pos,64)) do
|
||||
if players:is_player() then
|
||||
awards.unlock(players:get_player_name(), "mcl:freeTheEnd")
|
||||
minetest.set_node(vector.add(self._portal_pos, vector.new(3, 5, 3)), {name = mobs_mc.items.dragon_egg})
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
@ -142,7 +109,7 @@ mcl_mobs:register_mob("mobs_mc:enderdragon", {
|
|||
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
||||
|
||||
-- dragon fireball (projectile)
|
||||
mcl_mobs:register_arrow("mobs_mc:dragon_fireball", {
|
||||
mobs:register_arrow("mobs_mc:dragon_fireball", {
|
||||
visual = "sprite",
|
||||
visual_size = {x = 1.25, y = 1.25},
|
||||
textures = {"mobs_mc_dragon_fireball.png"},
|
||||
|
@ -166,11 +133,10 @@ mcl_mobs:register_arrow("mobs_mc:dragon_fireball", {
|
|||
|
||||
-- node hit, explode
|
||||
hit_node = function(self, pos, node)
|
||||
mcl_mobs:boom(self, pos, 2)
|
||||
mobs:boom(self, pos, 2)
|
||||
end
|
||||
})
|
||||
|
||||
mcl_mobs:register_egg("mobs_mc:enderdragon", S("Ender Dragon"), "#252525", "#b313c9", 0, true)
|
||||
|
||||
mobs:register_egg("mobs_mc:enderdragon", S("Ender Dragon"), "mobs_mc_spawn_icon_dragon.png", 0, true)
|
||||
|
||||
mcl_wip.register_wip_item("mobs_mc:enderdragon")
|
||||
|
|
|
@ -24,25 +24,7 @@
|
|||
-- added rain damage.
|
||||
-- fixed the grass_with_dirt issue.
|
||||
|
||||
minetest.register_entity("mobs_mc:ender_eyes", {
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_spider.b3d",
|
||||
visual_size = {x=1.01, y=1.01},
|
||||
textures = {
|
||||
"mobs_mc_enderman_eyes.png",
|
||||
},
|
||||
on_step = function(self)
|
||||
if self and self.object then
|
||||
if not self.object:get_attach() then
|
||||
self.object:remove()
|
||||
end
|
||||
end
|
||||
end,
|
||||
glow = 50,
|
||||
})
|
||||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
local enable_damage = minetest.settings:get_bool("enable_damage")
|
||||
|
||||
local telesound = function(pos, is_source)
|
||||
local snd
|
||||
|
@ -66,37 +48,6 @@ local take_frequency_max = 245
|
|||
local place_frequency_min = 235
|
||||
local place_frequency_max = 245
|
||||
|
||||
|
||||
-- Texuture overrides for enderman block. Required for cactus because it's original is a nodebox
|
||||
-- and the textures have tranparent pixels.
|
||||
local block_texture_overrides
|
||||
do
|
||||
local cbackground = "mobs_mc_enderman_cactus_background.png"
|
||||
local ctiles = minetest.registered_nodes["mcl_core:cactus"].tiles
|
||||
|
||||
local ctable = {}
|
||||
local last
|
||||
for i=1, 6 do
|
||||
if ctiles[i] then
|
||||
last = ctiles[i]
|
||||
end
|
||||
table.insert(ctable, cbackground .. "^" .. last)
|
||||
end
|
||||
|
||||
block_texture_overrides = {
|
||||
["mcl_core:cactus"] = ctable,
|
||||
-- FIXME: replace colorize colors with colors from palette
|
||||
["mcl_core:dirt_with_grass"] =
|
||||
{
|
||||
"mcl_core_grass_block_top.png^[colorize:green:90",
|
||||
"default_dirt.png",
|
||||
"default_dirt.png^(mcl_core_grass_block_side_overlay.png^[colorize:green:90)",
|
||||
"default_dirt.png^(mcl_core_grass_block_side_overlay.png^[colorize:green:90)",
|
||||
"default_dirt.png^(mcl_core_grass_block_side_overlay.png^[colorize:green:90)",
|
||||
"default_dirt.png^(mcl_core_grass_block_side_overlay.png^[colorize:green:90)"}
|
||||
}
|
||||
end
|
||||
|
||||
-- Create the textures table for the enderman, depending on which kind of block
|
||||
-- the enderman holds (if any).
|
||||
local create_enderman_textures = function(block_type, itemstring)
|
||||
|
@ -118,9 +69,9 @@ local create_enderman_textures = function(block_type, itemstring)
|
|||
local tiles = minetest.registered_nodes[itemstring].tiles
|
||||
local textures = {}
|
||||
local last
|
||||
if block_texture_overrides[itemstring] then
|
||||
if mobs_mc.enderman_block_texture_overrides[itemstring] then
|
||||
-- Texture override available? Use these instead!
|
||||
textures = block_texture_overrides[itemstring]
|
||||
textures = mobs_mc.enderman_block_texture_overrides[itemstring]
|
||||
else
|
||||
-- Extract the texture names
|
||||
for i = 1, 6 do
|
||||
|
@ -237,9 +188,8 @@ local select_enderman_animation = function(animation_type)
|
|||
end
|
||||
|
||||
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
||||
local spawners = {}
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:enderman", {
|
||||
mobs:register_mob("mobs_mc:enderman", {
|
||||
description = S("Enderman"),
|
||||
type = "monster",
|
||||
spawn_class = "passive",
|
||||
|
@ -255,19 +205,6 @@ mcl_mobs:register_mob("mobs_mc:enderman", {
|
|||
textures = create_enderman_textures(),
|
||||
visual_size = {x=3, y=3},
|
||||
makes_footstep_sound = true,
|
||||
on_spawn = function(self)
|
||||
local spider_eyes=false
|
||||
for n = 1, #self.object:get_children() do
|
||||
local obj = self.object:get_children()[n]
|
||||
if obj:get_luaentity() and self.object:get_luaentity().name == "mobs_mc:ender_eyes" then
|
||||
spider_eyes = true
|
||||
end
|
||||
end
|
||||
if not spider_eyes then
|
||||
minetest.add_entity(self.object:get_pos(), "mobs_mc:ender_eyes"):set_attach(self.object, "head.top", vector.new(0,2.54,-1.99), vector.new(90,0,180))
|
||||
minetest.add_entity(self.object:get_pos(), "mobs_mc:ender_eyes"):set_attach(self.object, "head.top", vector.new(1,2.54,-1.99), vector.new(90,0,180))
|
||||
end
|
||||
end,
|
||||
sounds = {
|
||||
-- TODO: Custom war cry sound
|
||||
war_cry = "mobs_sandmonster",
|
||||
|
@ -281,7 +218,7 @@ mcl_mobs:register_mob("mobs_mc:enderman", {
|
|||
damage = 7,
|
||||
reach = 2,
|
||||
drops = {
|
||||
{name = "mcl_throwing:ender_pearl",
|
||||
{name = mobs_mc.items.ender_pearl,
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 1,
|
||||
|
@ -289,48 +226,23 @@ mcl_mobs:register_mob("mobs_mc:enderman", {
|
|||
},
|
||||
animation = select_enderman_animation("normal"),
|
||||
_taken_node = "",
|
||||
can_spawn = function(pos)
|
||||
return #minetest.find_nodes_in_area(vector.offset(pos,0,1,0),vector.offset(pos,0,3,0),{"air"}) > 2
|
||||
end,
|
||||
do_custom = function(self, dtime)
|
||||
-- PARTICLE BEHAVIOUR HERE.
|
||||
local enderpos = self.object:get_pos()
|
||||
if self._particle_timer and self._particle_timer >= 1 then
|
||||
for _,player in pairs(minetest.get_connected_players()) do
|
||||
if not spawners[player] then spawners[player] = {} end
|
||||
local dst = vector.distance(player:get_pos(),enderpos)
|
||||
if dst < 128 and not spawners[player][self.object] then
|
||||
self._particle_timer = 0
|
||||
spawners[player][self.object] = minetest.add_particlespawner({
|
||||
amount = 5,
|
||||
minpos = vector.new(-0.6,0,-0.6),
|
||||
maxpos = vector.new(0.6,3,0.6),
|
||||
minvel = vector.new(-0.25,-0.25,-0.25),
|
||||
maxvel = vector.new(0.25,0.25,0.25),
|
||||
minacc = vector.new(-0.5,-0.5,-0.5),
|
||||
maxacc = vector.new(0.5,0.5,0.5),
|
||||
minexptime = 0.2,
|
||||
maxexptime = 3,
|
||||
minsize = 0.2,
|
||||
maxsize = 1.2,
|
||||
local chanceOfParticle = math.random(0, 1)
|
||||
if chanceOfParticle == 1 then
|
||||
minetest.add_particle({
|
||||
pos = {x=enderpos.x+math.random(-1,1)*math.random()/2,y=enderpos.y+math.random(0,3),z=enderpos.z+math.random(-1,1)*math.random()/2},
|
||||
velocity = {x=math.random(-.25,.25), y=math.random(-.25,.25), z=math.random(-.25,.25)},
|
||||
acceleration = {x=math.random(-.5,.5), y=math.random(-.5,.5), z=math.random(-.5,.5)},
|
||||
expirationtime = math.random(),
|
||||
size = math.random(),
|
||||
collisiondetection = true,
|
||||
vertical = false,
|
||||
time = 0,
|
||||
texture = "mcl_portals_particle"..math.random(1, 5)..".png",
|
||||
attached = self.object,
|
||||
playername = player:get_player_name(),
|
||||
})
|
||||
elseif dst > 128 and spawners[player][self.object] then
|
||||
minetest.delete_particlespawner(spawners[player][self.object])
|
||||
spawners[player][self.object] = nil
|
||||
end
|
||||
end
|
||||
elseif not self._particle_timer then
|
||||
self._particle_timer = 0
|
||||
end
|
||||
self._particle_timer = self._particle_timer + dtime
|
||||
-- RAIN DAMAGE / EVASIVE WARP BEHAVIOUR HERE.
|
||||
enderpos = self.object:get_pos()
|
||||
local dim = mcl_worlds.pos_to_dimension(enderpos)
|
||||
if dim == "overworld" then
|
||||
if mcl_weather.state == "rain" or mcl_weather.state == "lightning" then
|
||||
|
@ -366,6 +278,10 @@ mcl_mobs:register_mob("mobs_mc:enderman", {
|
|||
else return end
|
||||
-- AGRESSIVELY WARP/CHASE PLAYER BEHAVIOUR HERE.
|
||||
if self.state == "attack" then
|
||||
--if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
|
||||
--self:teleport(nil)
|
||||
--self.state = ""
|
||||
--else
|
||||
if self.attack then
|
||||
local target = self.attack
|
||||
local pos = target:get_pos()
|
||||
|
@ -375,16 +291,11 @@ mcl_mobs:register_mob("mobs_mc:enderman", {
|
|||
end
|
||||
end
|
||||
end
|
||||
else --if not attacking try to tp to the dark
|
||||
local light = minetest.get_node_light(enderpos)
|
||||
if light and light > minetest.LIGHT_MAX then
|
||||
self:teleport(nil)
|
||||
end
|
||||
--end
|
||||
end
|
||||
-- ARROW / DAYTIME PEOPLE AVOIDANCE BEHAVIOUR HERE.
|
||||
-- Check for arrows and people nearby.
|
||||
|
||||
enderpos = self.object:get_pos()
|
||||
local enderpos = self.object:get_pos()
|
||||
enderpos.y = enderpos.y + 1.5
|
||||
local objs = minetest.get_objects_inside_radius(enderpos, 2)
|
||||
for n = 1, #objs do
|
||||
|
@ -413,7 +324,7 @@ mcl_mobs:register_mob("mobs_mc:enderman", {
|
|||
-- self:teleport(nil)
|
||||
-- self.state = ""
|
||||
--else
|
||||
if self.attack ~= nil and enable_damage then
|
||||
if self.attack ~= nil and not minetest.settings:get_bool("creative_mode") then
|
||||
self.state = 'attack'
|
||||
end
|
||||
--end
|
||||
|
@ -477,17 +388,20 @@ mcl_mobs:register_mob("mobs_mc:enderman", {
|
|||
self._take_place_timer = 0
|
||||
self._next_take_place_time = math.random(place_frequency_min, place_frequency_max)
|
||||
local pos = self.object:get_pos()
|
||||
local takable_nodes = minetest.find_nodes_in_area_under_air({x=pos.x-2, y=pos.y-1, z=pos.z-2}, {x=pos.x+2, y=pos.y+1, z=pos.z+2}, "group:enderman_takable")
|
||||
local takable_nodes = minetest.find_nodes_in_area_under_air({x=pos.x-2, y=pos.y-1, z=pos.z-2}, {x=pos.x+2, y=pos.y+1, z=pos.z+2}, mobs_mc.enderman_takable)
|
||||
if #takable_nodes >= 1 then
|
||||
local r = pr:next(1, #takable_nodes)
|
||||
local take_pos = takable_nodes[r]
|
||||
local node = minetest.get_node(take_pos)
|
||||
-- Don't destroy protected stuff.
|
||||
if not minetest.is_protected(take_pos, "") then
|
||||
minetest.remove_node(take_pos)
|
||||
local dug = minetest.get_node_or_nil(take_pos)
|
||||
if dug and dug.name == "air" then
|
||||
local dug = minetest.dig_node(take_pos)
|
||||
if dug then
|
||||
if mobs_mc.enderman_replace_on_take[node.name] then
|
||||
self._taken_node = mobs_mc.enderman_replace_on_take[node.name]
|
||||
else
|
||||
self._taken_node = node.name
|
||||
end
|
||||
local def = minetest.registered_nodes[self._taken_node]
|
||||
-- Update animation and texture accordingly (adds visibly carried block)
|
||||
local block_type
|
||||
|
@ -516,7 +430,7 @@ mcl_mobs:register_mob("mobs_mc:enderman", {
|
|||
self.base_texture = create_enderman_textures(block_type, self._taken_node)
|
||||
self.object:set_properties({ textures = self.base_texture })
|
||||
self.animation = select_enderman_animation("block")
|
||||
mcl_mobs:set_animation(self, self.animation.current)
|
||||
mobs:set_animation(self, self.animation.current)
|
||||
if def.sounds and def.sounds.dug then
|
||||
minetest.sound_play(def.sounds.dug, {pos = take_pos, max_hear_distance = 16}, true)
|
||||
end
|
||||
|
@ -539,7 +453,7 @@ mcl_mobs:register_mob("mobs_mc:enderman", {
|
|||
local def = minetest.registered_nodes[self._taken_node]
|
||||
-- Update animation accordingly (removes visible block)
|
||||
self.animation = select_enderman_animation("normal")
|
||||
mcl_mobs:set_animation(self, self.animation.current)
|
||||
mobs:set_animation(self, self.animation.current)
|
||||
if def.sounds and def.sounds.place then
|
||||
minetest.sound_play(def.sounds.place, {pos = place_pos, max_hear_distance = 16}, true)
|
||||
end
|
||||
|
@ -648,37 +562,24 @@ mcl_mobs:register_mob("mobs_mc:enderman", {
|
|||
attack_type = "dogfight",
|
||||
})
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
if not spawners[player] then return end
|
||||
for _,s in pairs(spawners[player]) do
|
||||
minetest.delete_particlespawner(s)
|
||||
end
|
||||
spawners[player] = nil
|
||||
end)
|
||||
|
||||
|
||||
-- End spawn
|
||||
mcl_mobs:spawn_specific(
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:enderman",
|
||||
"end",
|
||||
"ground",
|
||||
{
|
||||
"End",
|
||||
"EndIsland",
|
||||
"EndMidlands",
|
||||
"EndBarrens",
|
||||
"EndBorder",
|
||||
"EndSmallIslands"
|
||||
"End"
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
3000,
|
||||
12,
|
||||
mcl_vars.mg_end_min,
|
||||
mcl_vars.mg_end_max)
|
||||
mobs_mc.spawn_height.end_min,
|
||||
mobs_mc.spawn_height.end_max)
|
||||
-- Overworld spawn
|
||||
mcl_mobs:spawn_specific(
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:enderman",
|
||||
"overworld",
|
||||
"ground",
|
||||
|
@ -698,6 +599,7 @@ mcl_mobs:spawn_specific(
|
|||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
|
@ -724,6 +626,7 @@ mcl_mobs:spawn_specific(
|
|||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
"MesaPlateauFM_sandlevel",
|
||||
|
@ -762,6 +665,7 @@ mcl_mobs:spawn_specific(
|
|||
"Forest_deep_ocean",
|
||||
"JungleM_deep_ocean",
|
||||
"FlowerForest_deep_ocean",
|
||||
"MushroomIsland_ocean",
|
||||
"MegaTaiga_ocean",
|
||||
"StoneBeach_deep_ocean",
|
||||
"IcePlainsSpikes_deep_ocean",
|
||||
|
@ -771,6 +675,7 @@ mcl_mobs:spawn_specific(
|
|||
"MesaBryce_deep_ocean",
|
||||
"ExtremeHills+_deep_ocean",
|
||||
"ExtremeHills_ocean",
|
||||
"MushroomIsland_deep_ocean",
|
||||
"Forest_ocean",
|
||||
"MegaTaiga_deep_ocean",
|
||||
"JungleEdge_ocean",
|
||||
|
@ -796,6 +701,7 @@ mcl_mobs:spawn_specific(
|
|||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
|
@ -823,41 +729,24 @@ mcl_mobs:spawn_specific(
|
|||
30,
|
||||
19000,
|
||||
2,
|
||||
mcl_vars.mg_overworld_min,
|
||||
mcl_vars.mg_overworld_max)
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- Nether spawn (rare)
|
||||
mcl_mobs:spawn_specific(
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:enderman",
|
||||
"nether",
|
||||
"ground",
|
||||
{
|
||||
"Nether",
|
||||
"SoulsandValley",
|
||||
"Nether"
|
||||
},
|
||||
0,
|
||||
11,
|
||||
7,
|
||||
30,
|
||||
27500,
|
||||
4,
|
||||
mcl_vars.mg_nether_min,
|
||||
mcl_vars.mg_nether_max)
|
||||
|
||||
-- Warped Forest spawn (common)
|
||||
mcl_mobs:spawn_specific(
|
||||
"mobs_mc:enderman",
|
||||
"nether",
|
||||
"ground",
|
||||
{
|
||||
"WarpedForest"
|
||||
},
|
||||
0,
|
||||
11,
|
||||
30,
|
||||
5000,
|
||||
4,
|
||||
mcl_vars.mg_nether_min,
|
||||
mcl_vars.mg_nether_max)
|
||||
mobs_mc.spawn_height.nether_min,
|
||||
mobs_mc.spawn_height.nether_max)
|
||||
|
||||
-- spawn eggs
|
||||
mcl_mobs:register_egg("mobs_mc:enderman", S("Enderman"), "#252525", "#151515", 0)
|
||||
mobs:register_egg("mobs_mc:enderman", S("Enderman"), "mobs_mc_spawn_icon_enderman.png", 0)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:endermite", {
|
||||
mobs:register_mob("mobs_mc:endermite", {
|
||||
description = S("Endermite"),
|
||||
type = "monster",
|
||||
spawn_class = "hostile",
|
||||
|
@ -38,4 +38,4 @@ mcl_mobs:register_mob("mobs_mc:endermite", {
|
|||
reach = 1,
|
||||
})
|
||||
|
||||
mcl_mobs:register_egg("mobs_mc:endermite", S("Endermite"), "#161616", "#6d6d6d", 0)
|
||||
mobs:register_egg("mobs_mc:endermite", S("Endermite"), "mobs_mc_spawn_icon_endermite.png", 0)
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# Game integration help
|
||||
|
||||
This mod has been designed to make game integration rather easy. Ideally, it should be possible to include this mod verbatim in your game, with modifications only done by an external mod.
|
||||
|
||||
To integrate this mod in a game, you have to do 2 things: Adding the mod, and adding another mod which tells `mobs_mc` which items to use. The idea is that `mobs_mc` should work with any items. Specifically, these are the steps you need to follow:
|
||||
|
||||
* Add the `mobs_mc` mod and its dependencies
|
||||
* Add a mod with name “`mobs_mc_gameconfig`”
|
||||
* In this mod, do this:
|
||||
* Do *not* depend on `mobs_mc`
|
||||
* Create the table `mobs_mc`
|
||||
* Create the table `mobs_mc.override`
|
||||
* In `mobs_mc.override`, create subtables (`items`, `spawn`, etc.) like in `0_gameconfig.lua`, defining the na
|
||||
* Read `0_gameconfig.lua` to see which items you can override (and more explanations)
|
||||
* In `on_construct` of a pumpkin or jack'o lantern node, call:
|
||||
* `mobs_mc.tools.check_iron_golem_summon(pos)`
|
||||
* `mobs_mc.tools.check_snow_golem_summon(pos)`
|
||||
* For more information, see `snowman.lua` and `iron_golem.lua`
|
||||
|
||||
Some things to note:
|
||||
|
||||
* Every override is optional, but explicitly setting all the item overrides is strongly recommended
|
||||
* `mobs_mc` ships many (but not all) items on its own. If not item name override is set, the `mobs_mc` item is used
|
||||
* You decide whether your game defines its own items, outside of `mobs_mc` or if you let `mobs_mc` do the work.
|
||||
* Make sure to avoid duplicate items!
|
||||
* After finishing this, throughly test this
|
||||
* Without `mobs_mc_gameconfig`, the mod assumes Minetest Game items
|
||||
* `mobs_mc` optionally depends on `mobs_mc_gameconfig`
|
||||
|
||||
## Example `init.lua` in `mobs_mc_gameconfig`
|
||||
```
|
||||
mobs_mc = {}
|
||||
|
||||
mobs_mc.override = {}
|
||||
|
||||
-- Set the item names here
|
||||
mobs_mc.override.items = {
|
||||
blaze_rod = "mcl_mobitems:blaze_rod",
|
||||
blaze_powder = "mcl_mobitems:blaze_powder",
|
||||
chicken_raw = "mcl_mobitems:chicken",
|
||||
-- And so on ...
|
||||
}
|
||||
|
||||
-- Set the “follow” field of mobs (used for attracting mob, feeding and breeding)
|
||||
mobs_mc.override.follow = {
|
||||
chicken = { "mcl_farming:wheat_seeds", "mcl_farming:melon_seeds", "mcl_farming:pumpkin_seeds", "mcl_farming:beetroot_seeds", },
|
||||
horse = { "mcl_core:apple", mobs_mc.override.items.wheat }, -- TODO
|
||||
pig = { "mcl_farming:potato", mobs_mc.override.items.carrot, mobs_mc.override.items.carrot_on_a_stick},
|
||||
-- And so on ...
|
||||
}
|
||||
|
||||
-- Custom spawn nodes
|
||||
mobs_mc.override.spawn = {
|
||||
snow = { "example:snow", "example:snow2" },
|
||||
-- And so on ...
|
||||
}
|
||||
|
||||
-- Take a look at the other possible tables, see 0_gameconfig.lua
|
||||
```
|
|
@ -10,7 +10,7 @@ local S = minetest.get_translator("mobs_mc")
|
|||
--###################
|
||||
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:ghast", {
|
||||
mobs:register_mob("mobs_mc:ghast", {
|
||||
description = S("Ghast"),
|
||||
type = "monster",
|
||||
spawn_class = "hostile",
|
||||
|
@ -23,7 +23,6 @@ mcl_mobs:register_mob("mobs_mc:ghast", {
|
|||
collisionbox = {-2, 5, -2, 2, 9, 2},
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_ghast.b3d",
|
||||
spawn_in_group = 1,
|
||||
textures = {
|
||||
{"mobs_mc_ghast.png"},
|
||||
},
|
||||
|
@ -40,8 +39,8 @@ mcl_mobs:register_mob("mobs_mc:ghast", {
|
|||
walk_velocity = 1.6,
|
||||
run_velocity = 3.2,
|
||||
drops = {
|
||||
{name = "mcl_mobitems:gunpowder", chance = 1, min = 0, max = 2, looting = "common"},
|
||||
{name = "mcl_mobitems:ghast_tear", chance = 10/6, min = 0, max = 1, looting = "common", looting_ignore_chance = true},
|
||||
{name = mobs_mc.items.gunpowder, chance = 1, min = 0, max = 2, looting = "common"},
|
||||
{name = mobs_mc.items.ghast_tear, chance = 10/6, min = 0, max = 1, looting = "common", looting_ignore_chance = true},
|
||||
},
|
||||
animation = {
|
||||
stand_speed = 50, walk_speed = 50, run_speed = 50,
|
||||
|
@ -65,14 +64,6 @@ mcl_mobs:register_mob("mobs_mc:ghast", {
|
|||
makes_footstep_sound = false,
|
||||
instant_death = true,
|
||||
fire_resistant = true,
|
||||
can_spawn = function(pos)
|
||||
if not minetest.get_item_group(minetest.get_node(pos).name,"solid") then return false end
|
||||
local p1=vector.offset(pos,-2,1,-2)
|
||||
local p2=vector.offset(pos,2,5,2)
|
||||
local nn = minetest.find_nodes_in_area(p1,p2,{"air"})
|
||||
if #nn< 41 then return false end
|
||||
return true
|
||||
end,
|
||||
do_custom = function(self)
|
||||
if self.firing == true then
|
||||
self.base_texture = {"mobs_mc_ghast_firing.png"}
|
||||
|
@ -85,25 +76,23 @@ mcl_mobs:register_mob("mobs_mc:ghast", {
|
|||
})
|
||||
|
||||
|
||||
mcl_mobs:spawn_specific(
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:ghast",
|
||||
"nether",
|
||||
"ground",
|
||||
{
|
||||
"Nether",
|
||||
"SoulsandValley",
|
||||
"BasaltDelta",
|
||||
"Nether"
|
||||
},
|
||||
0,
|
||||
7,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
72000,
|
||||
18000,
|
||||
2,
|
||||
mcl_vars.mg_nether_min,
|
||||
mcl_vars.mg_nether_max)
|
||||
mobs_mc.spawn_height.nether_min,
|
||||
mobs_mc.spawn_height.nether_max)
|
||||
|
||||
-- fireball (projectile)
|
||||
mcl_mobs:register_arrow("mobs_mc:fireball", {
|
||||
mobs:register_arrow("mobs_mc:fireball", {
|
||||
visual = "sprite",
|
||||
visual_size = {x = 1, y = 1},
|
||||
textures = {"mcl_fire_fire_charge.png"},
|
||||
|
@ -116,12 +105,7 @@ mcl_mobs:register_arrow("mobs_mc:fireball", {
|
|||
full_punch_interval = 1.0,
|
||||
damage_groups = {fleshy = 6},
|
||||
}, nil)
|
||||
local p = self.object:get_pos()
|
||||
if p then
|
||||
mcl_mobs:boom(self, p, 1, true)
|
||||
else
|
||||
mcl_mobs:boom(self, player:get_pos(), 1, true)
|
||||
end
|
||||
mobs:boom(self, self.object:get_pos(), 1, true)
|
||||
end,
|
||||
|
||||
hit_mob = function(self, mob)
|
||||
|
@ -129,11 +113,11 @@ mcl_mobs:register_arrow("mobs_mc:fireball", {
|
|||
full_punch_interval = 1.0,
|
||||
damage_groups = {fleshy = 6},
|
||||
}, nil)
|
||||
mcl_mobs:boom(self, self.object:get_pos(), 1, true)
|
||||
mobs:boom(self, self.object:get_pos(), 1, true)
|
||||
end,
|
||||
|
||||
hit_node = function(self, pos, node)
|
||||
mcl_mobs:boom(self, pos, 1, true)
|
||||
mobs:boom(self, pos, 1, true)
|
||||
end
|
||||
})
|
||||
|
||||
|
@ -141,4 +125,4 @@ mcl_mobs:register_arrow("mobs_mc:fireball", {
|
|||
|
||||
|
||||
-- spawn eggs
|
||||
mcl_mobs:register_egg("mobs_mc:ghast", S("Ghast"), "#f9f9f9", "#bcbcbc", 0)
|
||||
mobs:register_egg("mobs_mc:ghast", S("Ghast"), "mobs_mc_spawn_icon_ghast.png", 0)
|
||||
|
|
|
@ -4,12 +4,10 @@
|
|||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:guardian", {
|
||||
mobs:register_mob("mobs_mc:guardian", {
|
||||
description = S("Guardian"),
|
||||
type = "monster",
|
||||
spawn_class = "hostile",
|
||||
spawn_in_group_min = 2,
|
||||
spawn_in_group = 4,
|
||||
hp_min = 30,
|
||||
hp_max = 30,
|
||||
xp_min = 10,
|
||||
|
@ -46,7 +44,7 @@ mcl_mobs:register_mob("mobs_mc:guardian", {
|
|||
},
|
||||
drops = {
|
||||
-- Greatly increased amounts of prismarine
|
||||
{name = "mcl_ocean:prismarine_shard",
|
||||
{name = mobs_mc.items.prismarine_shard,
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 32,
|
||||
|
@ -55,37 +53,37 @@ mcl_mobs:register_mob("mobs_mc:guardian", {
|
|||
|
||||
-- The following drops are approximations
|
||||
-- Fish / prismarine crystal
|
||||
{name = "mcl_fishing:fish_raw",
|
||||
{name = mobs_mc.items.fish_raw,
|
||||
chance = 4,
|
||||
min = 1,
|
||||
max = 1,
|
||||
looting = "common",},
|
||||
{name = "mcl_ocean:prismarine_crystals",
|
||||
{name = mobs_mc.items.prismarine_crystals,
|
||||
chance = 4,
|
||||
min = 1,
|
||||
max = 2,
|
||||
looting = "common",},
|
||||
|
||||
-- Rare drop: fish
|
||||
{name = "mcl_fishing:fish_raw",
|
||||
{name = mobs_mc.items.fish_raw,
|
||||
chance = 160, -- 2.5% / 4
|
||||
min = 1,
|
||||
max = 1,
|
||||
looting = "rare",
|
||||
looting_factor = 0.0025,},
|
||||
{name = "mcl_fishing:salmon_raw",
|
||||
{name = mobs_mc.items.salmon_raw,
|
||||
chance = 160,
|
||||
min = 1,
|
||||
max = 1,
|
||||
looting = "rare",
|
||||
looting_factor = 0.0025,},
|
||||
{name = "mcl_fishing:clownfish_raw",
|
||||
{name = mobs_mc.items.clownfish_raw,
|
||||
chance = 160,
|
||||
min = 1,
|
||||
max = 1,
|
||||
looting = "rare",
|
||||
looting_factor = 0.0025,},
|
||||
{name = "mcl_fishing:pufferfish_raw",
|
||||
{name = mobs_mc.items.pufferfish_raw,
|
||||
chance = 160,
|
||||
min = 1,
|
||||
max = 1,
|
||||
|
@ -94,14 +92,14 @@ mcl_mobs:register_mob("mobs_mc:guardian", {
|
|||
},
|
||||
fly = true,
|
||||
makes_footstep_sound = false,
|
||||
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
|
||||
fly_in = { mobs_mc.items.water_source, mobs_mc.items.river_water_source },
|
||||
jump = false,
|
||||
view_range = 16,
|
||||
})
|
||||
|
||||
-- Spawning disabled due to size issues
|
||||
-- TODO: Re-enable spawning
|
||||
--mcl_mobs:spawn_specific("mobs_mc:guardian", { "mcl_core:water_source", "mclx_core:river_water_source" }, { "mcl_core:water_source", "mclx_core:river_water_source" }, 0, minetest.LIGHT_MAX+1, 30, 25000, 2, mcl_vars.mg_overworld_min, mobs_mc.water_level - 10)
|
||||
--mobs:spawn_specific("mobs_mc:guardian", mobs_mc.spawn.water, mobs_mc.spawn_water, 0, minetest.LIGHT_MAX+1, 30, 25000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.water - 10)
|
||||
|
||||
-- spawn eggs
|
||||
mcl_mobs:register_egg("mobs_mc:guardian", S("Guardian"), "#5a8272", "#f17d31", 0)
|
||||
mobs:register_egg("mobs_mc:guardian", S("Guardian"), "mobs_mc_spawn_icon_guardian.png", 0)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:guardian_elder", {
|
||||
mobs:register_mob("mobs_mc:guardian_elder", {
|
||||
description = S("Elder Guardian"),
|
||||
type = "monster",
|
||||
spawn_class = "hostile",
|
||||
|
@ -49,51 +49,51 @@ mcl_mobs:register_mob("mobs_mc:guardian_elder", {
|
|||
-- TODO: Reduce # of drops when ocean monument is ready.
|
||||
|
||||
-- Greatly increased amounts of prismarine
|
||||
{name = "mcl_ocean:prismarine_shard",
|
||||
{name = mobs_mc.items.prismarine_shard,
|
||||
chance = 1,
|
||||
min = 1,
|
||||
max = 64,
|
||||
looting = "common",},
|
||||
|
||||
-- TODO: Only drop if killed by player
|
||||
{name = "mcl_sponges:sponge_wet",
|
||||
{name = mobs_mc.items.wet_sponge,
|
||||
chance = 1,
|
||||
min = 1,
|
||||
max = 1,},
|
||||
|
||||
-- The following drops are approximations
|
||||
-- Fish / prismarine crystal
|
||||
{name = "mcl_fishing:fish_raw",
|
||||
{name = mobs_mc.items.fish_raw,
|
||||
chance = 4,
|
||||
min = 1,
|
||||
max = 1,
|
||||
looting = "common",},
|
||||
{name = "mcl_ocean:prismarine_crystals",
|
||||
{name = mobs_mc.items.prismarine_crystals,
|
||||
chance = 1,
|
||||
min = 1,
|
||||
max = 10,
|
||||
looting = "common",},
|
||||
|
||||
-- Rare drop: fish
|
||||
{name = "mcl_fishing:fish_raw",
|
||||
{name = mobs_mc.items.fish_raw,
|
||||
chance = 160, -- 2.5% / 4
|
||||
min = 1,
|
||||
max = 1,
|
||||
looting = "rare",
|
||||
looting_factor = 0.01 / 4,},
|
||||
{name = "mcl_fishing:salmon_raw",
|
||||
{name = mobs_mc.items.salmon_raw,
|
||||
chance = 160,
|
||||
min = 1,
|
||||
max = 1,
|
||||
looting = "rare",
|
||||
looting_factor = 0.01 / 4,},
|
||||
{name = "mcl_fishing:clownfish_raw",
|
||||
{name = mobs_mc.items.clownfish_raw,
|
||||
chance = 160,
|
||||
min = 1,
|
||||
max = 1,
|
||||
looting = "rare",
|
||||
looting_factor = 0.01 / 4,},
|
||||
{name = "mcl_fishing:pufferfish_raw",
|
||||
{name = mobs_mc.items.pufferfish_raw,
|
||||
chance = 160,
|
||||
min = 1,
|
||||
max = 1,
|
||||
|
@ -102,16 +102,15 @@ mcl_mobs:register_mob("mobs_mc:guardian_elder", {
|
|||
},
|
||||
fly = true,
|
||||
makes_footstep_sound = false,
|
||||
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
|
||||
fly_in = { mobs_mc.items.water_source, mobs_mc.items.river_water_source },
|
||||
jump = false,
|
||||
view_range = 16,
|
||||
})
|
||||
|
||||
-- Spawning disabled due to size issues <- what do you mean? -j4i
|
||||
-- TODO: Re-enable spawning
|
||||
-- mcl_mobs:spawn_specific("mobs_mc:guardian_elder", { "mcl_core:water_source", "mclx_core:river_water_source" }, { "mcl_core:water_source", "mclx_core:river_water_source" }, 0, minetest.LIGHT_MAX+1, 30, 40000, 2, mcl_vars.mg_overworld_min, mobs_mc.water_level-18)
|
||||
-- mobs:spawn_specific("mobs_mc:guardian_elder", mobs_mc.spawn.water, mobs_mc.spawn_water, 0, minetest.LIGHT_MAX+1, 30, 40000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.water-18)
|
||||
|
||||
-- spawn eggs
|
||||
mcl_mobs:register_egg("mobs_mc:guardian_elder", S("Elder Guardian"), "#ceccba", "#747693", 0)
|
||||
|
||||
mobs:register_egg("mobs_mc:guardian_elder", S("Elder Guardian"), "mobs_mc_spawn_icon_guardian_elder.png", 0)
|
||||
|
||||
|
|
|
@ -34,30 +34,6 @@ local horse_extra_texture = function(horse)
|
|||
return textures
|
||||
end
|
||||
|
||||
|
||||
local function get_drops(self)
|
||||
self.drops = {}
|
||||
table.insert(self.drops,
|
||||
{name = "mcl_mobitems:leather",
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,
|
||||
looting = "common",
|
||||
})
|
||||
if self._saddle then
|
||||
table.insert(self.drops,{name = "mcl_mobitems:saddle",
|
||||
chance = 1,
|
||||
min = 1,
|
||||
max = 1,})
|
||||
end
|
||||
if self._chest then
|
||||
table.insert(self.drops,{name = "mcl_chests:chest",
|
||||
chance = 1,
|
||||
min = 1,
|
||||
max = 1,})
|
||||
end
|
||||
end
|
||||
|
||||
-- Helper functions to determine equipment rules
|
||||
local can_equip_horse_armor = function(entity_id)
|
||||
return entity_id == "mobs_mc:horse" or entity_id == "mobs_mc:skeleton_horse" or entity_id == "mobs_mc:zombie_horse"
|
||||
|
@ -105,27 +81,11 @@ for b=1, #horse_base do
|
|||
end
|
||||
end
|
||||
|
||||
-- in e7898352d890c2414af653eba624939df9c0b8b4 (0.76-dev) all items from mobs_mc were moved to mcl_mobitems
|
||||
-- this results in existing horses wearing armor would still have the old texture filename in their
|
||||
-- properties this function updates them. It should be removed some time in the future when we can be
|
||||
-- reasonably sure all horses that want it get the new nexture.
|
||||
local function update_textures(self)
|
||||
local old = "mobs_mc_horse_armor_"
|
||||
local txt = self.object:get_properties().textures
|
||||
if txt[2]:find(old) then
|
||||
txt[2] = txt[2]:gsub(old,"mcl_mobitems_horse_armor_")
|
||||
self.object:set_properties({textures=txt})
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Horse
|
||||
local horse = {
|
||||
description = S("Horse"),
|
||||
type = "animal",
|
||||
spawn_class = "passive",
|
||||
spawn_in_group_min = 2,
|
||||
spawn_in_group = 6,
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_horse.b3d",
|
||||
visual_size = {x=3.0, y=3.0},
|
||||
|
@ -154,14 +114,7 @@ local horse = {
|
|||
fly = false,
|
||||
walk_chance = 60,
|
||||
view_range = 16,
|
||||
follow = {
|
||||
"mcl_core:apple",
|
||||
"mcl_core:sugar",
|
||||
"mcl_farming:wheat_item",
|
||||
"mcl_farming:hay_block",
|
||||
"mcl_core:apple_gold",
|
||||
"mcl_farming:carrot_item_gold",
|
||||
},
|
||||
follow = mobs_mc.follow.horse,
|
||||
passive = true,
|
||||
hp_min = 15,
|
||||
hp_max = 30,
|
||||
|
@ -172,13 +125,13 @@ local horse = {
|
|||
jump = true,
|
||||
jump_height = 5.75, -- can clear 2.5 blocks
|
||||
drops = {
|
||||
{name = "mcl_mobitems:leather",
|
||||
{name = mobs_mc.items.leather,
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,
|
||||
looting = "common",},
|
||||
},
|
||||
on_spawn = update_textures,
|
||||
|
||||
do_custom = function(self, dtime)
|
||||
|
||||
-- set needed values if not already present
|
||||
|
@ -208,7 +161,7 @@ local horse = {
|
|||
-- Some weird human is riding. Buck them off?
|
||||
if self.driver and not self.tamed and self.buck_off_time <= 0 then
|
||||
if math.random() < 0.2 then
|
||||
mcl_mobs.detach(self.driver, {x = 1, y = 0, z = 1})
|
||||
mobs.detach(self.driver, {x = 1, y = 0, z = 1})
|
||||
-- TODO bucking animation
|
||||
else
|
||||
-- Nah, can't be bothered. Think about it again in one second
|
||||
|
@ -229,7 +182,7 @@ local horse = {
|
|||
-- if driver present and horse has a saddle allow control of horse
|
||||
if self.driver and self._saddle then
|
||||
|
||||
mcl_mobs.drive(self, "walk", "stand", false, dtime)
|
||||
mobs.drive(self, "walk", "stand", false, dtime)
|
||||
|
||||
return false -- skip rest of mob functions
|
||||
end
|
||||
|
@ -241,11 +194,11 @@ local horse = {
|
|||
|
||||
-- drop saddle when horse is killed while riding
|
||||
if self._saddle then
|
||||
minetest.add_item(pos, "mcl_mobitems:saddle")
|
||||
minetest.add_item(pos, mobs_mc.items.saddle)
|
||||
end
|
||||
-- also detach from horse properly
|
||||
if self.driver then
|
||||
mcl_mobs.detach(self.driver, {x = 1, y = 0, z = 1})
|
||||
mobs.detach(self.driver, {x = 1, y = 0, z = 1})
|
||||
end
|
||||
|
||||
end,
|
||||
|
@ -261,27 +214,6 @@ local horse = {
|
|||
local iname = item:get_name()
|
||||
local heal = 0
|
||||
|
||||
if self._inv_id then
|
||||
if not self._chest and item:get_name() == "mcl_chests:chest" then
|
||||
item:take_item()
|
||||
clicker:set_wielded_item(item)
|
||||
self._chest = true
|
||||
-- Update texture
|
||||
if not self._naked_texture then
|
||||
-- Base horse texture without chest or saddle
|
||||
self._naked_texture = self.base_texture[2]
|
||||
end
|
||||
local tex = horse_extra_texture(self)
|
||||
self.base_texture = tex
|
||||
self.object:set_properties({textures = self.base_texture})
|
||||
get_drops(self)
|
||||
return
|
||||
elseif self._chest and clicker:get_player_control().sneak then
|
||||
mcl_entity_invs.show_inv_form(self,clicker)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Taming
|
||||
self.temper = self.temper or (math.random(1,100))
|
||||
|
||||
|
@ -290,20 +222,20 @@ local horse = {
|
|||
|
||||
-- Feeding, intentionally not using mobs:feed_tame because horse taming is
|
||||
-- different and more complicated
|
||||
if (iname == "mcl_core:sugar") then
|
||||
if (iname == mobs_mc.items.sugar) then
|
||||
temper_increase = 3
|
||||
elseif (iname == "mcl_farming:wheat_item") then
|
||||
elseif (iname == mobs_mc.items.wheat) then
|
||||
temper_increase = 3
|
||||
elseif (iname == "mcl_core:apple") then
|
||||
elseif (iname == mobs_mc.items.apple) then
|
||||
temper_increase = 3
|
||||
elseif (iname == "mcl_farming:carrot_item_gold") then
|
||||
elseif (iname == mobs_mc.items.golden_carrot) then
|
||||
temper_increase = 5
|
||||
elseif (iname == "mcl_core:apple_gold") then
|
||||
elseif (iname == mobs_mc.items.golden_apple) then
|
||||
temper_increase = 10
|
||||
-- Trying to ride
|
||||
elseif not self.driver then
|
||||
self.object:set_properties({stepheight = 1.1})
|
||||
mcl_mobs.attach(self, clicker)
|
||||
mobs.attach(self, clicker)
|
||||
self.buck_off_time = 40 -- TODO how long does it take in minecraft?
|
||||
if self.temper > 100 then
|
||||
self.tamed = true -- NOTE taming can only be finished by riding the horse
|
||||
|
@ -315,7 +247,7 @@ local horse = {
|
|||
|
||||
-- Clicking on the horse while riding ==> unmount
|
||||
elseif self.driver and self.driver == clicker then
|
||||
mcl_mobs.detach(clicker, {x = 1, y = 0, z = 1})
|
||||
mobs.detach(clicker, {x = 1, y = 0, z = 1})
|
||||
end
|
||||
|
||||
-- If nothing happened temper_increase = 0 and addition does nothing
|
||||
|
@ -326,31 +258,31 @@ local horse = {
|
|||
|
||||
if can_breed(self.name) then
|
||||
-- Breed horse with golden apple or golden carrot
|
||||
if (iname == "mcl_core:apple_gold") then
|
||||
if (iname == mobs_mc.items.golden_apple) then
|
||||
heal = 10
|
||||
elseif (iname == "mcl_farming:carrot_item_gold") then
|
||||
elseif (iname == mobs_mc.items.golden_carrot) then
|
||||
heal = 4
|
||||
end
|
||||
if heal > 0 and mcl_mobs:feed_tame(self, clicker, heal, true, false) then
|
||||
if heal > 0 and mobs:feed_tame(self, clicker, heal, true, false) then
|
||||
return
|
||||
end
|
||||
end
|
||||
-- Feed with anything else
|
||||
-- TODO heal amounts don't work
|
||||
if (iname == "mcl_core:sugar") then
|
||||
if (iname == mobs_mc.items.sugar) then
|
||||
heal = 1
|
||||
elseif (iname == "mcl_farming:wheat_item") then
|
||||
elseif (iname == mobs_mc.items.wheat) then
|
||||
heal = 2
|
||||
elseif (iname == "mcl_core:apple") then
|
||||
elseif (iname == mobs_mc.items.apple) then
|
||||
heal = 3
|
||||
elseif (iname == "mcl_farming:hay_block") then
|
||||
elseif (iname == mobs_mc.items.hay_bale) then
|
||||
heal = 20
|
||||
end
|
||||
if heal > 0 and mcl_mobs:feed_tame(self, clicker, heal, false, false) then
|
||||
if heal > 0 and mobs:feed_tame(self, clicker, heal, false, false) then
|
||||
return
|
||||
end
|
||||
|
||||
if mcl_mobs:protect(self, clicker) then
|
||||
if mobs:protect(self, clicker) then
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -362,11 +294,11 @@ local horse = {
|
|||
-- detatch player already riding horse
|
||||
if self.driver and clicker == self.driver then
|
||||
|
||||
mcl_mobs.detach(clicker, {x = 1, y = 0, z = 1})
|
||||
mobs.detach(clicker, {x = 1, y = 0, z = 1})
|
||||
|
||||
-- Put on saddle if tamed
|
||||
elseif not self.driver and not self._saddle
|
||||
and iname == "mcl_mobitems:saddle" then
|
||||
and iname == mobs_mc.items.saddle then
|
||||
|
||||
-- Put on saddle and take saddle from player's inventory
|
||||
local w = clicker:get_wielded_item()
|
||||
|
@ -385,7 +317,6 @@ local horse = {
|
|||
self.base_texture = tex
|
||||
self.object:set_properties({textures = self.base_texture})
|
||||
minetest.sound_play({name = "mcl_armor_equip_leather"}, {gain=0.5, max_hear_distance=12, pos=self.object:get_pos()}, true)
|
||||
get_drops(self)
|
||||
|
||||
-- Put on horse armor if tamed
|
||||
elseif can_equip_horse_armor(self.name) and not self.driver and not self._horse_armor
|
||||
|
@ -424,18 +355,18 @@ local horse = {
|
|||
elseif not self.driver and self._saddle then
|
||||
|
||||
self.object:set_properties({stepheight = 1.1})
|
||||
mcl_mobs.attach(self, clicker)
|
||||
mobs.attach(self, clicker)
|
||||
|
||||
-- Used to capture horse
|
||||
elseif not self.driver and iname ~= "" then
|
||||
mcl_mobs:capture_mob(self, clicker, 0, 5, 60, false, nil)
|
||||
mobs:capture_mob(self, clicker, 0, 5, 60, false, nil)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
on_breed = function(parent1, parent2)
|
||||
local pos = parent1.object:get_pos()
|
||||
local child = mcl_mobs:spawn_child(pos, parent1.name)
|
||||
local child = mobs:spawn_child(pos, parent1.name)
|
||||
if child then
|
||||
local ent_c = child:get_luaentity()
|
||||
local p = math.random(1, 2)
|
||||
|
@ -484,7 +415,7 @@ local horse = {
|
|||
end,
|
||||
}
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:horse", horse)
|
||||
mobs:register_mob("mobs_mc:horse", horse)
|
||||
|
||||
-- Skeleton horse
|
||||
local skeleton_horse = table.copy(horse)
|
||||
|
@ -493,7 +424,7 @@ skeleton_horse.breath_max = -1
|
|||
skeleton_horse.armor = {undead = 100, fleshy = 100}
|
||||
skeleton_horse.textures = {{"blank.png", "mobs_mc_horse_skeleton.png", "blank.png"}}
|
||||
skeleton_horse.drops = {
|
||||
{name = "mcl_mobitems:bone",
|
||||
{name = mobs_mc.items.bone,
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,},
|
||||
|
@ -507,7 +438,7 @@ skeleton_horse.sounds = {
|
|||
distance = 16,
|
||||
}
|
||||
skeleton_horse.harmed_by_heal = true
|
||||
mcl_mobs:register_mob("mobs_mc:skeleton_horse", skeleton_horse)
|
||||
mobs:register_mob("mobs_mc:skeleton_horse", skeleton_horse)
|
||||
|
||||
-- Zombie horse
|
||||
local zombie_horse = table.copy(horse)
|
||||
|
@ -516,7 +447,7 @@ zombie_horse.breath_max = -1
|
|||
zombie_horse.armor = {undead = 100, fleshy = 100}
|
||||
zombie_horse.textures = {{"blank.png", "mobs_mc_horse_zombie.png", "blank.png"}}
|
||||
zombie_horse.drops = {
|
||||
{name = "mcl_mobitems:rotten_flesh",
|
||||
{name = mobs_mc.items.rotten_flesh,
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,},
|
||||
|
@ -531,15 +462,13 @@ zombie_horse.sounds = {
|
|||
distance = 16,
|
||||
}
|
||||
zombie_horse.harmed_by_heal = true
|
||||
mcl_mobs:register_mob("mobs_mc:zombie_horse", zombie_horse)
|
||||
mobs:register_mob("mobs_mc:zombie_horse", zombie_horse)
|
||||
|
||||
-- Donkey
|
||||
local d = 0.86 -- donkey scale
|
||||
local donkey = table.copy(horse)
|
||||
donkey.description = S("Donkey")
|
||||
donkey.textures = {{"blank.png", "mobs_mc_donkey.png", "blank.png"}}
|
||||
donkey.spawn_in_group = 3
|
||||
donkey.spawn_in_group_min = 1
|
||||
donkey.animation = {
|
||||
speed_normal = 25,
|
||||
stand_start = 0, stand_end = 0,
|
||||
|
@ -564,9 +493,8 @@ donkey.collisionbox = {
|
|||
donkey.jump = true
|
||||
donkey.jump_height = 3.75 -- can clear 1 block height
|
||||
|
||||
mobs:register_mob("mobs_mc:donkey", donkey)
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:donkey", donkey)
|
||||
mcl_entity_invs.register_inv("mobs_mc:donkey","Donkey",15,true)
|
||||
-- Mule
|
||||
local m = 0.94
|
||||
local mule = table.copy(donkey)
|
||||
|
@ -583,60 +511,64 @@ mule.collisionbox = {
|
|||
horse.collisionbox[5] * m,
|
||||
horse.collisionbox[6] * m,
|
||||
}
|
||||
mcl_mobs:register_mob("mobs_mc:mule", mule)
|
||||
mcl_entity_invs.register_inv("mobs_mc:mule","Mule",15,true)
|
||||
mobs:register_mob("mobs_mc:mule", mule)
|
||||
|
||||
--===========================
|
||||
--Spawn Function
|
||||
mcl_mobs:spawn_specific(
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:horse",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"flat",
|
||||
"Plains",
|
||||
"Plains_beach",
|
||||
"SunflowerPlains",
|
||||
"Savanna",
|
||||
"Savanna_beach",
|
||||
"SavannaM",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"ColdTaiga",
|
||||
"SunflowerPlains",
|
||||
"RoofedForest",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"ExtremeHillsM",
|
||||
"BirchForestM",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
15000,
|
||||
4,
|
||||
mobs_mc.water_level+3,
|
||||
mcl_vars.mg_overworld_max)
|
||||
mobs_mc.spawn_height.water+3,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
mcl_mobs:spawn_specific(
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:donkey",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"flat",
|
||||
"Plains",
|
||||
"Plains_beach",
|
||||
"SunflowerPlains",
|
||||
"Savanna",
|
||||
"Savanna_beach",
|
||||
"SavannaM",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"Mesa",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
},
|
||||
9,
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
15000,
|
||||
4,
|
||||
mobs_mc.water_level+3,
|
||||
mcl_vars.mg_overworld_max)
|
||||
mobs_mc.spawn_height.water+3,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn eggs
|
||||
mcl_mobs:register_egg("mobs_mc:horse", S("Horse"), "#c09e7d", "#eee500", 0)
|
||||
mcl_mobs:register_egg("mobs_mc:skeleton_horse", S("Skeleton Horse"), "#68684f", "#e5e5d8", 0)
|
||||
--mobs:register_egg("mobs_mc:zombie_horse", S("Zombie Horse"), "#2a5a37", "#84d080", 0)
|
||||
mcl_mobs:register_egg("mobs_mc:donkey", S("Donkey"), "#534539", "#867566", 0)
|
||||
mcl_mobs:register_egg("mobs_mc:mule", S("Mule"), "#1b0200", "#51331d", 0)
|
||||
mobs:register_egg("mobs_mc:horse", S("Horse"), "mobs_mc_spawn_icon_horse.png", 0)
|
||||
mobs:register_egg("mobs_mc:skeleton_horse", S("Skeleton Horse"), "mobs_mc_spawn_icon_horse_skeleton.png", 0)
|
||||
--mobs:register_egg("mobs_mc:zombie_horse", S("Zombie Horse"), "mobs_mc_spawn_icon_horse_zombie.png", 0)
|
||||
mobs:register_egg("mobs_mc:donkey", S("Donkey"), "mobs_mc_spawn_icon_donkey.png", 0)
|
||||
mobs:register_egg("mobs_mc:mule", S("Mule"), "mobs_mc_spawn_icon_mule.png", 0)
|
||||
|
|
|
@ -2,100 +2,45 @@
|
|||
--maikerumine
|
||||
--made for MC like Survival game
|
||||
--License for code WTFPL and otherwise stated in readmes
|
||||
mobs_mc = {}
|
||||
|
||||
local pr = PseudoRandom(os.time()*5)
|
||||
local path = minetest.get_modpath("mobs_mc")
|
||||
|
||||
local offsets = {}
|
||||
for x=-2, 2 do
|
||||
for z=-2, 2 do
|
||||
table.insert(offsets, {x=x, y=0, z=z})
|
||||
end
|
||||
if not minetest.get_modpath("mobs_mc_gameconfig") then
|
||||
mobs_mc = {}
|
||||
end
|
||||
|
||||
--[[ Periodically check and teleport mob to owner if not sitting (order ~= "sit") and
|
||||
the owner is too far away. To be used with do_custom. Note: Optimized for mobs smaller than 1×1×1.
|
||||
Larger mobs might have space problems after teleportation.
|
||||
-- For utility functions
|
||||
mobs_mc.tools = {}
|
||||
|
||||
* dist: Minimum required distance from owner to teleport. Default: 12
|
||||
* teleport_check_interval: Optional. Interval in seconds to check the mob teleportation. Default: 4 ]]
|
||||
mobs_mc.make_owner_teleport_function = function(dist, teleport_check_interval)
|
||||
return function(self, dtime)
|
||||
-- No teleportation if no owner or if sitting
|
||||
if not self.owner or self.order == "sit" then
|
||||
return
|
||||
-- This function checks if the item ID has been overwritten and returns true if it is unchanged
|
||||
if minetest.get_modpath("mobs_mc_gameconfig") and mobs_mc.override and mobs_mc.override.items then
|
||||
mobs_mc.is_item_variable_overridden = function(id)
|
||||
return mobs_mc.override.items[id] == nil
|
||||
end
|
||||
if not teleport_check_interval then
|
||||
teleport_check_interval = 4
|
||||
end
|
||||
if not dist then
|
||||
dist = 12
|
||||
end
|
||||
if self._teleport_timer == nil then
|
||||
self._teleport_timer = teleport_check_interval
|
||||
return
|
||||
end
|
||||
self._teleport_timer = self._teleport_timer - dtime
|
||||
if self._teleport_timer <= 0 then
|
||||
self._teleport_timer = teleport_check_interval
|
||||
local mob_pos = self.object:get_pos()
|
||||
local owner = minetest.get_player_by_name(self.owner)
|
||||
if not owner then
|
||||
-- No owner found, no teleportation
|
||||
return
|
||||
end
|
||||
local owner_pos = owner:get_pos()
|
||||
local dist_from_owner = vector.distance(owner_pos, mob_pos)
|
||||
if dist_from_owner > dist then
|
||||
-- Check for nodes below air in a 5×1×5 area around the owner position
|
||||
local check_offsets = table.copy(offsets)
|
||||
-- Attempt to place mob near player. Must be placed on walkable node below a non-walkable one. Place inside that air node.
|
||||
while #check_offsets > 0 do
|
||||
local r = pr:next(1, #check_offsets)
|
||||
local telepos = vector.add(owner_pos, check_offsets[r])
|
||||
local telepos_below = {x=telepos.x, y=telepos.y-1, z=telepos.z}
|
||||
table.remove(check_offsets, r)
|
||||
-- Long story short, spawn on a platform
|
||||
local trynode = minetest.registered_nodes[minetest.get_node(telepos).name]
|
||||
local trybelownode = minetest.registered_nodes[minetest.get_node(telepos_below).name]
|
||||
if trynode and not trynode.walkable and
|
||||
trybelownode and trybelownode.walkable then
|
||||
-- Correct position found! Let's teleport.
|
||||
self.object:set_pos(telepos)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function is_forbidden_node(pos, node)
|
||||
node = node or minetest.get_node(pos)
|
||||
return minetest.get_item_group(node.name, "stair") > 0 or minetest.get_item_group(node.name, "slab") > 0 or minetest.get_item_group(node.name, "carpet") > 0
|
||||
end
|
||||
|
||||
function mcl_mobs:spawn_abm_check(pos, node, name)
|
||||
-- Don't spawn monsters on mycelium
|
||||
if (node.name == "mcl_core:mycelium" or node.name == "mcl_core:mycelium_snow") and minetest.registered_entities[name].type == "monster" then
|
||||
else
|
||||
-- No items are overwritten, so always return true
|
||||
mobs_mc.is_item_variable_overridden = function(id)
|
||||
return true
|
||||
--Don't Spawn mobs on stairs, slabs, or carpets
|
||||
elseif is_forbidden_node(pos, node) or is_forbidden_node(vector.add(pos, vector.new(0, 1, 0))) then
|
||||
return true
|
||||
-- Spawn on opaque or liquid nodes
|
||||
elseif minetest.get_item_group(node.name, "opaque") ~= 0 or minetest.registered_nodes[node.name].liquidtype ~= "none" or node.name == "mcl_core:grass_path" then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Reject everything else
|
||||
return true
|
||||
end
|
||||
|
||||
mobs_mc.shears_wear = 276
|
||||
mobs_mc.water_level = tonumber(minetest.settings:get("water_level")) or 0
|
||||
--MOB ITEMS SELECTOR SWITCH
|
||||
dofile(path .. "/0_gameconfig.lua")
|
||||
--Items
|
||||
dofile(path .. "/1_items_default.lua")
|
||||
|
||||
-- Bow, arrow and throwables
|
||||
dofile(path .. "/2_throwing.lua")
|
||||
|
||||
-- Shared functions
|
||||
dofile(path .. "/3_shared.lua")
|
||||
|
||||
--Mob heads
|
||||
dofile(path .. "/4_heads.lua")
|
||||
|
||||
dofile(path .. "/5_spawn_abm_check.lua")
|
||||
|
||||
-- Animals
|
||||
local path = minetest.get_modpath("mobs_mc")
|
||||
dofile(path .. "/bat.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||
dofile(path .. "/rabbit.lua") -- Mesh and animation byExeterDad
|
||||
dofile(path .. "/chicken.lua") -- Mesh and animation by Pavel_S
|
||||
|
@ -112,9 +57,10 @@ dofile(path .. "/squid.lua") -- Animation, sound and egg texture by daufinsyd
|
|||
|
||||
-- NPCs
|
||||
dofile(path .. "/villager.lua") -- KrupnoPavel Mesh and animation by toby109tt / https://github.com/22i
|
||||
-- Agent texture missing
|
||||
--dofile(path .. "/agent.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||
|
||||
-- Illagers and witch
|
||||
dofile(path .. "/pillager.lua") -- Mesh by KrupnoPavel and MrRar, animation by MrRar
|
||||
dofile(path .. "/villager_evoker.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||
dofile(path .. "/villager_vindicator.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||
dofile(path .. "/villager_zombie.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||
|
@ -143,8 +89,12 @@ dofile(path .. "/slime+magma_cube.lua") -- Wuzzy
|
|||
dofile(path .. "/spider.lua") -- Spider by AspireMint (fishyWET (CC-BY-SA 3.0 license for texture)
|
||||
dofile(path .. "/vex.lua") -- KrupnoPavel
|
||||
dofile(path .. "/wither.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||
|
||||
dofile(path .. "/cod.lua")
|
||||
dofile(path .. "/salmon.lua")
|
||||
dofile(path .. "/tropical_fish.lua")
|
||||
dofile(path .. "/dolphin.lua")
|
||||
--NOTES:
|
||||
--
|
||||
--[[
|
||||
COLISIONBOX in minetest press f5 to see where you are looking at then put these wool collor nodes on the ground in direction of north/east/west... to make colisionbox editing easier
|
||||
#1west-pink/#2down/#3south-blue/#4east-red/#5up/#6north-yelow
|
||||
{-1, -0.5, -1, 1, 3, 1}, Right, Bottom, Back, Left, Top, Front
|
||||
--]]
|
||||
--
|
||||
--
|
||||
|
|
|
@ -9,9 +9,9 @@ local S = minetest.get_translator("mobs_mc")
|
|||
--################### IRON GOLEM
|
||||
--###################
|
||||
|
||||
local etime = 0
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:iron_golem", {
|
||||
|
||||
mobs:register_mob("mobs_mc:iron_golem", {
|
||||
description = S("Iron Golem"),
|
||||
type = "npc",
|
||||
spawn_class = "passive",
|
||||
|
@ -22,9 +22,6 @@ mcl_mobs:register_mob("mobs_mc:iron_golem", {
|
|||
collisionbox = {-0.7, -0.01, -0.7, 0.7, 2.69, 0.7},
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_iron_golem.b3d",
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 3.38,
|
||||
curiosity = 10,
|
||||
textures = {
|
||||
{"mobs_mc_iron_golem.png"},
|
||||
},
|
||||
|
@ -40,37 +37,16 @@ mcl_mobs:register_mob("mobs_mc:iron_golem", {
|
|||
run_velocity = 1.2,
|
||||
-- Approximation
|
||||
damage = 14,
|
||||
knock_back = false,
|
||||
reach = 3,
|
||||
group_attack = true,
|
||||
attacks_monsters = true,
|
||||
attack_type = "dogfight",
|
||||
_got_poppy = false,
|
||||
pick_up = {"mcl_flowers:poppy"},
|
||||
on_pick_up = function(self,n)
|
||||
if n.itemstring:find("mcl_flowers:poppy") then
|
||||
if not self._got_poppy then
|
||||
self._got_poppy=true
|
||||
return
|
||||
end
|
||||
return true
|
||||
end
|
||||
end,
|
||||
replace_what = {"mcl_flowers:poppy"},
|
||||
replace_with = {"air"},
|
||||
on_replace = function(self, pos, oldnode, newnode)
|
||||
if not self.got_poppy and oldnode.name == "mcl_flowers:poppy" then
|
||||
self._got_poppy=true
|
||||
return
|
||||
end
|
||||
return false
|
||||
end,
|
||||
drops = {
|
||||
{name = "mcl_core:iron_ingot",
|
||||
{name = mobs_mc.items.iron_ingot,
|
||||
chance = 1,
|
||||
min = 3,
|
||||
max = 5,},
|
||||
{name = "mcl_flowers:poppy",
|
||||
{name = mobs_mc.items.poppy,
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,},
|
||||
|
@ -84,19 +60,12 @@ mcl_mobs:register_mob("mobs_mc:iron_golem", {
|
|||
punch_start = 40, punch_end = 50,
|
||||
},
|
||||
jump = true,
|
||||
on_step = function(self,dtime)
|
||||
etime = etime + dtime
|
||||
if etime > 10 then
|
||||
if self._home and vector.distance(self._home,self.object:get_pos()) > 50 then
|
||||
mcl_mobs:gopath(self,self._home)
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
-- spawn eggs
|
||||
mcl_mobs:register_egg("mobs_mc:iron_golem", S("Iron Golem"), "#3b3b3b", "#f57223", 0)
|
||||
mobs:register_egg("mobs_mc:iron_golem", S("Iron Golem"), "mobs_mc_spawn_icon_iron_golem.png", 0)
|
||||
|
||||
|
||||
--[[ This is to be called when a pumpkin or jack'o lantern has been placed. Recommended: In the on_construct function of the node.
|
||||
This summons an iron golen if placing the pumpkin created an iron golem summon pattern:
|
||||
|
@ -110,7 +79,7 @@ I = Iron block
|
|||
. = Air
|
||||
]]
|
||||
|
||||
function mobs_mc.check_iron_golem_summon(pos)
|
||||
mobs_mc.tools.check_iron_golem_summon = function(pos)
|
||||
local checks = {
|
||||
-- These are the possible placement patterns, with offset from the pumpkin block.
|
||||
-- These tables include the positions of the iron blocks (1-4) and air blocks (5-8)
|
||||
|
@ -168,7 +137,7 @@ function mobs_mc.check_iron_golem_summon(pos)
|
|||
for i=1, 4 do
|
||||
local cpos = vector.add(pos, checks[c][i])
|
||||
local node = minetest.get_node(cpos)
|
||||
if node.name ~= "mcl_core:ironblock" then
|
||||
if node.name ~= mobs_mc.items.iron_block then
|
||||
ok = false
|
||||
break
|
||||
end
|
||||
|
|
|
@ -24,52 +24,15 @@ local carpets = {
|
|||
unicolor_light_blue = { "mcl_wool:light_blue_carpet", "light_blue" },
|
||||
}
|
||||
|
||||
local function get_drops(self)
|
||||
self.drops = {}
|
||||
table.insert(self.drops,
|
||||
{name = "mcl_mobitems:leather",
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,
|
||||
looting = "common",
|
||||
})
|
||||
if self.carpet then
|
||||
table.insert(self.drops,{name = self.carpet,
|
||||
chance = 1,
|
||||
min = 1,
|
||||
max = 1,})
|
||||
end
|
||||
if self._has_chest then
|
||||
table.insert(self.drops,{name = "mcl_chests:chest",
|
||||
chance = 1,
|
||||
min = 1,
|
||||
max = 1,})
|
||||
end
|
||||
end
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:llama", {
|
||||
mobs:register_mob("mobs_mc:llama", {
|
||||
description = S("Llama"),
|
||||
type = "animal",
|
||||
spawn_class = "passive",
|
||||
passive = false,
|
||||
attack_type = "shoot",
|
||||
shoot_interval = 5.5,
|
||||
arrow = "mobs_mc:llamaspit",
|
||||
shoot_offset = 1, --3.5 *would* be a good value visually but it somehow messes with the projectiles trajectory
|
||||
spawn_in_group_min = 4,
|
||||
spawn_in_group = 6,
|
||||
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 11,
|
||||
head_eye_height = 3,
|
||||
horrizonatal_head_height=0,
|
||||
curiosity = 60,
|
||||
head_yaw = "z",
|
||||
|
||||
hp_min = 15,
|
||||
hp_max = 30,
|
||||
xp_min = 1,
|
||||
xp_max = 3,
|
||||
passive = false,
|
||||
collisionbox = {-0.45, -0.01, -0.45, 0.45, 1.86, 0.45},
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_llama.b3d",
|
||||
|
@ -79,15 +42,17 @@ mcl_mobs:register_mob("mobs_mc:llama", {
|
|||
{"blank.png", "blank.png", "mobs_mc_llama_gray.png"},
|
||||
{"blank.png", "blank.png", "mobs_mc_llama_white.png"},
|
||||
{"blank.png", "blank.png", "mobs_mc_llama.png"},
|
||||
-- TODO: Add llama carpet textures (Pixel Perfection seems to use verbatim copy from Minecraft :-( )
|
||||
},
|
||||
visual_size = {x=3, y=3},
|
||||
makes_footstep_sound = true,
|
||||
runaway = false,
|
||||
runaway = true,
|
||||
walk_velocity = 1,
|
||||
run_velocity = 4.4,
|
||||
follow_velocity = 4.4,
|
||||
floats = 1,
|
||||
drops = {
|
||||
{name = "mcl_mobitems:leather",
|
||||
{name = mobs_mc.items.leather,
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,
|
||||
|
@ -101,35 +66,43 @@ mcl_mobs:register_mob("mobs_mc:llama", {
|
|||
distance = 16,
|
||||
},
|
||||
animation = {
|
||||
stand_start = 0, stand_end = 0,
|
||||
walk_start = 0, walk_end = 40, walk_speed = 35,
|
||||
run_start = 0, run_end = 40, run_speed = 50,
|
||||
speed_normal = 24,
|
||||
run_speed = 60,
|
||||
run_start = 0,
|
||||
run_end = 40,
|
||||
stand_start = 0,
|
||||
stand_end = 0,
|
||||
walk_start = 0,
|
||||
walk_end = 40,
|
||||
hurt_start = 118,
|
||||
hurt_end = 154,
|
||||
death_start = 154,
|
||||
death_end = 179,
|
||||
eat_start = 49,
|
||||
eat_end = 78,
|
||||
look_start = 78,
|
||||
look_end = 108,
|
||||
},
|
||||
child_animations = {
|
||||
stand_start = 41, stand_end = 41,
|
||||
walk_start = 41, walk_end = 81, walk_speed = 50,
|
||||
run_start = 41, run_end = 81, run_speed = 75,
|
||||
},
|
||||
follow = { "mcl_farming:wheat_item", "mcl_farming:hay_block" },
|
||||
follow = mobs_mc.follow.llama,
|
||||
view_range = 16,
|
||||
do_custom = function(self, dtime)
|
||||
|
||||
-- set needed values if not already present
|
||||
if not self.v3 then
|
||||
self.v3 = 0
|
||||
if not self.v2 then
|
||||
self.v2 = 0
|
||||
self.max_speed_forward = 4
|
||||
self.max_speed_reverse = 2
|
||||
self.accel = 4
|
||||
self.terrain_type = 3
|
||||
self.driver_attach_at = {x = 0, y = 12.7, z = -5}
|
||||
self.driver_eye_offset = {x = 0, y = 6, z = 0}
|
||||
self.driver_attach_at = {x = 0, y = 4.17, z = -1.5}
|
||||
self.driver_eye_offset = {x = 0, y = 3, z = 0}
|
||||
self.driver_scale = {x = 1/self.visual_size.x, y = 1/self.visual_size.y}
|
||||
end
|
||||
|
||||
-- if driver present allow control of llama
|
||||
if self.driver then
|
||||
|
||||
mcl_mobs.drive(self, "walk", "stand", false, dtime)
|
||||
mobs.drive(self, "walk", "stand", false, dtime)
|
||||
|
||||
return false -- skip rest of mob functions
|
||||
end
|
||||
|
@ -141,7 +114,7 @@ mcl_mobs:register_mob("mobs_mc:llama", {
|
|||
|
||||
-- detach from llama properly
|
||||
if self.driver then
|
||||
mcl_mobs.detach(self.driver, {x = 1, y = 0, z = 1})
|
||||
mobs.detach(self.driver, {x = 1, y = 0, z = 1})
|
||||
end
|
||||
|
||||
end,
|
||||
|
@ -154,32 +127,20 @@ mcl_mobs:register_mob("mobs_mc:llama", {
|
|||
end
|
||||
|
||||
local item = clicker:get_wielded_item()
|
||||
if item:get_name() == "mcl_farming:hay_block" then
|
||||
if item:get_name() == mobs_mc.items.hay_bale then
|
||||
-- Breed with hay bale
|
||||
if mcl_mobs:feed_tame(self, clicker, 1, true, false) then return end
|
||||
elseif not self._has_chest and item:get_name() == "mcl_chests:chest" then
|
||||
item:take_item()
|
||||
clicker:set_wielded_item(item)
|
||||
self._has_chest = true
|
||||
self.base_texture = table.copy(self.base_texture)
|
||||
self.base_texture[1] = self.base_texture[3]
|
||||
self.object:set_properties({
|
||||
textures = self.base_texture,
|
||||
})
|
||||
get_drops(self)
|
||||
return
|
||||
elseif self._has_chest and clicker:get_player_control().sneak then
|
||||
mcl_entity_invs.show_inv_form(self,clicker," - Strength "..math.floor(self._inv_size / 3))
|
||||
return
|
||||
if mobs:feed_tame(self, clicker, 1, true, false) then return end
|
||||
else
|
||||
-- Feed with anything else
|
||||
if mcl_mobs:feed_tame(self, clicker, 1, false, true) then return end
|
||||
if mobs:feed_tame(self, clicker, 1, false, true) then return end
|
||||
end
|
||||
if mcl_mobs:protect(self, clicker) then return end
|
||||
if mobs:protect(self, clicker) then return end
|
||||
|
||||
-- Make sure tamed llama is mature and being clicked by owner only
|
||||
if self.tamed and not self.child and self.owner == clicker:get_player_name() then
|
||||
|
||||
-- Place carpet
|
||||
--[[ TODO: Re-enable this code when carpet textures arrived.
|
||||
if minetest.get_item_group(item:get_name(), "carpet") == 1 and not self.carpet then
|
||||
for group, carpetdata in pairs(carpets) do
|
||||
if minetest.get_item_group(item:get_name(), group) == 1 then
|
||||
|
@ -195,23 +156,42 @@ mcl_mobs:register_mob("mobs_mc:llama", {
|
|||
textures = self.base_texture,
|
||||
})
|
||||
self.carpet = item:get_name()
|
||||
get_drops(self)
|
||||
self.drops = {
|
||||
{name = mobs_mc.items.leather,
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,},
|
||||
{name = item:get_name(),
|
||||
chance = 1,
|
||||
min = 1,
|
||||
max = 1,},
|
||||
}
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
]]
|
||||
|
||||
-- detatch player already riding llama
|
||||
if self.driver and clicker == self.driver then
|
||||
mcl_mobs.detach(clicker, {x = 1, y = 0, z = 1})
|
||||
|
||||
mobs.detach(clicker, {x = 1, y = 0, z = 1})
|
||||
|
||||
-- attach player to llama
|
||||
elseif not self.driver then
|
||||
|
||||
self.object:set_properties({stepheight = 1.1})
|
||||
mcl_mobs.attach(self, clicker)
|
||||
mobs.attach(self, clicker)
|
||||
end
|
||||
|
||||
-- Used to capture llama
|
||||
elseif not self.driver and clicker:get_wielded_item():get_name() ~= "" then
|
||||
mobs:capture_mob(self, clicker, 0, 5, 60, false, nil)
|
||||
end
|
||||
end,
|
||||
|
||||
--[[
|
||||
TODO: Enable this code when carpet textures arrived.
|
||||
on_breed = function(parent1, parent2)
|
||||
-- When breeding, make sure the child has no carpet
|
||||
local pos = parent1.object:get_pos()
|
||||
|
@ -221,7 +201,7 @@ mcl_mobs:register_mob("mobs_mc:llama", {
|
|||
else
|
||||
parent = parent2
|
||||
end
|
||||
child = mcl_mobs:spawn_child(pos, parent.name)
|
||||
child = mobs:spawn_child(pos, parent.name)
|
||||
if child then
|
||||
local ent_c = child:get_luaentity()
|
||||
ent_c.base_texture = table.copy(ent_c.base_texture)
|
||||
|
@ -233,68 +213,30 @@ mcl_mobs:register_mob("mobs_mc:llama", {
|
|||
return false
|
||||
end
|
||||
end,
|
||||
on_spawn = function(self)
|
||||
if not self._inv_size then
|
||||
local r = math.random(1000)
|
||||
if r < 80 then
|
||||
self._inv_size = 15
|
||||
elseif r < 160 then
|
||||
self._inv_size = 12
|
||||
elseif r < 488 then
|
||||
self._inv_size = 9
|
||||
elseif r < 816 then
|
||||
self._inv_size = 6
|
||||
else
|
||||
self._inv_size = 3
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
]]
|
||||
|
||||
mcl_entity_invs.register_inv("mobs_mc:llama","Llama",nil,true)
|
||||
|
||||
-- spit arrow (weapon)
|
||||
mcl_mobs:register_arrow("mobs_mc:llamaspit", {
|
||||
visual = "sprite",
|
||||
visual_size = {x = 0.10, y = 0.10},
|
||||
textures = {"mobs_mc_llama_spit.png"},
|
||||
velocity = 5,
|
||||
hit_player = function(self, player)
|
||||
player:punch(self.object, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {fleshy = 1},
|
||||
}, nil)
|
||||
end,
|
||||
|
||||
hit_mob = function(self, mob)
|
||||
end,
|
||||
|
||||
hit_node = function(self, pos, node)
|
||||
end
|
||||
})
|
||||
|
||||
--spawn
|
||||
mcl_mobs:spawn_specific(
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:llama",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"Savanna",
|
||||
"SavannaM",
|
||||
"SavannaM_beach",
|
||||
"Savanna_beach",
|
||||
"Savanna_ocean",
|
||||
"ExtremeHills",
|
||||
"ExtremeHills_beach",
|
||||
"ExtremeHillsM",
|
||||
}, --FIXME: Needs Windswept Forest when that is added.
|
||||
"Mesa",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
15000,
|
||||
5,
|
||||
mobs_mc.water_level+15,
|
||||
mcl_vars.mg_overworld_max)
|
||||
mobs_mc.spawn_height.water+15,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn eggs
|
||||
mcl_mobs:register_egg("mobs_mc:llama", S("Llama"), "#c09e7d", "#995f40", 0)
|
||||
mobs:register_egg("mobs_mc:llama", S("Llama"), "mobs_mc_spawn_icon_llama.png", 0)
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
# textdomain: mobs_mc
|
||||
Totem of Undying=Totem der Unsterblichkeit
|
||||
A totem of undying is a rare artifact which may safe you from certain death.=Ein Totem der Unsterblichkeit ist ein seltenes Artefakt, dass Sie vor dem sicheren Tod bewahren kann.
|
||||
The totem only works while you hold it in your hand. If you receive fatal damage, you are saved from death and you get a second chance with 1 HP. The totem is destroyed in the process, however.=Der Totem funktioniert nur, während Sie ihn halten. Wenn Sie normalerweise tödlich hohen Schaden erhalten, werden Sie vor dem Tod bewahrt und Sie erhalten eine zweite Chance mit 1 TP. Der Totem wird dabei zerstört.
|
||||
Agent=Akteur
|
||||
Bat=Fledermaus
|
||||
Blaze=Lohe
|
||||
|
@ -49,6 +52,13 @@ Wolf=Wolf
|
|||
Husk=Wüstenzombie
|
||||
Zombie=Zombie
|
||||
Zombie Pigman=Schweinezombie
|
||||
Iron Horse Armor=Eisenpferderüstung
|
||||
Iron horse armor can be worn by horses to increase their protection from harm a bit.=Eine Eisenpferderüstung kann von Pferden getragen werden, um ihren Schutz vor Schaden etwas zu erhöhen.
|
||||
Golden Horse Armor=Goldpferderüstung
|
||||
Golden horse armor can be worn by horses to increase their protection from harm.=Eine Goldpferderüstung kann von Pferden getragen werden, um ihren Schutz vor Schaden zu erhöhen.
|
||||
Diamond Horse Armor=Diamantpferderüstung
|
||||
Diamond horse armor can be worn by horses to greatly increase their protection from harm.=Eine Diamantpferderüstung kann von Pferden getragen werden, um ihren Schutz vor Schaden beträchtlich zu erhöhen.
|
||||
Place it on a horse to put on the horse armor. Donkeys and mules can't wear horse armor.=Platzieren Sie es auf einem Pferd, um die Pferderüstung aufzusetzen. Esel und Maultiere können keine Pferderüstung tragen.
|
||||
Farmer=Bauer
|
||||
Fisherman=Fischer
|
||||
Fletcher=Pfeilmacher
|
||||
|
@ -62,3 +72,4 @@ Weapon Smith=Waffenschmied
|
|||
Tool Smith=Werkzeugschmied
|
||||
Cleric=Priester
|
||||
Nitwit=Dorftrottel
|
||||
Protects you from death while wielding it=Schützt vor dem Tod, wenn es gehalten wird
|
||||
|
|
|
@ -1,58 +1,74 @@
|
|||
# textdomain: mobs_mc
|
||||
Totem of Undying=Tótem de la inmortalidad
|
||||
A totem of undying is a rare artifact which may safe you from certain death.=Un tótem de la inmortalidad es un artefacto raro que puede salvarte de una muerte segura.
|
||||
The totem only works while you hold it in your hand. If you receive fatal damage, you are saved from death and you get a second chance with 1 HP. The totem is destroyed in the process, however.=El tótem solo funciona mientras lo sostienes en tu mano. Si recibes un daño crítico, no mueres y obtienes una segunda oportunidad con 1 HP. Sin embargo, el tótem se destruye en el proceso.
|
||||
Agent=Agente
|
||||
Bat=Murciélago
|
||||
Blaze=Blaze
|
||||
Chicken=Pollo
|
||||
Cod=Bacalao
|
||||
Cow=Vaca
|
||||
Mooshroom=Champivaca
|
||||
Mooshroom=Champiñaca
|
||||
Creeper=Creeper
|
||||
Dolphin=Delfín
|
||||
Ender Dragon=Ender Dragon
|
||||
Ender Dragon=Enderdragón
|
||||
Enderman=Enderman
|
||||
Endermite=Endermite
|
||||
Ghast=Ghast
|
||||
Elder Guardian=Gran guardián
|
||||
Guardian=Guardián
|
||||
Elder Guardian=Guardián Anciano
|
||||
Donkey=Burro
|
||||
Horse=Caballo
|
||||
Skeleton Horse=Caballo esquelético
|
||||
Zombie Horse=Caballo zombie
|
||||
Donkey=Burro
|
||||
Mule=Mula
|
||||
Skeleton Horse=Caballo esqueleto
|
||||
Zombie Horse=Caballo zombi
|
||||
Iron Golem=Golem de hierro
|
||||
Llama=Llama
|
||||
Cat=Gato
|
||||
Ocelot=Ocelote
|
||||
Parrot=Loro
|
||||
Pig=Cerdo
|
||||
Polar Bear=Oso polar
|
||||
Killer Bunny=Conejo asesino
|
||||
Rabbit=Conejo
|
||||
Salmon=Salmón
|
||||
Killer Bunny=Conejo asesino
|
||||
Sheep=Oveja
|
||||
Shulker=Shulker
|
||||
Silverfish=Lepisma
|
||||
Skeleton=Esqueleto
|
||||
Stray=Esqueleto glacial
|
||||
Wither Skeleton=Esqueleto del Wither
|
||||
Stray=Esqueleto
|
||||
Wither Skeleton=Esqueleto wither
|
||||
Magma Cube=Cubo de Magma
|
||||
Slime=Slime
|
||||
Snow Golem=Golem de nieve
|
||||
Cave Spider=Araña de las cuevas
|
||||
Spider=Araña
|
||||
Cave Spider=Araña de las cuevas
|
||||
Squid=Calamar
|
||||
Vex=Ánima
|
||||
Master=Maestro
|
||||
Villager=Aldeano
|
||||
Evoker=Invocador
|
||||
Illusioner=Illusionista
|
||||
Villager=Aldeano
|
||||
Vindicator=Vindicador
|
||||
Zombie Villager=Aldeano zombi
|
||||
Zombie Villager=Aldeano zombie
|
||||
Witch=Bruja
|
||||
Wither=Wither
|
||||
Wolf=Lobo
|
||||
Baby Husk=Bebé Zombi Momificado
|
||||
Baby Zombie=Bebé Zombi
|
||||
Husk=Zombi Momificado
|
||||
Zombie=Zombi
|
||||
Baby Zombie Pigman=Bebé Hombrecerdo Zombi
|
||||
Zombie Pigman=Hombrecerdo Zombi
|
||||
Husk=Husk
|
||||
Zombie=Zombie
|
||||
Zombie Pigman=Cerdo Zombie
|
||||
Iron Horse Armor=Armadura de hierro para caballo
|
||||
Iron horse armor can be worn by horses to increase their protection from harm a bit.=Los caballos pueden usar armadura de caballo de hierro para aumentar un poco su protección contra el daño.
|
||||
Golden Horse Armor=Armadura de oro para caballo
|
||||
Golden horse armor can be worn by horses to increase their protection from harm.=Los caballos pueden usar armadura de caballo de oro para aumentar su protección contra el daño.
|
||||
Diamond Horse Armor=Armadura de diamante para caballo
|
||||
Diamond horse armor can be worn by horses to greatly increase their protection from harm.=Los caballos pueden usar armadura de caballo de diamante para aumentar en gran medida su protección contra el daño.
|
||||
Place it on a horse to put on the horse armor. Donkeys and mules can't wear horse armor.=Colóquelo en un caballo para ponerle la armadura de caballo. Los burros y las mulas no pueden usar armadura de caballo.
|
||||
Farmer=Granjero
|
||||
Fisherman=Pescador
|
||||
Fletcher=Flechador
|
||||
Shepherd=Sacerdote
|
||||
Librarian=Bibliotecario
|
||||
Cartographer=Cartógrafo
|
||||
Armorer=Armero
|
||||
Leatherworker=Peletero
|
||||
Butcher=Carnicero
|
||||
Weapon Smith=Herrero de Armas
|
||||
Tool Smith=Herrero de Herramientas
|
||||
Cleric=Sacerdote
|
||||
Nitwit=Simple
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
# textdomain: mobs_mc
|
||||
Totem of Undying=Totem d'immortalité
|
||||
A totem of undying is a rare artifact which may safe you from certain death.=Un totem d'immortalité est un artefact rare qui peut vous protéger d'une mort certaine.
|
||||
The totem only works while you hold it in your hand. If you receive fatal damage, you are saved from death and you get a second chance with 1 HP. The totem is destroyed in the process, however.=Le totem ne fonctionne que lorsque vous le tenez dans votre main. Si vous recevez des dégâts mortels, vous êtes sauvé de la mort et vous obtenez une seconde chance avec 1 HP. Cependant, le totem est détruit.
|
||||
Agent=Agent
|
||||
Bat=Chauve-souris
|
||||
Blaze=Blaze
|
||||
|
@ -49,6 +52,13 @@ Wolf=Loup
|
|||
Husk=Zombie Momifié
|
||||
Zombie=Zombie
|
||||
Zombie Pigman=Zombie Cochon
|
||||
Iron Horse Armor=Armure de cheval en fer
|
||||
Iron horse armor can be worn by horses to increase their protection from harm a bit.=L'armure de cheval en fer peut être portée par les chevaux pour augmenter un peu leur protection contre les dommages.
|
||||
Golden Horse Armor=Armure de cheval en or
|
||||
Golden horse armor can be worn by horses to increase their protection from harm.=Une armure de cheval en or peut être portée par les chevaux pour augmenter leur protection contre les dommages.
|
||||
Diamond Horse Armor=Armure de cheval en diamant
|
||||
Diamond horse armor can be worn by horses to greatly increase their protection from harm.=Une armure de cheval en diament peut être portée par les chevaux pour augmenter fortement leur protection contre les dommages.
|
||||
Place it on a horse to put on the horse armor. Donkeys and mules can't wear horse armor.=Placez-la sur un cheval pour mettre l'armure de cheval. Les ânes et les mules ne peuvent pas porter d'armure de cheval.
|
||||
Farmer=Fermier
|
||||
Fisherman=Pêcheur
|
||||
Fletcher=Archer
|
||||
|
@ -62,8 +72,4 @@ Weapon Smith=Fabriquant d'arme
|
|||
Tool Smith=Fabriquant d'outil
|
||||
Cleric=Clerc
|
||||
Nitwit=Crétin
|
||||
Cod=Morue
|
||||
Salmon=Saumon
|
||||
Dolphin=Dauphin
|
||||
Pillager=Pilleur
|
||||
Tropical fish=Poisson tropical
|
||||
Protects you from death while wielding it=Vous protège de la mort en la maniant
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
# textdomain: mobs_mc
|
||||
Totem of Undying=Тотем бессмертия
|
||||
A totem of undying is a rare artifact which may safe you from certain death.=Тотем бессмертия это редкий артефакт, способный спасти вас от смерти.
|
||||
The totem only works while you hold it in your hand. If you receive fatal damage, you are saved from death and you get a second chance with 1 HP. The totem is destroyed in the process, however.=Тотем работает только когда вы держите его в руке. Если вы получаете смертельный урон, вы спасаетесь от смерти и получаете второй шанс с 1 HP. Однако тотем при этом уничтожается.
|
||||
Agent=Агент
|
||||
Bat=Летучая мышь
|
||||
Blaze=Ифрит
|
||||
|
@ -49,6 +52,13 @@ Wolf=Волк
|
|||
Husk=Кадавр
|
||||
Zombie=Зомби
|
||||
Zombie Pigman=Зомби-свиночеловек
|
||||
Iron Horse Armor=Железные доспехи лошади
|
||||
Iron horse armor can be worn by horses to increase their protection from harm a bit.=Железные доспехи лошади, надетые на лошадь, немного защищают её от вреда.
|
||||
Golden Horse Armor=Золотые доспехи лошади
|
||||
Golden horse armor can be worn by horses to increase their protection from harm.=Золотые доспехи лошади, надетые на лошадь, защищают её от вреда.
|
||||
Diamond Horse Armor=Алмазные доспехи лошади
|
||||
Diamond horse armor can be worn by horses to greatly increase their protection from harm.=Алмазные доспехи лошади, надетые на лошадь, отлично защищают её от вреда.
|
||||
Place it on a horse to put on the horse armor. Donkeys and mules can't wear horse armor.=Поместите это на лошадь, чтобы одеть лошадь в доспехи. Ослики и мулы не могут носить лошадиные доспехи.
|
||||
Farmer=Фермер
|
||||
Fisherman=Рыбак
|
||||
Fletcher=Лучник
|
||||
|
@ -62,3 +72,4 @@ Weapon Smith=Оружейник
|
|||
Tool Smith=Инструментальщик
|
||||
Cleric=Церковник
|
||||
Nitwit=Нищий
|
||||
Protects you from death while wielding it=Защищает вас от смерти, пока вы владеете им
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
# textdomain: mobs_mc
|
||||
Totem of Undying=
|
||||
A totem of undying is a rare artifact which may safe you from certain death.=
|
||||
The totem only works while you hold it in your hand. If you receive fatal damage, you are saved from death and you get a second chance with 1 HP. The totem is destroyed in the process, however.=
|
||||
Agent=
|
||||
Bat=
|
||||
Blaze=
|
||||
|
@ -49,6 +52,13 @@ Wolf=
|
|||
Husk=
|
||||
Zombie=
|
||||
Zombie Pigman=
|
||||
Iron Horse Armor=
|
||||
Iron horse armor can be worn by horses to increase their protection from harm a bit.=
|
||||
Golden Horse Armor=
|
||||
Golden horse armor can be worn by horses to increase their protection from harm.=
|
||||
Diamond Horse Armor=
|
||||
Diamond horse armor can be worn by horses to greatly increase their protection from harm.=
|
||||
Place it on a horse to put on the horse armor. Donkeys and mules can't wear horse armor.=
|
||||
Farmer=
|
||||
Fisherman=
|
||||
Fletcher=
|
||||
|
@ -62,8 +72,4 @@ Weapon Smith=
|
|||
Tool Smith=
|
||||
Cleric=
|
||||
Nitwit=
|
||||
Cod=
|
||||
Salmon=
|
||||
Dolphin=
|
||||
Pillager=
|
||||
Tropical fish=
|
||||
Protects you from death while wielding it=
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name = mobs_mc
|
||||
author = maikerumine
|
||||
description = Adds Minecraft-like monsters and animals.
|
||||
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip, mcl_core
|
||||
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip
|
||||
optional_depends = default, mcl_tnt, mcl_bows, mcl_throwing, mcl_fishing, bones, mesecons_materials, mobs_mc_gameconfig, doc_items
|
||||
|
||||
|
|